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
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> &
GetAdditionalSummaries()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> &
GetAdditionalSynthetics()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(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 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
122 namespace 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
141 namespace 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
__NSArrayMSize_Impl(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)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
__NSArrayMSize(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)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
186 namespace CallStackArray {
187 struct DataDescriptor_32 {
188 uint32_t _data;
189 uint32_t _used;
190 uint32_t _offset;
191 const uint32_t _size = 0;
192 };
193
194 struct DataDescriptor_64 {
195 uint64_t _data;
196 uint64_t _used;
197 uint64_t _offset;
198 const uint64_t _size = 0;
199 };
200
201 using NSCallStackArraySyntheticFrontEnd =
202 GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
203 } // namespace CallStackArray
204
205 template <typename D32, typename D64, bool Inline>
206 class GenericNSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
207 public:
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
222 private:
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
231 namespace 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
246 namespace Foundation1430 {
247 using NSArrayISyntheticFrontEnd =
248 Foundation1428::NSArrayMSyntheticFrontEnd;
249 }
250
251 namespace 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
__NSFrozenArrayMSize(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)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
278 class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
279 public:
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
295 class NSArray1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
296 public:
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
NSArraySummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)314 bool 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
NSArrayMSyntheticFrontEndBase(lldb::ValueObjectSP valobj_sp)434 lldb_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
450 template <typename D32, typename D64>
451 lldb_private::formatters::
452 GenericNSArrayMSyntheticFrontEnd<D32, D64>::
GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)453 GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
454 : NSArrayMSyntheticFrontEndBase(valobj_sp), m_data_32(nullptr),
455 m_data_64(nullptr) {}
456
457 size_t
CalculateNumChildren()458 lldb_private::formatters::NSArrayMSyntheticFrontEndBase::CalculateNumChildren() {
459 return GetUsedCount();
460 }
461
462 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)463 lldb_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
479 template <typename D32, typename D64>
480 bool
481 lldb_private::formatters::
Update()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
513 bool
MightHaveChildren()514 lldb_private::formatters::NSArrayMSyntheticFrontEndBase::MightHaveChildren() {
515 return true;
516 }
517
518 size_t
GetIndexOfChildWithName(ConstString name)519 lldb_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
528 template <typename D32, typename D64>
529 lldb_private::formatters::
530 GenericNSArrayMSyntheticFrontEnd<D32, D64>::
~GenericNSArrayMSyntheticFrontEnd()531 ~GenericNSArrayMSyntheticFrontEnd() {
532 delete m_data_32;
533 m_data_32 = nullptr;
534 delete m_data_64;
535 m_data_64 = nullptr;
536 }
537
538 template <typename D32, typename D64>
539 lldb::addr_t
540 lldb_private::formatters::
541 GenericNSArrayMSyntheticFrontEnd<D32, D64>::
GetDataAddress()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
548 template <typename D32, typename D64>
549 uint64_t
550 lldb_private::formatters::
551 GenericNSArrayMSyntheticFrontEnd<D32, D64>::
GetUsedCount()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
558 template <typename D32, typename D64>
559 uint64_t
560 lldb_private::formatters::
561 GenericNSArrayMSyntheticFrontEnd<D32, D64>::
GetOffset()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
568 template <typename D32, typename D64>
569 uint64_t
570 lldb_private::formatters::
571 GenericNSArrayMSyntheticFrontEnd<D32, D64>::
GetSize()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
578 template <typename D32, typename D64, bool Inline>
579 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
GenericNSArrayISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)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
596 template <typename D32, typename D64, bool Inline>
597 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
~GenericNSArrayISyntheticFrontEnd()598 ~GenericNSArrayISyntheticFrontEnd() {
599 delete m_data_32;
600 m_data_32 = nullptr;
601 delete m_data_64;
602 m_data_64 = nullptr;
603 }
604
605 template <typename D32, typename D64, bool Inline>
606 size_t
607 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
GetIndexOfChildWithName(ConstString name)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
616 template <typename D32, typename D64, bool Inline>
617 size_t
618 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
CalculateNumChildren()619 CalculateNumChildren() {
620 return m_data_32 ? m_data_32->used : m_data_64->used;
621 }
622
623 template <typename D32, typename D64, bool Inline>
624 bool
625 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
Update()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
657 template <typename D32, typename D64, bool Inline>
658 bool
659 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
MightHaveChildren()660 MightHaveChildren() {
661 return true;
662 }
663
664 template <typename D32, typename D64, bool Inline>
665 lldb::ValueObjectSP
666 lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
GetChildAtIndex(size_t idx)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
NSArray0SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)692 lldb_private::formatters::NSArray0SyntheticFrontEnd::NSArray0SyntheticFrontEnd(
693 lldb::ValueObjectSP valobj_sp)
694 : SyntheticChildrenFrontEnd(*valobj_sp) {}
695
696 size_t
GetIndexOfChildWithName(ConstString name)697 lldb_private::formatters::NSArray0SyntheticFrontEnd::GetIndexOfChildWithName(
698 ConstString name) {
699 return UINT32_MAX;
700 }
701
702 size_t
CalculateNumChildren()703 lldb_private::formatters::NSArray0SyntheticFrontEnd::CalculateNumChildren() {
704 return 0;
705 }
706
Update()707 bool lldb_private::formatters::NSArray0SyntheticFrontEnd::Update() {
708 return false;
709 }
710
MightHaveChildren()711 bool lldb_private::formatters::NSArray0SyntheticFrontEnd::MightHaveChildren() {
712 return false;
713 }
714
715 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)716 lldb_private::formatters::NSArray0SyntheticFrontEnd::GetChildAtIndex(
717 size_t idx) {
718 return lldb::ValueObjectSP();
719 }
720
NSArray1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)721 lldb_private::formatters::NSArray1SyntheticFrontEnd::NSArray1SyntheticFrontEnd(
722 lldb::ValueObjectSP valobj_sp)
723 : SyntheticChildrenFrontEnd(*valobj_sp.get()) {}
724
725 size_t
GetIndexOfChildWithName(ConstString name)726 lldb_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
736 size_t
CalculateNumChildren()737 lldb_private::formatters::NSArray1SyntheticFrontEnd::CalculateNumChildren() {
738 return 1;
739 }
740
Update()741 bool lldb_private::formatters::NSArray1SyntheticFrontEnd::Update() {
742 return false;
743 }
744
MightHaveChildren()745 bool lldb_private::formatters::NSArray1SyntheticFrontEnd::MightHaveChildren() {
746 return true;
747 }
748
749 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)750 lldb_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
768 SyntheticChildrenFrontEnd *
NSArraySyntheticFrontEndCreator(CXXSyntheticChildren * synth,lldb::ValueObjectSP valobj_sp)769 lldb_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