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 =
229       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
230           lldb::eLanguageTypeObjC);
231 
232   if (!runtime)
233     return false;
234 
235   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
236       runtime->GetClassDescriptor(valobj));
237 
238   if (!descriptor || !descriptor->IsValid())
239     return false;
240 
241   uint32_t ptr_size = process_sp->GetAddressByteSize();
242   bool is_64bit = (ptr_size == 8);
243 
244   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
245 
246   if (!valobj_addr)
247     return false;
248 
249   uint64_t value = 0;
250 
251   ConstString class_name_cs = descriptor->GetClassName();
252   const char *class_name = class_name_cs.GetCString();
253 
254   if (!class_name || !*class_name)
255     return false;
256 
257   if (!strcmp(class_name, "__NSSetI") ||
258       !strcmp(class_name, "__NSOrderedSetI")) {
259     Status error;
260     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
261                                                       ptr_size, 0, error);
262     if (error.Fail())
263       return false;
264     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
265   } else if (!strcmp(class_name, "__NSSetM")) {
266     AppleObjCRuntime *apple_runtime =
267         llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
268     Status error;
269     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
270       value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error);
271     } else {
272       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
273                                                         ptr_size, 0, error);
274       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
275     }
276     if (error.Fail())
277       return false;
278   } else {
279     auto &map(NSSet_Additionals::GetAdditionalSummaries());
280     auto iter = map.find(class_name_cs), end = map.end();
281     if (iter != end)
282       return iter->second(valobj, stream, options);
283     else
284       return false;
285   }
286 
287   std::string prefix, suffix;
288   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
289     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
290                                             suffix)) {
291       prefix.clear();
292       suffix.clear();
293     }
294   }
295 
296   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
297                 value == 1 ? "" : "s", suffix.c_str());
298   return true;
299 }
300 
301 SyntheticChildrenFrontEnd *
302 lldb_private::formatters::NSSetSyntheticFrontEndCreator(
303     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
304   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
305   if (!process_sp)
306     return nullptr;
307   ObjCLanguageRuntime *runtime =
308       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
309           lldb::eLanguageTypeObjC);
310   if (!runtime)
311     return nullptr;
312 
313   CompilerType valobj_type(valobj_sp->GetCompilerType());
314   Flags flags(valobj_type.GetTypeInfo());
315 
316   if (flags.IsClear(eTypeIsPointer)) {
317     Status error;
318     valobj_sp = valobj_sp->AddressOf(error);
319     if (error.Fail() || !valobj_sp)
320       return nullptr;
321   }
322 
323   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
324       runtime->GetClassDescriptor(*valobj_sp));
325 
326   if (!descriptor || !descriptor->IsValid())
327     return nullptr;
328 
329   ConstString class_name_cs = descriptor->GetClassName();
330   const char *class_name = class_name_cs.GetCString();
331 
332   if (!class_name || !*class_name)
333     return nullptr;
334 
335   if (!strcmp(class_name, "__NSSetI") ||
336       !strcmp(class_name, "__NSOrderedSetI")) {
337     return (new NSSetISyntheticFrontEnd(valobj_sp));
338   } else if (!strcmp(class_name, "__NSSetM")) {
339     AppleObjCRuntime *apple_runtime =
340         llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
341     if (apple_runtime) {
342       if (apple_runtime->GetFoundationVersion() >= 1437)
343         return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp));
344       else if (apple_runtime->GetFoundationVersion() >= 1428)
345         return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp));
346       else
347         return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
348     } else {
349       return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
350     }
351   } else {
352     auto &map(NSSet_Additionals::GetAdditionalSynthetics());
353     auto iter = map.find(class_name_cs), end = map.end();
354     if (iter != end)
355       return iter->second(synth, valobj_sp);
356     return nullptr;
357   }
358 }
359 
360 lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd(
361     lldb::ValueObjectSP valobj_sp)
362     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
363       m_data_32(nullptr), m_data_64(nullptr) {
364   if (valobj_sp)
365     Update();
366 }
367 
368 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() {
369   delete m_data_32;
370   m_data_32 = nullptr;
371   delete m_data_64;
372   m_data_64 = nullptr;
373 }
374 
375 size_t
376 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName(
377     ConstString name) {
378   const char *item_name = name.GetCString();
379   uint32_t idx = ExtractIndexFromString(item_name);
380   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
381     return UINT32_MAX;
382   return idx;
383 }
384 
385 size_t
386 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() {
387   if (!m_data_32 && !m_data_64)
388     return 0;
389   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
390 }
391 
392 bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() {
393   m_children.clear();
394   delete m_data_32;
395   m_data_32 = nullptr;
396   delete m_data_64;
397   m_data_64 = nullptr;
398   m_ptr_size = 0;
399   ValueObjectSP valobj_sp = m_backend.GetSP();
400   if (!valobj_sp)
401     return false;
402   if (!valobj_sp)
403     return false;
404   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
405   Status error;
406   if (valobj_sp->IsPointerType()) {
407     valobj_sp = valobj_sp->Dereference(error);
408     if (error.Fail() || !valobj_sp)
409       return false;
410   }
411   error.Clear();
412   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
413   if (!process_sp)
414     return false;
415   m_ptr_size = process_sp->GetAddressByteSize();
416   uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
417   if (m_ptr_size == 4) {
418     m_data_32 = new DataDescriptor_32();
419     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
420                            error);
421   } else {
422     m_data_64 = new DataDescriptor_64();
423     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
424                            error);
425   }
426   if (error.Fail())
427     return false;
428   m_data_ptr = data_location + m_ptr_size;
429   return false;
430 }
431 
432 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
433   return true;
434 }
435 
436 lldb::ValueObjectSP
437 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) {
438   uint32_t num_children = CalculateNumChildren();
439 
440   if (idx >= num_children)
441     return lldb::ValueObjectSP();
442 
443   ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
444   if (!process_sp)
445     return lldb::ValueObjectSP();
446 
447   if (m_children.empty()) {
448     // do the scan phase
449     lldb::addr_t obj_at_idx = 0;
450 
451     uint32_t tries = 0;
452     uint32_t test_idx = 0;
453 
454     while (tries < num_children) {
455       obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
456       if (!process_sp)
457         return lldb::ValueObjectSP();
458       Status error;
459       obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
460       if (error.Fail())
461         return lldb::ValueObjectSP();
462 
463       test_idx++;
464 
465       if (!obj_at_idx)
466         continue;
467       tries++;
468 
469       SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
470 
471       m_children.push_back(descriptor);
472     }
473   }
474 
475   if (idx >= m_children.size()) // should never happen
476     return lldb::ValueObjectSP();
477 
478   SetItemDescriptor &set_item = m_children[idx];
479   if (!set_item.valobj_sp) {
480     auto ptr_size = process_sp->GetAddressByteSize();
481     DataBufferHeap buffer(ptr_size, 0);
482     switch (ptr_size) {
483     case 0: // architecture has no clue?? - fail
484       return lldb::ValueObjectSP();
485     case 4:
486       *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
487       break;
488     case 8:
489       *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
490       break;
491     default:
492       assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
493     }
494     StreamString idx_name;
495     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
496 
497     DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
498                        process_sp->GetByteOrder(),
499                        process_sp->GetAddressByteSize());
500 
501     set_item.valobj_sp = CreateValueObjectFromData(
502         idx_name.GetString(), data, m_exe_ctx_ref,
503         m_backend.GetCompilerType().GetBasicTypeFromAST(
504             lldb::eBasicTypeObjCID));
505   }
506   return set_item.valobj_sp;
507 }
508 
509 template <typename D32, typename D64>
510 lldb_private::formatters::
511   GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd(
512     lldb::ValueObjectSP valobj_sp)
513     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
514       m_data_32(nullptr), m_data_64(nullptr) {
515   if (valobj_sp)
516     Update();
517 }
518 
519 template <typename D32, typename D64>
520 lldb_private::formatters::
521   GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd() {
522   delete m_data_32;
523   m_data_32 = nullptr;
524   delete m_data_64;
525   m_data_64 = nullptr;
526 }
527 
528 template <typename D32, typename D64>
529 size_t
530 lldb_private::formatters::
531   GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName(
532     ConstString name) {
533   const char *item_name = name.GetCString();
534   uint32_t idx = ExtractIndexFromString(item_name);
535   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
536     return UINT32_MAX;
537   return idx;
538 }
539 
540 template <typename D32, typename D64>
541 size_t
542 lldb_private::formatters::
543   GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() {
544   if (!m_data_32 && !m_data_64)
545     return 0;
546   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
547 }
548 
549 template <typename D32, typename D64>
550 bool
551 lldb_private::formatters::
552   GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() {
553   m_children.clear();
554   ValueObjectSP valobj_sp = m_backend.GetSP();
555   m_ptr_size = 0;
556   delete m_data_32;
557   m_data_32 = nullptr;
558   delete m_data_64;
559   m_data_64 = nullptr;
560   if (!valobj_sp)
561     return false;
562   if (!valobj_sp)
563     return false;
564   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
565   Status error;
566   if (valobj_sp->IsPointerType()) {
567     valobj_sp = valobj_sp->Dereference(error);
568     if (error.Fail() || !valobj_sp)
569       return false;
570   }
571   error.Clear();
572   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
573   if (!process_sp)
574     return false;
575   m_ptr_size = process_sp->GetAddressByteSize();
576   uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
577   if (m_ptr_size == 4) {
578     m_data_32 = new D32();
579     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
580                            error);
581   } else {
582     m_data_64 = new D64();
583     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
584                            error);
585   }
586   if (error.Fail())
587     return false;
588   return false;
589 }
590 
591 template <typename D32, typename D64>
592 bool
593 lldb_private::formatters::
594   GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
595   return true;
596 }
597 
598 template <typename D32, typename D64>
599 lldb::ValueObjectSP
600 lldb_private::formatters::
601   GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) {
602   lldb::addr_t m_objs_addr =
603       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
604 
605   uint32_t num_children = CalculateNumChildren();
606 
607   if (idx >= num_children)
608     return lldb::ValueObjectSP();
609 
610   ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
611   if (!process_sp)
612     return lldb::ValueObjectSP();
613 
614   if (m_children.empty()) {
615     // do the scan phase
616     lldb::addr_t obj_at_idx = 0;
617 
618     uint32_t tries = 0;
619     uint32_t test_idx = 0;
620 
621     while (tries < num_children) {
622       obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
623       if (!process_sp)
624         return lldb::ValueObjectSP();
625       Status error;
626       obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
627       if (error.Fail())
628         return lldb::ValueObjectSP();
629 
630       test_idx++;
631 
632       if (!obj_at_idx)
633         continue;
634       tries++;
635 
636       SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
637 
638       m_children.push_back(descriptor);
639     }
640   }
641 
642   if (idx >= m_children.size()) // should never happen
643     return lldb::ValueObjectSP();
644 
645   SetItemDescriptor &set_item = m_children[idx];
646   if (!set_item.valobj_sp) {
647     auto ptr_size = process_sp->GetAddressByteSize();
648     DataBufferHeap buffer(ptr_size, 0);
649     switch (ptr_size) {
650     case 0: // architecture has no clue?? - fail
651       return lldb::ValueObjectSP();
652     case 4:
653       *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
654       break;
655     case 8:
656       *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
657       break;
658     default:
659       assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
660     }
661     StreamString idx_name;
662     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
663 
664     DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
665                        process_sp->GetByteOrder(),
666                        process_sp->GetAddressByteSize());
667 
668     set_item.valobj_sp = CreateValueObjectFromData(
669         idx_name.GetString(), data, m_exe_ctx_ref,
670         m_backend.GetCompilerType().GetBasicTypeFromAST(
671             lldb::eBasicTypeObjCID));
672   }
673   return set_item.valobj_sp;
674 }
675 
676 template bool lldb_private::formatters::NSSetSummaryProvider<true>(
677     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
678 
679 template bool lldb_private::formatters::NSSetSummaryProvider<false>(
680     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
681