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