LibCxxVariant.cpp revision 360784
1//===-- LibCxxVariant.cpp --------------------------------------*- C++ -*-===//
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
15using namespace lldb;
16using 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
61namespace {
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
66enum class LibcxxVariantIndexValidity { VALID, INVALID, NPOS };
67
68LibcxxVariantIndexValidity
69LibcxxVariantGetIndexValidity(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
84llvm::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
94ValueObjectSP 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
116namespace lldb_private {
117namespace formatters {
118bool 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.Printf(" Active Type = %s ", template_type.GetTypeName().GetCString());
163
164  return true;
165}
166} // namespace formatters
167} // namespace lldb_private
168
169namespace {
170class VariantFrontEnd : public SyntheticChildrenFrontEnd {
171public:
172  VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
173    Update();
174  }
175
176  size_t GetIndexOfChildWithName(ConstString name) override {
177    return formatters::ExtractIndexFromString(name.GetCString());
178  }
179
180  bool MightHaveChildren() override { return true; }
181  bool Update() override;
182  size_t CalculateNumChildren() override { return m_size; }
183  ValueObjectSP GetChildAtIndex(size_t idx) override;
184
185private:
186  size_t m_size = 0;
187};
188} // namespace
189
190bool 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
210ValueObjectSP 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(ConstString("Value").AsCString()));
246}
247
248SyntheticChildrenFrontEnd *
249formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
250                                         lldb::ValueObjectSP valobj_sp) {
251  if (valobj_sp)
252    return new VariantFrontEnd(*valobj_sp);
253  return nullptr;
254}
255