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 = 8;
137   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
138   DataDescriptor_32 *m_data_32 = nullptr;
139   DataDescriptor_64 *m_data_64 = nullptr;
140   lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS;
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 = 8;
200   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
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 = 8;
254   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
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 = 8;
305     lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
306     DataDescriptor_32 *m_data_32 = nullptr;
307     DataDescriptor_64 *m_data_64 = nullptr;
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_pair_type() {}
596 
597 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
598     ~NSDictionaryISyntheticFrontEnd() {
599   delete m_data_32;
600   m_data_32 = nullptr;
601   delete m_data_64;
602   m_data_64 = nullptr;
603 }
604 
605 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
606     GetIndexOfChildWithName(ConstString name) {
607   const char *item_name = name.GetCString();
608   uint32_t idx = ExtractIndexFromString(item_name);
609   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
610     return UINT32_MAX;
611   return idx;
612 }
613 
614 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
615     CalculateNumChildren() {
616   if (!m_data_32 && !m_data_64)
617     return 0;
618   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
619 }
620 
621 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
622   m_children.clear();
623   delete m_data_32;
624   m_data_32 = nullptr;
625   delete m_data_64;
626   m_data_64 = nullptr;
627   m_ptr_size = 0;
628   ValueObjectSP valobj_sp = m_backend.GetSP();
629   if (!valobj_sp)
630     return false;
631   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
632   Status error;
633   error.Clear();
634   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
635   if (!process_sp)
636     return false;
637   m_ptr_size = process_sp->GetAddressByteSize();
638   m_order = process_sp->GetByteOrder();
639   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
640   if (m_ptr_size == 4) {
641     m_data_32 = new DataDescriptor_32();
642     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
643                            error);
644   } else {
645     m_data_64 = new DataDescriptor_64();
646     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
647                            error);
648   }
649   if (error.Fail())
650     return false;
651   m_data_ptr = data_location + m_ptr_size;
652   return false;
653 }
654 
655 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
656     MightHaveChildren() {
657   return true;
658 }
659 
660 lldb::ValueObjectSP
661 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
662     size_t idx) {
663   uint32_t num_children = CalculateNumChildren();
664 
665   if (idx >= num_children)
666     return lldb::ValueObjectSP();
667 
668   if (m_children.empty()) {
669     // do the scan phase
670     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
671 
672     uint32_t tries = 0;
673     uint32_t test_idx = 0;
674 
675     while (tries < num_children) {
676       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
677       val_at_idx = key_at_idx + m_ptr_size;
678       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
679       if (!process_sp)
680         return lldb::ValueObjectSP();
681       Status error;
682       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
683       if (error.Fail())
684         return lldb::ValueObjectSP();
685       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
686       if (error.Fail())
687         return lldb::ValueObjectSP();
688 
689       test_idx++;
690 
691       if (!key_at_idx || !val_at_idx)
692         continue;
693       tries++;
694 
695       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
696                                              lldb::ValueObjectSP()};
697 
698       m_children.push_back(descriptor);
699     }
700   }
701 
702   if (idx >= m_children.size()) // should never happen
703     return lldb::ValueObjectSP();
704 
705   DictionaryItemDescriptor &dict_item = m_children[idx];
706   if (!dict_item.valobj_sp) {
707     if (!m_pair_type.IsValid()) {
708       TargetSP target_sp(m_backend.GetTargetSP());
709       if (!target_sp)
710         return ValueObjectSP();
711       m_pair_type = GetLLDBNSPairType(target_sp);
712     }
713     if (!m_pair_type.IsValid())
714       return ValueObjectSP();
715 
716     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
717 
718     if (m_ptr_size == 8) {
719       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
720       *data_ptr = dict_item.key_ptr;
721       *(data_ptr + 1) = dict_item.val_ptr;
722     } else {
723       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
724       *data_ptr = dict_item.key_ptr;
725       *(data_ptr + 1) = dict_item.val_ptr;
726     }
727 
728     StreamString idx_name;
729     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
730     DataExtractor data(buffer_sp, m_order, m_ptr_size);
731     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
732                                                     m_exe_ctx_ref, m_pair_type);
733   }
734   return dict_item.valobj_sp;
735 }
736 
737 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
738     NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
739     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(),
740       m_pair_type() {}
741 
742 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
743     GetIndexOfChildWithName(ConstString name) {
744   const char *item_name = name.GetCString();
745   const uint32_t idx = ExtractIndexFromString(item_name);
746   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
747     return UINT32_MAX;
748   return idx;
749 }
750 
751 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
752     CalculateNumChildren() {
753   if (!m_hashtable.IsValid())
754     return 0;
755   return m_hashtable.GetCount();
756 }
757 
758 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::Update() {
759   m_children.clear();
760   ValueObjectSP valobj_sp = m_backend.GetSP();
761   m_ptr_size = 0;
762   if (!valobj_sp)
763     return false;
764   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
765 
766   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
767   if (!process_sp)
768     return false;
769   m_ptr_size = process_sp->GetAddressByteSize();
770   m_order = process_sp->GetByteOrder();
771   return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref);
772 }
773 
774 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
775     MightHaveChildren() {
776   return true;
777 }
778 
779 lldb::ValueObjectSP
780 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex(
781     size_t idx) {
782   lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
783   lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
784 
785   const uint32_t num_children = CalculateNumChildren();
786 
787   if (idx >= num_children)
788     return lldb::ValueObjectSP();
789 
790   if (m_children.empty()) {
791     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
792     if (!process_sp)
793       return lldb::ValueObjectSP();
794 
795     Status error;
796     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
797 
798     uint32_t tries = 0;
799     uint32_t test_idx = 0;
800 
801     // Iterate over inferior memory, reading key/value pointers by shifting each
802     // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
803     // fails, otherwise, continue until the number of tries matches the number
804     // of childen.
805     while (tries < num_children) {
806       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
807       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
808 
809       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
810       if (error.Fail())
811         return lldb::ValueObjectSP();
812       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
813       if (error.Fail())
814         return lldb::ValueObjectSP();
815 
816       test_idx++;
817 
818       if (!key_at_idx || !val_at_idx)
819         continue;
820       tries++;
821 
822       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
823                                              lldb::ValueObjectSP()};
824 
825       m_children.push_back(descriptor);
826     }
827   }
828 
829   if (idx >= m_children.size()) // should never happen
830     return lldb::ValueObjectSP();
831 
832   DictionaryItemDescriptor &dict_item = m_children[idx];
833   if (!dict_item.valobj_sp) {
834     if (!m_pair_type.IsValid()) {
835       TargetSP target_sp(m_backend.GetTargetSP());
836       if (!target_sp)
837         return ValueObjectSP();
838       m_pair_type = GetLLDBNSPairType(target_sp);
839     }
840     if (!m_pair_type.IsValid())
841       return ValueObjectSP();
842 
843     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
844 
845     switch (m_ptr_size) {
846     case 0: // architecture has no clue - fail
847       return lldb::ValueObjectSP();
848     case 4: {
849       uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
850       *data_ptr = dict_item.key_ptr;
851       *(data_ptr + 1) = dict_item.val_ptr;
852     } break;
853     case 8: {
854       uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
855       *data_ptr = dict_item.key_ptr;
856       *(data_ptr + 1) = dict_item.val_ptr;
857     } break;
858     default:
859       lldbassert(false && "pointer size is not 4 nor 8");
860     }
861 
862     StreamString idx_name;
863     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
864     DataExtractor data(buffer_sp, m_order, m_ptr_size);
865     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
866                                                     m_exe_ctx_ref, m_pair_type);
867   }
868   return dict_item.valobj_sp;
869 }
870 
871 lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
872     NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
873     : SyntheticChildrenFrontEnd(*valobj_sp) {}
874 
875 size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
876     GetIndexOfChildWithName(ConstString name) {
877   const char *item_name = name.GetCString();
878   uint32_t idx = ExtractIndexFromString(item_name);
879   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
880     return UINT32_MAX;
881   return idx;
882 }
883 
884 size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
885     CalculateNumChildren() {
886   return m_size;
887 }
888 
889 bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::Update() {
890   ValueObjectSP valobj_sp = m_backend.GetSP();
891   if (!valobj_sp)
892     return false;
893   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
894   Status error;
895   error.Clear();
896   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
897   if (!process_sp)
898     return false;
899   m_ptr_size = process_sp->GetAddressByteSize();
900   m_order = process_sp->GetByteOrder();
901   uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
902   m_size = process_sp->ReadUnsignedIntegerFromMemory(
903       valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
904   if (error.Fail())
905     return false;
906   m_keys_ptr =
907       process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
908   if (error.Fail())
909     return false;
910   m_objects_ptr =
911       process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
912   return !error.Fail();
913 }
914 
915 bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
916     MightHaveChildren() {
917   return true;
918 }
919 
920 lldb::ValueObjectSP lldb_private::formatters::
921     NSConstantDictionarySyntheticFrontEnd::GetChildAtIndex(size_t idx) {
922   uint32_t num_children = CalculateNumChildren();
923 
924   if (idx >= num_children)
925     return lldb::ValueObjectSP();
926 
927   if (m_children.empty()) {
928     // do the scan phase
929     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
930     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
931     if (!process_sp)
932       return lldb::ValueObjectSP();
933 
934     for (unsigned int child = 0; child < num_children; ++child) {
935       Status error;
936       key_at_idx = process_sp->ReadPointerFromMemory(
937           m_keys_ptr + child * m_ptr_size, error);
938       if (error.Fail())
939         return lldb::ValueObjectSP();
940       val_at_idx = process_sp->ReadPointerFromMemory(
941           m_objects_ptr + child * m_ptr_size, error);
942       if (error.Fail())
943         return lldb::ValueObjectSP();
944       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
945                                              lldb::ValueObjectSP()};
946       m_children.push_back(descriptor);
947     }
948   }
949 
950   if (idx >= m_children.size()) // should never happen
951     return lldb::ValueObjectSP();
952 
953   DictionaryItemDescriptor &dict_item = m_children[idx];
954   if (!dict_item.valobj_sp) {
955     if (!m_pair_type.IsValid()) {
956       TargetSP target_sp(m_backend.GetTargetSP());
957       if (!target_sp)
958         return ValueObjectSP();
959       m_pair_type = GetLLDBNSPairType(target_sp);
960     }
961     if (!m_pair_type.IsValid())
962       return ValueObjectSP();
963 
964     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
965 
966     if (m_ptr_size == 8) {
967       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
968       *data_ptr = dict_item.key_ptr;
969       *(data_ptr + 1) = dict_item.val_ptr;
970     } else {
971       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
972       *data_ptr = dict_item.key_ptr;
973       *(data_ptr + 1) = dict_item.val_ptr;
974     }
975 
976     StreamString idx_name;
977     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
978     DataExtractor data(buffer_sp, m_order, m_ptr_size);
979     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
980                                                     m_exe_ctx_ref, m_pair_type);
981   }
982   return dict_item.valobj_sp;
983 }
984 
985 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
986     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
987     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
988 
989 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
990     GetIndexOfChildWithName(ConstString name) {
991   static const ConstString g_zero("[0]");
992   return name == g_zero ? 0 : UINT32_MAX;
993 }
994 
995 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
996     CalculateNumChildren() {
997   return 1;
998 }
999 
1000 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
1001   m_pair.reset();
1002   return false;
1003 }
1004 
1005 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
1006     MightHaveChildren() {
1007   return true;
1008 }
1009 
1010 lldb::ValueObjectSP
1011 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
1012     size_t idx) {
1013   if (idx != 0)
1014     return lldb::ValueObjectSP();
1015 
1016   if (m_pair.get())
1017     return m_pair;
1018 
1019   auto process_sp(m_backend.GetProcessSP());
1020   if (!process_sp)
1021     return nullptr;
1022 
1023   auto ptr_size = process_sp->GetAddressByteSize();
1024 
1025   lldb::addr_t key_ptr =
1026       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
1027   lldb::addr_t value_ptr = key_ptr + ptr_size;
1028 
1029   Status error;
1030 
1031   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
1032   if (error.Fail())
1033     return nullptr;
1034   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
1035   if (error.Fail())
1036     return nullptr;
1037 
1038   auto pair_type =
1039       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
1040 
1041   WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1042 
1043   if (ptr_size == 8) {
1044     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1045     *data_ptr = key_at_idx;
1046     *(data_ptr + 1) = value_at_idx;
1047   } else {
1048     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1049     *data_ptr = key_at_idx;
1050     *(data_ptr + 1) = value_at_idx;
1051   }
1052 
1053   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1054   m_pair = CreateValueObjectFromData(
1055       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1056 
1057   return m_pair;
1058 }
1059 
1060 template <typename D32, typename D64>
1061 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32, D64>::
1062     GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1063     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
1064       m_data_32(nullptr), m_data_64(nullptr), m_pair_type() {}
1065 
1066 template <typename D32, typename D64>
1067 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
1068     ~GenericNSDictionaryMSyntheticFrontEnd<D32,D64>() {
1069   delete m_data_32;
1070   m_data_32 = nullptr;
1071   delete m_data_64;
1072   m_data_64 = nullptr;
1073 }
1074 
1075 template <typename D32, typename D64>
1076 size_t lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
1077     D32, D64>::GetIndexOfChildWithName(ConstString name) {
1078   const char *item_name = name.GetCString();
1079   uint32_t idx = ExtractIndexFromString(item_name);
1080   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
1081     return UINT32_MAX;
1082   return idx;
1083 }
1084 
1085 template <typename D32, typename D64>
1086 size_t
1087 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
1088   if (!m_data_32 && !m_data_64)
1089     return 0;
1090   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1091 }
1092 
1093 template <typename D32, typename D64>
1094 bool
1095 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
1096   Update() {
1097   m_children.clear();
1098   ValueObjectSP valobj_sp = m_backend.GetSP();
1099   m_ptr_size = 0;
1100   delete m_data_32;
1101   m_data_32 = nullptr;
1102   delete m_data_64;
1103   m_data_64 = nullptr;
1104   if (!valobj_sp)
1105     return false;
1106   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1107   Status error;
1108   error.Clear();
1109   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1110   if (!process_sp)
1111     return false;
1112   m_ptr_size = process_sp->GetAddressByteSize();
1113   m_order = process_sp->GetByteOrder();
1114   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1115   if (m_ptr_size == 4) {
1116     m_data_32 = new D32();
1117     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1118                            error);
1119   } else {
1120     m_data_64 = new D64();
1121     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1122                            error);
1123   }
1124   if (error.Fail())
1125     return false;
1126   return true;
1127 }
1128 
1129 template <typename D32, typename D64>
1130 bool
1131 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
1132     MightHaveChildren() {
1133   return true;
1134 }
1135 
1136 template <typename D32, typename D64>
1137 lldb::ValueObjectSP
1138 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
1139     D32, D64>::GetChildAtIndex(size_t idx) {
1140   lldb::addr_t m_keys_ptr;
1141   lldb::addr_t m_values_ptr;
1142   if (m_data_32) {
1143     uint32_t size = m_data_32->GetSize();
1144     m_keys_ptr = m_data_32->_buffer;
1145     m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1146   } else {
1147     uint32_t size = m_data_64->GetSize();
1148     m_keys_ptr = m_data_64->_buffer;
1149     m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1150   }
1151 
1152   uint32_t num_children = CalculateNumChildren();
1153 
1154   if (idx >= num_children)
1155     return lldb::ValueObjectSP();
1156 
1157   if (m_children.empty()) {
1158     // do the scan phase
1159     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1160 
1161     uint32_t tries = 0;
1162     uint32_t test_idx = 0;
1163 
1164     while (tries < num_children) {
1165       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1166       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1167       ;
1168       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1169       if (!process_sp)
1170         return lldb::ValueObjectSP();
1171       Status error;
1172       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1173       if (error.Fail())
1174         return lldb::ValueObjectSP();
1175       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1176       if (error.Fail())
1177         return lldb::ValueObjectSP();
1178 
1179       test_idx++;
1180 
1181       if (!key_at_idx || !val_at_idx)
1182         continue;
1183       tries++;
1184 
1185       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1186                                              lldb::ValueObjectSP()};
1187 
1188       m_children.push_back(descriptor);
1189     }
1190   }
1191 
1192   if (idx >= m_children.size()) // should never happen
1193     return lldb::ValueObjectSP();
1194 
1195   DictionaryItemDescriptor &dict_item = m_children[idx];
1196   if (!dict_item.valobj_sp) {
1197     if (!m_pair_type.IsValid()) {
1198       TargetSP target_sp(m_backend.GetTargetSP());
1199       if (!target_sp)
1200         return ValueObjectSP();
1201       m_pair_type = GetLLDBNSPairType(target_sp);
1202     }
1203     if (!m_pair_type.IsValid())
1204       return ValueObjectSP();
1205 
1206     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1207 
1208     if (m_ptr_size == 8) {
1209       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1210       *data_ptr = dict_item.key_ptr;
1211       *(data_ptr + 1) = dict_item.val_ptr;
1212     } else {
1213       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1214       *data_ptr = dict_item.key_ptr;
1215       *(data_ptr + 1) = dict_item.val_ptr;
1216     }
1217 
1218     StreamString idx_name;
1219     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1220     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1221     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1222                                                     m_exe_ctx_ref, m_pair_type);
1223   }
1224   return dict_item.valobj_sp;
1225 }
1226 
1227 lldb_private::formatters::Foundation1100::NSDictionaryMSyntheticFrontEnd::
1228     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1229     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
1230 
1231 lldb_private::formatters::Foundation1100::
1232   NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
1233   delete m_data_32;
1234   m_data_32 = nullptr;
1235   delete m_data_64;
1236   m_data_64 = nullptr;
1237 }
1238 
1239 size_t
1240 lldb_private::formatters::Foundation1100::
1241   NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
1242   const char *item_name = name.GetCString();
1243   uint32_t idx = ExtractIndexFromString(item_name);
1244   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
1245     return UINT32_MAX;
1246   return idx;
1247 }
1248 
1249 size_t
1250 lldb_private::formatters::Foundation1100::
1251   NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
1252   if (!m_data_32 && !m_data_64)
1253     return 0;
1254   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1255 }
1256 
1257 bool
1258 lldb_private::formatters::Foundation1100::
1259   NSDictionaryMSyntheticFrontEnd::Update() {
1260   m_children.clear();
1261   ValueObjectSP valobj_sp = m_backend.GetSP();
1262   m_ptr_size = 0;
1263   delete m_data_32;
1264   m_data_32 = nullptr;
1265   delete m_data_64;
1266   m_data_64 = nullptr;
1267   if (!valobj_sp)
1268     return false;
1269   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1270   Status error;
1271   error.Clear();
1272   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1273   if (!process_sp)
1274     return false;
1275   m_ptr_size = process_sp->GetAddressByteSize();
1276   m_order = process_sp->GetByteOrder();
1277   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1278   if (m_ptr_size == 4) {
1279     m_data_32 = new DataDescriptor_32();
1280     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1281                            error);
1282   } else {
1283     m_data_64 = new DataDescriptor_64();
1284     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1285                            error);
1286   }
1287   if (error.Fail())
1288     return false;
1289   return false;
1290 }
1291 
1292 bool
1293 lldb_private::formatters::Foundation1100::
1294   NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
1295   return true;
1296 }
1297 
1298 lldb::ValueObjectSP
1299 lldb_private::formatters::Foundation1100::
1300   NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
1301   lldb::addr_t m_keys_ptr =
1302       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1303   lldb::addr_t m_values_ptr =
1304       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1305 
1306   uint32_t num_children = CalculateNumChildren();
1307 
1308   if (idx >= num_children)
1309     return lldb::ValueObjectSP();
1310 
1311   if (m_children.empty()) {
1312     // do the scan phase
1313     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1314 
1315     uint32_t tries = 0;
1316     uint32_t test_idx = 0;
1317 
1318     while (tries < num_children) {
1319       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1320       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1321       ;
1322       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1323       if (!process_sp)
1324         return lldb::ValueObjectSP();
1325       Status error;
1326       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1327       if (error.Fail())
1328         return lldb::ValueObjectSP();
1329       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1330       if (error.Fail())
1331         return lldb::ValueObjectSP();
1332 
1333       test_idx++;
1334 
1335       if (!key_at_idx || !val_at_idx)
1336         continue;
1337       tries++;
1338 
1339       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1340                                              lldb::ValueObjectSP()};
1341 
1342       m_children.push_back(descriptor);
1343     }
1344   }
1345 
1346   if (idx >= m_children.size()) // should never happen
1347     return lldb::ValueObjectSP();
1348 
1349   DictionaryItemDescriptor &dict_item = m_children[idx];
1350   if (!dict_item.valobj_sp) {
1351     if (!m_pair_type.IsValid()) {
1352       TargetSP target_sp(m_backend.GetTargetSP());
1353       if (!target_sp)
1354         return ValueObjectSP();
1355       m_pair_type = GetLLDBNSPairType(target_sp);
1356     }
1357     if (!m_pair_type.IsValid())
1358       return ValueObjectSP();
1359 
1360     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1361 
1362     if (m_ptr_size == 8) {
1363       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1364       *data_ptr = dict_item.key_ptr;
1365       *(data_ptr + 1) = dict_item.val_ptr;
1366     } else {
1367       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1368       *data_ptr = dict_item.key_ptr;
1369       *(data_ptr + 1) = dict_item.val_ptr;
1370     }
1371 
1372     StreamString idx_name;
1373     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1374     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1375     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1376                                                     m_exe_ctx_ref, m_pair_type);
1377   }
1378   return dict_item.valobj_sp;
1379 }
1380 
1381 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1382     ValueObject &, Stream &, const TypeSummaryOptions &);
1383 
1384 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1385     ValueObject &, Stream &, const TypeSummaryOptions &);
1386