1dda28197Spatrick //===-- LibCxxUnorderedMap.cpp --------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "LibCxx.h"
10061da546Spatrick
11dda28197Spatrick #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12061da546Spatrick #include "lldb/Core/ValueObject.h"
13061da546Spatrick #include "lldb/Core/ValueObjectConstResult.h"
14061da546Spatrick #include "lldb/DataFormatters/FormattersHelpers.h"
15061da546Spatrick #include "lldb/Target/Target.h"
16*f6aab3d8Srobert #include "lldb/Utility/ConstString.h"
17061da546Spatrick #include "lldb/Utility/DataBufferHeap.h"
18061da546Spatrick #include "lldb/Utility/Endian.h"
19061da546Spatrick #include "lldb/Utility/Status.h"
20061da546Spatrick #include "lldb/Utility/Stream.h"
21*f6aab3d8Srobert #include "llvm/ADT/StringRef.h"
22061da546Spatrick
23061da546Spatrick using namespace lldb;
24061da546Spatrick using namespace lldb_private;
25061da546Spatrick using namespace lldb_private::formatters;
26061da546Spatrick
27061da546Spatrick namespace lldb_private {
28061da546Spatrick namespace formatters {
29061da546Spatrick class LibcxxStdUnorderedMapSyntheticFrontEnd
30061da546Spatrick : public SyntheticChildrenFrontEnd {
31061da546Spatrick public:
32061da546Spatrick LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
33061da546Spatrick
34061da546Spatrick ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
35061da546Spatrick
36061da546Spatrick size_t CalculateNumChildren() override;
37061da546Spatrick
38061da546Spatrick lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
39061da546Spatrick
40061da546Spatrick bool Update() override;
41061da546Spatrick
42061da546Spatrick bool MightHaveChildren() override;
43061da546Spatrick
44061da546Spatrick size_t GetIndexOfChildWithName(ConstString name) override;
45061da546Spatrick
46061da546Spatrick private:
47061da546Spatrick CompilerType m_element_type;
48061da546Spatrick CompilerType m_node_type;
49*f6aab3d8Srobert ValueObject *m_tree = nullptr;
50*f6aab3d8Srobert size_t m_num_elements = 0;
51*f6aab3d8Srobert ValueObject *m_next_element = nullptr;
52061da546Spatrick std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
53061da546Spatrick };
54061da546Spatrick } // namespace formatters
55061da546Spatrick } // namespace lldb_private
56061da546Spatrick
57061da546Spatrick lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)58061da546Spatrick LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
59*f6aab3d8Srobert : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(),
60*f6aab3d8Srobert m_elements_cache() {
61061da546Spatrick if (valobj_sp)
62061da546Spatrick Update();
63061da546Spatrick }
64061da546Spatrick
65061da546Spatrick size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
CalculateNumChildren()66061da546Spatrick CalculateNumChildren() {
67061da546Spatrick return m_num_elements;
68*f6aab3d8Srobert }
69*f6aab3d8Srobert
consumeInlineNamespace(llvm::StringRef & name)70*f6aab3d8Srobert static void consumeInlineNamespace(llvm::StringRef &name) {
71*f6aab3d8Srobert // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
72*f6aab3d8Srobert auto scratch = name;
73*f6aab3d8Srobert if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
74*f6aab3d8Srobert scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
75*f6aab3d8Srobert if (scratch.consume_front("::")) {
76*f6aab3d8Srobert // Successfully consumed a namespace.
77*f6aab3d8Srobert name = scratch;
78*f6aab3d8Srobert }
79*f6aab3d8Srobert }
80*f6aab3d8Srobert }
81*f6aab3d8Srobert
isStdTemplate(ConstString type_name,llvm::StringRef type)82*f6aab3d8Srobert static bool isStdTemplate(ConstString type_name, llvm::StringRef type) {
83*f6aab3d8Srobert llvm::StringRef name = type_name.GetStringRef();
84*f6aab3d8Srobert // The type name may be prefixed with `std::__<inline-namespace>::`.
85*f6aab3d8Srobert if (name.consume_front("std::"))
86*f6aab3d8Srobert consumeInlineNamespace(name);
87*f6aab3d8Srobert return name.consume_front(type) && name.startswith("<");
88*f6aab3d8Srobert }
89*f6aab3d8Srobert
isUnorderedMap(ConstString type_name)90*f6aab3d8Srobert static bool isUnorderedMap(ConstString type_name) {
91*f6aab3d8Srobert return isStdTemplate(type_name, "unordered_map") ||
92*f6aab3d8Srobert isStdTemplate(type_name, "unordered_multimap");
93061da546Spatrick }
94061da546Spatrick
95061da546Spatrick lldb::ValueObjectSP lldb_private::formatters::
GetChildAtIndex(size_t idx)96061da546Spatrick LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
97061da546Spatrick if (idx >= CalculateNumChildren())
98061da546Spatrick return lldb::ValueObjectSP();
99061da546Spatrick if (m_tree == nullptr)
100061da546Spatrick return lldb::ValueObjectSP();
101061da546Spatrick
102061da546Spatrick while (idx >= m_elements_cache.size()) {
103061da546Spatrick if (m_next_element == nullptr)
104061da546Spatrick return lldb::ValueObjectSP();
105061da546Spatrick
106061da546Spatrick Status error;
107061da546Spatrick ValueObjectSP node_sp = m_next_element->Dereference(error);
108061da546Spatrick if (!node_sp || error.Fail())
109061da546Spatrick return lldb::ValueObjectSP();
110061da546Spatrick
111061da546Spatrick ValueObjectSP value_sp =
112061da546Spatrick node_sp->GetChildMemberWithName(ConstString("__value_"), true);
113061da546Spatrick ValueObjectSP hash_sp =
114061da546Spatrick node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
115061da546Spatrick if (!hash_sp || !value_sp) {
116061da546Spatrick if (!m_element_type) {
117061da546Spatrick auto p1_sp = m_backend.GetChildAtNamePath({ConstString("__table_"),
118061da546Spatrick ConstString("__p1_")});
119061da546Spatrick if (!p1_sp)
120061da546Spatrick return nullptr;
121061da546Spatrick
122061da546Spatrick ValueObjectSP first_sp = nullptr;
123061da546Spatrick switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) {
124061da546Spatrick case 1:
125061da546Spatrick // Assume a pre llvm r300140 __compressed_pair implementation:
126061da546Spatrick first_sp = p1_sp->GetChildMemberWithName(ConstString("__first_"),
127061da546Spatrick true);
128061da546Spatrick break;
129061da546Spatrick case 2: {
130061da546Spatrick // Assume a post llvm r300140 __compressed_pair implementation:
131061da546Spatrick ValueObjectSP first_elem_parent_sp =
132061da546Spatrick p1_sp->GetChildAtIndex(0, true);
133061da546Spatrick first_sp = p1_sp->GetChildMemberWithName(ConstString("__value_"),
134061da546Spatrick true);
135061da546Spatrick break;
136061da546Spatrick }
137061da546Spatrick default:
138061da546Spatrick return nullptr;
139061da546Spatrick }
140061da546Spatrick
141061da546Spatrick if (!first_sp)
142061da546Spatrick return nullptr;
143061da546Spatrick m_element_type = first_sp->GetCompilerType();
144061da546Spatrick m_element_type = m_element_type.GetTypeTemplateArgument(0);
145061da546Spatrick m_element_type = m_element_type.GetPointeeType();
146061da546Spatrick m_node_type = m_element_type;
147061da546Spatrick m_element_type = m_element_type.GetTypeTemplateArgument(0);
148*f6aab3d8Srobert // This synthetic provider is used for both unordered_(multi)map and
149*f6aab3d8Srobert // unordered_(multi)set. For unordered_map, the element type has an
150*f6aab3d8Srobert // additional type layer, an internal struct (`__hash_value_type`)
151*f6aab3d8Srobert // that wraps a std::pair. Peel away the internal wrapper type - whose
152*f6aab3d8Srobert // structure is of no value to users, to expose the std::pair. This
153*f6aab3d8Srobert // matches the structure returned by the std::map synthetic provider.
154*f6aab3d8Srobert if (isUnorderedMap(m_backend.GetTypeName())) {
155061da546Spatrick std::string name;
156*f6aab3d8Srobert CompilerType field_type = m_element_type.GetFieldAtIndex(
157*f6aab3d8Srobert 0, name, nullptr, nullptr, nullptr);
158*f6aab3d8Srobert CompilerType actual_type = field_type.GetTypedefedType();
159*f6aab3d8Srobert if (isStdTemplate(actual_type.GetTypeName(), "pair"))
160*f6aab3d8Srobert m_element_type = actual_type;
161*f6aab3d8Srobert }
162061da546Spatrick }
163061da546Spatrick if (!m_node_type)
164061da546Spatrick return nullptr;
165061da546Spatrick node_sp = node_sp->Cast(m_node_type);
166061da546Spatrick value_sp = node_sp->GetChildMemberWithName(ConstString("__value_"), true);
167061da546Spatrick hash_sp = node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
168061da546Spatrick if (!value_sp || !hash_sp)
169061da546Spatrick return nullptr;
170061da546Spatrick }
171061da546Spatrick m_elements_cache.push_back(
172061da546Spatrick {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
173061da546Spatrick m_next_element =
174061da546Spatrick node_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
175061da546Spatrick if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
176061da546Spatrick m_next_element = nullptr;
177061da546Spatrick }
178061da546Spatrick
179061da546Spatrick std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
180061da546Spatrick if (!val_hash.first)
181061da546Spatrick return lldb::ValueObjectSP();
182061da546Spatrick StreamString stream;
183061da546Spatrick stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
184061da546Spatrick DataExtractor data;
185061da546Spatrick Status error;
186061da546Spatrick val_hash.first->GetData(data, error);
187061da546Spatrick if (error.Fail())
188061da546Spatrick return lldb::ValueObjectSP();
189061da546Spatrick const bool thread_and_frame_only_if_stopped = true;
190061da546Spatrick ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
191061da546Spatrick thread_and_frame_only_if_stopped);
192061da546Spatrick return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
193*f6aab3d8Srobert m_element_type);
194061da546Spatrick }
195061da546Spatrick
196061da546Spatrick bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
Update()197061da546Spatrick Update() {
198*f6aab3d8Srobert m_num_elements = 0;
199061da546Spatrick m_next_element = nullptr;
200061da546Spatrick m_elements_cache.clear();
201061da546Spatrick ValueObjectSP table_sp =
202061da546Spatrick m_backend.GetChildMemberWithName(ConstString("__table_"), true);
203061da546Spatrick if (!table_sp)
204061da546Spatrick return false;
205061da546Spatrick
206061da546Spatrick ValueObjectSP p2_sp = table_sp->GetChildMemberWithName(
207061da546Spatrick ConstString("__p2_"), true);
208061da546Spatrick ValueObjectSP num_elements_sp = nullptr;
209061da546Spatrick llvm::SmallVector<ConstString, 3> next_path;
210061da546Spatrick switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) {
211061da546Spatrick case 1:
212061da546Spatrick // Assume a pre llvm r300140 __compressed_pair implementation:
213061da546Spatrick num_elements_sp = p2_sp->GetChildMemberWithName(
214061da546Spatrick ConstString("__first_"), true);
215061da546Spatrick next_path.append({ConstString("__p1_"), ConstString("__first_"),
216061da546Spatrick ConstString("__next_")});
217061da546Spatrick break;
218061da546Spatrick case 2: {
219061da546Spatrick // Assume a post llvm r300140 __compressed_pair implementation:
220061da546Spatrick ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0, true);
221061da546Spatrick num_elements_sp = first_elem_parent->GetChildMemberWithName(
222061da546Spatrick ConstString("__value_"), true);
223061da546Spatrick next_path.append({ConstString("__p1_"), ConstString("__value_"),
224061da546Spatrick ConstString("__next_")});
225061da546Spatrick break;
226061da546Spatrick }
227061da546Spatrick default:
228061da546Spatrick return false;
229061da546Spatrick }
230061da546Spatrick
231061da546Spatrick if (!num_elements_sp)
232061da546Spatrick return false;
233*f6aab3d8Srobert
234061da546Spatrick m_tree = table_sp->GetChildAtNamePath(next_path).get();
235*f6aab3d8Srobert if (m_tree == nullptr)
236*f6aab3d8Srobert return false;
237*f6aab3d8Srobert
238*f6aab3d8Srobert m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
239*f6aab3d8Srobert
240061da546Spatrick if (m_num_elements > 0)
241061da546Spatrick m_next_element =
242061da546Spatrick table_sp->GetChildAtNamePath(next_path).get();
243061da546Spatrick return false;
244061da546Spatrick }
245061da546Spatrick
246061da546Spatrick bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
MightHaveChildren()247061da546Spatrick MightHaveChildren() {
248061da546Spatrick return true;
249061da546Spatrick }
250061da546Spatrick
251061da546Spatrick size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)252061da546Spatrick GetIndexOfChildWithName(ConstString name) {
253061da546Spatrick return ExtractIndexFromString(name.GetCString());
254061da546Spatrick }
255061da546Spatrick
256061da546Spatrick SyntheticChildrenFrontEnd *
LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)257061da546Spatrick lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
258061da546Spatrick CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
259061da546Spatrick return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
260061da546Spatrick : nullptr);
261061da546Spatrick }
262