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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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