1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
//===-- GenericBitset.cpp //-----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "LibCxx.h"
#include "LibStdcpp.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/Target/Target.h"
#include <optional>
using namespace lldb;
using namespace lldb_private;
namespace {
/// This class can be used for handling bitsets from both libcxx and libstdcpp.
class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd {
public:
enum class StdLib {
LibCxx,
LibStdcpp,
};
GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib);
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
if (!optional_idx) {
return llvm::createStringError("Type has no child named '%s'",
name.AsCString());
}
return *optional_idx;
}
lldb::ChildCacheState Update() override;
llvm::Expected<uint32_t> CalculateNumChildren() override {
return m_elements.size();
}
ValueObjectSP GetChildAtIndex(uint32_t idx) override;
private:
llvm::StringRef GetDataContainerMemberName();
// The lifetime of a ValueObject and all its derivative ValueObjects
// (children, clones, etc.) is managed by a ClusterManager. These
// objects are only destroyed when every shared pointer to any of them
// is destroyed, so we must not store a shared pointer to any ValueObject
// derived from our backend ValueObject (since we're in the same cluster).
// Value objects created from raw data (i.e. in a different cluster) must
// be referenced via shared pointer to keep them alive, however.
std::vector<ValueObjectSP> m_elements;
ValueObject *m_first = nullptr;
CompilerType m_bool_type;
ByteOrder m_byte_order = eByteOrderInvalid;
uint8_t m_byte_size = 0;
StdLib m_stdlib;
};
} // namespace
GenericBitsetFrontEnd::GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib)
: SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) {
m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeBool);
if (auto target_sp = m_backend.GetTargetSP()) {
m_byte_order = target_sp->GetArchitecture().GetByteOrder();
m_byte_size = target_sp->GetArchitecture().GetAddressByteSize();
Update();
}
}
llvm::StringRef GenericBitsetFrontEnd::GetDataContainerMemberName() {
static constexpr llvm::StringLiteral s_libcxx_case("__first_");
static constexpr llvm::StringLiteral s_libstdcpp_case("_M_w");
switch (m_stdlib) {
case StdLib::LibCxx:
return s_libcxx_case;
case StdLib::LibStdcpp:
return s_libstdcpp_case;
}
llvm_unreachable("Unknown StdLib enum");
}
lldb::ChildCacheState GenericBitsetFrontEnd::Update() {
m_elements.clear();
m_first = nullptr;
TargetSP target_sp = m_backend.GetTargetSP();
if (!target_sp)
return lldb::ChildCacheState::eRefetch;
size_t size = 0;
if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0))
size = arg->value.GetAPSInt().getLimitedValue();
m_elements.assign(size, ValueObjectSP());
m_first =
m_backend.GetChildMemberWithName(GetDataContainerMemberName()).get();
return lldb::ChildCacheState::eRefetch;
}
ValueObjectSP GenericBitsetFrontEnd::GetChildAtIndex(uint32_t idx) {
if (idx >= m_elements.size() || !m_first)
return ValueObjectSP();
if (m_elements[idx])
return m_elements[idx];
ExecutionContext ctx = m_backend.GetExecutionContextRef().Lock(false);
CompilerType type;
ValueObjectSP chunk;
// For small bitsets __first_ is not an array, but a plain size_t.
if (m_first->GetCompilerType().IsArrayType(&type)) {
std::optional<uint64_t> bit_size = llvm::expectedToOptional(
type.GetBitSize(ctx.GetBestExecutionContextScope()));
if (!bit_size || *bit_size == 0)
return {};
chunk = m_first->GetChildAtIndex(idx / *bit_size);
} else {
type = m_first->GetCompilerType();
chunk = m_first->GetSP();
}
if (!type || !chunk)
return {};
std::optional<uint64_t> bit_size = llvm::expectedToOptional(
type.GetBitSize(ctx.GetBestExecutionContextScope()));
if (!bit_size || *bit_size == 0)
return {};
size_t chunk_idx = idx % *bit_size;
uint8_t value = !!(chunk->GetValueAsUnsigned(0) & (uint64_t(1) << chunk_idx));
DataExtractor data(&value, sizeof(value), m_byte_order, m_byte_size);
m_elements[idx] = CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(),
data, ctx, m_bool_type);
return m_elements[idx];
}
SyntheticChildrenFrontEnd *formatters::LibStdcppBitsetSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
if (valobj_sp)
return new GenericBitsetFrontEnd(*valobj_sp,
GenericBitsetFrontEnd::StdLib::LibStdcpp);
return nullptr;
}
SyntheticChildrenFrontEnd *formatters::LibcxxBitsetSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
if (valobj_sp)
return new GenericBitsetFrontEnd(*valobj_sp,
GenericBitsetFrontEnd::StdLib::LibCxx);
return nullptr;
}
|