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 | |
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 | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; |
60 | |
61 | protected: |
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 | |
75 | template <typename D32, typename D64> |
76 | class GenericNSArrayMSyntheticFrontEnd : public NSArrayMSyntheticFrontEndBase { |
77 | public: |
78 | GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
79 | |
80 | ~GenericNSArrayMSyntheticFrontEnd() override; |
81 | |
82 | lldb::ChildCacheState Update() override; |
83 | |
84 | protected: |
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 | |
93 | private: |
94 | D32 *m_data_32; |
95 | D64 *m_data_64; |
96 | }; |
97 | |
98 | namespace 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 | |
123 | namespace 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 | |
144 | namespace 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 | |
189 | namespace CallStackArray { |
190 | struct DataDescriptor_32 { |
191 | uint32_t _data; |
192 | uint32_t _used; |
193 | uint32_t _offset; |
194 | const uint32_t _size = 0; |
195 | }; |
196 | |
197 | struct DataDescriptor_64 { |
198 | uint64_t _data; |
199 | uint64_t _used; |
200 | uint64_t _offset; |
201 | const uint64_t _size = 0; |
202 | }; |
203 | |
204 | using NSCallStackArraySyntheticFrontEnd = |
205 | GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; |
206 | } // namespace CallStackArray |
207 | |
208 | template <typename D32, typename D64, bool Inline> |
209 | class GenericNSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
210 | public: |
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 | |
223 | private: |
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 | |
232 | namespace 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 | |
247 | namespace Foundation1430 { |
248 | using NSArrayISyntheticFrontEnd = |
249 | Foundation1428::NSArrayMSyntheticFrontEnd; |
250 | } |
251 | |
252 | namespace 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 | |
279 | namespace ConstantArray { |
280 | |
281 | struct ConstantArray32 { |
282 | uint64_t used; |
283 | uint32_t list; |
284 | }; |
285 | |
286 | struct ConstantArray64 { |
287 | uint64_t used; |
288 | uint64_t list; |
289 | }; |
290 | |
291 | using NSConstantArraySyntheticFrontEnd = |
292 | GenericNSArrayISyntheticFrontEnd<ConstantArray32, ConstantArray64, false>; |
293 | } // namespace ConstantArray |
294 | |
295 | class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
296 | public: |
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 | |
312 | class NSArray1SyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
313 | public: |
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 | |
329 | bool 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 | |
452 | lldb_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 | |
467 | template <typename D32, typename D64> |
468 | lldb_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 | |
474 | llvm::Expected<uint32_t> lldb_private::formatters:: |
475 | NSArrayMSyntheticFrontEndBase::CalculateNumChildren() { |
476 | return GetUsedCount(); |
477 | } |
478 | |
479 | lldb::ValueObjectSP |
480 | lldb_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 | |
496 | template <typename D32, typename D64> |
497 | lldb::ChildCacheState |
498 | lldb_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 | |
529 | llvm::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 | |
543 | template <typename D32, typename D64> |
544 | lldb_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 | |
552 | template <typename D32, typename D64> |
553 | lldb::addr_t |
554 | lldb_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 | |
562 | template <typename D32, typename D64> |
563 | uint64_t |
564 | lldb_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 | |
572 | template <typename D32, typename D64> |
573 | uint64_t |
574 | lldb_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 | |
582 | template <typename D32, typename D64> |
583 | uint64_t |
584 | lldb_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 | |
592 | template <typename D32, typename D64, bool Inline> |
593 | lldb_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 | |
609 | template <typename D32, typename D64, bool Inline> |
610 | lldb_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 | |
618 | template <typename D32, typename D64, bool Inline> |
619 | llvm::Expected<size_t> |
620 | lldb_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 | |
634 | template <typename D32, typename D64, bool Inline> |
635 | llvm::Expected<uint32_t> |
636 | lldb_private::formatters::GenericNSArrayISyntheticFrontEnd< |
637 | D32, D64, Inline>::CalculateNumChildren() { |
638 | return m_data_32 ? m_data_32->used : m_data_64->used; |
639 | } |
640 | |
641 | template <typename D32, typename D64, bool Inline> |
642 | lldb::ChildCacheState |
643 | lldb_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 | |
675 | template <typename D32, typename D64, bool Inline> |
676 | lldb::ValueObjectSP |
677 | lldb_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 | |
703 | lldb_private::formatters::NSArray0SyntheticFrontEnd::NSArray0SyntheticFrontEnd( |
704 | lldb::ValueObjectSP valobj_sp) |
705 | : SyntheticChildrenFrontEnd(*valobj_sp) {} |
706 | |
707 | llvm::Expected<size_t> |
708 | lldb_private::formatters::NSArray0SyntheticFrontEnd::GetIndexOfChildWithName( |
709 | ConstString name) { |
710 | return UINT32_MAX; |
711 | } |
712 | |
713 | llvm::Expected<uint32_t> |
714 | lldb_private::formatters::NSArray0SyntheticFrontEnd::CalculateNumChildren() { |
715 | return 0; |
716 | } |
717 | |
718 | lldb::ChildCacheState |
719 | lldb_private::formatters::NSArray0SyntheticFrontEnd::Update() { |
720 | return lldb::ChildCacheState::eRefetch; |
721 | } |
722 | |
723 | bool lldb_private::formatters::NSArray0SyntheticFrontEnd::MightHaveChildren() { |
724 | return false; |
725 | } |
726 | |
727 | lldb::ValueObjectSP |
728 | lldb_private::formatters::NSArray0SyntheticFrontEnd::GetChildAtIndex( |
729 | uint32_t idx) { |
730 | return lldb::ValueObjectSP(); |
731 | } |
732 | |
733 | lldb_private::formatters::NSArray1SyntheticFrontEnd::NSArray1SyntheticFrontEnd( |
734 | lldb::ValueObjectSP valobj_sp) |
735 | : SyntheticChildrenFrontEnd(*valobj_sp.get()) {} |
736 | |
737 | llvm::Expected<size_t> |
738 | lldb_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 | |
748 | llvm::Expected<uint32_t> |
749 | lldb_private::formatters::NSArray1SyntheticFrontEnd::CalculateNumChildren() { |
750 | return 1; |
751 | } |
752 | |
753 | lldb::ChildCacheState |
754 | lldb_private::formatters::NSArray1SyntheticFrontEnd::Update() { |
755 | return lldb::ChildCacheState::eRefetch; |
756 | } |
757 | |
758 | lldb::ValueObjectSP |
759 | lldb_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 | |
776 | SyntheticChildrenFrontEnd * |
777 | lldb_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 |
Definitions
- GetAdditionalSummaries
- GetAdditionalSynthetics
- NSArrayMSyntheticFrontEndBase
- ~NSArrayMSyntheticFrontEndBase
- GenericNSArrayMSyntheticFrontEnd
- DataDescriptor_32
- DataDescriptor_64
- DataDescriptor_32
- DataDescriptor_64
- DataDescriptor
- __NSArrayMSize_Impl
- __NSArrayMSize
- DataDescriptor_32
- DataDescriptor_64
- GenericNSArrayISyntheticFrontEnd
- IDD32
- IDD64
- IDD32
- IDD64
- __NSFrozenArrayMSize
- ConstantArray32
- ConstantArray64
- NSArray0SyntheticFrontEnd
- ~NSArray0SyntheticFrontEnd
- NSArray1SyntheticFrontEnd
- ~NSArray1SyntheticFrontEnd
- NSArraySummaryProvider
- NSArrayMSyntheticFrontEndBase
- GenericNSArrayMSyntheticFrontEnd
- CalculateNumChildren
- GetChildAtIndex
- Update
- GetIndexOfChildWithName
- ~GenericNSArrayMSyntheticFrontEnd
- GetDataAddress
- GetUsedCount
- GetOffset
- GetSize
- GenericNSArrayISyntheticFrontEnd
- ~GenericNSArrayISyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
- NSArray0SyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- MightHaveChildren
- GetChildAtIndex
- NSArray1SyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
Learn to use CMake with our Intro Training
Find out more