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