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