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