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