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