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 | |
30 | using namespace lldb; |
31 | using namespace lldb_private; |
32 | using namespace lldb_private::formatters; |
33 | |
34 | NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix( |
35 | ConstString p) |
36 | : m_prefix(p) {} |
37 | |
38 | bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match( |
39 | ConstString class_name) { |
40 | return class_name.GetStringRef().starts_with(Prefix: m_prefix.GetStringRef()); |
41 | } |
42 | |
43 | NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n) |
44 | : m_name(n) {} |
45 | |
46 | bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match( |
47 | ConstString class_name) { |
48 | return (class_name == m_name); |
49 | } |
50 | |
51 | NSDictionary_Additionals::AdditionalFormatters< |
52 | CXXFunctionSummaryFormat::Callback> & |
53 | NSDictionary_Additionals::GetAdditionalSummaries() { |
54 | static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map; |
55 | return g_map; |
56 | } |
57 | |
58 | NSDictionary_Additionals::AdditionalFormatters< |
59 | CXXSyntheticChildren::CreateFrontEndCallback> & |
60 | NSDictionary_Additionals::GetAdditionalSynthetics() { |
61 | static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback> |
62 | g_map; |
63 | return g_map; |
64 | } |
65 | |
66 | static 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 | |
98 | namespace lldb_private { |
99 | namespace formatters { |
100 | class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
101 | public: |
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 | |
114 | private: |
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 | |
141 | class NSConstantDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
142 | public: |
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 | |
153 | private: |
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 | |
171 | class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
172 | public: |
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 | |
183 | private: |
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 | |
200 | class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
201 | public: |
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 | |
214 | private: |
215 | ValueObjectSP m_pair; |
216 | }; |
217 | |
218 | template <typename D32, typename D64> |
219 | class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
220 | public: |
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 | |
233 | private: |
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 | |
249 | namespace 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 | |
299 | namespace 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 | |
322 | namespace 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 | |
396 | template <bool name_entries> |
397 | bool 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 | |
502 | SyntheticChildrenFrontEnd * |
503 | lldb_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 | |
576 | lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: |
577 | NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
578 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {} |
579 | |
580 | lldb_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 | |
588 | llvm::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 | |
602 | llvm::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 | |
609 | lldb::ChildCacheState |
610 | lldb_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 | |
644 | lldb::ValueObjectSP |
645 | lldb_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 | |
721 | lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: |
722 | NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
723 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(), |
724 | m_pair_type() {} |
725 | |
726 | llvm::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 | |
740 | llvm::Expected<uint32_t> lldb_private::formatters:: |
741 | NSCFDictionarySyntheticFrontEnd::CalculateNumChildren() { |
742 | if (!m_hashtable.IsValid()) |
743 | return 0; |
744 | return m_hashtable.GetCount(); |
745 | } |
746 | |
747 | lldb::ChildCacheState |
748 | lldb_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 | |
766 | lldb::ValueObjectSP |
767 | lldb_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 | |
858 | lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: |
859 | NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
860 | : SyntheticChildrenFrontEnd(*valobj_sp) {} |
861 | |
862 | llvm::Expected<size_t> |
863 | lldb_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 | |
877 | llvm::Expected<uint32_t> lldb_private::formatters:: |
878 | NSConstantDictionarySyntheticFrontEnd::CalculateNumChildren() { |
879 | return m_size; |
880 | } |
881 | |
882 | lldb::ChildCacheState |
883 | lldb_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 | |
911 | lldb::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 | |
976 | lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: |
977 | NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
978 | : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {} |
979 | |
980 | llvm::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 | |
989 | llvm::Expected<uint32_t> lldb_private::formatters:: |
990 | NSDictionary1SyntheticFrontEnd::CalculateNumChildren() { |
991 | return 1; |
992 | } |
993 | |
994 | lldb::ChildCacheState |
995 | lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() { |
996 | m_pair.reset(); |
997 | return lldb::ChildCacheState::eRefetch; |
998 | } |
999 | |
1000 | lldb::ValueObjectSP |
1001 | lldb_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 | |
1050 | template <typename D32, typename D64> |
1051 | lldb_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 | |
1056 | template <typename D32, typename D64> |
1057 | lldb_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 | |
1066 | template <typename D32, typename D64> |
1067 | llvm::Expected<size_t> |
1068 | lldb_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 | |
1082 | template <typename D32, typename D64> |
1083 | llvm::Expected<uint32_t> |
1084 | lldb_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 | |
1091 | template <typename D32, typename D64> |
1092 | lldb::ChildCacheState |
1093 | lldb_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 | |
1127 | template <typename D32, typename D64> |
1128 | lldb::ValueObjectSP |
1129 | lldb_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 | |
1218 | lldb_private::formatters::Foundation1100::NSDictionaryMSyntheticFrontEnd:: |
1219 | NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
1220 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {} |
1221 | |
1222 | lldb_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 | |
1230 | llvm::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 | |
1244 | llvm::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 | |
1251 | lldb::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 | |
1285 | lldb::ValueObjectSP |
1286 | lldb_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 | |
1368 | template bool lldb_private::formatters::NSDictionarySummaryProvider<true>( |
1369 | ValueObject &, Stream &, const TypeSummaryOptions &); |
1370 | |
1371 | template bool lldb_private::formatters::NSDictionarySummaryProvider<false>( |
1372 | ValueObject &, Stream &, const TypeSummaryOptions &); |
1373 |
Definitions
- Prefix
- Match
- Full
- Match
- GetAdditionalSummaries
- GetAdditionalSynthetics
- GetLLDBNSPairType
- NSDictionaryISyntheticFrontEnd
- DataDescriptor_32
- DataDescriptor_64
- DictionaryItemDescriptor
- NSConstantDictionarySyntheticFrontEnd
- DictionaryItemDescriptor
- NSCFDictionarySyntheticFrontEnd
- DictionaryItemDescriptor
- NSDictionary1SyntheticFrontEnd
- ~NSDictionary1SyntheticFrontEnd
- GenericNSDictionaryMSyntheticFrontEnd
- DictionaryItemDescriptor
- NSDictionaryMSyntheticFrontEnd
- DataDescriptor_32
- DataDescriptor_64
- DictionaryItemDescriptor
- DataDescriptor_32
- GetSize
- DataDescriptor_64
- GetSize
- NSDictionaryCapacities
- NSDictionaryNumSizeBuckets
- DataDescriptor_32
- GetSize
- DataDescriptor_64
- GetSize
- __NSDictionaryMSize_Impl
- __NSDictionaryMSize
- NSDictionarySummaryProvider
- NSDictionarySyntheticFrontEndCreator
- NSDictionaryISyntheticFrontEnd
- ~NSDictionaryISyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
- NSCFDictionarySyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
- NSConstantDictionarySyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
- NSDictionary1SyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
- GenericNSDictionaryMSyntheticFrontEnd
- ~GenericNSDictionaryMSyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
- NSDictionaryMSyntheticFrontEnd
- ~NSDictionaryMSyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
Learn to use CMake with our Intro Training
Find out more