xref: /openbsd-src/gnu/llvm/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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