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 constexpr llvm::StringLiteral 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 llvm::StringRef prefix, suffix; 326 if (Language *language = Language::FindPlugin(options.GetLanguage())) 327 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 328 329 stream << prefix; 330 stream.Printf("%" PRIu64 " %s%s", value, "element", value == 1 ? "" : "s"); 331 stream << suffix; 332 return true; 333 } 334 335 SyntheticChildrenFrontEnd * 336 lldb_private::formatters::NSSetSyntheticFrontEndCreator( 337 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { 338 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 339 if (!process_sp) 340 return nullptr; 341 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 342 if (!runtime) 343 return nullptr; 344 345 CompilerType valobj_type(valobj_sp->GetCompilerType()); 346 Flags flags(valobj_type.GetTypeInfo()); 347 348 if (flags.IsClear(eTypeIsPointer)) { 349 Status error; 350 valobj_sp = valobj_sp->AddressOf(error); 351 if (error.Fail() || !valobj_sp) 352 return nullptr; 353 } 354 355 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 356 runtime->GetClassDescriptor(*valobj_sp)); 357 358 if (!descriptor || !descriptor->IsValid()) 359 return nullptr; 360 361 ConstString class_name = descriptor->GetClassName(); 362 363 static const ConstString g_SetI("__NSSetI"); 364 static const ConstString g_OrderedSetI("__NSOrderedSetI"); 365 static const ConstString g_SetM("__NSSetM"); 366 static const ConstString g_SetCF("__NSCFSet"); 367 static const ConstString g_SetCFRef("CFSetRef"); 368 369 if (class_name.IsEmpty()) 370 return nullptr; 371 372 if (class_name == g_SetI || class_name == g_OrderedSetI) { 373 return (new NSSetISyntheticFrontEnd(valobj_sp)); 374 } else if (class_name == g_SetM) { 375 AppleObjCRuntime *apple_runtime = 376 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); 377 if (apple_runtime) { 378 if (apple_runtime->GetFoundationVersion() >= 1437) 379 return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp)); 380 else if (apple_runtime->GetFoundationVersion() >= 1428) 381 return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp)); 382 else 383 return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); 384 } else { 385 return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); 386 } 387 } else if (class_name == g_SetCF || class_name == g_SetCFRef) { 388 return (new NSCFSetSyntheticFrontEnd(valobj_sp)); 389 } else { 390 auto &map(NSSet_Additionals::GetAdditionalSynthetics()); 391 auto iter = map.find(class_name), end = map.end(); 392 if (iter != end) 393 return iter->second(synth, valobj_sp); 394 return nullptr; 395 } 396 } 397 398 lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd( 399 lldb::ValueObjectSP valobj_sp) 400 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref() { 401 if (valobj_sp) 402 Update(); 403 } 404 405 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { 406 delete m_data_32; 407 m_data_32 = nullptr; 408 delete m_data_64; 409 m_data_64 = nullptr; 410 } 411 412 size_t 413 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( 414 ConstString name) { 415 const char *item_name = name.GetCString(); 416 uint32_t idx = ExtractIndexFromString(item_name); 417 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 418 return UINT32_MAX; 419 return idx; 420 } 421 422 size_t 423 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() { 424 if (!m_data_32 && !m_data_64) 425 return 0; 426 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 427 } 428 429 bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() { 430 m_children.clear(); 431 delete m_data_32; 432 m_data_32 = nullptr; 433 delete m_data_64; 434 m_data_64 = nullptr; 435 m_ptr_size = 0; 436 ValueObjectSP valobj_sp = m_backend.GetSP(); 437 if (!valobj_sp) 438 return false; 439 if (!valobj_sp) 440 return false; 441 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 442 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 443 if (!process_sp) 444 return false; 445 m_ptr_size = process_sp->GetAddressByteSize(); 446 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; 447 Status error; 448 if (m_ptr_size == 4) { 449 m_data_32 = new DataDescriptor_32(); 450 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), 451 error); 452 } else { 453 m_data_64 = new DataDescriptor_64(); 454 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), 455 error); 456 } 457 if (error.Fail()) 458 return false; 459 m_data_ptr = data_location + m_ptr_size; 460 return true; 461 } 462 463 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() { 464 return true; 465 } 466 467 lldb::ValueObjectSP 468 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) { 469 uint32_t num_children = CalculateNumChildren(); 470 471 if (idx >= num_children) 472 return lldb::ValueObjectSP(); 473 474 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 475 if (!process_sp) 476 return lldb::ValueObjectSP(); 477 478 if (m_children.empty()) { 479 // do the scan phase 480 lldb::addr_t obj_at_idx = 0; 481 482 uint32_t tries = 0; 483 uint32_t test_idx = 0; 484 485 while (tries < num_children) { 486 obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); 487 if (!process_sp) 488 return lldb::ValueObjectSP(); 489 Status error; 490 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 491 if (error.Fail()) 492 return lldb::ValueObjectSP(); 493 494 test_idx++; 495 496 if (!obj_at_idx) 497 continue; 498 tries++; 499 500 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; 501 502 m_children.push_back(descriptor); 503 } 504 } 505 506 if (idx >= m_children.size()) // should never happen 507 return lldb::ValueObjectSP(); 508 509 SetItemDescriptor &set_item = m_children[idx]; 510 if (!set_item.valobj_sp) { 511 auto ptr_size = process_sp->GetAddressByteSize(); 512 DataBufferHeap buffer(ptr_size, 0); 513 switch (ptr_size) { 514 case 0: // architecture has no clue - fail 515 return lldb::ValueObjectSP(); 516 case 4: 517 *reinterpret_cast<uint32_t *>(buffer.GetBytes()) = 518 static_cast<uint32_t>(set_item.item_ptr); 519 break; 520 case 8: 521 *reinterpret_cast<uint64_t *>(buffer.GetBytes()) = 522 static_cast<uint64_t>(set_item.item_ptr); 523 break; 524 default: 525 lldbassert(false && "pointer size is not 4 nor 8"); 526 } 527 StreamString idx_name; 528 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 529 530 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), 531 process_sp->GetByteOrder(), 532 process_sp->GetAddressByteSize()); 533 534 set_item.valobj_sp = CreateValueObjectFromData( 535 idx_name.GetString(), data, m_exe_ctx_ref, 536 m_backend.GetCompilerType().GetBasicTypeFromAST( 537 lldb::eBasicTypeObjCID)); 538 } 539 return set_item.valobj_sp; 540 } 541 542 lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( 543 lldb::ValueObjectSP valobj_sp) 544 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(), 545 m_pair_type() {} 546 547 size_t 548 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( 549 ConstString name) { 550 const char *item_name = name.GetCString(); 551 const uint32_t idx = ExtractIndexFromString(item_name); 552 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 553 return UINT32_MAX; 554 return idx; 555 } 556 557 size_t 558 lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() { 559 if (!m_hashtable.IsValid()) 560 return 0; 561 return m_hashtable.GetCount(); 562 } 563 564 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() { 565 m_children.clear(); 566 ValueObjectSP valobj_sp = m_backend.GetSP(); 567 m_ptr_size = 0; 568 if (!valobj_sp) 569 return false; 570 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 571 572 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 573 if (!process_sp) 574 return false; 575 m_ptr_size = process_sp->GetAddressByteSize(); 576 m_order = process_sp->GetByteOrder(); 577 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref); 578 } 579 580 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() { 581 return true; 582 } 583 584 lldb::ValueObjectSP 585 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex( 586 size_t idx) { 587 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); 588 589 const uint32_t num_children = CalculateNumChildren(); 590 591 if (idx >= num_children) 592 return lldb::ValueObjectSP(); 593 594 if (m_children.empty()) { 595 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 596 if (!process_sp) 597 return lldb::ValueObjectSP(); 598 599 Status error; 600 lldb::addr_t val_at_idx = 0; 601 602 uint32_t tries = 0; 603 uint32_t test_idx = 0; 604 605 // Iterate over inferior memory, reading value pointers by shifting the 606 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read 607 // fails, otherwise, continue until the number of tries matches the number 608 // of childen. 609 while (tries < num_children) { 610 val_at_idx = m_values_ptr + (test_idx * m_ptr_size); 611 612 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); 613 if (error.Fail()) 614 return lldb::ValueObjectSP(); 615 616 test_idx++; 617 618 if (!val_at_idx) 619 continue; 620 tries++; 621 622 SetItemDescriptor descriptor = {val_at_idx, lldb::ValueObjectSP()}; 623 624 m_children.push_back(descriptor); 625 } 626 } 627 628 if (idx >= m_children.size()) // should never happen 629 return lldb::ValueObjectSP(); 630 631 SetItemDescriptor &set_item = m_children[idx]; 632 if (!set_item.valobj_sp) { 633 634 WritableDataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0)); 635 636 switch (m_ptr_size) { 637 case 0: // architecture has no clue - fail 638 return lldb::ValueObjectSP(); 639 case 4: 640 *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) = 641 static_cast<uint32_t>(set_item.item_ptr); 642 break; 643 case 8: 644 *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) = 645 static_cast<uint64_t>(set_item.item_ptr); 646 break; 647 default: 648 lldbassert(false && "pointer size is not 4 nor 8"); 649 } 650 StreamString idx_name; 651 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 652 653 DataExtractor data(buffer_sp, m_order, m_ptr_size); 654 655 set_item.valobj_sp = CreateValueObjectFromData( 656 idx_name.GetString(), data, m_exe_ctx_ref, 657 m_backend.GetCompilerType().GetBasicTypeFromAST( 658 lldb::eBasicTypeObjCID)); 659 } 660 661 return set_item.valobj_sp; 662 } 663 664 template <typename D32, typename D64> 665 lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< 666 D32, D64>::GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 667 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), 668 m_data_32(nullptr), m_data_64(nullptr) { 669 if (valobj_sp) 670 Update(); 671 } 672 673 template <typename D32, typename D64> 674 lldb_private::formatters:: 675 GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd<D32, D64>() { 676 delete m_data_32; 677 m_data_32 = nullptr; 678 delete m_data_64; 679 m_data_64 = nullptr; 680 } 681 682 template <typename D32, typename D64> 683 size_t 684 lldb_private::formatters:: 685 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName( 686 ConstString name) { 687 const char *item_name = name.GetCString(); 688 uint32_t idx = ExtractIndexFromString(item_name); 689 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 690 return UINT32_MAX; 691 return idx; 692 } 693 694 template <typename D32, typename D64> 695 size_t 696 lldb_private::formatters:: 697 GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() { 698 if (!m_data_32 && !m_data_64) 699 return 0; 700 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 701 } 702 703 template <typename D32, typename D64> 704 bool 705 lldb_private::formatters:: 706 GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() { 707 m_children.clear(); 708 ValueObjectSP valobj_sp = m_backend.GetSP(); 709 m_ptr_size = 0; 710 delete m_data_32; 711 m_data_32 = nullptr; 712 delete m_data_64; 713 m_data_64 = nullptr; 714 if (!valobj_sp) 715 return false; 716 if (!valobj_sp) 717 return false; 718 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 719 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 720 if (!process_sp) 721 return false; 722 m_ptr_size = process_sp->GetAddressByteSize(); 723 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; 724 Status error; 725 if (m_ptr_size == 4) { 726 m_data_32 = new D32(); 727 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), 728 error); 729 } else { 730 m_data_64 = new D64(); 731 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), 732 error); 733 } 734 return error.Success(); 735 } 736 737 template <typename D32, typename D64> 738 bool 739 lldb_private::formatters:: 740 GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() { 741 return true; 742 } 743 744 template <typename D32, typename D64> 745 lldb::ValueObjectSP 746 lldb_private::formatters:: 747 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) { 748 lldb::addr_t m_objs_addr = 749 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); 750 751 uint32_t num_children = CalculateNumChildren(); 752 753 if (idx >= num_children) 754 return lldb::ValueObjectSP(); 755 756 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 757 if (!process_sp) 758 return lldb::ValueObjectSP(); 759 760 if (m_children.empty()) { 761 // do the scan phase 762 lldb::addr_t obj_at_idx = 0; 763 764 uint32_t tries = 0; 765 uint32_t test_idx = 0; 766 767 while (tries < num_children) { 768 obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); 769 if (!process_sp) 770 return lldb::ValueObjectSP(); 771 Status error; 772 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 773 if (error.Fail()) 774 return lldb::ValueObjectSP(); 775 776 test_idx++; 777 778 if (!obj_at_idx) 779 continue; 780 tries++; 781 782 SetItemDescriptor descriptor = {obj_at_idx, 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 SetItemDescriptor &set_item = m_children[idx]; 792 if (!set_item.valobj_sp) { 793 auto ptr_size = process_sp->GetAddressByteSize(); 794 DataBufferHeap buffer(ptr_size, 0); 795 switch (ptr_size) { 796 case 0: // architecture has no clue?? - fail 797 return lldb::ValueObjectSP(); 798 case 4: 799 *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; 800 break; 801 case 8: 802 *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; 803 break; 804 default: 805 assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); 806 } 807 StreamString idx_name; 808 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 809 810 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), 811 process_sp->GetByteOrder(), 812 process_sp->GetAddressByteSize()); 813 814 set_item.valobj_sp = CreateValueObjectFromData( 815 idx_name.GetString(), data, m_exe_ctx_ref, 816 m_backend.GetCompilerType().GetBasicTypeFromAST( 817 lldb::eBasicTypeObjCID)); 818 } 819 return set_item.valobj_sp; 820 } 821 822 template bool lldb_private::formatters::NSSetSummaryProvider<true>( 823 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); 824 825 template bool lldb_private::formatters::NSSetSummaryProvider<false>( 826 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); 827