xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
GetIndexOfChildWithName(ConstString name)31   llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
32     auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
33     if (!optional_idx) {
34       return llvm::createStringError("Type has no child named '%s'",
35                                      name.AsCString());
36     }
37     return *optional_idx;
38   }
39 
40   lldb::ChildCacheState Update() override;
CalculateNumChildren()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 
GenericBitsetFrontEnd(ValueObject & valobj,StdLib stdlib)65 GenericBitsetFrontEnd::GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib)
66     : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) {
67   m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(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 
GetDataContainerMemberName()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 
Update()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(0))
98     size = arg->value.GetAPSInt().getLimitedValue();
99 
100   m_elements.assign(size, ValueObjectSP());
101   m_first =
102       m_backend.GetChildMemberWithName(GetDataContainerMemberName()).get();
103   return lldb::ChildCacheState::eRefetch;
104 }
105 
GetChildAtIndex(uint32_t idx)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(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(&type)) {
118     std::optional<uint64_t> bit_size = llvm::expectedToOptional(
119         type.GetBitSize(ctx.GetBestExecutionContextScope()));
120     if (!bit_size || *bit_size == 0)
121       return {};
122     chunk = m_first->GetChildAtIndex(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       type.GetBitSize(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(0) & (uint64_t(1) << chunk_idx));
136   DataExtractor data(&value, sizeof(value), m_byte_order, m_byte_size);
137 
138   m_elements[idx] = CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(),
139                                               data, ctx, m_bool_type);
140 
141   return m_elements[idx];
142 }
143 
LibStdcppBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)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 
LibcxxBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)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