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