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