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; 77 DataDescriptor_32 *m_data_32; 78 DataDescriptor_64 *m_data_64; 79 lldb::addr_t m_data_ptr; 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; 105 lldb::ByteOrder m_order; 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; 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(), m_ptr_size(8), 405 m_data_32(nullptr), m_data_64(nullptr) { 406 if (valobj_sp) 407 Update(); 408 } 409 410 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { 411 delete m_data_32; 412 m_data_32 = nullptr; 413 delete m_data_64; 414 m_data_64 = nullptr; 415 } 416 417 size_t 418 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( 419 ConstString name) { 420 const char *item_name = name.GetCString(); 421 uint32_t idx = ExtractIndexFromString(item_name); 422 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 423 return UINT32_MAX; 424 return idx; 425 } 426 427 size_t 428 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() { 429 if (!m_data_32 && !m_data_64) 430 return 0; 431 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 432 } 433 434 bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() { 435 m_children.clear(); 436 delete m_data_32; 437 m_data_32 = nullptr; 438 delete m_data_64; 439 m_data_64 = nullptr; 440 m_ptr_size = 0; 441 ValueObjectSP valobj_sp = m_backend.GetSP(); 442 if (!valobj_sp) 443 return false; 444 if (!valobj_sp) 445 return false; 446 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 447 Status error; 448 if (valobj_sp->IsPointerType()) { 449 valobj_sp = valobj_sp->Dereference(error); 450 if (error.Fail() || !valobj_sp) 451 return false; 452 } 453 error.Clear(); 454 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 455 if (!process_sp) 456 return false; 457 m_ptr_size = process_sp->GetAddressByteSize(); 458 uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size; 459 if (m_ptr_size == 4) { 460 m_data_32 = new DataDescriptor_32(); 461 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), 462 error); 463 } else { 464 m_data_64 = new DataDescriptor_64(); 465 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), 466 error); 467 } 468 if (error.Fail()) 469 return false; 470 m_data_ptr = data_location + m_ptr_size; 471 return false; 472 } 473 474 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() { 475 return true; 476 } 477 478 lldb::ValueObjectSP 479 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) { 480 uint32_t num_children = CalculateNumChildren(); 481 482 if (idx >= num_children) 483 return lldb::ValueObjectSP(); 484 485 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 486 if (!process_sp) 487 return lldb::ValueObjectSP(); 488 489 if (m_children.empty()) { 490 // do the scan phase 491 lldb::addr_t obj_at_idx = 0; 492 493 uint32_t tries = 0; 494 uint32_t test_idx = 0; 495 496 while (tries < num_children) { 497 obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); 498 if (!process_sp) 499 return lldb::ValueObjectSP(); 500 Status error; 501 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 502 if (error.Fail()) 503 return lldb::ValueObjectSP(); 504 505 test_idx++; 506 507 if (!obj_at_idx) 508 continue; 509 tries++; 510 511 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; 512 513 m_children.push_back(descriptor); 514 } 515 } 516 517 if (idx >= m_children.size()) // should never happen 518 return lldb::ValueObjectSP(); 519 520 SetItemDescriptor &set_item = m_children[idx]; 521 if (!set_item.valobj_sp) { 522 auto ptr_size = process_sp->GetAddressByteSize(); 523 DataBufferHeap buffer(ptr_size, 0); 524 switch (ptr_size) { 525 case 0: // architecture has no clue - fail 526 return lldb::ValueObjectSP(); 527 case 4: 528 *reinterpret_cast<uint32_t *>(buffer.GetBytes()) = 529 static_cast<uint32_t>(set_item.item_ptr); 530 break; 531 case 8: 532 *reinterpret_cast<uint64_t *>(buffer.GetBytes()) = 533 static_cast<uint64_t>(set_item.item_ptr); 534 break; 535 default: 536 lldbassert(false && "pointer size is not 4 nor 8"); 537 } 538 StreamString idx_name; 539 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 540 541 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), 542 process_sp->GetByteOrder(), 543 process_sp->GetAddressByteSize()); 544 545 set_item.valobj_sp = CreateValueObjectFromData( 546 idx_name.GetString(), data, m_exe_ctx_ref, 547 m_backend.GetCompilerType().GetBasicTypeFromAST( 548 lldb::eBasicTypeObjCID)); 549 } 550 return set_item.valobj_sp; 551 } 552 553 lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( 554 lldb::ValueObjectSP valobj_sp) 555 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), 556 m_order(lldb::eByteOrderInvalid), m_hashtable(), m_pair_type() {} 557 558 size_t 559 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( 560 ConstString name) { 561 const char *item_name = name.GetCString(); 562 const uint32_t idx = ExtractIndexFromString(item_name); 563 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 564 return UINT32_MAX; 565 return idx; 566 } 567 568 size_t 569 lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() { 570 if (!m_hashtable.IsValid()) 571 return 0; 572 return m_hashtable.GetCount(); 573 } 574 575 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() { 576 m_children.clear(); 577 ValueObjectSP valobj_sp = m_backend.GetSP(); 578 m_ptr_size = 0; 579 if (!valobj_sp) 580 return false; 581 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 582 583 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 584 if (!process_sp) 585 return false; 586 m_ptr_size = process_sp->GetAddressByteSize(); 587 m_order = process_sp->GetByteOrder(); 588 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref); 589 } 590 591 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() { 592 return true; 593 } 594 595 lldb::ValueObjectSP 596 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex( 597 size_t idx) { 598 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); 599 600 const uint32_t num_children = CalculateNumChildren(); 601 602 if (idx >= num_children) 603 return lldb::ValueObjectSP(); 604 605 if (m_children.empty()) { 606 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 607 if (!process_sp) 608 return lldb::ValueObjectSP(); 609 610 Status error; 611 lldb::addr_t val_at_idx = 0; 612 613 uint32_t tries = 0; 614 uint32_t test_idx = 0; 615 616 // Iterate over inferior memory, reading value pointers by shifting the 617 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read 618 // fails, otherwise, continue until the number of tries matches the number 619 // of childen. 620 while (tries < num_children) { 621 val_at_idx = m_values_ptr + (test_idx * m_ptr_size); 622 623 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); 624 if (error.Fail()) 625 return lldb::ValueObjectSP(); 626 627 test_idx++; 628 629 if (!val_at_idx) 630 continue; 631 tries++; 632 633 SetItemDescriptor descriptor = {val_at_idx, lldb::ValueObjectSP()}; 634 635 m_children.push_back(descriptor); 636 } 637 } 638 639 if (idx >= m_children.size()) // should never happen 640 return lldb::ValueObjectSP(); 641 642 SetItemDescriptor &set_item = m_children[idx]; 643 if (!set_item.valobj_sp) { 644 645 DataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0)); 646 647 switch (m_ptr_size) { 648 case 0: // architecture has no clue - fail 649 return lldb::ValueObjectSP(); 650 case 4: 651 *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) = 652 static_cast<uint32_t>(set_item.item_ptr); 653 break; 654 case 8: 655 *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) = 656 static_cast<uint64_t>(set_item.item_ptr); 657 break; 658 default: 659 lldbassert(false && "pointer size is not 4 nor 8"); 660 } 661 StreamString idx_name; 662 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 663 664 DataExtractor data(buffer_sp, m_order, m_ptr_size); 665 666 set_item.valobj_sp = CreateValueObjectFromData( 667 idx_name.GetString(), data, m_exe_ctx_ref, 668 m_backend.GetCompilerType().GetBasicTypeFromAST( 669 lldb::eBasicTypeObjCID)); 670 } 671 672 return set_item.valobj_sp; 673 } 674 675 template <typename D32, typename D64> 676 lldb_private::formatters:: 677 GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd( 678 lldb::ValueObjectSP valobj_sp) 679 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), 680 m_data_32(nullptr), m_data_64(nullptr) { 681 if (valobj_sp) 682 Update(); 683 } 684 685 template <typename D32, typename D64> 686 lldb_private::formatters:: 687 GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd<D32, D64>() { 688 delete m_data_32; 689 m_data_32 = nullptr; 690 delete m_data_64; 691 m_data_64 = nullptr; 692 } 693 694 template <typename D32, typename D64> 695 size_t 696 lldb_private::formatters:: 697 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName( 698 ConstString name) { 699 const char *item_name = name.GetCString(); 700 uint32_t idx = ExtractIndexFromString(item_name); 701 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 702 return UINT32_MAX; 703 return idx; 704 } 705 706 template <typename D32, typename D64> 707 size_t 708 lldb_private::formatters:: 709 GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() { 710 if (!m_data_32 && !m_data_64) 711 return 0; 712 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 713 } 714 715 template <typename D32, typename D64> 716 bool 717 lldb_private::formatters:: 718 GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() { 719 m_children.clear(); 720 ValueObjectSP valobj_sp = m_backend.GetSP(); 721 m_ptr_size = 0; 722 delete m_data_32; 723 m_data_32 = nullptr; 724 delete m_data_64; 725 m_data_64 = nullptr; 726 if (!valobj_sp) 727 return false; 728 if (!valobj_sp) 729 return false; 730 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 731 Status error; 732 if (valobj_sp->IsPointerType()) { 733 valobj_sp = valobj_sp->Dereference(error); 734 if (error.Fail() || !valobj_sp) 735 return false; 736 } 737 error.Clear(); 738 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 739 if (!process_sp) 740 return false; 741 m_ptr_size = process_sp->GetAddressByteSize(); 742 uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size; 743 if (m_ptr_size == 4) { 744 m_data_32 = new D32(); 745 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), 746 error); 747 } else { 748 m_data_64 = new D64(); 749 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), 750 error); 751 } 752 if (error.Fail()) 753 return false; 754 return false; 755 } 756 757 template <typename D32, typename D64> 758 bool 759 lldb_private::formatters:: 760 GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() { 761 return true; 762 } 763 764 template <typename D32, typename D64> 765 lldb::ValueObjectSP 766 lldb_private::formatters:: 767 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) { 768 lldb::addr_t m_objs_addr = 769 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); 770 771 uint32_t num_children = CalculateNumChildren(); 772 773 if (idx >= num_children) 774 return lldb::ValueObjectSP(); 775 776 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 777 if (!process_sp) 778 return lldb::ValueObjectSP(); 779 780 if (m_children.empty()) { 781 // do the scan phase 782 lldb::addr_t obj_at_idx = 0; 783 784 uint32_t tries = 0; 785 uint32_t test_idx = 0; 786 787 while (tries < num_children) { 788 obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); 789 if (!process_sp) 790 return lldb::ValueObjectSP(); 791 Status error; 792 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 793 if (error.Fail()) 794 return lldb::ValueObjectSP(); 795 796 test_idx++; 797 798 if (!obj_at_idx) 799 continue; 800 tries++; 801 802 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; 803 804 m_children.push_back(descriptor); 805 } 806 } 807 808 if (idx >= m_children.size()) // should never happen 809 return lldb::ValueObjectSP(); 810 811 SetItemDescriptor &set_item = m_children[idx]; 812 if (!set_item.valobj_sp) { 813 auto ptr_size = process_sp->GetAddressByteSize(); 814 DataBufferHeap buffer(ptr_size, 0); 815 switch (ptr_size) { 816 case 0: // architecture has no clue?? - fail 817 return lldb::ValueObjectSP(); 818 case 4: 819 *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; 820 break; 821 case 8: 822 *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; 823 break; 824 default: 825 assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); 826 } 827 StreamString idx_name; 828 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 829 830 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), 831 process_sp->GetByteOrder(), 832 process_sp->GetAddressByteSize()); 833 834 set_item.valobj_sp = CreateValueObjectFromData( 835 idx_name.GetString(), data, m_exe_ctx_ref, 836 m_backend.GetCompilerType().GetBasicTypeFromAST( 837 lldb::eBasicTypeObjCID)); 838 } 839 return set_item.valobj_sp; 840 } 841 842 template bool lldb_private::formatters::NSSetSummaryProvider<true>( 843 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); 844 845 template bool lldb_private::formatters::NSSetSummaryProvider<false>( 846 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); 847