1 //===-- NSDictionary.cpp ----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <mutex>
10 
11 #include "clang/AST/DeclCXX.h"
12 
13 #include "NSDictionary.h"
14 
15 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
16 
17 #include "lldb/Core/ValueObject.h"
18 #include "lldb/Core/ValueObjectConstResult.h"
19 #include "lldb/DataFormatters/FormattersHelpers.h"
20 #include "lldb/Symbol/ClangASTContext.h"
21 #include "lldb/Target/Language.h"
22 #include "lldb/Target/StackFrame.h"
23 #include "lldb/Target/Target.h"
24 #include "lldb/Utility/DataBufferHeap.h"
25 #include "lldb/Utility/Endian.h"
26 #include "lldb/Utility/Status.h"
27 #include "lldb/Utility/Stream.h"
28 
29 using namespace lldb;
30 using namespace lldb_private;
31 using namespace lldb_private::formatters;
32 
33 NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
34     ConstString p)
35     : m_prefix(p) {}
36 
37 bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
38     ConstString class_name) {
39   return class_name.GetStringRef().startswith(m_prefix.GetStringRef());
40 }
41 
42 NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
43     : m_name(n) {}
44 
45 bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
46     ConstString class_name) {
47   return (class_name == m_name);
48 }
49 
50 NSDictionary_Additionals::AdditionalFormatters<
51     CXXFunctionSummaryFormat::Callback> &
52 NSDictionary_Additionals::GetAdditionalSummaries() {
53   static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
54   return g_map;
55 }
56 
57 NSDictionary_Additionals::AdditionalFormatters<
58     CXXSyntheticChildren::CreateFrontEndCallback> &
59 NSDictionary_Additionals::GetAdditionalSynthetics() {
60   static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
61       g_map;
62   return g_map;
63 }
64 
65 static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
66   CompilerType compiler_type;
67 
68   ClangASTContext *target_ast_context = ClangASTContext::GetScratch(*target_sp);
69 
70   if (target_ast_context) {
71     ConstString g___lldb_autogen_nspair("__lldb_autogen_nspair");
72 
73     compiler_type =
74         target_ast_context->GetTypeForIdentifier<clang::CXXRecordDecl>(
75             g___lldb_autogen_nspair);
76 
77     if (!compiler_type) {
78       compiler_type = target_ast_context->CreateRecordType(
79           nullptr, lldb::eAccessPublic, g___lldb_autogen_nspair.GetCString(),
80           clang::TTK_Struct, lldb::eLanguageTypeC);
81 
82       if (compiler_type) {
83         ClangASTContext::StartTagDeclarationDefinition(compiler_type);
84         CompilerType id_compiler_type =
85             target_ast_context->GetBasicType(eBasicTypeObjCID);
86         ClangASTContext::AddFieldToRecordType(
87             compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
88         ClangASTContext::AddFieldToRecordType(
89             compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
90         ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
91       }
92     }
93   }
94   return compiler_type;
95 }
96 
97 namespace lldb_private {
98 namespace formatters {
99 class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
100 public:
101   NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
102 
103   ~NSDictionaryISyntheticFrontEnd() override;
104 
105   size_t CalculateNumChildren() override;
106 
107   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
108 
109   bool Update() override;
110 
111   bool MightHaveChildren() override;
112 
113   size_t GetIndexOfChildWithName(ConstString name) override;
114 
115 private:
116   struct DataDescriptor_32 {
117     uint32_t _used : 26;
118     uint32_t _szidx : 6;
119   };
120 
121   struct DataDescriptor_64 {
122     uint64_t _used : 58;
123     uint32_t _szidx : 6;
124   };
125 
126   struct DictionaryItemDescriptor {
127     lldb::addr_t key_ptr;
128     lldb::addr_t val_ptr;
129     lldb::ValueObjectSP valobj_sp;
130   };
131 
132   ExecutionContextRef m_exe_ctx_ref;
133   uint8_t m_ptr_size;
134   lldb::ByteOrder m_order;
135   DataDescriptor_32 *m_data_32;
136   DataDescriptor_64 *m_data_64;
137   lldb::addr_t m_data_ptr;
138   CompilerType m_pair_type;
139   std::vector<DictionaryItemDescriptor> m_children;
140 };
141 
142 class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
143 public:
144   NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
145 
146   ~NSDictionary1SyntheticFrontEnd() override = default;
147 
148   size_t CalculateNumChildren() override;
149 
150   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
151 
152   bool Update() override;
153 
154   bool MightHaveChildren() override;
155 
156   size_t GetIndexOfChildWithName(ConstString name) override;
157 
158 private:
159   ValueObjectSP m_pair;
160 };
161 
162 template <typename D32, typename D64>
163 class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
164 public:
165   GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
166 
167   ~GenericNSDictionaryMSyntheticFrontEnd() override;
168 
169   size_t CalculateNumChildren() override;
170 
171   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
172 
173   bool Update() override;
174 
175   bool MightHaveChildren() override;
176 
177   size_t GetIndexOfChildWithName(ConstString name) override;
178 
179 private:
180   struct DictionaryItemDescriptor {
181     lldb::addr_t key_ptr;
182     lldb::addr_t val_ptr;
183     lldb::ValueObjectSP valobj_sp;
184   };
185 
186   ExecutionContextRef m_exe_ctx_ref;
187   uint8_t m_ptr_size;
188   lldb::ByteOrder m_order;
189   D32 *m_data_32;
190   D64 *m_data_64;
191   CompilerType m_pair_type;
192   std::vector<DictionaryItemDescriptor> m_children;
193 };
194 
195 namespace Foundation1100 {
196   class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
197   public:
198     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
199 
200     ~NSDictionaryMSyntheticFrontEnd() override;
201 
202     size_t CalculateNumChildren() override;
203 
204     lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
205 
206     bool Update() override;
207 
208     bool MightHaveChildren() override;
209 
210     size_t GetIndexOfChildWithName(ConstString name) override;
211 
212   private:
213     struct DataDescriptor_32 {
214       uint32_t _used : 26;
215       uint32_t _kvo : 1;
216       uint32_t _size;
217       uint32_t _mutations;
218       uint32_t _objs_addr;
219       uint32_t _keys_addr;
220     };
221 
222     struct DataDescriptor_64 {
223       uint64_t _used : 58;
224       uint32_t _kvo : 1;
225       uint64_t _size;
226       uint64_t _mutations;
227       uint64_t _objs_addr;
228       uint64_t _keys_addr;
229     };
230 
231     struct DictionaryItemDescriptor {
232       lldb::addr_t key_ptr;
233       lldb::addr_t val_ptr;
234       lldb::ValueObjectSP valobj_sp;
235     };
236 
237     ExecutionContextRef m_exe_ctx_ref;
238     uint8_t m_ptr_size;
239     lldb::ByteOrder m_order;
240     DataDescriptor_32 *m_data_32;
241     DataDescriptor_64 *m_data_64;
242     CompilerType m_pair_type;
243     std::vector<DictionaryItemDescriptor> m_children;
244   };
245 }
246 
247 namespace Foundation1428 {
248   struct DataDescriptor_32 {
249     uint32_t _used : 26;
250     uint32_t _kvo : 1;
251     uint32_t _size;
252     uint32_t _buffer;
253     uint64_t GetSize() { return _size; }
254   };
255 
256   struct DataDescriptor_64 {
257     uint64_t _used : 58;
258     uint32_t _kvo : 1;
259     uint64_t _size;
260     uint64_t _buffer;
261     uint64_t GetSize() { return _size; }
262   };
263 
264 
265 
266   using NSDictionaryMSyntheticFrontEnd =
267     GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
268 }
269 
270 namespace Foundation1437 {
271   static const uint64_t NSDictionaryCapacities[] = {
272       0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
273       2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
274       214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
275       6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
276       111638519, 180634607, 292272623, 472907251
277   };
278 
279   static const size_t NSDictionaryNumSizeBuckets = sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
280 
281   struct DataDescriptor_32 {
282     uint32_t _buffer;
283     uint32_t _muts;
284     uint32_t _used : 25;
285     uint32_t _kvo : 1;
286     uint32_t _szidx : 6;
287 
288     uint64_t GetSize() {
289       return (_szidx) >= NSDictionaryNumSizeBuckets ?
290           0 : NSDictionaryCapacities[_szidx];
291     }
292   };
293 
294   struct DataDescriptor_64 {
295     uint64_t _buffer;
296     uint32_t _muts;
297     uint32_t _used : 25;
298     uint32_t _kvo : 1;
299     uint32_t _szidx : 6;
300 
301     uint64_t GetSize() {
302       return (_szidx) >= NSDictionaryNumSizeBuckets ?
303           0 : NSDictionaryCapacities[_szidx];
304     }
305   };
306 
307   using NSDictionaryMSyntheticFrontEnd =
308     GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
309 
310   template <typename DD>
311   uint64_t
312   __NSDictionaryMSize_Impl(lldb_private::Process &process,
313                            lldb::addr_t valobj_addr, Status &error) {
314     const lldb::addr_t start_of_descriptor =
315         valobj_addr + process.GetAddressByteSize();
316     DD descriptor = DD();
317     process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
318                        error);
319     if (error.Fail()) {
320       return 0;
321     }
322     return descriptor._used;
323   }
324 
325   uint64_t
326   __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
327                Status &error) {
328     if (process.GetAddressByteSize() == 4) {
329       return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
330                                                          error);
331     } else {
332       return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
333                                                          error);
334     }
335   }
336 
337 }
338 } // namespace formatters
339 } // namespace lldb_private
340 
341 template <bool name_entries>
342 bool lldb_private::formatters::NSDictionarySummaryProvider(
343     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
344   static ConstString g_TypeHint("NSDictionary");
345   ProcessSP process_sp = valobj.GetProcessSP();
346   if (!process_sp)
347     return false;
348 
349   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
350 
351   if (!runtime)
352     return false;
353 
354   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
355       runtime->GetClassDescriptor(valobj));
356 
357   if (!descriptor || !descriptor->IsValid())
358     return false;
359 
360   uint32_t ptr_size = process_sp->GetAddressByteSize();
361   bool is_64bit = (ptr_size == 8);
362 
363   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
364 
365   if (!valobj_addr)
366     return false;
367 
368   uint64_t value = 0;
369 
370   ConstString class_name(descriptor->GetClassName());
371 
372   static const ConstString g_DictionaryI("__NSDictionaryI");
373   static const ConstString g_DictionaryM("__NSDictionaryM");
374   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
375   static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
376   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
377   static const ConstString g_Dictionary0("__NSDictionary0");
378   static const ConstString g_DictionaryCF("__NSCFDictionary");
379 
380   if (class_name.IsEmpty())
381     return false;
382 
383   if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
384     Status error;
385     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
386                                                       ptr_size, 0, error);
387     if (error.Fail())
388       return false;
389     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
390   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
391              class_name == g_DictionaryCF) {
392     AppleObjCRuntime *apple_runtime =
393     llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
394     Status error;
395     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
396       value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
397                                                   error);
398     } else {
399       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
400                                                         ptr_size, 0, error);
401       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
402     }
403     if (error.Fail())
404       return false;
405   } else if (class_name == g_Dictionary1) {
406     value = 1;
407   } else if (class_name == g_Dictionary0) {
408     value = 0;
409   }
410   else {
411     auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
412     for (auto &candidate : map) {
413       if (candidate.first && candidate.first->Match(class_name))
414         return candidate.second(valobj, stream, options);
415     }
416     return false;
417   }
418 
419   std::string prefix, suffix;
420   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
421     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
422                                             suffix)) {
423       prefix.clear();
424       suffix.clear();
425     }
426   }
427 
428   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
429                 value == 1 ? "" : "s", suffix.c_str());
430   return true;
431 }
432 
433 SyntheticChildrenFrontEnd *
434 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
435     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
436   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
437   if (!process_sp)
438     return nullptr;
439   AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
440       ObjCLanguageRuntime::Get(*process_sp));
441   if (!runtime)
442     return nullptr;
443 
444   CompilerType valobj_type(valobj_sp->GetCompilerType());
445   Flags flags(valobj_type.GetTypeInfo());
446 
447   if (flags.IsClear(eTypeIsPointer)) {
448     Status error;
449     valobj_sp = valobj_sp->AddressOf(error);
450     if (error.Fail() || !valobj_sp)
451       return nullptr;
452   }
453 
454   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
455       runtime->GetClassDescriptor(*valobj_sp));
456 
457   if (!descriptor || !descriptor->IsValid())
458     return nullptr;
459 
460   ConstString class_name(descriptor->GetClassName());
461 
462   static const ConstString g_DictionaryI("__NSDictionaryI");
463   static const ConstString g_DictionaryM("__NSDictionaryM");
464   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
465   static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
466   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
467   static const ConstString g_Dictionary0("__NSDictionary0");
468 
469   if (class_name.IsEmpty())
470     return nullptr;
471 
472   if (class_name == g_DictionaryI) {
473     return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
474   } else if (class_name == g_DictionaryM) {
475     if (runtime->GetFoundationVersion() >= 1437) {
476       return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
477     } else if (runtime->GetFoundationVersion() >= 1428) {
478       return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
479     } else {
480       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
481     }
482   } else if (class_name == g_DictionaryMLegacy) {
483       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
484   } else if (class_name == g_Dictionary1) {
485     return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
486   } else {
487     auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
488     for (auto &candidate : map) {
489       if (candidate.first && candidate.first->Match((class_name)))
490         return candidate.second(synth, valobj_sp);
491     }
492   }
493 
494   return nullptr;
495 }
496 
497 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
498     NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
499     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
500       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
501       m_pair_type() {}
502 
503 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
504     ~NSDictionaryISyntheticFrontEnd() {
505   delete m_data_32;
506   m_data_32 = nullptr;
507   delete m_data_64;
508   m_data_64 = nullptr;
509 }
510 
511 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
512     GetIndexOfChildWithName(ConstString name) {
513   const char *item_name = name.GetCString();
514   uint32_t idx = ExtractIndexFromString(item_name);
515   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
516     return UINT32_MAX;
517   return idx;
518 }
519 
520 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
521     CalculateNumChildren() {
522   if (!m_data_32 && !m_data_64)
523     return 0;
524   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
525 }
526 
527 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
528   m_children.clear();
529   delete m_data_32;
530   m_data_32 = nullptr;
531   delete m_data_64;
532   m_data_64 = nullptr;
533   m_ptr_size = 0;
534   ValueObjectSP valobj_sp = m_backend.GetSP();
535   if (!valobj_sp)
536     return false;
537   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
538   Status error;
539   error.Clear();
540   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
541   if (!process_sp)
542     return false;
543   m_ptr_size = process_sp->GetAddressByteSize();
544   m_order = process_sp->GetByteOrder();
545   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
546   if (m_ptr_size == 4) {
547     m_data_32 = new DataDescriptor_32();
548     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
549                            error);
550   } else {
551     m_data_64 = new DataDescriptor_64();
552     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
553                            error);
554   }
555   if (error.Fail())
556     return false;
557   m_data_ptr = data_location + m_ptr_size;
558   return false;
559 }
560 
561 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
562     MightHaveChildren() {
563   return true;
564 }
565 
566 lldb::ValueObjectSP
567 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
568     size_t idx) {
569   uint32_t num_children = CalculateNumChildren();
570 
571   if (idx >= num_children)
572     return lldb::ValueObjectSP();
573 
574   if (m_children.empty()) {
575     // do the scan phase
576     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
577 
578     uint32_t tries = 0;
579     uint32_t test_idx = 0;
580 
581     while (tries < num_children) {
582       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
583       val_at_idx = key_at_idx + m_ptr_size;
584       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
585       if (!process_sp)
586         return lldb::ValueObjectSP();
587       Status error;
588       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
589       if (error.Fail())
590         return lldb::ValueObjectSP();
591       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
592       if (error.Fail())
593         return lldb::ValueObjectSP();
594 
595       test_idx++;
596 
597       if (!key_at_idx || !val_at_idx)
598         continue;
599       tries++;
600 
601       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
602                                              lldb::ValueObjectSP()};
603 
604       m_children.push_back(descriptor);
605     }
606   }
607 
608   if (idx >= m_children.size()) // should never happen
609     return lldb::ValueObjectSP();
610 
611   DictionaryItemDescriptor &dict_item = m_children[idx];
612   if (!dict_item.valobj_sp) {
613     if (!m_pair_type.IsValid()) {
614       TargetSP target_sp(m_backend.GetTargetSP());
615       if (!target_sp)
616         return ValueObjectSP();
617       m_pair_type = GetLLDBNSPairType(target_sp);
618     }
619     if (!m_pair_type.IsValid())
620       return ValueObjectSP();
621 
622     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
623 
624     if (m_ptr_size == 8) {
625       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
626       *data_ptr = dict_item.key_ptr;
627       *(data_ptr + 1) = dict_item.val_ptr;
628     } else {
629       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
630       *data_ptr = dict_item.key_ptr;
631       *(data_ptr + 1) = dict_item.val_ptr;
632     }
633 
634     StreamString idx_name;
635     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
636     DataExtractor data(buffer_sp, m_order, m_ptr_size);
637     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
638                                                     m_exe_ctx_ref, m_pair_type);
639   }
640   return dict_item.valobj_sp;
641 }
642 
643 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
644     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
645     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
646 
647 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
648     GetIndexOfChildWithName(ConstString name) {
649   static const ConstString g_zero("[0]");
650   return name == g_zero ? 0 : UINT32_MAX;
651 }
652 
653 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
654     CalculateNumChildren() {
655   return 1;
656 }
657 
658 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
659   m_pair.reset();
660   return false;
661 }
662 
663 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
664     MightHaveChildren() {
665   return true;
666 }
667 
668 lldb::ValueObjectSP
669 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
670     size_t idx) {
671   if (idx != 0)
672     return lldb::ValueObjectSP();
673 
674   if (m_pair.get())
675     return m_pair;
676 
677   auto process_sp(m_backend.GetProcessSP());
678   if (!process_sp)
679     return nullptr;
680 
681   auto ptr_size = process_sp->GetAddressByteSize();
682 
683   lldb::addr_t key_ptr =
684       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
685   lldb::addr_t value_ptr = key_ptr + ptr_size;
686 
687   Status error;
688 
689   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
690   if (error.Fail())
691     return nullptr;
692   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
693   if (error.Fail())
694     return nullptr;
695 
696   auto pair_type =
697       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
698 
699   DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
700 
701   if (ptr_size == 8) {
702     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
703     *data_ptr = key_at_idx;
704     *(data_ptr + 1) = value_at_idx;
705   } else {
706     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
707     *data_ptr = key_at_idx;
708     *(data_ptr + 1) = value_at_idx;
709   }
710 
711   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
712   m_pair = CreateValueObjectFromData(
713       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
714 
715   return m_pair;
716 }
717 
718 template <typename D32, typename D64>
719 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
720     GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
721     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
722       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
723       m_pair_type() {}
724 
725 template <typename D32, typename D64>
726 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
727     ~GenericNSDictionaryMSyntheticFrontEnd() {
728   delete m_data_32;
729   m_data_32 = nullptr;
730   delete m_data_64;
731   m_data_64 = nullptr;
732 }
733 
734 template <typename D32, typename D64>
735 size_t
736 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::    GetIndexOfChildWithName(ConstString name) {
737   const char *item_name = name.GetCString();
738   uint32_t idx = ExtractIndexFromString(item_name);
739   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
740     return UINT32_MAX;
741   return idx;
742 }
743 
744 template <typename D32, typename D64>
745 size_t
746 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
747   if (!m_data_32 && !m_data_64)
748     return 0;
749   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
750 }
751 
752 template <typename D32, typename D64>
753 bool
754 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
755   Update() {
756   m_children.clear();
757   ValueObjectSP valobj_sp = m_backend.GetSP();
758   m_ptr_size = 0;
759   delete m_data_32;
760   m_data_32 = nullptr;
761   delete m_data_64;
762   m_data_64 = nullptr;
763   if (!valobj_sp)
764     return false;
765   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
766   Status error;
767   error.Clear();
768   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
769   if (!process_sp)
770     return false;
771   m_ptr_size = process_sp->GetAddressByteSize();
772   m_order = process_sp->GetByteOrder();
773   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
774   if (m_ptr_size == 4) {
775     m_data_32 = new D32();
776     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
777                            error);
778   } else {
779     m_data_64 = new D64();
780     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
781                            error);
782   }
783   if (error.Fail())
784     return false;
785   return false;
786 }
787 
788 template <typename D32, typename D64>
789 bool
790 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
791     MightHaveChildren() {
792   return true;
793 }
794 
795 template <typename D32, typename D64>
796 lldb::ValueObjectSP
797 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
798     GetChildAtIndex(
799     size_t idx) {
800   lldb::addr_t m_keys_ptr;
801   lldb::addr_t m_values_ptr;
802   if (m_data_32) {
803     uint32_t size = m_data_32->GetSize();
804     m_keys_ptr = m_data_32->_buffer;
805     m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
806   } else {
807     uint32_t size = m_data_64->GetSize();
808     m_keys_ptr = m_data_64->_buffer;
809     m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
810   }
811 
812   uint32_t num_children = CalculateNumChildren();
813 
814   if (idx >= num_children)
815     return lldb::ValueObjectSP();
816 
817   if (m_children.empty()) {
818     // do the scan phase
819     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
820 
821     uint32_t tries = 0;
822     uint32_t test_idx = 0;
823 
824     while (tries < num_children) {
825       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
826       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
827       ;
828       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
829       if (!process_sp)
830         return lldb::ValueObjectSP();
831       Status error;
832       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
833       if (error.Fail())
834         return lldb::ValueObjectSP();
835       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
836       if (error.Fail())
837         return lldb::ValueObjectSP();
838 
839       test_idx++;
840 
841       if (!key_at_idx || !val_at_idx)
842         continue;
843       tries++;
844 
845       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
846                                              lldb::ValueObjectSP()};
847 
848       m_children.push_back(descriptor);
849     }
850   }
851 
852   if (idx >= m_children.size()) // should never happen
853     return lldb::ValueObjectSP();
854 
855   DictionaryItemDescriptor &dict_item = m_children[idx];
856   if (!dict_item.valobj_sp) {
857     if (!m_pair_type.IsValid()) {
858       TargetSP target_sp(m_backend.GetTargetSP());
859       if (!target_sp)
860         return ValueObjectSP();
861       m_pair_type = GetLLDBNSPairType(target_sp);
862     }
863     if (!m_pair_type.IsValid())
864       return ValueObjectSP();
865 
866     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
867 
868     if (m_ptr_size == 8) {
869       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
870       *data_ptr = dict_item.key_ptr;
871       *(data_ptr + 1) = dict_item.val_ptr;
872     } else {
873       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
874       *data_ptr = dict_item.key_ptr;
875       *(data_ptr + 1) = dict_item.val_ptr;
876     }
877 
878     StreamString idx_name;
879     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
880     DataExtractor data(buffer_sp, m_order, m_ptr_size);
881     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
882                                                     m_exe_ctx_ref, m_pair_type);
883   }
884   return dict_item.valobj_sp;
885 }
886 
887 
888 lldb_private::formatters::Foundation1100::
889   NSDictionaryMSyntheticFrontEnd::
890     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
891     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
892       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
893       m_pair_type() {}
894 
895 lldb_private::formatters::Foundation1100::
896   NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
897   delete m_data_32;
898   m_data_32 = nullptr;
899   delete m_data_64;
900   m_data_64 = nullptr;
901 }
902 
903 size_t
904 lldb_private::formatters::Foundation1100::
905   NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
906   const char *item_name = name.GetCString();
907   uint32_t idx = ExtractIndexFromString(item_name);
908   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
909     return UINT32_MAX;
910   return idx;
911 }
912 
913 size_t
914 lldb_private::formatters::Foundation1100::
915   NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
916   if (!m_data_32 && !m_data_64)
917     return 0;
918   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
919 }
920 
921 bool
922 lldb_private::formatters::Foundation1100::
923   NSDictionaryMSyntheticFrontEnd::Update() {
924   m_children.clear();
925   ValueObjectSP valobj_sp = m_backend.GetSP();
926   m_ptr_size = 0;
927   delete m_data_32;
928   m_data_32 = nullptr;
929   delete m_data_64;
930   m_data_64 = nullptr;
931   if (!valobj_sp)
932     return false;
933   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
934   Status error;
935   error.Clear();
936   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
937   if (!process_sp)
938     return false;
939   m_ptr_size = process_sp->GetAddressByteSize();
940   m_order = process_sp->GetByteOrder();
941   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
942   if (m_ptr_size == 4) {
943     m_data_32 = new DataDescriptor_32();
944     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
945                            error);
946   } else {
947     m_data_64 = new DataDescriptor_64();
948     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
949                            error);
950   }
951   if (error.Fail())
952     return false;
953   return false;
954 }
955 
956 bool
957 lldb_private::formatters::Foundation1100::
958   NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
959   return true;
960 }
961 
962 lldb::ValueObjectSP
963 lldb_private::formatters::Foundation1100::
964   NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
965   lldb::addr_t m_keys_ptr =
966       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
967   lldb::addr_t m_values_ptr =
968       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
969 
970   uint32_t num_children = CalculateNumChildren();
971 
972   if (idx >= num_children)
973     return lldb::ValueObjectSP();
974 
975   if (m_children.empty()) {
976     // do the scan phase
977     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
978 
979     uint32_t tries = 0;
980     uint32_t test_idx = 0;
981 
982     while (tries < num_children) {
983       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
984       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
985       ;
986       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
987       if (!process_sp)
988         return lldb::ValueObjectSP();
989       Status error;
990       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
991       if (error.Fail())
992         return lldb::ValueObjectSP();
993       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
994       if (error.Fail())
995         return lldb::ValueObjectSP();
996 
997       test_idx++;
998 
999       if (!key_at_idx || !val_at_idx)
1000         continue;
1001       tries++;
1002 
1003       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1004                                              lldb::ValueObjectSP()};
1005 
1006       m_children.push_back(descriptor);
1007     }
1008   }
1009 
1010   if (idx >= m_children.size()) // should never happen
1011     return lldb::ValueObjectSP();
1012 
1013   DictionaryItemDescriptor &dict_item = m_children[idx];
1014   if (!dict_item.valobj_sp) {
1015     if (!m_pair_type.IsValid()) {
1016       TargetSP target_sp(m_backend.GetTargetSP());
1017       if (!target_sp)
1018         return ValueObjectSP();
1019       m_pair_type = GetLLDBNSPairType(target_sp);
1020     }
1021     if (!m_pair_type.IsValid())
1022       return ValueObjectSP();
1023 
1024     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1025 
1026     if (m_ptr_size == 8) {
1027       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1028       *data_ptr = dict_item.key_ptr;
1029       *(data_ptr + 1) = dict_item.val_ptr;
1030     } else {
1031       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1032       *data_ptr = dict_item.key_ptr;
1033       *(data_ptr + 1) = dict_item.val_ptr;
1034     }
1035 
1036     StreamString idx_name;
1037     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1038     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1039     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1040                                                     m_exe_ctx_ref, m_pair_type);
1041   }
1042   return dict_item.valobj_sp;
1043 }
1044 
1045 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1046     ValueObject &, Stream &, const TypeSummaryOptions &);
1047 
1048 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1049     ValueObject &, Stream &, const TypeSummaryOptions &);
1050