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