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