1 | //===-- NSSet.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 "NSSet.h" |
10 | #include "CFBasicHash.h" |
11 | |
12 | #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" |
13 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
14 | #include "lldb/Core/ValueObject.h" |
15 | #include "lldb/Core/ValueObjectConstResult.h" |
16 | #include "lldb/DataFormatters/FormattersHelpers.h" |
17 | #include "lldb/Target/Language.h" |
18 | #include "lldb/Target/Target.h" |
19 | #include "lldb/Utility/DataBufferHeap.h" |
20 | #include "lldb/Utility/Endian.h" |
21 | #include "lldb/Utility/Status.h" |
22 | #include "lldb/Utility/Stream.h" |
23 | |
24 | using namespace lldb; |
25 | using namespace lldb_private; |
26 | using namespace lldb_private::formatters; |
27 | |
28 | std::map<ConstString, CXXFunctionSummaryFormat::Callback> & |
29 | NSSet_Additionals::GetAdditionalSummaries() { |
30 | static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map; |
31 | return g_map; |
32 | } |
33 | |
34 | std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> & |
35 | NSSet_Additionals::GetAdditionalSynthetics() { |
36 | static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> |
37 | g_map; |
38 | return g_map; |
39 | } |
40 | |
41 | namespace lldb_private { |
42 | namespace formatters { |
43 | class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
44 | public: |
45 | NSSetISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
46 | |
47 | ~NSSetISyntheticFrontEnd() override; |
48 | |
49 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
50 | |
51 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
52 | |
53 | lldb::ChildCacheState Update() override; |
54 | |
55 | bool MightHaveChildren() override; |
56 | |
57 | size_t GetIndexOfChildWithName(ConstString name) override; |
58 | |
59 | private: |
60 | struct DataDescriptor_32 { |
61 | uint32_t _used : 26; |
62 | uint32_t _szidx : 6; |
63 | }; |
64 | |
65 | struct DataDescriptor_64 { |
66 | uint64_t _used : 58; |
67 | uint32_t _szidx : 6; |
68 | }; |
69 | |
70 | struct SetItemDescriptor { |
71 | lldb::addr_t item_ptr; |
72 | lldb::ValueObjectSP valobj_sp; |
73 | }; |
74 | |
75 | ExecutionContextRef m_exe_ctx_ref; |
76 | uint8_t m_ptr_size = 8; |
77 | DataDescriptor_32 *m_data_32 = nullptr; |
78 | DataDescriptor_64 *m_data_64 = nullptr; |
79 | lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS; |
80 | std::vector<SetItemDescriptor> m_children; |
81 | }; |
82 | |
83 | class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
84 | public: |
85 | NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
86 | |
87 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
88 | |
89 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
90 | |
91 | lldb::ChildCacheState Update() override; |
92 | |
93 | bool MightHaveChildren() override; |
94 | |
95 | size_t GetIndexOfChildWithName(ConstString name) override; |
96 | |
97 | private: |
98 | struct SetItemDescriptor { |
99 | lldb::addr_t item_ptr; |
100 | lldb::ValueObjectSP valobj_sp; |
101 | }; |
102 | |
103 | ExecutionContextRef m_exe_ctx_ref; |
104 | uint8_t m_ptr_size = 8; |
105 | lldb::ByteOrder m_order = lldb::eByteOrderInvalid; |
106 | |
107 | CFBasicHash m_hashtable; |
108 | |
109 | CompilerType m_pair_type; |
110 | std::vector<SetItemDescriptor> m_children; |
111 | }; |
112 | |
113 | template <typename D32, typename D64> |
114 | class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
115 | public: |
116 | GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
117 | |
118 | ~GenericNSSetMSyntheticFrontEnd() override; |
119 | |
120 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
121 | |
122 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
123 | |
124 | lldb::ChildCacheState Update() override; |
125 | |
126 | bool MightHaveChildren() override; |
127 | |
128 | size_t GetIndexOfChildWithName(ConstString name) override; |
129 | |
130 | private: |
131 | |
132 | struct SetItemDescriptor { |
133 | lldb::addr_t item_ptr; |
134 | lldb::ValueObjectSP valobj_sp; |
135 | }; |
136 | |
137 | ExecutionContextRef m_exe_ctx_ref; |
138 | uint8_t m_ptr_size = 8; |
139 | D32 *m_data_32; |
140 | D64 *m_data_64; |
141 | std::vector<SetItemDescriptor> m_children; |
142 | }; |
143 | |
144 | namespace Foundation1300 { |
145 | struct DataDescriptor_32 { |
146 | uint32_t _used : 26; |
147 | uint32_t _size; |
148 | uint32_t _mutations; |
149 | uint32_t _objs_addr; |
150 | }; |
151 | |
152 | struct DataDescriptor_64 { |
153 | uint64_t _used : 58; |
154 | uint64_t _size; |
155 | uint64_t _mutations; |
156 | uint64_t _objs_addr; |
157 | }; |
158 | |
159 | using NSSetMSyntheticFrontEnd = |
160 | GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; |
161 | } |
162 | |
163 | namespace Foundation1428 { |
164 | struct DataDescriptor_32 { |
165 | uint32_t _used : 26; |
166 | uint32_t _size; |
167 | uint32_t _objs_addr; |
168 | uint32_t _mutations; |
169 | }; |
170 | |
171 | struct DataDescriptor_64 { |
172 | uint64_t _used : 58; |
173 | uint64_t _size; |
174 | uint64_t _objs_addr; |
175 | uint64_t _mutations; |
176 | }; |
177 | |
178 | using NSSetMSyntheticFrontEnd = |
179 | GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; |
180 | } |
181 | |
182 | namespace Foundation1437 { |
183 | struct DataDescriptor_32 { |
184 | uint32_t _cow; |
185 | // __table storage |
186 | uint32_t _objs_addr; |
187 | uint32_t _muts; |
188 | uint32_t _used : 26; |
189 | uint32_t _szidx : 6; |
190 | }; |
191 | |
192 | struct DataDescriptor_64 { |
193 | uint64_t _cow; |
194 | // __Table storage |
195 | uint64_t _objs_addr; |
196 | uint32_t _muts; |
197 | uint32_t _used : 26; |
198 | uint32_t _szidx : 6; |
199 | }; |
200 | |
201 | using NSSetMSyntheticFrontEnd = |
202 | GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; |
203 | |
204 | template <typename DD> |
205 | uint64_t |
206 | __NSSetMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr, |
207 | Status &error) { |
208 | const lldb::addr_t start_of_descriptor = |
209 | valobj_addr + process.GetAddressByteSize(); |
210 | DD descriptor = DD(); |
211 | process.ReadMemory(vm_addr: start_of_descriptor, buf: &descriptor, size: sizeof(descriptor), |
212 | error); |
213 | if (error.Fail()) { |
214 | return 0; |
215 | } |
216 | return descriptor._used; |
217 | } |
218 | |
219 | uint64_t |
220 | __NSSetMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, |
221 | Status &error) { |
222 | if (process.GetAddressByteSize() == 4) { |
223 | return __NSSetMSize_Impl<DataDescriptor_32>(process, valobj_addr, error); |
224 | } else { |
225 | return __NSSetMSize_Impl<DataDescriptor_64>(process, valobj_addr, error); |
226 | } |
227 | } |
228 | } |
229 | |
230 | class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
231 | public: |
232 | NSSetCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
233 | |
234 | ~NSSetCodeRunningSyntheticFrontEnd() override; |
235 | |
236 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
237 | |
238 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
239 | |
240 | lldb::ChildCacheState Update() override; |
241 | |
242 | bool MightHaveChildren() override; |
243 | |
244 | size_t GetIndexOfChildWithName(ConstString name) override; |
245 | }; |
246 | } // namespace formatters |
247 | } // namespace lldb_private |
248 | |
249 | template <bool cf_style> |
250 | bool lldb_private::formatters::NSSetSummaryProvider( |
251 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
252 | static constexpr llvm::StringLiteral g_TypeHint("NSSet" ); |
253 | |
254 | ProcessSP process_sp = valobj.GetProcessSP(); |
255 | if (!process_sp) |
256 | return false; |
257 | |
258 | ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(process&: *process_sp); |
259 | |
260 | if (!runtime) |
261 | return false; |
262 | |
263 | ObjCLanguageRuntime::ClassDescriptorSP descriptor( |
264 | runtime->GetClassDescriptor(in_value&: valobj)); |
265 | |
266 | if (!descriptor || !descriptor->IsValid()) |
267 | return false; |
268 | |
269 | uint32_t ptr_size = process_sp->GetAddressByteSize(); |
270 | bool is_64bit = (ptr_size == 8); |
271 | |
272 | lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(fail_value: 0); |
273 | |
274 | if (!valobj_addr) |
275 | return false; |
276 | |
277 | uint64_t value = 0; |
278 | |
279 | ConstString class_name(descriptor->GetClassName()); |
280 | |
281 | static const ConstString g_SetI("__NSSetI" ); |
282 | static const ConstString g_OrderedSetI("__NSOrderedSetI" ); |
283 | static const ConstString g_SetM("__NSSetM" ); |
284 | static const ConstString g_SetCF("__NSCFSet" ); |
285 | static const ConstString g_SetCFRef("CFSetRef" ); |
286 | |
287 | if (class_name.IsEmpty()) |
288 | return false; |
289 | |
290 | if (class_name == g_SetI || class_name == g_OrderedSetI) { |
291 | Status error; |
292 | value = process_sp->ReadUnsignedIntegerFromMemory(load_addr: valobj_addr + ptr_size, |
293 | byte_size: ptr_size, fail_value: 0, error); |
294 | if (error.Fail()) |
295 | return false; |
296 | value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); |
297 | } else if (class_name == g_SetM) { |
298 | AppleObjCRuntime *apple_runtime = |
299 | llvm::dyn_cast_or_null<AppleObjCRuntime>(Val: runtime); |
300 | Status error; |
301 | if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) { |
302 | value = Foundation1437::__NSSetMSize(process&: *process_sp, valobj_addr, error); |
303 | } else { |
304 | value = process_sp->ReadUnsignedIntegerFromMemory(load_addr: valobj_addr + ptr_size, |
305 | byte_size: ptr_size, fail_value: 0, error); |
306 | value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); |
307 | } |
308 | if (error.Fail()) |
309 | return false; |
310 | } else if (class_name == g_SetCF || class_name == g_SetCFRef) { |
311 | ExecutionContext exe_ctx(process_sp); |
312 | CFBasicHash cfbh; |
313 | if (!cfbh.Update(addr: valobj_addr, exe_ctx_rf: exe_ctx)) |
314 | return false; |
315 | value = cfbh.GetCount(); |
316 | } else { |
317 | auto &map(NSSet_Additionals::GetAdditionalSummaries()); |
318 | auto iter = map.find(x: class_name), end = map.end(); |
319 | if (iter != end) |
320 | return iter->second(valobj, stream, options); |
321 | else |
322 | return false; |
323 | } |
324 | |
325 | llvm::StringRef prefix, suffix; |
326 | if (Language *language = Language::FindPlugin(language: options.GetLanguage())) |
327 | std::tie(args&: prefix, args&: suffix) = language->GetFormatterPrefixSuffix(type_hint: g_TypeHint); |
328 | |
329 | stream << prefix; |
330 | stream.Printf(format: "%" PRIu64 " %s%s" , value, "element" , value == 1 ? "" : "s" ); |
331 | stream << suffix; |
332 | return true; |
333 | } |
334 | |
335 | SyntheticChildrenFrontEnd * |
336 | lldb_private::formatters::NSSetSyntheticFrontEndCreator( |
337 | CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { |
338 | lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
339 | if (!process_sp) |
340 | return nullptr; |
341 | ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(process&: *process_sp); |
342 | if (!runtime) |
343 | return nullptr; |
344 | |
345 | CompilerType valobj_type(valobj_sp->GetCompilerType()); |
346 | Flags flags(valobj_type.GetTypeInfo()); |
347 | |
348 | if (flags.IsClear(bit: eTypeIsPointer)) { |
349 | Status error; |
350 | valobj_sp = valobj_sp->AddressOf(error); |
351 | if (error.Fail() || !valobj_sp) |
352 | return nullptr; |
353 | } |
354 | |
355 | ObjCLanguageRuntime::ClassDescriptorSP descriptor( |
356 | runtime->GetClassDescriptor(in_value&: *valobj_sp)); |
357 | |
358 | if (!descriptor || !descriptor->IsValid()) |
359 | return nullptr; |
360 | |
361 | ConstString class_name = descriptor->GetClassName(); |
362 | |
363 | static const ConstString g_SetI("__NSSetI" ); |
364 | static const ConstString g_OrderedSetI("__NSOrderedSetI" ); |
365 | static const ConstString g_SetM("__NSSetM" ); |
366 | static const ConstString g_SetCF("__NSCFSet" ); |
367 | static const ConstString g_SetCFRef("CFSetRef" ); |
368 | |
369 | if (class_name.IsEmpty()) |
370 | return nullptr; |
371 | |
372 | if (class_name == g_SetI || class_name == g_OrderedSetI) { |
373 | return (new NSSetISyntheticFrontEnd(valobj_sp)); |
374 | } else if (class_name == g_SetM) { |
375 | AppleObjCRuntime *apple_runtime = |
376 | llvm::dyn_cast_or_null<AppleObjCRuntime>(Val: runtime); |
377 | if (apple_runtime) { |
378 | if (apple_runtime->GetFoundationVersion() >= 1437) |
379 | return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp)); |
380 | else if (apple_runtime->GetFoundationVersion() >= 1428) |
381 | return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp)); |
382 | else |
383 | return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); |
384 | } else { |
385 | return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); |
386 | } |
387 | } else if (class_name == g_SetCF || class_name == g_SetCFRef) { |
388 | return (new NSCFSetSyntheticFrontEnd(valobj_sp)); |
389 | } else { |
390 | auto &map(NSSet_Additionals::GetAdditionalSynthetics()); |
391 | auto iter = map.find(x: class_name), end = map.end(); |
392 | if (iter != end) |
393 | return iter->second(synth, valobj_sp); |
394 | return nullptr; |
395 | } |
396 | } |
397 | |
398 | lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd( |
399 | lldb::ValueObjectSP valobj_sp) |
400 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref() { |
401 | if (valobj_sp) |
402 | Update(); |
403 | } |
404 | |
405 | lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { |
406 | delete m_data_32; |
407 | m_data_32 = nullptr; |
408 | delete m_data_64; |
409 | m_data_64 = nullptr; |
410 | } |
411 | |
412 | size_t |
413 | lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( |
414 | ConstString name) { |
415 | const char *item_name = name.GetCString(); |
416 | uint32_t idx = ExtractIndexFromString(item_name); |
417 | if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) |
418 | return UINT32_MAX; |
419 | return idx; |
420 | } |
421 | |
422 | llvm::Expected<uint32_t> |
423 | lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() { |
424 | if (!m_data_32 && !m_data_64) |
425 | return 0; |
426 | return (m_data_32 ? m_data_32->_used : m_data_64->_used); |
427 | } |
428 | |
429 | lldb::ChildCacheState |
430 | lldb_private::formatters::NSSetISyntheticFrontEnd::Update() { |
431 | m_children.clear(); |
432 | delete m_data_32; |
433 | m_data_32 = nullptr; |
434 | delete m_data_64; |
435 | m_data_64 = nullptr; |
436 | m_ptr_size = 0; |
437 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
438 | if (!valobj_sp) |
439 | return lldb::ChildCacheState::eRefetch; |
440 | if (!valobj_sp) |
441 | return lldb::ChildCacheState::eRefetch; |
442 | m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); |
443 | lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
444 | if (!process_sp) |
445 | return lldb::ChildCacheState::eRefetch; |
446 | m_ptr_size = process_sp->GetAddressByteSize(); |
447 | uint64_t data_location = valobj_sp->GetValueAsUnsigned(fail_value: 0) + m_ptr_size; |
448 | Status error; |
449 | if (m_ptr_size == 4) { |
450 | m_data_32 = new DataDescriptor_32(); |
451 | process_sp->ReadMemory(vm_addr: data_location, buf: m_data_32, size: sizeof(DataDescriptor_32), |
452 | error); |
453 | } else { |
454 | m_data_64 = new DataDescriptor_64(); |
455 | process_sp->ReadMemory(vm_addr: data_location, buf: m_data_64, size: sizeof(DataDescriptor_64), |
456 | error); |
457 | } |
458 | if (error.Fail()) |
459 | return lldb::ChildCacheState::eRefetch; |
460 | m_data_ptr = data_location + m_ptr_size; |
461 | return lldb::ChildCacheState::eReuse; |
462 | } |
463 | |
464 | bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() { |
465 | return true; |
466 | } |
467 | |
468 | lldb::ValueObjectSP |
469 | lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex( |
470 | uint32_t idx) { |
471 | uint32_t num_children = CalculateNumChildrenIgnoringErrors(); |
472 | |
473 | if (idx >= num_children) |
474 | return lldb::ValueObjectSP(); |
475 | |
476 | ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); |
477 | if (!process_sp) |
478 | return lldb::ValueObjectSP(); |
479 | |
480 | if (m_children.empty()) { |
481 | // do the scan phase |
482 | lldb::addr_t obj_at_idx = 0; |
483 | |
484 | uint32_t tries = 0; |
485 | uint32_t test_idx = 0; |
486 | |
487 | while (tries < num_children) { |
488 | obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); |
489 | if (!process_sp) |
490 | return lldb::ValueObjectSP(); |
491 | Status error; |
492 | obj_at_idx = process_sp->ReadPointerFromMemory(vm_addr: obj_at_idx, error); |
493 | if (error.Fail()) |
494 | return lldb::ValueObjectSP(); |
495 | |
496 | test_idx++; |
497 | |
498 | if (!obj_at_idx) |
499 | continue; |
500 | tries++; |
501 | |
502 | SetItemDescriptor descriptor = {.item_ptr: obj_at_idx, .valobj_sp: lldb::ValueObjectSP()}; |
503 | |
504 | m_children.push_back(x: descriptor); |
505 | } |
506 | } |
507 | |
508 | if (idx >= m_children.size()) // should never happen |
509 | return lldb::ValueObjectSP(); |
510 | |
511 | SetItemDescriptor &set_item = m_children[idx]; |
512 | if (!set_item.valobj_sp) { |
513 | auto ptr_size = process_sp->GetAddressByteSize(); |
514 | DataBufferHeap buffer(ptr_size, 0); |
515 | switch (ptr_size) { |
516 | case 0: // architecture has no clue - fail |
517 | return lldb::ValueObjectSP(); |
518 | case 4: |
519 | *reinterpret_cast<uint32_t *>(buffer.GetBytes()) = |
520 | static_cast<uint32_t>(set_item.item_ptr); |
521 | break; |
522 | case 8: |
523 | *reinterpret_cast<uint64_t *>(buffer.GetBytes()) = |
524 | static_cast<uint64_t>(set_item.item_ptr); |
525 | break; |
526 | default: |
527 | lldbassert(false && "pointer size is not 4 nor 8" ); |
528 | } |
529 | StreamString idx_name; |
530 | idx_name.Printf(format: "[%" PRIu64 "]" , (uint64_t)idx); |
531 | |
532 | DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), |
533 | process_sp->GetByteOrder(), |
534 | process_sp->GetAddressByteSize()); |
535 | |
536 | set_item.valobj_sp = CreateValueObjectFromData( |
537 | name: idx_name.GetString(), data, exe_ctx: m_exe_ctx_ref, |
538 | type: m_backend.GetCompilerType().GetBasicTypeFromAST( |
539 | basic_type: lldb::eBasicTypeObjCID)); |
540 | } |
541 | return set_item.valobj_sp; |
542 | } |
543 | |
544 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( |
545 | lldb::ValueObjectSP valobj_sp) |
546 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(), |
547 | m_pair_type() {} |
548 | |
549 | size_t |
550 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( |
551 | ConstString name) { |
552 | const char *item_name = name.GetCString(); |
553 | const uint32_t idx = ExtractIndexFromString(item_name); |
554 | if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) |
555 | return UINT32_MAX; |
556 | return idx; |
557 | } |
558 | |
559 | llvm::Expected<uint32_t> |
560 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() { |
561 | if (!m_hashtable.IsValid()) |
562 | return 0; |
563 | return m_hashtable.GetCount(); |
564 | } |
565 | |
566 | lldb::ChildCacheState |
567 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() { |
568 | m_children.clear(); |
569 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
570 | m_ptr_size = 0; |
571 | if (!valobj_sp) |
572 | return lldb::ChildCacheState::eRefetch; |
573 | m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); |
574 | |
575 | lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
576 | if (!process_sp) |
577 | return lldb::ChildCacheState::eRefetch; |
578 | m_ptr_size = process_sp->GetAddressByteSize(); |
579 | m_order = process_sp->GetByteOrder(); |
580 | return m_hashtable.Update(addr: valobj_sp->GetValueAsUnsigned(fail_value: 0), exe_ctx_rf: m_exe_ctx_ref) |
581 | ? lldb::ChildCacheState::eReuse |
582 | : lldb::ChildCacheState::eRefetch; |
583 | } |
584 | |
585 | bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() { |
586 | return true; |
587 | } |
588 | |
589 | lldb::ValueObjectSP |
590 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex( |
591 | uint32_t idx) { |
592 | lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); |
593 | |
594 | const uint32_t num_children = CalculateNumChildrenIgnoringErrors(); |
595 | |
596 | if (idx >= num_children) |
597 | return lldb::ValueObjectSP(); |
598 | |
599 | if (m_children.empty()) { |
600 | ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); |
601 | if (!process_sp) |
602 | return lldb::ValueObjectSP(); |
603 | |
604 | Status error; |
605 | lldb::addr_t val_at_idx = 0; |
606 | |
607 | uint32_t tries = 0; |
608 | uint32_t test_idx = 0; |
609 | |
610 | // Iterate over inferior memory, reading value pointers by shifting the |
611 | // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read |
612 | // fails, otherwise, continue until the number of tries matches the number |
613 | // of childen. |
614 | while (tries < num_children) { |
615 | val_at_idx = m_values_ptr + (test_idx * m_ptr_size); |
616 | |
617 | val_at_idx = process_sp->ReadPointerFromMemory(vm_addr: val_at_idx, error); |
618 | if (error.Fail()) |
619 | return lldb::ValueObjectSP(); |
620 | |
621 | test_idx++; |
622 | |
623 | if (!val_at_idx) |
624 | continue; |
625 | tries++; |
626 | |
627 | SetItemDescriptor descriptor = {.item_ptr: val_at_idx, .valobj_sp: lldb::ValueObjectSP()}; |
628 | |
629 | m_children.push_back(x: descriptor); |
630 | } |
631 | } |
632 | |
633 | if (idx >= m_children.size()) // should never happen |
634 | return lldb::ValueObjectSP(); |
635 | |
636 | SetItemDescriptor &set_item = m_children[idx]; |
637 | if (!set_item.valobj_sp) { |
638 | |
639 | WritableDataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0)); |
640 | |
641 | switch (m_ptr_size) { |
642 | case 0: // architecture has no clue - fail |
643 | return lldb::ValueObjectSP(); |
644 | case 4: |
645 | *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) = |
646 | static_cast<uint32_t>(set_item.item_ptr); |
647 | break; |
648 | case 8: |
649 | *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) = |
650 | static_cast<uint64_t>(set_item.item_ptr); |
651 | break; |
652 | default: |
653 | lldbassert(false && "pointer size is not 4 nor 8" ); |
654 | } |
655 | StreamString idx_name; |
656 | idx_name.Printf(format: "[%" PRIu64 "]" , (uint64_t)idx); |
657 | |
658 | DataExtractor data(buffer_sp, m_order, m_ptr_size); |
659 | |
660 | set_item.valobj_sp = CreateValueObjectFromData( |
661 | name: idx_name.GetString(), data, exe_ctx: m_exe_ctx_ref, |
662 | type: m_backend.GetCompilerType().GetBasicTypeFromAST( |
663 | basic_type: lldb::eBasicTypeObjCID)); |
664 | } |
665 | |
666 | return set_item.valobj_sp; |
667 | } |
668 | |
669 | template <typename D32, typename D64> |
670 | lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< |
671 | D32, D64>::GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
672 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), |
673 | m_data_32(nullptr), m_data_64(nullptr) { |
674 | if (valobj_sp) |
675 | Update(); |
676 | } |
677 | |
678 | template <typename D32, typename D64> |
679 | lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<D32, D64>:: |
680 | GenericNSSetMSyntheticFrontEnd::~GenericNSSetMSyntheticFrontEnd() { |
681 | delete m_data_32; |
682 | m_data_32 = nullptr; |
683 | delete m_data_64; |
684 | m_data_64 = nullptr; |
685 | } |
686 | |
687 | template <typename D32, typename D64> |
688 | size_t |
689 | lldb_private::formatters:: |
690 | GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName( |
691 | ConstString name) { |
692 | const char *item_name = name.GetCString(); |
693 | uint32_t idx = ExtractIndexFromString(item_name); |
694 | if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) |
695 | return UINT32_MAX; |
696 | return idx; |
697 | } |
698 | |
699 | template <typename D32, typename D64> |
700 | llvm::Expected<uint32_t> |
701 | lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< |
702 | D32, D64>::CalculateNumChildren() { |
703 | if (!m_data_32 && !m_data_64) |
704 | return 0; |
705 | return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used); |
706 | } |
707 | |
708 | template <typename D32, typename D64> |
709 | lldb::ChildCacheState |
710 | lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() { |
711 | m_children.clear(); |
712 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
713 | m_ptr_size = 0; |
714 | delete m_data_32; |
715 | m_data_32 = nullptr; |
716 | delete m_data_64; |
717 | m_data_64 = nullptr; |
718 | if (!valobj_sp) |
719 | return lldb::ChildCacheState::eRefetch; |
720 | if (!valobj_sp) |
721 | return lldb::ChildCacheState::eRefetch; |
722 | m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); |
723 | lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
724 | if (!process_sp) |
725 | return lldb::ChildCacheState::eRefetch; |
726 | m_ptr_size = process_sp->GetAddressByteSize(); |
727 | uint64_t data_location = valobj_sp->GetValueAsUnsigned(fail_value: 0) + m_ptr_size; |
728 | Status error; |
729 | if (m_ptr_size == 4) { |
730 | m_data_32 = new D32(); |
731 | process_sp->ReadMemory(vm_addr: data_location, buf: m_data_32, size: sizeof(D32), |
732 | error); |
733 | } else { |
734 | m_data_64 = new D64(); |
735 | process_sp->ReadMemory(vm_addr: data_location, buf: m_data_64, size: sizeof(D64), |
736 | error); |
737 | } |
738 | return error.Success() ? lldb::ChildCacheState::eReuse |
739 | : lldb::ChildCacheState::eRefetch; |
740 | } |
741 | |
742 | template <typename D32, typename D64> |
743 | bool |
744 | lldb_private::formatters:: |
745 | GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() { |
746 | return true; |
747 | } |
748 | |
749 | template <typename D32, typename D64> |
750 | lldb::ValueObjectSP |
751 | lldb_private::formatters:: |
752 | GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(uint32_t idx) { |
753 | lldb::addr_t m_objs_addr = |
754 | (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); |
755 | |
756 | uint32_t num_children = CalculateNumChildrenIgnoringErrors(); |
757 | |
758 | if (idx >= num_children) |
759 | return lldb::ValueObjectSP(); |
760 | |
761 | ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); |
762 | if (!process_sp) |
763 | return lldb::ValueObjectSP(); |
764 | |
765 | if (m_children.empty()) { |
766 | // do the scan phase |
767 | lldb::addr_t obj_at_idx = 0; |
768 | |
769 | uint32_t tries = 0; |
770 | uint32_t test_idx = 0; |
771 | |
772 | while (tries < num_children) { |
773 | obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); |
774 | if (!process_sp) |
775 | return lldb::ValueObjectSP(); |
776 | Status error; |
777 | obj_at_idx = process_sp->ReadPointerFromMemory(vm_addr: obj_at_idx, error); |
778 | if (error.Fail()) |
779 | return lldb::ValueObjectSP(); |
780 | |
781 | test_idx++; |
782 | |
783 | if (!obj_at_idx) |
784 | continue; |
785 | tries++; |
786 | |
787 | SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; |
788 | |
789 | m_children.push_back(descriptor); |
790 | } |
791 | } |
792 | |
793 | if (idx >= m_children.size()) // should never happen |
794 | return lldb::ValueObjectSP(); |
795 | |
796 | SetItemDescriptor &set_item = m_children[idx]; |
797 | if (!set_item.valobj_sp) { |
798 | auto ptr_size = process_sp->GetAddressByteSize(); |
799 | DataBufferHeap buffer(ptr_size, 0); |
800 | switch (ptr_size) { |
801 | case 0: // architecture has no clue?? - fail |
802 | return lldb::ValueObjectSP(); |
803 | case 4: |
804 | *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; |
805 | break; |
806 | case 8: |
807 | *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; |
808 | break; |
809 | default: |
810 | assert(false && "pointer size is not 4 nor 8 - get out of here ASAP" ); |
811 | } |
812 | StreamString idx_name; |
813 | idx_name.Printf(format: "[%" PRIu64 "]" , (uint64_t)idx); |
814 | |
815 | DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), |
816 | process_sp->GetByteOrder(), |
817 | process_sp->GetAddressByteSize()); |
818 | |
819 | set_item.valobj_sp = CreateValueObjectFromData( |
820 | name: idx_name.GetString(), data, exe_ctx: m_exe_ctx_ref, |
821 | type: m_backend.GetCompilerType().GetBasicTypeFromAST( |
822 | lldb::eBasicTypeObjCID)); |
823 | } |
824 | return set_item.valobj_sp; |
825 | } |
826 | |
827 | template bool lldb_private::formatters::NSSetSummaryProvider<true>( |
828 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); |
829 | |
830 | template bool lldb_private::formatters::NSSetSummaryProvider<false>( |
831 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); |
832 | |