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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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