LLDB mainline
LibCxxVariant.cpp
Go to the documentation of this file.
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"
14
15#include "llvm/ADT/ScopeExit.h"
16#include <optional>
17
18using namespace lldb;
19using 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
64namespace {
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
69enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos };
70
71uint64_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
84LibcxxVariantIndexValidity
85LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
86 ValueObjectSP index_sp(
87 impl_sp->GetChildMemberWithName(ConstString("__index"), true));
88
89 if (!index_sp)
90 return LibcxxVariantIndexValidity::Invalid;
91
92 // In the stable ABI, the type of __index is just int.
93 // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is
94 // enabled, the type can either be unsigned char/short/int depending on
95 // how many variant types there are.
96 // We only need to do this here when comparing against npos, because npos is
97 // just `-1`, but that translates to different unsigned values depending on
98 // the byte size.
99 CompilerType index_type = index_sp->GetCompilerType();
100
101 std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr);
102 if (!index_type_bytes)
103 return LibcxxVariantIndexValidity::Invalid;
104
105 uint64_t npos_value = VariantNposValue(*index_type_bytes);
106 uint64_t index_value = index_sp->GetValueAsUnsigned(0);
107
108 if (index_value == npos_value)
109 return LibcxxVariantIndexValidity::NPos;
110
111 return LibcxxVariantIndexValidity::Valid;
112}
113
114std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
115 ValueObjectSP index_sp(
116 impl_sp->GetChildMemberWithName(ConstString("__index"), true));
117
118 if (!index_sp)
119 return {};
120
121 return {index_sp->GetValueAsUnsigned(0)};
122}
123
124ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
125 ValueObjectSP data_sp(
126 impl_sp->GetChildMemberWithName(ConstString("__data"), true));
127
128 if (!data_sp)
129 return ValueObjectSP{};
130
131 ValueObjectSP current_level = data_sp;
132 for (uint64_t n = index; n != 0; --n) {
133 ValueObjectSP tail_sp(
134 current_level->GetChildMemberWithName(ConstString("__tail"), true));
135
136 if (!tail_sp)
137 return ValueObjectSP{};
138
139 current_level = tail_sp;
140 }
141
142 return current_level->GetChildMemberWithName(ConstString("__head"), true);
143}
144} // namespace
145
146namespace lldb_private {
147namespace formatters {
149 const TypeSummaryOptions &options) {
150 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
151 if (!valobj_sp)
152 return false;
153
154 ValueObjectSP impl_sp = GetChildMemberWithName(
155 *valobj_sp, {ConstString("__impl_"), ConstString("__impl")});
156
157 if (!impl_sp)
158 return false;
159
160 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
161
162 if (validity == LibcxxVariantIndexValidity::Invalid)
163 return false;
164
165 if (validity == LibcxxVariantIndexValidity::NPos) {
166 stream.Printf(" No Value");
167 return true;
168 }
169
170 auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
171
172 if (!optional_index_value)
173 return false;
174
175 uint64_t index_value = *optional_index_value;
176
177 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
178
179 if (!nth_head)
180 return false;
181
182 CompilerType head_type = nth_head->GetCompilerType();
183
184 if (!head_type)
185 return false;
186
187 CompilerType template_type = head_type.GetTypeTemplateArgument(1);
188
189 if (!template_type)
190 return false;
191
192 stream << " Active Type = " << template_type.GetDisplayTypeName() << " ";
193
194 return true;
195}
196} // namespace formatters
197} // namespace lldb_private
198
199namespace {
200class VariantFrontEnd : public SyntheticChildrenFrontEnd {
201public:
202 VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
203 Update();
204 }
205
206 size_t GetIndexOfChildWithName(ConstString name) override {
208 }
209
210 bool MightHaveChildren() override { return true; }
211 bool Update() override;
212 size_t CalculateNumChildren() override { return m_size; }
213 ValueObjectSP GetChildAtIndex(size_t idx) override;
214
215private:
216 size_t m_size = 0;
217};
218} // namespace
219
220bool VariantFrontEnd::Update() {
221 m_size = 0;
222 ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
223 m_backend, {ConstString("__impl_"), ConstString("__impl")});
224 if (!impl_sp)
225 return false;
226
227 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
228
229 if (validity == LibcxxVariantIndexValidity::Invalid)
230 return false;
231
232 if (validity == LibcxxVariantIndexValidity::NPos)
233 return true;
234
235 m_size = 1;
236
237 return false;
238}
239
240ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) {
241 if (idx >= m_size)
242 return {};
243
244 ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
245 m_backend, {ConstString("__impl_"), ConstString("__impl")});
246 if (!impl_sp)
247 return {};
248
249 auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
250
251 if (!optional_index_value)
252 return {};
253
254 uint64_t index_value = *optional_index_value;
255
256 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
257
258 if (!nth_head)
259 return {};
260
261 CompilerType head_type = nth_head->GetCompilerType();
262
263 if (!head_type)
264 return {};
265
266 CompilerType template_type = head_type.GetTypeTemplateArgument(1);
267
268 if (!template_type)
269 return {};
270
271 ValueObjectSP head_value(
272 nth_head->GetChildMemberWithName(ConstString("__value"), true));
273
274 if (!head_value)
275 return {};
276
277 return head_value->Clone(ConstString("Value"));
278}
279
282 lldb::ValueObjectSP valobj_sp) {
283 if (valobj_sp)
284 return new VariantFrontEnd(*valobj_sp);
285 return nullptr;
286}
#define lldbassert(x)
Definition: LLDBAssert.h:15
Generic representation of a type in a programming language.
Definition: CompilerType.h:36
CompilerType GetTypeTemplateArgument(size_t idx, bool expand_pack=false) const
std::optional< uint64_t > GetByteSize(ExecutionContextScope *exe_scope) const
Return the size of the type in bytes.
ConstString GetDisplayTypeName() const
A uniqued constant string class.
Definition: ConstString.h:39
const char * GetCString() const
Get the string value as a C string.
Definition: ConstString.h:215
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
virtual size_t GetIndexOfChildWithName(ConstString name)=0
virtual lldb::ValueObjectSP GetChildAtIndex(size_t idx)=0
virtual lldb::ValueObjectSP GetNonSyntheticValue()
Definition: ValueObject.h:592
lldb::ValueObjectSP GetChildMemberWithName(ValueObject &obj, llvm::ArrayRef< ConstString > alternative_names)
Find a child member of obj_sp, trying all alternative names in order.
Definition: LibCxx.cpp:38
size_t ExtractIndexFromString(const char *item_name)
SyntheticChildrenFrontEnd * LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp)
bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
A class that represents a running process on the host machine.
Definition: SBAddress.h:15