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