1 //===-- NSArray.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 "clang/AST/ASTContext.h"
10 #include "clang/Basic/TargetInfo.h"
11 
12 #include "Cocoa.h"
13 
14 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
15 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
16 
17 #include "lldb/Core/ValueObject.h"
18 #include "lldb/Core/ValueObjectConstResult.h"
19 #include "lldb/DataFormatters/FormattersHelpers.h"
20 #include "lldb/Expression/FunctionCaller.h"
21 #include "lldb/Target/Language.h"
22 #include "lldb/Target/Target.h"
23 #include "lldb/Utility/DataBufferHeap.h"
24 #include "lldb/Utility/Endian.h"
25 #include "lldb/Utility/Status.h"
26 #include "lldb/Utility/Stream.h"
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 using namespace lldb_private::formatters;
31 
32 namespace lldb_private {
33 namespace formatters {
34 std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
35 NSArray_Additionals::GetAdditionalSummaries() {
36   static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
37   return g_map;
38 }
39 
40 std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
41 NSArray_Additionals::GetAdditionalSynthetics() {
42   static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>
43       g_map;
44   return g_map;
45 }
46 
47 class NSArrayMSyntheticFrontEndBase : public SyntheticChildrenFrontEnd {
48 public:
49   NSArrayMSyntheticFrontEndBase(lldb::ValueObjectSP valobj_sp);
50 
51   ~NSArrayMSyntheticFrontEndBase() override = default;
52 
53   size_t CalculateNumChildren() override;
54 
55   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
56 
57   bool Update() override = 0;
58 
59   bool MightHaveChildren() override;
60 
61   size_t GetIndexOfChildWithName(ConstString name) override;
62 
63 protected:
64   virtual lldb::addr_t GetDataAddress() = 0;
65 
66   virtual uint64_t GetUsedCount() = 0;
67 
68   virtual uint64_t GetOffset() = 0;
69 
70   virtual uint64_t GetSize() = 0;
71 
72   ExecutionContextRef m_exe_ctx_ref;
73   uint8_t m_ptr_size;
74   CompilerType m_id_type;
75 };
76 
77 template <typename D32, typename D64>
78 class GenericNSArrayMSyntheticFrontEnd : public NSArrayMSyntheticFrontEndBase {
79 public:
80   GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
81 
82   ~GenericNSArrayMSyntheticFrontEnd() override;
83 
84   bool Update() override;
85 
86 protected:
87   lldb::addr_t GetDataAddress() override;
88 
89   uint64_t GetUsedCount() override;
90 
91   uint64_t GetOffset() override;
92 
93   uint64_t GetSize() override;
94 
95 private:
96   D32 *m_data_32;
97   D64 *m_data_64;
98 };
99 
100 namespace Foundation1010 {
101   struct DataDescriptor_32 {
102     uint32_t _used;
103     uint32_t _offset;
104     uint32_t _size : 28;
105     uint64_t _priv1 : 4;
106     uint32_t _priv2;
107     uint32_t _data;
108   };
109 
110   struct DataDescriptor_64 {
111     uint64_t _used;
112     uint64_t _offset;
113     uint64_t _size : 60;
114     uint64_t _priv1 : 4;
115     uint32_t _priv2;
116     uint64_t _data;
117   };
118 
119   using NSArrayMSyntheticFrontEnd =
120       GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
121 }
122 
123 namespace Foundation1428 {
124   struct DataDescriptor_32 {
125     uint32_t _used;
126     uint32_t _offset;
127     uint32_t _size;
128     uint32_t _data;
129   };
130 
131   struct DataDescriptor_64 {
132     uint64_t _used;
133     uint64_t _offset;
134     uint64_t _size;
135     uint64_t _data;
136   };
137 
138   using NSArrayMSyntheticFrontEnd =
139       GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
140 }
141 
142 namespace Foundation1437 {
143   template <typename PtrType>
144   struct DataDescriptor {
145     PtrType _cow;
146     // __deque
147     PtrType _data;
148     uint32_t _offset;
149     uint32_t _size;
150     uint32_t _muts;
151     uint32_t _used;
152   };
153 
154   using NSArrayMSyntheticFrontEnd =
155      GenericNSArrayMSyntheticFrontEnd<
156         DataDescriptor<uint32_t>, DataDescriptor<uint64_t>>;
157 
158   template <typename DD>
159   uint64_t
160   __NSArrayMSize_Impl(lldb_private::Process &process,
161                       lldb::addr_t valobj_addr, Status &error) {
162     const lldb::addr_t start_of_descriptor =
163     valobj_addr + process.GetAddressByteSize();
164     DD descriptor = DD();
165     process.ReadMemory(start_of_descriptor, &descriptor,
166                        sizeof(descriptor), error);
167     if (error.Fail()) {
168       return 0;
169     }
170     return descriptor._used;
171   }
172 
173   uint64_t
174   __NSArrayMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
175                  Status &error) {
176     if (process.GetAddressByteSize() == 4) {
177       return __NSArrayMSize_Impl<DataDescriptor<uint32_t>>(process, valobj_addr,
178                                                            error);
179     } else {
180       return __NSArrayMSize_Impl<DataDescriptor<uint64_t>>(process, valobj_addr,
181                                                            error);
182     }
183   }
184 
185 }
186 
187 namespace CallStackArray {
188 struct DataDescriptor_32 {
189   uint32_t _data;
190   uint32_t _used;
191   uint32_t _offset;
192   const uint32_t _size = 0;
193 };
194 
195 struct DataDescriptor_64 {
196   uint64_t _data;
197   uint64_t _used;
198   uint64_t _offset;
199   const uint64_t _size = 0;
200 };
201 
202 using NSCallStackArraySyntheticFrontEnd =
203     GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
204 } // namespace CallStackArray
205 
206 template <typename D32, typename D64, bool Inline>
207 class GenericNSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
208 public:
209   GenericNSArrayISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
210 
211   ~GenericNSArrayISyntheticFrontEnd() override;
212 
213   size_t CalculateNumChildren() override;
214 
215   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
216 
217   bool Update() override;
218 
219   bool MightHaveChildren() override;
220 
221   size_t GetIndexOfChildWithName(ConstString name) override;
222 
223 private:
224   ExecutionContextRef m_exe_ctx_ref;
225   uint8_t m_ptr_size;
226 
227   D32 *m_data_32;
228   D64 *m_data_64;
229   CompilerType m_id_type;
230 };
231 
232 namespace Foundation1300 {
233     struct IDD32 {
234         uint32_t used;
235         uint32_t list;
236     };
237 
238     struct IDD64 {
239         uint64_t used;
240         uint64_t list;
241     };
242 
243     using NSArrayISyntheticFrontEnd =
244         GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, true>;
245 }
246 
247 namespace Foundation1430 {
248     using NSArrayISyntheticFrontEnd =
249         Foundation1428::NSArrayMSyntheticFrontEnd;
250 }
251 
252 namespace Foundation1436 {
253     struct IDD32 {
254         uint32_t used;
255         uint32_t list; // in Inline cases, this is the first element
256     };
257 
258     struct IDD64 {
259         uint64_t used;
260         uint64_t list; // in Inline cases, this is the first element
261     };
262 
263     using NSArrayI_TransferSyntheticFrontEnd =
264         GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, false>;
265 
266     using NSArrayISyntheticFrontEnd =
267         GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, true>;
268 
269     using NSFrozenArrayMSyntheticFrontEnd =
270         Foundation1437::NSArrayMSyntheticFrontEnd;
271 
272     uint64_t
273     __NSFrozenArrayMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
274                          Status &error) {
275       return Foundation1437::__NSArrayMSize(process, valobj_addr, error);
276     }
277 }
278 
279 class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
280 public:
281   NSArray0SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
282 
283   ~NSArray0SyntheticFrontEnd() override = default;
284 
285   size_t CalculateNumChildren() override;
286 
287   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
288 
289   bool Update() override;
290 
291   bool MightHaveChildren() override;
292 
293   size_t GetIndexOfChildWithName(ConstString name) override;
294 };
295 
296 class NSArray1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
297 public:
298   NSArray1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
299 
300   ~NSArray1SyntheticFrontEnd() override = default;
301 
302   size_t CalculateNumChildren() override;
303 
304   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
305 
306   bool Update() override;
307 
308   bool MightHaveChildren() override;
309 
310   size_t GetIndexOfChildWithName(ConstString name) override;
311 };
312 } // namespace formatters
313 } // namespace lldb_private
314 
315 bool lldb_private::formatters::NSArraySummaryProvider(
316     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
317   static ConstString g_TypeHint("NSArray");
318 
319   ProcessSP process_sp = valobj.GetProcessSP();
320   if (!process_sp)
321     return false;
322 
323   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
324 
325   if (!runtime)
326     return false;
327 
328   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
329       runtime->GetClassDescriptor(valobj));
330 
331   if (!descriptor || !descriptor->IsValid())
332     return false;
333 
334   uint32_t ptr_size = process_sp->GetAddressByteSize();
335 
336   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
337 
338   if (!valobj_addr)
339     return false;
340 
341   uint64_t value = 0;
342 
343   ConstString class_name(descriptor->GetClassName());
344 
345   static const ConstString g_NSArrayI("__NSArrayI");
346   static const ConstString g_NSArrayM("__NSArrayM");
347   static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer");
348   static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM");
349   static const ConstString g_NSArray0("__NSArray0");
350   static const ConstString g_NSArray1("__NSSingleObjectArrayI");
351   static const ConstString g_NSArrayCF("__NSCFArray");
352   static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
353   static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
354   static const ConstString g_NSCallStackArray("_NSCallStackArray");
355 
356   if (class_name.IsEmpty())
357     return false;
358 
359   if (class_name == g_NSArrayI) {
360     Status error;
361     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
362                                                       ptr_size, 0, error);
363     if (error.Fail())
364       return false;
365   } else if (class_name == g_NSArrayM) {
366     AppleObjCRuntime *apple_runtime =
367     llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
368     Status error;
369     if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
370       value = Foundation1437::__NSArrayMSize(*process_sp, valobj_addr, error);
371     } else {
372       value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
373                                                         ptr_size, 0, error);
374     }
375     if (error.Fail())
376       return false;
377   } else if (class_name == g_NSArrayI_Transfer) {
378     Status error;
379     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
380                                                       ptr_size, 0, error);
381     if (error.Fail())
382       return false;
383   } else if (class_name == g_NSFrozenArrayM) {
384     Status error;
385     value = Foundation1436::__NSFrozenArrayMSize(*process_sp, valobj_addr, error);
386     if (error.Fail())
387       return false;
388   } else if (class_name == g_NSArrayMLegacy) {
389     Status error;
390     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
391                                                       ptr_size, 0, error);
392     if (error.Fail())
393       return false;
394   } else if (class_name == g_NSArrayMImmutable) {
395     Status error;
396     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
397                                                       ptr_size, 0, error);
398     if (error.Fail())
399       return false;
400   } else if (class_name == g_NSArray0) {
401     value = 0;
402   } else if (class_name == g_NSArray1) {
403     value = 1;
404   } else if (class_name == g_NSArrayCF || class_name == g_NSCallStackArray) {
405     // __NSCFArray and _NSCallStackArray store the number of elements as a
406     // pointer-sized value at offset `2 * ptr_size`.
407     Status error;
408     value = process_sp->ReadUnsignedIntegerFromMemory(
409         valobj_addr + 2 * ptr_size, ptr_size, 0, error);
410     if (error.Fail())
411       return false;
412   } else {
413     auto &map(NSArray_Additionals::GetAdditionalSummaries());
414     auto iter = map.find(class_name), end = map.end();
415     if (iter != end)
416       return iter->second(valobj, stream, options);
417     else
418       return false;
419   }
420 
421   std::string prefix, suffix;
422   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
423     if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
424                                             suffix)) {
425       prefix.clear();
426       suffix.clear();
427     }
428   }
429 
430   stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
431                 value == 1 ? "" : "s", suffix.c_str());
432   return true;
433 }
434 
435 lldb_private::formatters::NSArrayMSyntheticFrontEndBase::NSArrayMSyntheticFrontEndBase(
436     lldb::ValueObjectSP valobj_sp)
437     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
438       m_id_type() {
439   if (valobj_sp) {
440     auto *clang_ast_context = TypeSystemClang::GetScratch(
441         *valobj_sp->GetExecutionContextRef().GetTargetSP());
442     if (clang_ast_context)
443       m_id_type = CompilerType(
444           clang_ast_context,
445           clang_ast_context->getASTContext().ObjCBuiltinIdTy.getAsOpaquePtr());
446     if (valobj_sp->GetProcessSP())
447       m_ptr_size = valobj_sp->GetProcessSP()->GetAddressByteSize();
448   }
449 }
450 
451 template <typename D32, typename D64>
452 lldb_private::formatters::
453   GenericNSArrayMSyntheticFrontEnd<D32, D64>::
454     GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
455     : NSArrayMSyntheticFrontEndBase(valobj_sp), m_data_32(nullptr),
456       m_data_64(nullptr) {}
457 
458 size_t
459 lldb_private::formatters::NSArrayMSyntheticFrontEndBase::CalculateNumChildren() {
460   return GetUsedCount();
461 }
462 
463 lldb::ValueObjectSP
464 lldb_private::formatters::NSArrayMSyntheticFrontEndBase::GetChildAtIndex(
465     size_t idx) {
466   if (idx >= CalculateNumChildren())
467     return lldb::ValueObjectSP();
468   lldb::addr_t object_at_idx = GetDataAddress();
469   size_t pyhs_idx = idx;
470   pyhs_idx += GetOffset();
471   if (GetSize() <= pyhs_idx)
472     pyhs_idx -= GetSize();
473   object_at_idx += (pyhs_idx * m_ptr_size);
474   StreamString idx_name;
475   idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
476   return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx,
477                                       m_exe_ctx_ref, m_id_type);
478 }
479 
480 template <typename D32, typename D64>
481 bool
482 lldb_private::formatters::
483   GenericNSArrayMSyntheticFrontEnd<D32, D64>::Update() {
484   ValueObjectSP valobj_sp = m_backend.GetSP();
485   m_ptr_size = 0;
486   delete m_data_32;
487   m_data_32 = nullptr;
488   delete m_data_64;
489   m_data_64 = nullptr;
490   if (!valobj_sp)
491     return false;
492   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
493   Status error;
494   error.Clear();
495   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
496   if (!process_sp)
497     return false;
498   m_ptr_size = process_sp->GetAddressByteSize();
499   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
500   if (m_ptr_size == 4) {
501     m_data_32 = new D32();
502     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
503                            error);
504   } else {
505     m_data_64 = new D64();
506     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
507                            error);
508   }
509   if (error.Fail())
510     return false;
511   return false;
512 }
513 
514 bool
515 lldb_private::formatters::NSArrayMSyntheticFrontEndBase::MightHaveChildren() {
516   return true;
517 }
518 
519 size_t
520 lldb_private::formatters::NSArrayMSyntheticFrontEndBase::GetIndexOfChildWithName(
521     ConstString name) {
522   const char *item_name = name.GetCString();
523   uint32_t idx = ExtractIndexFromString(item_name);
524   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
525     return UINT32_MAX;
526   return idx;
527 }
528 
529 template <typename D32, typename D64>
530 lldb_private::formatters::
531   GenericNSArrayMSyntheticFrontEnd<D32, D64>::
532     ~GenericNSArrayMSyntheticFrontEnd<D32, D64>() {
533   delete m_data_32;
534   m_data_32 = nullptr;
535   delete m_data_64;
536   m_data_64 = nullptr;
537 }
538 
539 template <typename D32, typename D64>
540 lldb::addr_t
541 lldb_private::formatters::
542   GenericNSArrayMSyntheticFrontEnd<D32, D64>::
543     GenericNSArrayMSyntheticFrontEnd::GetDataAddress() {
544   if (!m_data_32 && !m_data_64)
545     return LLDB_INVALID_ADDRESS;
546   return m_data_32 ? m_data_32->_data : m_data_64->_data;
547 }
548 
549 template <typename D32, typename D64>
550 uint64_t
551 lldb_private::formatters::
552   GenericNSArrayMSyntheticFrontEnd<D32, D64>::
553     GenericNSArrayMSyntheticFrontEnd::GetUsedCount() {
554   if (!m_data_32 && !m_data_64)
555     return 0;
556   return m_data_32 ? m_data_32->_used : m_data_64->_used;
557 }
558 
559 template <typename D32, typename D64>
560 uint64_t
561 lldb_private::formatters::
562   GenericNSArrayMSyntheticFrontEnd<D32, D64>::
563     GenericNSArrayMSyntheticFrontEnd::GetOffset() {
564   if (!m_data_32 && !m_data_64)
565     return 0;
566   return m_data_32 ? m_data_32->_offset : m_data_64->_offset;
567 }
568 
569 template <typename D32, typename D64>
570 uint64_t
571 lldb_private::formatters::
572   GenericNSArrayMSyntheticFrontEnd<D32, D64>::
573     GenericNSArrayMSyntheticFrontEnd::GetSize() {
574   if (!m_data_32 && !m_data_64)
575     return 0;
576   return m_data_32 ? m_data_32->_size : m_data_64->_size;
577 }
578 
579 template <typename D32, typename D64, bool Inline>
580 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
581   GenericNSArrayISyntheticFrontEnd(
582     lldb::ValueObjectSP valobj_sp)
583     : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
584       m_data_32(nullptr), m_data_64(nullptr) {
585   if (valobj_sp) {
586     CompilerType type = valobj_sp->GetCompilerType();
587     if (type) {
588       auto *clang_ast_context = TypeSystemClang::GetScratch(
589           *valobj_sp->GetExecutionContextRef().GetTargetSP());
590       if (clang_ast_context)
591         m_id_type = clang_ast_context->GetType(
592             clang_ast_context->getASTContext().ObjCBuiltinIdTy);
593     }
594   }
595 }
596 
597 template <typename D32, typename D64, bool Inline>
598 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
599   ~GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>() {
600   delete m_data_32;
601   m_data_32 = nullptr;
602   delete m_data_64;
603   m_data_64 = nullptr;
604 }
605 
606 template <typename D32, typename D64, bool Inline>
607 size_t
608 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
609   GetIndexOfChildWithName(ConstString name) {
610   const char *item_name = name.GetCString();
611   uint32_t idx = ExtractIndexFromString(item_name);
612   if (idx < UINT32_MAX && idx >= CalculateNumChildren())
613     return UINT32_MAX;
614   return idx;
615 }
616 
617 template <typename D32, typename D64, bool Inline>
618 size_t
619 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
620   CalculateNumChildren() {
621   return m_data_32 ? m_data_32->used : m_data_64->used;
622 }
623 
624 template <typename D32, typename D64, bool Inline>
625 bool
626 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
627   Update() {
628   ValueObjectSP valobj_sp = m_backend.GetSP();
629   m_ptr_size = 0;
630   delete m_data_32;
631   m_data_32 = nullptr;
632   delete m_data_64;
633   m_data_64 = nullptr;
634   if (!valobj_sp)
635     return false;
636   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
637   Status error;
638   error.Clear();
639   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
640   if (!process_sp)
641     return false;
642   m_ptr_size = process_sp->GetAddressByteSize();
643   uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
644   if (m_ptr_size == 4) {
645     m_data_32 = new D32();
646     process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
647                            error);
648   } else {
649     m_data_64 = new D64();
650     process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
651                            error);
652   }
653   if (error.Fail())
654     return false;
655   return false;
656 }
657 
658 template <typename D32, typename D64, bool Inline>
659 bool
660 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
661   MightHaveChildren() {
662   return true;
663 }
664 
665 template <typename D32, typename D64, bool Inline>
666 lldb::ValueObjectSP
667 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
668   GetChildAtIndex(size_t idx) {
669   if (idx >= CalculateNumChildren())
670     return lldb::ValueObjectSP();
671   lldb::addr_t object_at_idx;
672   if (Inline) {
673     object_at_idx = m_backend.GetSP()->GetValueAsUnsigned(0) + m_ptr_size;
674     object_at_idx += m_ptr_size == 4 ? sizeof(D32) : sizeof(D64); // skip the data header
675     object_at_idx -= m_ptr_size; // we treat the last entry in the data header as the first pointer
676   } else {
677     object_at_idx = m_data_32 ? m_data_32->list : m_data_64->list;
678   }
679   object_at_idx += (idx * m_ptr_size);
680 
681   ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
682   if (!process_sp)
683     return lldb::ValueObjectSP();
684   Status error;
685   if (error.Fail())
686     return lldb::ValueObjectSP();
687   StreamString idx_name;
688   idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
689   return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx,
690                                       m_exe_ctx_ref, m_id_type);
691 }
692 
693 lldb_private::formatters::NSArray0SyntheticFrontEnd::NSArray0SyntheticFrontEnd(
694     lldb::ValueObjectSP valobj_sp)
695     : SyntheticChildrenFrontEnd(*valobj_sp) {}
696 
697 size_t
698 lldb_private::formatters::NSArray0SyntheticFrontEnd::GetIndexOfChildWithName(
699     ConstString name) {
700   return UINT32_MAX;
701 }
702 
703 size_t
704 lldb_private::formatters::NSArray0SyntheticFrontEnd::CalculateNumChildren() {
705   return 0;
706 }
707 
708 bool lldb_private::formatters::NSArray0SyntheticFrontEnd::Update() {
709   return false;
710 }
711 
712 bool lldb_private::formatters::NSArray0SyntheticFrontEnd::MightHaveChildren() {
713   return false;
714 }
715 
716 lldb::ValueObjectSP
717 lldb_private::formatters::NSArray0SyntheticFrontEnd::GetChildAtIndex(
718     size_t idx) {
719   return lldb::ValueObjectSP();
720 }
721 
722 lldb_private::formatters::NSArray1SyntheticFrontEnd::NSArray1SyntheticFrontEnd(
723     lldb::ValueObjectSP valobj_sp)
724     : SyntheticChildrenFrontEnd(*valobj_sp.get()) {}
725 
726 size_t
727 lldb_private::formatters::NSArray1SyntheticFrontEnd::GetIndexOfChildWithName(
728     ConstString name) {
729   static const ConstString g_zero("[0]");
730 
731   if (name == g_zero)
732     return 0;
733 
734   return UINT32_MAX;
735 }
736 
737 size_t
738 lldb_private::formatters::NSArray1SyntheticFrontEnd::CalculateNumChildren() {
739   return 1;
740 }
741 
742 bool lldb_private::formatters::NSArray1SyntheticFrontEnd::Update() {
743   return false;
744 }
745 
746 bool lldb_private::formatters::NSArray1SyntheticFrontEnd::MightHaveChildren() {
747   return true;
748 }
749 
750 lldb::ValueObjectSP
751 lldb_private::formatters::NSArray1SyntheticFrontEnd::GetChildAtIndex(
752     size_t idx) {
753   static const ConstString g_zero("[0]");
754 
755   if (idx == 0) {
756     auto *clang_ast_context =
757         TypeSystemClang::GetScratch(*m_backend.GetTargetSP());
758     if (clang_ast_context) {
759       CompilerType id_type(
760           clang_ast_context->GetBasicType(lldb::eBasicTypeObjCID));
761       return m_backend.GetSyntheticChildAtOffset(
762           m_backend.GetProcessSP()->GetAddressByteSize(), id_type, true,
763           g_zero);
764     }
765   }
766   return lldb::ValueObjectSP();
767 }
768 
769 SyntheticChildrenFrontEnd *
770 lldb_private::formatters::NSArraySyntheticFrontEndCreator(
771     CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
772   if (!valobj_sp)
773     return nullptr;
774 
775   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
776   if (!process_sp)
777     return nullptr;
778   AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
779       ObjCLanguageRuntime::Get(*process_sp));
780   if (!runtime)
781     return nullptr;
782 
783   CompilerType valobj_type(valobj_sp->GetCompilerType());
784   Flags flags(valobj_type.GetTypeInfo());
785 
786   if (flags.IsClear(eTypeIsPointer)) {
787     Status error;
788     valobj_sp = valobj_sp->AddressOf(error);
789     if (error.Fail() || !valobj_sp)
790       return nullptr;
791   }
792 
793   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
794       runtime->GetClassDescriptor(*valobj_sp));
795 
796   if (!descriptor || !descriptor->IsValid())
797     return nullptr;
798 
799   ConstString class_name(descriptor->GetClassName());
800 
801   static const ConstString g_NSArrayI("__NSArrayI");
802   static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer");
803   static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM");
804   static const ConstString g_NSArrayM("__NSArrayM");
805   static const ConstString g_NSArray0("__NSArray0");
806   static const ConstString g_NSArray1("__NSSingleObjectArrayI");
807   static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
808   static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
809   static const ConstString g_NSCallStackArray("_NSCallStackArray");
810 
811   if (class_name.IsEmpty())
812     return nullptr;
813 
814   if (class_name == g_NSArrayI) {
815     if (runtime->GetFoundationVersion() >= 1436)
816       return (new Foundation1436::NSArrayISyntheticFrontEnd(valobj_sp));
817     if (runtime->GetFoundationVersion() >= 1430)
818       return (new Foundation1430::NSArrayISyntheticFrontEnd(valobj_sp));
819     else
820       return (new Foundation1300::NSArrayISyntheticFrontEnd(valobj_sp));
821   } else if (class_name == g_NSArrayI_Transfer) {
822       return (new Foundation1436::NSArrayI_TransferSyntheticFrontEnd(valobj_sp));
823   } else if (class_name == g_NSArray0) {
824   } else if (class_name == g_NSFrozenArrayM) {
825     return (new Foundation1436::NSFrozenArrayMSyntheticFrontEnd(valobj_sp));
826   } else if (class_name == g_NSArray0) {
827     return (new NSArray0SyntheticFrontEnd(valobj_sp));
828   } else if (class_name == g_NSArray1) {
829     return (new NSArray1SyntheticFrontEnd(valobj_sp));
830   } else if (class_name == g_NSArrayM) {
831     if (runtime->GetFoundationVersion() >= 1437)
832       return (new Foundation1437::NSArrayMSyntheticFrontEnd(valobj_sp));
833     if (runtime->GetFoundationVersion() >= 1428)
834       return (new Foundation1428::NSArrayMSyntheticFrontEnd(valobj_sp));
835     if (runtime->GetFoundationVersion() >= 1100)
836       return (new Foundation1010::NSArrayMSyntheticFrontEnd(valobj_sp));
837   } else if (class_name == g_NSCallStackArray) {
838     return (new CallStackArray::NSCallStackArraySyntheticFrontEnd(valobj_sp));
839   } else {
840     auto &map(NSArray_Additionals::GetAdditionalSynthetics());
841     auto iter = map.find(class_name), end = map.end();
842     if (iter != end)
843       return iter->second(synth, valobj_sp);
844   }
845 
846   return nullptr;
847 }
848