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