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 class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
169 public:
170   NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
171 
172   ~NSDictionaryMSyntheticFrontEnd() override;
173 
174   size_t CalculateNumChildren() override;
175 
176   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
177 
178   bool Update() override;
179 
180   bool MightHaveChildren() override;
181 
182   size_t GetIndexOfChildWithName(const ConstString &name) override;
183 
184 private:
185   struct DataDescriptor_32 {
186     uint32_t used : 26;
187     uint32_t kvo : 1;
188     uint32_t size;
189     uint32_t buffer;
190   };
191 
192   struct DataDescriptor_64 {
193     uint64_t used : 58;
194     uint32_t kvo : 1;
195     uint64_t size;
196     uint64_t buffer;
197   };
198 
199   struct DictionaryItemDescriptor {
200     lldb::addr_t key_ptr;
201     lldb::addr_t val_ptr;
202     lldb::ValueObjectSP valobj_sp;
203   };
204 
205   ExecutionContextRef m_exe_ctx_ref;
206   uint8_t m_ptr_size;
207   lldb::ByteOrder m_order;
208   DataDescriptor_32 *m_data_32;
209   DataDescriptor_64 *m_data_64;
210   CompilerType m_pair_type;
211   std::vector<DictionaryItemDescriptor> m_children;
212 };
213 
214 class NSDictionaryMLegacySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
215 public:
216   NSDictionaryMLegacySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
217 
218   ~NSDictionaryMLegacySyntheticFrontEnd() override;
219 
220   size_t CalculateNumChildren() override;
221 
222   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
223 
224   bool Update() override;
225 
226   bool MightHaveChildren() override;
227 
228   size_t GetIndexOfChildWithName(const ConstString &name) override;
229 
230 private:
231   struct DataDescriptor_32 {
232     uint32_t _used : 26;
233     uint32_t _kvo : 1;
234     uint32_t _size;
235     uint32_t _mutations;
236     uint32_t _objs_addr;
237     uint32_t _keys_addr;
238   };
239 
240   struct DataDescriptor_64 {
241     uint64_t _used : 58;
242     uint32_t _kvo : 1;
243     uint64_t _size;
244     uint64_t _mutations;
245     uint64_t _objs_addr;
246     uint64_t _keys_addr;
247   };
248 
249   struct DictionaryItemDescriptor {
250     lldb::addr_t key_ptr;
251     lldb::addr_t val_ptr;
252     lldb::ValueObjectSP valobj_sp;
253   };
254 
255   ExecutionContextRef m_exe_ctx_ref;
256   uint8_t m_ptr_size;
257   lldb::ByteOrder m_order;
258   DataDescriptor_32 *m_data_32;
259   DataDescriptor_64 *m_data_64;
260   CompilerType m_pair_type;
261   std::vector<DictionaryItemDescriptor> m_children;
262 };
263 } // namespace formatters
264 } // namespace lldb_private
265 
266 template <bool name_entries>
267 bool lldb_private::formatters::NSDictionarySummaryProvider(
268     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
269   static ConstString g_TypeHint("NSDictionary");
270   ProcessSP process_sp = valobj.GetProcessSP();
271   if (!process_sp)
272     return false;
273 
274   ObjCLanguageRuntime *runtime =
275       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
276           lldb::eLanguageTypeObjC);
277 
278   if (!runtime)
279     return false;
280 
281   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
282       runtime->GetClassDescriptor(valobj));
283 
284   if (!descriptor || !descriptor->IsValid())
285     return false;
286 
287   uint32_t ptr_size = process_sp->GetAddressByteSize();
288   bool is_64bit = (ptr_size == 8);
289 
290   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
291 
292   if (!valobj_addr)
293     return false;
294 
295   uint64_t value = 0;
296 
297   ConstString class_name(descriptor->GetClassName());
298 
299   static const ConstString g_DictionaryI("__NSDictionaryI");
300   static const ConstString g_DictionaryM("__NSDictionaryM");
301   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
302   static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
303   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
304 
305   if (class_name.IsEmpty())
306     return false;
307 
308   if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
309     Status error;
310     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
311                                                       ptr_size, 0, error);
312     if (error.Fail())
313       return false;
314     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
315   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy) {
316     Status error;
317     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
318                                                       ptr_size, 0, error);
319     if (error.Fail())
320       return false;
321     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
322   } else if (class_name == g_Dictionary1) {
323     value = 1;
324   }
325   /*else if (!strcmp(class_name,"__NSCFDictionary"))
326    {
327    Status error;
328    value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ?
329    20 : 12), 4, 0, error);
330    if (error.Fail())
331    return false;
332    if (is_64bit)
333    value &= ~0x0f1f000000000000UL;
334    }*/
335   else {
336     auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
337     for (auto &candidate : map) {
338       if (candidate.first && candidate.first->Match(class_name))
339         return candidate.second(valobj, stream, options);
340     }
341     return false;
342   }
343 
344   std::string prefix, suffix;
345   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
346     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
347                                             suffix)) {
348       prefix.clear();
349       suffix.clear();
350     }
351   }
352 
353   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
354                 value == 1 ? "" : "s", suffix.c_str());
355   return true;
356 }
357 
358 SyntheticChildrenFrontEnd *
359 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
360     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
361   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
362   if (!process_sp)
363     return nullptr;
364   AppleObjCRuntime *runtime =
365       llvm::dyn_cast_or_null<AppleObjCRuntime>(process_sp->GetObjCLanguageRuntime());
366   if (!runtime)
367     return nullptr;
368 
369   CompilerType valobj_type(valobj_sp->GetCompilerType());
370   Flags flags(valobj_type.GetTypeInfo());
371 
372   if (flags.IsClear(eTypeIsPointer)) {
373     Status error;
374     valobj_sp = valobj_sp->AddressOf(error);
375     if (error.Fail() || !valobj_sp)
376       return nullptr;
377   }
378 
379   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
380       runtime->GetClassDescriptor(*valobj_sp));
381 
382   if (!descriptor || !descriptor->IsValid())
383     return nullptr;
384 
385   ConstString class_name(descriptor->GetClassName());
386 
387   static const ConstString g_DictionaryI("__NSDictionaryI");
388   static const ConstString g_DictionaryM("__NSDictionaryM");
389   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
390   static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
391   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
392 
393   if (class_name.IsEmpty())
394     return nullptr;
395 
396   if (class_name == g_DictionaryI) {
397     return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
398   } else if (class_name == g_DictionaryM) {
399     if (runtime->GetFoundationVersion() > 1400) {
400       return (new NSDictionaryMSyntheticFrontEnd(valobj_sp));
401     } else {
402       return (new NSDictionaryMLegacySyntheticFrontEnd(valobj_sp));
403     }
404   } else if (class_name == g_DictionaryMLegacy) {
405       return (new NSDictionaryMLegacySyntheticFrontEnd(valobj_sp));
406   } else if (class_name == g_Dictionary1) {
407     return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
408   } else {
409     auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
410     for (auto &candidate : map) {
411       if (candidate.first && candidate.first->Match((class_name)))
412         return candidate.second(synth, valobj_sp);
413     }
414   }
415 
416   return nullptr;
417 }
418 
419 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
420     NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
421     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
422       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
423       m_pair_type() {}
424 
425 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
426     ~NSDictionaryISyntheticFrontEnd() {
427   delete m_data_32;
428   m_data_32 = nullptr;
429   delete m_data_64;
430   m_data_64 = nullptr;
431 }
432 
433 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
434     GetIndexOfChildWithName(const ConstString &name) {
435   const char *item_name = name.GetCString();
436   uint32_t idx = ExtractIndexFromString(item_name);
437   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
438     return UINT32_MAX;
439   return idx;
440 }
441 
442 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
443     CalculateNumChildren() {
444   if (!m_data_32 && !m_data_64)
445     return 0;
446   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
447 }
448 
449 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
450   m_children.clear();
451   delete m_data_32;
452   m_data_32 = nullptr;
453   delete m_data_64;
454   m_data_64 = nullptr;
455   m_ptr_size = 0;
456   ValueObjectSP valobj_sp = m_backend.GetSP();
457   if (!valobj_sp)
458     return false;
459   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
460   Status error;
461   error.Clear();
462   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
463   if (!process_sp)
464     return false;
465   m_ptr_size = process_sp->GetAddressByteSize();
466   m_order = process_sp->GetByteOrder();
467   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
468   if (m_ptr_size == 4) {
469     m_data_32 = new DataDescriptor_32();
470     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
471                            error);
472   } else {
473     m_data_64 = new DataDescriptor_64();
474     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
475                            error);
476   }
477   if (error.Fail())
478     return false;
479   m_data_ptr = data_location + m_ptr_size;
480   return false;
481 }
482 
483 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
484     MightHaveChildren() {
485   return true;
486 }
487 
488 lldb::ValueObjectSP
489 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
490     size_t idx) {
491   uint32_t num_children = CalculateNumChildren();
492 
493   if (idx >= num_children)
494     return lldb::ValueObjectSP();
495 
496   if (m_children.empty()) {
497     // do the scan phase
498     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
499 
500     uint32_t tries = 0;
501     uint32_t test_idx = 0;
502 
503     while (tries < num_children) {
504       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
505       val_at_idx = key_at_idx + m_ptr_size;
506       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
507       if (!process_sp)
508         return lldb::ValueObjectSP();
509       Status error;
510       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
511       if (error.Fail())
512         return lldb::ValueObjectSP();
513       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
514       if (error.Fail())
515         return lldb::ValueObjectSP();
516 
517       test_idx++;
518 
519       if (!key_at_idx || !val_at_idx)
520         continue;
521       tries++;
522 
523       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
524                                              lldb::ValueObjectSP()};
525 
526       m_children.push_back(descriptor);
527     }
528   }
529 
530   if (idx >= m_children.size()) // should never happen
531     return lldb::ValueObjectSP();
532 
533   DictionaryItemDescriptor &dict_item = m_children[idx];
534   if (!dict_item.valobj_sp) {
535     if (!m_pair_type.IsValid()) {
536       TargetSP target_sp(m_backend.GetTargetSP());
537       if (!target_sp)
538         return ValueObjectSP();
539       m_pair_type = GetLLDBNSPairType(target_sp);
540     }
541     if (!m_pair_type.IsValid())
542       return ValueObjectSP();
543 
544     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
545 
546     if (m_ptr_size == 8) {
547       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
548       *data_ptr = dict_item.key_ptr;
549       *(data_ptr + 1) = dict_item.val_ptr;
550     } else {
551       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
552       *data_ptr = dict_item.key_ptr;
553       *(data_ptr + 1) = dict_item.val_ptr;
554     }
555 
556     StreamString idx_name;
557     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
558     DataExtractor data(buffer_sp, m_order, m_ptr_size);
559     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
560                                                     m_exe_ctx_ref, m_pair_type);
561   }
562   return dict_item.valobj_sp;
563 }
564 
565 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
566     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
567     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
568 
569 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
570     GetIndexOfChildWithName(const ConstString &name) {
571   static const ConstString g_zero("[0]");
572 
573   if (name == g_zero)
574     return 0;
575 
576   return UINT32_MAX;
577 }
578 
579 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
580     CalculateNumChildren() {
581   return 1;
582 }
583 
584 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
585   m_pair.reset();
586   return false;
587 }
588 
589 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
590     MightHaveChildren() {
591   return true;
592 }
593 
594 lldb::ValueObjectSP
595 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
596     size_t idx) {
597   if (idx != 0)
598     return lldb::ValueObjectSP();
599 
600   if (m_pair.get())
601     return m_pair;
602 
603   auto process_sp(m_backend.GetProcessSP());
604   if (!process_sp)
605     return nullptr;
606 
607   auto ptr_size = process_sp->GetAddressByteSize();
608 
609   lldb::addr_t key_ptr =
610       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
611   lldb::addr_t value_ptr = key_ptr + ptr_size;
612 
613   Status error;
614 
615   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
616   if (error.Fail())
617     return nullptr;
618   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
619   if (error.Fail())
620     return nullptr;
621 
622   auto pair_type =
623       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
624 
625   DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
626 
627   if (ptr_size == 8) {
628     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
629     *data_ptr = key_at_idx;
630     *(data_ptr + 1) = value_at_idx;
631   } else {
632     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
633     *data_ptr = key_at_idx;
634     *(data_ptr + 1) = value_at_idx;
635   }
636 
637   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
638   m_pair = CreateValueObjectFromData(
639       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
640 
641   return m_pair;
642 }
643 
644 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
645     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
646     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
647       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
648       m_pair_type() {}
649 
650 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
651     ~NSDictionaryMSyntheticFrontEnd() {
652   delete m_data_32;
653   m_data_32 = nullptr;
654   delete m_data_64;
655   m_data_64 = nullptr;
656 }
657 
658 size_t lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
659     GetIndexOfChildWithName(const ConstString &name) {
660   const char *item_name = name.GetCString();
661   uint32_t idx = ExtractIndexFromString(item_name);
662   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
663     return UINT32_MAX;
664   return idx;
665 }
666 
667 size_t lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
668     CalculateNumChildren() {
669   if (!m_data_32 && !m_data_64)
670     return 0;
671   return (m_data_32 ? m_data_32->used : m_data_64->used);
672 }
673 
674 bool lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::Update() {
675   m_children.clear();
676   ValueObjectSP valobj_sp = m_backend.GetSP();
677   m_ptr_size = 0;
678   delete m_data_32;
679   m_data_32 = nullptr;
680   delete m_data_64;
681   m_data_64 = nullptr;
682   if (!valobj_sp)
683     return false;
684   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
685   Status error;
686   error.Clear();
687   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
688   if (!process_sp)
689     return false;
690   m_ptr_size = process_sp->GetAddressByteSize();
691   m_order = process_sp->GetByteOrder();
692   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
693   if (m_ptr_size == 4) {
694     m_data_32 = new DataDescriptor_32();
695     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
696                            error);
697   } else {
698     m_data_64 = new DataDescriptor_64();
699     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
700                            error);
701   }
702   if (error.Fail())
703     return false;
704   return false;
705 }
706 
707 bool lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
708     MightHaveChildren() {
709   return true;
710 }
711 
712 lldb::ValueObjectSP
713 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(
714     size_t idx) {
715   lldb::addr_t m_keys_ptr;
716   lldb::addr_t m_values_ptr;
717   if (m_data_32) {
718     uint32_t size = m_data_32->size;
719     m_keys_ptr = m_data_32->buffer;
720     m_values_ptr = m_data_32->buffer + (m_ptr_size * size);
721   } else {
722     uint32_t size = m_data_64->size;
723     m_keys_ptr = m_data_64->buffer;
724     m_values_ptr = m_data_64->buffer + (m_ptr_size * size);
725   }
726 
727   uint32_t num_children = CalculateNumChildren();
728 
729   if (idx >= num_children)
730     return lldb::ValueObjectSP();
731 
732   if (m_children.empty()) {
733     // do the scan phase
734     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
735 
736     uint32_t tries = 0;
737     uint32_t test_idx = 0;
738 
739     while (tries < num_children) {
740       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
741       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
742       ;
743       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
744       if (!process_sp)
745         return lldb::ValueObjectSP();
746       Status error;
747       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
748       if (error.Fail())
749         return lldb::ValueObjectSP();
750       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
751       if (error.Fail())
752         return lldb::ValueObjectSP();
753 
754       test_idx++;
755 
756       if (!key_at_idx || !val_at_idx)
757         continue;
758       tries++;
759 
760       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
761                                              lldb::ValueObjectSP()};
762 
763       m_children.push_back(descriptor);
764     }
765   }
766 
767   if (idx >= m_children.size()) // should never happen
768     return lldb::ValueObjectSP();
769 
770   DictionaryItemDescriptor &dict_item = m_children[idx];
771   if (!dict_item.valobj_sp) {
772     if (!m_pair_type.IsValid()) {
773       TargetSP target_sp(m_backend.GetTargetSP());
774       if (!target_sp)
775         return ValueObjectSP();
776       m_pair_type = GetLLDBNSPairType(target_sp);
777     }
778     if (!m_pair_type.IsValid())
779       return ValueObjectSP();
780 
781     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
782 
783     if (m_ptr_size == 8) {
784       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
785       *data_ptr = dict_item.key_ptr;
786       *(data_ptr + 1) = dict_item.val_ptr;
787     } else {
788       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
789       *data_ptr = dict_item.key_ptr;
790       *(data_ptr + 1) = dict_item.val_ptr;
791     }
792 
793     StreamString idx_name;
794     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
795     DataExtractor data(buffer_sp, m_order, m_ptr_size);
796     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
797                                                     m_exe_ctx_ref, m_pair_type);
798   }
799   return dict_item.valobj_sp;
800 }
801 
802 
803 lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::
804     NSDictionaryMLegacySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
805     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
806       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
807       m_pair_type() {}
808 
809 lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::
810     ~NSDictionaryMLegacySyntheticFrontEnd() {
811   delete m_data_32;
812   m_data_32 = nullptr;
813   delete m_data_64;
814   m_data_64 = nullptr;
815 }
816 
817 size_t lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::
818     GetIndexOfChildWithName(const ConstString &name) {
819   const char *item_name = name.GetCString();
820   uint32_t idx = ExtractIndexFromString(item_name);
821   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
822     return UINT32_MAX;
823   return idx;
824 }
825 
826 size_t lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::
827     CalculateNumChildren() {
828   if (!m_data_32 && !m_data_64)
829     return 0;
830   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
831 }
832 
833 bool lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::Update() {
834   m_children.clear();
835   ValueObjectSP valobj_sp = m_backend.GetSP();
836   m_ptr_size = 0;
837   delete m_data_32;
838   m_data_32 = nullptr;
839   delete m_data_64;
840   m_data_64 = nullptr;
841   if (!valobj_sp)
842     return false;
843   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
844   Status error;
845   error.Clear();
846   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
847   if (!process_sp)
848     return false;
849   m_ptr_size = process_sp->GetAddressByteSize();
850   m_order = process_sp->GetByteOrder();
851   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
852   if (m_ptr_size == 4) {
853     m_data_32 = new DataDescriptor_32();
854     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
855                            error);
856   } else {
857     m_data_64 = new DataDescriptor_64();
858     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
859                            error);
860   }
861   if (error.Fail())
862     return false;
863   return false;
864 }
865 
866 bool lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::
867     MightHaveChildren() {
868   return true;
869 }
870 
871 lldb::ValueObjectSP
872 lldb_private::formatters::NSDictionaryMLegacySyntheticFrontEnd::GetChildAtIndex(
873     size_t idx) {
874   lldb::addr_t m_keys_ptr =
875       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
876   lldb::addr_t m_values_ptr =
877       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
878 
879   uint32_t num_children = CalculateNumChildren();
880 
881   if (idx >= num_children)
882     return lldb::ValueObjectSP();
883 
884   if (m_children.empty()) {
885     // do the scan phase
886     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
887 
888     uint32_t tries = 0;
889     uint32_t test_idx = 0;
890 
891     while (tries < num_children) {
892       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
893       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
894       ;
895       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
896       if (!process_sp)
897         return lldb::ValueObjectSP();
898       Status error;
899       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
900       if (error.Fail())
901         return lldb::ValueObjectSP();
902       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
903       if (error.Fail())
904         return lldb::ValueObjectSP();
905 
906       test_idx++;
907 
908       if (!key_at_idx || !val_at_idx)
909         continue;
910       tries++;
911 
912       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
913                                              lldb::ValueObjectSP()};
914 
915       m_children.push_back(descriptor);
916     }
917   }
918 
919   if (idx >= m_children.size()) // should never happen
920     return lldb::ValueObjectSP();
921 
922   DictionaryItemDescriptor &dict_item = m_children[idx];
923   if (!dict_item.valobj_sp) {
924     if (!m_pair_type.IsValid()) {
925       TargetSP target_sp(m_backend.GetTargetSP());
926       if (!target_sp)
927         return ValueObjectSP();
928       m_pair_type = GetLLDBNSPairType(target_sp);
929     }
930     if (!m_pair_type.IsValid())
931       return ValueObjectSP();
932 
933     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
934 
935     if (m_ptr_size == 8) {
936       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
937       *data_ptr = dict_item.key_ptr;
938       *(data_ptr + 1) = dict_item.val_ptr;
939     } else {
940       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
941       *data_ptr = dict_item.key_ptr;
942       *(data_ptr + 1) = dict_item.val_ptr;
943     }
944 
945     StreamString idx_name;
946     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
947     DataExtractor data(buffer_sp, m_order, m_ptr_size);
948     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
949                                                     m_exe_ctx_ref, m_pair_type);
950   }
951   return dict_item.valobj_sp;
952 }
953 
954 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
955     ValueObject &, Stream &, const TypeSummaryOptions &);
956 
957 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
958     ValueObject &, Stream &, const TypeSummaryOptions &);
959