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 "lldb/Core/DataBufferHeap.h"
21 #include "lldb/Core/Error.h"
22 #include "lldb/Core/Stream.h"
23 #include "lldb/Core/ValueObject.h"
24 #include "lldb/Core/ValueObjectConstResult.h"
25 #include "lldb/DataFormatters/FormattersHelpers.h"
26 #include "lldb/Host/Endian.h"
27 #include "lldb/Symbol/ClangASTContext.h"
28 #include "lldb/Target/Language.h"
29 #include "lldb/Target/ObjCLanguageRuntime.h"
30 #include "lldb/Target/StackFrame.h"
31 #include "lldb/Target/Target.h"
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 using namespace lldb_private::formatters;
36 
37 NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
38     ConstString p)
39     : m_prefix(p) {}
40 
41 bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
42     ConstString class_name) {
43   return class_name.GetStringRef().startswith(m_prefix.GetStringRef());
44 }
45 
46 NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
47     : m_name(n) {}
48 
49 bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
50     ConstString class_name) {
51   return (class_name == m_name);
52 }
53 
54 NSDictionary_Additionals::AdditionalFormatters<
55     CXXFunctionSummaryFormat::Callback> &
56 NSDictionary_Additionals::GetAdditionalSummaries() {
57   static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
58   return g_map;
59 }
60 
61 NSDictionary_Additionals::AdditionalFormatters<
62     CXXSyntheticChildren::CreateFrontEndCallback> &
63 NSDictionary_Additionals::GetAdditionalSynthetics() {
64   static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
65       g_map;
66   return g_map;
67 }
68 
69 static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
70   CompilerType compiler_type;
71 
72   ClangASTContext *target_ast_context = target_sp->GetScratchClangASTContext();
73 
74   if (target_ast_context) {
75     ConstString g___lldb_autogen_nspair("__lldb_autogen_nspair");
76 
77     compiler_type =
78         target_ast_context->GetTypeForIdentifier<clang::CXXRecordDecl>(
79             g___lldb_autogen_nspair);
80 
81     if (!compiler_type) {
82       compiler_type = target_ast_context->CreateRecordType(
83           nullptr, lldb::eAccessPublic, g___lldb_autogen_nspair.GetCString(),
84           clang::TTK_Struct, lldb::eLanguageTypeC);
85 
86       if (compiler_type) {
87         ClangASTContext::StartTagDeclarationDefinition(compiler_type);
88         CompilerType id_compiler_type =
89             target_ast_context->GetBasicType(eBasicTypeObjCID);
90         ClangASTContext::AddFieldToRecordType(
91             compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
92         ClangASTContext::AddFieldToRecordType(
93             compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
94         ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
95       }
96     }
97   }
98   return compiler_type;
99 }
100 
101 namespace lldb_private {
102 namespace formatters {
103 class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
104 public:
105   NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
106 
107   ~NSDictionaryISyntheticFrontEnd() override;
108 
109   size_t CalculateNumChildren() override;
110 
111   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
112 
113   bool Update() override;
114 
115   bool MightHaveChildren() override;
116 
117   size_t GetIndexOfChildWithName(const ConstString &name) override;
118 
119 private:
120   struct DataDescriptor_32 {
121     uint32_t _used : 26;
122     uint32_t _szidx : 6;
123   };
124 
125   struct DataDescriptor_64 {
126     uint64_t _used : 58;
127     uint32_t _szidx : 6;
128   };
129 
130   struct DictionaryItemDescriptor {
131     lldb::addr_t key_ptr;
132     lldb::addr_t val_ptr;
133     lldb::ValueObjectSP valobj_sp;
134   };
135 
136   ExecutionContextRef m_exe_ctx_ref;
137   uint8_t m_ptr_size;
138   lldb::ByteOrder m_order;
139   DataDescriptor_32 *m_data_32;
140   DataDescriptor_64 *m_data_64;
141   lldb::addr_t m_data_ptr;
142   CompilerType m_pair_type;
143   std::vector<DictionaryItemDescriptor> m_children;
144 };
145 
146 class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
147 public:
148   NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
149 
150   ~NSDictionary1SyntheticFrontEnd() override = default;
151 
152   size_t CalculateNumChildren() override;
153 
154   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
155 
156   bool Update() override;
157 
158   bool MightHaveChildren() override;
159 
160   size_t GetIndexOfChildWithName(const ConstString &name) override;
161 
162 private:
163   ValueObjectSP m_pair;
164 };
165 
166 class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
167 public:
168   NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
169 
170   ~NSDictionaryMSyntheticFrontEnd() override;
171 
172   size_t CalculateNumChildren() override;
173 
174   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
175 
176   bool Update() override;
177 
178   bool MightHaveChildren() override;
179 
180   size_t GetIndexOfChildWithName(const ConstString &name) override;
181 
182 private:
183   struct DataDescriptor_32 {
184     uint32_t _used : 26;
185     uint32_t _kvo : 1;
186     uint32_t _size;
187     uint32_t _mutations;
188     uint32_t _objs_addr;
189     uint32_t _keys_addr;
190   };
191 
192   struct DataDescriptor_64 {
193     uint64_t _used : 58;
194     uint32_t _kvo : 1;
195     uint64_t _size;
196     uint64_t _mutations;
197     uint64_t _objs_addr;
198     uint64_t _keys_addr;
199   };
200 
201   struct DictionaryItemDescriptor {
202     lldb::addr_t key_ptr;
203     lldb::addr_t val_ptr;
204     lldb::ValueObjectSP valobj_sp;
205   };
206 
207   ExecutionContextRef m_exe_ctx_ref;
208   uint8_t m_ptr_size;
209   lldb::ByteOrder m_order;
210   DataDescriptor_32 *m_data_32;
211   DataDescriptor_64 *m_data_64;
212   CompilerType m_pair_type;
213   std::vector<DictionaryItemDescriptor> m_children;
214 };
215 } // namespace formatters
216 } // namespace lldb_private
217 
218 template <bool name_entries>
219 bool lldb_private::formatters::NSDictionarySummaryProvider(
220     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
221   static ConstString g_TypeHint("NSDictionary");
222   ProcessSP process_sp = valobj.GetProcessSP();
223   if (!process_sp)
224     return false;
225 
226   ObjCLanguageRuntime *runtime =
227       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
228           lldb::eLanguageTypeObjC);
229 
230   if (!runtime)
231     return false;
232 
233   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
234       runtime->GetClassDescriptor(valobj));
235 
236   if (!descriptor || !descriptor->IsValid())
237     return false;
238 
239   uint32_t ptr_size = process_sp->GetAddressByteSize();
240   bool is_64bit = (ptr_size == 8);
241 
242   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
243 
244   if (!valobj_addr)
245     return false;
246 
247   uint64_t value = 0;
248 
249   ConstString class_name(descriptor->GetClassName());
250 
251   static const ConstString g_DictionaryI("__NSDictionaryI");
252   static const ConstString g_DictionaryM("__NSDictionaryM");
253   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
254 
255   if (class_name.IsEmpty())
256     return false;
257 
258   if (class_name == g_DictionaryI) {
259     Error error;
260     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
261                                                       ptr_size, 0, error);
262     if (error.Fail())
263       return false;
264     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
265   } else if (class_name == g_DictionaryM) {
266     Error error;
267     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
268                                                       ptr_size, 0, error);
269     if (error.Fail())
270       return false;
271     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
272   } else if (class_name == g_Dictionary1) {
273     value = 1;
274   }
275   /*else if (!strcmp(class_name,"__NSCFDictionary"))
276    {
277    Error error;
278    value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ?
279    20 : 12), 4, 0, error);
280    if (error.Fail())
281    return false;
282    if (is_64bit)
283    value &= ~0x0f1f000000000000UL;
284    }*/
285   else {
286     auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
287     for (auto &candidate : map) {
288       if (candidate.first && candidate.first->Match(class_name))
289         return candidate.second(valobj, stream, options);
290     }
291     return false;
292   }
293 
294   std::string prefix, suffix;
295   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
296     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
297                                             suffix)) {
298       prefix.clear();
299       suffix.clear();
300     }
301   }
302 
303   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
304                 value == 1 ? "" : "s", suffix.c_str());
305   return true;
306 }
307 
308 SyntheticChildrenFrontEnd *
309 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
310     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
311   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
312   if (!process_sp)
313     return nullptr;
314   ObjCLanguageRuntime *runtime =
315       (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
316           lldb::eLanguageTypeObjC);
317   if (!runtime)
318     return nullptr;
319 
320   CompilerType valobj_type(valobj_sp->GetCompilerType());
321   Flags flags(valobj_type.GetTypeInfo());
322 
323   if (flags.IsClear(eTypeIsPointer)) {
324     Error error;
325     valobj_sp = valobj_sp->AddressOf(error);
326     if (error.Fail() || !valobj_sp)
327       return nullptr;
328   }
329 
330   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
331       runtime->GetClassDescriptor(*valobj_sp));
332 
333   if (!descriptor || !descriptor->IsValid())
334     return nullptr;
335 
336   ConstString class_name(descriptor->GetClassName());
337 
338   static const ConstString g_DictionaryI("__NSDictionaryI");
339   static const ConstString g_DictionaryM("__NSDictionaryM");
340   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
341 
342   if (class_name.IsEmpty())
343     return nullptr;
344 
345   if (class_name == g_DictionaryI) {
346     return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
347   } else if (class_name == g_DictionaryM) {
348     return (new NSDictionaryMSyntheticFrontEnd(valobj_sp));
349   } else if (class_name == g_Dictionary1) {
350     return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
351   } else {
352     auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
353     for (auto &candidate : map) {
354       if (candidate.first && candidate.first->Match((class_name)))
355         return candidate.second(synth, valobj_sp);
356     }
357   }
358 
359   return nullptr;
360 }
361 
362 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
363     NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
364     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
365       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
366       m_pair_type() {}
367 
368 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
369     ~NSDictionaryISyntheticFrontEnd() {
370   delete m_data_32;
371   m_data_32 = nullptr;
372   delete m_data_64;
373   m_data_64 = nullptr;
374 }
375 
376 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
377     GetIndexOfChildWithName(const ConstString &name) {
378   const char *item_name = name.GetCString();
379   uint32_t idx = ExtractIndexFromString(item_name);
380   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
381     return UINT32_MAX;
382   return idx;
383 }
384 
385 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
386     CalculateNumChildren() {
387   if (!m_data_32 && !m_data_64)
388     return 0;
389   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
390 }
391 
392 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
393   m_children.clear();
394   delete m_data_32;
395   m_data_32 = nullptr;
396   delete m_data_64;
397   m_data_64 = nullptr;
398   m_ptr_size = 0;
399   ValueObjectSP valobj_sp = m_backend.GetSP();
400   if (!valobj_sp)
401     return false;
402   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
403   Error error;
404   error.Clear();
405   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
406   if (!process_sp)
407     return false;
408   m_ptr_size = process_sp->GetAddressByteSize();
409   m_order = process_sp->GetByteOrder();
410   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
411   if (m_ptr_size == 4) {
412     m_data_32 = new DataDescriptor_32();
413     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
414                            error);
415   } else {
416     m_data_64 = new DataDescriptor_64();
417     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
418                            error);
419   }
420   if (error.Fail())
421     return false;
422   m_data_ptr = data_location + m_ptr_size;
423   return false;
424 }
425 
426 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
427     MightHaveChildren() {
428   return true;
429 }
430 
431 lldb::ValueObjectSP
432 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
433     size_t idx) {
434   uint32_t num_children = CalculateNumChildren();
435 
436   if (idx >= num_children)
437     return lldb::ValueObjectSP();
438 
439   if (m_children.empty()) {
440     // do the scan phase
441     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
442 
443     uint32_t tries = 0;
444     uint32_t test_idx = 0;
445 
446     while (tries < num_children) {
447       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
448       val_at_idx = key_at_idx + m_ptr_size;
449       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
450       if (!process_sp)
451         return lldb::ValueObjectSP();
452       Error error;
453       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
454       if (error.Fail())
455         return lldb::ValueObjectSP();
456       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
457       if (error.Fail())
458         return lldb::ValueObjectSP();
459 
460       test_idx++;
461 
462       if (!key_at_idx || !val_at_idx)
463         continue;
464       tries++;
465 
466       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
467                                              lldb::ValueObjectSP()};
468 
469       m_children.push_back(descriptor);
470     }
471   }
472 
473   if (idx >= m_children.size()) // should never happen
474     return lldb::ValueObjectSP();
475 
476   DictionaryItemDescriptor &dict_item = m_children[idx];
477   if (!dict_item.valobj_sp) {
478     if (!m_pair_type.IsValid()) {
479       TargetSP target_sp(m_backend.GetTargetSP());
480       if (!target_sp)
481         return ValueObjectSP();
482       m_pair_type = GetLLDBNSPairType(target_sp);
483     }
484     if (!m_pair_type.IsValid())
485       return ValueObjectSP();
486 
487     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
488 
489     if (m_ptr_size == 8) {
490       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
491       *data_ptr = dict_item.key_ptr;
492       *(data_ptr + 1) = dict_item.val_ptr;
493     } else {
494       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
495       *data_ptr = dict_item.key_ptr;
496       *(data_ptr + 1) = dict_item.val_ptr;
497     }
498 
499     StreamString idx_name;
500     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
501     DataExtractor data(buffer_sp, m_order, m_ptr_size);
502     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetData(), data,
503                                                     m_exe_ctx_ref, m_pair_type);
504   }
505   return dict_item.valobj_sp;
506 }
507 
508 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
509     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
510     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
511 
512 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
513     GetIndexOfChildWithName(const ConstString &name) {
514   static const ConstString g_zero("[0]");
515 
516   if (name == g_zero)
517     return 0;
518 
519   return UINT32_MAX;
520 }
521 
522 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
523     CalculateNumChildren() {
524   return 1;
525 }
526 
527 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
528   m_pair.reset();
529   return false;
530 }
531 
532 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
533     MightHaveChildren() {
534   return true;
535 }
536 
537 lldb::ValueObjectSP
538 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
539     size_t idx) {
540   if (idx != 0)
541     return lldb::ValueObjectSP();
542 
543   if (m_pair.get())
544     return m_pair;
545 
546   auto process_sp(m_backend.GetProcessSP());
547   if (!process_sp)
548     return nullptr;
549 
550   auto ptr_size = process_sp->GetAddressByteSize();
551 
552   lldb::addr_t key_ptr =
553       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
554   lldb::addr_t value_ptr = key_ptr + ptr_size;
555 
556   Error error;
557 
558   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
559   if (error.Fail())
560     return nullptr;
561   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
562   if (error.Fail())
563     return nullptr;
564 
565   auto pair_type =
566       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
567 
568   DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
569 
570   if (ptr_size == 8) {
571     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
572     *data_ptr = key_at_idx;
573     *(data_ptr + 1) = value_at_idx;
574   } else {
575     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
576     *data_ptr = key_at_idx;
577     *(data_ptr + 1) = value_at_idx;
578   }
579 
580   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
581   m_pair = CreateValueObjectFromData(
582       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
583 
584   return m_pair;
585 }
586 
587 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
588     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
589     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
590       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
591       m_pair_type() {}
592 
593 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
594     ~NSDictionaryMSyntheticFrontEnd() {
595   delete m_data_32;
596   m_data_32 = nullptr;
597   delete m_data_64;
598   m_data_64 = nullptr;
599 }
600 
601 size_t lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
602     GetIndexOfChildWithName(const ConstString &name) {
603   const char *item_name = name.GetCString();
604   uint32_t idx = ExtractIndexFromString(item_name);
605   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
606     return UINT32_MAX;
607   return idx;
608 }
609 
610 size_t lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
611     CalculateNumChildren() {
612   if (!m_data_32 && !m_data_64)
613     return 0;
614   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
615 }
616 
617 bool lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::Update() {
618   m_children.clear();
619   ValueObjectSP valobj_sp = m_backend.GetSP();
620   m_ptr_size = 0;
621   delete m_data_32;
622   m_data_32 = nullptr;
623   delete m_data_64;
624   m_data_64 = nullptr;
625   if (!valobj_sp)
626     return false;
627   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
628   Error error;
629   error.Clear();
630   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
631   if (!process_sp)
632     return false;
633   m_ptr_size = process_sp->GetAddressByteSize();
634   m_order = process_sp->GetByteOrder();
635   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
636   if (m_ptr_size == 4) {
637     m_data_32 = new DataDescriptor_32();
638     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
639                            error);
640   } else {
641     m_data_64 = new DataDescriptor_64();
642     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
643                            error);
644   }
645   if (error.Fail())
646     return false;
647   return false;
648 }
649 
650 bool lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::
651     MightHaveChildren() {
652   return true;
653 }
654 
655 lldb::ValueObjectSP
656 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(
657     size_t idx) {
658   lldb::addr_t m_keys_ptr =
659       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
660   lldb::addr_t m_values_ptr =
661       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
662 
663   uint32_t num_children = CalculateNumChildren();
664 
665   if (idx >= num_children)
666     return lldb::ValueObjectSP();
667 
668   if (m_children.empty()) {
669     // do the scan phase
670     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
671 
672     uint32_t tries = 0;
673     uint32_t test_idx = 0;
674 
675     while (tries < num_children) {
676       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
677       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
678       ;
679       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
680       if (!process_sp)
681         return lldb::ValueObjectSP();
682       Error error;
683       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
684       if (error.Fail())
685         return lldb::ValueObjectSP();
686       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
687       if (error.Fail())
688         return lldb::ValueObjectSP();
689 
690       test_idx++;
691 
692       if (!key_at_idx || !val_at_idx)
693         continue;
694       tries++;
695 
696       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
697                                              lldb::ValueObjectSP()};
698 
699       m_children.push_back(descriptor);
700     }
701   }
702 
703   if (idx >= m_children.size()) // should never happen
704     return lldb::ValueObjectSP();
705 
706   DictionaryItemDescriptor &dict_item = m_children[idx];
707   if (!dict_item.valobj_sp) {
708     if (!m_pair_type.IsValid()) {
709       TargetSP target_sp(m_backend.GetTargetSP());
710       if (!target_sp)
711         return ValueObjectSP();
712       m_pair_type = GetLLDBNSPairType(target_sp);
713     }
714     if (!m_pair_type.IsValid())
715       return ValueObjectSP();
716 
717     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
718 
719     if (m_ptr_size == 8) {
720       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
721       *data_ptr = dict_item.key_ptr;
722       *(data_ptr + 1) = dict_item.val_ptr;
723     } else {
724       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
725       *data_ptr = dict_item.key_ptr;
726       *(data_ptr + 1) = dict_item.val_ptr;
727     }
728 
729     StreamString idx_name;
730     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
731     DataExtractor data(buffer_sp, m_order, m_ptr_size);
732     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetData(), data,
733                                                     m_exe_ctx_ref, m_pair_type);
734   }
735   return dict_item.valobj_sp;
736 }
737 
738 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
739     ValueObject &, Stream &, const TypeSummaryOptions &);
740 
741 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
742     ValueObject &, Stream &, const TypeSummaryOptions &);
743