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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of lldb/source/Plugins/Language/ObjC/NSArray.cpp