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