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