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 | |
16 | using namespace lldb; |
17 | using namespace lldb_private; |
18 | |
19 | namespace { |
20 | |
21 | /// This class can be used for handling bitsets from both libcxx and libstdcpp. |
22 | class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { |
23 | public: |
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 | |
46 | private: |
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 | |
65 | GenericBitsetFrontEnd::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 | |
75 | llvm::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 | |
87 | lldb::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 | |
106 | ValueObjectSP 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 | |
144 | SyntheticChildrenFrontEnd *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 | |
152 | SyntheticChildrenFrontEnd *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 | |