1 | //===-- LibCxxVariant.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 "LibCxxVariant.h" |
10 | #include "LibCxx.h" |
11 | #include "lldb/DataFormatters/FormattersHelpers.h" |
12 | #include "lldb/Symbol/CompilerType.h" |
13 | #include "lldb/Utility/LLDBAssert.h" |
14 | |
15 | #include "llvm/ADT/ScopeExit.h" |
16 | #include <optional> |
17 | |
18 | using namespace lldb; |
19 | using namespace lldb_private; |
20 | |
21 | // libc++ variant implementation contains two members that we care about both |
22 | // are contained in the __impl member. |
23 | // - __index which tells us which of the variadic template types is the active |
24 | // type for the variant |
25 | // - __data is a variadic union which recursively contains itself as member |
26 | // which refers to the tailing variadic types. |
27 | // - __head which refers to the leading non pack type |
28 | // - __value refers to the actual value contained |
29 | // - __tail which refers to the remaining pack types |
30 | // |
31 | // e.g. given std::variant<int,double,char> v1 |
32 | // |
33 | // (lldb) frame var -R v1.__impl.__data |
34 | //(... __union<... 0, int, double, char>) v1.__impl.__data = { |
35 | // ... |
36 | // __head = { |
37 | // __value = ... |
38 | // } |
39 | // __tail = { |
40 | // ... |
41 | // __head = { |
42 | // __value = ... |
43 | // } |
44 | // __tail = { |
45 | // ... |
46 | // __head = { |
47 | // __value = ... |
48 | // ... |
49 | // |
50 | // So given |
51 | // - __index equal to 0 the active value is contained in |
52 | // |
53 | // __data.__head.__value |
54 | // |
55 | // - __index equal to 1 the active value is contained in |
56 | // |
57 | // __data.__tail.__head.__value |
58 | // |
59 | // - __index equal to 2 the active value is contained in |
60 | // |
61 | // __data.__tail.__tail.__head.__value |
62 | // |
63 | |
64 | namespace { |
65 | // libc++ std::variant index could have one of three states |
66 | // 1) Valid, we can obtain it and its not variant_npos |
67 | // 2) Invalid, we can't obtain it or it is not a type we expect |
68 | // 3) NPos, its value is variant_npos which means the variant has no value |
69 | enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos }; |
70 | |
71 | uint64_t VariantNposValue(uint64_t index_byte_size) { |
72 | switch (index_byte_size) { |
73 | case 1: |
74 | return static_cast<uint8_t>(-1); |
75 | case 2: |
76 | return static_cast<uint16_t>(-1); |
77 | case 4: |
78 | return static_cast<uint32_t>(-1); |
79 | } |
80 | lldbassert(false && "Unknown index type size" ); |
81 | return static_cast<uint32_t>(-1); // Fallback to stable ABI type. |
82 | } |
83 | |
84 | LibcxxVariantIndexValidity |
85 | LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { |
86 | ValueObjectSP index_sp(impl_sp->GetChildMemberWithName(name: "__index" )); |
87 | |
88 | if (!index_sp) |
89 | return LibcxxVariantIndexValidity::Invalid; |
90 | |
91 | // In the stable ABI, the type of __index is just int. |
92 | // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is |
93 | // enabled, the type can either be unsigned char/short/int depending on |
94 | // how many variant types there are. |
95 | // We only need to do this here when comparing against npos, because npos is |
96 | // just `-1`, but that translates to different unsigned values depending on |
97 | // the byte size. |
98 | CompilerType index_type = index_sp->GetCompilerType(); |
99 | |
100 | std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(exe_scope: nullptr); |
101 | if (!index_type_bytes) |
102 | return LibcxxVariantIndexValidity::Invalid; |
103 | |
104 | uint64_t npos_value = VariantNposValue(index_byte_size: *index_type_bytes); |
105 | uint64_t index_value = index_sp->GetValueAsUnsigned(fail_value: 0); |
106 | |
107 | if (index_value == npos_value) |
108 | return LibcxxVariantIndexValidity::NPos; |
109 | |
110 | return LibcxxVariantIndexValidity::Valid; |
111 | } |
112 | |
113 | std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { |
114 | ValueObjectSP index_sp(impl_sp->GetChildMemberWithName(name: "__index" )); |
115 | |
116 | if (!index_sp) |
117 | return {}; |
118 | |
119 | return {index_sp->GetValueAsUnsigned(fail_value: 0)}; |
120 | } |
121 | |
122 | ValueObjectSP (ValueObjectSP &impl_sp, uint64_t index) { |
123 | ValueObjectSP data_sp(impl_sp->GetChildMemberWithName(name: "__data" )); |
124 | |
125 | if (!data_sp) |
126 | return ValueObjectSP{}; |
127 | |
128 | ValueObjectSP current_level = data_sp; |
129 | for (uint64_t n = index; n != 0; --n) { |
130 | ValueObjectSP tail_sp(current_level->GetChildMemberWithName(name: "__tail" )); |
131 | |
132 | if (!tail_sp) |
133 | return ValueObjectSP{}; |
134 | |
135 | current_level = tail_sp; |
136 | } |
137 | |
138 | return current_level->GetChildMemberWithName(name: "__head" ); |
139 | } |
140 | } // namespace |
141 | |
142 | namespace lldb_private { |
143 | namespace formatters { |
144 | bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, |
145 | const TypeSummaryOptions &options) { |
146 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
147 | if (!valobj_sp) |
148 | return false; |
149 | |
150 | ValueObjectSP impl_sp = GetChildMemberWithName( |
151 | obj&: *valobj_sp, alternative_names: {ConstString("__impl_" ), ConstString("__impl" )}); |
152 | |
153 | if (!impl_sp) |
154 | return false; |
155 | |
156 | LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); |
157 | |
158 | if (validity == LibcxxVariantIndexValidity::Invalid) |
159 | return false; |
160 | |
161 | if (validity == LibcxxVariantIndexValidity::NPos) { |
162 | stream.Printf(format: " No Value" ); |
163 | return true; |
164 | } |
165 | |
166 | auto optional_index_value = LibcxxVariantIndexValue(impl_sp); |
167 | |
168 | if (!optional_index_value) |
169 | return false; |
170 | |
171 | uint64_t index_value = *optional_index_value; |
172 | |
173 | ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index: index_value); |
174 | |
175 | if (!nth_head) |
176 | return false; |
177 | |
178 | CompilerType head_type = nth_head->GetCompilerType(); |
179 | |
180 | if (!head_type) |
181 | return false; |
182 | |
183 | CompilerType template_type = head_type.GetTypeTemplateArgument(idx: 1); |
184 | |
185 | if (!template_type) |
186 | return false; |
187 | |
188 | stream << " Active Type = " << template_type.GetDisplayTypeName() << " " ; |
189 | |
190 | return true; |
191 | } |
192 | } // namespace formatters |
193 | } // namespace lldb_private |
194 | |
195 | namespace { |
196 | class VariantFrontEnd : public SyntheticChildrenFrontEnd { |
197 | public: |
198 | VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { |
199 | Update(); |
200 | } |
201 | |
202 | size_t GetIndexOfChildWithName(ConstString name) override { |
203 | return formatters::ExtractIndexFromString(item_name: name.GetCString()); |
204 | } |
205 | |
206 | bool MightHaveChildren() override { return true; } |
207 | lldb::ChildCacheState Update() override; |
208 | llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; } |
209 | ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
210 | |
211 | private: |
212 | size_t m_size = 0; |
213 | }; |
214 | } // namespace |
215 | |
216 | lldb::ChildCacheState VariantFrontEnd::Update() { |
217 | m_size = 0; |
218 | ValueObjectSP impl_sp = formatters::GetChildMemberWithName( |
219 | obj&: m_backend, alternative_names: {ConstString("__impl_" ), ConstString("__impl" )}); |
220 | if (!impl_sp) |
221 | return lldb::ChildCacheState::eRefetch; |
222 | |
223 | LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); |
224 | |
225 | if (validity == LibcxxVariantIndexValidity::Invalid) |
226 | return lldb::ChildCacheState::eRefetch; |
227 | |
228 | if (validity == LibcxxVariantIndexValidity::NPos) |
229 | return lldb::ChildCacheState::eReuse; |
230 | |
231 | m_size = 1; |
232 | |
233 | return lldb::ChildCacheState::eRefetch; |
234 | } |
235 | |
236 | ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) { |
237 | if (idx >= m_size) |
238 | return {}; |
239 | |
240 | ValueObjectSP impl_sp = formatters::GetChildMemberWithName( |
241 | obj&: m_backend, alternative_names: {ConstString("__impl_" ), ConstString("__impl" )}); |
242 | if (!impl_sp) |
243 | return {}; |
244 | |
245 | auto optional_index_value = LibcxxVariantIndexValue(impl_sp); |
246 | |
247 | if (!optional_index_value) |
248 | return {}; |
249 | |
250 | uint64_t index_value = *optional_index_value; |
251 | |
252 | ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index: index_value); |
253 | |
254 | if (!nth_head) |
255 | return {}; |
256 | |
257 | CompilerType head_type = nth_head->GetCompilerType(); |
258 | |
259 | if (!head_type) |
260 | return {}; |
261 | |
262 | CompilerType template_type = head_type.GetTypeTemplateArgument(idx: 1); |
263 | |
264 | if (!template_type) |
265 | return {}; |
266 | |
267 | ValueObjectSP head_value(nth_head->GetChildMemberWithName(name: "__value" )); |
268 | |
269 | if (!head_value) |
270 | return {}; |
271 | |
272 | return head_value->Clone(new_name: ConstString("Value" )); |
273 | } |
274 | |
275 | SyntheticChildrenFrontEnd * |
276 | formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, |
277 | lldb::ValueObjectSP valobj_sp) { |
278 | if (valobj_sp) |
279 | return new VariantFrontEnd(*valobj_sp); |
280 | return nullptr; |
281 | } |
282 | |