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   return name == g_zero ? 0 : UINT32_MAX;
671 }
672 
673 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
674     CalculateNumChildren() {
675   return 1;
676 }
677 
678 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
679   m_pair.reset();
680   return false;
681 }
682 
683 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
684     MightHaveChildren() {
685   return true;
686 }
687 
688 lldb::ValueObjectSP
689 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
690     size_t idx) {
691   if (idx != 0)
692     return lldb::ValueObjectSP();
693 
694   if (m_pair.get())
695     return m_pair;
696 
697   auto process_sp(m_backend.GetProcessSP());
698   if (!process_sp)
699     return nullptr;
700 
701   auto ptr_size = process_sp->GetAddressByteSize();
702 
703   lldb::addr_t key_ptr =
704       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
705   lldb::addr_t value_ptr = key_ptr + ptr_size;
706 
707   Status error;
708 
709   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
710   if (error.Fail())
711     return nullptr;
712   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
713   if (error.Fail())
714     return nullptr;
715 
716   auto pair_type =
717       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
718 
719   DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
720 
721   if (ptr_size == 8) {
722     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
723     *data_ptr = key_at_idx;
724     *(data_ptr + 1) = value_at_idx;
725   } else {
726     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
727     *data_ptr = key_at_idx;
728     *(data_ptr + 1) = value_at_idx;
729   }
730 
731   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
732   m_pair = CreateValueObjectFromData(
733       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
734 
735   return m_pair;
736 }
737 
738 template <typename D32, typename D64>
739 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
740     GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
741     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
742       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
743       m_pair_type() {}
744 
745 template <typename D32, typename D64>
746 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
747     ~GenericNSDictionaryMSyntheticFrontEnd() {
748   delete m_data_32;
749   m_data_32 = nullptr;
750   delete m_data_64;
751   m_data_64 = nullptr;
752 }
753 
754 template <typename D32, typename D64>
755 size_t
756 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::    GetIndexOfChildWithName(const ConstString &name) {
757   const char *item_name = name.GetCString();
758   uint32_t idx = ExtractIndexFromString(item_name);
759   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
760     return UINT32_MAX;
761   return idx;
762 }
763 
764 template <typename D32, typename D64>
765 size_t
766 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
767   if (!m_data_32 && !m_data_64)
768     return 0;
769   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
770 }
771 
772 template <typename D32, typename D64>
773 bool
774 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
775   Update() {
776   m_children.clear();
777   ValueObjectSP valobj_sp = m_backend.GetSP();
778   m_ptr_size = 0;
779   delete m_data_32;
780   m_data_32 = nullptr;
781   delete m_data_64;
782   m_data_64 = nullptr;
783   if (!valobj_sp)
784     return false;
785   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
786   Status error;
787   error.Clear();
788   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
789   if (!process_sp)
790     return false;
791   m_ptr_size = process_sp->GetAddressByteSize();
792   m_order = process_sp->GetByteOrder();
793   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
794   if (m_ptr_size == 4) {
795     m_data_32 = new D32();
796     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
797                            error);
798   } else {
799     m_data_64 = new D64();
800     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
801                            error);
802   }
803   if (error.Fail())
804     return false;
805   return false;
806 }
807 
808 template <typename D32, typename D64>
809 bool
810 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
811     MightHaveChildren() {
812   return true;
813 }
814 
815 template <typename D32, typename D64>
816 lldb::ValueObjectSP
817 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
818     GetChildAtIndex(
819     size_t idx) {
820   lldb::addr_t m_keys_ptr;
821   lldb::addr_t m_values_ptr;
822   if (m_data_32) {
823     uint32_t size = m_data_32->GetSize();
824     m_keys_ptr = m_data_32->_buffer;
825     m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
826   } else {
827     uint32_t size = m_data_64->GetSize();
828     m_keys_ptr = m_data_64->_buffer;
829     m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
830   }
831 
832   uint32_t num_children = CalculateNumChildren();
833 
834   if (idx >= num_children)
835     return lldb::ValueObjectSP();
836 
837   if (m_children.empty()) {
838     // do the scan phase
839     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
840 
841     uint32_t tries = 0;
842     uint32_t test_idx = 0;
843 
844     while (tries < num_children) {
845       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
846       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
847       ;
848       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
849       if (!process_sp)
850         return lldb::ValueObjectSP();
851       Status error;
852       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
853       if (error.Fail())
854         return lldb::ValueObjectSP();
855       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
856       if (error.Fail())
857         return lldb::ValueObjectSP();
858 
859       test_idx++;
860 
861       if (!key_at_idx || !val_at_idx)
862         continue;
863       tries++;
864 
865       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
866                                              lldb::ValueObjectSP()};
867 
868       m_children.push_back(descriptor);
869     }
870   }
871 
872   if (idx >= m_children.size()) // should never happen
873     return lldb::ValueObjectSP();
874 
875   DictionaryItemDescriptor &dict_item = m_children[idx];
876   if (!dict_item.valobj_sp) {
877     if (!m_pair_type.IsValid()) {
878       TargetSP target_sp(m_backend.GetTargetSP());
879       if (!target_sp)
880         return ValueObjectSP();
881       m_pair_type = GetLLDBNSPairType(target_sp);
882     }
883     if (!m_pair_type.IsValid())
884       return ValueObjectSP();
885 
886     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
887 
888     if (m_ptr_size == 8) {
889       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
890       *data_ptr = dict_item.key_ptr;
891       *(data_ptr + 1) = dict_item.val_ptr;
892     } else {
893       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
894       *data_ptr = dict_item.key_ptr;
895       *(data_ptr + 1) = dict_item.val_ptr;
896     }
897 
898     StreamString idx_name;
899     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
900     DataExtractor data(buffer_sp, m_order, m_ptr_size);
901     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
902                                                     m_exe_ctx_ref, m_pair_type);
903   }
904   return dict_item.valobj_sp;
905 }
906 
907 
908 lldb_private::formatters::Foundation1100::
909   NSDictionaryMSyntheticFrontEnd::
910     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
911     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
912       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
913       m_pair_type() {}
914 
915 lldb_private::formatters::Foundation1100::
916   NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
917   delete m_data_32;
918   m_data_32 = nullptr;
919   delete m_data_64;
920   m_data_64 = nullptr;
921 }
922 
923 size_t
924 lldb_private::formatters::Foundation1100::
925   NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(const ConstString &name) {
926   const char *item_name = name.GetCString();
927   uint32_t idx = ExtractIndexFromString(item_name);
928   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
929     return UINT32_MAX;
930   return idx;
931 }
932 
933 size_t
934 lldb_private::formatters::Foundation1100::
935   NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
936   if (!m_data_32 && !m_data_64)
937     return 0;
938   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
939 }
940 
941 bool
942 lldb_private::formatters::Foundation1100::
943   NSDictionaryMSyntheticFrontEnd::Update() {
944   m_children.clear();
945   ValueObjectSP valobj_sp = m_backend.GetSP();
946   m_ptr_size = 0;
947   delete m_data_32;
948   m_data_32 = nullptr;
949   delete m_data_64;
950   m_data_64 = nullptr;
951   if (!valobj_sp)
952     return false;
953   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
954   Status error;
955   error.Clear();
956   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
957   if (!process_sp)
958     return false;
959   m_ptr_size = process_sp->GetAddressByteSize();
960   m_order = process_sp->GetByteOrder();
961   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
962   if (m_ptr_size == 4) {
963     m_data_32 = new DataDescriptor_32();
964     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
965                            error);
966   } else {
967     m_data_64 = new DataDescriptor_64();
968     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
969                            error);
970   }
971   if (error.Fail())
972     return false;
973   return false;
974 }
975 
976 bool
977 lldb_private::formatters::Foundation1100::
978   NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
979   return true;
980 }
981 
982 lldb::ValueObjectSP
983 lldb_private::formatters::Foundation1100::
984   NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
985   lldb::addr_t m_keys_ptr =
986       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
987   lldb::addr_t m_values_ptr =
988       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
989 
990   uint32_t num_children = CalculateNumChildren();
991 
992   if (idx >= num_children)
993     return lldb::ValueObjectSP();
994 
995   if (m_children.empty()) {
996     // do the scan phase
997     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
998 
999     uint32_t tries = 0;
1000     uint32_t test_idx = 0;
1001 
1002     while (tries < num_children) {
1003       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1004       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1005       ;
1006       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1007       if (!process_sp)
1008         return lldb::ValueObjectSP();
1009       Status error;
1010       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1011       if (error.Fail())
1012         return lldb::ValueObjectSP();
1013       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1014       if (error.Fail())
1015         return lldb::ValueObjectSP();
1016 
1017       test_idx++;
1018 
1019       if (!key_at_idx || !val_at_idx)
1020         continue;
1021       tries++;
1022 
1023       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1024                                              lldb::ValueObjectSP()};
1025 
1026       m_children.push_back(descriptor);
1027     }
1028   }
1029 
1030   if (idx >= m_children.size()) // should never happen
1031     return lldb::ValueObjectSP();
1032 
1033   DictionaryItemDescriptor &dict_item = m_children[idx];
1034   if (!dict_item.valobj_sp) {
1035     if (!m_pair_type.IsValid()) {
1036       TargetSP target_sp(m_backend.GetTargetSP());
1037       if (!target_sp)
1038         return ValueObjectSP();
1039       m_pair_type = GetLLDBNSPairType(target_sp);
1040     }
1041     if (!m_pair_type.IsValid())
1042       return ValueObjectSP();
1043 
1044     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1045 
1046     if (m_ptr_size == 8) {
1047       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1048       *data_ptr = dict_item.key_ptr;
1049       *(data_ptr + 1) = dict_item.val_ptr;
1050     } else {
1051       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1052       *data_ptr = dict_item.key_ptr;
1053       *(data_ptr + 1) = dict_item.val_ptr;
1054     }
1055 
1056     StreamString idx_name;
1057     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1058     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1059     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1060                                                     m_exe_ctx_ref, m_pair_type);
1061   }
1062   return dict_item.valobj_sp;
1063 }
1064 
1065 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1066     ValueObject &, Stream &, const TypeSummaryOptions &);
1067 
1068 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1069     ValueObject &, Stream &, const TypeSummaryOptions &);
1070