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