xref: /llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp (revision cb463c34dd4c3ad2ac6c13f98edcf684a3fcbe38)
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   ~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     : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(),
60       m_elements_cache() {
61   if (valobj_sp)
62     Update();
63 }
64 
65 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
66     CalculateNumChildren() {
67   return m_num_elements;
68 }
69 
70 static 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("__") && std::isalnum(scratch[0])) {
74     scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
75     if (scratch.consume_front("::")) {
76       // Successfully consumed a namespace.
77       name = scratch;
78     }
79   }
80 }
81 
82 static 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("std::"))
86     consumeInlineNamespace(name);
87   return name.consume_front(type) && name.startswith("<");
88 }
89 
90 static bool isUnorderedMap(ConstString type_name) {
91   return isStdTemplate(type_name, "unordered_map") ||
92          isStdTemplate(type_name, "unordered_multimap");
93 }
94 
95 lldb::ValueObjectSP lldb_private::formatters::
96     LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
97   if (idx >= CalculateNumChildren())
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("__value_", true);
112     ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_", true);
113     if (!hash_sp || !value_sp) {
114       if (!m_element_type) {
115         auto p1_sp = m_backend.GetChildAtNamePath({ConstString("__table_"),
116                                                    ConstString("__p1_")});
117         if (!p1_sp)
118           return nullptr;
119 
120         ValueObjectSP first_sp = nullptr;
121         switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) {
122         case 1:
123           // Assume a pre llvm r300140 __compressed_pair implementation:
124           first_sp = p1_sp->GetChildMemberWithName("__first_", true);
125           break;
126         case 2: {
127           // Assume a post llvm r300140 __compressed_pair implementation:
128           ValueObjectSP first_elem_parent_sp =
129             p1_sp->GetChildAtIndex(0, true);
130           first_sp = p1_sp->GetChildMemberWithName("__value_", true);
131           break;
132         }
133         default:
134           return nullptr;
135         }
136 
137         if (!first_sp)
138           return nullptr;
139         m_element_type = first_sp->GetCompilerType();
140         m_element_type = m_element_type.GetTypeTemplateArgument(0);
141         m_element_type = m_element_type.GetPointeeType();
142         m_node_type = m_element_type;
143         m_element_type = m_element_type.GetTypeTemplateArgument(0);
144         // This synthetic provider is used for both unordered_(multi)map and
145         // unordered_(multi)set. For unordered_map, the element type has an
146         // additional type layer, an internal struct (`__hash_value_type`)
147         // that wraps a std::pair. Peel away the internal wrapper type - whose
148         // structure is of no value to users, to expose the std::pair. This
149         // matches the structure returned by the std::map synthetic provider.
150         if (isUnorderedMap(m_backend.GetTypeName())) {
151           std::string name;
152           CompilerType field_type = m_element_type.GetFieldAtIndex(
153               0, name, nullptr, nullptr, nullptr);
154           CompilerType actual_type = field_type.GetTypedefedType();
155           if (isStdTemplate(actual_type.GetTypeName(), "pair"))
156             m_element_type = actual_type;
157         }
158       }
159       if (!m_node_type)
160         return nullptr;
161       node_sp = node_sp->Cast(m_node_type);
162       value_sp = node_sp->GetChildMemberWithName("__value_", true);
163       hash_sp = node_sp->GetChildMemberWithName("__hash_", true);
164       if (!value_sp || !hash_sp)
165         return nullptr;
166     }
167     m_elements_cache.push_back(
168         {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
169     m_next_element = node_sp->GetChildMemberWithName("__next_", true).get();
170     if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
171       m_next_element = nullptr;
172   }
173 
174   std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
175   if (!val_hash.first)
176     return lldb::ValueObjectSP();
177   StreamString stream;
178   stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
179   DataExtractor data;
180   Status error;
181   val_hash.first->GetData(data, error);
182   if (error.Fail())
183     return lldb::ValueObjectSP();
184   const bool thread_and_frame_only_if_stopped = true;
185   ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
186       thread_and_frame_only_if_stopped);
187   return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
188                                    m_element_type);
189 }
190 
191 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
192     Update() {
193   m_num_elements = 0;
194   m_next_element = nullptr;
195   m_elements_cache.clear();
196   ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_", true);
197   if (!table_sp)
198     return false;
199 
200   ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_", true);
201   ValueObjectSP num_elements_sp = nullptr;
202   llvm::SmallVector<ConstString, 3> next_path;
203   switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) {
204   case 1:
205     // Assume a pre llvm r300140 __compressed_pair implementation:
206     num_elements_sp = p2_sp->GetChildMemberWithName("__first_", true);
207     next_path.append({ConstString("__p1_"), ConstString("__first_"),
208                       ConstString("__next_")});
209     break;
210   case 2: {
211     // Assume a post llvm r300140 __compressed_pair implementation:
212     ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0, true);
213     num_elements_sp =
214         first_elem_parent->GetChildMemberWithName("__value_", true);
215     next_path.append({ConstString("__p1_"), ConstString("__value_"),
216                       ConstString("__next_")});
217     break;
218   }
219   default:
220     return false;
221   }
222 
223   if (!num_elements_sp)
224     return false;
225 
226   m_tree = table_sp->GetChildAtNamePath(next_path).get();
227   if (m_tree == nullptr)
228     return false;
229 
230   m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
231 
232   if (m_num_elements > 0)
233     m_next_element =
234         table_sp->GetChildAtNamePath(next_path).get();
235   return false;
236 }
237 
238 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
239     MightHaveChildren() {
240   return true;
241 }
242 
243 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
244     GetIndexOfChildWithName(ConstString name) {
245   return ExtractIndexFromString(name.GetCString());
246 }
247 
248 SyntheticChildrenFrontEnd *
249 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
250     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
251   return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
252                     : nullptr);
253 }
254