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   static const ConstString g_SetCFRef("CFSetRef");
286 
287   if (class_name.IsEmpty())
288     return false;
289 
290   if (class_name == g_SetI || class_name == g_OrderedSetI) {
291     Status error;
292     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
293                                                       ptr_size, 0, error);
294     if (error.Fail())
295       return false;
296     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
297   } else if (class_name == g_SetM) {
298     AppleObjCRuntime *apple_runtime =
299         llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
300     Status error;
301     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
302       value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error);
303     } else {
304       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
305                                                         ptr_size, 0, error);
306       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
307     }
308     if (error.Fail())
309       return false;
310   } else if (class_name == g_SetCF || class_name == g_SetCFRef) {
311     ExecutionContext exe_ctx(process_sp);
312     CFBasicHash cfbh;
313     if (!cfbh.Update(valobj_addr, exe_ctx))
314       return false;
315     value = cfbh.GetCount();
316   } else {
317     auto &map(NSSet_Additionals::GetAdditionalSummaries());
318     auto iter = map.find(class_name), end = map.end();
319     if (iter != end)
320       return iter->second(valobj, stream, options);
321     else
322       return false;
323   }
324 
325   std::string prefix, suffix;
326   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
327     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
328                                             suffix)) {
329       prefix.clear();
330       suffix.clear();
331     }
332   }
333 
334   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
335                 value == 1 ? "" : "s", suffix.c_str());
336   return true;
337 }
338 
339 SyntheticChildrenFrontEnd *
340 lldb_private::formatters::NSSetSyntheticFrontEndCreator(
341     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
342   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
343   if (!process_sp)
344     return nullptr;
345   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
346   if (!runtime)
347     return nullptr;
348 
349   CompilerType valobj_type(valobj_sp->GetCompilerType());
350   Flags flags(valobj_type.GetTypeInfo());
351 
352   if (flags.IsClear(eTypeIsPointer)) {
353     Status error;
354     valobj_sp = valobj_sp->AddressOf(error);
355     if (error.Fail() || !valobj_sp)
356       return nullptr;
357   }
358 
359   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
360       runtime->GetClassDescriptor(*valobj_sp));
361 
362   if (!descriptor || !descriptor->IsValid())
363     return nullptr;
364 
365   ConstString class_name = descriptor->GetClassName();
366 
367   static const ConstString g_SetI("__NSSetI");
368   static const ConstString g_OrderedSetI("__NSOrderedSetI");
369   static const ConstString g_SetM("__NSSetM");
370   static const ConstString g_SetCF("__NSCFSet");
371   static const ConstString g_SetCFRef("CFSetRef");
372 
373   if (class_name.IsEmpty())
374     return nullptr;
375 
376   if (class_name == g_SetI || class_name == g_OrderedSetI) {
377     return (new NSSetISyntheticFrontEnd(valobj_sp));
378   } else if (class_name == g_SetM) {
379     AppleObjCRuntime *apple_runtime =
380         llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
381     if (apple_runtime) {
382       if (apple_runtime->GetFoundationVersion() >= 1437)
383         return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp));
384       else if (apple_runtime->GetFoundationVersion() >= 1428)
385         return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp));
386       else
387         return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
388     } else {
389       return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
390     }
391   } else if (class_name == g_SetCF || class_name == g_SetCFRef) {
392     return (new NSCFSetSyntheticFrontEnd(valobj_sp));
393   } else {
394     auto &map(NSSet_Additionals::GetAdditionalSynthetics());
395     auto iter = map.find(class_name), end = map.end();
396     if (iter != end)
397       return iter->second(synth, valobj_sp);
398     return nullptr;
399   }
400 }
401 
402 lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd(
403     lldb::ValueObjectSP valobj_sp)
404     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
405       m_data_32(nullptr), m_data_64(nullptr) {
406   if (valobj_sp)
407     Update();
408 }
409 
410 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() {
411   delete m_data_32;
412   m_data_32 = nullptr;
413   delete m_data_64;
414   m_data_64 = nullptr;
415 }
416 
417 size_t
418 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName(
419     ConstString name) {
420   const char *item_name = name.GetCString();
421   uint32_t idx = ExtractIndexFromString(item_name);
422   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
423     return UINT32_MAX;
424   return idx;
425 }
426 
427 size_t
428 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() {
429   if (!m_data_32 && !m_data_64)
430     return 0;
431   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
432 }
433 
434 bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() {
435   m_children.clear();
436   delete m_data_32;
437   m_data_32 = nullptr;
438   delete m_data_64;
439   m_data_64 = nullptr;
440   m_ptr_size = 0;
441   ValueObjectSP valobj_sp = m_backend.GetSP();
442   if (!valobj_sp)
443     return false;
444   if (!valobj_sp)
445     return false;
446   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
447   Status error;
448   if (valobj_sp->IsPointerType()) {
449     valobj_sp = valobj_sp->Dereference(error);
450     if (error.Fail() || !valobj_sp)
451       return false;
452   }
453   error.Clear();
454   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
455   if (!process_sp)
456     return false;
457   m_ptr_size = process_sp->GetAddressByteSize();
458   uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
459   if (m_ptr_size == 4) {
460     m_data_32 = new DataDescriptor_32();
461     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
462                            error);
463   } else {
464     m_data_64 = new DataDescriptor_64();
465     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
466                            error);
467   }
468   if (error.Fail())
469     return false;
470   m_data_ptr = data_location + m_ptr_size;
471   return false;
472 }
473 
474 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
475   return true;
476 }
477 
478 lldb::ValueObjectSP
479 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) {
480   uint32_t num_children = CalculateNumChildren();
481 
482   if (idx >= num_children)
483     return lldb::ValueObjectSP();
484 
485   ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
486   if (!process_sp)
487     return lldb::ValueObjectSP();
488 
489   if (m_children.empty()) {
490     // do the scan phase
491     lldb::addr_t obj_at_idx = 0;
492 
493     uint32_t tries = 0;
494     uint32_t test_idx = 0;
495 
496     while (tries < num_children) {
497       obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
498       if (!process_sp)
499         return lldb::ValueObjectSP();
500       Status error;
501       obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
502       if (error.Fail())
503         return lldb::ValueObjectSP();
504 
505       test_idx++;
506 
507       if (!obj_at_idx)
508         continue;
509       tries++;
510 
511       SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
512 
513       m_children.push_back(descriptor);
514     }
515   }
516 
517   if (idx >= m_children.size()) // should never happen
518     return lldb::ValueObjectSP();
519 
520   SetItemDescriptor &set_item = m_children[idx];
521   if (!set_item.valobj_sp) {
522     auto ptr_size = process_sp->GetAddressByteSize();
523     DataBufferHeap buffer(ptr_size, 0);
524     switch (ptr_size) {
525     case 0: // architecture has no clue - fail
526       return lldb::ValueObjectSP();
527     case 4:
528       *reinterpret_cast<uint32_t *>(buffer.GetBytes()) =
529           static_cast<uint32_t>(set_item.item_ptr);
530       break;
531     case 8:
532       *reinterpret_cast<uint64_t *>(buffer.GetBytes()) =
533           static_cast<uint64_t>(set_item.item_ptr);
534       break;
535     default:
536       lldbassert(false && "pointer size is not 4 nor 8");
537     }
538     StreamString idx_name;
539     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
540 
541     DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
542                        process_sp->GetByteOrder(),
543                        process_sp->GetAddressByteSize());
544 
545     set_item.valobj_sp = CreateValueObjectFromData(
546         idx_name.GetString(), data, m_exe_ctx_ref,
547         m_backend.GetCompilerType().GetBasicTypeFromAST(
548             lldb::eBasicTypeObjCID));
549   }
550   return set_item.valobj_sp;
551 }
552 
553 lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd(
554     lldb::ValueObjectSP valobj_sp)
555     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
556       m_order(lldb::eByteOrderInvalid), m_hashtable(), m_pair_type() {}
557 
558 size_t
559 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName(
560     ConstString name) {
561   const char *item_name = name.GetCString();
562   const uint32_t idx = ExtractIndexFromString(item_name);
563   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
564     return UINT32_MAX;
565   return idx;
566 }
567 
568 size_t
569 lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() {
570   if (!m_hashtable.IsValid())
571     return 0;
572   return m_hashtable.GetCount();
573 }
574 
575 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() {
576   m_children.clear();
577   ValueObjectSP valobj_sp = m_backend.GetSP();
578   m_ptr_size = 0;
579   if (!valobj_sp)
580     return false;
581   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
582 
583   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
584   if (!process_sp)
585     return false;
586   m_ptr_size = process_sp->GetAddressByteSize();
587   m_order = process_sp->GetByteOrder();
588   return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref);
589 }
590 
591 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() {
592   return true;
593 }
594 
595 lldb::ValueObjectSP
596 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex(
597     size_t idx) {
598   lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
599 
600   const uint32_t num_children = CalculateNumChildren();
601 
602   if (idx >= num_children)
603     return lldb::ValueObjectSP();
604 
605   if (m_children.empty()) {
606     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
607     if (!process_sp)
608       return lldb::ValueObjectSP();
609 
610     Status error;
611     lldb::addr_t val_at_idx = 0;
612 
613     uint32_t tries = 0;
614     uint32_t test_idx = 0;
615 
616     // Iterate over inferior memory, reading value pointers by shifting the
617     // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
618     // fails, otherwise, continue until the number of tries matches the number
619     // of childen.
620     while (tries < num_children) {
621       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
622 
623       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
624       if (error.Fail())
625         return lldb::ValueObjectSP();
626 
627       test_idx++;
628 
629       if (!val_at_idx)
630         continue;
631       tries++;
632 
633       SetItemDescriptor descriptor = {val_at_idx, lldb::ValueObjectSP()};
634 
635       m_children.push_back(descriptor);
636     }
637   }
638 
639   if (idx >= m_children.size()) // should never happen
640     return lldb::ValueObjectSP();
641 
642   SetItemDescriptor &set_item = m_children[idx];
643   if (!set_item.valobj_sp) {
644 
645     DataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0));
646 
647     switch (m_ptr_size) {
648     case 0: // architecture has no clue - fail
649       return lldb::ValueObjectSP();
650     case 4:
651       *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) =
652           static_cast<uint32_t>(set_item.item_ptr);
653       break;
654     case 8:
655       *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) =
656           static_cast<uint64_t>(set_item.item_ptr);
657       break;
658     default:
659       lldbassert(false && "pointer size is not 4 nor 8");
660     }
661     StreamString idx_name;
662     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
663 
664     DataExtractor data(buffer_sp, m_order, m_ptr_size);
665 
666     set_item.valobj_sp = CreateValueObjectFromData(
667         idx_name.GetString(), data, m_exe_ctx_ref,
668         m_backend.GetCompilerType().GetBasicTypeFromAST(
669             lldb::eBasicTypeObjCID));
670   }
671 
672   return set_item.valobj_sp;
673 }
674 
675 template <typename D32, typename D64>
676 lldb_private::formatters::
677   GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd(
678     lldb::ValueObjectSP valobj_sp)
679     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
680       m_data_32(nullptr), m_data_64(nullptr) {
681   if (valobj_sp)
682     Update();
683 }
684 
685 template <typename D32, typename D64>
686 lldb_private::formatters::
687   GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd<D32, D64>() {
688   delete m_data_32;
689   m_data_32 = nullptr;
690   delete m_data_64;
691   m_data_64 = nullptr;
692 }
693 
694 template <typename D32, typename D64>
695 size_t
696 lldb_private::formatters::
697   GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName(
698     ConstString name) {
699   const char *item_name = name.GetCString();
700   uint32_t idx = ExtractIndexFromString(item_name);
701   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
702     return UINT32_MAX;
703   return idx;
704 }
705 
706 template <typename D32, typename D64>
707 size_t
708 lldb_private::formatters::
709   GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() {
710   if (!m_data_32 && !m_data_64)
711     return 0;
712   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
713 }
714 
715 template <typename D32, typename D64>
716 bool
717 lldb_private::formatters::
718   GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() {
719   m_children.clear();
720   ValueObjectSP valobj_sp = m_backend.GetSP();
721   m_ptr_size = 0;
722   delete m_data_32;
723   m_data_32 = nullptr;
724   delete m_data_64;
725   m_data_64 = nullptr;
726   if (!valobj_sp)
727     return false;
728   if (!valobj_sp)
729     return false;
730   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
731   Status error;
732   if (valobj_sp->IsPointerType()) {
733     valobj_sp = valobj_sp->Dereference(error);
734     if (error.Fail() || !valobj_sp)
735       return false;
736   }
737   error.Clear();
738   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
739   if (!process_sp)
740     return false;
741   m_ptr_size = process_sp->GetAddressByteSize();
742   uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
743   if (m_ptr_size == 4) {
744     m_data_32 = new D32();
745     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
746                            error);
747   } else {
748     m_data_64 = new D64();
749     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
750                            error);
751   }
752   if (error.Fail())
753     return false;
754   return false;
755 }
756 
757 template <typename D32, typename D64>
758 bool
759 lldb_private::formatters::
760   GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
761   return true;
762 }
763 
764 template <typename D32, typename D64>
765 lldb::ValueObjectSP
766 lldb_private::formatters::
767   GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) {
768   lldb::addr_t m_objs_addr =
769       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
770 
771   uint32_t num_children = CalculateNumChildren();
772 
773   if (idx >= num_children)
774     return lldb::ValueObjectSP();
775 
776   ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
777   if (!process_sp)
778     return lldb::ValueObjectSP();
779 
780   if (m_children.empty()) {
781     // do the scan phase
782     lldb::addr_t obj_at_idx = 0;
783 
784     uint32_t tries = 0;
785     uint32_t test_idx = 0;
786 
787     while (tries < num_children) {
788       obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
789       if (!process_sp)
790         return lldb::ValueObjectSP();
791       Status error;
792       obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
793       if (error.Fail())
794         return lldb::ValueObjectSP();
795 
796       test_idx++;
797 
798       if (!obj_at_idx)
799         continue;
800       tries++;
801 
802       SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
803 
804       m_children.push_back(descriptor);
805     }
806   }
807 
808   if (idx >= m_children.size()) // should never happen
809     return lldb::ValueObjectSP();
810 
811   SetItemDescriptor &set_item = m_children[idx];
812   if (!set_item.valobj_sp) {
813     auto ptr_size = process_sp->GetAddressByteSize();
814     DataBufferHeap buffer(ptr_size, 0);
815     switch (ptr_size) {
816     case 0: // architecture has no clue?? - fail
817       return lldb::ValueObjectSP();
818     case 4:
819       *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
820       break;
821     case 8:
822       *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
823       break;
824     default:
825       assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
826     }
827     StreamString idx_name;
828     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
829 
830     DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
831                        process_sp->GetByteOrder(),
832                        process_sp->GetAddressByteSize());
833 
834     set_item.valobj_sp = CreateValueObjectFromData(
835         idx_name.GetString(), data, m_exe_ctx_ref,
836         m_backend.GetCompilerType().GetBasicTypeFromAST(
837             lldb::eBasicTypeObjCID));
838   }
839   return set_item.valobj_sp;
840 }
841 
842 template bool lldb_private::formatters::NSSetSummaryProvider<true>(
843     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
844 
845 template bool lldb_private::formatters::NSSetSummaryProvider<false>(
846     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
847