xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===-- NSDictionary.cpp --------------------------------------------------===//
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 "CFBasicHash.h"
14 #include "NSDictionary.h"
15 
16 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
17 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
18 
19 #include "lldb/Core/ValueObject.h"
20 #include "lldb/Core/ValueObjectConstResult.h"
21 #include "lldb/DataFormatters/FormattersHelpers.h"
22 #include "lldb/Target/Language.h"
23 #include "lldb/Target/StackFrame.h"
24 #include "lldb/Target/Target.h"
25 #include "lldb/Utility/DataBufferHeap.h"
26 #include "lldb/Utility/Endian.h"
27 #include "lldb/Utility/Status.h"
28 #include "lldb/Utility/Stream.h"
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 using namespace lldb_private::formatters;
33 
Prefix(ConstString p)34 NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
35     ConstString p)
36     : m_prefix(p) {}
37 
Match(ConstString class_name)38 bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
39     ConstString class_name) {
40   return class_name.GetStringRef().starts_with(m_prefix.GetStringRef());
41 }
42 
Full(ConstString n)43 NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
44     : m_name(n) {}
45 
Match(ConstString class_name)46 bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
47     ConstString class_name) {
48   return (class_name == m_name);
49 }
50 
51 NSDictionary_Additionals::AdditionalFormatters<
52     CXXFunctionSummaryFormat::Callback> &
GetAdditionalSummaries()53 NSDictionary_Additionals::GetAdditionalSummaries() {
54   static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
55   return g_map;
56 }
57 
58 NSDictionary_Additionals::AdditionalFormatters<
59     CXXSyntheticChildren::CreateFrontEndCallback> &
GetAdditionalSynthetics()60 NSDictionary_Additionals::GetAdditionalSynthetics() {
61   static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
62       g_map;
63   return g_map;
64 }
65 
GetLLDBNSPairType(TargetSP target_sp)66 static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
67   CompilerType compiler_type;
68   TypeSystemClangSP scratch_ts_sp =
69       ScratchTypeSystemClang::GetForTarget(*target_sp);
70 
71   if (!scratch_ts_sp)
72     return compiler_type;
73 
74   static constexpr llvm::StringLiteral g_lldb_autogen_nspair("__lldb_autogen_nspair");
75 
76   compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(g_lldb_autogen_nspair);
77 
78   if (!compiler_type) {
79     compiler_type = scratch_ts_sp->CreateRecordType(
80         nullptr, OptionalClangModuleID(), lldb::eAccessPublic,
81         g_lldb_autogen_nspair, llvm::to_underlying(clang::TagTypeKind::Struct),
82         lldb::eLanguageTypeC);
83 
84     if (compiler_type) {
85       TypeSystemClang::StartTagDeclarationDefinition(compiler_type);
86       CompilerType id_compiler_type =
87           scratch_ts_sp->GetBasicType(eBasicTypeObjCID);
88       TypeSystemClang::AddFieldToRecordType(
89           compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
90       TypeSystemClang::AddFieldToRecordType(
91           compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
92       TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type);
93     }
94   }
95   return compiler_type;
96 }
97 
98 namespace lldb_private {
99 namespace formatters {
100 class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
101 public:
102   NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
103 
104   ~NSDictionaryISyntheticFrontEnd() override;
105 
106   llvm::Expected<uint32_t> CalculateNumChildren() override;
107 
108   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
109 
110   lldb::ChildCacheState Update() override;
111 
112   bool MightHaveChildren() override;
113 
114   size_t GetIndexOfChildWithName(ConstString name) override;
115 
116 private:
117   struct DataDescriptor_32 {
118     uint32_t _used : 26;
119     uint32_t _szidx : 6;
120   };
121 
122   struct DataDescriptor_64 {
123     uint64_t _used : 58;
124     uint32_t _szidx : 6;
125   };
126 
127   struct DictionaryItemDescriptor {
128     lldb::addr_t key_ptr;
129     lldb::addr_t val_ptr;
130     lldb::ValueObjectSP valobj_sp;
131   };
132 
133   ExecutionContextRef m_exe_ctx_ref;
134   uint8_t m_ptr_size = 8;
135   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
136   DataDescriptor_32 *m_data_32 = nullptr;
137   DataDescriptor_64 *m_data_64 = nullptr;
138   lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS;
139   CompilerType m_pair_type;
140   std::vector<DictionaryItemDescriptor> m_children;
141 };
142 
143 class NSConstantDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
144 public:
145   NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
146 
147   llvm::Expected<uint32_t> CalculateNumChildren() override;
148 
149   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
150 
151   lldb::ChildCacheState Update() override;
152 
153   bool MightHaveChildren() override;
154 
155   size_t GetIndexOfChildWithName(ConstString name) override;
156 
157 private:
158   ExecutionContextRef m_exe_ctx_ref;
159   CompilerType m_pair_type;
160   uint8_t m_ptr_size = 8;
161   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
162   unsigned int m_size = 0;
163   lldb::addr_t m_keys_ptr = LLDB_INVALID_ADDRESS;
164   lldb::addr_t m_objects_ptr = LLDB_INVALID_ADDRESS;
165 
166   struct DictionaryItemDescriptor {
167     lldb::addr_t key_ptr;
168     lldb::addr_t val_ptr;
169     lldb::ValueObjectSP valobj_sp;
170   };
171 
172   std::vector<DictionaryItemDescriptor> m_children;
173 };
174 
175 class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
176 public:
177   NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
178 
179   llvm::Expected<uint32_t> CalculateNumChildren() override;
180 
181   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
182 
183   lldb::ChildCacheState Update() override;
184 
185   bool MightHaveChildren() override;
186 
187   size_t GetIndexOfChildWithName(ConstString name) override;
188 
189 private:
190   struct DictionaryItemDescriptor {
191     lldb::addr_t key_ptr;
192     lldb::addr_t val_ptr;
193     lldb::ValueObjectSP valobj_sp;
194   };
195 
196   ExecutionContextRef m_exe_ctx_ref;
197   uint8_t m_ptr_size = 8;
198   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
199 
200   CFBasicHash m_hashtable;
201 
202   CompilerType m_pair_type;
203   std::vector<DictionaryItemDescriptor> m_children;
204 };
205 
206 class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
207 public:
208   NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
209 
210   ~NSDictionary1SyntheticFrontEnd() override = default;
211 
212   llvm::Expected<uint32_t> CalculateNumChildren() override;
213 
214   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
215 
216   lldb::ChildCacheState Update() override;
217 
218   bool MightHaveChildren() override;
219 
220   size_t GetIndexOfChildWithName(ConstString name) override;
221 
222 private:
223   ValueObjectSP m_pair;
224 };
225 
226 template <typename D32, typename D64>
227 class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
228 public:
229   GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
230 
231   ~GenericNSDictionaryMSyntheticFrontEnd() override;
232 
233   llvm::Expected<uint32_t> CalculateNumChildren() override;
234 
235   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
236 
237   lldb::ChildCacheState Update() override;
238 
239   bool MightHaveChildren() override;
240 
241   size_t GetIndexOfChildWithName(ConstString name) override;
242 
243 private:
244   struct DictionaryItemDescriptor {
245     lldb::addr_t key_ptr;
246     lldb::addr_t val_ptr;
247     lldb::ValueObjectSP valobj_sp;
248   };
249 
250   ExecutionContextRef m_exe_ctx_ref;
251   uint8_t m_ptr_size = 8;
252   lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
253   D32 *m_data_32;
254   D64 *m_data_64;
255   CompilerType m_pair_type;
256   std::vector<DictionaryItemDescriptor> m_children;
257 };
258 
259 namespace Foundation1100 {
260   class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
261   public:
262     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
263 
264     ~NSDictionaryMSyntheticFrontEnd() override;
265 
266     llvm::Expected<uint32_t> CalculateNumChildren() override;
267 
268     lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
269 
270     lldb::ChildCacheState Update() override;
271 
272     bool MightHaveChildren() override;
273 
274     size_t GetIndexOfChildWithName(ConstString name) override;
275 
276   private:
277     struct DataDescriptor_32 {
278       uint32_t _used : 26;
279       uint32_t _kvo : 1;
280       uint32_t _size;
281       uint32_t _mutations;
282       uint32_t _objs_addr;
283       uint32_t _keys_addr;
284     };
285 
286     struct DataDescriptor_64 {
287       uint64_t _used : 58;
288       uint32_t _kvo : 1;
289       uint64_t _size;
290       uint64_t _mutations;
291       uint64_t _objs_addr;
292       uint64_t _keys_addr;
293     };
294 
295     struct DictionaryItemDescriptor {
296       lldb::addr_t key_ptr;
297       lldb::addr_t val_ptr;
298       lldb::ValueObjectSP valobj_sp;
299     };
300 
301     ExecutionContextRef m_exe_ctx_ref;
302     uint8_t m_ptr_size = 8;
303     lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
304     DataDescriptor_32 *m_data_32 = nullptr;
305     DataDescriptor_64 *m_data_64 = nullptr;
306     CompilerType m_pair_type;
307     std::vector<DictionaryItemDescriptor> m_children;
308   };
309 }
310 
311 namespace Foundation1428 {
312   namespace {
313     struct DataDescriptor_32 {
314       uint32_t _used : 26;
315       uint32_t _kvo : 1;
316       uint32_t _size;
317       uint32_t _buffer;
GetSizelldb_private::formatters::Foundation1428::__anon1422a3d50111::DataDescriptor_32318       uint64_t GetSize() { return _size; }
319     };
320 
321     struct DataDescriptor_64 {
322       uint64_t _used : 58;
323       uint32_t _kvo : 1;
324       uint64_t _size;
325       uint64_t _buffer;
GetSizelldb_private::formatters::Foundation1428::__anon1422a3d50111::DataDescriptor_64326       uint64_t GetSize() { return _size; }
327     };
328   }
329 
330   using NSDictionaryMSyntheticFrontEnd =
331     GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
332 }
333 
334 namespace Foundation1437 {
335     static const uint64_t NSDictionaryCapacities[] = {
336         0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
337         2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
338         214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
339         6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
340         111638519, 180634607, 292272623, 472907251
341     };
342 
343     static const size_t NSDictionaryNumSizeBuckets =
344         sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
345 
346     namespace {
347     struct DataDescriptor_32 {
348       uint32_t _buffer;
349       uint32_t _muts;
350       uint32_t _used : 25;
351       uint32_t _kvo : 1;
352       uint32_t _szidx : 6;
353 
GetSizelldb_private::formatters::Foundation1437::__anon1422a3d50211::DataDescriptor_32354       uint64_t GetSize() {
355         return (_szidx) >= NSDictionaryNumSizeBuckets ?
356             0 : NSDictionaryCapacities[_szidx];
357       }
358     };
359 
360     struct DataDescriptor_64 {
361       uint64_t _buffer;
362       uint32_t _muts;
363       uint32_t _used : 25;
364       uint32_t _kvo : 1;
365       uint32_t _szidx : 6;
366 
GetSizelldb_private::formatters::Foundation1437::__anon1422a3d50211::DataDescriptor_64367       uint64_t GetSize() {
368         return (_szidx) >= NSDictionaryNumSizeBuckets ?
369             0 : NSDictionaryCapacities[_szidx];
370       }
371     };
372     } // namespace
373 
374   using NSDictionaryMSyntheticFrontEnd =
375     GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
376 
377   template <typename DD>
378   uint64_t
__NSDictionaryMSize_Impl(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)379   __NSDictionaryMSize_Impl(lldb_private::Process &process,
380                            lldb::addr_t valobj_addr, Status &error) {
381     const lldb::addr_t start_of_descriptor =
382         valobj_addr + process.GetAddressByteSize();
383     DD descriptor = DD();
384     process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
385                        error);
386     if (error.Fail()) {
387       return 0;
388     }
389     return descriptor._used;
390   }
391 
392   uint64_t
__NSDictionaryMSize(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)393   __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
394                Status &error) {
395     if (process.GetAddressByteSize() == 4) {
396       return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
397                                                          error);
398     } else {
399       return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
400                                                          error);
401     }
402   }
403 
404 }
405 } // namespace formatters
406 } // namespace lldb_private
407 
408 template <bool name_entries>
NSDictionarySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)409 bool lldb_private::formatters::NSDictionarySummaryProvider(
410     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
411   static constexpr llvm::StringLiteral g_TypeHint("NSDictionary");
412   ProcessSP process_sp = valobj.GetProcessSP();
413   if (!process_sp)
414     return false;
415 
416   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
417 
418   if (!runtime)
419     return false;
420 
421   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
422       runtime->GetNonKVOClassDescriptor(valobj));
423 
424   if (!descriptor || !descriptor->IsValid())
425     return false;
426 
427   uint32_t ptr_size = process_sp->GetAddressByteSize();
428   bool is_64bit = (ptr_size == 8);
429 
430   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
431 
432   if (!valobj_addr)
433     return false;
434 
435   uint64_t value = 0;
436 
437   ConstString class_name(descriptor->GetClassName());
438 
439   static const ConstString g_DictionaryI("__NSDictionaryI");
440   static const ConstString g_DictionaryM("__NSDictionaryM");
441   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
442   static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
443   static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
444   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
445   static const ConstString g_Dictionary0("__NSDictionary0");
446   static const ConstString g_DictionaryCF("__CFDictionary");
447   static const ConstString g_DictionaryNSCF("__NSCFDictionary");
448   static const ConstString g_DictionaryCFRef("CFDictionaryRef");
449   static const ConstString g_ConstantDictionary("NSConstantDictionary");
450 
451   if (class_name.IsEmpty())
452     return false;
453 
454   if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
455     Status error;
456     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
457                                                       ptr_size, 0, error);
458     if (error.Fail())
459       return false;
460 
461     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
462   } else if (class_name == g_ConstantDictionary) {
463     Status error;
464     value = process_sp->ReadUnsignedIntegerFromMemory(
465         valobj_addr + 2 * ptr_size, ptr_size, 0, error);
466     if (error.Fail())
467       return false;
468   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
469              class_name == g_DictionaryMFrozen) {
470     AppleObjCRuntime *apple_runtime =
471     llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
472     Status error;
473     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
474       value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
475                                                   error);
476     } else {
477       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
478                                                         ptr_size, 0, error);
479       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
480     }
481     if (error.Fail())
482       return false;
483   } else if (class_name == g_Dictionary1) {
484     value = 1;
485   } else if (class_name == g_Dictionary0) {
486     value = 0;
487   } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
488              class_name == g_DictionaryCFRef) {
489     ExecutionContext exe_ctx(process_sp);
490     CFBasicHash cfbh;
491     if (!cfbh.Update(valobj_addr, exe_ctx))
492       return false;
493     value = cfbh.GetCount();
494   } else {
495     auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
496     for (auto &candidate : map) {
497       if (candidate.first && candidate.first->Match(class_name))
498         return candidate.second(valobj, stream, options);
499     }
500     return false;
501   }
502 
503   llvm::StringRef prefix, suffix;
504   if (Language *language = Language::FindPlugin(options.GetLanguage()))
505     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
506 
507   stream << prefix;
508   stream.Printf("%" PRIu64 " %s%s", value, "key/value pair",
509                 value == 1 ? "" : "s");
510   stream << suffix;
511   return true;
512 }
513 
514 SyntheticChildrenFrontEnd *
NSDictionarySyntheticFrontEndCreator(CXXSyntheticChildren * synth,lldb::ValueObjectSP valobj_sp)515 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
516     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
517   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
518   if (!process_sp)
519     return nullptr;
520   AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
521       ObjCLanguageRuntime::Get(*process_sp));
522   if (!runtime)
523     return nullptr;
524 
525   CompilerType valobj_type(valobj_sp->GetCompilerType());
526   Flags flags(valobj_type.GetTypeInfo());
527 
528   if (flags.IsClear(eTypeIsPointer)) {
529     Status error;
530     valobj_sp = valobj_sp->AddressOf(error);
531     if (error.Fail() || !valobj_sp)
532       return nullptr;
533   }
534 
535   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
536       runtime->GetClassDescriptor(*valobj_sp));
537 
538   if (!descriptor || !descriptor->IsValid())
539     return nullptr;
540 
541   ConstString class_name(descriptor->GetClassName());
542 
543   static const ConstString g_DictionaryI("__NSDictionaryI");
544   static const ConstString g_DictionaryM("__NSDictionaryM");
545   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
546   static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
547   static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
548   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
549   static const ConstString g_Dictionary0("__NSDictionary0");
550   static const ConstString g_DictionaryCF("__CFDictionary");
551   static const ConstString g_DictionaryNSCF("__NSCFDictionary");
552   static const ConstString g_DictionaryCFRef("CFDictionaryRef");
553   static const ConstString g_ConstantDictionary("NSConstantDictionary");
554 
555   if (class_name.IsEmpty())
556     return nullptr;
557 
558   if (class_name == g_DictionaryI) {
559     return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
560   } else if (class_name == g_ConstantDictionary) {
561     return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
562   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) {
563     if (runtime->GetFoundationVersion() >= 1437) {
564       return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
565     } else if (runtime->GetFoundationVersion() >= 1428) {
566       return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
567     } else {
568       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
569     }
570   } else if (class_name == g_DictionaryMLegacy) {
571     return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
572   } else if (class_name == g_Dictionary1) {
573     return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
574   } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
575              class_name == g_DictionaryCFRef) {
576     return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
577   } else {
578     auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
579     for (auto &candidate : map) {
580       if (candidate.first && candidate.first->Match((class_name)))
581         return candidate.second(synth, valobj_sp);
582     }
583   }
584 
585   return nullptr;
586 }
587 
588 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)589     NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
590     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
591 
592 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
~NSDictionaryISyntheticFrontEnd()593     ~NSDictionaryISyntheticFrontEnd() {
594   delete m_data_32;
595   m_data_32 = nullptr;
596   delete m_data_64;
597   m_data_64 = nullptr;
598 }
599 
600 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)601     GetIndexOfChildWithName(ConstString name) {
602   const char *item_name = name.GetCString();
603   uint32_t idx = ExtractIndexFromString(item_name);
604   if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
605     return UINT32_MAX;
606   return idx;
607 }
608 
609 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()610     NSDictionaryISyntheticFrontEnd::CalculateNumChildren() {
611   if (!m_data_32 && !m_data_64)
612     return 0;
613   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
614 }
615 
616 lldb::ChildCacheState
Update()617 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
618   m_children.clear();
619   delete m_data_32;
620   m_data_32 = nullptr;
621   delete m_data_64;
622   m_data_64 = nullptr;
623   m_ptr_size = 0;
624   ValueObjectSP valobj_sp = m_backend.GetSP();
625   if (!valobj_sp)
626     return lldb::ChildCacheState::eRefetch;
627   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
628   Status error;
629   error.Clear();
630   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
631   if (!process_sp)
632     return lldb::ChildCacheState::eRefetch;
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     m_data_32 = new DataDescriptor_32();
638     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
639                            error);
640   } else {
641     m_data_64 = new DataDescriptor_64();
642     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
643                            error);
644   }
645   if (error.Fail())
646     return lldb::ChildCacheState::eRefetch;
647   m_data_ptr = data_location + m_ptr_size;
648   return lldb::ChildCacheState::eRefetch;
649 }
650 
651 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
MightHaveChildren()652     MightHaveChildren() {
653   return true;
654 }
655 
656 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)657 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
658     uint32_t idx) {
659   uint32_t num_children = CalculateNumChildrenIgnoringErrors();
660 
661   if (idx >= num_children)
662     return lldb::ValueObjectSP();
663 
664   if (m_children.empty()) {
665     // do the scan phase
666     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
667 
668     uint32_t tries = 0;
669     uint32_t test_idx = 0;
670 
671     while (tries < num_children) {
672       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
673       val_at_idx = key_at_idx + m_ptr_size;
674       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
675       if (!process_sp)
676         return lldb::ValueObjectSP();
677       Status error;
678       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
679       if (error.Fail())
680         return lldb::ValueObjectSP();
681       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
682       if (error.Fail())
683         return lldb::ValueObjectSP();
684 
685       test_idx++;
686 
687       if (!key_at_idx || !val_at_idx)
688         continue;
689       tries++;
690 
691       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
692                                              lldb::ValueObjectSP()};
693 
694       m_children.push_back(descriptor);
695     }
696   }
697 
698   if (idx >= m_children.size()) // should never happen
699     return lldb::ValueObjectSP();
700 
701   DictionaryItemDescriptor &dict_item = m_children[idx];
702   if (!dict_item.valobj_sp) {
703     if (!m_pair_type.IsValid()) {
704       TargetSP target_sp(m_backend.GetTargetSP());
705       if (!target_sp)
706         return ValueObjectSP();
707       m_pair_type = GetLLDBNSPairType(target_sp);
708     }
709     if (!m_pair_type.IsValid())
710       return ValueObjectSP();
711 
712     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
713 
714     if (m_ptr_size == 8) {
715       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
716       *data_ptr = dict_item.key_ptr;
717       *(data_ptr + 1) = dict_item.val_ptr;
718     } else {
719       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
720       *data_ptr = dict_item.key_ptr;
721       *(data_ptr + 1) = dict_item.val_ptr;
722     }
723 
724     StreamString idx_name;
725     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
726     DataExtractor data(buffer_sp, m_order, m_ptr_size);
727     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
728                                                     m_exe_ctx_ref, m_pair_type);
729   }
730   return dict_item.valobj_sp;
731 }
732 
733 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)734     NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
735     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(),
736       m_pair_type() {}
737 
738 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)739     GetIndexOfChildWithName(ConstString name) {
740   const char *item_name = name.GetCString();
741   const uint32_t idx = ExtractIndexFromString(item_name);
742   if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
743     return UINT32_MAX;
744   return idx;
745 }
746 
747 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()748     NSCFDictionarySyntheticFrontEnd::CalculateNumChildren() {
749   if (!m_hashtable.IsValid())
750     return 0;
751   return m_hashtable.GetCount();
752 }
753 
754 lldb::ChildCacheState
Update()755 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::Update() {
756   m_children.clear();
757   ValueObjectSP valobj_sp = m_backend.GetSP();
758   m_ptr_size = 0;
759   if (!valobj_sp)
760     return lldb::ChildCacheState::eRefetch;
761   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
762 
763   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
764   if (!process_sp)
765     return lldb::ChildCacheState::eRefetch;
766   m_ptr_size = process_sp->GetAddressByteSize();
767   m_order = process_sp->GetByteOrder();
768   return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref)
769              ? lldb::ChildCacheState::eReuse
770              : lldb::ChildCacheState::eRefetch;
771 }
772 
773 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
MightHaveChildren()774     MightHaveChildren() {
775   return true;
776 }
777 
778 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)779 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex(
780     uint32_t idx) {
781   lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
782   lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
783 
784   const uint32_t num_children = CalculateNumChildrenIgnoringErrors();
785 
786   if (idx >= num_children)
787     return lldb::ValueObjectSP();
788 
789   if (m_children.empty()) {
790     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
791     if (!process_sp)
792       return lldb::ValueObjectSP();
793 
794     Status error;
795     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
796 
797     uint32_t tries = 0;
798     uint32_t test_idx = 0;
799 
800     // Iterate over inferior memory, reading key/value pointers by shifting each
801     // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
802     // fails, otherwise, continue until the number of tries matches the number
803     // of childen.
804     while (tries < num_children) {
805       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
806       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
807 
808       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
809       if (error.Fail())
810         return lldb::ValueObjectSP();
811       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
812       if (error.Fail())
813         return lldb::ValueObjectSP();
814 
815       test_idx++;
816 
817       if (!key_at_idx || !val_at_idx)
818         continue;
819       tries++;
820 
821       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
822                                              lldb::ValueObjectSP()};
823 
824       m_children.push_back(descriptor);
825     }
826   }
827 
828   if (idx >= m_children.size()) // should never happen
829     return lldb::ValueObjectSP();
830 
831   DictionaryItemDescriptor &dict_item = m_children[idx];
832   if (!dict_item.valobj_sp) {
833     if (!m_pair_type.IsValid()) {
834       TargetSP target_sp(m_backend.GetTargetSP());
835       if (!target_sp)
836         return ValueObjectSP();
837       m_pair_type = GetLLDBNSPairType(target_sp);
838     }
839     if (!m_pair_type.IsValid())
840       return ValueObjectSP();
841 
842     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
843 
844     switch (m_ptr_size) {
845     case 0: // architecture has no clue - fail
846       return lldb::ValueObjectSP();
847     case 4: {
848       uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
849       *data_ptr = dict_item.key_ptr;
850       *(data_ptr + 1) = dict_item.val_ptr;
851     } break;
852     case 8: {
853       uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
854       *data_ptr = dict_item.key_ptr;
855       *(data_ptr + 1) = dict_item.val_ptr;
856     } break;
857     default:
858       lldbassert(false && "pointer size is not 4 nor 8");
859     }
860 
861     StreamString idx_name;
862     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
863     DataExtractor data(buffer_sp, m_order, m_ptr_size);
864     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
865                                                     m_exe_ctx_ref, m_pair_type);
866   }
867   return dict_item.valobj_sp;
868 }
869 
870 lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)871     NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
872     : SyntheticChildrenFrontEnd(*valobj_sp) {}
873 
874 size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)875     GetIndexOfChildWithName(ConstString name) {
876   const char *item_name = name.GetCString();
877   uint32_t idx = ExtractIndexFromString(item_name);
878   if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
879     return UINT32_MAX;
880   return idx;
881 }
882 
883 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()884     NSConstantDictionarySyntheticFrontEnd::CalculateNumChildren() {
885   return m_size;
886 }
887 
888 lldb::ChildCacheState
Update()889 lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::Update() {
890   ValueObjectSP valobj_sp = m_backend.GetSP();
891   if (!valobj_sp)
892     return lldb::ChildCacheState::eRefetch;
893   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
894   Status error;
895   error.Clear();
896   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
897   if (!process_sp)
898     return lldb::ChildCacheState::eRefetch;
899   m_ptr_size = process_sp->GetAddressByteSize();
900   m_order = process_sp->GetByteOrder();
901   uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
902   m_size = process_sp->ReadUnsignedIntegerFromMemory(
903       valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
904   if (error.Fail())
905     return lldb::ChildCacheState::eRefetch;
906   m_keys_ptr =
907       process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
908   if (error.Fail())
909     return lldb::ChildCacheState::eRefetch;
910   m_objects_ptr =
911       process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
912 
913   return error.Success() ? lldb::ChildCacheState::eReuse
914                          : lldb::ChildCacheState::eRefetch;
915 }
916 
917 bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
MightHaveChildren()918     MightHaveChildren() {
919   return true;
920 }
921 
922 lldb::ValueObjectSP lldb_private::formatters::
GetChildAtIndex(uint32_t idx)923     NSConstantDictionarySyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
924   uint32_t num_children = CalculateNumChildrenIgnoringErrors();
925 
926   if (idx >= num_children)
927     return lldb::ValueObjectSP();
928 
929   if (m_children.empty()) {
930     // do the scan phase
931     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
932     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
933     if (!process_sp)
934       return lldb::ValueObjectSP();
935 
936     for (unsigned int child = 0; child < num_children; ++child) {
937       Status error;
938       key_at_idx = process_sp->ReadPointerFromMemory(
939           m_keys_ptr + child * m_ptr_size, error);
940       if (error.Fail())
941         return lldb::ValueObjectSP();
942       val_at_idx = process_sp->ReadPointerFromMemory(
943           m_objects_ptr + child * m_ptr_size, error);
944       if (error.Fail())
945         return lldb::ValueObjectSP();
946       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
947                                              lldb::ValueObjectSP()};
948       m_children.push_back(descriptor);
949     }
950   }
951 
952   if (idx >= m_children.size()) // should never happen
953     return lldb::ValueObjectSP();
954 
955   DictionaryItemDescriptor &dict_item = m_children[idx];
956   if (!dict_item.valobj_sp) {
957     if (!m_pair_type.IsValid()) {
958       TargetSP target_sp(m_backend.GetTargetSP());
959       if (!target_sp)
960         return ValueObjectSP();
961       m_pair_type = GetLLDBNSPairType(target_sp);
962     }
963     if (!m_pair_type.IsValid())
964       return ValueObjectSP();
965 
966     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
967 
968     if (m_ptr_size == 8) {
969       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
970       *data_ptr = dict_item.key_ptr;
971       *(data_ptr + 1) = dict_item.val_ptr;
972     } else {
973       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
974       *data_ptr = dict_item.key_ptr;
975       *(data_ptr + 1) = dict_item.val_ptr;
976     }
977 
978     StreamString idx_name;
979     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
980     DataExtractor data(buffer_sp, m_order, m_ptr_size);
981     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
982                                                     m_exe_ctx_ref, m_pair_type);
983   }
984   return dict_item.valobj_sp;
985 }
986 
987 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)988     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
989     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
990 
991 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)992     GetIndexOfChildWithName(ConstString name) {
993   static const ConstString g_zero("[0]");
994   return name == g_zero ? 0 : UINT32_MAX;
995 }
996 
997 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()998     NSDictionary1SyntheticFrontEnd::CalculateNumChildren() {
999   return 1;
1000 }
1001 
1002 lldb::ChildCacheState
Update()1003 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
1004   m_pair.reset();
1005   return lldb::ChildCacheState::eRefetch;
1006 }
1007 
1008 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
MightHaveChildren()1009     MightHaveChildren() {
1010   return true;
1011 }
1012 
1013 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)1014 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
1015     uint32_t idx) {
1016   if (idx != 0)
1017     return lldb::ValueObjectSP();
1018 
1019   if (m_pair.get())
1020     return m_pair;
1021 
1022   auto process_sp(m_backend.GetProcessSP());
1023   if (!process_sp)
1024     return nullptr;
1025 
1026   auto ptr_size = process_sp->GetAddressByteSize();
1027 
1028   lldb::addr_t key_ptr =
1029       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
1030   lldb::addr_t value_ptr = key_ptr + ptr_size;
1031 
1032   Status error;
1033 
1034   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
1035   if (error.Fail())
1036     return nullptr;
1037   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
1038   if (error.Fail())
1039     return nullptr;
1040 
1041   auto pair_type =
1042       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
1043 
1044   WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1045 
1046   if (ptr_size == 8) {
1047     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1048     *data_ptr = key_at_idx;
1049     *(data_ptr + 1) = value_at_idx;
1050   } else {
1051     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1052     *data_ptr = key_at_idx;
1053     *(data_ptr + 1) = value_at_idx;
1054   }
1055 
1056   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1057   m_pair = CreateValueObjectFromData(
1058       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1059 
1060   return m_pair;
1061 }
1062 
1063 template <typename D32, typename D64>
1064 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32, D64>::
GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)1065     GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1066     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
1067       m_data_32(nullptr), m_data_64(nullptr), m_pair_type() {}
1068 
1069 template <typename D32, typename D64>
1070 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
1071     D32, D64>::GenericNSDictionaryMSyntheticFrontEnd::
~GenericNSDictionaryMSyntheticFrontEnd()1072     ~GenericNSDictionaryMSyntheticFrontEnd() {
1073   delete m_data_32;
1074   m_data_32 = nullptr;
1075   delete m_data_64;
1076   m_data_64 = nullptr;
1077 }
1078 
1079 template <typename D32, typename D64>
1080 size_t lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
GetIndexOfChildWithName(ConstString name)1081     D32, D64>::GetIndexOfChildWithName(ConstString name) {
1082   const char *item_name = name.GetCString();
1083   uint32_t idx = ExtractIndexFromString(item_name);
1084   if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
1085     return UINT32_MAX;
1086   return idx;
1087 }
1088 
1089 template <typename D32, typename D64>
1090 llvm::Expected<uint32_t>
1091 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
CalculateNumChildren()1092     D32, D64>::CalculateNumChildren() {
1093   if (!m_data_32 && !m_data_64)
1094     return 0;
1095   return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1096 }
1097 
1098 template <typename D32, typename D64>
1099 lldb::ChildCacheState
1100 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,
Update()1101                                                                 D64>::Update() {
1102   m_children.clear();
1103   ValueObjectSP valobj_sp = m_backend.GetSP();
1104   m_ptr_size = 0;
1105   delete m_data_32;
1106   m_data_32 = nullptr;
1107   delete m_data_64;
1108   m_data_64 = nullptr;
1109   if (!valobj_sp)
1110     return lldb::ChildCacheState::eRefetch;
1111   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1112   Status error;
1113   error.Clear();
1114   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1115   if (!process_sp)
1116     return lldb::ChildCacheState::eRefetch;
1117   m_ptr_size = process_sp->GetAddressByteSize();
1118   m_order = process_sp->GetByteOrder();
1119   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1120   if (m_ptr_size == 4) {
1121     m_data_32 = new D32();
1122     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1123                            error);
1124   } else {
1125     m_data_64 = new D64();
1126     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1127                            error);
1128   }
1129 
1130   return error.Success() ? lldb::ChildCacheState::eReuse
1131                          : lldb::ChildCacheState::eRefetch;
1132 }
1133 
1134 template <typename D32, typename D64>
1135 bool
1136 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
MightHaveChildren()1137     MightHaveChildren() {
1138   return true;
1139 }
1140 
1141 template <typename D32, typename D64>
1142 lldb::ValueObjectSP
1143 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
GetChildAtIndex(uint32_t idx)1144     D32, D64>::GetChildAtIndex(uint32_t idx) {
1145   lldb::addr_t m_keys_ptr;
1146   lldb::addr_t m_values_ptr;
1147   if (m_data_32) {
1148     uint32_t size = m_data_32->GetSize();
1149     m_keys_ptr = m_data_32->_buffer;
1150     m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1151   } else {
1152     uint32_t size = m_data_64->GetSize();
1153     m_keys_ptr = m_data_64->_buffer;
1154     m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1155   }
1156 
1157   uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1158 
1159   if (idx >= num_children)
1160     return lldb::ValueObjectSP();
1161 
1162   if (m_children.empty()) {
1163     // do the scan phase
1164     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1165 
1166     uint32_t tries = 0;
1167     uint32_t test_idx = 0;
1168 
1169     while (tries < num_children) {
1170       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1171       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1172       ;
1173       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1174       if (!process_sp)
1175         return lldb::ValueObjectSP();
1176       Status error;
1177       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1178       if (error.Fail())
1179         return lldb::ValueObjectSP();
1180       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1181       if (error.Fail())
1182         return lldb::ValueObjectSP();
1183 
1184       test_idx++;
1185 
1186       if (!key_at_idx || !val_at_idx)
1187         continue;
1188       tries++;
1189 
1190       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1191                                              lldb::ValueObjectSP()};
1192 
1193       m_children.push_back(descriptor);
1194     }
1195   }
1196 
1197   if (idx >= m_children.size()) // should never happen
1198     return lldb::ValueObjectSP();
1199 
1200   DictionaryItemDescriptor &dict_item = m_children[idx];
1201   if (!dict_item.valobj_sp) {
1202     if (!m_pair_type.IsValid()) {
1203       TargetSP target_sp(m_backend.GetTargetSP());
1204       if (!target_sp)
1205         return ValueObjectSP();
1206       m_pair_type = GetLLDBNSPairType(target_sp);
1207     }
1208     if (!m_pair_type.IsValid())
1209       return ValueObjectSP();
1210 
1211     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1212 
1213     if (m_ptr_size == 8) {
1214       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1215       *data_ptr = dict_item.key_ptr;
1216       *(data_ptr + 1) = dict_item.val_ptr;
1217     } else {
1218       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1219       *data_ptr = dict_item.key_ptr;
1220       *(data_ptr + 1) = dict_item.val_ptr;
1221     }
1222 
1223     StreamString idx_name;
1224     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1225     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1226     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1227                                                     m_exe_ctx_ref, m_pair_type);
1228   }
1229   return dict_item.valobj_sp;
1230 }
1231 
1232 lldb_private::formatters::Foundation1100::NSDictionaryMSyntheticFrontEnd::
NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)1233     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1234     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
1235 
1236 lldb_private::formatters::Foundation1100::
~NSDictionaryMSyntheticFrontEnd()1237   NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
1238   delete m_data_32;
1239   m_data_32 = nullptr;
1240   delete m_data_64;
1241   m_data_64 = nullptr;
1242 }
1243 
1244 size_t
1245 lldb_private::formatters::Foundation1100::
GetIndexOfChildWithName(ConstString name)1246   NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
1247   const char *item_name = name.GetCString();
1248   uint32_t idx = ExtractIndexFromString(item_name);
1249   if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
1250     return UINT32_MAX;
1251   return idx;
1252 }
1253 
1254 llvm::Expected<uint32_t> lldb_private::formatters::Foundation1100::
CalculateNumChildren()1255     NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
1256   if (!m_data_32 && !m_data_64)
1257     return 0;
1258   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1259 }
1260 
1261 lldb::ChildCacheState lldb_private::formatters::Foundation1100::
Update()1262     NSDictionaryMSyntheticFrontEnd::Update() {
1263   m_children.clear();
1264   ValueObjectSP valobj_sp = m_backend.GetSP();
1265   m_ptr_size = 0;
1266   delete m_data_32;
1267   m_data_32 = nullptr;
1268   delete m_data_64;
1269   m_data_64 = nullptr;
1270   if (!valobj_sp)
1271     return lldb::ChildCacheState::eRefetch;
1272   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1273   Status error;
1274   error.Clear();
1275   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1276   if (!process_sp)
1277     return lldb::ChildCacheState::eRefetch;
1278   m_ptr_size = process_sp->GetAddressByteSize();
1279   m_order = process_sp->GetByteOrder();
1280   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1281   if (m_ptr_size == 4) {
1282     m_data_32 = new DataDescriptor_32();
1283     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1284                            error);
1285   } else {
1286     m_data_64 = new DataDescriptor_64();
1287     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1288                            error);
1289   }
1290 
1291   return error.Success() ? lldb::ChildCacheState::eReuse
1292                          : lldb::ChildCacheState::eRefetch;
1293 }
1294 
1295 bool
1296 lldb_private::formatters::Foundation1100::
MightHaveChildren()1297   NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
1298   return true;
1299 }
1300 
1301 lldb::ValueObjectSP
1302 lldb_private::formatters::Foundation1100::
GetChildAtIndex(uint32_t idx)1303   NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
1304   lldb::addr_t m_keys_ptr =
1305       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1306   lldb::addr_t m_values_ptr =
1307       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1308 
1309   uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1310 
1311   if (idx >= num_children)
1312     return lldb::ValueObjectSP();
1313 
1314   if (m_children.empty()) {
1315     // do the scan phase
1316     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1317 
1318     uint32_t tries = 0;
1319     uint32_t test_idx = 0;
1320 
1321     while (tries < num_children) {
1322       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1323       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1324       ;
1325       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1326       if (!process_sp)
1327         return lldb::ValueObjectSP();
1328       Status error;
1329       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1330       if (error.Fail())
1331         return lldb::ValueObjectSP();
1332       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1333       if (error.Fail())
1334         return lldb::ValueObjectSP();
1335 
1336       test_idx++;
1337 
1338       if (!key_at_idx || !val_at_idx)
1339         continue;
1340       tries++;
1341 
1342       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1343                                              lldb::ValueObjectSP()};
1344 
1345       m_children.push_back(descriptor);
1346     }
1347   }
1348 
1349   if (idx >= m_children.size()) // should never happen
1350     return lldb::ValueObjectSP();
1351 
1352   DictionaryItemDescriptor &dict_item = m_children[idx];
1353   if (!dict_item.valobj_sp) {
1354     if (!m_pair_type.IsValid()) {
1355       TargetSP target_sp(m_backend.GetTargetSP());
1356       if (!target_sp)
1357         return ValueObjectSP();
1358       m_pair_type = GetLLDBNSPairType(target_sp);
1359     }
1360     if (!m_pair_type.IsValid())
1361       return ValueObjectSP();
1362 
1363     WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1364 
1365     if (m_ptr_size == 8) {
1366       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1367       *data_ptr = dict_item.key_ptr;
1368       *(data_ptr + 1) = dict_item.val_ptr;
1369     } else {
1370       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1371       *data_ptr = dict_item.key_ptr;
1372       *(data_ptr + 1) = dict_item.val_ptr;
1373     }
1374 
1375     StreamString idx_name;
1376     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1377     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1378     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1379                                                     m_exe_ctx_ref, m_pair_type);
1380   }
1381   return dict_item.valobj_sp;
1382 }
1383 
1384 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1385     ValueObject &, Stream &, const TypeSummaryOptions &);
1386 
1387 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1388     ValueObject &, Stream &, const TypeSummaryOptions &);
1389