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