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