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