Coverage Report

Created: 2023-11-11 10:31

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
Line
Count
Source (jump to first uncovered line)
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
71
54
uint64_t VariantNposValue(uint64_t index_byte_size) {
72
54
  switch (index_byte_size) {
73
0
  case 1:
74
0
    return static_cast<uint8_t>(-1);
75
0
  case 2:
76
0
    return static_cast<uint16_t>(-1);
77
54
  case 4:
78
54
    return static_cast<uint32_t>(-1);
79
54
  }
80
0
  lldbassert(false && "Unknown index type size");
81
0
  return static_cast<uint32_t>(-1); // Fallback to stable ABI type.
82
0
}
83
84
LibcxxVariantIndexValidity
85
54
LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
86
54
  ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
87
88
54
  if (!index_sp)
89
0
    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
54
  CompilerType index_type = index_sp->GetCompilerType();
99
100
54
  std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr);
101
54
  if (!index_type_bytes)
102
0
    return LibcxxVariantIndexValidity::Invalid;
103
104
54
  uint64_t npos_value = VariantNposValue(*index_type_bytes);
105
54
  uint64_t index_value = index_sp->GetValueAsUnsigned(0);
106
107
54
  if (index_value == npos_value)
108
12
    return LibcxxVariantIndexValidity::NPos;
109
110
42
  return LibcxxVariantIndexValidity::Valid;
111
54
}
112
113
28
std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
114
28
  ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
115
116
28
  if (!index_sp)
117
0
    return {};
118
119
28
  return {index_sp->GetValueAsUnsigned(0)};
120
28
}
121
122
28
ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
123
28
  ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data"));
124
125
28
  if (!data_sp)
126
0
    return ValueObjectSP{};
127
128
28
  ValueObjectSP current_level = data_sp;
129
44
  for (uint64_t n = index; n != 0; 
--n16
) {
130
16
    ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail"));
131
132
16
    if (!tail_sp)
133
0
      return ValueObjectSP{};
134
135
16
    current_level = tail_sp;
136
16
  }
137
138
28
  return current_level->GetChildMemberWithName("__head");
139
28
}
140
} // namespace
141
142
namespace lldb_private {
143
namespace formatters {
144
bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
145
18
                                  const TypeSummaryOptions &options) {
146
18
  ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
147
18
  if (!valobj_sp)
148
0
    return false;
149
150
18
  ValueObjectSP impl_sp = GetChildMemberWithName(
151
18
      *valobj_sp, {ConstString("__impl_"), ConstString("__impl")});
152
153
18
  if (!impl_sp)
154
0
    return false;
155
156
18
  LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
157
158
18
  if (validity == LibcxxVariantIndexValidity::Invalid)
159
0
    return false;
160
161
18
  if (validity == LibcxxVariantIndexValidity::NPos) {
162
4
    stream.Printf(" No Value");
163
4
    return true;
164
4
  }
165
166
14
  auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
167
168
14
  if (!optional_index_value)
169
0
    return false;
170
171
14
  uint64_t index_value = *optional_index_value;
172
173
14
  ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
174
175
14
  if (!nth_head)
176
0
    return false;
177
178
14
  CompilerType head_type = nth_head->GetCompilerType();
179
180
14
  if (!head_type)
181
0
    return false;
182
183
14
  CompilerType template_type = head_type.GetTypeTemplateArgument(1);
184
185
14
  if (!template_type)
186
0
    return false;
187
188
14
  stream << " Active Type = " << template_type.GetDisplayTypeName() << " ";
189
190
14
  return true;
191
14
}
192
} // namespace formatters
193
} // namespace lldb_private
194
195
namespace {
196
class VariantFrontEnd : public SyntheticChildrenFrontEnd {
197
public:
198
18
  VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
199
18
    Update();
200
18
  }
201
202
0
  size_t GetIndexOfChildWithName(ConstString name) override {
203
0
    return formatters::ExtractIndexFromString(name.GetCString());
204
0
  }
205
206
4
  bool MightHaveChildren() override { return true; }
207
  bool Update() override;
208
18
  size_t CalculateNumChildren() override { return m_size; }
209
  ValueObjectSP GetChildAtIndex(size_t idx) override;
210
211
private:
212
  size_t m_size = 0;
213
};
214
} // namespace
215
216
36
bool VariantFrontEnd::Update() {
217
36
  m_size = 0;
218
36
  ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
219
36
      m_backend, {ConstString("__impl_"), ConstString("__impl")});
220
36
  if (!impl_sp)
221
0
    return false;
222
223
36
  LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
224
225
36
  if (validity == LibcxxVariantIndexValidity::Invalid)
226
0
    return false;
227
228
36
  if (validity == LibcxxVariantIndexValidity::NPos)
229
8
    return true;
230
231
28
  m_size = 1;
232
233
28
  return false;
234
36
}
235
236
14
ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) {
237
14
  if (idx >= m_size)
238
0
    return {};
239
240
14
  ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
241
14
      m_backend, {ConstString("__impl_"), ConstString("__impl")});
242
14
  if (!impl_sp)
243
0
    return {};
244
245
14
  auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
246
247
14
  if (!optional_index_value)
248
0
    return {};
249
250
14
  uint64_t index_value = *optional_index_value;
251
252
14
  ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
253
254
14
  if (!nth_head)
255
0
    return {};
256
257
14
  CompilerType head_type = nth_head->GetCompilerType();
258
259
14
  if (!head_type)
260
0
    return {};
261
262
14
  CompilerType template_type = head_type.GetTypeTemplateArgument(1);
263
264
14
  if (!template_type)
265
0
    return {};
266
267
14
  ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value"));
268
269
14
  if (!head_value)
270
0
    return {};
271
272
14
  return head_value->Clone(ConstString("Value"));
273
14
}
274
275
SyntheticChildrenFrontEnd *
276
formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
277
18
                                         lldb::ValueObjectSP valobj_sp) {
278
18
  if (valobj_sp)
279
18
    return new VariantFrontEnd(*valobj_sp);
280
0
  return nullptr;
281
18
}