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/DataBufferHeap.h"
17 #include "lldb/Utility/Endian.h"
18 #include "lldb/Utility/Status.h"
19 #include "lldb/Utility/Stream.h"
20
21 using namespace lldb;
22 using namespace lldb_private;
23 using namespace lldb_private::formatters;
24
25 namespace lldb_private {
26 namespace formatters {
27 class LibcxxStdUnorderedMapSyntheticFrontEnd
28 : public SyntheticChildrenFrontEnd {
29 public:
30 LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
31
32 ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
33
34 size_t CalculateNumChildren() override;
35
36 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
37
38 bool Update() override;
39
40 bool MightHaveChildren() override;
41
42 size_t GetIndexOfChildWithName(ConstString name) override;
43
44 private:
45 CompilerType m_element_type;
46 CompilerType m_node_type;
47 ValueObject *m_tree = nullptr;
48 size_t m_num_elements = 0;
49 ValueObject *m_next_element = nullptr;
50 std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
51 };
52 } // namespace formatters
53 } // namespace lldb_private
54
55 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)56 LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
57 : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(),
58 m_elements_cache() {
59 if (valobj_sp)
60 Update();
61 }
62
63 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
CalculateNumChildren()64 CalculateNumChildren() {
65 return m_num_elements;
66 }
67
68 lldb::ValueObjectSP lldb_private::formatters::
GetChildAtIndex(size_t idx)69 LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
70 if (idx >= CalculateNumChildren())
71 return lldb::ValueObjectSP();
72 if (m_tree == nullptr)
73 return lldb::ValueObjectSP();
74
75 while (idx >= m_elements_cache.size()) {
76 if (m_next_element == nullptr)
77 return lldb::ValueObjectSP();
78
79 Status error;
80 ValueObjectSP node_sp = m_next_element->Dereference(error);
81 if (!node_sp || error.Fail())
82 return lldb::ValueObjectSP();
83
84 ValueObjectSP value_sp =
85 node_sp->GetChildMemberWithName(ConstString("__value_"), true);
86 ValueObjectSP hash_sp =
87 node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
88 if (!hash_sp || !value_sp) {
89 if (!m_element_type) {
90 auto p1_sp = m_backend.GetChildAtNamePath({ConstString("__table_"),
91 ConstString("__p1_")});
92 if (!p1_sp)
93 return nullptr;
94
95 ValueObjectSP first_sp = nullptr;
96 switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) {
97 case 1:
98 // Assume a pre llvm r300140 __compressed_pair implementation:
99 first_sp = p1_sp->GetChildMemberWithName(ConstString("__first_"),
100 true);
101 break;
102 case 2: {
103 // Assume a post llvm r300140 __compressed_pair implementation:
104 ValueObjectSP first_elem_parent_sp =
105 p1_sp->GetChildAtIndex(0, true);
106 first_sp = p1_sp->GetChildMemberWithName(ConstString("__value_"),
107 true);
108 break;
109 }
110 default:
111 return nullptr;
112 }
113
114 if (!first_sp)
115 return nullptr;
116 m_element_type = first_sp->GetCompilerType();
117 m_element_type = m_element_type.GetTypeTemplateArgument(0);
118 m_element_type = m_element_type.GetPointeeType();
119 m_node_type = m_element_type;
120 m_element_type = m_element_type.GetTypeTemplateArgument(0);
121 std::string name;
122 m_element_type =
123 m_element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
124 m_element_type = m_element_type.GetTypedefedType();
125 }
126 if (!m_node_type)
127 return nullptr;
128 node_sp = node_sp->Cast(m_node_type);
129 value_sp = node_sp->GetChildMemberWithName(ConstString("__value_"), true);
130 hash_sp = node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
131 if (!value_sp || !hash_sp)
132 return nullptr;
133 }
134 m_elements_cache.push_back(
135 {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
136 m_next_element =
137 node_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
138 if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
139 m_next_element = nullptr;
140 }
141
142 std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
143 if (!val_hash.first)
144 return lldb::ValueObjectSP();
145 StreamString stream;
146 stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
147 DataExtractor data;
148 Status error;
149 val_hash.first->GetData(data, error);
150 if (error.Fail())
151 return lldb::ValueObjectSP();
152 const bool thread_and_frame_only_if_stopped = true;
153 ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
154 thread_and_frame_only_if_stopped);
155 return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
156 val_hash.first->GetCompilerType());
157 }
158
159 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
Update()160 Update() {
161 m_num_elements = 0;
162 m_next_element = nullptr;
163 m_elements_cache.clear();
164 ValueObjectSP table_sp =
165 m_backend.GetChildMemberWithName(ConstString("__table_"), true);
166 if (!table_sp)
167 return false;
168
169 ValueObjectSP p2_sp = table_sp->GetChildMemberWithName(
170 ConstString("__p2_"), true);
171 ValueObjectSP num_elements_sp = nullptr;
172 llvm::SmallVector<ConstString, 3> next_path;
173 switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) {
174 case 1:
175 // Assume a pre llvm r300140 __compressed_pair implementation:
176 num_elements_sp = p2_sp->GetChildMemberWithName(
177 ConstString("__first_"), true);
178 next_path.append({ConstString("__p1_"), ConstString("__first_"),
179 ConstString("__next_")});
180 break;
181 case 2: {
182 // Assume a post llvm r300140 __compressed_pair implementation:
183 ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0, true);
184 num_elements_sp = first_elem_parent->GetChildMemberWithName(
185 ConstString("__value_"), true);
186 next_path.append({ConstString("__p1_"), ConstString("__value_"),
187 ConstString("__next_")});
188 break;
189 }
190 default:
191 return false;
192 }
193
194 if (!num_elements_sp)
195 return false;
196
197 m_tree = table_sp->GetChildAtNamePath(next_path).get();
198 if (m_tree == nullptr)
199 return false;
200
201 m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
202
203 if (m_num_elements > 0)
204 m_next_element =
205 table_sp->GetChildAtNamePath(next_path).get();
206 return false;
207 }
208
209 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
MightHaveChildren()210 MightHaveChildren() {
211 return true;
212 }
213
214 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)215 GetIndexOfChildWithName(ConstString name) {
216 return ExtractIndexFromString(name.GetCString());
217 }
218
219 SyntheticChildrenFrontEnd *
LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)220 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
221 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
222 return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
223 : nullptr);
224 }
225