/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
23 | | using namespace lldb; |
24 | | using namespace lldb_private; |
25 | | using namespace lldb_private::formatters; |
26 | | |
27 | | namespace lldb_private { |
28 | | namespace formatters { |
29 | | class LibcxxStdUnorderedMapSyntheticFrontEnd |
30 | | : public SyntheticChildrenFrontEnd { |
31 | | public: |
32 | | LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
33 | | |
34 | 14 | ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default; |
35 | | |
36 | | size_t CalculateNumChildren() override; |
37 | | |
38 | | lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; |
39 | | |
40 | | bool Update() override; |
41 | | |
42 | | bool MightHaveChildren() override; |
43 | | |
44 | | size_t GetIndexOfChildWithName(ConstString name) override; |
45 | | |
46 | | private: |
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 | | |
57 | | lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
58 | | LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
59 | 14 | : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), |
60 | 14 | m_elements_cache() { |
61 | 14 | if (valobj_sp) |
62 | 14 | Update(); |
63 | 14 | } |
64 | | |
65 | | size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
66 | 78 | CalculateNumChildren() { |
67 | 78 | return m_num_elements; |
68 | 78 | } |
69 | | |
70 | 4 | static void consumeInlineNamespace(llvm::StringRef &name) { |
71 | | // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+:: |
72 | 4 | auto scratch = name; |
73 | 4 | if (scratch.consume_front("__") && std::isalnum(scratch[0])) { |
74 | 8 | scratch = scratch.drop_while([](char c) { return std::isalnum(c); }); |
75 | 4 | if (scratch.consume_front("::")) { |
76 | | // Successfully consumed a namespace. |
77 | 4 | name = scratch; |
78 | 4 | } |
79 | 4 | } |
80 | 4 | } |
81 | | |
82 | 26 | static bool isStdTemplate(ConstString type_name, llvm::StringRef type) { |
83 | 26 | llvm::StringRef name = type_name.GetStringRef(); |
84 | | // The type name may be prefixed with `std::__<inline-namespace>::`. |
85 | 26 | if (name.consume_front("std::")) |
86 | 4 | consumeInlineNamespace(name); |
87 | 26 | return name.consume_front(type) && name.startswith("<")8 ; |
88 | 26 | } |
89 | | |
90 | 12 | static bool isUnorderedMap(ConstString type_name) { |
91 | 12 | return isStdTemplate(type_name, "unordered_map") || |
92 | 12 | isStdTemplate(type_name, "unordered_multimap")10 ; |
93 | 12 | } |
94 | | |
95 | | lldb::ValueObjectSP lldb_private::formatters:: |
96 | 64 | LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) { |
97 | 64 | if (idx >= CalculateNumChildren()) |
98 | 0 | return lldb::ValueObjectSP(); |
99 | 64 | if (m_tree == nullptr) |
100 | 0 | return lldb::ValueObjectSP(); |
101 | | |
102 | 128 | while (64 idx >= m_elements_cache.size()) { |
103 | 64 | if (m_next_element == nullptr) |
104 | 0 | return lldb::ValueObjectSP(); |
105 | | |
106 | 64 | Status error; |
107 | 64 | ValueObjectSP node_sp = m_next_element->Dereference(error); |
108 | 64 | if (!node_sp || error.Fail()) |
109 | 0 | return lldb::ValueObjectSP(); |
110 | | |
111 | 64 | ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_"); |
112 | 64 | ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_"); |
113 | 64 | if (!hash_sp || !value_sp0 ) { |
114 | 64 | if (!m_element_type) { |
115 | 12 | auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"}); |
116 | 12 | if (!p1_sp) |
117 | 0 | return nullptr; |
118 | | |
119 | 12 | ValueObjectSP first_sp = nullptr; |
120 | 12 | switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) { |
121 | 0 | case 1: |
122 | | // Assume a pre llvm r300140 __compressed_pair implementation: |
123 | 0 | first_sp = p1_sp->GetChildMemberWithName("__first_"); |
124 | 0 | break; |
125 | 12 | case 2: { |
126 | | // Assume a post llvm r300140 __compressed_pair implementation: |
127 | 12 | ValueObjectSP first_elem_parent_sp = |
128 | 12 | p1_sp->GetChildAtIndex(0); |
129 | 12 | first_sp = p1_sp->GetChildMemberWithName("__value_"); |
130 | 12 | break; |
131 | 0 | } |
132 | 0 | default: |
133 | 0 | return nullptr; |
134 | 12 | } |
135 | | |
136 | 12 | if (!first_sp) |
137 | 0 | return nullptr; |
138 | 12 | m_element_type = first_sp->GetCompilerType(); |
139 | 12 | m_element_type = m_element_type.GetTypeTemplateArgument(0); |
140 | 12 | m_element_type = m_element_type.GetPointeeType(); |
141 | 12 | m_node_type = m_element_type; |
142 | 12 | m_element_type = m_element_type.GetTypeTemplateArgument(0); |
143 | | // This synthetic provider is used for both unordered_(multi)map and |
144 | | // unordered_(multi)set. For unordered_map, the element type has an |
145 | | // additional type layer, an internal struct (`__hash_value_type`) |
146 | | // that wraps a std::pair. Peel away the internal wrapper type - whose |
147 | | // structure is of no value to users, to expose the std::pair. This |
148 | | // matches the structure returned by the std::map synthetic provider. |
149 | 12 | if (isUnorderedMap(m_backend.GetTypeName())) { |
150 | 4 | std::string name; |
151 | 4 | CompilerType field_type = m_element_type.GetFieldAtIndex( |
152 | 4 | 0, name, nullptr, nullptr, nullptr); |
153 | 4 | CompilerType actual_type = field_type.GetTypedefedType(); |
154 | 4 | if (isStdTemplate(actual_type.GetTypeName(), "pair")) |
155 | 4 | m_element_type = actual_type; |
156 | 4 | } |
157 | 12 | } |
158 | 64 | if (!m_node_type) |
159 | 0 | return nullptr; |
160 | 64 | node_sp = m_next_element->Cast(m_node_type.GetPointerType()) |
161 | 64 | ->Dereference(error); |
162 | 64 | if (!node_sp || error.Fail()) |
163 | 0 | return nullptr; |
164 | | |
165 | 64 | value_sp = node_sp->GetChildMemberWithName("__value_"); |
166 | 64 | hash_sp = node_sp->GetChildMemberWithName("__hash_"); |
167 | 64 | if (!value_sp || !hash_sp) |
168 | 0 | return nullptr; |
169 | 64 | } |
170 | 64 | m_elements_cache.push_back( |
171 | 64 | {value_sp.get(), hash_sp->GetValueAsUnsigned(0)}); |
172 | 64 | m_next_element = node_sp->GetChildMemberWithName("__next_").get(); |
173 | 64 | if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0) |
174 | 12 | m_next_element = nullptr; |
175 | 64 | } |
176 | | |
177 | 64 | std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx]; |
178 | 64 | if (!val_hash.first) |
179 | 0 | return lldb::ValueObjectSP(); |
180 | 64 | StreamString stream; |
181 | 64 | stream.Printf("[%" PRIu64 "]", (uint64_t)idx); |
182 | 64 | DataExtractor data; |
183 | 64 | Status error; |
184 | 64 | val_hash.first->GetData(data, error); |
185 | 64 | if (error.Fail()) |
186 | 0 | return lldb::ValueObjectSP(); |
187 | 64 | const bool thread_and_frame_only_if_stopped = true; |
188 | 64 | ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock( |
189 | 64 | thread_and_frame_only_if_stopped); |
190 | 64 | return CreateValueObjectFromData(stream.GetString(), data, exe_ctx, |
191 | 64 | m_element_type); |
192 | 64 | } |
193 | | |
194 | | bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
195 | 28 | Update() { |
196 | 28 | m_num_elements = 0; |
197 | 28 | m_next_element = nullptr; |
198 | 28 | m_elements_cache.clear(); |
199 | 28 | ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_"); |
200 | 28 | if (!table_sp) |
201 | 0 | return false; |
202 | | |
203 | 28 | ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_"); |
204 | 28 | ValueObjectSP num_elements_sp = nullptr; |
205 | 28 | llvm::SmallVector<llvm::StringRef, 3> next_path; |
206 | 28 | switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) { |
207 | 0 | case 1: |
208 | | // Assume a pre llvm r300140 __compressed_pair implementation: |
209 | 0 | num_elements_sp = p2_sp->GetChildMemberWithName("__first_"); |
210 | 0 | next_path.append({"__p1_", "__first_", "__next_"}); |
211 | 0 | break; |
212 | 28 | case 2: { |
213 | | // Assume a post llvm r300140 __compressed_pair implementation: |
214 | 28 | ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0); |
215 | 28 | num_elements_sp = first_elem_parent->GetChildMemberWithName("__value_"); |
216 | 28 | next_path.append({"__p1_", "__value_", "__next_"}); |
217 | 28 | break; |
218 | 0 | } |
219 | 0 | default: |
220 | 0 | return false; |
221 | 28 | } |
222 | | |
223 | 28 | if (!num_elements_sp) |
224 | 0 | return false; |
225 | | |
226 | 28 | m_tree = table_sp->GetChildAtNamePath(next_path).get(); |
227 | 28 | if (m_tree == nullptr) |
228 | 0 | return false; |
229 | | |
230 | 28 | m_num_elements = num_elements_sp->GetValueAsUnsigned(0); |
231 | | |
232 | 28 | if (m_num_elements > 0) |
233 | 24 | m_next_element = |
234 | 24 | table_sp->GetChildAtNamePath(next_path).get(); |
235 | 28 | return false; |
236 | 28 | } |
237 | | |
238 | | bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
239 | 2 | MightHaveChildren() { |
240 | 2 | return true; |
241 | 2 | } |
242 | | |
243 | | size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
244 | 0 | GetIndexOfChildWithName(ConstString name) { |
245 | 0 | return ExtractIndexFromString(name.GetCString()); |
246 | 0 | } |
247 | | |
248 | | SyntheticChildrenFrontEnd * |
249 | | lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator( |
250 | 14 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
251 | 14 | return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp) |
252 | 14 | : nullptr0 ); |
253 | 14 | } |