1 //===-- NSSet.cpp -----------------------------------------------*- C++ -*-===// 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 11 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" 12 #include "lldb/Core/ValueObject.h" 13 #include "lldb/Core/ValueObjectConstResult.h" 14 #include "lldb/DataFormatters/FormattersHelpers.h" 15 #include "lldb/Symbol/ClangASTContext.h" 16 #include "lldb/Target/Language.h" 17 #include "lldb/Target/ObjCLanguageRuntime.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 template <typename D32, typename D64> 84 class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 85 public: 86 GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 87 88 ~GenericNSSetMSyntheticFrontEnd() override; 89 90 size_t CalculateNumChildren() override; 91 92 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 93 94 bool Update() override; 95 96 bool MightHaveChildren() override; 97 98 size_t GetIndexOfChildWithName(ConstString name) override; 99 100 private: 101 102 struct SetItemDescriptor { 103 lldb::addr_t item_ptr; 104 lldb::ValueObjectSP valobj_sp; 105 }; 106 107 ExecutionContextRef m_exe_ctx_ref; 108 uint8_t m_ptr_size; 109 D32 *m_data_32; 110 D64 *m_data_64; 111 std::vector<SetItemDescriptor> m_children; 112 }; 113 114 namespace Foundation1300 { 115 struct DataDescriptor_32 { 116 uint32_t _used : 26; 117 uint32_t _size; 118 uint32_t _mutations; 119 uint32_t _objs_addr; 120 }; 121 122 struct DataDescriptor_64 { 123 uint64_t _used : 58; 124 uint64_t _size; 125 uint64_t _mutations; 126 uint64_t _objs_addr; 127 }; 128 129 using NSSetMSyntheticFrontEnd = 130 GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; 131 } 132 133 namespace Foundation1428 { 134 struct DataDescriptor_32 { 135 uint32_t _used : 26; 136 uint32_t _size; 137 uint32_t _objs_addr; 138 uint32_t _mutations; 139 }; 140 141 struct DataDescriptor_64 { 142 uint64_t _used : 58; 143 uint64_t _size; 144 uint64_t _objs_addr; 145 uint64_t _mutations; 146 }; 147 148 using NSSetMSyntheticFrontEnd = 149 GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; 150 } 151 152 namespace Foundation1437 { 153 struct DataDescriptor_32 { 154 uint32_t _cow; 155 // __table storage 156 uint32_t _objs_addr; 157 uint32_t _muts; 158 uint32_t _used : 26; 159 uint32_t _szidx : 6; 160 }; 161 162 struct DataDescriptor_64 { 163 uint64_t _cow; 164 // __Table storage 165 uint64_t _objs_addr; 166 uint32_t _muts; 167 uint32_t _used : 26; 168 uint32_t _szidx : 6; 169 }; 170 171 using NSSetMSyntheticFrontEnd = 172 GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; 173 174 template <typename DD> 175 uint64_t 176 __NSSetMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr, 177 Status &error) { 178 const lldb::addr_t start_of_descriptor = 179 valobj_addr + process.GetAddressByteSize(); 180 DD descriptor = DD(); 181 process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor), 182 error); 183 if (error.Fail()) { 184 return 0; 185 } 186 return descriptor._used; 187 } 188 189 uint64_t 190 __NSSetMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, 191 Status &error) { 192 if (process.GetAddressByteSize() == 4) { 193 return __NSSetMSize_Impl<DataDescriptor_32>(process, valobj_addr, error); 194 } else { 195 return __NSSetMSize_Impl<DataDescriptor_64>(process, valobj_addr, error); 196 } 197 } 198 } 199 200 class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 201 public: 202 NSSetCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 203 204 ~NSSetCodeRunningSyntheticFrontEnd() override; 205 206 size_t CalculateNumChildren() override; 207 208 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 209 210 bool Update() override; 211 212 bool MightHaveChildren() override; 213 214 size_t GetIndexOfChildWithName(ConstString name) override; 215 }; 216 } // namespace formatters 217 } // namespace lldb_private 218 219 template <bool cf_style> 220 bool lldb_private::formatters::NSSetSummaryProvider( 221 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 222 static ConstString g_TypeHint("NSSet"); 223 224 ProcessSP process_sp = valobj.GetProcessSP(); 225 if (!process_sp) 226 return false; 227 228 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 229 230 if (!runtime) 231 return false; 232 233 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 234 runtime->GetClassDescriptor(valobj)); 235 236 if (!descriptor || !descriptor->IsValid()) 237 return false; 238 239 uint32_t ptr_size = process_sp->GetAddressByteSize(); 240 bool is_64bit = (ptr_size == 8); 241 242 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 243 244 if (!valobj_addr) 245 return false; 246 247 uint64_t value = 0; 248 249 ConstString class_name_cs = descriptor->GetClassName(); 250 const char *class_name = class_name_cs.GetCString(); 251 252 if (!class_name || !*class_name) 253 return false; 254 255 if (!strcmp(class_name, "__NSSetI") || 256 !strcmp(class_name, "__NSOrderedSetI")) { 257 Status error; 258 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 259 ptr_size, 0, error); 260 if (error.Fail()) 261 return false; 262 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); 263 } else if (!strcmp(class_name, "__NSSetM")) { 264 AppleObjCRuntime *apple_runtime = 265 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); 266 Status error; 267 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) { 268 value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error); 269 } else { 270 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 271 ptr_size, 0, error); 272 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); 273 } 274 if (error.Fail()) 275 return false; 276 } else { 277 auto &map(NSSet_Additionals::GetAdditionalSummaries()); 278 auto iter = map.find(class_name_cs), end = map.end(); 279 if (iter != end) 280 return iter->second(valobj, stream, options); 281 else 282 return false; 283 } 284 285 std::string prefix, suffix; 286 if (Language *language = Language::FindPlugin(options.GetLanguage())) { 287 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 288 suffix)) { 289 prefix.clear(); 290 suffix.clear(); 291 } 292 } 293 294 stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element", 295 value == 1 ? "" : "s", suffix.c_str()); 296 return true; 297 } 298 299 SyntheticChildrenFrontEnd * 300 lldb_private::formatters::NSSetSyntheticFrontEndCreator( 301 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { 302 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 303 if (!process_sp) 304 return nullptr; 305 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 306 if (!runtime) 307 return nullptr; 308 309 CompilerType valobj_type(valobj_sp->GetCompilerType()); 310 Flags flags(valobj_type.GetTypeInfo()); 311 312 if (flags.IsClear(eTypeIsPointer)) { 313 Status error; 314 valobj_sp = valobj_sp->AddressOf(error); 315 if (error.Fail() || !valobj_sp) 316 return nullptr; 317 } 318 319 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 320 runtime->GetClassDescriptor(*valobj_sp)); 321 322 if (!descriptor || !descriptor->IsValid()) 323 return nullptr; 324 325 ConstString class_name_cs = descriptor->GetClassName(); 326 const char *class_name = class_name_cs.GetCString(); 327 328 if (!class_name || !*class_name) 329 return nullptr; 330 331 if (!strcmp(class_name, "__NSSetI") || 332 !strcmp(class_name, "__NSOrderedSetI")) { 333 return (new NSSetISyntheticFrontEnd(valobj_sp)); 334 } else if (!strcmp(class_name, "__NSSetM")) { 335 AppleObjCRuntime *apple_runtime = 336 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); 337 if (apple_runtime) { 338 if (apple_runtime->GetFoundationVersion() >= 1437) 339 return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp)); 340 else if (apple_runtime->GetFoundationVersion() >= 1428) 341 return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp)); 342 else 343 return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); 344 } else { 345 return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); 346 } 347 } else { 348 auto &map(NSSet_Additionals::GetAdditionalSynthetics()); 349 auto iter = map.find(class_name_cs), end = map.end(); 350 if (iter != end) 351 return iter->second(synth, valobj_sp); 352 return nullptr; 353 } 354 } 355 356 lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd( 357 lldb::ValueObjectSP valobj_sp) 358 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), 359 m_data_32(nullptr), m_data_64(nullptr) { 360 if (valobj_sp) 361 Update(); 362 } 363 364 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { 365 delete m_data_32; 366 m_data_32 = nullptr; 367 delete m_data_64; 368 m_data_64 = nullptr; 369 } 370 371 size_t 372 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( 373 ConstString name) { 374 const char *item_name = name.GetCString(); 375 uint32_t idx = ExtractIndexFromString(item_name); 376 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 377 return UINT32_MAX; 378 return idx; 379 } 380 381 size_t 382 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() { 383 if (!m_data_32 && !m_data_64) 384 return 0; 385 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 386 } 387 388 bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() { 389 m_children.clear(); 390 delete m_data_32; 391 m_data_32 = nullptr; 392 delete m_data_64; 393 m_data_64 = nullptr; 394 m_ptr_size = 0; 395 ValueObjectSP valobj_sp = m_backend.GetSP(); 396 if (!valobj_sp) 397 return false; 398 if (!valobj_sp) 399 return false; 400 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 401 Status error; 402 if (valobj_sp->IsPointerType()) { 403 valobj_sp = valobj_sp->Dereference(error); 404 if (error.Fail() || !valobj_sp) 405 return false; 406 } 407 error.Clear(); 408 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 409 if (!process_sp) 410 return false; 411 m_ptr_size = process_sp->GetAddressByteSize(); 412 uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size; 413 if (m_ptr_size == 4) { 414 m_data_32 = new DataDescriptor_32(); 415 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), 416 error); 417 } else { 418 m_data_64 = new DataDescriptor_64(); 419 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), 420 error); 421 } 422 if (error.Fail()) 423 return false; 424 m_data_ptr = data_location + m_ptr_size; 425 return false; 426 } 427 428 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() { 429 return true; 430 } 431 432 lldb::ValueObjectSP 433 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) { 434 uint32_t num_children = CalculateNumChildren(); 435 436 if (idx >= num_children) 437 return lldb::ValueObjectSP(); 438 439 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 440 if (!process_sp) 441 return lldb::ValueObjectSP(); 442 443 if (m_children.empty()) { 444 // do the scan phase 445 lldb::addr_t obj_at_idx = 0; 446 447 uint32_t tries = 0; 448 uint32_t test_idx = 0; 449 450 while (tries < num_children) { 451 obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); 452 if (!process_sp) 453 return lldb::ValueObjectSP(); 454 Status error; 455 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 456 if (error.Fail()) 457 return lldb::ValueObjectSP(); 458 459 test_idx++; 460 461 if (!obj_at_idx) 462 continue; 463 tries++; 464 465 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; 466 467 m_children.push_back(descriptor); 468 } 469 } 470 471 if (idx >= m_children.size()) // should never happen 472 return lldb::ValueObjectSP(); 473 474 SetItemDescriptor &set_item = m_children[idx]; 475 if (!set_item.valobj_sp) { 476 auto ptr_size = process_sp->GetAddressByteSize(); 477 DataBufferHeap buffer(ptr_size, 0); 478 switch (ptr_size) { 479 case 0: // architecture has no clue?? - fail 480 return lldb::ValueObjectSP(); 481 case 4: 482 *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; 483 break; 484 case 8: 485 *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; 486 break; 487 default: 488 assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); 489 } 490 StreamString idx_name; 491 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 492 493 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), 494 process_sp->GetByteOrder(), 495 process_sp->GetAddressByteSize()); 496 497 set_item.valobj_sp = CreateValueObjectFromData( 498 idx_name.GetString(), data, m_exe_ctx_ref, 499 m_backend.GetCompilerType().GetBasicTypeFromAST( 500 lldb::eBasicTypeObjCID)); 501 } 502 return set_item.valobj_sp; 503 } 504 505 template <typename D32, typename D64> 506 lldb_private::formatters:: 507 GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd( 508 lldb::ValueObjectSP valobj_sp) 509 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), 510 m_data_32(nullptr), m_data_64(nullptr) { 511 if (valobj_sp) 512 Update(); 513 } 514 515 template <typename D32, typename D64> 516 lldb_private::formatters:: 517 GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd() { 518 delete m_data_32; 519 m_data_32 = nullptr; 520 delete m_data_64; 521 m_data_64 = nullptr; 522 } 523 524 template <typename D32, typename D64> 525 size_t 526 lldb_private::formatters:: 527 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName( 528 ConstString name) { 529 const char *item_name = name.GetCString(); 530 uint32_t idx = ExtractIndexFromString(item_name); 531 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 532 return UINT32_MAX; 533 return idx; 534 } 535 536 template <typename D32, typename D64> 537 size_t 538 lldb_private::formatters:: 539 GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() { 540 if (!m_data_32 && !m_data_64) 541 return 0; 542 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 543 } 544 545 template <typename D32, typename D64> 546 bool 547 lldb_private::formatters:: 548 GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() { 549 m_children.clear(); 550 ValueObjectSP valobj_sp = m_backend.GetSP(); 551 m_ptr_size = 0; 552 delete m_data_32; 553 m_data_32 = nullptr; 554 delete m_data_64; 555 m_data_64 = nullptr; 556 if (!valobj_sp) 557 return false; 558 if (!valobj_sp) 559 return false; 560 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 561 Status error; 562 if (valobj_sp->IsPointerType()) { 563 valobj_sp = valobj_sp->Dereference(error); 564 if (error.Fail() || !valobj_sp) 565 return false; 566 } 567 error.Clear(); 568 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 569 if (!process_sp) 570 return false; 571 m_ptr_size = process_sp->GetAddressByteSize(); 572 uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size; 573 if (m_ptr_size == 4) { 574 m_data_32 = new D32(); 575 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), 576 error); 577 } else { 578 m_data_64 = new D64(); 579 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), 580 error); 581 } 582 if (error.Fail()) 583 return false; 584 return false; 585 } 586 587 template <typename D32, typename D64> 588 bool 589 lldb_private::formatters:: 590 GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() { 591 return true; 592 } 593 594 template <typename D32, typename D64> 595 lldb::ValueObjectSP 596 lldb_private::formatters:: 597 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) { 598 lldb::addr_t m_objs_addr = 599 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); 600 601 uint32_t num_children = CalculateNumChildren(); 602 603 if (idx >= num_children) 604 return lldb::ValueObjectSP(); 605 606 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 607 if (!process_sp) 608 return lldb::ValueObjectSP(); 609 610 if (m_children.empty()) { 611 // do the scan phase 612 lldb::addr_t obj_at_idx = 0; 613 614 uint32_t tries = 0; 615 uint32_t test_idx = 0; 616 617 while (tries < num_children) { 618 obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); 619 if (!process_sp) 620 return lldb::ValueObjectSP(); 621 Status error; 622 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 623 if (error.Fail()) 624 return lldb::ValueObjectSP(); 625 626 test_idx++; 627 628 if (!obj_at_idx) 629 continue; 630 tries++; 631 632 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; 633 634 m_children.push_back(descriptor); 635 } 636 } 637 638 if (idx >= m_children.size()) // should never happen 639 return lldb::ValueObjectSP(); 640 641 SetItemDescriptor &set_item = m_children[idx]; 642 if (!set_item.valobj_sp) { 643 auto ptr_size = process_sp->GetAddressByteSize(); 644 DataBufferHeap buffer(ptr_size, 0); 645 switch (ptr_size) { 646 case 0: // architecture has no clue?? - fail 647 return lldb::ValueObjectSP(); 648 case 4: 649 *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; 650 break; 651 case 8: 652 *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; 653 break; 654 default: 655 assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); 656 } 657 StreamString idx_name; 658 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 659 660 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), 661 process_sp->GetByteOrder(), 662 process_sp->GetAddressByteSize()); 663 664 set_item.valobj_sp = CreateValueObjectFromData( 665 idx_name.GetString(), data, m_exe_ctx_ref, 666 m_backend.GetCompilerType().GetBasicTypeFromAST( 667 lldb::eBasicTypeObjCID)); 668 } 669 return set_item.valobj_sp; 670 } 671 672 template bool lldb_private::formatters::NSSetSummaryProvider<true>( 673 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); 674 675 template bool lldb_private::formatters::NSSetSummaryProvider<false>( 676 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); 677