xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp (revision 2eb4d8dc723da3cf7d735a3226ae49da4c8c5dbc)
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_Dictionary1("__NSSingleEntryDictionaryI");
414   static const ConstString g_Dictionary0("__NSDictionary0");
415   static const ConstString g_DictionaryCF("__CFDictionary");
416   static const ConstString g_DictionaryNSCF("__NSCFDictionary");
417   static const ConstString g_DictionaryCFRef("CFDictionaryRef");
418 
419   if (class_name.IsEmpty())
420     return false;
421 
422   if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
423     Status error;
424     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
425                                                       ptr_size, 0, error);
426     if (error.Fail())
427       return false;
428 
429     value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
430   } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy) {
431     AppleObjCRuntime *apple_runtime =
432     llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
433     Status error;
434     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
435       value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
436                                                   error);
437     } else {
438       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
439                                                         ptr_size, 0, error);
440       value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
441     }
442     if (error.Fail())
443       return false;
444   } else if (class_name == g_Dictionary1) {
445     value = 1;
446   } else if (class_name == g_Dictionary0) {
447     value = 0;
448   } else if (class_name == g_DictionaryCF ||
449              class_name == g_DictionaryNSCF ||
450              class_name == g_DictionaryCFRef) {
451     ExecutionContext exe_ctx(process_sp);
452     CFBasicHash cfbh;
453     if (!cfbh.Update(valobj_addr, exe_ctx))
454       return false;
455     value = cfbh.GetCount();
456   } else {
457     auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
458     for (auto &candidate : map) {
459       if (candidate.first && candidate.first->Match(class_name))
460         return candidate.second(valobj, stream, options);
461     }
462     return false;
463   }
464 
465   std::string prefix, suffix;
466   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
467     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
468                                             suffix)) {
469       prefix.clear();
470       suffix.clear();
471     }
472   }
473 
474   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
475                 value == 1 ? "" : "s", suffix.c_str());
476   return true;
477 }
478 
479 SyntheticChildrenFrontEnd *
480 lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
481     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
482   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
483   if (!process_sp)
484     return nullptr;
485   AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
486       ObjCLanguageRuntime::Get(*process_sp));
487   if (!runtime)
488     return nullptr;
489 
490   CompilerType valobj_type(valobj_sp->GetCompilerType());
491   Flags flags(valobj_type.GetTypeInfo());
492 
493   if (flags.IsClear(eTypeIsPointer)) {
494     Status error;
495     valobj_sp = valobj_sp->AddressOf(error);
496     if (error.Fail() || !valobj_sp)
497       return nullptr;
498   }
499 
500   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
501       runtime->GetClassDescriptor(*valobj_sp));
502 
503   if (!descriptor || !descriptor->IsValid())
504     return nullptr;
505 
506   ConstString class_name(descriptor->GetClassName());
507 
508   static const ConstString g_DictionaryI("__NSDictionaryI");
509   static const ConstString g_DictionaryM("__NSDictionaryM");
510   static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
511   static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
512   static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
513   static const ConstString g_Dictionary0("__NSDictionary0");
514   static const ConstString g_DictionaryCF("__CFDictionary");
515   static const ConstString g_DictionaryNSCF("__NSCFDictionary");
516   static const ConstString g_DictionaryCFRef("CFDictionaryRef");
517 
518   if (class_name.IsEmpty())
519     return nullptr;
520 
521   if (class_name == g_DictionaryI) {
522     return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
523   } else if (class_name == g_DictionaryM) {
524     if (runtime->GetFoundationVersion() >= 1437) {
525       return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
526     } else if (runtime->GetFoundationVersion() >= 1428) {
527       return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
528     } else {
529       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
530     }
531   } else if (class_name == g_DictionaryMLegacy) {
532       return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
533   } else if (class_name == g_Dictionary1) {
534     return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
535   } else if (class_name == g_DictionaryCF ||
536              class_name == g_DictionaryNSCF ||
537              class_name == g_DictionaryCFRef) {
538     return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
539   } else {
540     auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
541     for (auto &candidate : map) {
542       if (candidate.first && candidate.first->Match((class_name)))
543         return candidate.second(synth, valobj_sp);
544     }
545   }
546 
547   return nullptr;
548 }
549 
550 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
551     NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
552     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
553       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
554       m_pair_type() {}
555 
556 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
557     ~NSDictionaryISyntheticFrontEnd() {
558   delete m_data_32;
559   m_data_32 = nullptr;
560   delete m_data_64;
561   m_data_64 = nullptr;
562 }
563 
564 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
565     GetIndexOfChildWithName(ConstString name) {
566   const char *item_name = name.GetCString();
567   uint32_t idx = ExtractIndexFromString(item_name);
568   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
569     return UINT32_MAX;
570   return idx;
571 }
572 
573 size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
574     CalculateNumChildren() {
575   if (!m_data_32 && !m_data_64)
576     return 0;
577   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
578 }
579 
580 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
581   m_children.clear();
582   delete m_data_32;
583   m_data_32 = nullptr;
584   delete m_data_64;
585   m_data_64 = nullptr;
586   m_ptr_size = 0;
587   ValueObjectSP valobj_sp = m_backend.GetSP();
588   if (!valobj_sp)
589     return false;
590   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
591   Status error;
592   error.Clear();
593   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
594   if (!process_sp)
595     return false;
596   m_ptr_size = process_sp->GetAddressByteSize();
597   m_order = process_sp->GetByteOrder();
598   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
599   if (m_ptr_size == 4) {
600     m_data_32 = new DataDescriptor_32();
601     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
602                            error);
603   } else {
604     m_data_64 = new DataDescriptor_64();
605     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
606                            error);
607   }
608   if (error.Fail())
609     return false;
610   m_data_ptr = data_location + m_ptr_size;
611   return false;
612 }
613 
614 bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
615     MightHaveChildren() {
616   return true;
617 }
618 
619 lldb::ValueObjectSP
620 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
621     size_t idx) {
622   uint32_t num_children = CalculateNumChildren();
623 
624   if (idx >= num_children)
625     return lldb::ValueObjectSP();
626 
627   if (m_children.empty()) {
628     // do the scan phase
629     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
630 
631     uint32_t tries = 0;
632     uint32_t test_idx = 0;
633 
634     while (tries < num_children) {
635       key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
636       val_at_idx = key_at_idx + m_ptr_size;
637       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
638       if (!process_sp)
639         return lldb::ValueObjectSP();
640       Status error;
641       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
642       if (error.Fail())
643         return lldb::ValueObjectSP();
644       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
645       if (error.Fail())
646         return lldb::ValueObjectSP();
647 
648       test_idx++;
649 
650       if (!key_at_idx || !val_at_idx)
651         continue;
652       tries++;
653 
654       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
655                                              lldb::ValueObjectSP()};
656 
657       m_children.push_back(descriptor);
658     }
659   }
660 
661   if (idx >= m_children.size()) // should never happen
662     return lldb::ValueObjectSP();
663 
664   DictionaryItemDescriptor &dict_item = m_children[idx];
665   if (!dict_item.valobj_sp) {
666     if (!m_pair_type.IsValid()) {
667       TargetSP target_sp(m_backend.GetTargetSP());
668       if (!target_sp)
669         return ValueObjectSP();
670       m_pair_type = GetLLDBNSPairType(target_sp);
671     }
672     if (!m_pair_type.IsValid())
673       return ValueObjectSP();
674 
675     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
676 
677     if (m_ptr_size == 8) {
678       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
679       *data_ptr = dict_item.key_ptr;
680       *(data_ptr + 1) = dict_item.val_ptr;
681     } else {
682       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
683       *data_ptr = dict_item.key_ptr;
684       *(data_ptr + 1) = dict_item.val_ptr;
685     }
686 
687     StreamString idx_name;
688     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
689     DataExtractor data(buffer_sp, m_order, m_ptr_size);
690     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
691                                                     m_exe_ctx_ref, m_pair_type);
692   }
693   return dict_item.valobj_sp;
694 }
695 
696 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
697     NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
698     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
699       m_order(lldb::eByteOrderInvalid), m_hashtable(), m_pair_type() {}
700 
701 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
702     GetIndexOfChildWithName(ConstString name) {
703   const char *item_name = name.GetCString();
704   const uint32_t idx = ExtractIndexFromString(item_name);
705   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
706     return UINT32_MAX;
707   return idx;
708 }
709 
710 size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
711     CalculateNumChildren() {
712   if (!m_hashtable.IsValid())
713     return 0;
714   return m_hashtable.GetCount();
715 }
716 
717 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::Update() {
718   m_children.clear();
719   ValueObjectSP valobj_sp = m_backend.GetSP();
720   m_ptr_size = 0;
721   if (!valobj_sp)
722     return false;
723   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
724 
725   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
726   if (!process_sp)
727     return false;
728   m_ptr_size = process_sp->GetAddressByteSize();
729   m_order = process_sp->GetByteOrder();
730   return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref);
731 }
732 
733 bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
734     MightHaveChildren() {
735   return true;
736 }
737 
738 lldb::ValueObjectSP
739 lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex(
740     size_t idx) {
741   lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
742   lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
743 
744   const uint32_t num_children = CalculateNumChildren();
745 
746   if (idx >= num_children)
747     return lldb::ValueObjectSP();
748 
749   if (m_children.empty()) {
750     ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
751     if (!process_sp)
752       return lldb::ValueObjectSP();
753 
754     Status error;
755     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
756 
757     uint32_t tries = 0;
758     uint32_t test_idx = 0;
759 
760     // Iterate over inferior memory, reading key/value pointers by shifting each
761     // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
762     // fails, otherwise, continue until the number of tries matches the number
763     // of childen.
764     while (tries < num_children) {
765       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
766       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
767 
768       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
769       if (error.Fail())
770         return lldb::ValueObjectSP();
771       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
772       if (error.Fail())
773         return lldb::ValueObjectSP();
774 
775       test_idx++;
776 
777       if (!key_at_idx || !val_at_idx)
778         continue;
779       tries++;
780 
781       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
782                                              lldb::ValueObjectSP()};
783 
784       m_children.push_back(descriptor);
785     }
786   }
787 
788   if (idx >= m_children.size()) // should never happen
789     return lldb::ValueObjectSP();
790 
791   DictionaryItemDescriptor &dict_item = m_children[idx];
792   if (!dict_item.valobj_sp) {
793     if (!m_pair_type.IsValid()) {
794       TargetSP target_sp(m_backend.GetTargetSP());
795       if (!target_sp)
796         return ValueObjectSP();
797       m_pair_type = GetLLDBNSPairType(target_sp);
798     }
799     if (!m_pair_type.IsValid())
800       return ValueObjectSP();
801 
802     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
803 
804     switch (m_ptr_size) {
805     case 0: // architecture has no clue - fail
806       return lldb::ValueObjectSP();
807     case 4: {
808       uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
809       *data_ptr = dict_item.key_ptr;
810       *(data_ptr + 1) = dict_item.val_ptr;
811     } break;
812     case 8: {
813       uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
814       *data_ptr = dict_item.key_ptr;
815       *(data_ptr + 1) = dict_item.val_ptr;
816     } break;
817     default:
818       lldbassert(false && "pointer size is not 4 nor 8");
819     }
820 
821     StreamString idx_name;
822     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
823     DataExtractor data(buffer_sp, m_order, m_ptr_size);
824     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
825                                                     m_exe_ctx_ref, m_pair_type);
826   }
827   return dict_item.valobj_sp;
828 }
829 
830 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
831     NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
832     : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
833 
834 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
835     GetIndexOfChildWithName(ConstString name) {
836   static const ConstString g_zero("[0]");
837   return name == g_zero ? 0 : UINT32_MAX;
838 }
839 
840 size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
841     CalculateNumChildren() {
842   return 1;
843 }
844 
845 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
846   m_pair.reset();
847   return false;
848 }
849 
850 bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
851     MightHaveChildren() {
852   return true;
853 }
854 
855 lldb::ValueObjectSP
856 lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
857     size_t idx) {
858   if (idx != 0)
859     return lldb::ValueObjectSP();
860 
861   if (m_pair.get())
862     return m_pair;
863 
864   auto process_sp(m_backend.GetProcessSP());
865   if (!process_sp)
866     return nullptr;
867 
868   auto ptr_size = process_sp->GetAddressByteSize();
869 
870   lldb::addr_t key_ptr =
871       m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
872   lldb::addr_t value_ptr = key_ptr + ptr_size;
873 
874   Status error;
875 
876   lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
877   if (error.Fail())
878     return nullptr;
879   lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
880   if (error.Fail())
881     return nullptr;
882 
883   auto pair_type =
884       GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
885 
886   DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
887 
888   if (ptr_size == 8) {
889     uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
890     *data_ptr = key_at_idx;
891     *(data_ptr + 1) = value_at_idx;
892   } else {
893     uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
894     *data_ptr = key_at_idx;
895     *(data_ptr + 1) = value_at_idx;
896   }
897 
898   DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
899   m_pair = CreateValueObjectFromData(
900       "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
901 
902   return m_pair;
903 }
904 
905 template <typename D32, typename D64>
906 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
907     GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
908     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
909       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
910       m_pair_type() {}
911 
912 template <typename D32, typename D64>
913 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
914     ~GenericNSDictionaryMSyntheticFrontEnd<D32,D64>() {
915   delete m_data_32;
916   m_data_32 = nullptr;
917   delete m_data_64;
918   m_data_64 = nullptr;
919 }
920 
921 template <typename D32, typename D64>
922 size_t lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
923     D32, D64>::GetIndexOfChildWithName(ConstString name) {
924   const char *item_name = name.GetCString();
925   uint32_t idx = ExtractIndexFromString(item_name);
926   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
927     return UINT32_MAX;
928   return idx;
929 }
930 
931 template <typename D32, typename D64>
932 size_t
933 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
934   if (!m_data_32 && !m_data_64)
935     return 0;
936   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
937 }
938 
939 template <typename D32, typename D64>
940 bool
941 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
942   Update() {
943   m_children.clear();
944   ValueObjectSP valobj_sp = m_backend.GetSP();
945   m_ptr_size = 0;
946   delete m_data_32;
947   m_data_32 = nullptr;
948   delete m_data_64;
949   m_data_64 = nullptr;
950   if (!valobj_sp)
951     return false;
952   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
953   Status error;
954   error.Clear();
955   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
956   if (!process_sp)
957     return false;
958   m_ptr_size = process_sp->GetAddressByteSize();
959   m_order = process_sp->GetByteOrder();
960   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
961   if (m_ptr_size == 4) {
962     m_data_32 = new D32();
963     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
964                            error);
965   } else {
966     m_data_64 = new D64();
967     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
968                            error);
969   }
970   if (error.Fail())
971     return false;
972   return true;
973 }
974 
975 template <typename D32, typename D64>
976 bool
977 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
978     MightHaveChildren() {
979   return true;
980 }
981 
982 template <typename D32, typename D64>
983 lldb::ValueObjectSP
984 lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
985     D32, D64>::GetChildAtIndex(size_t idx) {
986   lldb::addr_t m_keys_ptr;
987   lldb::addr_t m_values_ptr;
988   if (m_data_32) {
989     uint32_t size = m_data_32->GetSize();
990     m_keys_ptr = m_data_32->_buffer;
991     m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
992   } else {
993     uint32_t size = m_data_64->GetSize();
994     m_keys_ptr = m_data_64->_buffer;
995     m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
996   }
997 
998   uint32_t num_children = CalculateNumChildren();
999 
1000   if (idx >= num_children)
1001     return lldb::ValueObjectSP();
1002 
1003   if (m_children.empty()) {
1004     // do the scan phase
1005     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1006 
1007     uint32_t tries = 0;
1008     uint32_t test_idx = 0;
1009 
1010     while (tries < num_children) {
1011       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1012       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1013       ;
1014       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1015       if (!process_sp)
1016         return lldb::ValueObjectSP();
1017       Status error;
1018       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1019       if (error.Fail())
1020         return lldb::ValueObjectSP();
1021       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1022       if (error.Fail())
1023         return lldb::ValueObjectSP();
1024 
1025       test_idx++;
1026 
1027       if (!key_at_idx || !val_at_idx)
1028         continue;
1029       tries++;
1030 
1031       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1032                                              lldb::ValueObjectSP()};
1033 
1034       m_children.push_back(descriptor);
1035     }
1036   }
1037 
1038   if (idx >= m_children.size()) // should never happen
1039     return lldb::ValueObjectSP();
1040 
1041   DictionaryItemDescriptor &dict_item = m_children[idx];
1042   if (!dict_item.valobj_sp) {
1043     if (!m_pair_type.IsValid()) {
1044       TargetSP target_sp(m_backend.GetTargetSP());
1045       if (!target_sp)
1046         return ValueObjectSP();
1047       m_pair_type = GetLLDBNSPairType(target_sp);
1048     }
1049     if (!m_pair_type.IsValid())
1050       return ValueObjectSP();
1051 
1052     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1053 
1054     if (m_ptr_size == 8) {
1055       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1056       *data_ptr = dict_item.key_ptr;
1057       *(data_ptr + 1) = dict_item.val_ptr;
1058     } else {
1059       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1060       *data_ptr = dict_item.key_ptr;
1061       *(data_ptr + 1) = dict_item.val_ptr;
1062     }
1063 
1064     StreamString idx_name;
1065     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1066     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1067     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1068                                                     m_exe_ctx_ref, m_pair_type);
1069   }
1070   return dict_item.valobj_sp;
1071 }
1072 
1073 lldb_private::formatters::Foundation1100::
1074   NSDictionaryMSyntheticFrontEnd::
1075     NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1076     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
1077       m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
1078       m_pair_type() {}
1079 
1080 lldb_private::formatters::Foundation1100::
1081   NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
1082   delete m_data_32;
1083   m_data_32 = nullptr;
1084   delete m_data_64;
1085   m_data_64 = nullptr;
1086 }
1087 
1088 size_t
1089 lldb_private::formatters::Foundation1100::
1090   NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
1091   const char *item_name = name.GetCString();
1092   uint32_t idx = ExtractIndexFromString(item_name);
1093   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
1094     return UINT32_MAX;
1095   return idx;
1096 }
1097 
1098 size_t
1099 lldb_private::formatters::Foundation1100::
1100   NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
1101   if (!m_data_32 && !m_data_64)
1102     return 0;
1103   return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1104 }
1105 
1106 bool
1107 lldb_private::formatters::Foundation1100::
1108   NSDictionaryMSyntheticFrontEnd::Update() {
1109   m_children.clear();
1110   ValueObjectSP valobj_sp = m_backend.GetSP();
1111   m_ptr_size = 0;
1112   delete m_data_32;
1113   m_data_32 = nullptr;
1114   delete m_data_64;
1115   m_data_64 = nullptr;
1116   if (!valobj_sp)
1117     return false;
1118   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1119   Status error;
1120   error.Clear();
1121   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1122   if (!process_sp)
1123     return false;
1124   m_ptr_size = process_sp->GetAddressByteSize();
1125   m_order = process_sp->GetByteOrder();
1126   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1127   if (m_ptr_size == 4) {
1128     m_data_32 = new DataDescriptor_32();
1129     process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1130                            error);
1131   } else {
1132     m_data_64 = new DataDescriptor_64();
1133     process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1134                            error);
1135   }
1136   if (error.Fail())
1137     return false;
1138   return false;
1139 }
1140 
1141 bool
1142 lldb_private::formatters::Foundation1100::
1143   NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
1144   return true;
1145 }
1146 
1147 lldb::ValueObjectSP
1148 lldb_private::formatters::Foundation1100::
1149   NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
1150   lldb::addr_t m_keys_ptr =
1151       (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1152   lldb::addr_t m_values_ptr =
1153       (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1154 
1155   uint32_t num_children = CalculateNumChildren();
1156 
1157   if (idx >= num_children)
1158     return lldb::ValueObjectSP();
1159 
1160   if (m_children.empty()) {
1161     // do the scan phase
1162     lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1163 
1164     uint32_t tries = 0;
1165     uint32_t test_idx = 0;
1166 
1167     while (tries < num_children) {
1168       key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1169       val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1170       ;
1171       ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1172       if (!process_sp)
1173         return lldb::ValueObjectSP();
1174       Status error;
1175       key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1176       if (error.Fail())
1177         return lldb::ValueObjectSP();
1178       val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1179       if (error.Fail())
1180         return lldb::ValueObjectSP();
1181 
1182       test_idx++;
1183 
1184       if (!key_at_idx || !val_at_idx)
1185         continue;
1186       tries++;
1187 
1188       DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1189                                              lldb::ValueObjectSP()};
1190 
1191       m_children.push_back(descriptor);
1192     }
1193   }
1194 
1195   if (idx >= m_children.size()) // should never happen
1196     return lldb::ValueObjectSP();
1197 
1198   DictionaryItemDescriptor &dict_item = m_children[idx];
1199   if (!dict_item.valobj_sp) {
1200     if (!m_pair_type.IsValid()) {
1201       TargetSP target_sp(m_backend.GetTargetSP());
1202       if (!target_sp)
1203         return ValueObjectSP();
1204       m_pair_type = GetLLDBNSPairType(target_sp);
1205     }
1206     if (!m_pair_type.IsValid())
1207       return ValueObjectSP();
1208 
1209     DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1210 
1211     if (m_ptr_size == 8) {
1212       uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1213       *data_ptr = dict_item.key_ptr;
1214       *(data_ptr + 1) = dict_item.val_ptr;
1215     } else {
1216       uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1217       *data_ptr = dict_item.key_ptr;
1218       *(data_ptr + 1) = dict_item.val_ptr;
1219     }
1220 
1221     StreamString idx_name;
1222     idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1223     DataExtractor data(buffer_sp, m_order, m_ptr_size);
1224     dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1225                                                     m_exe_ctx_ref, m_pair_type);
1226   }
1227   return dict_item.valobj_sp;
1228 }
1229 
1230 template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1231     ValueObject &, Stream &, const TypeSummaryOptions &);
1232 
1233 template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1234     ValueObject &, Stream &, const TypeSummaryOptions &);
1235