1//===-- LibCxxUnorderedMap.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
11#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12#include "lldb/Core/ValueObject.h"
13#include "lldb/Core/ValueObjectConstResult.h"
14#include "lldb/DataFormatters/FormattersHelpers.h"
15#include "lldb/Target/Target.h"
16#include "lldb/Utility/ConstString.h"
17#include "lldb/Utility/DataBufferHeap.h"
18#include "lldb/Utility/Endian.h"
19#include "lldb/Utility/Status.h"
20#include "lldb/Utility/Stream.h"
21#include "llvm/ADT/StringRef.h"
22
23using namespace lldb;
24using namespace lldb_private;
25using namespace lldb_private::formatters;
26
27namespace lldb_private {
28namespace formatters {
29class LibcxxStdUnorderedMapSyntheticFrontEnd
30 : public SyntheticChildrenFrontEnd {
31public:
32 LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
33
34 ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
35
36 llvm::Expected<uint32_t> CalculateNumChildren() override;
37
38 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
39
40 lldb::ChildCacheState Update() override;
41
42 bool MightHaveChildren() override;
43
44 size_t GetIndexOfChildWithName(ConstString name) override;
45
46private:
47 CompilerType m_element_type;
48 CompilerType m_node_type;
49 ValueObject *m_tree = nullptr;
50 size_t m_num_elements = 0;
51 ValueObject *m_next_element = nullptr;
52 std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
53};
54} // namespace formatters
55} // namespace lldb_private
56
57lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
58 LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
59 : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(),
60 m_elements_cache() {
61 if (valobj_sp)
62 Update();
63}
64
65llvm::Expected<uint32_t> lldb_private::formatters::
66 LibcxxStdUnorderedMapSyntheticFrontEnd::CalculateNumChildren() {
67 return m_num_elements;
68}
69
70static void consumeInlineNamespace(llvm::StringRef &name) {
71 // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
72 auto scratch = name;
73 if (scratch.consume_front(Prefix: "__") && std::isalnum(scratch[0])) {
74 scratch = scratch.drop_while(F: [](char c) { return std::isalnum(c); });
75 if (scratch.consume_front(Prefix: "::")) {
76 // Successfully consumed a namespace.
77 name = scratch;
78 }
79 }
80}
81
82static bool isStdTemplate(ConstString type_name, llvm::StringRef type) {
83 llvm::StringRef name = type_name.GetStringRef();
84 // The type name may be prefixed with `std::__<inline-namespace>::`.
85 if (name.consume_front(Prefix: "std::"))
86 consumeInlineNamespace(name);
87 return name.consume_front(Prefix: type) && name.starts_with(Prefix: "<");
88}
89
90static bool isUnorderedMap(ConstString type_name) {
91 return isStdTemplate(type_name, type: "unordered_map") ||
92 isStdTemplate(type_name, type: "unordered_multimap");
93}
94
95lldb::ValueObjectSP lldb_private::formatters::
96 LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
97 if (idx >= CalculateNumChildrenIgnoringErrors())
98 return lldb::ValueObjectSP();
99 if (m_tree == nullptr)
100 return lldb::ValueObjectSP();
101
102 while (idx >= m_elements_cache.size()) {
103 if (m_next_element == nullptr)
104 return lldb::ValueObjectSP();
105
106 Status error;
107 ValueObjectSP node_sp = m_next_element->Dereference(error);
108 if (!node_sp || error.Fail())
109 return lldb::ValueObjectSP();
110
111 ValueObjectSP value_sp = node_sp->GetChildMemberWithName(name: "__value_");
112 ValueObjectSP hash_sp = node_sp->GetChildMemberWithName(name: "__hash_");
113 if (!hash_sp || !value_sp) {
114 if (!m_element_type) {
115 auto p1_sp = m_backend.GetChildAtNamePath(names: {"__table_", "__p1_"});
116 if (!p1_sp)
117 return nullptr;
118
119 ValueObjectSP first_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *p1_sp);
120 if (!first_sp)
121 return nullptr;
122
123 m_element_type = first_sp->GetCompilerType();
124 m_element_type = m_element_type.GetTypeTemplateArgument(idx: 0);
125 m_element_type = m_element_type.GetPointeeType();
126 m_node_type = m_element_type;
127 m_element_type = m_element_type.GetTypeTemplateArgument(idx: 0);
128 // This synthetic provider is used for both unordered_(multi)map and
129 // unordered_(multi)set. For unordered_map, the element type has an
130 // additional type layer, an internal struct (`__hash_value_type`)
131 // that wraps a std::pair. Peel away the internal wrapper type - whose
132 // structure is of no value to users, to expose the std::pair. This
133 // matches the structure returned by the std::map synthetic provider.
134 if (isUnorderedMap(type_name: m_backend.GetTypeName())) {
135 std::string name;
136 CompilerType field_type = m_element_type.GetFieldAtIndex(
137 idx: 0, name, bit_offset_ptr: nullptr, bitfield_bit_size_ptr: nullptr, is_bitfield_ptr: nullptr);
138 CompilerType actual_type = field_type.GetTypedefedType();
139 if (isStdTemplate(type_name: actual_type.GetTypeName(), type: "pair"))
140 m_element_type = actual_type;
141 }
142 }
143 if (!m_node_type)
144 return nullptr;
145 node_sp = m_next_element->Cast(compiler_type: m_node_type.GetPointerType())
146 ->Dereference(error);
147 if (!node_sp || error.Fail())
148 return nullptr;
149
150 hash_sp = node_sp->GetChildMemberWithName(name: "__hash_");
151 if (!hash_sp)
152 return nullptr;
153
154 value_sp = node_sp->GetChildMemberWithName(name: "__value_");
155 if (!value_sp) {
156 // clang-format off
157 // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an
158 // anonymous union.
159 // Child 0: __hash_node_base base class
160 // Child 1: __hash_
161 // Child 2: anonymous union
162 // clang-format on
163 auto anon_union_sp = node_sp->GetChildAtIndex(idx: 2);
164 if (!anon_union_sp)
165 return nullptr;
166
167 value_sp = anon_union_sp->GetChildMemberWithName(name: "__value_");
168 if (!value_sp)
169 return nullptr;
170 }
171 }
172 m_elements_cache.push_back(
173 x: {value_sp.get(), hash_sp->GetValueAsUnsigned(fail_value: 0)});
174 m_next_element = node_sp->GetChildMemberWithName(name: "__next_").get();
175 if (!m_next_element || m_next_element->GetValueAsUnsigned(fail_value: 0) == 0)
176 m_next_element = nullptr;
177 }
178
179 std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
180 if (!val_hash.first)
181 return lldb::ValueObjectSP();
182 StreamString stream;
183 stream.Printf(format: "[%" PRIu64 "]", (uint64_t)idx);
184 DataExtractor data;
185 Status error;
186 val_hash.first->GetData(data, error);
187 if (error.Fail())
188 return lldb::ValueObjectSP();
189 const bool thread_and_frame_only_if_stopped = true;
190 ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
191 thread_and_frame_only_if_stopped);
192 return CreateValueObjectFromData(name: stream.GetString(), data, exe_ctx,
193 type: m_element_type);
194}
195
196lldb::ChildCacheState
197lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() {
198 m_num_elements = 0;
199 m_next_element = nullptr;
200 m_elements_cache.clear();
201 ValueObjectSP table_sp = m_backend.GetChildMemberWithName(name: "__table_");
202 if (!table_sp)
203 return lldb::ChildCacheState::eRefetch;
204
205 ValueObjectSP p2_sp = table_sp->GetChildMemberWithName(name: "__p2_");
206 if (!p2_sp)
207 return lldb::ChildCacheState::eRefetch;
208
209 ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *p2_sp);
210 if (!num_elements_sp)
211 return lldb::ChildCacheState::eRefetch;
212
213 ValueObjectSP p1_sp = table_sp->GetChildMemberWithName(name: "__p1_");
214 if (!p1_sp)
215 return lldb::ChildCacheState::eRefetch;
216
217 ValueObjectSP value_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *p1_sp);
218 if (!value_sp)
219 return lldb::ChildCacheState::eRefetch;
220
221 m_tree = value_sp->GetChildMemberWithName(name: "__next_").get();
222 if (m_tree == nullptr)
223 return lldb::ChildCacheState::eRefetch;
224
225 m_num_elements = num_elements_sp->GetValueAsUnsigned(fail_value: 0);
226
227 if (m_num_elements > 0)
228 m_next_element = m_tree;
229
230 return lldb::ChildCacheState::eRefetch;
231}
232
233bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
234 MightHaveChildren() {
235 return true;
236}
237
238size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
239 GetIndexOfChildWithName(ConstString name) {
240 return ExtractIndexFromString(item_name: name.GetCString());
241}
242
243SyntheticChildrenFrontEnd *
244lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
245 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
246 return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
247 : nullptr);
248}
249

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