1 //===-- NSSet.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 "NSSet.h" 10 #include "CFBasicHash.h" 11 12 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" 13 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 14 #include "lldb/Core/ValueObject.h" 15 #include "lldb/Core/ValueObjectConstResult.h" 16 #include "lldb/DataFormatters/FormattersHelpers.h" 17 #include "lldb/Target/Language.h" 18 #include "lldb/Target/Target.h" 19 #include "lldb/Utility/DataBufferHeap.h" 20 #include "lldb/Utility/Endian.h" 21 #include "lldb/Utility/Status.h" 22 #include "lldb/Utility/Stream.h" 23 24 using namespace lldb; 25 using namespace lldb_private; 26 using namespace lldb_private::formatters; 27 28 std::map<ConstString, CXXFunctionSummaryFormat::Callback> & 29 NSSet_Additionals::GetAdditionalSummaries() { 30 static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map; 31 return g_map; 32 } 33 34 std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> & 35 NSSet_Additionals::GetAdditionalSynthetics() { 36 static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> 37 g_map; 38 return g_map; 39 } 40 41 namespace lldb_private { 42 namespace formatters { 43 class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd { 44 public: 45 NSSetISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 46 47 ~NSSetISyntheticFrontEnd() override; 48 49 size_t CalculateNumChildren() override; 50 51 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 52 53 bool Update() override; 54 55 bool MightHaveChildren() override; 56 57 size_t GetIndexOfChildWithName(ConstString name) override; 58 59 private: 60 struct DataDescriptor_32 { 61 uint32_t _used : 26; 62 uint32_t _szidx : 6; 63 }; 64 65 struct DataDescriptor_64 { 66 uint64_t _used : 58; 67 uint32_t _szidx : 6; 68 }; 69 70 struct SetItemDescriptor { 71 lldb::addr_t item_ptr; 72 lldb::ValueObjectSP valobj_sp; 73 }; 74 75 ExecutionContextRef m_exe_ctx_ref; 76 uint8_t m_ptr_size = 8; 77 DataDescriptor_32 *m_data_32 = nullptr; 78 DataDescriptor_64 *m_data_64 = nullptr; 79 lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS; 80 std::vector<SetItemDescriptor> m_children; 81 }; 82 83 class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 84 public: 85 NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 86 87 size_t CalculateNumChildren() override; 88 89 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 90 91 bool Update() override; 92 93 bool MightHaveChildren() override; 94 95 size_t GetIndexOfChildWithName(ConstString name) override; 96 97 private: 98 struct SetItemDescriptor { 99 lldb::addr_t item_ptr; 100 lldb::ValueObjectSP valobj_sp; 101 }; 102 103 ExecutionContextRef m_exe_ctx_ref; 104 uint8_t m_ptr_size = 8; 105 lldb::ByteOrder m_order = lldb::eByteOrderInvalid; 106 107 CFBasicHash m_hashtable; 108 109 CompilerType m_pair_type; 110 std::vector<SetItemDescriptor> m_children; 111 }; 112 113 template <typename D32, typename D64> 114 class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 115 public: 116 GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 117 118 ~GenericNSSetMSyntheticFrontEnd() override; 119 120 size_t CalculateNumChildren() override; 121 122 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 123 124 bool Update() override; 125 126 bool MightHaveChildren() override; 127 128 size_t GetIndexOfChildWithName(ConstString name) override; 129 130 private: 131 132 struct SetItemDescriptor { 133 lldb::addr_t item_ptr; 134 lldb::ValueObjectSP valobj_sp; 135 }; 136 137 ExecutionContextRef m_exe_ctx_ref; 138 uint8_t m_ptr_size = 8; 139 D32 *m_data_32; 140 D64 *m_data_64; 141 std::vector<SetItemDescriptor> m_children; 142 }; 143 144 namespace Foundation1300 { 145 struct DataDescriptor_32 { 146 uint32_t _used : 26; 147 uint32_t _size; 148 uint32_t _mutations; 149 uint32_t _objs_addr; 150 }; 151 152 struct DataDescriptor_64 { 153 uint64_t _used : 58; 154 uint64_t _size; 155 uint64_t _mutations; 156 uint64_t _objs_addr; 157 }; 158 159 using NSSetMSyntheticFrontEnd = 160 GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; 161 } 162 163 namespace Foundation1428 { 164 struct DataDescriptor_32 { 165 uint32_t _used : 26; 166 uint32_t _size; 167 uint32_t _objs_addr; 168 uint32_t _mutations; 169 }; 170 171 struct DataDescriptor_64 { 172 uint64_t _used : 58; 173 uint64_t _size; 174 uint64_t _objs_addr; 175 uint64_t _mutations; 176 }; 177 178 using NSSetMSyntheticFrontEnd = 179 GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; 180 } 181 182 namespace Foundation1437 { 183 struct DataDescriptor_32 { 184 uint32_t _cow; 185 // __table storage 186 uint32_t _objs_addr; 187 uint32_t _muts; 188 uint32_t _used : 26; 189 uint32_t _szidx : 6; 190 }; 191 192 struct DataDescriptor_64 { 193 uint64_t _cow; 194 // __Table storage 195 uint64_t _objs_addr; 196 uint32_t _muts; 197 uint32_t _used : 26; 198 uint32_t _szidx : 6; 199 }; 200 201 using NSSetMSyntheticFrontEnd = 202 GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; 203 204 template <typename DD> 205 uint64_t 206 __NSSetMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr, 207 Status &error) { 208 const lldb::addr_t start_of_descriptor = 209 valobj_addr + process.GetAddressByteSize(); 210 DD descriptor = DD(); 211 process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor), 212 error); 213 if (error.Fail()) { 214 return 0; 215 } 216 return descriptor._used; 217 } 218 219 uint64_t 220 __NSSetMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, 221 Status &error) { 222 if (process.GetAddressByteSize() == 4) { 223 return __NSSetMSize_Impl<DataDescriptor_32>(process, valobj_addr, error); 224 } else { 225 return __NSSetMSize_Impl<DataDescriptor_64>(process, valobj_addr, error); 226 } 227 } 228 } 229 230 class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 231 public: 232 NSSetCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 233 234 ~NSSetCodeRunningSyntheticFrontEnd() 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 } // namespace formatters 247 } // namespace lldb_private 248 249 template <bool cf_style> 250 bool lldb_private::formatters::NSSetSummaryProvider( 251 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 252 static ConstString g_TypeHint("NSSet"); 253 254 ProcessSP process_sp = valobj.GetProcessSP(); 255 if (!process_sp) 256 return false; 257 258 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 259 260 if (!runtime) 261 return false; 262 263 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 264 runtime->GetClassDescriptor(valobj)); 265 266 if (!descriptor || !descriptor->IsValid()) 267 return false; 268 269 uint32_t ptr_size = process_sp->GetAddressByteSize(); 270 bool is_64bit = (ptr_size == 8); 271 272 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 273 274 if (!valobj_addr) 275 return false; 276 277 uint64_t value = 0; 278 279 ConstString class_name(descriptor->GetClassName()); 280 281 static const ConstString g_SetI("__NSSetI"); 282 static const ConstString g_OrderedSetI("__NSOrderedSetI"); 283 static const ConstString g_SetM("__NSSetM"); 284 static const ConstString g_SetCF("__NSCFSet"); 285 static const ConstString g_SetCFRef("CFSetRef"); 286 287 if (class_name.IsEmpty()) 288 return false; 289 290 if (class_name == g_SetI || class_name == g_OrderedSetI) { 291 Status error; 292 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 293 ptr_size, 0, error); 294 if (error.Fail()) 295 return false; 296 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); 297 } else if (class_name == g_SetM) { 298 AppleObjCRuntime *apple_runtime = 299 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); 300 Status error; 301 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) { 302 value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error); 303 } else { 304 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 305 ptr_size, 0, error); 306 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); 307 } 308 if (error.Fail()) 309 return false; 310 } else if (class_name == g_SetCF || class_name == g_SetCFRef) { 311 ExecutionContext exe_ctx(process_sp); 312 CFBasicHash cfbh; 313 if (!cfbh.Update(valobj_addr, exe_ctx)) 314 return false; 315 value = cfbh.GetCount(); 316 } else { 317 auto &map(NSSet_Additionals::GetAdditionalSummaries()); 318 auto iter = map.find(class_name), end = map.end(); 319 if (iter != end) 320 return iter->second(valobj, stream, options); 321 else 322 return false; 323 } 324 325 std::string prefix, suffix; 326 if (Language *language = Language::FindPlugin(options.GetLanguage())) { 327 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 328 suffix)) { 329 prefix.clear(); 330 suffix.clear(); 331 } 332 } 333 334 stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element", 335 value == 1 ? "" : "s", suffix.c_str()); 336 return true; 337 } 338 339 SyntheticChildrenFrontEnd * 340 lldb_private::formatters::NSSetSyntheticFrontEndCreator( 341 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { 342 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 343 if (!process_sp) 344 return nullptr; 345 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 346 if (!runtime) 347 return nullptr; 348 349 CompilerType valobj_type(valobj_sp->GetCompilerType()); 350 Flags flags(valobj_type.GetTypeInfo()); 351 352 if (flags.IsClear(eTypeIsPointer)) { 353 Status error; 354 valobj_sp = valobj_sp->AddressOf(error); 355 if (error.Fail() || !valobj_sp) 356 return nullptr; 357 } 358 359 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 360 runtime->GetClassDescriptor(*valobj_sp)); 361 362 if (!descriptor || !descriptor->IsValid()) 363 return nullptr; 364 365 ConstString class_name = descriptor->GetClassName(); 366 367 static const ConstString g_SetI("__NSSetI"); 368 static const ConstString g_OrderedSetI("__NSOrderedSetI"); 369 static const ConstString g_SetM("__NSSetM"); 370 static const ConstString g_SetCF("__NSCFSet"); 371 static const ConstString g_SetCFRef("CFSetRef"); 372 373 if (class_name.IsEmpty()) 374 return nullptr; 375 376 if (class_name == g_SetI || class_name == g_OrderedSetI) { 377 return (new NSSetISyntheticFrontEnd(valobj_sp)); 378 } else if (class_name == g_SetM) { 379 AppleObjCRuntime *apple_runtime = 380 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); 381 if (apple_runtime) { 382 if (apple_runtime->GetFoundationVersion() >= 1437) 383 return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp)); 384 else if (apple_runtime->GetFoundationVersion() >= 1428) 385 return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp)); 386 else 387 return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); 388 } else { 389 return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); 390 } 391 } else if (class_name == g_SetCF || class_name == g_SetCFRef) { 392 return (new NSCFSetSyntheticFrontEnd(valobj_sp)); 393 } else { 394 auto &map(NSSet_Additionals::GetAdditionalSynthetics()); 395 auto iter = map.find(class_name), end = map.end(); 396 if (iter != end) 397 return iter->second(synth, valobj_sp); 398 return nullptr; 399 } 400 } 401 402 lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd( 403 lldb::ValueObjectSP valobj_sp) 404 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref() { 405 if (valobj_sp) 406 Update(); 407 } 408 409 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { 410 delete m_data_32; 411 m_data_32 = nullptr; 412 delete m_data_64; 413 m_data_64 = nullptr; 414 } 415 416 size_t 417 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( 418 ConstString name) { 419 const char *item_name = name.GetCString(); 420 uint32_t idx = ExtractIndexFromString(item_name); 421 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 422 return UINT32_MAX; 423 return idx; 424 } 425 426 size_t 427 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() { 428 if (!m_data_32 && !m_data_64) 429 return 0; 430 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 431 } 432 433 bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() { 434 m_children.clear(); 435 delete m_data_32; 436 m_data_32 = nullptr; 437 delete m_data_64; 438 m_data_64 = nullptr; 439 m_ptr_size = 0; 440 ValueObjectSP valobj_sp = m_backend.GetSP(); 441 if (!valobj_sp) 442 return false; 443 if (!valobj_sp) 444 return false; 445 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 446 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 447 if (!process_sp) 448 return false; 449 m_ptr_size = process_sp->GetAddressByteSize(); 450 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; 451 Status error; 452 if (m_ptr_size == 4) { 453 m_data_32 = new DataDescriptor_32(); 454 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), 455 error); 456 } else { 457 m_data_64 = new DataDescriptor_64(); 458 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), 459 error); 460 } 461 if (error.Fail()) 462 return false; 463 m_data_ptr = data_location + m_ptr_size; 464 return false; 465 } 466 467 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() { 468 return true; 469 } 470 471 lldb::ValueObjectSP 472 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) { 473 uint32_t num_children = CalculateNumChildren(); 474 475 if (idx >= num_children) 476 return lldb::ValueObjectSP(); 477 478 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 479 if (!process_sp) 480 return lldb::ValueObjectSP(); 481 482 if (m_children.empty()) { 483 // do the scan phase 484 lldb::addr_t obj_at_idx = 0; 485 486 uint32_t tries = 0; 487 uint32_t test_idx = 0; 488 489 while (tries < num_children) { 490 obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); 491 if (!process_sp) 492 return lldb::ValueObjectSP(); 493 Status error; 494 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 495 if (error.Fail()) 496 return lldb::ValueObjectSP(); 497 498 test_idx++; 499 500 if (!obj_at_idx) 501 continue; 502 tries++; 503 504 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; 505 506 m_children.push_back(descriptor); 507 } 508 } 509 510 if (idx >= m_children.size()) // should never happen 511 return lldb::ValueObjectSP(); 512 513 SetItemDescriptor &set_item = m_children[idx]; 514 if (!set_item.valobj_sp) { 515 auto ptr_size = process_sp->GetAddressByteSize(); 516 DataBufferHeap buffer(ptr_size, 0); 517 switch (ptr_size) { 518 case 0: // architecture has no clue - fail 519 return lldb::ValueObjectSP(); 520 case 4: 521 *reinterpret_cast<uint32_t *>(buffer.GetBytes()) = 522 static_cast<uint32_t>(set_item.item_ptr); 523 break; 524 case 8: 525 *reinterpret_cast<uint64_t *>(buffer.GetBytes()) = 526 static_cast<uint64_t>(set_item.item_ptr); 527 break; 528 default: 529 lldbassert(false && "pointer size is not 4 nor 8"); 530 } 531 StreamString idx_name; 532 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 533 534 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), 535 process_sp->GetByteOrder(), 536 process_sp->GetAddressByteSize()); 537 538 set_item.valobj_sp = CreateValueObjectFromData( 539 idx_name.GetString(), data, m_exe_ctx_ref, 540 m_backend.GetCompilerType().GetBasicTypeFromAST( 541 lldb::eBasicTypeObjCID)); 542 } 543 return set_item.valobj_sp; 544 } 545 546 lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( 547 lldb::ValueObjectSP valobj_sp) 548 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(), 549 m_pair_type() {} 550 551 size_t 552 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( 553 ConstString name) { 554 const char *item_name = name.GetCString(); 555 const uint32_t idx = ExtractIndexFromString(item_name); 556 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 557 return UINT32_MAX; 558 return idx; 559 } 560 561 size_t 562 lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() { 563 if (!m_hashtable.IsValid()) 564 return 0; 565 return m_hashtable.GetCount(); 566 } 567 568 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() { 569 m_children.clear(); 570 ValueObjectSP valobj_sp = m_backend.GetSP(); 571 m_ptr_size = 0; 572 if (!valobj_sp) 573 return false; 574 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 575 576 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 577 if (!process_sp) 578 return false; 579 m_ptr_size = process_sp->GetAddressByteSize(); 580 m_order = process_sp->GetByteOrder(); 581 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref); 582 } 583 584 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() { 585 return true; 586 } 587 588 lldb::ValueObjectSP 589 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex( 590 size_t idx) { 591 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); 592 593 const uint32_t num_children = CalculateNumChildren(); 594 595 if (idx >= num_children) 596 return lldb::ValueObjectSP(); 597 598 if (m_children.empty()) { 599 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 600 if (!process_sp) 601 return lldb::ValueObjectSP(); 602 603 Status error; 604 lldb::addr_t val_at_idx = 0; 605 606 uint32_t tries = 0; 607 uint32_t test_idx = 0; 608 609 // Iterate over inferior memory, reading value pointers by shifting the 610 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read 611 // fails, otherwise, continue until the number of tries matches the number 612 // of childen. 613 while (tries < num_children) { 614 val_at_idx = m_values_ptr + (test_idx * m_ptr_size); 615 616 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); 617 if (error.Fail()) 618 return lldb::ValueObjectSP(); 619 620 test_idx++; 621 622 if (!val_at_idx) 623 continue; 624 tries++; 625 626 SetItemDescriptor descriptor = {val_at_idx, lldb::ValueObjectSP()}; 627 628 m_children.push_back(descriptor); 629 } 630 } 631 632 if (idx >= m_children.size()) // should never happen 633 return lldb::ValueObjectSP(); 634 635 SetItemDescriptor &set_item = m_children[idx]; 636 if (!set_item.valobj_sp) { 637 638 WritableDataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0)); 639 640 switch (m_ptr_size) { 641 case 0: // architecture has no clue - fail 642 return lldb::ValueObjectSP(); 643 case 4: 644 *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) = 645 static_cast<uint32_t>(set_item.item_ptr); 646 break; 647 case 8: 648 *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) = 649 static_cast<uint64_t>(set_item.item_ptr); 650 break; 651 default: 652 lldbassert(false && "pointer size is not 4 nor 8"); 653 } 654 StreamString idx_name; 655 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 656 657 DataExtractor data(buffer_sp, m_order, m_ptr_size); 658 659 set_item.valobj_sp = CreateValueObjectFromData( 660 idx_name.GetString(), data, m_exe_ctx_ref, 661 m_backend.GetCompilerType().GetBasicTypeFromAST( 662 lldb::eBasicTypeObjCID)); 663 } 664 665 return set_item.valobj_sp; 666 } 667 668 template <typename D32, typename D64> 669 lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< 670 D32, D64>::GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 671 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), 672 m_data_32(nullptr), m_data_64(nullptr) { 673 if (valobj_sp) 674 Update(); 675 } 676 677 template <typename D32, typename D64> 678 lldb_private::formatters:: 679 GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd<D32, D64>() { 680 delete m_data_32; 681 m_data_32 = nullptr; 682 delete m_data_64; 683 m_data_64 = nullptr; 684 } 685 686 template <typename D32, typename D64> 687 size_t 688 lldb_private::formatters:: 689 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName( 690 ConstString name) { 691 const char *item_name = name.GetCString(); 692 uint32_t idx = ExtractIndexFromString(item_name); 693 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 694 return UINT32_MAX; 695 return idx; 696 } 697 698 template <typename D32, typename D64> 699 size_t 700 lldb_private::formatters:: 701 GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() { 702 if (!m_data_32 && !m_data_64) 703 return 0; 704 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 705 } 706 707 template <typename D32, typename D64> 708 bool 709 lldb_private::formatters:: 710 GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() { 711 m_children.clear(); 712 ValueObjectSP valobj_sp = m_backend.GetSP(); 713 m_ptr_size = 0; 714 delete m_data_32; 715 m_data_32 = nullptr; 716 delete m_data_64; 717 m_data_64 = nullptr; 718 if (!valobj_sp) 719 return false; 720 if (!valobj_sp) 721 return false; 722 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 723 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 724 if (!process_sp) 725 return false; 726 m_ptr_size = process_sp->GetAddressByteSize(); 727 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; 728 Status error; 729 if (m_ptr_size == 4) { 730 m_data_32 = new D32(); 731 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), 732 error); 733 } else { 734 m_data_64 = new D64(); 735 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), 736 error); 737 } 738 if (error.Fail()) 739 return false; 740 return false; 741 } 742 743 template <typename D32, typename D64> 744 bool 745 lldb_private::formatters:: 746 GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() { 747 return true; 748 } 749 750 template <typename D32, typename D64> 751 lldb::ValueObjectSP 752 lldb_private::formatters:: 753 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) { 754 lldb::addr_t m_objs_addr = 755 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); 756 757 uint32_t num_children = CalculateNumChildren(); 758 759 if (idx >= num_children) 760 return lldb::ValueObjectSP(); 761 762 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 763 if (!process_sp) 764 return lldb::ValueObjectSP(); 765 766 if (m_children.empty()) { 767 // do the scan phase 768 lldb::addr_t obj_at_idx = 0; 769 770 uint32_t tries = 0; 771 uint32_t test_idx = 0; 772 773 while (tries < num_children) { 774 obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); 775 if (!process_sp) 776 return lldb::ValueObjectSP(); 777 Status error; 778 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 779 if (error.Fail()) 780 return lldb::ValueObjectSP(); 781 782 test_idx++; 783 784 if (!obj_at_idx) 785 continue; 786 tries++; 787 788 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; 789 790 m_children.push_back(descriptor); 791 } 792 } 793 794 if (idx >= m_children.size()) // should never happen 795 return lldb::ValueObjectSP(); 796 797 SetItemDescriptor &set_item = m_children[idx]; 798 if (!set_item.valobj_sp) { 799 auto ptr_size = process_sp->GetAddressByteSize(); 800 DataBufferHeap buffer(ptr_size, 0); 801 switch (ptr_size) { 802 case 0: // architecture has no clue?? - fail 803 return lldb::ValueObjectSP(); 804 case 4: 805 *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; 806 break; 807 case 8: 808 *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; 809 break; 810 default: 811 assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); 812 } 813 StreamString idx_name; 814 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 815 816 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), 817 process_sp->GetByteOrder(), 818 process_sp->GetAddressByteSize()); 819 820 set_item.valobj_sp = CreateValueObjectFromData( 821 idx_name.GetString(), data, m_exe_ctx_ref, 822 m_backend.GetCompilerType().GetBasicTypeFromAST( 823 lldb::eBasicTypeObjCID)); 824 } 825 return set_item.valobj_sp; 826 } 827 828 template bool lldb_private::formatters::NSSetSummaryProvider<true>( 829 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); 830 831 template bool lldb_private::formatters::NSSetSummaryProvider<false>( 832 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); 833