1 //===-- NSSet.cpp ---------------------------------------------------------===//
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 #include "CFBasicHash.h"
11 
12 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
13 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
14 #include "lldb/Core/ValueObject.h"
15 #include "lldb/Core/ValueObjectConstResult.h"
16 #include "lldb/DataFormatters/FormattersHelpers.h"
17 #include "lldb/Target/Language.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 class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
84 public:
85   NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
86 
87   size_t CalculateNumChildren() override;
88 
89   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
90 
91   bool Update() override;
92 
93   bool MightHaveChildren() override;
94 
95   size_t GetIndexOfChildWithName(ConstString name) override;
96 
97 private:
98   struct SetItemDescriptor {
99     lldb::addr_t item_ptr;
100     lldb::ValueObjectSP valobj_sp;
101   };
102 
103   ExecutionContextRef m_exe_ctx_ref;
104   uint8_t m_ptr_size;
105   lldb::ByteOrder m_order;
106 
107   CFBasicHash m_hashtable;
108 
109   CompilerType m_pair_type;
110   std::vector<SetItemDescriptor> m_children;
111 };
112 
113 template <typename D32, typename D64>
114 class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
115 public:
116   GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
117 
118   ~GenericNSSetMSyntheticFrontEnd() override;
119 
120   size_t CalculateNumChildren() override;
121 
122   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
123 
124   bool Update() override;
125 
126   bool MightHaveChildren() override;
127 
128   size_t GetIndexOfChildWithName(ConstString name) override;
129 
130 private:
131 
132   struct SetItemDescriptor {
133     lldb::addr_t item_ptr;
134     lldb::ValueObjectSP valobj_sp;
135   };
136 
137   ExecutionContextRef m_exe_ctx_ref;
138   uint8_t m_ptr_size;
139   D32 *m_data_32;
140   D64 *m_data_64;
141   std::vector<SetItemDescriptor> m_children;
142 };
143 
144 namespace Foundation1300 {
145   struct DataDescriptor_32 {
146     uint32_t _used : 26;
147     uint32_t _size;
148     uint32_t _mutations;
149     uint32_t _objs_addr;
150   };
151 
152   struct DataDescriptor_64 {
153     uint64_t _used : 58;
154     uint64_t _size;
155     uint64_t _mutations;
156     uint64_t _objs_addr;
157   };
158 
159   using NSSetMSyntheticFrontEnd =
160       GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
161 }
162 
163 namespace Foundation1428 {
164   struct DataDescriptor_32 {
165     uint32_t _used : 26;
166     uint32_t _size;
167     uint32_t _objs_addr;
168     uint32_t _mutations;
169   };
170 
171   struct DataDescriptor_64 {
172     uint64_t _used : 58;
173     uint64_t _size;
174     uint64_t _objs_addr;
175     uint64_t _mutations;
176   };
177 
178   using NSSetMSyntheticFrontEnd =
179       GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
180 }
181 
182 namespace Foundation1437 {
183   struct DataDescriptor_32 {
184     uint32_t _cow;
185     // __table storage
186     uint32_t _objs_addr;
187     uint32_t _muts;
188     uint32_t _used : 26;
189     uint32_t _szidx : 6;
190   };
191 
192   struct DataDescriptor_64 {
193     uint64_t _cow;
194     // __Table storage
195     uint64_t _objs_addr;
196     uint32_t _muts;
197     uint32_t _used : 26;
198     uint32_t _szidx : 6;
199   };
200 
201   using NSSetMSyntheticFrontEnd =
202       GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
203 
204   template <typename DD>
205   uint64_t
206   __NSSetMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr,
207                     Status &error) {
208     const lldb::addr_t start_of_descriptor =
209         valobj_addr + process.GetAddressByteSize();
210     DD descriptor = DD();
211     process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
212                        error);
213     if (error.Fail()) {
214       return 0;
215     }
216     return descriptor._used;
217   }
218 
219   uint64_t
220   __NSSetMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
221                Status &error) {
222     if (process.GetAddressByteSize() == 4) {
223       return __NSSetMSize_Impl<DataDescriptor_32>(process, valobj_addr, error);
224     } else {
225       return __NSSetMSize_Impl<DataDescriptor_64>(process, valobj_addr, error);
226     }
227   }
228 }
229 
230 class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
231 public:
232   NSSetCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
233 
234   ~NSSetCodeRunningSyntheticFrontEnd() override;
235 
236   size_t CalculateNumChildren() override;
237 
238   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
239 
240   bool Update() override;
241 
242   bool MightHaveChildren() override;
243 
244   size_t GetIndexOfChildWithName(ConstString name) override;
245 };
246 } // namespace formatters
247 } // namespace lldb_private
248 
249 template <bool cf_style>
250 bool lldb_private::formatters::NSSetSummaryProvider(
251     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
252   static ConstString g_TypeHint("NSSet");
253 
254   ProcessSP process_sp = valobj.GetProcessSP();
255   if (!process_sp)
256     return false;
257 
258   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
259 
260   if (!runtime)
261     return false;
262 
263   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
264       runtime->GetClassDescriptor(valobj));
265 
266   if (!descriptor || !descriptor->IsValid())
267     return false;
268 
269   uint32_t ptr_size = process_sp->GetAddressByteSize();
270   bool is_64bit = (ptr_size == 8);
271 
272   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
273 
274   if (!valobj_addr)
275     return false;
276 
277   uint64_t value = 0;
278 
279   ConstString class_name(descriptor->GetClassName());
280 
281   static const ConstString g_SetI("__NSSetI");
282   static const ConstString g_OrderedSetI("__NSOrderedSetI");
283   static const ConstString g_SetM("__NSSetM");
284   static const ConstString g_SetCF("__NSCFSet");
285 
286   if (class_name.IsEmpty())
287     return false;
288 
289   if (class_name == g_SetI || class_name == g_OrderedSetI) {
290     Status error;
291     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
292                                                       ptr_size, 0, error);
293     if (error.Fail())
294       return false;
295     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
296   } else if (class_name == g_SetM) {
297     AppleObjCRuntime *apple_runtime =
298         llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
299     Status error;
300     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
301       value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error);
302     } else {
303       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
304                                                         ptr_size, 0, error);
305       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
306     }
307     if (error.Fail())
308       return false;
309   } else if (class_name == g_SetCF) {
310     ExecutionContext exe_ctx(process_sp);
311     CFBasicHash cfbh;
312     if (!cfbh.Update(valobj_addr, exe_ctx))
313       return false;
314     value = cfbh.GetCount();
315   } else {
316     auto &map(NSSet_Additionals::GetAdditionalSummaries());
317     auto iter = map.find(class_name), end = map.end();
318     if (iter != end)
319       return iter->second(valobj, stream, options);
320     else
321       return false;
322   }
323 
324   std::string prefix, suffix;
325   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
326     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
327                                             suffix)) {
328       prefix.clear();
329       suffix.clear();
330     }
331   }
332 
333   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
334                 value == 1 ? "" : "s", suffix.c_str());
335   return true;
336 }
337 
338 SyntheticChildrenFrontEnd *
339 lldb_private::formatters::NSSetSyntheticFrontEndCreator(
340     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
341   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
342   if (!process_sp)
343     return nullptr;
344   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
345   if (!runtime)
346     return nullptr;
347 
348   CompilerType valobj_type(valobj_sp->GetCompilerType());
349   Flags flags(valobj_type.GetTypeInfo());
350 
351   if (flags.IsClear(eTypeIsPointer)) {
352     Status error;
353     valobj_sp = valobj_sp->AddressOf(error);
354     if (error.Fail() || !valobj_sp)
355       return nullptr;
356   }
357 
358   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
359       runtime->GetClassDescriptor(*valobj_sp));
360 
361   if (!descriptor || !descriptor->IsValid())
362     return nullptr;
363 
364   ConstString class_name = descriptor->GetClassName();
365 
366   static const ConstString g_SetI("__NSSetI");
367   static const ConstString g_OrderedSetI("__NSOrderedSetI");
368   static const ConstString g_SetM("__NSSetM");
369   static const ConstString g_SetCF("__NSCFSet");
370 
371   if (class_name.IsEmpty())
372     return nullptr;
373 
374   if (class_name == g_SetI || class_name == g_OrderedSetI) {
375     return (new NSSetISyntheticFrontEnd(valobj_sp));
376   } else if (class_name == g_SetM) {
377     AppleObjCRuntime *apple_runtime =
378         llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
379     if (apple_runtime) {
380       if (apple_runtime->GetFoundationVersion() >= 1437)
381         return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp));
382       else if (apple_runtime->GetFoundationVersion() >= 1428)
383         return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp));
384       else
385         return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
386     } else {
387       return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
388     }
389   } else if (class_name == g_SetCF) {
390     return (new NSCFSetSyntheticFrontEnd(valobj_sp));
391   } else {
392     auto &map(NSSet_Additionals::GetAdditionalSynthetics());
393     auto iter = map.find(class_name), end = map.end();
394     if (iter != end)
395       return iter->second(synth, valobj_sp);
396     return nullptr;
397   }
398 }
399 
400 lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd(
401     lldb::ValueObjectSP valobj_sp)
402     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
403       m_data_32(nullptr), m_data_64(nullptr) {
404   if (valobj_sp)
405     Update();
406 }
407 
408 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() {
409   delete m_data_32;
410   m_data_32 = nullptr;
411   delete m_data_64;
412   m_data_64 = nullptr;
413 }
414 
415 size_t
416 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName(
417     ConstString name) {
418   const char *item_name = name.GetCString();
419   uint32_t idx = ExtractIndexFromString(item_name);
420   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
421     return UINT32_MAX;
422   return idx;
423 }
424 
425 size_t
426 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() {
427   if (!m_data_32 && !m_data_64)
428     return 0;
429   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
430 }
431 
432 bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() {
433   m_children.clear();
434   delete m_data_32;
435   m_data_32 = nullptr;
436   delete m_data_64;
437   m_data_64 = nullptr;
438   m_ptr_size = 0;
439   ValueObjectSP valobj_sp = m_backend.GetSP();
440   if (!valobj_sp)
441     return false;
442   if (!valobj_sp)
443     return false;
444   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
445   Status error;
446   if (valobj_sp->IsPointerType()) {
447     valobj_sp = valobj_sp->Dereference(error);
448     if (error.Fail() || !valobj_sp)
449       return false;
450   }
451   error.Clear();
452   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
453   if (!process_sp)
454     return false;
455   m_ptr_size = process_sp->GetAddressByteSize();
456   uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
457   if (m_ptr_size == 4) {
458     m_data_32 = new DataDescriptor_32();
459     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
460                            error);
461   } else {
462     m_data_64 = new DataDescriptor_64();
463     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
464                            error);
465   }
466   if (error.Fail())
467     return false;
468   m_data_ptr = data_location + m_ptr_size;
469   return false;
470 }
471 
472 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
473   return true;
474 }
475 
476 lldb::ValueObjectSP
477 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) {
478   uint32_t num_children = CalculateNumChildren();
479 
480   if (idx >= num_children)
481     return lldb::ValueObjectSP();
482 
483   ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
484   if (!process_sp)
485     return lldb::ValueObjectSP();
486 
487   if (m_children.empty()) {
488     // do the scan phase
489     lldb::addr_t obj_at_idx = 0;
490 
491     uint32_t tries = 0;
492     uint32_t test_idx = 0;
493 
494     while (tries < num_children) {
495       obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
496       if (!process_sp)
497         return lldb::ValueObjectSP();
498       Status error;
499       obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
500       if (error.Fail())
501         return lldb::ValueObjectSP();
502 
503       test_idx++;
504 
505       if (!obj_at_idx)
506         continue;
507       tries++;
508 
509       SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
510 
511       m_children.push_back(descriptor);
512     }
513   }
514 
515   if (idx >= m_children.size()) // should never happen
516     return lldb::ValueObjectSP();
517 
518   SetItemDescriptor &set_item = m_children[idx];
519   if (!set_item.valobj_sp) {
520     auto ptr_size = process_sp->GetAddressByteSize();
521     DataBufferHeap buffer(ptr_size, 0);
522     switch (ptr_size) {
523     case 0: // architecture has no clue - fail
524       return lldb::ValueObjectSP();
525     case 4:
526       *reinterpret_cast<uint32_t *>(buffer.GetBytes()) =
527           static_cast<uint32_t>(set_item.item_ptr);
528       break;
529     case 8:
530       *reinterpret_cast<uint64_t *>(buffer.GetBytes()) =
531           static_cast<uint64_t>(set_item.item_ptr);
532       break;
533     default:
534       lldbassert(false && "pointer size is not 4 nor 8");
535     }
536     StreamString idx_name;
537     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
538 
539     DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
540                        process_sp->GetByteOrder(),
541                        process_sp->GetAddressByteSize());
542 
543     set_item.valobj_sp = CreateValueObjectFromData(
544         idx_name.GetString(), data, m_exe_ctx_ref,
545         m_backend.GetCompilerType().GetBasicTypeFromAST(
546             lldb::eBasicTypeObjCID));
547   }
548   return set_item.valobj_sp;
549 }
550 
551 lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd(
552     lldb::ValueObjectSP valobj_sp)
553     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
554       m_order(lldb::eByteOrderInvalid), m_hashtable(), m_pair_type() {}
555 
556 size_t
557 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName(
558     ConstString name) {
559   const char *item_name = name.GetCString();
560   const uint32_t idx = ExtractIndexFromString(item_name);
561   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
562     return UINT32_MAX;
563   return idx;
564 }
565 
566 size_t
567 lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() {
568   if (!m_hashtable.IsValid())
569     return 0;
570   return m_hashtable.GetCount();
571 }
572 
573 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() {
574   m_children.clear();
575   ValueObjectSP valobj_sp = m_backend.GetSP();
576   m_ptr_size = 0;
577   if (!valobj_sp)
578     return false;
579   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
580 
581   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
582   if (!process_sp)
583     return false;
584   m_ptr_size = process_sp->GetAddressByteSize();
585   m_order = process_sp->GetByteOrder();
586   return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref);
587 }
588 
589 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() {
590   return true;
591 }
592 
593 lldb::ValueObjectSP
594 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex(
595     size_t idx) {
596   lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
597 
598   const uint32_t num_children = CalculateNumChildren();
599 
600   if (idx >= num_children)
601     return lldb::ValueObjectSP();
602 
603   if (m_children.empty()) {
604     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
605     if (!process_sp)
606       return lldb::ValueObjectSP();
607 
608     Status error;
609     lldb::addr_t val_at_idx = 0;
610 
611     uint32_t tries = 0;
612     uint32_t test_idx = 0;
613 
614     // Iterate over inferior memory, reading value pointers by shifting the
615     // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
616     // fails, otherwise, continue until the number of tries matches the number
617     // of childen.
618     while (tries < num_children) {
619       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
620 
621       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
622       if (error.Fail())
623         return lldb::ValueObjectSP();
624 
625       test_idx++;
626 
627       if (!val_at_idx)
628         continue;
629       tries++;
630 
631       SetItemDescriptor descriptor = {val_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 
643     DataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0));
644 
645     switch (m_ptr_size) {
646     case 0: // architecture has no clue - fail
647       return lldb::ValueObjectSP();
648     case 4:
649       *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) =
650           static_cast<uint32_t>(set_item.item_ptr);
651       break;
652     case 8:
653       *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) =
654           static_cast<uint64_t>(set_item.item_ptr);
655       break;
656     default:
657       lldbassert(false && "pointer size is not 4 nor 8");
658     }
659     StreamString idx_name;
660     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
661 
662     DataExtractor data(buffer_sp, m_order, m_ptr_size);
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 
670   return set_item.valobj_sp;
671 }
672 
673 template <typename D32, typename D64>
674 lldb_private::formatters::
675   GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd(
676     lldb::ValueObjectSP valobj_sp)
677     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
678       m_data_32(nullptr), m_data_64(nullptr) {
679   if (valobj_sp)
680     Update();
681 }
682 
683 template <typename D32, typename D64>
684 lldb_private::formatters::
685   GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd<D32, D64>() {
686   delete m_data_32;
687   m_data_32 = nullptr;
688   delete m_data_64;
689   m_data_64 = nullptr;
690 }
691 
692 template <typename D32, typename D64>
693 size_t
694 lldb_private::formatters::
695   GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName(
696     ConstString name) {
697   const char *item_name = name.GetCString();
698   uint32_t idx = ExtractIndexFromString(item_name);
699   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
700     return UINT32_MAX;
701   return idx;
702 }
703 
704 template <typename D32, typename D64>
705 size_t
706 lldb_private::formatters::
707   GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() {
708   if (!m_data_32 && !m_data_64)
709     return 0;
710   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
711 }
712 
713 template <typename D32, typename D64>
714 bool
715 lldb_private::formatters::
716   GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() {
717   m_children.clear();
718   ValueObjectSP valobj_sp = m_backend.GetSP();
719   m_ptr_size = 0;
720   delete m_data_32;
721   m_data_32 = nullptr;
722   delete m_data_64;
723   m_data_64 = nullptr;
724   if (!valobj_sp)
725     return false;
726   if (!valobj_sp)
727     return false;
728   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
729   Status error;
730   if (valobj_sp->IsPointerType()) {
731     valobj_sp = valobj_sp->Dereference(error);
732     if (error.Fail() || !valobj_sp)
733       return false;
734   }
735   error.Clear();
736   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
737   if (!process_sp)
738     return false;
739   m_ptr_size = process_sp->GetAddressByteSize();
740   uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
741   if (m_ptr_size == 4) {
742     m_data_32 = new D32();
743     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
744                            error);
745   } else {
746     m_data_64 = new D64();
747     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
748                            error);
749   }
750   if (error.Fail())
751     return false;
752   return false;
753 }
754 
755 template <typename D32, typename D64>
756 bool
757 lldb_private::formatters::
758   GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
759   return true;
760 }
761 
762 template <typename D32, typename D64>
763 lldb::ValueObjectSP
764 lldb_private::formatters::
765   GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) {
766   lldb::addr_t m_objs_addr =
767       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
768 
769   uint32_t num_children = CalculateNumChildren();
770 
771   if (idx >= num_children)
772     return lldb::ValueObjectSP();
773 
774   ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
775   if (!process_sp)
776     return lldb::ValueObjectSP();
777 
778   if (m_children.empty()) {
779     // do the scan phase
780     lldb::addr_t obj_at_idx = 0;
781 
782     uint32_t tries = 0;
783     uint32_t test_idx = 0;
784 
785     while (tries < num_children) {
786       obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
787       if (!process_sp)
788         return lldb::ValueObjectSP();
789       Status error;
790       obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
791       if (error.Fail())
792         return lldb::ValueObjectSP();
793 
794       test_idx++;
795 
796       if (!obj_at_idx)
797         continue;
798       tries++;
799 
800       SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
801 
802       m_children.push_back(descriptor);
803     }
804   }
805 
806   if (idx >= m_children.size()) // should never happen
807     return lldb::ValueObjectSP();
808 
809   SetItemDescriptor &set_item = m_children[idx];
810   if (!set_item.valobj_sp) {
811     auto ptr_size = process_sp->GetAddressByteSize();
812     DataBufferHeap buffer(ptr_size, 0);
813     switch (ptr_size) {
814     case 0: // architecture has no clue?? - fail
815       return lldb::ValueObjectSP();
816     case 4:
817       *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
818       break;
819     case 8:
820       *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
821       break;
822     default:
823       assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
824     }
825     StreamString idx_name;
826     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
827 
828     DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
829                        process_sp->GetByteOrder(),
830                        process_sp->GetAddressByteSize());
831 
832     set_item.valobj_sp = CreateValueObjectFromData(
833         idx_name.GetString(), data, m_exe_ctx_ref,
834         m_backend.GetCompilerType().GetBasicTypeFromAST(
835             lldb::eBasicTypeObjCID));
836   }
837   return set_item.valobj_sp;
838 }
839 
840 template bool lldb_private::formatters::NSSetSummaryProvider<true>(
841     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
842 
843 template bool lldb_private::formatters::NSSetSummaryProvider<false>(
844     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
845