1//===-- NSDictionary.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 <mutex>
10
11#include "clang/AST/DeclCXX.h"
12
13#include "CFBasicHash.h"
14#include "NSDictionary.h"
15
16#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
17#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
18
19#include "lldb/Core/ValueObject.h"
20#include "lldb/Core/ValueObjectConstResult.h"
21#include "lldb/DataFormatters/FormattersHelpers.h"
22#include "lldb/Target/Language.h"
23#include "lldb/Target/StackFrame.h"
24#include "lldb/Target/Target.h"
25#include "lldb/Utility/DataBufferHeap.h"
26#include "lldb/Utility/Endian.h"
27#include "lldb/Utility/Status.h"
28#include "lldb/Utility/Stream.h"
29
30using namespace lldb;
31using namespace lldb_private;
32using namespace lldb_private::formatters;
33
34NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
35 ConstString p)
36 : m_prefix(p) {}
37
38bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
39 ConstString class_name) {
40 return class_name.GetStringRef().starts_with(Prefix: m_prefix.GetStringRef());
41}
42
43NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
44 : m_name(n) {}
45
46bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
47 ConstString class_name) {
48 return (class_name == m_name);
49}
50
51NSDictionary_Additionals::AdditionalFormatters<
52 CXXFunctionSummaryFormat::Callback> &
53NSDictionary_Additionals::GetAdditionalSummaries() {
54 static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
55 return g_map;
56}
57
58NSDictionary_Additionals::AdditionalFormatters<
59 CXXSyntheticChildren::CreateFrontEndCallback> &
60NSDictionary_Additionals::GetAdditionalSynthetics() {
61 static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
62 g_map;
63 return g_map;
64}
65
66static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
67 CompilerType compiler_type;
68 TypeSystemClangSP scratch_ts_sp =
69 ScratchTypeSystemClang::GetForTarget(target&: *target_sp);
70
71 if (!scratch_ts_sp)
72 return compiler_type;
73
74 static constexpr llvm::StringLiteral g_lldb_autogen_nspair("__lldb_autogen_nspair");
75
76 compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(type_name: g_lldb_autogen_nspair);
77
78 if (!compiler_type) {
79 compiler_type = scratch_ts_sp->CreateRecordType(
80 decl_ctx: nullptr, owning_module: OptionalClangModuleID(), access_type: lldb::eAccessPublic,
81 name: g_lldb_autogen_nspair, kind: llvm::to_underlying(E: clang::TagTypeKind::Struct),
82 language: lldb::eLanguageTypeC);
83
84 if (compiler_type) {
85 TypeSystemClang::StartTagDeclarationDefinition(type: compiler_type);
86 CompilerType id_compiler_type =
87 scratch_ts_sp->GetBasicType(type: eBasicTypeObjCID);
88 TypeSystemClang::AddFieldToRecordType(
89 type: compiler_type, name: "key", field_type: id_compiler_type, access: lldb::eAccessPublic, bitfield_bit_size: 0);
90 TypeSystemClang::AddFieldToRecordType(
91 type: compiler_type, name: "value", field_type: id_compiler_type, access: lldb::eAccessPublic, bitfield_bit_size: 0);
92 TypeSystemClang::CompleteTagDeclarationDefinition(type: compiler_type);
93 }
94 }
95 return compiler_type;
96}
97
98namespace lldb_private {
99namespace formatters {
100class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
101public:
102 NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
103
104 ~NSDictionaryISyntheticFrontEnd() override;
105
106 llvm::Expected<uint32_t> CalculateNumChildren() override;
107
108 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
109
110 lldb::ChildCacheState Update() override;
111
112 bool MightHaveChildren() override;
113
114 size_t GetIndexOfChildWithName(ConstString name) override;
115
116private:
117 struct DataDescriptor_32 {
118 uint32_t _used : 26;
119 uint32_t _szidx : 6;
120 };
121
122 struct DataDescriptor_64 {
123 uint64_t _used : 58;
124 uint32_t _szidx : 6;
125 };
126
127 struct DictionaryItemDescriptor {
128 lldb::addr_t key_ptr;
129 lldb::addr_t val_ptr;
130 lldb::ValueObjectSP valobj_sp;
131 };
132
133 ExecutionContextRef m_exe_ctx_ref;
134 uint8_t m_ptr_size = 8;
135 lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
136 DataDescriptor_32 *m_data_32 = nullptr;
137 DataDescriptor_64 *m_data_64 = nullptr;
138 lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS;
139 CompilerType m_pair_type;
140 std::vector<DictionaryItemDescriptor> m_children;
141};
142
143class NSConstantDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
144public:
145 NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
146
147 llvm::Expected<uint32_t> CalculateNumChildren() override;
148
149 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
150
151 lldb::ChildCacheState Update() override;
152
153 bool MightHaveChildren() override;
154
155 size_t GetIndexOfChildWithName(ConstString name) override;
156
157private:
158 ExecutionContextRef m_exe_ctx_ref;
159 CompilerType m_pair_type;
160 uint8_t m_ptr_size = 8;
161 lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
162 unsigned int m_size = 0;
163 lldb::addr_t m_keys_ptr = LLDB_INVALID_ADDRESS;
164 lldb::addr_t m_objects_ptr = LLDB_INVALID_ADDRESS;
165
166 struct DictionaryItemDescriptor {
167 lldb::addr_t key_ptr;
168 lldb::addr_t val_ptr;
169 lldb::ValueObjectSP valobj_sp;
170 };
171
172 std::vector<DictionaryItemDescriptor> m_children;
173};
174
175class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
176public:
177 NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
178
179 llvm::Expected<uint32_t> CalculateNumChildren() override;
180
181 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
182
183 lldb::ChildCacheState Update() override;
184
185 bool MightHaveChildren() override;
186
187 size_t GetIndexOfChildWithName(ConstString name) override;
188
189private:
190 struct DictionaryItemDescriptor {
191 lldb::addr_t key_ptr;
192 lldb::addr_t val_ptr;
193 lldb::ValueObjectSP valobj_sp;
194 };
195
196 ExecutionContextRef m_exe_ctx_ref;
197 uint8_t m_ptr_size = 8;
198 lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
199
200 CFBasicHash m_hashtable;
201
202 CompilerType m_pair_type;
203 std::vector<DictionaryItemDescriptor> m_children;
204};
205
206class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
207public:
208 NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
209
210 ~NSDictionary1SyntheticFrontEnd() override = default;
211
212 llvm::Expected<uint32_t> CalculateNumChildren() override;
213
214 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
215
216 lldb::ChildCacheState Update() override;
217
218 bool MightHaveChildren() override;
219
220 size_t GetIndexOfChildWithName(ConstString name) override;
221
222private:
223 ValueObjectSP m_pair;
224};
225
226template <typename D32, typename D64>
227class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
228public:
229 GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
230
231 ~GenericNSDictionaryMSyntheticFrontEnd() override;
232
233 llvm::Expected<uint32_t> CalculateNumChildren() override;
234
235 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
236
237 lldb::ChildCacheState Update() override;
238
239 bool MightHaveChildren() override;
240
241 size_t GetIndexOfChildWithName(ConstString name) override;
242
243private:
244 struct DictionaryItemDescriptor {
245 lldb::addr_t key_ptr;
246 lldb::addr_t val_ptr;
247 lldb::ValueObjectSP valobj_sp;
248 };
249
250 ExecutionContextRef m_exe_ctx_ref;
251 uint8_t m_ptr_size = 8;
252 lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
253 D32 *m_data_32;
254 D64 *m_data_64;
255 CompilerType m_pair_type;
256 std::vector<DictionaryItemDescriptor> m_children;
257};
258
259namespace Foundation1100 {
260 class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
261 public:
262 NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
263
264 ~NSDictionaryMSyntheticFrontEnd() override;
265
266 llvm::Expected<uint32_t> CalculateNumChildren() override;
267
268 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
269
270 lldb::ChildCacheState Update() override;
271
272 bool MightHaveChildren() override;
273
274 size_t GetIndexOfChildWithName(ConstString name) override;
275
276 private:
277 struct DataDescriptor_32 {
278 uint32_t _used : 26;
279 uint32_t _kvo : 1;
280 uint32_t _size;
281 uint32_t _mutations;
282 uint32_t _objs_addr;
283 uint32_t _keys_addr;
284 };
285
286 struct DataDescriptor_64 {
287 uint64_t _used : 58;
288 uint32_t _kvo : 1;
289 uint64_t _size;
290 uint64_t _mutations;
291 uint64_t _objs_addr;
292 uint64_t _keys_addr;
293 };
294
295 struct DictionaryItemDescriptor {
296 lldb::addr_t key_ptr;
297 lldb::addr_t val_ptr;
298 lldb::ValueObjectSP valobj_sp;
299 };
300
301 ExecutionContextRef m_exe_ctx_ref;
302 uint8_t m_ptr_size = 8;
303 lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
304 DataDescriptor_32 *m_data_32 = nullptr;
305 DataDescriptor_64 *m_data_64 = nullptr;
306 CompilerType m_pair_type;
307 std::vector<DictionaryItemDescriptor> m_children;
308 };
309}
310
311namespace Foundation1428 {
312 namespace {
313 struct DataDescriptor_32 {
314 uint32_t _used : 26;
315 uint32_t _kvo : 1;
316 uint32_t _size;
317 uint32_t _buffer;
318 uint64_t GetSize() { return _size; }
319 };
320
321 struct DataDescriptor_64 {
322 uint64_t _used : 58;
323 uint32_t _kvo : 1;
324 uint64_t _size;
325 uint64_t _buffer;
326 uint64_t GetSize() { return _size; }
327 };
328 }
329
330 using NSDictionaryMSyntheticFrontEnd =
331 GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
332}
333
334namespace Foundation1437 {
335 static const uint64_t NSDictionaryCapacities[] = {
336 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
337 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
338 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
339 6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
340 111638519, 180634607, 292272623, 472907251
341 };
342
343 static const size_t NSDictionaryNumSizeBuckets =
344 sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
345
346 namespace {
347 struct DataDescriptor_32 {
348 uint32_t _buffer;
349 uint32_t _muts;
350 uint32_t _used : 25;
351 uint32_t _kvo : 1;
352 uint32_t _szidx : 6;
353
354 uint64_t GetSize() {
355 return (_szidx) >= NSDictionaryNumSizeBuckets ?
356 0 : NSDictionaryCapacities[_szidx];
357 }
358 };
359
360 struct DataDescriptor_64 {
361 uint64_t _buffer;
362 uint32_t _muts;
363 uint32_t _used : 25;
364 uint32_t _kvo : 1;
365 uint32_t _szidx : 6;
366
367 uint64_t GetSize() {
368 return (_szidx) >= NSDictionaryNumSizeBuckets ?
369 0 : NSDictionaryCapacities[_szidx];
370 }
371 };
372 } // namespace
373
374 using NSDictionaryMSyntheticFrontEnd =
375 GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
376
377 template <typename DD>
378 uint64_t
379 __NSDictionaryMSize_Impl(lldb_private::Process &process,
380 lldb::addr_t valobj_addr, Status &error) {
381 const lldb::addr_t start_of_descriptor =
382 valobj_addr + process.GetAddressByteSize();
383 DD descriptor = DD();
384 process.ReadMemory(vm_addr: start_of_descriptor, buf: &descriptor, size: sizeof(descriptor),
385 error);
386 if (error.Fail()) {
387 return 0;
388 }
389 return descriptor._used;
390 }
391
392 uint64_t
393 __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
394 Status &error) {
395 if (process.GetAddressByteSize() == 4) {
396 return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
397 error);
398 } else {
399 return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
400 error);
401 }
402 }
403
404}
405} // namespace formatters
406} // namespace lldb_private
407
408template <bool name_entries>
409bool lldb_private::formatters::NSDictionarySummaryProvider(
410 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
411 static constexpr llvm::StringLiteral g_TypeHint("NSDictionary");
412 ProcessSP process_sp = valobj.GetProcessSP();
413 if (!process_sp)
414 return false;
415
416 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(process&: *process_sp);
417
418 if (!runtime)
419 return false;
420
421 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
422 runtime->GetNonKVOClassDescriptor(in_value&: valobj));
423
424 if (!descriptor || !descriptor->IsValid())
425 return false;
426
427 uint32_t ptr_size = process_sp->GetAddressByteSize();
428 bool is_64bit = (ptr_size == 8);
429
430 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(fail_value: 0);
431
432 if (!valobj_addr)
433 return false;
434
435 uint64_t value = 0;
436
437 ConstString class_name(descriptor->GetClassName());
438
439 static const ConstString g_DictionaryI("__NSDictionaryI");
440 static const ConstString g_DictionaryM("__NSDictionaryM");
441 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
442 static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
443 static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
444 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
445 static const ConstString g_Dictionary0("__NSDictionary0");
446 static const ConstString g_DictionaryCF("__CFDictionary");
447 static const ConstString g_DictionaryNSCF("__NSCFDictionary");
448 static const ConstString g_DictionaryCFRef("CFDictionaryRef");
449 static const ConstString g_ConstantDictionary("NSConstantDictionary");
450
451 if (class_name.IsEmpty())
452 return false;
453
454 if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
455 Status error;
456 value = process_sp->ReadUnsignedIntegerFromMemory(load_addr: valobj_addr + ptr_size,
457 byte_size: ptr_size, fail_value: 0, error);
458 if (error.Fail())
459 return false;
460
461 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
462 } else if (class_name == g_ConstantDictionary) {
463 Status error;
464 value = process_sp->ReadUnsignedIntegerFromMemory(
465 load_addr: valobj_addr + 2 * ptr_size, byte_size: ptr_size, fail_value: 0, error);
466 if (error.Fail())
467 return false;
468 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
469 class_name == g_DictionaryMFrozen) {
470 AppleObjCRuntime *apple_runtime =
471 llvm::dyn_cast_or_null<AppleObjCRuntime>(Val: runtime);
472 Status error;
473 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
474 value = Foundation1437::__NSDictionaryMSize(process&: *process_sp, valobj_addr,
475 error);
476 } else {
477 value = process_sp->ReadUnsignedIntegerFromMemory(load_addr: valobj_addr + ptr_size,
478 byte_size: ptr_size, fail_value: 0, error);
479 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
480 }
481 if (error.Fail())
482 return false;
483 } else if (class_name == g_Dictionary1) {
484 value = 1;
485 } else if (class_name == g_Dictionary0) {
486 value = 0;
487 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
488 class_name == g_DictionaryCFRef) {
489 ExecutionContext exe_ctx(process_sp);
490 CFBasicHash cfbh;
491 if (!cfbh.Update(addr: valobj_addr, exe_ctx_rf: exe_ctx))
492 return false;
493 value = cfbh.GetCount();
494 } else {
495 auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
496 for (auto &candidate : map) {
497 if (candidate.first && candidate.first->Match(class_name))
498 return candidate.second(valobj, stream, options);
499 }
500 return false;
501 }
502
503 llvm::StringRef prefix, suffix;
504 if (Language *language = Language::FindPlugin(language: options.GetLanguage()))
505 std::tie(args&: prefix, args&: suffix) = language->GetFormatterPrefixSuffix(type_hint: g_TypeHint);
506
507 stream << prefix;
508 stream.Printf(format: "%" PRIu64 " %s%s", value, "key/value pair",
509 value == 1 ? "" : "s");
510 stream << suffix;
511 return true;
512}
513
514SyntheticChildrenFrontEnd *
515lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
516 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
517 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
518 if (!process_sp)
519 return nullptr;
520 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
521 Val: ObjCLanguageRuntime::Get(process&: *process_sp));
522 if (!runtime)
523 return nullptr;
524
525 CompilerType valobj_type(valobj_sp->GetCompilerType());
526 Flags flags(valobj_type.GetTypeInfo());
527
528 if (flags.IsClear(bit: eTypeIsPointer)) {
529 Status error;
530 valobj_sp = valobj_sp->AddressOf(error);
531 if (error.Fail() || !valobj_sp)
532 return nullptr;
533 }
534
535 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
536 runtime->GetClassDescriptor(in_value&: *valobj_sp));
537
538 if (!descriptor || !descriptor->IsValid())
539 return nullptr;
540
541 ConstString class_name(descriptor->GetClassName());
542
543 static const ConstString g_DictionaryI("__NSDictionaryI");
544 static const ConstString g_DictionaryM("__NSDictionaryM");
545 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
546 static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
547 static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
548 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
549 static const ConstString g_Dictionary0("__NSDictionary0");
550 static const ConstString g_DictionaryCF("__CFDictionary");
551 static const ConstString g_DictionaryNSCF("__NSCFDictionary");
552 static const ConstString g_DictionaryCFRef("CFDictionaryRef");
553 static const ConstString g_ConstantDictionary("NSConstantDictionary");
554
555 if (class_name.IsEmpty())
556 return nullptr;
557
558 if (class_name == g_DictionaryI) {
559 return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
560 } else if (class_name == g_ConstantDictionary) {
561 return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
562 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) {
563 if (runtime->GetFoundationVersion() >= 1437) {
564 return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
565 } else if (runtime->GetFoundationVersion() >= 1428) {
566 return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
567 } else {
568 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
569 }
570 } else if (class_name == g_DictionaryMLegacy) {
571 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
572 } else if (class_name == g_Dictionary1) {
573 return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
574 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
575 class_name == g_DictionaryCFRef) {
576 return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
577 } else {
578 auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
579 for (auto &candidate : map) {
580 if (candidate.first && candidate.first->Match((class_name)))
581 return candidate.second(synth, valobj_sp);
582 }
583 }
584
585 return nullptr;
586}
587
588lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
589 NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
590 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
591
592lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
593 ~NSDictionaryISyntheticFrontEnd() {
594 delete m_data_32;
595 m_data_32 = nullptr;
596 delete m_data_64;
597 m_data_64 = nullptr;
598}
599
600size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
601 GetIndexOfChildWithName(ConstString name) {
602 const char *item_name = name.GetCString();
603 uint32_t idx = ExtractIndexFromString(item_name);
604 if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
605 return UINT32_MAX;
606 return idx;
607}
608
609llvm::Expected<uint32_t> lldb_private::formatters::
610 NSDictionaryISyntheticFrontEnd::CalculateNumChildren() {
611 if (!m_data_32 && !m_data_64)
612 return 0;
613 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
614}
615
616lldb::ChildCacheState
617lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
618 m_children.clear();
619 delete m_data_32;
620 m_data_32 = nullptr;
621 delete m_data_64;
622 m_data_64 = nullptr;
623 m_ptr_size = 0;
624 ValueObjectSP valobj_sp = m_backend.GetSP();
625 if (!valobj_sp)
626 return lldb::ChildCacheState::eRefetch;
627 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
628 Status error;
629 error.Clear();
630 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
631 if (!process_sp)
632 return lldb::ChildCacheState::eRefetch;
633 m_ptr_size = process_sp->GetAddressByteSize();
634 m_order = process_sp->GetByteOrder();
635 uint64_t data_location = valobj_sp->GetValueAsUnsigned(fail_value: 0) + m_ptr_size;
636 if (m_ptr_size == 4) {
637 m_data_32 = new DataDescriptor_32();
638 process_sp->ReadMemory(vm_addr: data_location, buf: m_data_32, size: sizeof(DataDescriptor_32),
639 error);
640 } else {
641 m_data_64 = new DataDescriptor_64();
642 process_sp->ReadMemory(vm_addr: data_location, buf: m_data_64, size: sizeof(DataDescriptor_64),
643 error);
644 }
645 if (error.Fail())
646 return lldb::ChildCacheState::eRefetch;
647 m_data_ptr = data_location + m_ptr_size;
648 return lldb::ChildCacheState::eRefetch;
649}
650
651bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
652 MightHaveChildren() {
653 return true;
654}
655
656lldb::ValueObjectSP
657lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
658 uint32_t idx) {
659 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
660
661 if (idx >= num_children)
662 return lldb::ValueObjectSP();
663
664 if (m_children.empty()) {
665 // do the scan phase
666 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
667
668 uint32_t tries = 0;
669 uint32_t test_idx = 0;
670
671 while (tries < num_children) {
672 key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
673 val_at_idx = key_at_idx + m_ptr_size;
674 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
675 if (!process_sp)
676 return lldb::ValueObjectSP();
677 Status error;
678 key_at_idx = process_sp->ReadPointerFromMemory(vm_addr: key_at_idx, error);
679 if (error.Fail())
680 return lldb::ValueObjectSP();
681 val_at_idx = process_sp->ReadPointerFromMemory(vm_addr: val_at_idx, error);
682 if (error.Fail())
683 return lldb::ValueObjectSP();
684
685 test_idx++;
686
687 if (!key_at_idx || !val_at_idx)
688 continue;
689 tries++;
690
691 DictionaryItemDescriptor descriptor = {.key_ptr: key_at_idx, .val_ptr: val_at_idx,
692 .valobj_sp: lldb::ValueObjectSP()};
693
694 m_children.push_back(x: descriptor);
695 }
696 }
697
698 if (idx >= m_children.size()) // should never happen
699 return lldb::ValueObjectSP();
700
701 DictionaryItemDescriptor &dict_item = m_children[idx];
702 if (!dict_item.valobj_sp) {
703 if (!m_pair_type.IsValid()) {
704 TargetSP target_sp(m_backend.GetTargetSP());
705 if (!target_sp)
706 return ValueObjectSP();
707 m_pair_type = GetLLDBNSPairType(target_sp);
708 }
709 if (!m_pair_type.IsValid())
710 return ValueObjectSP();
711
712 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
713
714 if (m_ptr_size == 8) {
715 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
716 *data_ptr = dict_item.key_ptr;
717 *(data_ptr + 1) = dict_item.val_ptr;
718 } else {
719 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
720 *data_ptr = dict_item.key_ptr;
721 *(data_ptr + 1) = dict_item.val_ptr;
722 }
723
724 StreamString idx_name;
725 idx_name.Printf(format: "[%" PRIu64 "]", (uint64_t)idx);
726 DataExtractor data(buffer_sp, m_order, m_ptr_size);
727 dict_item.valobj_sp = CreateValueObjectFromData(name: idx_name.GetString(), data,
728 exe_ctx: m_exe_ctx_ref, type: m_pair_type);
729 }
730 return dict_item.valobj_sp;
731}
732
733lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
734 NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
735 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(),
736 m_pair_type() {}
737
738size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
739 GetIndexOfChildWithName(ConstString name) {
740 const char *item_name = name.GetCString();
741 const uint32_t idx = ExtractIndexFromString(item_name);
742 if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
743 return UINT32_MAX;
744 return idx;
745}
746
747llvm::Expected<uint32_t> lldb_private::formatters::
748 NSCFDictionarySyntheticFrontEnd::CalculateNumChildren() {
749 if (!m_hashtable.IsValid())
750 return 0;
751 return m_hashtable.GetCount();
752}
753
754lldb::ChildCacheState
755lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::Update() {
756 m_children.clear();
757 ValueObjectSP valobj_sp = m_backend.GetSP();
758 m_ptr_size = 0;
759 if (!valobj_sp)
760 return lldb::ChildCacheState::eRefetch;
761 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
762
763 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
764 if (!process_sp)
765 return lldb::ChildCacheState::eRefetch;
766 m_ptr_size = process_sp->GetAddressByteSize();
767 m_order = process_sp->GetByteOrder();
768 return m_hashtable.Update(addr: valobj_sp->GetValueAsUnsigned(fail_value: 0), exe_ctx_rf: m_exe_ctx_ref)
769 ? lldb::ChildCacheState::eReuse
770 : lldb::ChildCacheState::eRefetch;
771}
772
773bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
774 MightHaveChildren() {
775 return true;
776}
777
778lldb::ValueObjectSP
779lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex(
780 uint32_t idx) {
781 lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
782 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
783
784 const uint32_t num_children = CalculateNumChildrenIgnoringErrors();
785
786 if (idx >= num_children)
787 return lldb::ValueObjectSP();
788
789 if (m_children.empty()) {
790 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
791 if (!process_sp)
792 return lldb::ValueObjectSP();
793
794 Status error;
795 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
796
797 uint32_t tries = 0;
798 uint32_t test_idx = 0;
799
800 // Iterate over inferior memory, reading key/value pointers by shifting each
801 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
802 // fails, otherwise, continue until the number of tries matches the number
803 // of childen.
804 while (tries < num_children) {
805 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
806 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
807
808 key_at_idx = process_sp->ReadPointerFromMemory(vm_addr: key_at_idx, error);
809 if (error.Fail())
810 return lldb::ValueObjectSP();
811 val_at_idx = process_sp->ReadPointerFromMemory(vm_addr: val_at_idx, error);
812 if (error.Fail())
813 return lldb::ValueObjectSP();
814
815 test_idx++;
816
817 if (!key_at_idx || !val_at_idx)
818 continue;
819 tries++;
820
821 DictionaryItemDescriptor descriptor = {.key_ptr: key_at_idx, .val_ptr: val_at_idx,
822 .valobj_sp: lldb::ValueObjectSP()};
823
824 m_children.push_back(x: descriptor);
825 }
826 }
827
828 if (idx >= m_children.size()) // should never happen
829 return lldb::ValueObjectSP();
830
831 DictionaryItemDescriptor &dict_item = m_children[idx];
832 if (!dict_item.valobj_sp) {
833 if (!m_pair_type.IsValid()) {
834 TargetSP target_sp(m_backend.GetTargetSP());
835 if (!target_sp)
836 return ValueObjectSP();
837 m_pair_type = GetLLDBNSPairType(target_sp);
838 }
839 if (!m_pair_type.IsValid())
840 return ValueObjectSP();
841
842 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
843
844 switch (m_ptr_size) {
845 case 0: // architecture has no clue - fail
846 return lldb::ValueObjectSP();
847 case 4: {
848 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
849 *data_ptr = dict_item.key_ptr;
850 *(data_ptr + 1) = dict_item.val_ptr;
851 } break;
852 case 8: {
853 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
854 *data_ptr = dict_item.key_ptr;
855 *(data_ptr + 1) = dict_item.val_ptr;
856 } break;
857 default:
858 lldbassert(false && "pointer size is not 4 nor 8");
859 }
860
861 StreamString idx_name;
862 idx_name.Printf(format: "[%" PRIu64 "]", (uint64_t)idx);
863 DataExtractor data(buffer_sp, m_order, m_ptr_size);
864 dict_item.valobj_sp = CreateValueObjectFromData(name: idx_name.GetString(), data,
865 exe_ctx: m_exe_ctx_ref, type: m_pair_type);
866 }
867 return dict_item.valobj_sp;
868}
869
870lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
871 NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
872 : SyntheticChildrenFrontEnd(*valobj_sp) {}
873
874size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
875 GetIndexOfChildWithName(ConstString name) {
876 const char *item_name = name.GetCString();
877 uint32_t idx = ExtractIndexFromString(item_name);
878 if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
879 return UINT32_MAX;
880 return idx;
881}
882
883llvm::Expected<uint32_t> lldb_private::formatters::
884 NSConstantDictionarySyntheticFrontEnd::CalculateNumChildren() {
885 return m_size;
886}
887
888lldb::ChildCacheState
889lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::Update() {
890 ValueObjectSP valobj_sp = m_backend.GetSP();
891 if (!valobj_sp)
892 return lldb::ChildCacheState::eRefetch;
893 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
894 Status error;
895 error.Clear();
896 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
897 if (!process_sp)
898 return lldb::ChildCacheState::eRefetch;
899 m_ptr_size = process_sp->GetAddressByteSize();
900 m_order = process_sp->GetByteOrder();
901 uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(fail_value: 0);
902 m_size = process_sp->ReadUnsignedIntegerFromMemory(
903 load_addr: valobj_addr + 2 * m_ptr_size, byte_size: m_ptr_size, fail_value: 0, error);
904 if (error.Fail())
905 return lldb::ChildCacheState::eRefetch;
906 m_keys_ptr =
907 process_sp->ReadPointerFromMemory(vm_addr: valobj_addr + 3 * m_ptr_size, error);
908 if (error.Fail())
909 return lldb::ChildCacheState::eRefetch;
910 m_objects_ptr =
911 process_sp->ReadPointerFromMemory(vm_addr: valobj_addr + 4 * m_ptr_size, error);
912
913 return error.Success() ? lldb::ChildCacheState::eReuse
914 : lldb::ChildCacheState::eRefetch;
915}
916
917bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
918 MightHaveChildren() {
919 return true;
920}
921
922lldb::ValueObjectSP lldb_private::formatters::
923 NSConstantDictionarySyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
924 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
925
926 if (idx >= num_children)
927 return lldb::ValueObjectSP();
928
929 if (m_children.empty()) {
930 // do the scan phase
931 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
932 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
933 if (!process_sp)
934 return lldb::ValueObjectSP();
935
936 for (unsigned int child = 0; child < num_children; ++child) {
937 Status error;
938 key_at_idx = process_sp->ReadPointerFromMemory(
939 vm_addr: m_keys_ptr + child * m_ptr_size, error);
940 if (error.Fail())
941 return lldb::ValueObjectSP();
942 val_at_idx = process_sp->ReadPointerFromMemory(
943 vm_addr: m_objects_ptr + child * m_ptr_size, error);
944 if (error.Fail())
945 return lldb::ValueObjectSP();
946 DictionaryItemDescriptor descriptor = {.key_ptr: key_at_idx, .val_ptr: val_at_idx,
947 .valobj_sp: lldb::ValueObjectSP()};
948 m_children.push_back(x: descriptor);
949 }
950 }
951
952 if (idx >= m_children.size()) // should never happen
953 return lldb::ValueObjectSP();
954
955 DictionaryItemDescriptor &dict_item = m_children[idx];
956 if (!dict_item.valobj_sp) {
957 if (!m_pair_type.IsValid()) {
958 TargetSP target_sp(m_backend.GetTargetSP());
959 if (!target_sp)
960 return ValueObjectSP();
961 m_pair_type = GetLLDBNSPairType(target_sp);
962 }
963 if (!m_pair_type.IsValid())
964 return ValueObjectSP();
965
966 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
967
968 if (m_ptr_size == 8) {
969 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
970 *data_ptr = dict_item.key_ptr;
971 *(data_ptr + 1) = dict_item.val_ptr;
972 } else {
973 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
974 *data_ptr = dict_item.key_ptr;
975 *(data_ptr + 1) = dict_item.val_ptr;
976 }
977
978 StreamString idx_name;
979 idx_name.Printf(format: "[%" PRIu64 "]", (uint64_t)idx);
980 DataExtractor data(buffer_sp, m_order, m_ptr_size);
981 dict_item.valobj_sp = CreateValueObjectFromData(name: idx_name.GetString(), data,
982 exe_ctx: m_exe_ctx_ref, type: m_pair_type);
983 }
984 return dict_item.valobj_sp;
985}
986
987lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
988 NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
989 : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
990
991size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
992 GetIndexOfChildWithName(ConstString name) {
993 static const ConstString g_zero("[0]");
994 return name == g_zero ? 0 : UINT32_MAX;
995}
996
997llvm::Expected<uint32_t> lldb_private::formatters::
998 NSDictionary1SyntheticFrontEnd::CalculateNumChildren() {
999 return 1;
1000}
1001
1002lldb::ChildCacheState
1003lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
1004 m_pair.reset();
1005 return lldb::ChildCacheState::eRefetch;
1006}
1007
1008bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
1009 MightHaveChildren() {
1010 return true;
1011}
1012
1013lldb::ValueObjectSP
1014lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
1015 uint32_t idx) {
1016 if (idx != 0)
1017 return lldb::ValueObjectSP();
1018
1019 if (m_pair.get())
1020 return m_pair;
1021
1022 auto process_sp(m_backend.GetProcessSP());
1023 if (!process_sp)
1024 return nullptr;
1025
1026 auto ptr_size = process_sp->GetAddressByteSize();
1027
1028 lldb::addr_t key_ptr =
1029 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
1030 lldb::addr_t value_ptr = key_ptr + ptr_size;
1031
1032 Status error;
1033
1034 lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(vm_addr: key_ptr, error);
1035 if (error.Fail())
1036 return nullptr;
1037 lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(vm_addr: value_ptr, error);
1038 if (error.Fail())
1039 return nullptr;
1040
1041 auto pair_type =
1042 GetLLDBNSPairType(target_sp: process_sp->GetTarget().shared_from_this());
1043
1044 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1045
1046 if (ptr_size == 8) {
1047 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1048 *data_ptr = key_at_idx;
1049 *(data_ptr + 1) = value_at_idx;
1050 } else {
1051 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1052 *data_ptr = key_at_idx;
1053 *(data_ptr + 1) = value_at_idx;
1054 }
1055
1056 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1057 m_pair = CreateValueObjectFromData(
1058 name: "[0]", data, exe_ctx: m_backend.GetExecutionContextRef(), type: pair_type);
1059
1060 return m_pair;
1061}
1062
1063template <typename D32, typename D64>
1064lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32, D64>::
1065 GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1066 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
1067 m_data_32(nullptr), m_data_64(nullptr), m_pair_type() {}
1068
1069template <typename D32, typename D64>
1070lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
1071 D32, D64>::GenericNSDictionaryMSyntheticFrontEnd::
1072 ~GenericNSDictionaryMSyntheticFrontEnd() {
1073 delete m_data_32;
1074 m_data_32 = nullptr;
1075 delete m_data_64;
1076 m_data_64 = nullptr;
1077}
1078
1079template <typename D32, typename D64>
1080size_t lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
1081 D32, D64>::GetIndexOfChildWithName(ConstString name) {
1082 const char *item_name = name.GetCString();
1083 uint32_t idx = ExtractIndexFromString(item_name);
1084 if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
1085 return UINT32_MAX;
1086 return idx;
1087}
1088
1089template <typename D32, typename D64>
1090llvm::Expected<uint32_t>
1091lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
1092 D32, D64>::CalculateNumChildren() {
1093 if (!m_data_32 && !m_data_64)
1094 return 0;
1095 return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1096}
1097
1098template <typename D32, typename D64>
1099lldb::ChildCacheState
1100lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,
1101 D64>::Update() {
1102 m_children.clear();
1103 ValueObjectSP valobj_sp = m_backend.GetSP();
1104 m_ptr_size = 0;
1105 delete m_data_32;
1106 m_data_32 = nullptr;
1107 delete m_data_64;
1108 m_data_64 = nullptr;
1109 if (!valobj_sp)
1110 return lldb::ChildCacheState::eRefetch;
1111 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1112 Status error;
1113 error.Clear();
1114 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1115 if (!process_sp)
1116 return lldb::ChildCacheState::eRefetch;
1117 m_ptr_size = process_sp->GetAddressByteSize();
1118 m_order = process_sp->GetByteOrder();
1119 uint64_t data_location = valobj_sp->GetValueAsUnsigned(fail_value: 0) + m_ptr_size;
1120 if (m_ptr_size == 4) {
1121 m_data_32 = new D32();
1122 process_sp->ReadMemory(vm_addr: data_location, buf: m_data_32, size: sizeof(D32),
1123 error);
1124 } else {
1125 m_data_64 = new D64();
1126 process_sp->ReadMemory(vm_addr: data_location, buf: m_data_64, size: sizeof(D64),
1127 error);
1128 }
1129
1130 return error.Success() ? lldb::ChildCacheState::eReuse
1131 : lldb::ChildCacheState::eRefetch;
1132}
1133
1134template <typename D32, typename D64>
1135bool
1136lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
1137 MightHaveChildren() {
1138 return true;
1139}
1140
1141template <typename D32, typename D64>
1142lldb::ValueObjectSP
1143lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<
1144 D32, D64>::GetChildAtIndex(uint32_t idx) {
1145 lldb::addr_t m_keys_ptr;
1146 lldb::addr_t m_values_ptr;
1147 if (m_data_32) {
1148 uint32_t size = m_data_32->GetSize();
1149 m_keys_ptr = m_data_32->_buffer;
1150 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1151 } else {
1152 uint32_t size = m_data_64->GetSize();
1153 m_keys_ptr = m_data_64->_buffer;
1154 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1155 }
1156
1157 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1158
1159 if (idx >= num_children)
1160 return lldb::ValueObjectSP();
1161
1162 if (m_children.empty()) {
1163 // do the scan phase
1164 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1165
1166 uint32_t tries = 0;
1167 uint32_t test_idx = 0;
1168
1169 while (tries < num_children) {
1170 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1171 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1172 ;
1173 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1174 if (!process_sp)
1175 return lldb::ValueObjectSP();
1176 Status error;
1177 key_at_idx = process_sp->ReadPointerFromMemory(vm_addr: key_at_idx, error);
1178 if (error.Fail())
1179 return lldb::ValueObjectSP();
1180 val_at_idx = process_sp->ReadPointerFromMemory(vm_addr: val_at_idx, error);
1181 if (error.Fail())
1182 return lldb::ValueObjectSP();
1183
1184 test_idx++;
1185
1186 if (!key_at_idx || !val_at_idx)
1187 continue;
1188 tries++;
1189
1190 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1191 lldb::ValueObjectSP()};
1192
1193 m_children.push_back(descriptor);
1194 }
1195 }
1196
1197 if (idx >= m_children.size()) // should never happen
1198 return lldb::ValueObjectSP();
1199
1200 DictionaryItemDescriptor &dict_item = m_children[idx];
1201 if (!dict_item.valobj_sp) {
1202 if (!m_pair_type.IsValid()) {
1203 TargetSP target_sp(m_backend.GetTargetSP());
1204 if (!target_sp)
1205 return ValueObjectSP();
1206 m_pair_type = GetLLDBNSPairType(target_sp);
1207 }
1208 if (!m_pair_type.IsValid())
1209 return ValueObjectSP();
1210
1211 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1212
1213 if (m_ptr_size == 8) {
1214 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1215 *data_ptr = dict_item.key_ptr;
1216 *(data_ptr + 1) = dict_item.val_ptr;
1217 } else {
1218 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1219 *data_ptr = dict_item.key_ptr;
1220 *(data_ptr + 1) = dict_item.val_ptr;
1221 }
1222
1223 StreamString idx_name;
1224 idx_name.Printf(format: "[%" PRIu64 "]", (uint64_t)idx);
1225 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1226 dict_item.valobj_sp = CreateValueObjectFromData(name: idx_name.GetString(), data,
1227 exe_ctx: m_exe_ctx_ref, type: m_pair_type);
1228 }
1229 return dict_item.valobj_sp;
1230}
1231
1232lldb_private::formatters::Foundation1100::NSDictionaryMSyntheticFrontEnd::
1233 NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1234 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
1235
1236lldb_private::formatters::Foundation1100::
1237 NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
1238 delete m_data_32;
1239 m_data_32 = nullptr;
1240 delete m_data_64;
1241 m_data_64 = nullptr;
1242}
1243
1244size_t
1245lldb_private::formatters::Foundation1100::
1246 NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
1247 const char *item_name = name.GetCString();
1248 uint32_t idx = ExtractIndexFromString(item_name);
1249 if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
1250 return UINT32_MAX;
1251 return idx;
1252}
1253
1254llvm::Expected<uint32_t> lldb_private::formatters::Foundation1100::
1255 NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
1256 if (!m_data_32 && !m_data_64)
1257 return 0;
1258 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1259}
1260
1261lldb::ChildCacheState lldb_private::formatters::Foundation1100::
1262 NSDictionaryMSyntheticFrontEnd::Update() {
1263 m_children.clear();
1264 ValueObjectSP valobj_sp = m_backend.GetSP();
1265 m_ptr_size = 0;
1266 delete m_data_32;
1267 m_data_32 = nullptr;
1268 delete m_data_64;
1269 m_data_64 = nullptr;
1270 if (!valobj_sp)
1271 return lldb::ChildCacheState::eRefetch;
1272 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1273 Status error;
1274 error.Clear();
1275 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1276 if (!process_sp)
1277 return lldb::ChildCacheState::eRefetch;
1278 m_ptr_size = process_sp->GetAddressByteSize();
1279 m_order = process_sp->GetByteOrder();
1280 uint64_t data_location = valobj_sp->GetValueAsUnsigned(fail_value: 0) + m_ptr_size;
1281 if (m_ptr_size == 4) {
1282 m_data_32 = new DataDescriptor_32();
1283 process_sp->ReadMemory(vm_addr: data_location, buf: m_data_32, size: sizeof(DataDescriptor_32),
1284 error);
1285 } else {
1286 m_data_64 = new DataDescriptor_64();
1287 process_sp->ReadMemory(vm_addr: data_location, buf: m_data_64, size: sizeof(DataDescriptor_64),
1288 error);
1289 }
1290
1291 return error.Success() ? lldb::ChildCacheState::eReuse
1292 : lldb::ChildCacheState::eRefetch;
1293}
1294
1295bool
1296lldb_private::formatters::Foundation1100::
1297 NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
1298 return true;
1299}
1300
1301lldb::ValueObjectSP
1302lldb_private::formatters::Foundation1100::
1303 NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
1304 lldb::addr_t m_keys_ptr =
1305 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1306 lldb::addr_t m_values_ptr =
1307 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1308
1309 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1310
1311 if (idx >= num_children)
1312 return lldb::ValueObjectSP();
1313
1314 if (m_children.empty()) {
1315 // do the scan phase
1316 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1317
1318 uint32_t tries = 0;
1319 uint32_t test_idx = 0;
1320
1321 while (tries < num_children) {
1322 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1323 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1324 ;
1325 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1326 if (!process_sp)
1327 return lldb::ValueObjectSP();
1328 Status error;
1329 key_at_idx = process_sp->ReadPointerFromMemory(vm_addr: key_at_idx, error);
1330 if (error.Fail())
1331 return lldb::ValueObjectSP();
1332 val_at_idx = process_sp->ReadPointerFromMemory(vm_addr: val_at_idx, error);
1333 if (error.Fail())
1334 return lldb::ValueObjectSP();
1335
1336 test_idx++;
1337
1338 if (!key_at_idx || !val_at_idx)
1339 continue;
1340 tries++;
1341
1342 DictionaryItemDescriptor descriptor = {.key_ptr: key_at_idx, .val_ptr: val_at_idx,
1343 .valobj_sp: lldb::ValueObjectSP()};
1344
1345 m_children.push_back(x: descriptor);
1346 }
1347 }
1348
1349 if (idx >= m_children.size()) // should never happen
1350 return lldb::ValueObjectSP();
1351
1352 DictionaryItemDescriptor &dict_item = m_children[idx];
1353 if (!dict_item.valobj_sp) {
1354 if (!m_pair_type.IsValid()) {
1355 TargetSP target_sp(m_backend.GetTargetSP());
1356 if (!target_sp)
1357 return ValueObjectSP();
1358 m_pair_type = GetLLDBNSPairType(target_sp);
1359 }
1360 if (!m_pair_type.IsValid())
1361 return ValueObjectSP();
1362
1363 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1364
1365 if (m_ptr_size == 8) {
1366 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1367 *data_ptr = dict_item.key_ptr;
1368 *(data_ptr + 1) = dict_item.val_ptr;
1369 } else {
1370 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1371 *data_ptr = dict_item.key_ptr;
1372 *(data_ptr + 1) = dict_item.val_ptr;
1373 }
1374
1375 StreamString idx_name;
1376 idx_name.Printf(format: "[%" PRIu64 "]", (uint64_t)idx);
1377 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1378 dict_item.valobj_sp = CreateValueObjectFromData(name: idx_name.GetString(), data,
1379 exe_ctx: m_exe_ctx_ref, type: m_pair_type);
1380 }
1381 return dict_item.valobj_sp;
1382}
1383
1384template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1385 ValueObject &, Stream &, const TypeSummaryOptions &);
1386
1387template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1388 ValueObject &, Stream &, const TypeSummaryOptions &);
1389

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