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 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 // Project includes
14 #include "LibCxx.h"
15 
16 #include "lldb/Core/DataBufferHeap.h"
17 #include "lldb/Core/Error.h"
18 #include "lldb/Core/Stream.h"
19 #include "lldb/Core/ValueObject.h"
20 #include "lldb/Core/ValueObjectConstResult.h"
21 #include "lldb/DataFormatters/FormattersHelpers.h"
22 #include "lldb/Host/Endian.h"
23 #include "lldb/Symbol/ClangASTContext.h"
24 #include "lldb/Target/Target.h"
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 using namespace lldb_private::formatters;
29 
30 namespace lldb_private {
31 namespace formatters {
32 class LibcxxStdUnorderedMapSyntheticFrontEnd
33     : public SyntheticChildrenFrontEnd {
34 public:
35   LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
36 
37   ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
38 
39   size_t CalculateNumChildren() override;
40 
41   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
42 
43   bool Update() override;
44 
45   bool MightHaveChildren() override;
46 
47   size_t GetIndexOfChildWithName(const ConstString &name) override;
48 
49 private:
50   CompilerType m_element_type;
51   CompilerType m_node_type;
52   ValueObject *m_tree;
53   size_t m_num_elements;
54   ValueObject *m_next_element;
55   std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
56 };
57 } // namespace formatters
58 } // namespace lldb_private
59 
60 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
61     LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
62     : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), m_tree(nullptr),
63       m_num_elements(0), m_next_element(nullptr), m_elements_cache() {
64   if (valobj_sp)
65     Update();
66 }
67 
68 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
69     CalculateNumChildren() {
70   if (m_num_elements != UINT32_MAX)
71     return m_num_elements;
72   return 0;
73 }
74 
75 lldb::ValueObjectSP lldb_private::formatters::
76     LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
77   if (idx >= CalculateNumChildren())
78     return lldb::ValueObjectSP();
79   if (m_tree == nullptr)
80     return lldb::ValueObjectSP();
81 
82   while (idx >= m_elements_cache.size()) {
83     if (m_next_element == nullptr)
84       return lldb::ValueObjectSP();
85 
86     Error error;
87     ValueObjectSP node_sp = m_next_element->Dereference(error);
88     if (!node_sp || error.Fail())
89       return lldb::ValueObjectSP();
90 
91     ValueObjectSP value_sp =
92         node_sp->GetChildMemberWithName(ConstString("__value_"), true);
93     ValueObjectSP hash_sp =
94         node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
95     if (!hash_sp || !value_sp) {
96       if (!m_element_type) {
97         auto first_sp = m_backend.GetChildAtNamePath({ConstString("__table_"),
98                                                       ConstString("__p1_"),
99                                                       ConstString("__first_")});
100         if (!first_sp)
101           return nullptr;
102         m_element_type = first_sp->GetCompilerType();
103         lldb::TemplateArgumentKind kind;
104         m_element_type = m_element_type.GetTemplateArgument(0, kind);
105         m_element_type = m_element_type.GetPointeeType();
106         m_node_type = m_element_type;
107         m_element_type = m_element_type.GetTemplateArgument(0, kind);
108         std::string name;
109         m_element_type =
110             m_element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
111         m_element_type = m_element_type.GetTypedefedType();
112       }
113       if (!m_node_type)
114         return nullptr;
115       node_sp = node_sp->Cast(m_node_type);
116       value_sp = node_sp->GetChildMemberWithName(ConstString("__value_"), true);
117       hash_sp = node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
118       if (!value_sp || !hash_sp)
119         return nullptr;
120     }
121     m_elements_cache.push_back(
122         {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
123     m_next_element =
124         node_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
125     if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
126       m_next_element = nullptr;
127   }
128 
129   std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
130   if (!val_hash.first)
131     return lldb::ValueObjectSP();
132   StreamString stream;
133   stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
134   DataExtractor data;
135   Error error;
136   val_hash.first->GetData(data, error);
137   if (error.Fail())
138     return lldb::ValueObjectSP();
139   const bool thread_and_frame_only_if_stopped = true;
140   ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
141       thread_and_frame_only_if_stopped);
142   return CreateValueObjectFromData(stream.GetData(), data, exe_ctx,
143                                    val_hash.first->GetCompilerType());
144 }
145 
146 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
147     Update() {
148   m_num_elements = UINT32_MAX;
149   m_next_element = nullptr;
150   m_elements_cache.clear();
151   ValueObjectSP table_sp =
152       m_backend.GetChildMemberWithName(ConstString("__table_"), true);
153   if (!table_sp)
154     return false;
155   ValueObjectSP num_elements_sp = table_sp->GetChildAtNamePath(
156       {ConstString("__p2_"), ConstString("__first_")});
157   if (!num_elements_sp)
158     return false;
159   m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
160   m_tree =
161       table_sp
162           ->GetChildAtNamePath({ConstString("__p1_"), ConstString("__first_"),
163                                 ConstString("__next_")})
164           .get();
165   if (m_num_elements > 0)
166     m_next_element =
167         table_sp
168             ->GetChildAtNamePath({ConstString("__p1_"), ConstString("__first_"),
169                                   ConstString("__next_")})
170             .get();
171   return false;
172 }
173 
174 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
175     MightHaveChildren() {
176   return true;
177 }
178 
179 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
180     GetIndexOfChildWithName(const ConstString &name) {
181   return ExtractIndexFromString(name.GetCString());
182 }
183 
184 SyntheticChildrenFrontEnd *
185 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
186     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
187   return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
188                     : nullptr);
189 }
190