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

source code of lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp