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   static const ConstString g_DictionaryCF("__NSCFDictionary");
401 
402   if (class_name.IsEmpty())
403     return false;
404 
405   if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
406     Status error;
407     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
408                                                       ptr_size, 0, error);
409     if (error.Fail())
410       return false;
411     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
412   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
413              class_name == g_DictionaryCF) {
414     AppleObjCRuntime *apple_runtime =
415     llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
416     Status error;
417     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
418       value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
419                                                   error);
420     } else {
421       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
422                                                         ptr_size, 0, error);
423       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
424     }
425     if (error.Fail())
426       return false;
427   } else if (class_name == g_Dictionary1) {
428     value = 1;
429   } else if (class_name == g_Dictionary0) {
430     value = 0;
431   }
432   else {
433     auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
434     for (auto &candidate : map) {
435       if (candidate.first && candidate.first->Match(class_name))
436         return candidate.second(valobj, stream, options);
437     }
438     return false;
439   }
440 
441   std::string prefix, suffix;
442   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
443     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
444                                             suffix)) {
445       prefix.clear();
446       suffix.clear();
447     }
448   }
449 
450   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
451                 value == 1 ? "" : "s", suffix.c_str());
452   return true;
453 }
454 
455 SyntheticChildrenFrontEnd *
456 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
457     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
458   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
459   if (!process_sp)
460     return nullptr;
461   AppleObjCRuntime *runtime =
462       llvm::dyn_cast_or_null<AppleObjCRuntime>(process_sp->GetObjCLanguageRuntime());
463   if (!runtime)
464     return nullptr;
465 
466   CompilerType valobj_type(valobj_sp->GetCompilerType());
467   Flags flags(valobj_type.GetTypeInfo());
468 
469   if (flags.IsClear(eTypeIsPointer)) {
470     Status error;
471     valobj_sp = valobj_sp->AddressOf(error);
472     if (error.Fail() || !valobj_sp)
473       return nullptr;
474   }
475 
476   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
477       runtime->GetClassDescriptor(*valobj_sp));
478 
479   if (!descriptor || !descriptor->IsValid())
480     return nullptr;
481 
482   ConstString class_name(descriptor->GetClassName());
483 
484   static const ConstString g_DictionaryI("__NSDictionaryI");
485   static const ConstString g_DictionaryM("__NSDictionaryM");
486   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
487   static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
488   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
489   static const ConstString g_Dictionary0("__NSDictionary0");
490 
491   if (class_name.IsEmpty())
492     return nullptr;
493 
494   if (class_name == g_DictionaryI) {
495     return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
496   } else if (class_name == g_DictionaryM) {
497     if (runtime->GetFoundationVersion() >= 1437) {
498       return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
499     } else if (runtime->GetFoundationVersion() >= 1428) {
500       return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
501     } else {
502       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
503     }
504   } else if (class_name == g_DictionaryMLegacy) {
505       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
506   } else if (class_name == g_Dictionary1) {
507     return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
508   } else {
509     auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
510     for (auto &candidate : map) {
511       if (candidate.first && candidate.first->Match((class_name)))
512         return candidate.second(synth, valobj_sp);
513     }
514   }
515 
516   return nullptr;
517 }
518 
519 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
520     NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
521     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
522       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
523       m_pair_type() {}
524 
525 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
526     ~NSDictionaryISyntheticFrontEnd() {
527   delete m_data_32;
528   m_data_32 = nullptr;
529   delete m_data_64;
530   m_data_64 = nullptr;
531 }
532 
533 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
534     GetIndexOfChildWithName(const ConstString &name) {
535   const char *item_name = name.GetCString();
536   uint32_t idx = ExtractIndexFromString(item_name);
537   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
538     return UINT32_MAX;
539   return idx;
540 }
541 
542 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
543     CalculateNumChildren() {
544   if (!m_data_32 && !m_data_64)
545     return 0;
546   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
547 }
548 
549 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
550   m_children.clear();
551   delete m_data_32;
552   m_data_32 = nullptr;
553   delete m_data_64;
554   m_data_64 = nullptr;
555   m_ptr_size = 0;
556   ValueObjectSP valobj_sp = m_backend.GetSP();
557   if (!valobj_sp)
558     return false;
559   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
560   Status error;
561   error.Clear();
562   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
563   if (!process_sp)
564     return false;
565   m_ptr_size = process_sp->GetAddressByteSize();
566   m_order = process_sp->GetByteOrder();
567   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
568   if (m_ptr_size == 4) {
569     m_data_32 = new DataDescriptor_32();
570     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
571                            error);
572   } else {
573     m_data_64 = new DataDescriptor_64();
574     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
575                            error);
576   }
577   if (error.Fail())
578     return false;
579   m_data_ptr = data_location + m_ptr_size;
580   return false;
581 }
582 
583 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
584     MightHaveChildren() {
585   return true;
586 }
587 
588 lldb::ValueObjectSP
589 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
590     size_t idx) {
591   uint32_t num_children = CalculateNumChildren();
592 
593   if (idx >= num_children)
594     return lldb::ValueObjectSP();
595 
596   if (m_children.empty()) {
597     // do the scan phase
598     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
599 
600     uint32_t tries = 0;
601     uint32_t test_idx = 0;
602 
603     while (tries < num_children) {
604       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
605       val_at_idx = key_at_idx + m_ptr_size;
606       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
607       if (!process_sp)
608         return lldb::ValueObjectSP();
609       Status error;
610       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
611       if (error.Fail())
612         return lldb::ValueObjectSP();
613       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
614       if (error.Fail())
615         return lldb::ValueObjectSP();
616 
617       test_idx++;
618 
619       if (!key_at_idx || !val_at_idx)
620         continue;
621       tries++;
622 
623       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
624                                              lldb::ValueObjectSP()};
625 
626       m_children.push_back(descriptor);
627     }
628   }
629 
630   if (idx >= m_children.size()) // should never happen
631     return lldb::ValueObjectSP();
632 
633   DictionaryItemDescriptor &dict_item = m_children[idx];
634   if (!dict_item.valobj_sp) {
635     if (!m_pair_type.IsValid()) {
636       TargetSP target_sp(m_backend.GetTargetSP());
637       if (!target_sp)
638         return ValueObjectSP();
639       m_pair_type = GetLLDBNSPairType(target_sp);
640     }
641     if (!m_pair_type.IsValid())
642       return ValueObjectSP();
643 
644     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
645 
646     if (m_ptr_size == 8) {
647       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
648       *data_ptr = dict_item.key_ptr;
649       *(data_ptr + 1) = dict_item.val_ptr;
650     } else {
651       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
652       *data_ptr = dict_item.key_ptr;
653       *(data_ptr + 1) = dict_item.val_ptr;
654     }
655 
656     StreamString idx_name;
657     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
658     DataExtractor data(buffer_sp, m_order, m_ptr_size);
659     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
660                                                     m_exe_ctx_ref, m_pair_type);
661   }
662   return dict_item.valobj_sp;
663 }
664 
665 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
666     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
667     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
668 
669 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
670     GetIndexOfChildWithName(const ConstString &name) {
671   static const ConstString g_zero("[0]");
672   return name == g_zero ? 0 : UINT32_MAX;
673 }
674 
675 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
676     CalculateNumChildren() {
677   return 1;
678 }
679 
680 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
681   m_pair.reset();
682   return false;
683 }
684 
685 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
686     MightHaveChildren() {
687   return true;
688 }
689 
690 lldb::ValueObjectSP
691 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
692     size_t idx) {
693   if (idx != 0)
694     return lldb::ValueObjectSP();
695 
696   if (m_pair.get())
697     return m_pair;
698 
699   auto process_sp(m_backend.GetProcessSP());
700   if (!process_sp)
701     return nullptr;
702 
703   auto ptr_size = process_sp->GetAddressByteSize();
704 
705   lldb::addr_t key_ptr =
706       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
707   lldb::addr_t value_ptr = key_ptr + ptr_size;
708 
709   Status error;
710 
711   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
712   if (error.Fail())
713     return nullptr;
714   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
715   if (error.Fail())
716     return nullptr;
717 
718   auto pair_type =
719       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
720 
721   DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
722 
723   if (ptr_size == 8) {
724     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
725     *data_ptr = key_at_idx;
726     *(data_ptr + 1) = value_at_idx;
727   } else {
728     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
729     *data_ptr = key_at_idx;
730     *(data_ptr + 1) = value_at_idx;
731   }
732 
733   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
734   m_pair = CreateValueObjectFromData(
735       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
736 
737   return m_pair;
738 }
739 
740 template <typename D32, typename D64>
741 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
742     GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
743     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
744       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
745       m_pair_type() {}
746 
747 template <typename D32, typename D64>
748 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
749     ~GenericNSDictionaryMSyntheticFrontEnd() {
750   delete m_data_32;
751   m_data_32 = nullptr;
752   delete m_data_64;
753   m_data_64 = nullptr;
754 }
755 
756 template <typename D32, typename D64>
757 size_t
758 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::    GetIndexOfChildWithName(const ConstString &name) {
759   const char *item_name = name.GetCString();
760   uint32_t idx = ExtractIndexFromString(item_name);
761   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
762     return UINT32_MAX;
763   return idx;
764 }
765 
766 template <typename D32, typename D64>
767 size_t
768 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
769   if (!m_data_32 && !m_data_64)
770     return 0;
771   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
772 }
773 
774 template <typename D32, typename D64>
775 bool
776 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
777   Update() {
778   m_children.clear();
779   ValueObjectSP valobj_sp = m_backend.GetSP();
780   m_ptr_size = 0;
781   delete m_data_32;
782   m_data_32 = nullptr;
783   delete m_data_64;
784   m_data_64 = nullptr;
785   if (!valobj_sp)
786     return false;
787   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
788   Status error;
789   error.Clear();
790   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
791   if (!process_sp)
792     return false;
793   m_ptr_size = process_sp->GetAddressByteSize();
794   m_order = process_sp->GetByteOrder();
795   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
796   if (m_ptr_size == 4) {
797     m_data_32 = new D32();
798     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
799                            error);
800   } else {
801     m_data_64 = new D64();
802     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
803                            error);
804   }
805   if (error.Fail())
806     return false;
807   return false;
808 }
809 
810 template <typename D32, typename D64>
811 bool
812 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
813     MightHaveChildren() {
814   return true;
815 }
816 
817 template <typename D32, typename D64>
818 lldb::ValueObjectSP
819 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
820     GetChildAtIndex(
821     size_t idx) {
822   lldb::addr_t m_keys_ptr;
823   lldb::addr_t m_values_ptr;
824   if (m_data_32) {
825     uint32_t size = m_data_32->GetSize();
826     m_keys_ptr = m_data_32->_buffer;
827     m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
828   } else {
829     uint32_t size = m_data_64->GetSize();
830     m_keys_ptr = m_data_64->_buffer;
831     m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
832   }
833 
834   uint32_t num_children = CalculateNumChildren();
835 
836   if (idx >= num_children)
837     return lldb::ValueObjectSP();
838 
839   if (m_children.empty()) {
840     // do the scan phase
841     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
842 
843     uint32_t tries = 0;
844     uint32_t test_idx = 0;
845 
846     while (tries < num_children) {
847       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
848       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
849       ;
850       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
851       if (!process_sp)
852         return lldb::ValueObjectSP();
853       Status error;
854       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
855       if (error.Fail())
856         return lldb::ValueObjectSP();
857       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
858       if (error.Fail())
859         return lldb::ValueObjectSP();
860 
861       test_idx++;
862 
863       if (!key_at_idx || !val_at_idx)
864         continue;
865       tries++;
866 
867       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
868                                              lldb::ValueObjectSP()};
869 
870       m_children.push_back(descriptor);
871     }
872   }
873 
874   if (idx >= m_children.size()) // should never happen
875     return lldb::ValueObjectSP();
876 
877   DictionaryItemDescriptor &dict_item = m_children[idx];
878   if (!dict_item.valobj_sp) {
879     if (!m_pair_type.IsValid()) {
880       TargetSP target_sp(m_backend.GetTargetSP());
881       if (!target_sp)
882         return ValueObjectSP();
883       m_pair_type = GetLLDBNSPairType(target_sp);
884     }
885     if (!m_pair_type.IsValid())
886       return ValueObjectSP();
887 
888     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
889 
890     if (m_ptr_size == 8) {
891       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
892       *data_ptr = dict_item.key_ptr;
893       *(data_ptr + 1) = dict_item.val_ptr;
894     } else {
895       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
896       *data_ptr = dict_item.key_ptr;
897       *(data_ptr + 1) = dict_item.val_ptr;
898     }
899 
900     StreamString idx_name;
901     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
902     DataExtractor data(buffer_sp, m_order, m_ptr_size);
903     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
904                                                     m_exe_ctx_ref, m_pair_type);
905   }
906   return dict_item.valobj_sp;
907 }
908 
909 
910 lldb_private::formatters::Foundation1100::
911   NSDictionaryMSyntheticFrontEnd::
912     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
913     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
914       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
915       m_pair_type() {}
916 
917 lldb_private::formatters::Foundation1100::
918   NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
919   delete m_data_32;
920   m_data_32 = nullptr;
921   delete m_data_64;
922   m_data_64 = nullptr;
923 }
924 
925 size_t
926 lldb_private::formatters::Foundation1100::
927   NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(const ConstString &name) {
928   const char *item_name = name.GetCString();
929   uint32_t idx = ExtractIndexFromString(item_name);
930   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
931     return UINT32_MAX;
932   return idx;
933 }
934 
935 size_t
936 lldb_private::formatters::Foundation1100::
937   NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
938   if (!m_data_32 && !m_data_64)
939     return 0;
940   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
941 }
942 
943 bool
944 lldb_private::formatters::Foundation1100::
945   NSDictionaryMSyntheticFrontEnd::Update() {
946   m_children.clear();
947   ValueObjectSP valobj_sp = m_backend.GetSP();
948   m_ptr_size = 0;
949   delete m_data_32;
950   m_data_32 = nullptr;
951   delete m_data_64;
952   m_data_64 = nullptr;
953   if (!valobj_sp)
954     return false;
955   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
956   Status error;
957   error.Clear();
958   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
959   if (!process_sp)
960     return false;
961   m_ptr_size = process_sp->GetAddressByteSize();
962   m_order = process_sp->GetByteOrder();
963   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
964   if (m_ptr_size == 4) {
965     m_data_32 = new DataDescriptor_32();
966     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
967                            error);
968   } else {
969     m_data_64 = new DataDescriptor_64();
970     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
971                            error);
972   }
973   if (error.Fail())
974     return false;
975   return false;
976 }
977 
978 bool
979 lldb_private::formatters::Foundation1100::
980   NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
981   return true;
982 }
983 
984 lldb::ValueObjectSP
985 lldb_private::formatters::Foundation1100::
986   NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
987   lldb::addr_t m_keys_ptr =
988       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
989   lldb::addr_t m_values_ptr =
990       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
991 
992   uint32_t num_children = CalculateNumChildren();
993 
994   if (idx >= num_children)
995     return lldb::ValueObjectSP();
996 
997   if (m_children.empty()) {
998     // do the scan phase
999     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1000 
1001     uint32_t tries = 0;
1002     uint32_t test_idx = 0;
1003 
1004     while (tries < num_children) {
1005       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1006       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1007       ;
1008       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1009       if (!process_sp)
1010         return lldb::ValueObjectSP();
1011       Status error;
1012       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1013       if (error.Fail())
1014         return lldb::ValueObjectSP();
1015       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1016       if (error.Fail())
1017         return lldb::ValueObjectSP();
1018 
1019       test_idx++;
1020 
1021       if (!key_at_idx || !val_at_idx)
1022         continue;
1023       tries++;
1024 
1025       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1026                                              lldb::ValueObjectSP()};
1027 
1028       m_children.push_back(descriptor);
1029     }
1030   }
1031 
1032   if (idx >= m_children.size()) // should never happen
1033     return lldb::ValueObjectSP();
1034 
1035   DictionaryItemDescriptor &dict_item = m_children[idx];
1036   if (!dict_item.valobj_sp) {
1037     if (!m_pair_type.IsValid()) {
1038       TargetSP target_sp(m_backend.GetTargetSP());
1039       if (!target_sp)
1040         return ValueObjectSP();
1041       m_pair_type = GetLLDBNSPairType(target_sp);
1042     }
1043     if (!m_pair_type.IsValid())
1044       return ValueObjectSP();
1045 
1046     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1047 
1048     if (m_ptr_size == 8) {
1049       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1050       *data_ptr = dict_item.key_ptr;
1051       *(data_ptr + 1) = dict_item.val_ptr;
1052     } else {
1053       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1054       *data_ptr = dict_item.key_ptr;
1055       *(data_ptr + 1) = dict_item.val_ptr;
1056     }
1057 
1058     StreamString idx_name;
1059     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1060     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1061     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1062                                                     m_exe_ctx_ref, m_pair_type);
1063   }
1064   return dict_item.valobj_sp;
1065 }
1066 
1067 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1068     ValueObject &, Stream &, const TypeSummaryOptions &);
1069 
1070 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1071     ValueObject &, Stream &, const TypeSummaryOptions &);
1072