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