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