xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
VariantNposValue(uint64_t index_byte_size)71 uint64_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 
84 LibcxxVariantIndexValidity
LibcxxVariantGetIndexValidity(ValueObjectSP & impl_sp)85 LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
86   ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
87 
88   if (!index_sp)
89     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   CompilerType index_type = index_sp->GetCompilerType();
99 
100   llvm::Expected<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr);
101   if (!index_type_bytes) {
102     LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters),
103                     index_type_bytes.takeError(), "{0}");
104     if (!index_type_bytes)
105       return LibcxxVariantIndexValidity::Invalid;
106   }
107   uint64_t npos_value = VariantNposValue(*index_type_bytes);
108   uint64_t index_value = index_sp->GetValueAsUnsigned(0);
109 
110   if (index_value == npos_value)
111     return LibcxxVariantIndexValidity::NPos;
112 
113   return LibcxxVariantIndexValidity::Valid;
114 }
115 
LibcxxVariantIndexValue(ValueObjectSP & impl_sp)116 std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
117   ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
118 
119   if (!index_sp)
120     return {};
121 
122   return {index_sp->GetValueAsUnsigned(0)};
123 }
124 
LibcxxVariantGetNthHead(ValueObjectSP & impl_sp,uint64_t index)125 ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
126   ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data"));
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(current_level->GetChildMemberWithName("__tail"));
134 
135     if (!tail_sp)
136       return ValueObjectSP{};
137 
138     current_level = tail_sp;
139   }
140 
141   return current_level->GetChildMemberWithName("__head");
142 }
143 } // namespace
144 
145 namespace lldb_private {
146 namespace formatters {
LibcxxVariantSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)147 bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
148                                   const TypeSummaryOptions &options) {
149   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
150   if (!valobj_sp)
151     return false;
152 
153   ValueObjectSP impl_sp = GetChildMemberWithName(
154       *valobj_sp, {ConstString("__impl_"), ConstString("__impl")});
155 
156   if (!impl_sp)
157     return false;
158 
159   LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
160 
161   if (validity == LibcxxVariantIndexValidity::Invalid)
162     return false;
163 
164   if (validity == LibcxxVariantIndexValidity::NPos) {
165     stream.Printf(" No Value");
166     return true;
167   }
168 
169   auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
170 
171   if (!optional_index_value)
172     return false;
173 
174   uint64_t index_value = *optional_index_value;
175 
176   ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
177 
178   if (!nth_head)
179     return false;
180 
181   CompilerType head_type = nth_head->GetCompilerType();
182 
183   if (!head_type)
184     return false;
185 
186   CompilerType template_type = head_type.GetTypeTemplateArgument(1);
187 
188   if (!template_type)
189     return false;
190 
191   stream << " Active Type = " << template_type.GetDisplayTypeName() << " ";
192 
193   return true;
194 }
195 } // namespace formatters
196 } // namespace lldb_private
197 
198 namespace {
199 class VariantFrontEnd : public SyntheticChildrenFrontEnd {
200 public:
VariantFrontEnd(ValueObject & valobj)201   VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
202     Update();
203   }
204 
GetIndexOfChildWithName(ConstString name)205   llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
206     auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
207     if (!optional_idx) {
208       return llvm::createStringError("Type has no child named '%s'",
209                                      name.AsCString());
210     }
211     return *optional_idx;
212   }
213 
214   lldb::ChildCacheState Update() override;
CalculateNumChildren()215   llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }
216   ValueObjectSP GetChildAtIndex(uint32_t idx) override;
217 
218 private:
219   size_t m_size = 0;
220 };
221 } // namespace
222 
Update()223 lldb::ChildCacheState VariantFrontEnd::Update() {
224   m_size = 0;
225   ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
226       m_backend, {ConstString("__impl_"), ConstString("__impl")});
227   if (!impl_sp)
228     return lldb::ChildCacheState::eRefetch;
229 
230   LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
231 
232   if (validity == LibcxxVariantIndexValidity::Invalid)
233     return lldb::ChildCacheState::eRefetch;
234 
235   if (validity == LibcxxVariantIndexValidity::NPos)
236     return lldb::ChildCacheState::eReuse;
237 
238   m_size = 1;
239 
240   return lldb::ChildCacheState::eRefetch;
241 }
242 
GetChildAtIndex(uint32_t idx)243 ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) {
244   if (idx >= m_size)
245     return {};
246 
247   ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
248       m_backend, {ConstString("__impl_"), ConstString("__impl")});
249   if (!impl_sp)
250     return {};
251 
252   auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
253 
254   if (!optional_index_value)
255     return {};
256 
257   uint64_t index_value = *optional_index_value;
258 
259   ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
260 
261   if (!nth_head)
262     return {};
263 
264   CompilerType head_type = nth_head->GetCompilerType();
265 
266   if (!head_type)
267     return {};
268 
269   CompilerType template_type = head_type.GetTypeTemplateArgument(1);
270 
271   if (!template_type)
272     return {};
273 
274   ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value"));
275 
276   if (!head_value)
277     return {};
278 
279   return head_value->Clone(ConstString("Value"));
280 }
281 
282 SyntheticChildrenFrontEnd *
LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)283 formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
284                                          lldb::ValueObjectSP valobj_sp) {
285   if (valobj_sp)
286     return new VariantFrontEnd(*valobj_sp);
287   return nullptr;
288 }
289