/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 54 | uint64_t VariantNposValue(uint64_t index_byte_size) { |
72 | 54 | switch (index_byte_size) { |
73 | 0 | case 1: |
74 | 0 | return static_cast<uint8_t>(-1); |
75 | 0 | case 2: |
76 | 0 | return static_cast<uint16_t>(-1); |
77 | 54 | case 4: |
78 | 54 | return static_cast<uint32_t>(-1); |
79 | 54 | } |
80 | 0 | lldbassert(false && "Unknown index type size"); |
81 | 0 | return static_cast<uint32_t>(-1); // Fallback to stable ABI type. |
82 | 0 | } |
83 | | |
84 | | LibcxxVariantIndexValidity |
85 | 54 | LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { |
86 | 54 | ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); |
87 | | |
88 | 54 | if (!index_sp) |
89 | 0 | 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 | 54 | CompilerType index_type = index_sp->GetCompilerType(); |
99 | | |
100 | 54 | std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr); |
101 | 54 | if (!index_type_bytes) |
102 | 0 | return LibcxxVariantIndexValidity::Invalid; |
103 | | |
104 | 54 | uint64_t npos_value = VariantNposValue(*index_type_bytes); |
105 | 54 | uint64_t index_value = index_sp->GetValueAsUnsigned(0); |
106 | | |
107 | 54 | if (index_value == npos_value) |
108 | 12 | return LibcxxVariantIndexValidity::NPos; |
109 | | |
110 | 42 | return LibcxxVariantIndexValidity::Valid; |
111 | 54 | } |
112 | | |
113 | 28 | std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { |
114 | 28 | ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); |
115 | | |
116 | 28 | if (!index_sp) |
117 | 0 | return {}; |
118 | | |
119 | 28 | return {index_sp->GetValueAsUnsigned(0)}; |
120 | 28 | } |
121 | | |
122 | 28 | ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { |
123 | 28 | ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data")); |
124 | | |
125 | 28 | if (!data_sp) |
126 | 0 | return ValueObjectSP{}; |
127 | | |
128 | 28 | ValueObjectSP current_level = data_sp; |
129 | 44 | for (uint64_t n = index; n != 0; --n16 ) { |
130 | 16 | ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail")); |
131 | | |
132 | 16 | if (!tail_sp) |
133 | 0 | return ValueObjectSP{}; |
134 | | |
135 | 16 | current_level = tail_sp; |
136 | 16 | } |
137 | | |
138 | 28 | return current_level->GetChildMemberWithName("__head"); |
139 | 28 | } |
140 | | } // namespace |
141 | | |
142 | | namespace lldb_private { |
143 | | namespace formatters { |
144 | | bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, |
145 | 18 | const TypeSummaryOptions &options) { |
146 | 18 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
147 | 18 | if (!valobj_sp) |
148 | 0 | return false; |
149 | | |
150 | 18 | ValueObjectSP impl_sp = GetChildMemberWithName( |
151 | 18 | *valobj_sp, {ConstString("__impl_"), ConstString("__impl")}); |
152 | | |
153 | 18 | if (!impl_sp) |
154 | 0 | return false; |
155 | | |
156 | 18 | LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); |
157 | | |
158 | 18 | if (validity == LibcxxVariantIndexValidity::Invalid) |
159 | 0 | return false; |
160 | | |
161 | 18 | if (validity == LibcxxVariantIndexValidity::NPos) { |
162 | 4 | stream.Printf(" No Value"); |
163 | 4 | return true; |
164 | 4 | } |
165 | | |
166 | 14 | auto optional_index_value = LibcxxVariantIndexValue(impl_sp); |
167 | | |
168 | 14 | if (!optional_index_value) |
169 | 0 | return false; |
170 | | |
171 | 14 | uint64_t index_value = *optional_index_value; |
172 | | |
173 | 14 | ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); |
174 | | |
175 | 14 | if (!nth_head) |
176 | 0 | return false; |
177 | | |
178 | 14 | CompilerType head_type = nth_head->GetCompilerType(); |
179 | | |
180 | 14 | if (!head_type) |
181 | 0 | return false; |
182 | | |
183 | 14 | CompilerType template_type = head_type.GetTypeTemplateArgument(1); |
184 | | |
185 | 14 | if (!template_type) |
186 | 0 | return false; |
187 | | |
188 | 14 | stream << " Active Type = " << template_type.GetDisplayTypeName() << " "; |
189 | | |
190 | 14 | return true; |
191 | 14 | } |
192 | | } // namespace formatters |
193 | | } // namespace lldb_private |
194 | | |
195 | | namespace { |
196 | | class VariantFrontEnd : public SyntheticChildrenFrontEnd { |
197 | | public: |
198 | 18 | VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { |
199 | 18 | Update(); |
200 | 18 | } |
201 | | |
202 | 0 | size_t GetIndexOfChildWithName(ConstString name) override { |
203 | 0 | return formatters::ExtractIndexFromString(name.GetCString()); |
204 | 0 | } |
205 | | |
206 | 4 | bool MightHaveChildren() override { return true; } |
207 | | bool Update() override; |
208 | 18 | size_t CalculateNumChildren() override { return m_size; } |
209 | | ValueObjectSP GetChildAtIndex(size_t idx) override; |
210 | | |
211 | | private: |
212 | | size_t m_size = 0; |
213 | | }; |
214 | | } // namespace |
215 | | |
216 | 36 | bool VariantFrontEnd::Update() { |
217 | 36 | m_size = 0; |
218 | 36 | ValueObjectSP impl_sp = formatters::GetChildMemberWithName( |
219 | 36 | m_backend, {ConstString("__impl_"), ConstString("__impl")}); |
220 | 36 | if (!impl_sp) |
221 | 0 | return false; |
222 | | |
223 | 36 | LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); |
224 | | |
225 | 36 | if (validity == LibcxxVariantIndexValidity::Invalid) |
226 | 0 | return false; |
227 | | |
228 | 36 | if (validity == LibcxxVariantIndexValidity::NPos) |
229 | 8 | return true; |
230 | | |
231 | 28 | m_size = 1; |
232 | | |
233 | 28 | return false; |
234 | 36 | } |
235 | | |
236 | 14 | ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) { |
237 | 14 | if (idx >= m_size) |
238 | 0 | return {}; |
239 | | |
240 | 14 | ValueObjectSP impl_sp = formatters::GetChildMemberWithName( |
241 | 14 | m_backend, {ConstString("__impl_"), ConstString("__impl")}); |
242 | 14 | if (!impl_sp) |
243 | 0 | return {}; |
244 | | |
245 | 14 | auto optional_index_value = LibcxxVariantIndexValue(impl_sp); |
246 | | |
247 | 14 | if (!optional_index_value) |
248 | 0 | return {}; |
249 | | |
250 | 14 | uint64_t index_value = *optional_index_value; |
251 | | |
252 | 14 | ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); |
253 | | |
254 | 14 | if (!nth_head) |
255 | 0 | return {}; |
256 | | |
257 | 14 | CompilerType head_type = nth_head->GetCompilerType(); |
258 | | |
259 | 14 | if (!head_type) |
260 | 0 | return {}; |
261 | | |
262 | 14 | CompilerType template_type = head_type.GetTypeTemplateArgument(1); |
263 | | |
264 | 14 | if (!template_type) |
265 | 0 | return {}; |
266 | | |
267 | 14 | ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value")); |
268 | | |
269 | 14 | if (!head_value) |
270 | 0 | return {}; |
271 | | |
272 | 14 | return head_value->Clone(ConstString("Value")); |
273 | 14 | } |
274 | | |
275 | | SyntheticChildrenFrontEnd * |
276 | | formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, |
277 | 18 | lldb::ValueObjectSP valobj_sp) { |
278 | 18 | if (valobj_sp) |
279 | 18 | return new VariantFrontEnd(*valobj_sp); |
280 | 0 | return nullptr; |
281 | 18 | } |