1 //===-- LibCxxUnorderedMap.cpp ----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "LibCxx.h"
11 
12 #include "lldb/Core/ValueObject.h"
13 #include "lldb/Core/ValueObjectConstResult.h"
14 #include "lldb/DataFormatters/FormattersHelpers.h"
15 #include "lldb/Symbol/ClangASTContext.h"
16 #include "lldb/Target/Target.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 
22 using namespace lldb;
23 using namespace lldb_private;
24 using namespace lldb_private::formatters;
25 
26 namespace lldb_private {
27 namespace formatters {
28 class LibcxxStdUnorderedMapSyntheticFrontEnd
29     : public SyntheticChildrenFrontEnd {
30 public:
31   LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
32 
33   ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
34 
35   size_t CalculateNumChildren() override;
36 
37   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
38 
39   bool Update() override;
40 
41   bool MightHaveChildren() override;
42 
43   size_t GetIndexOfChildWithName(const ConstString &name) override;
44 
45 private:
46   CompilerType m_element_type;
47   CompilerType m_node_type;
48   ValueObject *m_tree;
49   size_t m_num_elements;
50   ValueObject *m_next_element;
51   std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
52 };
53 } // namespace formatters
54 } // namespace lldb_private
55 
56 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)57     LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
58     : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), m_tree(nullptr),
59       m_num_elements(0), m_next_element(nullptr), m_elements_cache() {
60   if (valobj_sp)
61     Update();
62 }
63 
64 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
CalculateNumChildren()65     CalculateNumChildren() {
66   if (m_num_elements != UINT32_MAX)
67     return m_num_elements;
68   return 0;
69 }
70 
71 lldb::ValueObjectSP lldb_private::formatters::
GetChildAtIndex(size_t idx)72     LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
73   if (idx >= CalculateNumChildren())
74     return lldb::ValueObjectSP();
75   if (m_tree == nullptr)
76     return lldb::ValueObjectSP();
77 
78   while (idx >= m_elements_cache.size()) {
79     if (m_next_element == nullptr)
80       return lldb::ValueObjectSP();
81 
82     Status error;
83     ValueObjectSP node_sp = m_next_element->Dereference(error);
84     if (!node_sp || error.Fail())
85       return lldb::ValueObjectSP();
86 
87     ValueObjectSP value_sp =
88         node_sp->GetChildMemberWithName(ConstString("__value_"), true);
89     ValueObjectSP hash_sp =
90         node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
91     if (!hash_sp || !value_sp) {
92       if (!m_element_type) {
93         auto p1_sp = m_backend.GetChildAtNamePath({ConstString("__table_"),
94                                                    ConstString("__p1_")});
95         if (!p1_sp)
96           return nullptr;
97 
98         ValueObjectSP first_sp = nullptr;
99         switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) {
100         case 1:
101           // Assume a pre llvm r300140 __compressed_pair implementation:
102           first_sp = p1_sp->GetChildMemberWithName(ConstString("__first_"),
103                                                    true);
104           break;
105         case 2: {
106           // Assume a post llvm r300140 __compressed_pair implementation:
107           ValueObjectSP first_elem_parent_sp =
108             p1_sp->GetChildAtIndex(0, true);
109           first_sp = p1_sp->GetChildMemberWithName(ConstString("__value_"),
110                                                    true);
111           break;
112         }
113         default:
114           return nullptr;
115         }
116 
117         if (!first_sp)
118           return nullptr;
119         m_element_type = first_sp->GetCompilerType();
120         m_element_type = m_element_type.GetTypeTemplateArgument(0);
121         m_element_type = m_element_type.GetPointeeType();
122         m_node_type = m_element_type;
123         m_element_type = m_element_type.GetTypeTemplateArgument(0);
124         std::string name;
125         m_element_type =
126             m_element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
127         m_element_type = m_element_type.GetTypedefedType();
128       }
129       if (!m_node_type)
130         return nullptr;
131       node_sp = node_sp->Cast(m_node_type);
132       value_sp = node_sp->GetChildMemberWithName(ConstString("__value_"), true);
133       hash_sp = node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
134       if (!value_sp || !hash_sp)
135         return nullptr;
136     }
137     m_elements_cache.push_back(
138         {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
139     m_next_element =
140         node_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
141     if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
142       m_next_element = nullptr;
143   }
144 
145   std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
146   if (!val_hash.first)
147     return lldb::ValueObjectSP();
148   StreamString stream;
149   stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
150   DataExtractor data;
151   Status error;
152   val_hash.first->GetData(data, error);
153   if (error.Fail())
154     return lldb::ValueObjectSP();
155   const bool thread_and_frame_only_if_stopped = true;
156   ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
157       thread_and_frame_only_if_stopped);
158   return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
159                                    val_hash.first->GetCompilerType());
160 }
161 
162 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
Update()163     Update() {
164   m_num_elements = UINT32_MAX;
165   m_next_element = nullptr;
166   m_elements_cache.clear();
167   ValueObjectSP table_sp =
168       m_backend.GetChildMemberWithName(ConstString("__table_"), true);
169   if (!table_sp)
170     return false;
171 
172   ValueObjectSP p2_sp = table_sp->GetChildMemberWithName(
173     ConstString("__p2_"), true);
174   ValueObjectSP num_elements_sp = nullptr;
175   llvm::SmallVector<ConstString, 3> next_path;
176   switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) {
177   case 1:
178     // Assume a pre llvm r300140 __compressed_pair implementation:
179     num_elements_sp = p2_sp->GetChildMemberWithName(
180       ConstString("__first_"), true);
181     next_path.append({ConstString("__p1_"), ConstString("__first_"),
182                       ConstString("__next_")});
183     break;
184   case 2: {
185     // Assume a post llvm r300140 __compressed_pair implementation:
186     ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0, true);
187     num_elements_sp = first_elem_parent->GetChildMemberWithName(
188       ConstString("__value_"), true);
189     next_path.append({ConstString("__p1_"), ConstString("__value_"),
190                       ConstString("__next_")});
191     break;
192   }
193   default:
194     return false;
195   }
196 
197   if (!num_elements_sp)
198     return false;
199   m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
200   m_tree = table_sp->GetChildAtNamePath(next_path).get();
201   if (m_num_elements > 0)
202     m_next_element =
203         table_sp->GetChildAtNamePath(next_path).get();
204   return false;
205 }
206 
207 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
MightHaveChildren()208     MightHaveChildren() {
209   return true;
210 }
211 
212 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
GetIndexOfChildWithName(const ConstString & name)213     GetIndexOfChildWithName(const ConstString &name) {
214   return ExtractIndexFromString(name.GetCString());
215 }
216 
217 SyntheticChildrenFrontEnd *
LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)218 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
219     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
220   return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
221                     : nullptr);
222 }
223