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