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
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 bool MightHaveChildren() override;
56
57 size_t GetIndexOfChildWithName(ConstString name) override;
58
59private:
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
83class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
84public:
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
97private:
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
113template <typename D32, typename D64>
114class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
115public:
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
130private:
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
144namespace 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
163namespace 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
182namespace 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
230class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
231public:
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
249template <bool cf_style>
250bool 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
335SyntheticChildrenFrontEnd *
336lldb_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
398lldb_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
405lldb_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
412size_t
413lldb_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
422llvm::Expected<uint32_t>
423lldb_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
429lldb::ChildCacheState
430lldb_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
464bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
465 return true;
466}
467
468lldb::ValueObjectSP
469lldb_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
544lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd(
545 lldb::ValueObjectSP valobj_sp)
546 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(),
547 m_pair_type() {}
548
549size_t
550lldb_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
559llvm::Expected<uint32_t>
560lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() {
561 if (!m_hashtable.IsValid())
562 return 0;
563 return m_hashtable.GetCount();
564}
565
566lldb::ChildCacheState
567lldb_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
585bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() {
586 return true;
587}
588
589lldb::ValueObjectSP
590lldb_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
669template <typename D32, typename D64>
670lldb_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
678template <typename D32, typename D64>
679lldb_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
687template <typename D32, typename D64>
688size_t
689lldb_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
699template <typename D32, typename D64>
700llvm::Expected<uint32_t>
701lldb_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
708template <typename D32, typename D64>
709lldb::ChildCacheState
710lldb_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
742template <typename D32, typename D64>
743bool
744lldb_private::formatters::
745 GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
746 return true;
747}
748
749template <typename D32, typename D64>
750lldb::ValueObjectSP
751lldb_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
827template bool lldb_private::formatters::NSSetSummaryProvider<true>(
828 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
829
830template bool lldb_private::formatters::NSSetSummaryProvider<false>(
831 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
832

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