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