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