1 //===-- NSDictionary.cpp --------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include <mutex> 10 11 #include "clang/AST/DeclCXX.h" 12 13 #include "CFBasicHash.h" 14 #include "NSDictionary.h" 15 16 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" 17 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 18 19 #include "lldb/Core/ValueObject.h" 20 #include "lldb/Core/ValueObjectConstResult.h" 21 #include "lldb/DataFormatters/FormattersHelpers.h" 22 #include "lldb/Target/Language.h" 23 #include "lldb/Target/StackFrame.h" 24 #include "lldb/Target/Target.h" 25 #include "lldb/Utility/DataBufferHeap.h" 26 #include "lldb/Utility/Endian.h" 27 #include "lldb/Utility/Status.h" 28 #include "lldb/Utility/Stream.h" 29 30 using namespace lldb; 31 using namespace lldb_private; 32 using namespace lldb_private::formatters; 33 34 NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix( 35 ConstString p) 36 : m_prefix(p) {} 37 38 bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match( 39 ConstString class_name) { 40 return class_name.GetStringRef().startswith(m_prefix.GetStringRef()); 41 } 42 43 NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n) 44 : m_name(n) {} 45 46 bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match( 47 ConstString class_name) { 48 return (class_name == m_name); 49 } 50 51 NSDictionary_Additionals::AdditionalFormatters< 52 CXXFunctionSummaryFormat::Callback> & 53 NSDictionary_Additionals::GetAdditionalSummaries() { 54 static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map; 55 return g_map; 56 } 57 58 NSDictionary_Additionals::AdditionalFormatters< 59 CXXSyntheticChildren::CreateFrontEndCallback> & 60 NSDictionary_Additionals::GetAdditionalSynthetics() { 61 static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback> 62 g_map; 63 return g_map; 64 } 65 66 static CompilerType GetLLDBNSPairType(TargetSP target_sp) { 67 CompilerType compiler_type; 68 69 TypeSystemClang *target_ast_context = TypeSystemClang::GetScratch(*target_sp); 70 71 if (target_ast_context) { 72 ConstString g___lldb_autogen_nspair("__lldb_autogen_nspair"); 73 74 compiler_type = 75 target_ast_context->GetTypeForIdentifier<clang::CXXRecordDecl>( 76 g___lldb_autogen_nspair); 77 78 if (!compiler_type) { 79 compiler_type = target_ast_context->CreateRecordType( 80 nullptr, OptionalClangModuleID(), lldb::eAccessPublic, 81 g___lldb_autogen_nspair.GetCString(), clang::TTK_Struct, 82 lldb::eLanguageTypeC); 83 84 if (compiler_type) { 85 TypeSystemClang::StartTagDeclarationDefinition(compiler_type); 86 CompilerType id_compiler_type = 87 target_ast_context->GetBasicType(eBasicTypeObjCID); 88 TypeSystemClang::AddFieldToRecordType( 89 compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0); 90 TypeSystemClang::AddFieldToRecordType( 91 compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0); 92 TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type); 93 } 94 } 95 } 96 return compiler_type; 97 } 98 99 namespace lldb_private { 100 namespace formatters { 101 class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd { 102 public: 103 NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 104 105 ~NSDictionaryISyntheticFrontEnd() override; 106 107 size_t CalculateNumChildren() override; 108 109 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 110 111 bool Update() override; 112 113 bool MightHaveChildren() override; 114 115 size_t GetIndexOfChildWithName(ConstString name) override; 116 117 private: 118 struct DataDescriptor_32 { 119 uint32_t _used : 26; 120 uint32_t _szidx : 6; 121 }; 122 123 struct DataDescriptor_64 { 124 uint64_t _used : 58; 125 uint32_t _szidx : 6; 126 }; 127 128 struct DictionaryItemDescriptor { 129 lldb::addr_t key_ptr; 130 lldb::addr_t val_ptr; 131 lldb::ValueObjectSP valobj_sp; 132 }; 133 134 ExecutionContextRef m_exe_ctx_ref; 135 uint8_t m_ptr_size; 136 lldb::ByteOrder m_order; 137 DataDescriptor_32 *m_data_32; 138 DataDescriptor_64 *m_data_64; 139 lldb::addr_t m_data_ptr; 140 CompilerType m_pair_type; 141 std::vector<DictionaryItemDescriptor> m_children; 142 }; 143 144 class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd { 145 public: 146 NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 147 148 size_t CalculateNumChildren() override; 149 150 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 151 152 bool Update() override; 153 154 bool MightHaveChildren() override; 155 156 size_t GetIndexOfChildWithName(ConstString name) override; 157 158 private: 159 struct DictionaryItemDescriptor { 160 lldb::addr_t key_ptr; 161 lldb::addr_t val_ptr; 162 lldb::ValueObjectSP valobj_sp; 163 }; 164 165 ExecutionContextRef m_exe_ctx_ref; 166 uint8_t m_ptr_size; 167 lldb::ByteOrder m_order; 168 169 CFBasicHash m_hashtable; 170 171 CompilerType m_pair_type; 172 std::vector<DictionaryItemDescriptor> m_children; 173 }; 174 175 class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd { 176 public: 177 NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 178 179 ~NSDictionary1SyntheticFrontEnd() override = default; 180 181 size_t CalculateNumChildren() override; 182 183 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 184 185 bool Update() override; 186 187 bool MightHaveChildren() override; 188 189 size_t GetIndexOfChildWithName(ConstString name) override; 190 191 private: 192 ValueObjectSP m_pair; 193 }; 194 195 template <typename D32, typename D64> 196 class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 197 public: 198 GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 199 200 ~GenericNSDictionaryMSyntheticFrontEnd() override; 201 202 size_t CalculateNumChildren() override; 203 204 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 205 206 bool Update() override; 207 208 bool MightHaveChildren() override; 209 210 size_t GetIndexOfChildWithName(ConstString name) override; 211 212 private: 213 struct DictionaryItemDescriptor { 214 lldb::addr_t key_ptr; 215 lldb::addr_t val_ptr; 216 lldb::ValueObjectSP valobj_sp; 217 }; 218 219 ExecutionContextRef m_exe_ctx_ref; 220 uint8_t m_ptr_size; 221 lldb::ByteOrder m_order; 222 D32 *m_data_32; 223 D64 *m_data_64; 224 CompilerType m_pair_type; 225 std::vector<DictionaryItemDescriptor> m_children; 226 }; 227 228 namespace Foundation1100 { 229 class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 230 public: 231 NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 232 233 ~NSDictionaryMSyntheticFrontEnd() override; 234 235 size_t CalculateNumChildren() override; 236 237 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 238 239 bool Update() override; 240 241 bool MightHaveChildren() override; 242 243 size_t GetIndexOfChildWithName(ConstString name) override; 244 245 private: 246 struct DataDescriptor_32 { 247 uint32_t _used : 26; 248 uint32_t _kvo : 1; 249 uint32_t _size; 250 uint32_t _mutations; 251 uint32_t _objs_addr; 252 uint32_t _keys_addr; 253 }; 254 255 struct DataDescriptor_64 { 256 uint64_t _used : 58; 257 uint32_t _kvo : 1; 258 uint64_t _size; 259 uint64_t _mutations; 260 uint64_t _objs_addr; 261 uint64_t _keys_addr; 262 }; 263 264 struct DictionaryItemDescriptor { 265 lldb::addr_t key_ptr; 266 lldb::addr_t val_ptr; 267 lldb::ValueObjectSP valobj_sp; 268 }; 269 270 ExecutionContextRef m_exe_ctx_ref; 271 uint8_t m_ptr_size; 272 lldb::ByteOrder m_order; 273 DataDescriptor_32 *m_data_32; 274 DataDescriptor_64 *m_data_64; 275 CompilerType m_pair_type; 276 std::vector<DictionaryItemDescriptor> m_children; 277 }; 278 } 279 280 namespace Foundation1428 { 281 namespace { 282 struct DataDescriptor_32 { 283 uint32_t _used : 26; 284 uint32_t _kvo : 1; 285 uint32_t _size; 286 uint32_t _buffer; 287 uint64_t GetSize() { return _size; } 288 }; 289 290 struct DataDescriptor_64 { 291 uint64_t _used : 58; 292 uint32_t _kvo : 1; 293 uint64_t _size; 294 uint64_t _buffer; 295 uint64_t GetSize() { return _size; } 296 }; 297 } 298 299 using NSDictionaryMSyntheticFrontEnd = 300 GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; 301 } 302 303 namespace Foundation1437 { 304 namespace { 305 static const uint64_t NSDictionaryCapacities[] = { 306 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723, 307 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607, 308 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119, 309 6221311, 10066421, 16287743, 26354171, 42641881, 68996069, 310 111638519, 180634607, 292272623, 472907251 311 }; 312 313 static const size_t NSDictionaryNumSizeBuckets = 314 sizeof(NSDictionaryCapacities) / sizeof(uint64_t); 315 316 struct DataDescriptor_32 { 317 uint32_t _buffer; 318 uint32_t _muts; 319 uint32_t _used : 25; 320 uint32_t _kvo : 1; 321 uint32_t _szidx : 6; 322 323 uint64_t GetSize() { 324 return (_szidx) >= NSDictionaryNumSizeBuckets ? 325 0 : NSDictionaryCapacities[_szidx]; 326 } 327 }; 328 329 struct DataDescriptor_64 { 330 uint64_t _buffer; 331 uint32_t _muts; 332 uint32_t _used : 25; 333 uint32_t _kvo : 1; 334 uint32_t _szidx : 6; 335 336 uint64_t GetSize() { 337 return (_szidx) >= NSDictionaryNumSizeBuckets ? 338 0 : NSDictionaryCapacities[_szidx]; 339 } 340 }; 341 } 342 343 using NSDictionaryMSyntheticFrontEnd = 344 GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; 345 346 template <typename DD> 347 uint64_t 348 __NSDictionaryMSize_Impl(lldb_private::Process &process, 349 lldb::addr_t valobj_addr, Status &error) { 350 const lldb::addr_t start_of_descriptor = 351 valobj_addr + process.GetAddressByteSize(); 352 DD descriptor = DD(); 353 process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor), 354 error); 355 if (error.Fail()) { 356 return 0; 357 } 358 return descriptor._used; 359 } 360 361 uint64_t 362 __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, 363 Status &error) { 364 if (process.GetAddressByteSize() == 4) { 365 return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr, 366 error); 367 } else { 368 return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr, 369 error); 370 } 371 } 372 373 } 374 } // namespace formatters 375 } // namespace lldb_private 376 377 template <bool name_entries> 378 bool lldb_private::formatters::NSDictionarySummaryProvider( 379 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 380 static ConstString g_TypeHint("NSDictionary"); 381 ProcessSP process_sp = valobj.GetProcessSP(); 382 if (!process_sp) 383 return false; 384 385 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 386 387 if (!runtime) 388 return false; 389 390 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 391 runtime->GetClassDescriptor(valobj)); 392 393 if (!descriptor || !descriptor->IsValid()) 394 return false; 395 396 uint32_t ptr_size = process_sp->GetAddressByteSize(); 397 bool is_64bit = (ptr_size == 8); 398 399 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 400 401 if (!valobj_addr) 402 return false; 403 404 uint64_t value = 0; 405 406 ConstString class_name(descriptor->GetClassName()); 407 408 static const ConstString g_DictionaryI("__NSDictionaryI"); 409 static const ConstString g_DictionaryM("__NSDictionaryM"); 410 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy"); 411 static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable"); 412 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI"); 413 static const ConstString g_Dictionary0("__NSDictionary0"); 414 static const ConstString g_DictionaryCF("__CFDictionary"); 415 static const ConstString g_DictionaryNSCF("__NSCFDictionary"); 416 static const ConstString g_DictionaryCFRef("CFDictionaryRef"); 417 418 if (class_name.IsEmpty()) 419 return false; 420 421 if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) { 422 Status error; 423 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 424 ptr_size, 0, error); 425 if (error.Fail()) 426 return false; 427 428 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); 429 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy) { 430 AppleObjCRuntime *apple_runtime = 431 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); 432 Status error; 433 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) { 434 value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr, 435 error); 436 } else { 437 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 438 ptr_size, 0, error); 439 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); 440 } 441 if (error.Fail()) 442 return false; 443 } else if (class_name == g_Dictionary1) { 444 value = 1; 445 } else if (class_name == g_Dictionary0) { 446 value = 0; 447 } else if (class_name == g_DictionaryCF || 448 class_name == g_DictionaryNSCF || 449 class_name == g_DictionaryCFRef) { 450 ExecutionContext exe_ctx(process_sp); 451 CFBasicHash cfbh; 452 if (!cfbh.Update(valobj_addr, exe_ctx)) 453 return false; 454 value = cfbh.GetCount(); 455 } else { 456 auto &map(NSDictionary_Additionals::GetAdditionalSummaries()); 457 for (auto &candidate : map) { 458 if (candidate.first && candidate.first->Match(class_name)) 459 return candidate.second(valobj, stream, options); 460 } 461 return false; 462 } 463 464 std::string prefix, suffix; 465 if (Language *language = Language::FindPlugin(options.GetLanguage())) { 466 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 467 suffix)) { 468 prefix.clear(); 469 suffix.clear(); 470 } 471 } 472 473 stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair", 474 value == 1 ? "" : "s", suffix.c_str()); 475 return true; 476 } 477 478 SyntheticChildrenFrontEnd * 479 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator( 480 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { 481 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 482 if (!process_sp) 483 return nullptr; 484 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 485 ObjCLanguageRuntime::Get(*process_sp)); 486 if (!runtime) 487 return nullptr; 488 489 CompilerType valobj_type(valobj_sp->GetCompilerType()); 490 Flags flags(valobj_type.GetTypeInfo()); 491 492 if (flags.IsClear(eTypeIsPointer)) { 493 Status error; 494 valobj_sp = valobj_sp->AddressOf(error); 495 if (error.Fail() || !valobj_sp) 496 return nullptr; 497 } 498 499 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 500 runtime->GetClassDescriptor(*valobj_sp)); 501 502 if (!descriptor || !descriptor->IsValid()) 503 return nullptr; 504 505 ConstString class_name(descriptor->GetClassName()); 506 507 static const ConstString g_DictionaryI("__NSDictionaryI"); 508 static const ConstString g_DictionaryM("__NSDictionaryM"); 509 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI"); 510 static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable"); 511 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy"); 512 static const ConstString g_Dictionary0("__NSDictionary0"); 513 static const ConstString g_DictionaryCF("__CFDictionary"); 514 static const ConstString g_DictionaryNSCF("__NSCFDictionary"); 515 static const ConstString g_DictionaryCFRef("CFDictionaryRef"); 516 517 if (class_name.IsEmpty()) 518 return nullptr; 519 520 if (class_name == g_DictionaryI) { 521 return (new NSDictionaryISyntheticFrontEnd(valobj_sp)); 522 } else if (class_name == g_DictionaryM) { 523 if (runtime->GetFoundationVersion() >= 1437) { 524 return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp)); 525 } else if (runtime->GetFoundationVersion() >= 1428) { 526 return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp)); 527 } else { 528 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp)); 529 } 530 } else if (class_name == g_DictionaryMLegacy) { 531 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp)); 532 } else if (class_name == g_Dictionary1) { 533 return (new NSDictionary1SyntheticFrontEnd(valobj_sp)); 534 } else if (class_name == g_DictionaryCF || 535 class_name == g_DictionaryNSCF || 536 class_name == g_DictionaryCFRef) { 537 return (new NSCFDictionarySyntheticFrontEnd(valobj_sp)); 538 } else { 539 auto &map(NSDictionary_Additionals::GetAdditionalSynthetics()); 540 for (auto &candidate : map) { 541 if (candidate.first && candidate.first->Match((class_name))) 542 return candidate.second(synth, valobj_sp); 543 } 544 } 545 546 return nullptr; 547 } 548 549 lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: 550 NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 551 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), 552 m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr), 553 m_pair_type() {} 554 555 lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: 556 ~NSDictionaryISyntheticFrontEnd() { 557 delete m_data_32; 558 m_data_32 = nullptr; 559 delete m_data_64; 560 m_data_64 = nullptr; 561 } 562 563 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: 564 GetIndexOfChildWithName(ConstString name) { 565 const char *item_name = name.GetCString(); 566 uint32_t idx = ExtractIndexFromString(item_name); 567 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 568 return UINT32_MAX; 569 return idx; 570 } 571 572 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: 573 CalculateNumChildren() { 574 if (!m_data_32 && !m_data_64) 575 return 0; 576 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 577 } 578 579 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() { 580 m_children.clear(); 581 delete m_data_32; 582 m_data_32 = nullptr; 583 delete m_data_64; 584 m_data_64 = nullptr; 585 m_ptr_size = 0; 586 ValueObjectSP valobj_sp = m_backend.GetSP(); 587 if (!valobj_sp) 588 return false; 589 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 590 Status error; 591 error.Clear(); 592 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 593 if (!process_sp) 594 return false; 595 m_ptr_size = process_sp->GetAddressByteSize(); 596 m_order = process_sp->GetByteOrder(); 597 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; 598 if (m_ptr_size == 4) { 599 m_data_32 = new DataDescriptor_32(); 600 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), 601 error); 602 } else { 603 m_data_64 = new DataDescriptor_64(); 604 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), 605 error); 606 } 607 if (error.Fail()) 608 return false; 609 m_data_ptr = data_location + m_ptr_size; 610 return false; 611 } 612 613 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: 614 MightHaveChildren() { 615 return true; 616 } 617 618 lldb::ValueObjectSP 619 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex( 620 size_t idx) { 621 uint32_t num_children = CalculateNumChildren(); 622 623 if (idx >= num_children) 624 return lldb::ValueObjectSP(); 625 626 if (m_children.empty()) { 627 // do the scan phase 628 lldb::addr_t key_at_idx = 0, val_at_idx = 0; 629 630 uint32_t tries = 0; 631 uint32_t test_idx = 0; 632 633 while (tries < num_children) { 634 key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size); 635 val_at_idx = key_at_idx + m_ptr_size; 636 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 637 if (!process_sp) 638 return lldb::ValueObjectSP(); 639 Status error; 640 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); 641 if (error.Fail()) 642 return lldb::ValueObjectSP(); 643 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); 644 if (error.Fail()) 645 return lldb::ValueObjectSP(); 646 647 test_idx++; 648 649 if (!key_at_idx || !val_at_idx) 650 continue; 651 tries++; 652 653 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, 654 lldb::ValueObjectSP()}; 655 656 m_children.push_back(descriptor); 657 } 658 } 659 660 if (idx >= m_children.size()) // should never happen 661 return lldb::ValueObjectSP(); 662 663 DictionaryItemDescriptor &dict_item = m_children[idx]; 664 if (!dict_item.valobj_sp) { 665 if (!m_pair_type.IsValid()) { 666 TargetSP target_sp(m_backend.GetTargetSP()); 667 if (!target_sp) 668 return ValueObjectSP(); 669 m_pair_type = GetLLDBNSPairType(target_sp); 670 } 671 if (!m_pair_type.IsValid()) 672 return ValueObjectSP(); 673 674 DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); 675 676 if (m_ptr_size == 8) { 677 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); 678 *data_ptr = dict_item.key_ptr; 679 *(data_ptr + 1) = dict_item.val_ptr; 680 } else { 681 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); 682 *data_ptr = dict_item.key_ptr; 683 *(data_ptr + 1) = dict_item.val_ptr; 684 } 685 686 StreamString idx_name; 687 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 688 DataExtractor data(buffer_sp, m_order, m_ptr_size); 689 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, 690 m_exe_ctx_ref, m_pair_type); 691 } 692 return dict_item.valobj_sp; 693 } 694 695 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: 696 NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 697 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), 698 m_order(lldb::eByteOrderInvalid), m_hashtable(), m_pair_type() {} 699 700 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: 701 GetIndexOfChildWithName(ConstString name) { 702 const char *item_name = name.GetCString(); 703 const uint32_t idx = ExtractIndexFromString(item_name); 704 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 705 return UINT32_MAX; 706 return idx; 707 } 708 709 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: 710 CalculateNumChildren() { 711 if (!m_hashtable.IsValid()) 712 return 0; 713 return m_hashtable.GetCount(); 714 } 715 716 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::Update() { 717 m_children.clear(); 718 ValueObjectSP valobj_sp = m_backend.GetSP(); 719 m_ptr_size = 0; 720 if (!valobj_sp) 721 return false; 722 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 723 724 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 725 if (!process_sp) 726 return false; 727 m_ptr_size = process_sp->GetAddressByteSize(); 728 m_order = process_sp->GetByteOrder(); 729 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref); 730 } 731 732 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: 733 MightHaveChildren() { 734 return true; 735 } 736 737 lldb::ValueObjectSP 738 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex( 739 size_t idx) { 740 lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer(); 741 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); 742 743 const uint32_t num_children = CalculateNumChildren(); 744 745 if (idx >= num_children) 746 return lldb::ValueObjectSP(); 747 748 if (m_children.empty()) { 749 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 750 if (!process_sp) 751 return lldb::ValueObjectSP(); 752 753 Status error; 754 lldb::addr_t key_at_idx = 0, val_at_idx = 0; 755 756 uint32_t tries = 0; 757 uint32_t test_idx = 0; 758 759 // Iterate over inferior memory, reading key/value pointers by shifting each 760 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read 761 // fails, otherwise, continue until the number of tries matches the number 762 // of childen. 763 while (tries < num_children) { 764 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); 765 val_at_idx = m_values_ptr + (test_idx * m_ptr_size); 766 767 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); 768 if (error.Fail()) 769 return lldb::ValueObjectSP(); 770 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); 771 if (error.Fail()) 772 return lldb::ValueObjectSP(); 773 774 test_idx++; 775 776 if (!key_at_idx || !val_at_idx) 777 continue; 778 tries++; 779 780 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, 781 lldb::ValueObjectSP()}; 782 783 m_children.push_back(descriptor); 784 } 785 } 786 787 if (idx >= m_children.size()) // should never happen 788 return lldb::ValueObjectSP(); 789 790 DictionaryItemDescriptor &dict_item = m_children[idx]; 791 if (!dict_item.valobj_sp) { 792 if (!m_pair_type.IsValid()) { 793 TargetSP target_sp(m_backend.GetTargetSP()); 794 if (!target_sp) 795 return ValueObjectSP(); 796 m_pair_type = GetLLDBNSPairType(target_sp); 797 } 798 if (!m_pair_type.IsValid()) 799 return ValueObjectSP(); 800 801 DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); 802 803 switch (m_ptr_size) { 804 case 0: // architecture has no clue - fail 805 return lldb::ValueObjectSP(); 806 case 4: { 807 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()); 808 *data_ptr = dict_item.key_ptr; 809 *(data_ptr + 1) = dict_item.val_ptr; 810 } break; 811 case 8: { 812 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()); 813 *data_ptr = dict_item.key_ptr; 814 *(data_ptr + 1) = dict_item.val_ptr; 815 } break; 816 default: 817 lldbassert(false && "pointer size is not 4 nor 8"); 818 } 819 820 StreamString idx_name; 821 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 822 DataExtractor data(buffer_sp, m_order, m_ptr_size); 823 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, 824 m_exe_ctx_ref, m_pair_type); 825 } 826 return dict_item.valobj_sp; 827 } 828 829 lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: 830 NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 831 : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {} 832 833 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: 834 GetIndexOfChildWithName(ConstString name) { 835 static const ConstString g_zero("[0]"); 836 return name == g_zero ? 0 : UINT32_MAX; 837 } 838 839 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: 840 CalculateNumChildren() { 841 return 1; 842 } 843 844 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() { 845 m_pair.reset(); 846 return false; 847 } 848 849 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: 850 MightHaveChildren() { 851 return true; 852 } 853 854 lldb::ValueObjectSP 855 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex( 856 size_t idx) { 857 if (idx != 0) 858 return lldb::ValueObjectSP(); 859 860 if (m_pair.get()) 861 return m_pair; 862 863 auto process_sp(m_backend.GetProcessSP()); 864 if (!process_sp) 865 return nullptr; 866 867 auto ptr_size = process_sp->GetAddressByteSize(); 868 869 lldb::addr_t key_ptr = 870 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size; 871 lldb::addr_t value_ptr = key_ptr + ptr_size; 872 873 Status error; 874 875 lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error); 876 if (error.Fail()) 877 return nullptr; 878 lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error); 879 if (error.Fail()) 880 return nullptr; 881 882 auto pair_type = 883 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this()); 884 885 DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0)); 886 887 if (ptr_size == 8) { 888 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); 889 *data_ptr = key_at_idx; 890 *(data_ptr + 1) = value_at_idx; 891 } else { 892 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); 893 *data_ptr = key_at_idx; 894 *(data_ptr + 1) = value_at_idx; 895 } 896 897 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size); 898 m_pair = CreateValueObjectFromData( 899 "[0]", data, m_backend.GetExecutionContextRef(), pair_type); 900 901 return m_pair; 902 } 903 904 template <typename D32, typename D64> 905 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: 906 GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 907 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), 908 m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr), 909 m_pair_type() {} 910 911 template <typename D32, typename D64> 912 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: 913 ~GenericNSDictionaryMSyntheticFrontEnd<D32,D64>() { 914 delete m_data_32; 915 m_data_32 = nullptr; 916 delete m_data_64; 917 m_data_64 = nullptr; 918 } 919 920 template <typename D32, typename D64> 921 size_t lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< 922 D32, D64>::GetIndexOfChildWithName(ConstString name) { 923 const char *item_name = name.GetCString(); 924 uint32_t idx = ExtractIndexFromString(item_name); 925 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 926 return UINT32_MAX; 927 return idx; 928 } 929 930 template <typename D32, typename D64> 931 size_t 932 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() { 933 if (!m_data_32 && !m_data_64) 934 return 0; 935 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 936 } 937 938 template <typename D32, typename D64> 939 bool 940 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: 941 Update() { 942 m_children.clear(); 943 ValueObjectSP valobj_sp = m_backend.GetSP(); 944 m_ptr_size = 0; 945 delete m_data_32; 946 m_data_32 = nullptr; 947 delete m_data_64; 948 m_data_64 = nullptr; 949 if (!valobj_sp) 950 return false; 951 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 952 Status error; 953 error.Clear(); 954 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 955 if (!process_sp) 956 return false; 957 m_ptr_size = process_sp->GetAddressByteSize(); 958 m_order = process_sp->GetByteOrder(); 959 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; 960 if (m_ptr_size == 4) { 961 m_data_32 = new D32(); 962 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), 963 error); 964 } else { 965 m_data_64 = new D64(); 966 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), 967 error); 968 } 969 if (error.Fail()) 970 return false; 971 return true; 972 } 973 974 template <typename D32, typename D64> 975 bool 976 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: 977 MightHaveChildren() { 978 return true; 979 } 980 981 template <typename D32, typename D64> 982 lldb::ValueObjectSP 983 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< 984 D32, D64>::GetChildAtIndex(size_t idx) { 985 lldb::addr_t m_keys_ptr; 986 lldb::addr_t m_values_ptr; 987 if (m_data_32) { 988 uint32_t size = m_data_32->GetSize(); 989 m_keys_ptr = m_data_32->_buffer; 990 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size); 991 } else { 992 uint32_t size = m_data_64->GetSize(); 993 m_keys_ptr = m_data_64->_buffer; 994 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size); 995 } 996 997 uint32_t num_children = CalculateNumChildren(); 998 999 if (idx >= num_children) 1000 return lldb::ValueObjectSP(); 1001 1002 if (m_children.empty()) { 1003 // do the scan phase 1004 lldb::addr_t key_at_idx = 0, val_at_idx = 0; 1005 1006 uint32_t tries = 0; 1007 uint32_t test_idx = 0; 1008 1009 while (tries < num_children) { 1010 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); 1011 val_at_idx = m_values_ptr + (test_idx * m_ptr_size); 1012 ; 1013 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 1014 if (!process_sp) 1015 return lldb::ValueObjectSP(); 1016 Status error; 1017 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); 1018 if (error.Fail()) 1019 return lldb::ValueObjectSP(); 1020 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); 1021 if (error.Fail()) 1022 return lldb::ValueObjectSP(); 1023 1024 test_idx++; 1025 1026 if (!key_at_idx || !val_at_idx) 1027 continue; 1028 tries++; 1029 1030 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, 1031 lldb::ValueObjectSP()}; 1032 1033 m_children.push_back(descriptor); 1034 } 1035 } 1036 1037 if (idx >= m_children.size()) // should never happen 1038 return lldb::ValueObjectSP(); 1039 1040 DictionaryItemDescriptor &dict_item = m_children[idx]; 1041 if (!dict_item.valobj_sp) { 1042 if (!m_pair_type.IsValid()) { 1043 TargetSP target_sp(m_backend.GetTargetSP()); 1044 if (!target_sp) 1045 return ValueObjectSP(); 1046 m_pair_type = GetLLDBNSPairType(target_sp); 1047 } 1048 if (!m_pair_type.IsValid()) 1049 return ValueObjectSP(); 1050 1051 DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); 1052 1053 if (m_ptr_size == 8) { 1054 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); 1055 *data_ptr = dict_item.key_ptr; 1056 *(data_ptr + 1) = dict_item.val_ptr; 1057 } else { 1058 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); 1059 *data_ptr = dict_item.key_ptr; 1060 *(data_ptr + 1) = dict_item.val_ptr; 1061 } 1062 1063 StreamString idx_name; 1064 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 1065 DataExtractor data(buffer_sp, m_order, m_ptr_size); 1066 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, 1067 m_exe_ctx_ref, m_pair_type); 1068 } 1069 return dict_item.valobj_sp; 1070 } 1071 1072 lldb_private::formatters::Foundation1100:: 1073 NSDictionaryMSyntheticFrontEnd:: 1074 NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 1075 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), 1076 m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr), 1077 m_pair_type() {} 1078 1079 lldb_private::formatters::Foundation1100:: 1080 NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() { 1081 delete m_data_32; 1082 m_data_32 = nullptr; 1083 delete m_data_64; 1084 m_data_64 = nullptr; 1085 } 1086 1087 size_t 1088 lldb_private::formatters::Foundation1100:: 1089 NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { 1090 const char *item_name = name.GetCString(); 1091 uint32_t idx = ExtractIndexFromString(item_name); 1092 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 1093 return UINT32_MAX; 1094 return idx; 1095 } 1096 1097 size_t 1098 lldb_private::formatters::Foundation1100:: 1099 NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() { 1100 if (!m_data_32 && !m_data_64) 1101 return 0; 1102 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 1103 } 1104 1105 bool 1106 lldb_private::formatters::Foundation1100:: 1107 NSDictionaryMSyntheticFrontEnd::Update() { 1108 m_children.clear(); 1109 ValueObjectSP valobj_sp = m_backend.GetSP(); 1110 m_ptr_size = 0; 1111 delete m_data_32; 1112 m_data_32 = nullptr; 1113 delete m_data_64; 1114 m_data_64 = nullptr; 1115 if (!valobj_sp) 1116 return false; 1117 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 1118 Status error; 1119 error.Clear(); 1120 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 1121 if (!process_sp) 1122 return false; 1123 m_ptr_size = process_sp->GetAddressByteSize(); 1124 m_order = process_sp->GetByteOrder(); 1125 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; 1126 if (m_ptr_size == 4) { 1127 m_data_32 = new DataDescriptor_32(); 1128 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), 1129 error); 1130 } else { 1131 m_data_64 = new DataDescriptor_64(); 1132 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), 1133 error); 1134 } 1135 if (error.Fail()) 1136 return false; 1137 return false; 1138 } 1139 1140 bool 1141 lldb_private::formatters::Foundation1100:: 1142 NSDictionaryMSyntheticFrontEnd::MightHaveChildren() { 1143 return true; 1144 } 1145 1146 lldb::ValueObjectSP 1147 lldb_private::formatters::Foundation1100:: 1148 NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) { 1149 lldb::addr_t m_keys_ptr = 1150 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr); 1151 lldb::addr_t m_values_ptr = 1152 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); 1153 1154 uint32_t num_children = CalculateNumChildren(); 1155 1156 if (idx >= num_children) 1157 return lldb::ValueObjectSP(); 1158 1159 if (m_children.empty()) { 1160 // do the scan phase 1161 lldb::addr_t key_at_idx = 0, val_at_idx = 0; 1162 1163 uint32_t tries = 0; 1164 uint32_t test_idx = 0; 1165 1166 while (tries < num_children) { 1167 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); 1168 val_at_idx = m_values_ptr + (test_idx * m_ptr_size); 1169 ; 1170 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 1171 if (!process_sp) 1172 return lldb::ValueObjectSP(); 1173 Status error; 1174 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); 1175 if (error.Fail()) 1176 return lldb::ValueObjectSP(); 1177 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); 1178 if (error.Fail()) 1179 return lldb::ValueObjectSP(); 1180 1181 test_idx++; 1182 1183 if (!key_at_idx || !val_at_idx) 1184 continue; 1185 tries++; 1186 1187 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, 1188 lldb::ValueObjectSP()}; 1189 1190 m_children.push_back(descriptor); 1191 } 1192 } 1193 1194 if (idx >= m_children.size()) // should never happen 1195 return lldb::ValueObjectSP(); 1196 1197 DictionaryItemDescriptor &dict_item = m_children[idx]; 1198 if (!dict_item.valobj_sp) { 1199 if (!m_pair_type.IsValid()) { 1200 TargetSP target_sp(m_backend.GetTargetSP()); 1201 if (!target_sp) 1202 return ValueObjectSP(); 1203 m_pair_type = GetLLDBNSPairType(target_sp); 1204 } 1205 if (!m_pair_type.IsValid()) 1206 return ValueObjectSP(); 1207 1208 DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); 1209 1210 if (m_ptr_size == 8) { 1211 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); 1212 *data_ptr = dict_item.key_ptr; 1213 *(data_ptr + 1) = dict_item.val_ptr; 1214 } else { 1215 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); 1216 *data_ptr = dict_item.key_ptr; 1217 *(data_ptr + 1) = dict_item.val_ptr; 1218 } 1219 1220 StreamString idx_name; 1221 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 1222 DataExtractor data(buffer_sp, m_order, m_ptr_size); 1223 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, 1224 m_exe_ctx_ref, m_pair_type); 1225 } 1226 return dict_item.valobj_sp; 1227 } 1228 1229 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>( 1230 ValueObject &, Stream &, const TypeSummaryOptions &); 1231 1232 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>( 1233 ValueObject &, Stream &, const TypeSummaryOptions &); 1234