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