1 //===-- NSDictionary.cpp ----------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 #include <mutex>
13 
14 // Other libraries and framework includes
15 #include "clang/AST/DeclCXX.h"
16 
17 // Project includes
18 #include "NSDictionary.h"
19 
20 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
21 
22 #include "lldb/Core/ValueObject.h"
23 #include "lldb/Core/ValueObjectConstResult.h"
24 #include "lldb/DataFormatters/FormattersHelpers.h"
25 #include "lldb/Symbol/ClangASTContext.h"
26 #include "lldb/Target/Language.h"
27 #include "lldb/Target/ObjCLanguageRuntime.h"
28 #include "lldb/Target/StackFrame.h"
29 #include "lldb/Target/Target.h"
30 #include "lldb/Utility/DataBufferHeap.h"
31 #include "lldb/Utility/Endian.h"
32 #include "lldb/Utility/Status.h"
33 #include "lldb/Utility/Stream.h"
34 
35 using namespace lldb;
36 using namespace lldb_private;
37 using namespace lldb_private::formatters;
38 
39 NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
40     ConstString p)
41     : m_prefix(p) {}
42 
43 bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
44     ConstString class_name) {
45   return class_name.GetStringRef().startswith(m_prefix.GetStringRef());
46 }
47 
48 NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
49     : m_name(n) {}
50 
51 bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
52     ConstString class_name) {
53   return (class_name == m_name);
54 }
55 
56 NSDictionary_Additionals::AdditionalFormatters<
57     CXXFunctionSummaryFormat::Callback> &
58 NSDictionary_Additionals::GetAdditionalSummaries() {
59   static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
60   return g_map;
61 }
62 
63 NSDictionary_Additionals::AdditionalFormatters<
64     CXXSyntheticChildren::CreateFrontEndCallback> &
65 NSDictionary_Additionals::GetAdditionalSynthetics() {
66   static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
67       g_map;
68   return g_map;
69 }
70 
71 static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
72   CompilerType compiler_type;
73 
74   ClangASTContext *target_ast_context = target_sp->GetScratchClangASTContext();
75 
76   if (target_ast_context) {
77     ConstString g___lldb_autogen_nspair("__lldb_autogen_nspair");
78 
79     compiler_type =
80         target_ast_context->GetTypeForIdentifier<clang::CXXRecordDecl>(
81             g___lldb_autogen_nspair);
82 
83     if (!compiler_type) {
84       compiler_type = target_ast_context->CreateRecordType(
85           nullptr, lldb::eAccessPublic, g___lldb_autogen_nspair.GetCString(),
86           clang::TTK_Struct, lldb::eLanguageTypeC);
87 
88       if (compiler_type) {
89         ClangASTContext::StartTagDeclarationDefinition(compiler_type);
90         CompilerType id_compiler_type =
91             target_ast_context->GetBasicType(eBasicTypeObjCID);
92         ClangASTContext::AddFieldToRecordType(
93             compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
94         ClangASTContext::AddFieldToRecordType(
95             compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
96         ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
97       }
98     }
99   }
100   return compiler_type;
101 }
102 
103 namespace lldb_private {
104 namespace formatters {
105 class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
106 public:
107   NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
108 
109   ~NSDictionaryISyntheticFrontEnd() override;
110 
111   size_t CalculateNumChildren() override;
112 
113   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
114 
115   bool Update() override;
116 
117   bool MightHaveChildren() override;
118 
119   size_t GetIndexOfChildWithName(const ConstString &name) override;
120 
121 private:
122   struct DataDescriptor_32 {
123     uint32_t _used : 26;
124     uint32_t _szidx : 6;
125   };
126 
127   struct DataDescriptor_64 {
128     uint64_t _used : 58;
129     uint32_t _szidx : 6;
130   };
131 
132   struct DictionaryItemDescriptor {
133     lldb::addr_t key_ptr;
134     lldb::addr_t val_ptr;
135     lldb::ValueObjectSP valobj_sp;
136   };
137 
138   ExecutionContextRef m_exe_ctx_ref;
139   uint8_t m_ptr_size;
140   lldb::ByteOrder m_order;
141   DataDescriptor_32 *m_data_32;
142   DataDescriptor_64 *m_data_64;
143   lldb::addr_t m_data_ptr;
144   CompilerType m_pair_type;
145   std::vector<DictionaryItemDescriptor> m_children;
146 };
147 
148 class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
149 public:
150   NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
151 
152   ~NSDictionary1SyntheticFrontEnd() override = default;
153 
154   size_t CalculateNumChildren() override;
155 
156   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
157 
158   bool Update() override;
159 
160   bool MightHaveChildren() override;
161 
162   size_t GetIndexOfChildWithName(const ConstString &name) override;
163 
164 private:
165   ValueObjectSP m_pair;
166 };
167 
168 template <typename D32, typename D64>
169 class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
170 public:
171   GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
172 
173   ~GenericNSDictionaryMSyntheticFrontEnd() override;
174 
175   size_t CalculateNumChildren() override;
176 
177   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
178 
179   bool Update() override;
180 
181   bool MightHaveChildren() override;
182 
183   size_t GetIndexOfChildWithName(const ConstString &name) override;
184 
185 private:
186   struct DictionaryItemDescriptor {
187     lldb::addr_t key_ptr;
188     lldb::addr_t val_ptr;
189     lldb::ValueObjectSP valobj_sp;
190   };
191 
192   ExecutionContextRef m_exe_ctx_ref;
193   uint8_t m_ptr_size;
194   lldb::ByteOrder m_order;
195   D32 *m_data_32;
196   D64 *m_data_64;
197   CompilerType m_pair_type;
198   std::vector<DictionaryItemDescriptor> m_children;
199 };
200 
201 namespace Foundation1100 {
202   class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
203   public:
204     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
205 
206     ~NSDictionaryMSyntheticFrontEnd() override;
207 
208     size_t CalculateNumChildren() override;
209 
210     lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
211 
212     bool Update() override;
213 
214     bool MightHaveChildren() override;
215 
216     size_t GetIndexOfChildWithName(const ConstString &name) override;
217 
218   private:
219     struct DataDescriptor_32 {
220       uint32_t _used : 26;
221       uint32_t _kvo : 1;
222       uint32_t _size;
223       uint32_t _mutations;
224       uint32_t _objs_addr;
225       uint32_t _keys_addr;
226     };
227 
228     struct DataDescriptor_64 {
229       uint64_t _used : 58;
230       uint32_t _kvo : 1;
231       uint64_t _size;
232       uint64_t _mutations;
233       uint64_t _objs_addr;
234       uint64_t _keys_addr;
235     };
236 
237     struct DictionaryItemDescriptor {
238       lldb::addr_t key_ptr;
239       lldb::addr_t val_ptr;
240       lldb::ValueObjectSP valobj_sp;
241     };
242 
243     ExecutionContextRef m_exe_ctx_ref;
244     uint8_t m_ptr_size;
245     lldb::ByteOrder m_order;
246     DataDescriptor_32 *m_data_32;
247     DataDescriptor_64 *m_data_64;
248     CompilerType m_pair_type;
249     std::vector<DictionaryItemDescriptor> m_children;
250   };
251 }
252 
253 namespace Foundation1428 {
254   struct DataDescriptor_32 {
255     uint32_t _used : 26;
256     uint32_t _kvo : 1;
257     uint32_t _size;
258     uint32_t _buffer;
259     uint64_t GetSize() { return _size; }
260   };
261 
262   struct DataDescriptor_64 {
263     uint64_t _used : 58;
264     uint32_t _kvo : 1;
265     uint64_t _size;
266     uint64_t _buffer;
267     uint64_t GetSize() { return _size; }
268   };
269 
270 
271 
272   using NSDictionaryMSyntheticFrontEnd =
273     GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
274 }
275 
276 namespace Foundation1437 {
277   static const uint64_t NSDictionaryCapacities[] = {
278       0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
279       2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
280       214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
281       6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
282       111638519, 180634607, 292272623, 472907251
283   };
284 
285   static const size_t NSDictionaryNumSizeBuckets = sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
286 
287   struct DataDescriptor_32 {
288     uint32_t _buffer;
289     union {
290       struct {
291         uint32_t _mutations;
292       };
293       struct {
294         uint32_t _muts;
295         uint32_t _used:25;
296         uint32_t _kvo:1;
297         uint32_t _szidx:6;
298       };
299     };
300 
301     uint64_t GetSize() {
302       return (_szidx) >= NSDictionaryNumSizeBuckets ?
303           0 : NSDictionaryCapacities[_szidx];
304     }
305   };
306 
307   struct DataDescriptor_64 {
308     uint64_t _buffer;
309     union {
310       struct {
311         uint64_t _mutations;
312       };
313       struct {
314         uint32_t _muts;
315         uint32_t _used:25;
316         uint32_t _kvo:1;
317         uint32_t _szidx:6;
318       };
319     };
320 
321     uint64_t GetSize() {
322       return (_szidx) >= NSDictionaryNumSizeBuckets ?
323           0 : NSDictionaryCapacities[_szidx];
324     }
325   };
326 
327   using NSDictionaryMSyntheticFrontEnd =
328     GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
329 
330   template <typename DD>
331   uint64_t
332   __NSDictionaryMSize_Impl(lldb_private::Process &process,
333                            lldb::addr_t valobj_addr, Status &error) {
334     const lldb::addr_t start_of_descriptor =
335         valobj_addr + process.GetAddressByteSize();
336     DD descriptor = DD();
337     process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
338                        error);
339     if (error.Fail()) {
340       return 0;
341     }
342     return descriptor._used;
343   }
344 
345   uint64_t
346   __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
347                Status &error) {
348     if (process.GetAddressByteSize() == 4) {
349       return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
350                                                          error);
351     } else {
352       return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
353                                                          error);
354     }
355   }
356 
357 }
358 } // namespace formatters
359 } // namespace lldb_private
360 
361 template <bool name_entries>
362 bool lldb_private::formatters::NSDictionarySummaryProvider(
363     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
364   static ConstString g_TypeHint("NSDictionary");
365   ProcessSP process_sp = valobj.GetProcessSP();
366   if (!process_sp)
367     return false;
368 
369   ObjCLanguageRuntime *runtime =
370       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
371           lldb::eLanguageTypeObjC);
372 
373   if (!runtime)
374     return false;
375 
376   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
377       runtime->GetClassDescriptor(valobj));
378 
379   if (!descriptor || !descriptor->IsValid())
380     return false;
381 
382   uint32_t ptr_size = process_sp->GetAddressByteSize();
383   bool is_64bit = (ptr_size == 8);
384 
385   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
386 
387   if (!valobj_addr)
388     return false;
389 
390   uint64_t value = 0;
391 
392   ConstString class_name(descriptor->GetClassName());
393 
394   static const ConstString g_DictionaryI("__NSDictionaryI");
395   static const ConstString g_DictionaryM("__NSDictionaryM");
396   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
397   static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
398   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
399   static const ConstString g_Dictionary0("__NSDictionary0");
400 
401   if (class_name.IsEmpty())
402     return false;
403 
404   if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
405     Status error;
406     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
407                                                       ptr_size, 0, error);
408     if (error.Fail())
409       return false;
410     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
411   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy) {
412     AppleObjCRuntime *apple_runtime =
413     llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
414     Status error;
415     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
416       value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
417                                                   error);
418     } else {
419       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
420                                                         ptr_size, 0, error);
421       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
422     }
423     if (error.Fail())
424       return false;
425   } else if (class_name == g_Dictionary1) {
426     value = 1;
427   } else if (class_name == g_Dictionary0) {
428     value = 0;
429   }
430   else {
431     auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
432     for (auto &candidate : map) {
433       if (candidate.first && candidate.first->Match(class_name))
434         return candidate.second(valobj, stream, options);
435     }
436     return false;
437   }
438 
439   std::string prefix, suffix;
440   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
441     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
442                                             suffix)) {
443       prefix.clear();
444       suffix.clear();
445     }
446   }
447 
448   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
449                 value == 1 ? "" : "s", suffix.c_str());
450   return true;
451 }
452 
453 SyntheticChildrenFrontEnd *
454 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
455     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
456   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
457   if (!process_sp)
458     return nullptr;
459   AppleObjCRuntime *runtime =
460       llvm::dyn_cast_or_null<AppleObjCRuntime>(process_sp->GetObjCLanguageRuntime());
461   if (!runtime)
462     return nullptr;
463 
464   CompilerType valobj_type(valobj_sp->GetCompilerType());
465   Flags flags(valobj_type.GetTypeInfo());
466 
467   if (flags.IsClear(eTypeIsPointer)) {
468     Status error;
469     valobj_sp = valobj_sp->AddressOf(error);
470     if (error.Fail() || !valobj_sp)
471       return nullptr;
472   }
473 
474   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
475       runtime->GetClassDescriptor(*valobj_sp));
476 
477   if (!descriptor || !descriptor->IsValid())
478     return nullptr;
479 
480   ConstString class_name(descriptor->GetClassName());
481 
482   static const ConstString g_DictionaryI("__NSDictionaryI");
483   static const ConstString g_DictionaryM("__NSDictionaryM");
484   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
485   static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
486   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
487   static const ConstString g_Dictionary0("__NSDictionary0");
488 
489   if (class_name.IsEmpty())
490     return nullptr;
491 
492   if (class_name == g_DictionaryI) {
493     return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
494   } else if (class_name == g_DictionaryM) {
495     if (runtime->GetFoundationVersion() >= 1437) {
496       return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
497     } else if (runtime->GetFoundationVersion() >= 1428) {
498       return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
499     } else {
500       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
501     }
502   } else if (class_name == g_DictionaryMLegacy) {
503       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
504   } else if (class_name == g_Dictionary1) {
505     return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
506   } else {
507     auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
508     for (auto &candidate : map) {
509       if (candidate.first && candidate.first->Match((class_name)))
510         return candidate.second(synth, valobj_sp);
511     }
512   }
513 
514   return nullptr;
515 }
516 
517 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
518     NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
519     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
520       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
521       m_pair_type() {}
522 
523 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
524     ~NSDictionaryISyntheticFrontEnd() {
525   delete m_data_32;
526   m_data_32 = nullptr;
527   delete m_data_64;
528   m_data_64 = nullptr;
529 }
530 
531 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
532     GetIndexOfChildWithName(const ConstString &name) {
533   const char *item_name = name.GetCString();
534   uint32_t idx = ExtractIndexFromString(item_name);
535   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
536     return UINT32_MAX;
537   return idx;
538 }
539 
540 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
541     CalculateNumChildren() {
542   if (!m_data_32 && !m_data_64)
543     return 0;
544   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
545 }
546 
547 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
548   m_children.clear();
549   delete m_data_32;
550   m_data_32 = nullptr;
551   delete m_data_64;
552   m_data_64 = nullptr;
553   m_ptr_size = 0;
554   ValueObjectSP valobj_sp = m_backend.GetSP();
555   if (!valobj_sp)
556     return false;
557   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
558   Status error;
559   error.Clear();
560   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
561   if (!process_sp)
562     return false;
563   m_ptr_size = process_sp->GetAddressByteSize();
564   m_order = process_sp->GetByteOrder();
565   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
566   if (m_ptr_size == 4) {
567     m_data_32 = new DataDescriptor_32();
568     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
569                            error);
570   } else {
571     m_data_64 = new DataDescriptor_64();
572     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
573                            error);
574   }
575   if (error.Fail())
576     return false;
577   m_data_ptr = data_location + m_ptr_size;
578   return false;
579 }
580 
581 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
582     MightHaveChildren() {
583   return true;
584 }
585 
586 lldb::ValueObjectSP
587 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
588     size_t idx) {
589   uint32_t num_children = CalculateNumChildren();
590 
591   if (idx >= num_children)
592     return lldb::ValueObjectSP();
593 
594   if (m_children.empty()) {
595     // do the scan phase
596     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
597 
598     uint32_t tries = 0;
599     uint32_t test_idx = 0;
600 
601     while (tries < num_children) {
602       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
603       val_at_idx = key_at_idx + m_ptr_size;
604       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
605       if (!process_sp)
606         return lldb::ValueObjectSP();
607       Status error;
608       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
609       if (error.Fail())
610         return lldb::ValueObjectSP();
611       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
612       if (error.Fail())
613         return lldb::ValueObjectSP();
614 
615       test_idx++;
616 
617       if (!key_at_idx || !val_at_idx)
618         continue;
619       tries++;
620 
621       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
622                                              lldb::ValueObjectSP()};
623 
624       m_children.push_back(descriptor);
625     }
626   }
627 
628   if (idx >= m_children.size()) // should never happen
629     return lldb::ValueObjectSP();
630 
631   DictionaryItemDescriptor &dict_item = m_children[idx];
632   if (!dict_item.valobj_sp) {
633     if (!m_pair_type.IsValid()) {
634       TargetSP target_sp(m_backend.GetTargetSP());
635       if (!target_sp)
636         return ValueObjectSP();
637       m_pair_type = GetLLDBNSPairType(target_sp);
638     }
639     if (!m_pair_type.IsValid())
640       return ValueObjectSP();
641 
642     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
643 
644     if (m_ptr_size == 8) {
645       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
646       *data_ptr = dict_item.key_ptr;
647       *(data_ptr + 1) = dict_item.val_ptr;
648     } else {
649       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
650       *data_ptr = dict_item.key_ptr;
651       *(data_ptr + 1) = dict_item.val_ptr;
652     }
653 
654     StreamString idx_name;
655     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
656     DataExtractor data(buffer_sp, m_order, m_ptr_size);
657     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
658                                                     m_exe_ctx_ref, m_pair_type);
659   }
660   return dict_item.valobj_sp;
661 }
662 
663 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
664     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
665     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
666 
667 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
668     GetIndexOfChildWithName(const ConstString &name) {
669   static const ConstString g_zero("[0]");
670 
671   if (name == g_zero)
672     return 0;
673 
674   return UINT32_MAX;
675 }
676 
677 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
678     CalculateNumChildren() {
679   return 1;
680 }
681 
682 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
683   m_pair.reset();
684   return false;
685 }
686 
687 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
688     MightHaveChildren() {
689   return true;
690 }
691 
692 lldb::ValueObjectSP
693 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
694     size_t idx) {
695   if (idx != 0)
696     return lldb::ValueObjectSP();
697 
698   if (m_pair.get())
699     return m_pair;
700 
701   auto process_sp(m_backend.GetProcessSP());
702   if (!process_sp)
703     return nullptr;
704 
705   auto ptr_size = process_sp->GetAddressByteSize();
706 
707   lldb::addr_t key_ptr =
708       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
709   lldb::addr_t value_ptr = key_ptr + ptr_size;
710 
711   Status error;
712 
713   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
714   if (error.Fail())
715     return nullptr;
716   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
717   if (error.Fail())
718     return nullptr;
719 
720   auto pair_type =
721       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
722 
723   DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
724 
725   if (ptr_size == 8) {
726     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
727     *data_ptr = key_at_idx;
728     *(data_ptr + 1) = value_at_idx;
729   } else {
730     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
731     *data_ptr = key_at_idx;
732     *(data_ptr + 1) = value_at_idx;
733   }
734 
735   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
736   m_pair = CreateValueObjectFromData(
737       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
738 
739   return m_pair;
740 }
741 
742 template <typename D32, typename D64>
743 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
744     GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
745     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
746       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
747       m_pair_type() {}
748 
749 template <typename D32, typename D64>
750 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
751     ~GenericNSDictionaryMSyntheticFrontEnd() {
752   delete m_data_32;
753   m_data_32 = nullptr;
754   delete m_data_64;
755   m_data_64 = nullptr;
756 }
757 
758 template <typename D32, typename D64>
759 size_t
760 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::    GetIndexOfChildWithName(const ConstString &name) {
761   const char *item_name = name.GetCString();
762   uint32_t idx = ExtractIndexFromString(item_name);
763   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
764     return UINT32_MAX;
765   return idx;
766 }
767 
768 template <typename D32, typename D64>
769 size_t
770 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
771   if (!m_data_32 && !m_data_64)
772     return 0;
773   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
774 }
775 
776 template <typename D32, typename D64>
777 bool
778 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
779   Update() {
780   m_children.clear();
781   ValueObjectSP valobj_sp = m_backend.GetSP();
782   m_ptr_size = 0;
783   delete m_data_32;
784   m_data_32 = nullptr;
785   delete m_data_64;
786   m_data_64 = nullptr;
787   if (!valobj_sp)
788     return false;
789   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
790   Status error;
791   error.Clear();
792   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
793   if (!process_sp)
794     return false;
795   m_ptr_size = process_sp->GetAddressByteSize();
796   m_order = process_sp->GetByteOrder();
797   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
798   if (m_ptr_size == 4) {
799     m_data_32 = new D32();
800     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
801                            error);
802   } else {
803     m_data_64 = new D64();
804     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
805                            error);
806   }
807   if (error.Fail())
808     return false;
809   return false;
810 }
811 
812 template <typename D32, typename D64>
813 bool
814 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
815     MightHaveChildren() {
816   return true;
817 }
818 
819 template <typename D32, typename D64>
820 lldb::ValueObjectSP
821 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
822     GetChildAtIndex(
823     size_t idx) {
824   lldb::addr_t m_keys_ptr;
825   lldb::addr_t m_values_ptr;
826   if (m_data_32) {
827     uint32_t size = m_data_32->GetSize();
828     m_keys_ptr = m_data_32->_buffer;
829     m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
830   } else {
831     uint32_t size = m_data_64->GetSize();
832     m_keys_ptr = m_data_64->_buffer;
833     m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
834   }
835 
836   uint32_t num_children = CalculateNumChildren();
837 
838   if (idx >= num_children)
839     return lldb::ValueObjectSP();
840 
841   if (m_children.empty()) {
842     // do the scan phase
843     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
844 
845     uint32_t tries = 0;
846     uint32_t test_idx = 0;
847 
848     while (tries < num_children) {
849       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
850       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
851       ;
852       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
853       if (!process_sp)
854         return lldb::ValueObjectSP();
855       Status error;
856       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
857       if (error.Fail())
858         return lldb::ValueObjectSP();
859       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
860       if (error.Fail())
861         return lldb::ValueObjectSP();
862 
863       test_idx++;
864 
865       if (!key_at_idx || !val_at_idx)
866         continue;
867       tries++;
868 
869       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
870                                              lldb::ValueObjectSP()};
871 
872       m_children.push_back(descriptor);
873     }
874   }
875 
876   if (idx >= m_children.size()) // should never happen
877     return lldb::ValueObjectSP();
878 
879   DictionaryItemDescriptor &dict_item = m_children[idx];
880   if (!dict_item.valobj_sp) {
881     if (!m_pair_type.IsValid()) {
882       TargetSP target_sp(m_backend.GetTargetSP());
883       if (!target_sp)
884         return ValueObjectSP();
885       m_pair_type = GetLLDBNSPairType(target_sp);
886     }
887     if (!m_pair_type.IsValid())
888       return ValueObjectSP();
889 
890     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
891 
892     if (m_ptr_size == 8) {
893       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
894       *data_ptr = dict_item.key_ptr;
895       *(data_ptr + 1) = dict_item.val_ptr;
896     } else {
897       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
898       *data_ptr = dict_item.key_ptr;
899       *(data_ptr + 1) = dict_item.val_ptr;
900     }
901 
902     StreamString idx_name;
903     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
904     DataExtractor data(buffer_sp, m_order, m_ptr_size);
905     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
906                                                     m_exe_ctx_ref, m_pair_type);
907   }
908   return dict_item.valobj_sp;
909 }
910 
911 
912 lldb_private::formatters::Foundation1100::
913   NSDictionaryMSyntheticFrontEnd::
914     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
915     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
916       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
917       m_pair_type() {}
918 
919 lldb_private::formatters::Foundation1100::
920   NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
921   delete m_data_32;
922   m_data_32 = nullptr;
923   delete m_data_64;
924   m_data_64 = nullptr;
925 }
926 
927 size_t
928 lldb_private::formatters::Foundation1100::
929   NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(const ConstString &name) {
930   const char *item_name = name.GetCString();
931   uint32_t idx = ExtractIndexFromString(item_name);
932   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
933     return UINT32_MAX;
934   return idx;
935 }
936 
937 size_t
938 lldb_private::formatters::Foundation1100::
939   NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
940   if (!m_data_32 && !m_data_64)
941     return 0;
942   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
943 }
944 
945 bool
946 lldb_private::formatters::Foundation1100::
947   NSDictionaryMSyntheticFrontEnd::Update() {
948   m_children.clear();
949   ValueObjectSP valobj_sp = m_backend.GetSP();
950   m_ptr_size = 0;
951   delete m_data_32;
952   m_data_32 = nullptr;
953   delete m_data_64;
954   m_data_64 = nullptr;
955   if (!valobj_sp)
956     return false;
957   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
958   Status error;
959   error.Clear();
960   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
961   if (!process_sp)
962     return false;
963   m_ptr_size = process_sp->GetAddressByteSize();
964   m_order = process_sp->GetByteOrder();
965   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
966   if (m_ptr_size == 4) {
967     m_data_32 = new DataDescriptor_32();
968     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
969                            error);
970   } else {
971     m_data_64 = new DataDescriptor_64();
972     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
973                            error);
974   }
975   if (error.Fail())
976     return false;
977   return false;
978 }
979 
980 bool
981 lldb_private::formatters::Foundation1100::
982   NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
983   return true;
984 }
985 
986 lldb::ValueObjectSP
987 lldb_private::formatters::Foundation1100::
988   NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
989   lldb::addr_t m_keys_ptr =
990       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
991   lldb::addr_t m_values_ptr =
992       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
993 
994   uint32_t num_children = CalculateNumChildren();
995 
996   if (idx >= num_children)
997     return lldb::ValueObjectSP();
998 
999   if (m_children.empty()) {
1000     // do the scan phase
1001     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1002 
1003     uint32_t tries = 0;
1004     uint32_t test_idx = 0;
1005 
1006     while (tries < num_children) {
1007       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1008       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1009       ;
1010       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1011       if (!process_sp)
1012         return lldb::ValueObjectSP();
1013       Status error;
1014       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1015       if (error.Fail())
1016         return lldb::ValueObjectSP();
1017       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1018       if (error.Fail())
1019         return lldb::ValueObjectSP();
1020 
1021       test_idx++;
1022 
1023       if (!key_at_idx || !val_at_idx)
1024         continue;
1025       tries++;
1026 
1027       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1028                                              lldb::ValueObjectSP()};
1029 
1030       m_children.push_back(descriptor);
1031     }
1032   }
1033 
1034   if (idx >= m_children.size()) // should never happen
1035     return lldb::ValueObjectSP();
1036 
1037   DictionaryItemDescriptor &dict_item = m_children[idx];
1038   if (!dict_item.valobj_sp) {
1039     if (!m_pair_type.IsValid()) {
1040       TargetSP target_sp(m_backend.GetTargetSP());
1041       if (!target_sp)
1042         return ValueObjectSP();
1043       m_pair_type = GetLLDBNSPairType(target_sp);
1044     }
1045     if (!m_pair_type.IsValid())
1046       return ValueObjectSP();
1047 
1048     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1049 
1050     if (m_ptr_size == 8) {
1051       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1052       *data_ptr = dict_item.key_ptr;
1053       *(data_ptr + 1) = dict_item.val_ptr;
1054     } else {
1055       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1056       *data_ptr = dict_item.key_ptr;
1057       *(data_ptr + 1) = dict_item.val_ptr;
1058     }
1059 
1060     StreamString idx_name;
1061     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1062     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1063     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1064                                                     m_exe_ctx_ref, m_pair_type);
1065   }
1066   return dict_item.valobj_sp;
1067 }
1068 
1069 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1070     ValueObject &, Stream &, const TypeSummaryOptions &);
1071 
1072 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1073     ValueObject &, Stream &, const TypeSummaryOptions &);
1074