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