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