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