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