1 //===-- LibCxxUnorderedMap.cpp ----------------------------------*- C++ -*-===//
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 "lldb/Core/ValueObject.h"
12 #include "lldb/Core/ValueObjectConstResult.h"
13 #include "lldb/DataFormatters/FormattersHelpers.h"
14 #include "lldb/Symbol/ClangASTContext.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::
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::
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::
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::
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::
207     MightHaveChildren() {
208   return true;
209 }
210 
211 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
212     GetIndexOfChildWithName(ConstString name) {
213   return ExtractIndexFromString(name.GetCString());
214 }
215 
216 SyntheticChildrenFrontEnd *
217 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
218     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
219   return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
220                     : nullptr);
221 }
222