1 | //===-- BlockPointer.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 "BlockPointer.h" |
10 | |
11 | #include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" |
12 | #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" |
13 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
14 | #include "lldb/Core/ValueObject.h" |
15 | #include "lldb/DataFormatters/FormattersHelpers.h" |
16 | #include "lldb/Symbol/CompilerType.h" |
17 | #include "lldb/Symbol/TypeSystem.h" |
18 | #include "lldb/Target/Target.h" |
19 | #include "lldb/Utility/LLDBAssert.h" |
20 | #include "lldb/Utility/LLDBLog.h" |
21 | #include "lldb/Utility/Log.h" |
22 | |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | using namespace lldb_private::formatters; |
26 | |
27 | namespace lldb_private { |
28 | namespace formatters { |
29 | |
30 | class BlockPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
31 | public: |
32 | BlockPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
33 | : SyntheticChildrenFrontEnd(*valobj_sp), m_block_struct_type() { |
34 | CompilerType block_pointer_type(m_backend.GetCompilerType()); |
35 | CompilerType function_pointer_type; |
36 | block_pointer_type.IsBlockPointerType(function_pointer_type_ptr: &function_pointer_type); |
37 | |
38 | TargetSP target_sp(m_backend.GetTargetSP()); |
39 | |
40 | if (!target_sp) { |
41 | return; |
42 | } |
43 | |
44 | auto type_system_or_err = target_sp->GetScratchTypeSystemForLanguage( |
45 | language: lldb::eLanguageTypeC_plus_plus); |
46 | if (auto err = type_system_or_err.takeError()) { |
47 | LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), std::move(err), |
48 | "Failed to get scratch TypeSystemClang: {0}" ); |
49 | return; |
50 | } |
51 | |
52 | auto ts = block_pointer_type.GetTypeSystem(); |
53 | auto clang_ast_context = ts.dyn_cast_or_null<TypeSystemClang>(); |
54 | if (!clang_ast_context) |
55 | return; |
56 | |
57 | const char *const isa_name("__isa" ); |
58 | const CompilerType isa_type = |
59 | clang_ast_context->GetBasicType(type: lldb::eBasicTypeObjCClass); |
60 | const char *const flags_name("__flags" ); |
61 | const CompilerType flags_type = |
62 | clang_ast_context->GetBasicType(type: lldb::eBasicTypeInt); |
63 | const char *const reserved_name("__reserved" ); |
64 | const CompilerType reserved_type = |
65 | clang_ast_context->GetBasicType(type: lldb::eBasicTypeInt); |
66 | const char *const FuncPtr_name("__FuncPtr" ); |
67 | |
68 | m_block_struct_type = clang_ast_context->CreateStructForIdentifier( |
69 | type_name: llvm::StringRef(), type_fields: {{isa_name, isa_type}, |
70 | {flags_name, flags_type}, |
71 | {reserved_name, reserved_type}, |
72 | {FuncPtr_name, function_pointer_type}}); |
73 | } |
74 | |
75 | ~BlockPointerSyntheticFrontEnd() override = default; |
76 | |
77 | llvm::Expected<uint32_t> CalculateNumChildren() override { |
78 | const bool omit_empty_base_classes = false; |
79 | return m_block_struct_type.GetNumChildren(omit_empty_base_classes, exe_ctx: nullptr); |
80 | } |
81 | |
82 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { |
83 | if (!m_block_struct_type.IsValid()) { |
84 | return lldb::ValueObjectSP(); |
85 | } |
86 | |
87 | if (idx >= CalculateNumChildrenIgnoringErrors()) { |
88 | return lldb::ValueObjectSP(); |
89 | } |
90 | |
91 | const bool thread_and_frame_only_if_stopped = true; |
92 | ExecutionContext exe_ctx = m_backend.GetExecutionContextRef().Lock( |
93 | thread_and_frame_only_if_stopped); |
94 | const bool transparent_pointers = false; |
95 | const bool omit_empty_base_classes = false; |
96 | const bool ignore_array_bounds = false; |
97 | ValueObject *value_object = nullptr; |
98 | |
99 | std::string child_name; |
100 | uint32_t child_byte_size = 0; |
101 | int32_t child_byte_offset = 0; |
102 | uint32_t child_bitfield_bit_size = 0; |
103 | uint32_t child_bitfield_bit_offset = 0; |
104 | bool child_is_base_class = false; |
105 | bool child_is_deref_of_parent = false; |
106 | uint64_t language_flags = 0; |
107 | |
108 | const CompilerType child_type = |
109 | m_block_struct_type.GetChildCompilerTypeAtIndex( |
110 | exe_ctx: &exe_ctx, idx, transparent_pointers, omit_empty_base_classes, |
111 | ignore_array_bounds, child_name, child_byte_size, child_byte_offset, |
112 | child_bitfield_bit_size, child_bitfield_bit_offset, |
113 | child_is_base_class, child_is_deref_of_parent, valobj: value_object, |
114 | language_flags); |
115 | |
116 | ValueObjectSP struct_pointer_sp = |
117 | m_backend.Cast(compiler_type: m_block_struct_type.GetPointerType()); |
118 | |
119 | if (!struct_pointer_sp) { |
120 | return lldb::ValueObjectSP(); |
121 | } |
122 | |
123 | Status err; |
124 | ValueObjectSP struct_sp = struct_pointer_sp->Dereference(error&: err); |
125 | |
126 | if (!struct_sp || !err.Success()) { |
127 | return lldb::ValueObjectSP(); |
128 | } |
129 | |
130 | ValueObjectSP child_sp(struct_sp->GetSyntheticChildAtOffset( |
131 | offset: child_byte_offset, type: child_type, can_create: true, |
132 | name_const_str: ConstString(child_name.c_str(), child_name.size()))); |
133 | |
134 | return child_sp; |
135 | } |
136 | |
137 | // return true if this object is now safe to use forever without ever |
138 | // updating again; the typical (and tested) answer here is 'false' |
139 | lldb::ChildCacheState Update() override { |
140 | return lldb::ChildCacheState::eRefetch; |
141 | } |
142 | |
143 | // maybe return false if the block pointer is, say, null |
144 | bool MightHaveChildren() override { return true; } |
145 | |
146 | size_t GetIndexOfChildWithName(ConstString name) override { |
147 | if (!m_block_struct_type.IsValid()) |
148 | return UINT32_MAX; |
149 | |
150 | const bool omit_empty_base_classes = false; |
151 | return m_block_struct_type.GetIndexOfChildWithName(name: name.AsCString(), |
152 | omit_empty_base_classes); |
153 | } |
154 | |
155 | private: |
156 | CompilerType m_block_struct_type; |
157 | }; |
158 | |
159 | } // namespace formatters |
160 | } // namespace lldb_private |
161 | |
162 | bool lldb_private::formatters::BlockPointerSummaryProvider( |
163 | ValueObject &valobj, Stream &s, const TypeSummaryOptions &) { |
164 | lldb_private::SyntheticChildrenFrontEnd *synthetic_children = |
165 | BlockPointerSyntheticFrontEndCreator(nullptr, valobj.GetSP()); |
166 | if (!synthetic_children) { |
167 | return false; |
168 | } |
169 | |
170 | synthetic_children->Update(); |
171 | |
172 | static const ConstString s_FuncPtr_name("__FuncPtr" ); |
173 | |
174 | lldb::ValueObjectSP child_sp = synthetic_children->GetChildAtIndex( |
175 | idx: synthetic_children->GetIndexOfChildWithName(name: s_FuncPtr_name)); |
176 | |
177 | if (!child_sp) { |
178 | return false; |
179 | } |
180 | |
181 | lldb::ValueObjectSP qualified_child_representation_sp = |
182 | child_sp->GetQualifiedRepresentationIfAvailable( |
183 | dynValue: lldb::eDynamicDontRunTarget, synthValue: true); |
184 | |
185 | const char *child_value = |
186 | qualified_child_representation_sp->GetValueAsCString(); |
187 | |
188 | s.Printf(format: "%s" , child_value); |
189 | |
190 | return true; |
191 | } |
192 | |
193 | lldb_private::SyntheticChildrenFrontEnd * |
194 | lldb_private::formatters::BlockPointerSyntheticFrontEndCreator( |
195 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
196 | if (!valobj_sp) |
197 | return nullptr; |
198 | return new BlockPointerSyntheticFrontEnd(valobj_sp); |
199 | } |
200 | |