LLDB mainline
NSDictionary.cpp
Go to the documentation of this file.
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
18
24#include "lldb/Target/Target.h"
26#include "lldb/Utility/Endian.h"
27#include "lldb/Utility/Status.h"
28#include "lldb/Utility/Stream.h"
29
30using namespace lldb;
31using namespace lldb_private;
32using namespace lldb_private::formatters;
33
36 : m_prefix(p) {}
37
39 ConstString class_name) {
40 return class_name.GetStringRef().startswith(m_prefix.GetStringRef());
41}
42
44 : m_name(n) {}
45
47 ConstString class_name) {
48 return (class_name == m_name);
49}
50
55 return g_map;
56}
57
62 g_map;
63 return g_map;
64}
65
67 CompilerType compiler_type;
68 TypeSystemClangSP scratch_ts_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>(g_lldb_autogen_nspair);
77
78 if (!compiler_type) {
79 compiler_type = scratch_ts_sp->CreateRecordType(
81 g_lldb_autogen_nspair, llvm::to_underlying(clang::TagTypeKind::Struct),
83
84 if (compiler_type) {
86 CompilerType id_compiler_type =
87 scratch_ts_sp->GetBasicType(eBasicTypeObjCID);
89 compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
91 compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
93 }
94 }
95 return compiler_type;
96}
97
98namespace lldb_private {
99namespace formatters {
101public:
103
105
106 size_t CalculateNumChildren() override;
107
108 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
109
110 bool Update() override;
111
112 bool MightHaveChildren() override;
113
114 size_t GetIndexOfChildWithName(ConstString name) override;
115
116private:
118 uint32_t _used : 26;
119 uint32_t _szidx : 6;
120 };
121
123 uint64_t _used : 58;
124 uint32_t _szidx : 6;
125 };
126
131 };
132
134 uint8_t m_ptr_size = 8;
136 DataDescriptor_32 *m_data_32 = nullptr;
137 DataDescriptor_64 *m_data_64 = nullptr;
140 std::vector<DictionaryItemDescriptor> m_children;
141};
142
144public:
146
147 size_t CalculateNumChildren() override;
148
149 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
150
151 bool Update() override;
152
153 bool MightHaveChildren() override;
154
155 size_t GetIndexOfChildWithName(ConstString name) override;
156
157private:
160 uint8_t m_ptr_size = 8;
162 unsigned int m_size = 0;
165
170 };
171
172 std::vector<DictionaryItemDescriptor> m_children;
173};
174
176public:
178
179 size_t CalculateNumChildren() override;
180
181 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
182
183 bool Update() override;
184
185 bool MightHaveChildren() override;
186
187 size_t GetIndexOfChildWithName(ConstString name) override;
188
189private:
194 };
195
197 uint8_t m_ptr_size = 8;
199
201
203 std::vector<DictionaryItemDescriptor> m_children;
204};
205
207public:
209
211
212 size_t CalculateNumChildren() override;
213
214 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
215
216 bool Update() override;
217
218 bool MightHaveChildren() override;
219
220 size_t GetIndexOfChildWithName(ConstString name) override;
221
222private:
224};
225
226template <typename D32, typename D64>
228public:
230
232
233 size_t CalculateNumChildren() override;
234
235 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
236
237 bool Update() override;
238
239 bool MightHaveChildren() override;
240
241 size_t GetIndexOfChildWithName(ConstString name) override;
242
243private:
248 };
249
251 uint8_t m_ptr_size = 8;
256 std::vector<DictionaryItemDescriptor> m_children;
257};
258
259namespace Foundation1100 {
261 public:
263
265
266 size_t CalculateNumChildren() override;
267
268 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
269
270 bool Update() override;
271
272 bool MightHaveChildren() override;
273
274 size_t GetIndexOfChildWithName(ConstString name) override;
275
276 private:
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
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
299 };
300
302 uint8_t m_ptr_size = 8;
304 DataDescriptor_32 *m_data_32 = nullptr;
305 DataDescriptor_64 *m_data_64 = nullptr;
307 std::vector<DictionaryItemDescriptor> m_children;
308 };
309}
310
311namespace Foundation1428 {
312 namespace {
313 struct DataDescriptor_32 {
314 uint32_t _used : 26;
315 uint32_t _kvo : 1;
316 uint32_t _size;
317 uint32_t _buffer;
318 uint64_t GetSize() { return _size; }
319 };
320
321 struct DataDescriptor_64 {
322 uint64_t _used : 58;
323 uint32_t _kvo : 1;
324 uint64_t _size;
325 uint64_t _buffer;
326 uint64_t GetSize() { return _size; }
327 };
328 }
329
332}
333
334namespace Foundation1437 {
335 static const uint64_t NSDictionaryCapacities[] = {
336 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
337 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
338 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
339 6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
340 111638519, 180634607, 292272623, 472907251
341 };
342
343 static const size_t NSDictionaryNumSizeBuckets =
344 sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
345
346 namespace {
347 struct DataDescriptor_32 {
348 uint32_t _buffer;
349 uint32_t _muts;
350 uint32_t _used : 25;
351 uint32_t _kvo : 1;
352 uint32_t _szidx : 6;
353
354 uint64_t GetSize() {
355 return (_szidx) >= NSDictionaryNumSizeBuckets ?
356 0 : NSDictionaryCapacities[_szidx];
357 }
358 };
359
360 struct DataDescriptor_64 {
361 uint64_t _buffer;
362 uint32_t _muts;
363 uint32_t _used : 25;
364 uint32_t _kvo : 1;
365 uint32_t _szidx : 6;
366
367 uint64_t GetSize() {
370 }
371 };
372 } // namespace
373
376
377 template <typename DD>
378 uint64_t
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(start_of_descriptor, &descriptor, sizeof(descriptor),
385 error);
386 if (error.Fail()) {
387 return 0;
388 }
389 return descriptor._used;
390 }
391
392 uint64_t
394 Status &error) {
395 if (process.GetAddressByteSize() == 4) {
396 return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
397 error);
398 } else {
399 return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
400 error);
401 }
402 }
403
404}
405} // namespace formatters
406} // namespace lldb_private
407
408template <bool name_entries>
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_sp);
417
418 if (!runtime)
419 return false;
420
422 runtime->GetNonKVOClassDescriptor(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(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) {
456 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
457 ptr_size, 0, error);
458 if (error.Fail())
459 return false;
460
461 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
462 } else if (class_name == g_ConstantDictionary) {
464 value = process_sp->ReadUnsignedIntegerFromMemory(
465 valobj_addr + 2 * ptr_size, ptr_size, 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>(runtime);
473 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
474 value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
475 error);
476 } else {
477 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
478 ptr_size, 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(valobj_addr, exe_ctx))
492 return false;
493 value = cfbh.GetCount();
494 } else {
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(options.GetLanguage()))
505 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
506
507 stream << prefix;
508 stream.Printf("%" PRIu64 " %s%s", value, "key/value pair",
509 value == 1 ? "" : "s");
510 stream << suffix;
511 return true;
512}
513
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 ObjCLanguageRuntime::Get(*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(eTypeIsPointer)) {
530 valobj_sp = valobj_sp->AddressOf(error);
531 if (error.Fail() || !valobj_sp)
532 return nullptr;
533 }
534
536 runtime->GetClassDescriptor(*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) {
565 } else if (runtime->GetFoundationVersion() >= 1428) {
567 } else {
569 }
570 } else if (class_name == g_DictionaryMLegacy) {
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 {
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
590 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
591
594 delete m_data_32;
595 m_data_32 = nullptr;
596 delete m_data_64;
597 m_data_64 = nullptr;
598}
599
602 const char *item_name = name.GetCString();
603 uint32_t idx = ExtractIndexFromString(item_name);
604 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
605 return UINT32_MAX;
606 return idx;
607}
608
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
617 m_children.clear();
618 delete m_data_32;
619 m_data_32 = nullptr;
620 delete m_data_64;
621 m_data_64 = nullptr;
622 m_ptr_size = 0;
623 ValueObjectSP valobj_sp = m_backend.GetSP();
624 if (!valobj_sp)
625 return false;
626 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
628 error.Clear();
629 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
630 if (!process_sp)
631 return false;
632 m_ptr_size = process_sp->GetAddressByteSize();
633 m_order = process_sp->GetByteOrder();
634 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
635 if (m_ptr_size == 4) {
636 m_data_32 = new DataDescriptor_32();
637 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
638 error);
639 } else {
640 m_data_64 = new DataDescriptor_64();
641 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
642 error);
643 }
644 if (error.Fail())
645 return false;
646 m_data_ptr = data_location + m_ptr_size;
647 return false;
648}
649
652 return true;
653}
654
657 size_t idx) {
658 uint32_t num_children = CalculateNumChildren();
659
660 if (idx >= num_children)
661 return lldb::ValueObjectSP();
662
663 if (m_children.empty()) {
664 // do the scan phase
665 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
666
667 uint32_t tries = 0;
668 uint32_t test_idx = 0;
669
670 while (tries < num_children) {
671 key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
672 val_at_idx = key_at_idx + m_ptr_size;
673 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
674 if (!process_sp)
675 return lldb::ValueObjectSP();
677 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
678 if (error.Fail())
679 return lldb::ValueObjectSP();
680 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
681 if (error.Fail())
682 return lldb::ValueObjectSP();
683
684 test_idx++;
685
686 if (!key_at_idx || !val_at_idx)
687 continue;
688 tries++;
689
690 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
692
693 m_children.push_back(descriptor);
694 }
695 }
696
697 if (idx >= m_children.size()) // should never happen
698 return lldb::ValueObjectSP();
699
700 DictionaryItemDescriptor &dict_item = m_children[idx];
701 if (!dict_item.valobj_sp) {
702 if (!m_pair_type.IsValid()) {
703 TargetSP target_sp(m_backend.GetTargetSP());
704 if (!target_sp)
705 return ValueObjectSP();
706 m_pair_type = GetLLDBNSPairType(target_sp);
707 }
708 if (!m_pair_type.IsValid())
709 return ValueObjectSP();
710
711 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
712
713 if (m_ptr_size == 8) {
714 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
715 *data_ptr = dict_item.key_ptr;
716 *(data_ptr + 1) = dict_item.val_ptr;
717 } else {
718 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
719 *data_ptr = dict_item.key_ptr;
720 *(data_ptr + 1) = dict_item.val_ptr;
721 }
722
723 StreamString idx_name;
724 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
725 DataExtractor data(buffer_sp, m_order, m_ptr_size);
726 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
727 m_exe_ctx_ref, m_pair_type);
728 }
729 return dict_item.valobj_sp;
730}
731
734 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(),
735 m_pair_type() {}
736
739 const char *item_name = name.GetCString();
740 const uint32_t idx = ExtractIndexFromString(item_name);
741 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
742 return UINT32_MAX;
743 return idx;
744}
745
748 if (!m_hashtable.IsValid())
749 return 0;
750 return m_hashtable.GetCount();
751}
752
754 m_children.clear();
755 ValueObjectSP valobj_sp = m_backend.GetSP();
756 m_ptr_size = 0;
757 if (!valobj_sp)
758 return false;
759 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
760
761 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
762 if (!process_sp)
763 return false;
764 m_ptr_size = process_sp->GetAddressByteSize();
765 m_order = process_sp->GetByteOrder();
766 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref);
767}
768
771 return true;
772}
773
776 size_t idx) {
777 lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
778 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
779
780 const uint32_t num_children = CalculateNumChildren();
781
782 if (idx >= num_children)
783 return lldb::ValueObjectSP();
784
785 if (m_children.empty()) {
786 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
787 if (!process_sp)
788 return lldb::ValueObjectSP();
789
791 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
792
793 uint32_t tries = 0;
794 uint32_t test_idx = 0;
795
796 // Iterate over inferior memory, reading key/value pointers by shifting each
797 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
798 // fails, otherwise, continue until the number of tries matches the number
799 // of childen.
800 while (tries < num_children) {
801 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
802 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
803
804 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
805 if (error.Fail())
806 return lldb::ValueObjectSP();
807 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
808 if (error.Fail())
809 return lldb::ValueObjectSP();
810
811 test_idx++;
812
813 if (!key_at_idx || !val_at_idx)
814 continue;
815 tries++;
816
817 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
819
820 m_children.push_back(descriptor);
821 }
822 }
823
824 if (idx >= m_children.size()) // should never happen
825 return lldb::ValueObjectSP();
826
827 DictionaryItemDescriptor &dict_item = m_children[idx];
828 if (!dict_item.valobj_sp) {
829 if (!m_pair_type.IsValid()) {
830 TargetSP target_sp(m_backend.GetTargetSP());
831 if (!target_sp)
832 return ValueObjectSP();
833 m_pair_type = GetLLDBNSPairType(target_sp);
834 }
835 if (!m_pair_type.IsValid())
836 return ValueObjectSP();
837
838 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
839
840 switch (m_ptr_size) {
841 case 0: // architecture has no clue - fail
842 return lldb::ValueObjectSP();
843 case 4: {
844 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
845 *data_ptr = dict_item.key_ptr;
846 *(data_ptr + 1) = dict_item.val_ptr;
847 } break;
848 case 8: {
849 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
850 *data_ptr = dict_item.key_ptr;
851 *(data_ptr + 1) = dict_item.val_ptr;
852 } break;
853 default:
854 lldbassert(false && "pointer size is not 4 nor 8");
855 }
856
857 StreamString idx_name;
858 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
859 DataExtractor data(buffer_sp, m_order, m_ptr_size);
860 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
861 m_exe_ctx_ref, m_pair_type);
862 }
863 return dict_item.valobj_sp;
864}
865
868 : SyntheticChildrenFrontEnd(*valobj_sp) {}
869
872 const char *item_name = name.GetCString();
873 uint32_t idx = ExtractIndexFromString(item_name);
874 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
875 return UINT32_MAX;
876 return idx;
877}
878
881 return m_size;
882}
883
885 ValueObjectSP valobj_sp = m_backend.GetSP();
886 if (!valobj_sp)
887 return false;
888 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
890 error.Clear();
891 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
892 if (!process_sp)
893 return false;
894 m_ptr_size = process_sp->GetAddressByteSize();
895 m_order = process_sp->GetByteOrder();
896 uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
897 m_size = process_sp->ReadUnsignedIntegerFromMemory(
898 valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
899 if (error.Fail())
900 return false;
901 m_keys_ptr =
902 process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
903 if (error.Fail())
904 return false;
905 m_objects_ptr =
906 process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
907 return !error.Fail();
908}
909
912 return true;
913}
914
917 uint32_t num_children = CalculateNumChildren();
918
919 if (idx >= num_children)
920 return lldb::ValueObjectSP();
921
922 if (m_children.empty()) {
923 // do the scan phase
924 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
925 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
926 if (!process_sp)
927 return lldb::ValueObjectSP();
928
929 for (unsigned int child = 0; child < num_children; ++child) {
931 key_at_idx = process_sp->ReadPointerFromMemory(
932 m_keys_ptr + child * m_ptr_size, error);
933 if (error.Fail())
934 return lldb::ValueObjectSP();
935 val_at_idx = process_sp->ReadPointerFromMemory(
936 m_objects_ptr + child * m_ptr_size, error);
937 if (error.Fail())
938 return lldb::ValueObjectSP();
939 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
941 m_children.push_back(descriptor);
942 }
943 }
944
945 if (idx >= m_children.size()) // should never happen
946 return lldb::ValueObjectSP();
947
948 DictionaryItemDescriptor &dict_item = m_children[idx];
949 if (!dict_item.valobj_sp) {
950 if (!m_pair_type.IsValid()) {
951 TargetSP target_sp(m_backend.GetTargetSP());
952 if (!target_sp)
953 return ValueObjectSP();
954 m_pair_type = GetLLDBNSPairType(target_sp);
955 }
956 if (!m_pair_type.IsValid())
957 return ValueObjectSP();
958
959 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
960
961 if (m_ptr_size == 8) {
962 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
963 *data_ptr = dict_item.key_ptr;
964 *(data_ptr + 1) = dict_item.val_ptr;
965 } else {
966 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
967 *data_ptr = dict_item.key_ptr;
968 *(data_ptr + 1) = dict_item.val_ptr;
969 }
970
971 StreamString idx_name;
972 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
973 DataExtractor data(buffer_sp, m_order, m_ptr_size);
974 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
975 m_exe_ctx_ref, m_pair_type);
976 }
977 return dict_item.valobj_sp;
978}
979
982 : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
983
986 static const ConstString g_zero("[0]");
987 return name == g_zero ? 0 : UINT32_MAX;
988}
989
992 return 1;
993}
994
996 m_pair.reset();
997 return false;
998}
999
1002 return true;
1003}
1004
1007 size_t idx) {
1008 if (idx != 0)
1009 return lldb::ValueObjectSP();
1010
1011 if (m_pair.get())
1012 return m_pair;
1013
1014 auto process_sp(m_backend.GetProcessSP());
1015 if (!process_sp)
1016 return nullptr;
1017
1018 auto ptr_size = process_sp->GetAddressByteSize();
1019
1020 lldb::addr_t key_ptr =
1021 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
1022 lldb::addr_t value_ptr = key_ptr + ptr_size;
1023
1024 Status error;
1025
1026 lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
1027 if (error.Fail())
1028 return nullptr;
1029 lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
1030 if (error.Fail())
1031 return nullptr;
1032
1033 auto pair_type =
1034 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
1035
1036 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1037
1038 if (ptr_size == 8) {
1039 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1040 *data_ptr = key_at_idx;
1041 *(data_ptr + 1) = value_at_idx;
1042 } else {
1043 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1044 *data_ptr = key_at_idx;
1045 *(data_ptr + 1) = value_at_idx;
1046 }
1047
1048 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1049 m_pair = CreateValueObjectFromData(
1050 "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1051
1052 return m_pair;
1053}
1054
1055template <typename D32, typename D64>
1058 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
1059 m_data_32(nullptr), m_data_64(nullptr), m_pair_type() {}
1060
1061template <typename D32, typename D64>
1064 delete m_data_32;
1065 m_data_32 = nullptr;
1066 delete m_data_64;
1067 m_data_64 = nullptr;
1068}
1069
1070template <typename D32, typename D64>
1072 D32, D64>::GetIndexOfChildWithName(ConstString name) {
1073 const char *item_name = name.GetCString();
1074 uint32_t idx = ExtractIndexFromString(item_name);
1075 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
1076 return UINT32_MAX;
1077 return idx;
1078}
1079
1080template <typename D32, typename D64>
1081size_t
1083 if (!m_data_32 && !m_data_64)
1084 return 0;
1085 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1086}
1087
1088template <typename D32, typename D64>
1089bool
1091 Update() {
1092 m_children.clear();
1093 ValueObjectSP valobj_sp = m_backend.GetSP();
1094 m_ptr_size = 0;
1095 delete m_data_32;
1096 m_data_32 = nullptr;
1097 delete m_data_64;
1098 m_data_64 = nullptr;
1099 if (!valobj_sp)
1100 return false;
1101 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1102 Status error;
1103 error.Clear();
1104 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1105 if (!process_sp)
1106 return false;
1107 m_ptr_size = process_sp->GetAddressByteSize();
1108 m_order = process_sp->GetByteOrder();
1109 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1110 if (m_ptr_size == 4) {
1111 m_data_32 = new D32();
1112 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1113 error);
1114 } else {
1115 m_data_64 = new D64();
1116 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1117 error);
1118 }
1119
1120 return error.Success();
1121}
1122
1123template <typename D32, typename D64>
1124bool
1127 return true;
1128}
1129
1130template <typename D32, typename D64>
1133 D32, D64>::GetChildAtIndex(size_t idx) {
1134 lldb::addr_t m_keys_ptr;
1135 lldb::addr_t m_values_ptr;
1136 if (m_data_32) {
1137 uint32_t size = m_data_32->GetSize();
1138 m_keys_ptr = m_data_32->_buffer;
1139 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1140 } else {
1141 uint32_t size = m_data_64->GetSize();
1142 m_keys_ptr = m_data_64->_buffer;
1143 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1144 }
1145
1146 uint32_t num_children = CalculateNumChildren();
1147
1148 if (idx >= num_children)
1149 return lldb::ValueObjectSP();
1150
1151 if (m_children.empty()) {
1152 // do the scan phase
1153 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1154
1155 uint32_t tries = 0;
1156 uint32_t test_idx = 0;
1157
1158 while (tries < num_children) {
1159 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1160 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1161 ;
1162 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1163 if (!process_sp)
1164 return lldb::ValueObjectSP();
1165 Status error;
1166 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1167 if (error.Fail())
1168 return lldb::ValueObjectSP();
1169 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1170 if (error.Fail())
1171 return lldb::ValueObjectSP();
1172
1173 test_idx++;
1174
1175 if (!key_at_idx || !val_at_idx)
1176 continue;
1177 tries++;
1178
1179 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1181
1182 m_children.push_back(descriptor);
1183 }
1184 }
1185
1186 if (idx >= m_children.size()) // should never happen
1187 return lldb::ValueObjectSP();
1188
1189 DictionaryItemDescriptor &dict_item = m_children[idx];
1190 if (!dict_item.valobj_sp) {
1191 if (!m_pair_type.IsValid()) {
1192 TargetSP target_sp(m_backend.GetTargetSP());
1193 if (!target_sp)
1194 return ValueObjectSP();
1195 m_pair_type = GetLLDBNSPairType(target_sp);
1196 }
1197 if (!m_pair_type.IsValid())
1198 return ValueObjectSP();
1199
1200 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1201
1202 if (m_ptr_size == 8) {
1203 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1204 *data_ptr = dict_item.key_ptr;
1205 *(data_ptr + 1) = dict_item.val_ptr;
1206 } else {
1207 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1208 *data_ptr = dict_item.key_ptr;
1209 *(data_ptr + 1) = dict_item.val_ptr;
1210 }
1211
1212 StreamString idx_name;
1213 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1214 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1215 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1216 m_exe_ctx_ref, m_pair_type);
1217 }
1218 return dict_item.valobj_sp;
1219}
1220
1223 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {}
1224
1227 delete m_data_32;
1228 m_data_32 = nullptr;
1229 delete m_data_64;
1230 m_data_64 = nullptr;
1231}
1232
1233size_t
1236 const char *item_name = name.GetCString();
1237 uint32_t idx = ExtractIndexFromString(item_name);
1238 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
1239 return UINT32_MAX;
1240 return idx;
1241}
1242
1243size_t
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
1251bool
1254 m_children.clear();
1255 ValueObjectSP valobj_sp = m_backend.GetSP();
1256 m_ptr_size = 0;
1257 delete m_data_32;
1258 m_data_32 = nullptr;
1259 delete m_data_64;
1260 m_data_64 = nullptr;
1261 if (!valobj_sp)
1262 return false;
1263 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1264 Status error;
1265 error.Clear();
1266 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1267 if (!process_sp)
1268 return false;
1269 m_ptr_size = process_sp->GetAddressByteSize();
1270 m_order = process_sp->GetByteOrder();
1271 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1272 if (m_ptr_size == 4) {
1273 m_data_32 = new DataDescriptor_32();
1274 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1275 error);
1276 } else {
1277 m_data_64 = new DataDescriptor_64();
1278 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1279 error);
1280 }
1281
1282 return error.Success();
1283}
1284
1285bool
1288 return true;
1289}
1290
1294 lldb::addr_t m_keys_ptr =
1295 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1296 lldb::addr_t m_values_ptr =
1297 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1298
1299 uint32_t num_children = CalculateNumChildren();
1300
1301 if (idx >= num_children)
1302 return lldb::ValueObjectSP();
1303
1304 if (m_children.empty()) {
1305 // do the scan phase
1306 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1307
1308 uint32_t tries = 0;
1309 uint32_t test_idx = 0;
1310
1311 while (tries < num_children) {
1312 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1313 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1314 ;
1315 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1316 if (!process_sp)
1317 return lldb::ValueObjectSP();
1318 Status error;
1319 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1320 if (error.Fail())
1321 return lldb::ValueObjectSP();
1322 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1323 if (error.Fail())
1324 return lldb::ValueObjectSP();
1325
1326 test_idx++;
1327
1328 if (!key_at_idx || !val_at_idx)
1329 continue;
1330 tries++;
1331
1332 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1334
1335 m_children.push_back(descriptor);
1336 }
1337 }
1338
1339 if (idx >= m_children.size()) // should never happen
1340 return lldb::ValueObjectSP();
1341
1342 DictionaryItemDescriptor &dict_item = m_children[idx];
1343 if (!dict_item.valobj_sp) {
1344 if (!m_pair_type.IsValid()) {
1345 TargetSP target_sp(m_backend.GetTargetSP());
1346 if (!target_sp)
1347 return ValueObjectSP();
1348 m_pair_type = GetLLDBNSPairType(target_sp);
1349 }
1350 if (!m_pair_type.IsValid())
1351 return ValueObjectSP();
1352
1353 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1354
1355 if (m_ptr_size == 8) {
1356 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1357 *data_ptr = dict_item.key_ptr;
1358 *(data_ptr + 1) = dict_item.val_ptr;
1359 } else {
1360 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1361 *data_ptr = dict_item.key_ptr;
1362 *(data_ptr + 1) = dict_item.val_ptr;
1363 }
1364
1365 StreamString idx_name;
1366 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1367 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1368 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1369 m_exe_ctx_ref, m_pair_type);
1370 }
1371 return dict_item.valobj_sp;
1372}
1373
1375 ValueObject &, Stream &, const TypeSummaryOptions &);
1376
1378 ValueObject &, Stream &, const TypeSummaryOptions &);
static llvm::raw_ostream & error(Stream &strm)
#define lldbassert(x)
Definition: LLDBAssert.h:15
uint32_t _size
Definition: NSArray.cpp:105
uint32_t _used
Definition: NSArray.cpp:103
uint32_t _muts
uint32_t _kvo
uint32_t _buffer
static CompilerType GetLLDBNSPairType(TargetSP target_sp)
uint32_t _szidx
static std::optional< size_t > CalculateNumChildren(CompilerType container_elem_type, uint64_t num_elements, CompilerType element_type)
Calculates the number of elements stored in a container (with element type 'container_elem_type') as ...
Definition: VectorType.cpp:197
bool Update(lldb::addr_t addr, ExecutionContextRef exe_ctx_rf)
Definition: CFBasicHash.cpp:20
std::function< SyntheticChildrenFrontEnd *(CXXSyntheticChildren *, lldb::ValueObjectSP)> CreateFrontEndCallback
Generic representation of a type in a programming language.
Definition: CompilerType.h:36
uint32_t GetTypeInfo(CompilerType *pointee_or_element_compiler_type=nullptr) const
A uniqued constant string class.
Definition: ConstString.h:40
bool IsEmpty() const
Test for empty string.
Definition: ConstString.h:293
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
Definition: ConstString.h:191
const char * GetCString() const
Get the string value as a C string.
Definition: ConstString.h:205
A subclass of DataBuffer that stores a data buffer on the heap.
An data extractor class.
Definition: DataExtractor.h:48
Execution context objects refer to objects in the execution of the program that is being debugged.
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
A class to manage flags.
Definition: Flags.h:22
bool IsClear(ValueType bit) const
Test a single flag bit to see if it is clear (zero).
Definition: Flags.h:111
static Language * FindPlugin(lldb::LanguageType language)
Definition: Language.cpp:53
std::shared_ptr< ClassDescriptor > ClassDescriptorSP
ClassDescriptorSP GetNonKVOClassDescriptor(ValueObject &in_value)
static ObjCLanguageRuntime * Get(Process &process)
virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value)
A plug-in interface definition class for debugging a process.
Definition: Process.h:339
virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error)
Read of memory from a process.
Definition: Process.cpp:1930
uint32_t GetAddressByteSize() const
Definition: Process.cpp:3398
static lldb::TypeSystemClangSP GetForTarget(Target &target, std::optional< IsolatedASTKind > ast_kind=DefaultAST, bool create_on_demand=true)
Returns the scratch TypeSystemClang for the given target.
An error handling class.
Definition: Status.h:44
llvm::StringRef GetString() const
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:107
lldb::LanguageType GetLanguage() const
Definition: TypeSummary.cpp:31
static clang::FieldDecl * AddFieldToRecordType(const CompilerType &type, llvm::StringRef name, const CompilerType &field_type, lldb::AccessType access, uint32_t bitfield_bit_size)
static bool CompleteTagDeclarationDefinition(const CompilerType &type)
static bool StartTagDeclarationDefinition(const CompilerType &type)
lldb::ProcessSP GetProcessSP() const
Definition: ValueObject.h:338
virtual uint64_t GetValueAsUnsigned(uint64_t fail_value, bool *success=nullptr)
NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
std::vector< DictionaryItemDescriptor > m_children
size_t GetIndexOfChildWithName(ConstString name) override
lldb::ValueObjectSP GetChildAtIndex(size_t idx) override
lldb::ValueObjectSP GetChildAtIndex(size_t idx) override
lldb::ValueObjectSP GetChildAtIndex(size_t idx) override
NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
size_t GetIndexOfChildWithName(ConstString name) override
std::vector< DictionaryItemDescriptor > m_children
NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
lldb::ValueObjectSP GetChildAtIndex(size_t idx) override
size_t GetIndexOfChildWithName(ConstString name) override
static AdditionalFormatters< CXXFunctionSummaryFormat::Callback > & GetAdditionalSummaries()
std::vector< AdditionalFormatter< FormatterType > > AdditionalFormatters
Definition: NSDictionary.h:82
static AdditionalFormatters< CXXSyntheticChildren::CreateFrontEndCallback > & GetAdditionalSynthetics()
#define LLDB_INVALID_ADDRESS
Definition: lldb-defines.h:82
#define UINT32_MAX
Definition: lldb-defines.h:19
static const uint64_t NSDictionaryCapacities[]
uint64_t __NSDictionaryMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr, Status &error)
uint64_t __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, Status &error)
template bool NSDictionarySummaryProvider< false >(ValueObject &, Stream &, const TypeSummaryOptions &)
size_t ExtractIndexFromString(const char *item_name)
template bool NSDictionarySummaryProvider< true >(ValueObject &, Stream &, const TypeSummaryOptions &)
bool NSDictionarySummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
SyntheticChildrenFrontEnd * NSDictionarySyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
Definition: SBAddress.h:15
@ eBasicTypeObjCID
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
Definition: lldb-forward.h:467
@ eLanguageTypeC
Non-standardized C, such as K&R.
std::shared_ptr< lldb_private::Process > ProcessSP
Definition: lldb-forward.h:377
ByteOrder
Byte ordering definitions.
@ eByteOrderInvalid
std::shared_ptr< lldb_private::TypeSystemClang > TypeSystemClangSP
Definition: lldb-forward.h:454
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
Definition: lldb-forward.h:325
uint64_t addr_t
Definition: lldb-types.h:79
std::shared_ptr< lldb_private::Target > TargetSP
Definition: lldb-forward.h:432
std::function< bool(ValueObject &, Stream &, const TypeSummaryOptions &)> Callback
Definition: TypeSummary.h:311