1 //===-- NSDictionary.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 <mutex>
10 
11 #include "clang/AST/DeclCXX.h"
12 
13 #include "CFBasicHash.h"
14 #include "NSDictionary.h"
15 
16 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
17 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
18 
19 #include "lldb/Core/ValueObject.h"
20 #include "lldb/Core/ValueObjectConstResult.h"
21 #include "lldb/DataFormatters/FormattersHelpers.h"
22 #include "lldb/Target/Language.h"
23 #include "lldb/Target/StackFrame.h"
24 #include "lldb/Target/Target.h"
25 #include "lldb/Utility/DataBufferHeap.h"
26 #include "lldb/Utility/Endian.h"
27 #include "lldb/Utility/Status.h"
28 #include "lldb/Utility/Stream.h"
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 using namespace lldb_private::formatters;
33 
Prefix(ConstString p)34 NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
35     ConstString p)
36     : m_prefix(p) {}
37 
Match(ConstString class_name)38 bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
39     ConstString class_name) {
40   return class_name.GetStringRef().startswith(m_prefix.GetStringRef());
41 }
42 
Full(ConstString n)43 NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
44     : m_name(n) {}
45 
Match(ConstString class_name)46 bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
47     ConstString class_name) {
48   return (class_name == m_name);
49 }
50 
51 NSDictionary_Additionals::AdditionalFormatters<
52     CXXFunctionSummaryFormat::Callback> &
GetAdditionalSummaries()53 NSDictionary_Additionals::GetAdditionalSummaries() {
54   static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
55   return g_map;
56 }
57 
58 NSDictionary_Additionals::AdditionalFormatters<
59     CXXSyntheticChildren::CreateFrontEndCallback> &
GetAdditionalSynthetics()60 NSDictionary_Additionals::GetAdditionalSynthetics() {
61   static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
62       g_map;
63   return g_map;
64 }
65 
GetLLDBNSPairType(TargetSP target_sp)66 static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
67   CompilerType compiler_type;
68 
69   TypeSystemClangSP scratch_ts_sp =
70       ScratchTypeSystemClang::GetForTarget(*target_sp);
71 
72   if (scratch_ts_sp) {
73     ConstString g_lldb_autogen_nspair("__lldb_autogen_nspair");
74 
75     compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(
76         g_lldb_autogen_nspair);
77 
78     if (!compiler_type) {
79       compiler_type = scratch_ts_sp->CreateRecordType(
80           nullptr, OptionalClangModuleID(), lldb::eAccessPublic,
81           g_lldb_autogen_nspair.GetCString(), clang::TTK_Struct,
82           lldb::eLanguageTypeC);
83 
84       if (compiler_type) {
85         TypeSystemClang::StartTagDeclarationDefinition(compiler_type);
86         CompilerType id_compiler_type =
87             scratch_ts_sp->GetBasicType(eBasicTypeObjCID);
88         TypeSystemClang::AddFieldToRecordType(
89             compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
90         TypeSystemClang::AddFieldToRecordType(
91             compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
92         TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type);
93       }
94     }
95   }
96   return compiler_type;
97 }
98 
99 namespace lldb_private {
100 namespace formatters {
101 class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
102 public:
103   NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
104 
105   ~NSDictionaryISyntheticFrontEnd() override;
106 
107   size_t CalculateNumChildren() override;
108 
109   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
110 
111   bool Update() override;
112 
113   bool MightHaveChildren() override;
114 
115   size_t GetIndexOfChildWithName(ConstString name) override;
116 
117 private:
118   struct DataDescriptor_32 {
119     uint32_t _used : 26;
120     uint32_t _szidx : 6;
121   };
122 
123   struct DataDescriptor_64 {
124     uint64_t _used : 58;
125     uint32_t _szidx : 6;
126   };
127 
128   struct DictionaryItemDescriptor {
129     lldb::addr_t key_ptr;
130     lldb::addr_t val_ptr;
131     lldb::ValueObjectSP valobj_sp;
132   };
133 
134   ExecutionContextRef m_exe_ctx_ref;
135   uint8_t m_ptr_size = 8;
136   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
137   DataDescriptor_32 *m_data_32 = nullptr;
138   DataDescriptor_64 *m_data_64 = nullptr;
139   lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS;
140   CompilerType m_pair_type;
141   std::vector<DictionaryItemDescriptor> m_children;
142 };
143 
144 class NSConstantDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
145 public:
146   NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
147 
148   size_t CalculateNumChildren() override;
149 
150   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
151 
152   bool Update() override;
153 
154   bool MightHaveChildren() override;
155 
156   size_t GetIndexOfChildWithName(ConstString name) override;
157 
158 private:
159   ExecutionContextRef m_exe_ctx_ref;
160   CompilerType m_pair_type;
161   uint8_t m_ptr_size = 8;
162   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
163   unsigned int m_size = 0;
164   lldb::addr_t m_keys_ptr = LLDB_INVALID_ADDRESS;
165   lldb::addr_t m_objects_ptr = LLDB_INVALID_ADDRESS;
166 
167   struct DictionaryItemDescriptor {
168     lldb::addr_t key_ptr;
169     lldb::addr_t val_ptr;
170     lldb::ValueObjectSP valobj_sp;
171   };
172 
173   std::vector<DictionaryItemDescriptor> m_children;
174 };
175 
176 class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
177 public:
178   NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
179 
180   size_t CalculateNumChildren() override;
181 
182   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
183 
184   bool Update() override;
185 
186   bool MightHaveChildren() override;
187 
188   size_t GetIndexOfChildWithName(ConstString name) override;
189 
190 private:
191   struct DictionaryItemDescriptor {
192     lldb::addr_t key_ptr;
193     lldb::addr_t val_ptr;
194     lldb::ValueObjectSP valobj_sp;
195   };
196 
197   ExecutionContextRef m_exe_ctx_ref;
198   uint8_t m_ptr_size = 8;
199   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
200 
201   CFBasicHash m_hashtable;
202 
203   CompilerType m_pair_type;
204   std::vector<DictionaryItemDescriptor> m_children;
205 };
206 
207 class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
208 public:
209   NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
210 
211   ~NSDictionary1SyntheticFrontEnd() override = default;
212 
213   size_t CalculateNumChildren() override;
214 
215   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
216 
217   bool Update() override;
218 
219   bool MightHaveChildren() override;
220 
221   size_t GetIndexOfChildWithName(ConstString name) override;
222 
223 private:
224   ValueObjectSP m_pair;
225 };
226 
227 template <typename D32, typename D64>
228 class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
229 public:
230   GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
231 
232   ~GenericNSDictionaryMSyntheticFrontEnd() override;
233 
234   size_t CalculateNumChildren() override;
235 
236   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
237 
238   bool Update() override;
239 
240   bool MightHaveChildren() override;
241 
242   size_t GetIndexOfChildWithName(ConstString name) override;
243 
244 private:
245   struct DictionaryItemDescriptor {
246     lldb::addr_t key_ptr;
247     lldb::addr_t val_ptr;
248     lldb::ValueObjectSP valobj_sp;
249   };
250 
251   ExecutionContextRef m_exe_ctx_ref;
252   uint8_t m_ptr_size = 8;
253   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
254   D32 *m_data_32;
255   D64 *m_data_64;
256   CompilerType m_pair_type;
257   std::vector<DictionaryItemDescriptor> m_children;
258 };
259 
260 namespace Foundation1100 {
261   class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
262   public:
263     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
264 
265     ~NSDictionaryMSyntheticFrontEnd() override;
266 
267     size_t CalculateNumChildren() override;
268 
269     lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
270 
271     bool Update() override;
272 
273     bool MightHaveChildren() override;
274 
275     size_t GetIndexOfChildWithName(ConstString name) override;
276 
277   private:
278     struct DataDescriptor_32 {
279       uint32_t _used : 26;
280       uint32_t _kvo : 1;
281       uint32_t _size;
282       uint32_t _mutations;
283       uint32_t _objs_addr;
284       uint32_t _keys_addr;
285     };
286 
287     struct DataDescriptor_64 {
288       uint64_t _used : 58;
289       uint32_t _kvo : 1;
290       uint64_t _size;
291       uint64_t _mutations;
292       uint64_t _objs_addr;
293       uint64_t _keys_addr;
294     };
295 
296     struct DictionaryItemDescriptor {
297       lldb::addr_t key_ptr;
298       lldb::addr_t val_ptr;
299       lldb::ValueObjectSP valobj_sp;
300     };
301 
302     ExecutionContextRef m_exe_ctx_ref;
303     uint8_t m_ptr_size = 8;
304     lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
305     DataDescriptor_32 *m_data_32 = nullptr;
306     DataDescriptor_64 *m_data_64 = nullptr;
307     CompilerType m_pair_type;
308     std::vector<DictionaryItemDescriptor> m_children;
309   };
310 }
311 
312 namespace Foundation1428 {
313   namespace {
314     struct DataDescriptor_32 {
315       uint32_t _used : 26;
316       uint32_t _kvo : 1;
317       uint32_t _size;
318       uint32_t _buffer;
GetSizelldb_private::formatters::Foundation1428::__anon1c86f7020111::DataDescriptor_32319       uint64_t GetSize() { return _size; }
320     };
321 
322     struct DataDescriptor_64 {
323       uint64_t _used : 58;
324       uint32_t _kvo : 1;
325       uint64_t _size;
326       uint64_t _buffer;
GetSizelldb_private::formatters::Foundation1428::__anon1c86f7020111::DataDescriptor_64327       uint64_t GetSize() { return _size; }
328     };
329   }
330 
331   using NSDictionaryMSyntheticFrontEnd =
332     GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
333 }
334 
335 namespace Foundation1437 {
336     static const uint64_t NSDictionaryCapacities[] = {
337         0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
338         2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
339         214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
340         6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
341         111638519, 180634607, 292272623, 472907251
342     };
343 
344     static const size_t NSDictionaryNumSizeBuckets =
345         sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
346 
347     namespace {
348     struct DataDescriptor_32 {
349       uint32_t _buffer;
350       uint32_t _muts;
351       uint32_t _used : 25;
352       uint32_t _kvo : 1;
353       uint32_t _szidx : 6;
354 
GetSizelldb_private::formatters::Foundation1437::__anon1c86f7020211::DataDescriptor_32355       uint64_t GetSize() {
356         return (_szidx) >= NSDictionaryNumSizeBuckets ?
357             0 : NSDictionaryCapacities[_szidx];
358       }
359     };
360 
361     struct DataDescriptor_64 {
362       uint64_t _buffer;
363       uint32_t _muts;
364       uint32_t _used : 25;
365       uint32_t _kvo : 1;
366       uint32_t _szidx : 6;
367 
GetSizelldb_private::formatters::Foundation1437::__anon1c86f7020211::DataDescriptor_64368       uint64_t GetSize() {
369         return (_szidx) >= NSDictionaryNumSizeBuckets ?
370             0 : NSDictionaryCapacities[_szidx];
371       }
372     };
373     } // namespace
374 
375   using NSDictionaryMSyntheticFrontEnd =
376     GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
377 
378   template <typename DD>
379   uint64_t
__NSDictionaryMSize_Impl(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)380   __NSDictionaryMSize_Impl(lldb_private::Process &process,
381                            lldb::addr_t valobj_addr, Status &error) {
382     const lldb::addr_t start_of_descriptor =
383         valobj_addr + process.GetAddressByteSize();
384     DD descriptor = DD();
385     process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
386                        error);
387     if (error.Fail()) {
388       return 0;
389     }
390     return descriptor._used;
391   }
392 
393   uint64_t
__NSDictionaryMSize(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)394   __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
395                Status &error) {
396     if (process.GetAddressByteSize() == 4) {
397       return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
398                                                          error);
399     } else {
400       return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
401                                                          error);
402     }
403   }
404 
405 }
406 } // namespace formatters
407 } // namespace lldb_private
408 
409 template <bool name_entries>
NSDictionarySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)410 bool lldb_private::formatters::NSDictionarySummaryProvider(
411     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
412   static ConstString g_TypeHint("NSDictionary");
413   ProcessSP process_sp = valobj.GetProcessSP();
414   if (!process_sp)
415     return false;
416 
417   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
418 
419   if (!runtime)
420     return false;
421 
422   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
423       runtime->GetNonKVOClassDescriptor(valobj));
424 
425   if (!descriptor || !descriptor->IsValid())
426     return false;
427 
428   uint32_t ptr_size = process_sp->GetAddressByteSize();
429   bool is_64bit = (ptr_size == 8);
430 
431   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
432 
433   if (!valobj_addr)
434     return false;
435 
436   uint64_t value = 0;
437 
438   ConstString class_name(descriptor->GetClassName());
439 
440   static const ConstString g_DictionaryI("__NSDictionaryI");
441   static const ConstString g_DictionaryM("__NSDictionaryM");
442   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
443   static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
444   static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
445   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
446   static const ConstString g_Dictionary0("__NSDictionary0");
447   static const ConstString g_DictionaryCF("__CFDictionary");
448   static const ConstString g_DictionaryNSCF("__NSCFDictionary");
449   static const ConstString g_DictionaryCFRef("CFDictionaryRef");
450   static const ConstString g_ConstantDictionary("NSConstantDictionary");
451 
452   if (class_name.IsEmpty())
453     return false;
454 
455   if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
456     Status error;
457     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
458                                                       ptr_size, 0, error);
459     if (error.Fail())
460       return false;
461 
462     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
463   } else if (class_name == g_ConstantDictionary) {
464     Status error;
465     value = process_sp->ReadUnsignedIntegerFromMemory(
466         valobj_addr + 2 * ptr_size, ptr_size, 0, error);
467     if (error.Fail())
468       return false;
469   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
470              class_name == g_DictionaryMFrozen) {
471     AppleObjCRuntime *apple_runtime =
472     llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
473     Status error;
474     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
475       value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
476                                                   error);
477     } else {
478       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
479                                                         ptr_size, 0, error);
480       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
481     }
482     if (error.Fail())
483       return false;
484   } else if (class_name == g_Dictionary1) {
485     value = 1;
486   } else if (class_name == g_Dictionary0) {
487     value = 0;
488   } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
489              class_name == g_DictionaryCFRef) {
490     ExecutionContext exe_ctx(process_sp);
491     CFBasicHash cfbh;
492     if (!cfbh.Update(valobj_addr, exe_ctx))
493       return false;
494     value = cfbh.GetCount();
495   } else {
496     auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
497     for (auto &candidate : map) {
498       if (candidate.first && candidate.first->Match(class_name))
499         return candidate.second(valobj, stream, options);
500     }
501     return false;
502   }
503 
504   std::string prefix, suffix;
505   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
506     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
507                                             suffix)) {
508       prefix.clear();
509       suffix.clear();
510     }
511   }
512 
513   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
514                 value == 1 ? "" : "s", suffix.c_str());
515   return true;
516 }
517 
518 SyntheticChildrenFrontEnd *
NSDictionarySyntheticFrontEndCreator(CXXSyntheticChildren * synth,lldb::ValueObjectSP valobj_sp)519 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
520     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
521   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
522   if (!process_sp)
523     return nullptr;
524   AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
525       ObjCLanguageRuntime::Get(*process_sp));
526   if (!runtime)
527     return nullptr;
528 
529   CompilerType valobj_type(valobj_sp->GetCompilerType());
530   Flags flags(valobj_type.GetTypeInfo());
531 
532   if (flags.IsClear(eTypeIsPointer)) {
533     Status error;
534     valobj_sp = valobj_sp->AddressOf(error);
535     if (error.Fail() || !valobj_sp)
536       return nullptr;
537   }
538 
539   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
540       runtime->GetClassDescriptor(*valobj_sp));
541 
542   if (!descriptor || !descriptor->IsValid())
543     return nullptr;
544 
545   ConstString class_name(descriptor->GetClassName());
546 
547   static const ConstString g_DictionaryI("__NSDictionaryI");
548   static const ConstString g_DictionaryM("__NSDictionaryM");
549   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
550   static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
551   static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
552   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
553   static const ConstString g_Dictionary0("__NSDictionary0");
554   static const ConstString g_DictionaryCF("__CFDictionary");
555   static const ConstString g_DictionaryNSCF("__NSCFDictionary");
556   static const ConstString g_DictionaryCFRef("CFDictionaryRef");
557   static const ConstString g_ConstantDictionary("NSConstantDictionary");
558 
559   if (class_name.IsEmpty())
560     return nullptr;
561 
562   if (class_name == g_DictionaryI) {
563     return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
564   } else if (class_name == g_ConstantDictionary) {
565     return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
566   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) {
567     if (runtime->GetFoundationVersion() >= 1437) {
568       return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
569     } else if (runtime->GetFoundationVersion() >= 1428) {
570       return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
571     } else {
572       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
573     }
574   } else if (class_name == g_DictionaryMLegacy) {
575     return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
576   } else if (class_name == g_Dictionary1) {
577     return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
578   } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
579              class_name == g_DictionaryCFRef) {
580     return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
581   } else {
582     auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
583     for (auto &candidate : map) {
584       if (candidate.first && candidate.first->Match((class_name)))
585         return candidate.second(synth, valobj_sp);
586     }
587   }
588 
589   return nullptr;
590 }
591 
592 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)593     NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
594     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
595 
596 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
~NSDictionaryISyntheticFrontEnd()597     ~NSDictionaryISyntheticFrontEnd() {
598   delete m_data_32;
599   m_data_32 = nullptr;
600   delete m_data_64;
601   m_data_64 = nullptr;
602 }
603 
604 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)605     GetIndexOfChildWithName(ConstString name) {
606   const char *item_name = name.GetCString();
607   uint32_t idx = ExtractIndexFromString(item_name);
608   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
609     return UINT32_MAX;
610   return idx;
611 }
612 
613 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
CalculateNumChildren()614     CalculateNumChildren() {
615   if (!m_data_32 && !m_data_64)
616     return 0;
617   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
618 }
619 
Update()620 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
621   m_children.clear();
622   delete m_data_32;
623   m_data_32 = nullptr;
624   delete m_data_64;
625   m_data_64 = nullptr;
626   m_ptr_size = 0;
627   ValueObjectSP valobj_sp = m_backend.GetSP();
628   if (!valobj_sp)
629     return false;
630   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
631   Status error;
632   error.Clear();
633   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
634   if (!process_sp)
635     return false;
636   m_ptr_size = process_sp->GetAddressByteSize();
637   m_order = process_sp->GetByteOrder();
638   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
639   if (m_ptr_size == 4) {
640     m_data_32 = new DataDescriptor_32();
641     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
642                            error);
643   } else {
644     m_data_64 = new DataDescriptor_64();
645     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
646                            error);
647   }
648   if (error.Fail())
649     return false;
650   m_data_ptr = data_location + m_ptr_size;
651   return false;
652 }
653 
654 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
MightHaveChildren()655     MightHaveChildren() {
656   return true;
657 }
658 
659 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)660 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
661     size_t idx) {
662   uint32_t num_children = CalculateNumChildren();
663 
664   if (idx >= num_children)
665     return lldb::ValueObjectSP();
666 
667   if (m_children.empty()) {
668     // do the scan phase
669     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
670 
671     uint32_t tries = 0;
672     uint32_t test_idx = 0;
673 
674     while (tries < num_children) {
675       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
676       val_at_idx = key_at_idx + m_ptr_size;
677       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
678       if (!process_sp)
679         return lldb::ValueObjectSP();
680       Status error;
681       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
682       if (error.Fail())
683         return lldb::ValueObjectSP();
684       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
685       if (error.Fail())
686         return lldb::ValueObjectSP();
687 
688       test_idx++;
689 
690       if (!key_at_idx || !val_at_idx)
691         continue;
692       tries++;
693 
694       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
695                                              lldb::ValueObjectSP()};
696 
697       m_children.push_back(descriptor);
698     }
699   }
700 
701   if (idx >= m_children.size()) // should never happen
702     return lldb::ValueObjectSP();
703 
704   DictionaryItemDescriptor &dict_item = m_children[idx];
705   if (!dict_item.valobj_sp) {
706     if (!m_pair_type.IsValid()) {
707       TargetSP target_sp(m_backend.GetTargetSP());
708       if (!target_sp)
709         return ValueObjectSP();
710       m_pair_type = GetLLDBNSPairType(target_sp);
711     }
712     if (!m_pair_type.IsValid())
713       return ValueObjectSP();
714 
715     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
716 
717     if (m_ptr_size == 8) {
718       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
719       *data_ptr = dict_item.key_ptr;
720       *(data_ptr + 1) = dict_item.val_ptr;
721     } else {
722       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
723       *data_ptr = dict_item.key_ptr;
724       *(data_ptr + 1) = dict_item.val_ptr;
725     }
726 
727     StreamString idx_name;
728     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
729     DataExtractor data(buffer_sp, m_order, m_ptr_size);
730     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
731                                                     m_exe_ctx_ref, m_pair_type);
732   }
733   return dict_item.valobj_sp;
734 }
735 
736 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)737     NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
738     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(),
739       m_pair_type() {}
740 
741 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)742     GetIndexOfChildWithName(ConstString name) {
743   const char *item_name = name.GetCString();
744   const uint32_t idx = ExtractIndexFromString(item_name);
745   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
746     return UINT32_MAX;
747   return idx;
748 }
749 
750 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
CalculateNumChildren()751     CalculateNumChildren() {
752   if (!m_hashtable.IsValid())
753     return 0;
754   return m_hashtable.GetCount();
755 }
756 
Update()757 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::Update() {
758   m_children.clear();
759   ValueObjectSP valobj_sp = m_backend.GetSP();
760   m_ptr_size = 0;
761   if (!valobj_sp)
762     return false;
763   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
764 
765   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
766   if (!process_sp)
767     return false;
768   m_ptr_size = process_sp->GetAddressByteSize();
769   m_order = process_sp->GetByteOrder();
770   return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref);
771 }
772 
773 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
MightHaveChildren()774     MightHaveChildren() {
775   return true;
776 }
777 
778 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)779 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex(
780     size_t idx) {
781   lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
782   lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
783 
784   const uint32_t num_children = CalculateNumChildren();
785 
786   if (idx >= num_children)
787     return lldb::ValueObjectSP();
788 
789   if (m_children.empty()) {
790     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
791     if (!process_sp)
792       return lldb::ValueObjectSP();
793 
794     Status error;
795     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
796 
797     uint32_t tries = 0;
798     uint32_t test_idx = 0;
799 
800     // Iterate over inferior memory, reading key/value pointers by shifting each
801     // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
802     // fails, otherwise, continue until the number of tries matches the number
803     // of childen.
804     while (tries < num_children) {
805       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
806       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
807 
808       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
809       if (error.Fail())
810         return lldb::ValueObjectSP();
811       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
812       if (error.Fail())
813         return lldb::ValueObjectSP();
814 
815       test_idx++;
816 
817       if (!key_at_idx || !val_at_idx)
818         continue;
819       tries++;
820 
821       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
822                                              lldb::ValueObjectSP()};
823 
824       m_children.push_back(descriptor);
825     }
826   }
827 
828   if (idx >= m_children.size()) // should never happen
829     return lldb::ValueObjectSP();
830 
831   DictionaryItemDescriptor &dict_item = m_children[idx];
832   if (!dict_item.valobj_sp) {
833     if (!m_pair_type.IsValid()) {
834       TargetSP target_sp(m_backend.GetTargetSP());
835       if (!target_sp)
836         return ValueObjectSP();
837       m_pair_type = GetLLDBNSPairType(target_sp);
838     }
839     if (!m_pair_type.IsValid())
840       return ValueObjectSP();
841 
842     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
843 
844     switch (m_ptr_size) {
845     case 0: // architecture has no clue - fail
846       return lldb::ValueObjectSP();
847     case 4: {
848       uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
849       *data_ptr = dict_item.key_ptr;
850       *(data_ptr + 1) = dict_item.val_ptr;
851     } break;
852     case 8: {
853       uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
854       *data_ptr = dict_item.key_ptr;
855       *(data_ptr + 1) = dict_item.val_ptr;
856     } break;
857     default:
858       lldbassert(false && "pointer size is not 4 nor 8");
859     }
860 
861     StreamString idx_name;
862     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
863     DataExtractor data(buffer_sp, m_order, m_ptr_size);
864     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
865                                                     m_exe_ctx_ref, m_pair_type);
866   }
867   return dict_item.valobj_sp;
868 }
869 
870 lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)871     NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
872     : SyntheticChildrenFrontEnd(*valobj_sp) {}
873 
874 size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)875     GetIndexOfChildWithName(ConstString name) {
876   const char *item_name = name.GetCString();
877   uint32_t idx = ExtractIndexFromString(item_name);
878   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
879     return UINT32_MAX;
880   return idx;
881 }
882 
883 size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
CalculateNumChildren()884     CalculateNumChildren() {
885   return m_size;
886 }
887 
Update()888 bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::Update() {
889   ValueObjectSP valobj_sp = m_backend.GetSP();
890   if (!valobj_sp)
891     return false;
892   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
893   Status error;
894   error.Clear();
895   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
896   if (!process_sp)
897     return false;
898   m_ptr_size = process_sp->GetAddressByteSize();
899   m_order = process_sp->GetByteOrder();
900   uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
901   m_size = process_sp->ReadUnsignedIntegerFromMemory(
902       valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
903   if (error.Fail())
904     return false;
905   m_keys_ptr =
906       process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
907   if (error.Fail())
908     return false;
909   m_objects_ptr =
910       process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
911   return !error.Fail();
912 }
913 
914 bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
MightHaveChildren()915     MightHaveChildren() {
916   return true;
917 }
918 
919 lldb::ValueObjectSP lldb_private::formatters::
GetChildAtIndex(size_t idx)920     NSConstantDictionarySyntheticFrontEnd::GetChildAtIndex(size_t idx) {
921   uint32_t num_children = CalculateNumChildren();
922 
923   if (idx >= num_children)
924     return lldb::ValueObjectSP();
925 
926   if (m_children.empty()) {
927     // do the scan phase
928     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
929     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
930     if (!process_sp)
931       return lldb::ValueObjectSP();
932 
933     for (unsigned int child = 0; child < num_children; ++child) {
934       Status error;
935       key_at_idx = process_sp->ReadPointerFromMemory(
936           m_keys_ptr + child * m_ptr_size, error);
937       if (error.Fail())
938         return lldb::ValueObjectSP();
939       val_at_idx = process_sp->ReadPointerFromMemory(
940           m_objects_ptr + child * m_ptr_size, error);
941       if (error.Fail())
942         return lldb::ValueObjectSP();
943       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
944                                              lldb::ValueObjectSP()};
945       m_children.push_back(descriptor);
946     }
947   }
948 
949   if (idx >= m_children.size()) // should never happen
950     return lldb::ValueObjectSP();
951 
952   DictionaryItemDescriptor &dict_item = m_children[idx];
953   if (!dict_item.valobj_sp) {
954     if (!m_pair_type.IsValid()) {
955       TargetSP target_sp(m_backend.GetTargetSP());
956       if (!target_sp)
957         return ValueObjectSP();
958       m_pair_type = GetLLDBNSPairType(target_sp);
959     }
960     if (!m_pair_type.IsValid())
961       return ValueObjectSP();
962 
963     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
964 
965     if (m_ptr_size == 8) {
966       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
967       *data_ptr = dict_item.key_ptr;
968       *(data_ptr + 1) = dict_item.val_ptr;
969     } else {
970       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
971       *data_ptr = dict_item.key_ptr;
972       *(data_ptr + 1) = dict_item.val_ptr;
973     }
974 
975     StreamString idx_name;
976     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
977     DataExtractor data(buffer_sp, m_order, m_ptr_size);
978     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
979                                                     m_exe_ctx_ref, m_pair_type);
980   }
981   return dict_item.valobj_sp;
982 }
983 
984 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)985     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
986     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
987 
988 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)989     GetIndexOfChildWithName(ConstString name) {
990   static const ConstString g_zero("[0]");
991   return name == g_zero ? 0 : UINT32_MAX;
992 }
993 
994 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
CalculateNumChildren()995     CalculateNumChildren() {
996   return 1;
997 }
998 
Update()999 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
1000   m_pair.reset();
1001   return false;
1002 }
1003 
1004 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
MightHaveChildren()1005     MightHaveChildren() {
1006   return true;
1007 }
1008 
1009 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)1010 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
1011     size_t idx) {
1012   if (idx != 0)
1013     return lldb::ValueObjectSP();
1014 
1015   if (m_pair.get())
1016     return m_pair;
1017 
1018   auto process_sp(m_backend.GetProcessSP());
1019   if (!process_sp)
1020     return nullptr;
1021 
1022   auto ptr_size = process_sp->GetAddressByteSize();
1023 
1024   lldb::addr_t key_ptr =
1025       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
1026   lldb::addr_t value_ptr = key_ptr + ptr_size;
1027 
1028   Status error;
1029 
1030   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
1031   if (error.Fail())
1032     return nullptr;
1033   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
1034   if (error.Fail())
1035     return nullptr;
1036 
1037   auto pair_type =
1038       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
1039 
1040   WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1041 
1042   if (ptr_size == 8) {
1043     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1044     *data_ptr = key_at_idx;
1045     *(data_ptr + 1) = value_at_idx;
1046   } else {
1047     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1048     *data_ptr = key_at_idx;
1049     *(data_ptr + 1) = value_at_idx;
1050   }
1051 
1052   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1053   m_pair = CreateValueObjectFromData(
1054       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1055 
1056   return m_pair;
1057 }
1058 
1059 template <typename D32, typename D64>
1060 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32, D64>::
GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)1061     GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1062     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
1063       m_data_32(nullptr), m_data_64(nullptr), m_pair_type() {}
1064 
1065 template <typename D32, typename D64>
1066 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
~GenericNSDictionaryMSyntheticFrontEnd()1067     ~GenericNSDictionaryMSyntheticFrontEnd<D32,D64>() {
1068   delete m_data_32;
1069   m_data_32 = nullptr;
1070   delete m_data_64;
1071   m_data_64 = nullptr;
1072 }
1073 
1074 template <typename D32, typename D64>
1075 size_t lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
GetIndexOfChildWithName(ConstString name)1076     D32, D64>::GetIndexOfChildWithName(ConstString name) {
1077   const char *item_name = name.GetCString();
1078   uint32_t idx = ExtractIndexFromString(item_name);
1079   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
1080     return UINT32_MAX;
1081   return idx;
1082 }
1083 
1084 template <typename D32, typename D64>
1085 size_t
CalculateNumChildren()1086 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
1087   if (!m_data_32 && !m_data_64)
1088     return 0;
1089   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1090 }
1091 
1092 template <typename D32, typename D64>
1093 bool
1094 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
Update()1095   Update() {
1096   m_children.clear();
1097   ValueObjectSP valobj_sp = m_backend.GetSP();
1098   m_ptr_size = 0;
1099   delete m_data_32;
1100   m_data_32 = nullptr;
1101   delete m_data_64;
1102   m_data_64 = nullptr;
1103   if (!valobj_sp)
1104     return false;
1105   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1106   Status error;
1107   error.Clear();
1108   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1109   if (!process_sp)
1110     return false;
1111   m_ptr_size = process_sp->GetAddressByteSize();
1112   m_order = process_sp->GetByteOrder();
1113   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1114   if (m_ptr_size == 4) {
1115     m_data_32 = new D32();
1116     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1117                            error);
1118   } else {
1119     m_data_64 = new D64();
1120     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1121                            error);
1122   }
1123 
1124   return error.Success();
1125 }
1126 
1127 template <typename D32, typename D64>
1128 bool
1129 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
MightHaveChildren()1130     MightHaveChildren() {
1131   return true;
1132 }
1133 
1134 template <typename D32, typename D64>
1135 lldb::ValueObjectSP
1136 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
GetChildAtIndex(size_t idx)1137     D32, D64>::GetChildAtIndex(size_t idx) {
1138   lldb::addr_t m_keys_ptr;
1139   lldb::addr_t m_values_ptr;
1140   if (m_data_32) {
1141     uint32_t size = m_data_32->GetSize();
1142     m_keys_ptr = m_data_32->_buffer;
1143     m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1144   } else {
1145     uint32_t size = m_data_64->GetSize();
1146     m_keys_ptr = m_data_64->_buffer;
1147     m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1148   }
1149 
1150   uint32_t num_children = CalculateNumChildren();
1151 
1152   if (idx >= num_children)
1153     return lldb::ValueObjectSP();
1154 
1155   if (m_children.empty()) {
1156     // do the scan phase
1157     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1158 
1159     uint32_t tries = 0;
1160     uint32_t test_idx = 0;
1161 
1162     while (tries < num_children) {
1163       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1164       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1165       ;
1166       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1167       if (!process_sp)
1168         return lldb::ValueObjectSP();
1169       Status error;
1170       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1171       if (error.Fail())
1172         return lldb::ValueObjectSP();
1173       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1174       if (error.Fail())
1175         return lldb::ValueObjectSP();
1176 
1177       test_idx++;
1178 
1179       if (!key_at_idx || !val_at_idx)
1180         continue;
1181       tries++;
1182 
1183       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1184                                              lldb::ValueObjectSP()};
1185 
1186       m_children.push_back(descriptor);
1187     }
1188   }
1189 
1190   if (idx >= m_children.size()) // should never happen
1191     return lldb::ValueObjectSP();
1192 
1193   DictionaryItemDescriptor &dict_item = m_children[idx];
1194   if (!dict_item.valobj_sp) {
1195     if (!m_pair_type.IsValid()) {
1196       TargetSP target_sp(m_backend.GetTargetSP());
1197       if (!target_sp)
1198         return ValueObjectSP();
1199       m_pair_type = GetLLDBNSPairType(target_sp);
1200     }
1201     if (!m_pair_type.IsValid())
1202       return ValueObjectSP();
1203 
1204     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1205 
1206     if (m_ptr_size == 8) {
1207       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1208       *data_ptr = dict_item.key_ptr;
1209       *(data_ptr + 1) = dict_item.val_ptr;
1210     } else {
1211       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1212       *data_ptr = dict_item.key_ptr;
1213       *(data_ptr + 1) = dict_item.val_ptr;
1214     }
1215 
1216     StreamString idx_name;
1217     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1218     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1219     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1220                                                     m_exe_ctx_ref, m_pair_type);
1221   }
1222   return dict_item.valobj_sp;
1223 }
1224 
1225 lldb_private::formatters::Foundation1100::NSDictionaryMSyntheticFrontEnd::
NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)1226     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1227     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
1228 
1229 lldb_private::formatters::Foundation1100::
~NSDictionaryMSyntheticFrontEnd()1230   NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
1231   delete m_data_32;
1232   m_data_32 = nullptr;
1233   delete m_data_64;
1234   m_data_64 = nullptr;
1235 }
1236 
1237 size_t
1238 lldb_private::formatters::Foundation1100::
GetIndexOfChildWithName(ConstString name)1239   NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
1240   const char *item_name = name.GetCString();
1241   uint32_t idx = ExtractIndexFromString(item_name);
1242   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
1243     return UINT32_MAX;
1244   return idx;
1245 }
1246 
1247 size_t
1248 lldb_private::formatters::Foundation1100::
CalculateNumChildren()1249   NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
1250   if (!m_data_32 && !m_data_64)
1251     return 0;
1252   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1253 }
1254 
1255 bool
1256 lldb_private::formatters::Foundation1100::
Update()1257   NSDictionaryMSyntheticFrontEnd::Update() {
1258   m_children.clear();
1259   ValueObjectSP valobj_sp = m_backend.GetSP();
1260   m_ptr_size = 0;
1261   delete m_data_32;
1262   m_data_32 = nullptr;
1263   delete m_data_64;
1264   m_data_64 = nullptr;
1265   if (!valobj_sp)
1266     return false;
1267   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1268   Status error;
1269   error.Clear();
1270   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1271   if (!process_sp)
1272     return false;
1273   m_ptr_size = process_sp->GetAddressByteSize();
1274   m_order = process_sp->GetByteOrder();
1275   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1276   if (m_ptr_size == 4) {
1277     m_data_32 = new DataDescriptor_32();
1278     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1279                            error);
1280   } else {
1281     m_data_64 = new DataDescriptor_64();
1282     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1283                            error);
1284   }
1285 
1286   return error.Success();
1287 }
1288 
1289 bool
1290 lldb_private::formatters::Foundation1100::
MightHaveChildren()1291   NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
1292   return true;
1293 }
1294 
1295 lldb::ValueObjectSP
1296 lldb_private::formatters::Foundation1100::
GetChildAtIndex(size_t idx)1297   NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
1298   lldb::addr_t m_keys_ptr =
1299       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1300   lldb::addr_t m_values_ptr =
1301       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1302 
1303   uint32_t num_children = CalculateNumChildren();
1304 
1305   if (idx >= num_children)
1306     return lldb::ValueObjectSP();
1307 
1308   if (m_children.empty()) {
1309     // do the scan phase
1310     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1311 
1312     uint32_t tries = 0;
1313     uint32_t test_idx = 0;
1314 
1315     while (tries < num_children) {
1316       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1317       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1318       ;
1319       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1320       if (!process_sp)
1321         return lldb::ValueObjectSP();
1322       Status error;
1323       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1324       if (error.Fail())
1325         return lldb::ValueObjectSP();
1326       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1327       if (error.Fail())
1328         return lldb::ValueObjectSP();
1329 
1330       test_idx++;
1331 
1332       if (!key_at_idx || !val_at_idx)
1333         continue;
1334       tries++;
1335 
1336       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1337                                              lldb::ValueObjectSP()};
1338 
1339       m_children.push_back(descriptor);
1340     }
1341   }
1342 
1343   if (idx >= m_children.size()) // should never happen
1344     return lldb::ValueObjectSP();
1345 
1346   DictionaryItemDescriptor &dict_item = m_children[idx];
1347   if (!dict_item.valobj_sp) {
1348     if (!m_pair_type.IsValid()) {
1349       TargetSP target_sp(m_backend.GetTargetSP());
1350       if (!target_sp)
1351         return ValueObjectSP();
1352       m_pair_type = GetLLDBNSPairType(target_sp);
1353     }
1354     if (!m_pair_type.IsValid())
1355       return ValueObjectSP();
1356 
1357     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1358 
1359     if (m_ptr_size == 8) {
1360       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1361       *data_ptr = dict_item.key_ptr;
1362       *(data_ptr + 1) = dict_item.val_ptr;
1363     } else {
1364       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1365       *data_ptr = dict_item.key_ptr;
1366       *(data_ptr + 1) = dict_item.val_ptr;
1367     }
1368 
1369     StreamString idx_name;
1370     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1371     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1372     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1373                                                     m_exe_ctx_ref, m_pair_type);
1374   }
1375   return dict_item.valobj_sp;
1376 }
1377 
1378 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1379     ValueObject &, Stream &, const TypeSummaryOptions &);
1380 
1381 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1382     ValueObject &, Stream &, const TypeSummaryOptions &);
1383