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> &
GetAdditionalSummaries()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> &
GetAdditionalSynthetics()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 size_t CalculateNumChildren() override;
50
51 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
52
53 bool 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;
77 DataDescriptor_32 *m_data_32;
78 DataDescriptor_64 *m_data_64;
79 lldb::addr_t m_data_ptr;
80 std::vector<SetItemDescriptor> m_children;
81 };
82
83 class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
84 public:
85 NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
86
87 size_t CalculateNumChildren() override;
88
89 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
90
91 bool 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;
105 lldb::ByteOrder m_order;
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 size_t CalculateNumChildren() override;
121
122 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
123
124 bool 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;
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
__NSSetMSize_Impl(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)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(start_of_descriptor, &descriptor, sizeof(descriptor),
212 error);
213 if (error.Fail()) {
214 return 0;
215 }
216 return descriptor._used;
217 }
218
219 uint64_t
__NSSetMSize(lldb_private::Process & process,lldb::addr_t valobj_addr,Status & error)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 size_t CalculateNumChildren() override;
237
238 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
239
240 bool 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>
NSSetSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)250 bool lldb_private::formatters::NSSetSummaryProvider(
251 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
252 static ConstString g_TypeHint("NSSet");
253
254 ProcessSP process_sp = valobj.GetProcessSP();
255 if (!process_sp)
256 return false;
257
258 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
259
260 if (!runtime)
261 return false;
262
263 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
264 runtime->GetClassDescriptor(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(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(valobj_addr + ptr_size,
293 ptr_size, 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>(runtime);
300 Status error;
301 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
302 value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error);
303 } else {
304 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
305 ptr_size, 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(valobj_addr, exe_ctx))
314 return false;
315 value = cfbh.GetCount();
316 } else {
317 auto &map(NSSet_Additionals::GetAdditionalSummaries());
318 auto iter = map.find(class_name), end = map.end();
319 if (iter != end)
320 return iter->second(valobj, stream, options);
321 else
322 return false;
323 }
324
325 std::string prefix, suffix;
326 if (Language *language = Language::FindPlugin(options.GetLanguage())) {
327 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
328 suffix)) {
329 prefix.clear();
330 suffix.clear();
331 }
332 }
333
334 stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
335 value == 1 ? "" : "s", suffix.c_str());
336 return true;
337 }
338
339 SyntheticChildrenFrontEnd *
NSSetSyntheticFrontEndCreator(CXXSyntheticChildren * synth,lldb::ValueObjectSP valobj_sp)340 lldb_private::formatters::NSSetSyntheticFrontEndCreator(
341 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
342 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
343 if (!process_sp)
344 return nullptr;
345 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
346 if (!runtime)
347 return nullptr;
348
349 CompilerType valobj_type(valobj_sp->GetCompilerType());
350 Flags flags(valobj_type.GetTypeInfo());
351
352 if (flags.IsClear(eTypeIsPointer)) {
353 Status error;
354 valobj_sp = valobj_sp->AddressOf(error);
355 if (error.Fail() || !valobj_sp)
356 return nullptr;
357 }
358
359 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
360 runtime->GetClassDescriptor(*valobj_sp));
361
362 if (!descriptor || !descriptor->IsValid())
363 return nullptr;
364
365 ConstString class_name = descriptor->GetClassName();
366
367 static const ConstString g_SetI("__NSSetI");
368 static const ConstString g_OrderedSetI("__NSOrderedSetI");
369 static const ConstString g_SetM("__NSSetM");
370 static const ConstString g_SetCF("__NSCFSet");
371 static const ConstString g_SetCFRef("CFSetRef");
372
373 if (class_name.IsEmpty())
374 return nullptr;
375
376 if (class_name == g_SetI || class_name == g_OrderedSetI) {
377 return (new NSSetISyntheticFrontEnd(valobj_sp));
378 } else if (class_name == g_SetM) {
379 AppleObjCRuntime *apple_runtime =
380 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
381 if (apple_runtime) {
382 if (apple_runtime->GetFoundationVersion() >= 1437)
383 return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp));
384 else if (apple_runtime->GetFoundationVersion() >= 1428)
385 return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp));
386 else
387 return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
388 } else {
389 return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
390 }
391 } else if (class_name == g_SetCF || class_name == g_SetCFRef) {
392 return (new NSCFSetSyntheticFrontEnd(valobj_sp));
393 } else {
394 auto &map(NSSet_Additionals::GetAdditionalSynthetics());
395 auto iter = map.find(class_name), end = map.end();
396 if (iter != end)
397 return iter->second(synth, valobj_sp);
398 return nullptr;
399 }
400 }
401
NSSetISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)402 lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd(
403 lldb::ValueObjectSP valobj_sp)
404 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
405 m_data_32(nullptr), m_data_64(nullptr) {
406 if (valobj_sp)
407 Update();
408 }
409
~NSSetISyntheticFrontEnd()410 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() {
411 delete m_data_32;
412 m_data_32 = nullptr;
413 delete m_data_64;
414 m_data_64 = nullptr;
415 }
416
417 size_t
GetIndexOfChildWithName(ConstString name)418 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName(
419 ConstString name) {
420 const char *item_name = name.GetCString();
421 uint32_t idx = ExtractIndexFromString(item_name);
422 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
423 return UINT32_MAX;
424 return idx;
425 }
426
427 size_t
CalculateNumChildren()428 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() {
429 if (!m_data_32 && !m_data_64)
430 return 0;
431 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
432 }
433
Update()434 bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() {
435 m_children.clear();
436 delete m_data_32;
437 m_data_32 = nullptr;
438 delete m_data_64;
439 m_data_64 = nullptr;
440 m_ptr_size = 0;
441 ValueObjectSP valobj_sp = m_backend.GetSP();
442 if (!valobj_sp)
443 return false;
444 if (!valobj_sp)
445 return false;
446 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
447 Status error;
448 if (valobj_sp->IsPointerType()) {
449 valobj_sp = valobj_sp->Dereference(error);
450 if (error.Fail() || !valobj_sp)
451 return false;
452 }
453 error.Clear();
454 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
455 if (!process_sp)
456 return false;
457 m_ptr_size = process_sp->GetAddressByteSize();
458 uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
459 if (m_ptr_size == 4) {
460 m_data_32 = new DataDescriptor_32();
461 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
462 error);
463 } else {
464 m_data_64 = new DataDescriptor_64();
465 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
466 error);
467 }
468 if (error.Fail())
469 return false;
470 m_data_ptr = data_location + m_ptr_size;
471 return false;
472 }
473
MightHaveChildren()474 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
475 return true;
476 }
477
478 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)479 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) {
480 uint32_t num_children = CalculateNumChildren();
481
482 if (idx >= num_children)
483 return lldb::ValueObjectSP();
484
485 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
486 if (!process_sp)
487 return lldb::ValueObjectSP();
488
489 if (m_children.empty()) {
490 // do the scan phase
491 lldb::addr_t obj_at_idx = 0;
492
493 uint32_t tries = 0;
494 uint32_t test_idx = 0;
495
496 while (tries < num_children) {
497 obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
498 if (!process_sp)
499 return lldb::ValueObjectSP();
500 Status error;
501 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
502 if (error.Fail())
503 return lldb::ValueObjectSP();
504
505 test_idx++;
506
507 if (!obj_at_idx)
508 continue;
509 tries++;
510
511 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
512
513 m_children.push_back(descriptor);
514 }
515 }
516
517 if (idx >= m_children.size()) // should never happen
518 return lldb::ValueObjectSP();
519
520 SetItemDescriptor &set_item = m_children[idx];
521 if (!set_item.valobj_sp) {
522 auto ptr_size = process_sp->GetAddressByteSize();
523 DataBufferHeap buffer(ptr_size, 0);
524 switch (ptr_size) {
525 case 0: // architecture has no clue - fail
526 return lldb::ValueObjectSP();
527 case 4:
528 *reinterpret_cast<uint32_t *>(buffer.GetBytes()) =
529 static_cast<uint32_t>(set_item.item_ptr);
530 break;
531 case 8:
532 *reinterpret_cast<uint64_t *>(buffer.GetBytes()) =
533 static_cast<uint64_t>(set_item.item_ptr);
534 break;
535 default:
536 lldbassert(false && "pointer size is not 4 nor 8");
537 }
538 StreamString idx_name;
539 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
540
541 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
542 process_sp->GetByteOrder(),
543 process_sp->GetAddressByteSize());
544
545 set_item.valobj_sp = CreateValueObjectFromData(
546 idx_name.GetString(), data, m_exe_ctx_ref,
547 m_backend.GetCompilerType().GetBasicTypeFromAST(
548 lldb::eBasicTypeObjCID));
549 }
550 return set_item.valobj_sp;
551 }
552
NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)553 lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd(
554 lldb::ValueObjectSP valobj_sp)
555 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
556 m_order(lldb::eByteOrderInvalid), m_hashtable(), m_pair_type() {}
557
558 size_t
GetIndexOfChildWithName(ConstString name)559 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName(
560 ConstString name) {
561 const char *item_name = name.GetCString();
562 const uint32_t idx = ExtractIndexFromString(item_name);
563 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
564 return UINT32_MAX;
565 return idx;
566 }
567
568 size_t
CalculateNumChildren()569 lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() {
570 if (!m_hashtable.IsValid())
571 return 0;
572 return m_hashtable.GetCount();
573 }
574
Update()575 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() {
576 m_children.clear();
577 ValueObjectSP valobj_sp = m_backend.GetSP();
578 m_ptr_size = 0;
579 if (!valobj_sp)
580 return false;
581 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
582
583 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
584 if (!process_sp)
585 return false;
586 m_ptr_size = process_sp->GetAddressByteSize();
587 m_order = process_sp->GetByteOrder();
588 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref);
589 }
590
MightHaveChildren()591 bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() {
592 return true;
593 }
594
595 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)596 lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex(
597 size_t idx) {
598 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
599
600 const uint32_t num_children = CalculateNumChildren();
601
602 if (idx >= num_children)
603 return lldb::ValueObjectSP();
604
605 if (m_children.empty()) {
606 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
607 if (!process_sp)
608 return lldb::ValueObjectSP();
609
610 Status error;
611 lldb::addr_t val_at_idx = 0;
612
613 uint32_t tries = 0;
614 uint32_t test_idx = 0;
615
616 // Iterate over inferior memory, reading value pointers by shifting the
617 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
618 // fails, otherwise, continue until the number of tries matches the number
619 // of childen.
620 while (tries < num_children) {
621 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
622
623 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
624 if (error.Fail())
625 return lldb::ValueObjectSP();
626
627 test_idx++;
628
629 if (!val_at_idx)
630 continue;
631 tries++;
632
633 SetItemDescriptor descriptor = {val_at_idx, lldb::ValueObjectSP()};
634
635 m_children.push_back(descriptor);
636 }
637 }
638
639 if (idx >= m_children.size()) // should never happen
640 return lldb::ValueObjectSP();
641
642 SetItemDescriptor &set_item = m_children[idx];
643 if (!set_item.valobj_sp) {
644
645 DataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0));
646
647 switch (m_ptr_size) {
648 case 0: // architecture has no clue - fail
649 return lldb::ValueObjectSP();
650 case 4:
651 *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) =
652 static_cast<uint32_t>(set_item.item_ptr);
653 break;
654 case 8:
655 *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) =
656 static_cast<uint64_t>(set_item.item_ptr);
657 break;
658 default:
659 lldbassert(false && "pointer size is not 4 nor 8");
660 }
661 StreamString idx_name;
662 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
663
664 DataExtractor data(buffer_sp, m_order, m_ptr_size);
665
666 set_item.valobj_sp = CreateValueObjectFromData(
667 idx_name.GetString(), data, m_exe_ctx_ref,
668 m_backend.GetCompilerType().GetBasicTypeFromAST(
669 lldb::eBasicTypeObjCID));
670 }
671
672 return set_item.valobj_sp;
673 }
674
675 template <typename D32, typename D64>
676 lldb_private::formatters::
GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)677 GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd(
678 lldb::ValueObjectSP valobj_sp)
679 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
680 m_data_32(nullptr), m_data_64(nullptr) {
681 if (valobj_sp)
682 Update();
683 }
684
685 template <typename D32, typename D64>
686 lldb_private::formatters::
~GenericNSSetMSyntheticFrontEnd()687 GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd<D32, D64>() {
688 delete m_data_32;
689 m_data_32 = nullptr;
690 delete m_data_64;
691 m_data_64 = nullptr;
692 }
693
694 template <typename D32, typename D64>
695 size_t
696 lldb_private::formatters::
GetIndexOfChildWithName(ConstString name)697 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName(
698 ConstString name) {
699 const char *item_name = name.GetCString();
700 uint32_t idx = ExtractIndexFromString(item_name);
701 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
702 return UINT32_MAX;
703 return idx;
704 }
705
706 template <typename D32, typename D64>
707 size_t
708 lldb_private::formatters::
CalculateNumChildren()709 GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() {
710 if (!m_data_32 && !m_data_64)
711 return 0;
712 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
713 }
714
715 template <typename D32, typename D64>
716 bool
717 lldb_private::formatters::
Update()718 GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() {
719 m_children.clear();
720 ValueObjectSP valobj_sp = m_backend.GetSP();
721 m_ptr_size = 0;
722 delete m_data_32;
723 m_data_32 = nullptr;
724 delete m_data_64;
725 m_data_64 = nullptr;
726 if (!valobj_sp)
727 return false;
728 if (!valobj_sp)
729 return false;
730 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
731 Status error;
732 if (valobj_sp->IsPointerType()) {
733 valobj_sp = valobj_sp->Dereference(error);
734 if (error.Fail() || !valobj_sp)
735 return false;
736 }
737 error.Clear();
738 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
739 if (!process_sp)
740 return false;
741 m_ptr_size = process_sp->GetAddressByteSize();
742 uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
743 if (m_ptr_size == 4) {
744 m_data_32 = new D32();
745 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
746 error);
747 } else {
748 m_data_64 = new D64();
749 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
750 error);
751 }
752 if (error.Fail())
753 return false;
754 return false;
755 }
756
757 template <typename D32, typename D64>
758 bool
759 lldb_private::formatters::
MightHaveChildren()760 GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
761 return true;
762 }
763
764 template <typename D32, typename D64>
765 lldb::ValueObjectSP
766 lldb_private::formatters::
GetChildAtIndex(size_t idx)767 GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) {
768 lldb::addr_t m_objs_addr =
769 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
770
771 uint32_t num_children = CalculateNumChildren();
772
773 if (idx >= num_children)
774 return lldb::ValueObjectSP();
775
776 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
777 if (!process_sp)
778 return lldb::ValueObjectSP();
779
780 if (m_children.empty()) {
781 // do the scan phase
782 lldb::addr_t obj_at_idx = 0;
783
784 uint32_t tries = 0;
785 uint32_t test_idx = 0;
786
787 while (tries < num_children) {
788 obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
789 if (!process_sp)
790 return lldb::ValueObjectSP();
791 Status error;
792 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
793 if (error.Fail())
794 return lldb::ValueObjectSP();
795
796 test_idx++;
797
798 if (!obj_at_idx)
799 continue;
800 tries++;
801
802 SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
803
804 m_children.push_back(descriptor);
805 }
806 }
807
808 if (idx >= m_children.size()) // should never happen
809 return lldb::ValueObjectSP();
810
811 SetItemDescriptor &set_item = m_children[idx];
812 if (!set_item.valobj_sp) {
813 auto ptr_size = process_sp->GetAddressByteSize();
814 DataBufferHeap buffer(ptr_size, 0);
815 switch (ptr_size) {
816 case 0: // architecture has no clue?? - fail
817 return lldb::ValueObjectSP();
818 case 4:
819 *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
820 break;
821 case 8:
822 *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
823 break;
824 default:
825 assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
826 }
827 StreamString idx_name;
828 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
829
830 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
831 process_sp->GetByteOrder(),
832 process_sp->GetAddressByteSize());
833
834 set_item.valobj_sp = CreateValueObjectFromData(
835 idx_name.GetString(), data, m_exe_ctx_ref,
836 m_backend.GetCompilerType().GetBasicTypeFromAST(
837 lldb::eBasicTypeObjCID));
838 }
839 return set_item.valobj_sp;
840 }
841
842 template bool lldb_private::formatters::NSSetSummaryProvider<true>(
843 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
844
845 template bool lldb_private::formatters::NSSetSummaryProvider<false>(
846 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
847