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