1 //===-- LibCxxList.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 "lldb/lldb-python.h"
11 
12 #include "lldb/DataFormatters/CXXFormatterFunctions.h"
13 
14 #include "lldb/Core/DataBufferHeap.h"
15 #include "lldb/Core/Error.h"
16 #include "lldb/Core/Stream.h"
17 #include "lldb/Core/ValueObject.h"
18 #include "lldb/Core/ValueObjectConstResult.h"
19 #include "lldb/Host/Endian.h"
20 #include "lldb/Symbol/ClangASTContext.h"
21 #include "lldb/Target/ObjCLanguageRuntime.h"
22 #include "lldb/Target/Target.h"
23 
24 using namespace lldb;
25 using namespace lldb_private;
26 using namespace lldb_private::formatters;
27 
28 class ListEntry
29 {
30 public:
ListEntry()31     ListEntry () {}
ListEntry(ValueObjectSP entry_sp)32     ListEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
ListEntry(const ListEntry & rhs)33     ListEntry (const ListEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {}
ListEntry(ValueObject * entry)34     ListEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
35 
36     ValueObjectSP
next()37     next ()
38     {
39         if (!m_entry_sp)
40             return m_entry_sp;
41         return m_entry_sp->GetChildMemberWithName(ConstString("__next_"), true);
42     }
43 
44     ValueObjectSP
prev()45     prev ()
46     {
47         if (!m_entry_sp)
48             return m_entry_sp;
49         return m_entry_sp->GetChildMemberWithName(ConstString("__prev_"), true);
50     }
51 
52     uint64_t
value()53     value ()
54     {
55         if (!m_entry_sp)
56             return 0;
57         return m_entry_sp->GetValueAsUnsigned(0);
58     }
59 
60     bool
null()61     null()
62     {
63         return (value() == 0);
64     }
65 
66     ValueObjectSP
GetEntry()67     GetEntry ()
68     {
69         return m_entry_sp;
70     }
71 
72     void
SetEntry(ValueObjectSP entry)73     SetEntry (ValueObjectSP entry)
74     {
75         m_entry_sp = entry;
76     }
77 
78     bool
operator ==(const ListEntry & rhs) const79     operator == (const ListEntry& rhs) const
80     {
81         return (rhs.m_entry_sp.get() == m_entry_sp.get());
82     }
83 
84 private:
85     ValueObjectSP m_entry_sp;
86 };
87 
88 class ListIterator
89 {
90 public:
ListIterator()91     ListIterator () {}
ListIterator(ListEntry entry)92     ListIterator (ListEntry entry) : m_entry(entry) {}
ListIterator(ValueObjectSP entry)93     ListIterator (ValueObjectSP entry) : m_entry(entry) {}
ListIterator(const ListIterator & rhs)94     ListIterator (const ListIterator& rhs) : m_entry(rhs.m_entry) {}
ListIterator(ValueObject * entry)95     ListIterator (ValueObject* entry) : m_entry(entry) {}
96 
97     ValueObjectSP
value()98     value ()
99     {
100         return m_entry.GetEntry();
101     }
102 
103     ValueObjectSP
advance(size_t count)104     advance (size_t count)
105     {
106         if (count == 0)
107             return m_entry.GetEntry();
108         if (count == 1)
109         {
110             next ();
111             return m_entry.GetEntry();
112         }
113         while (count > 0)
114         {
115             next ();
116             count--;
117             if (m_entry.null())
118                 return lldb::ValueObjectSP();
119         }
120         return m_entry.GetEntry();
121     }
122 
123     bool
operator ==(const ListIterator & rhs) const124     operator == (const ListIterator& rhs) const
125     {
126         return (rhs.m_entry == m_entry);
127     }
128 
129 protected:
130     void
next()131     next ()
132     {
133         m_entry.SetEntry(m_entry.next());
134     }
135 
136     void
prev()137     prev ()
138     {
139         m_entry.SetEntry(m_entry.prev());
140     }
141 private:
142     ListEntry m_entry;
143 };
144 
LibcxxStdListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)145 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
146 SyntheticChildrenFrontEnd(*valobj_sp.get()),
147 m_list_capping_size(0),
148 m_node_address(),
149 m_head(NULL),
150 m_tail(NULL),
151 m_element_type(),
152 m_count(UINT32_MAX),
153 m_children()
154 {
155     if (valobj_sp)
156         Update();
157 }
158 
159 bool
HasLoop()160 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop()
161 {
162     if (g_use_loop_detect == false)
163         return false;
164     ListEntry slow(m_head);
165     ListEntry fast1(m_head);
166     ListEntry fast2(m_head);
167     while (slow.next() && slow.next()->GetValueAsUnsigned(0) != m_node_address)
168     {
169         auto slow_value = slow.value();
170         fast1.SetEntry(fast2.next());
171         fast2.SetEntry(fast1.next());
172         if (fast1.value() == slow_value || fast2.value() == slow_value)
173             return true;
174         slow.SetEntry(slow.next());
175     }
176     return false;
177 }
178 
179 size_t
CalculateNumChildren()180 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::CalculateNumChildren ()
181 {
182     if (m_count != UINT32_MAX)
183         return m_count;
184     if (!m_head || !m_tail || m_node_address == 0)
185         return 0;
186     ValueObjectSP size_alloc(m_backend.GetChildMemberWithName(ConstString("__size_alloc_"), true));
187     if (size_alloc)
188     {
189         ValueObjectSP first(size_alloc->GetChildMemberWithName(ConstString("__first_"), true));
190         if (first)
191         {
192             m_count = first->GetValueAsUnsigned(UINT32_MAX);
193         }
194     }
195     if (m_count != UINT32_MAX)
196     {
197         if (!HasLoop())
198             return m_count;
199         return m_count = 0;
200     }
201     else
202     {
203         uint64_t next_val = m_head->GetValueAsUnsigned(0);
204         uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
205         if (next_val == 0 || prev_val == 0)
206             return 0;
207         if (next_val == m_node_address)
208             return 0;
209         if (next_val == prev_val)
210             return 1;
211         if (HasLoop())
212             return 0;
213         uint64_t size = 2;
214         ListEntry current(m_head);
215         while (current.next() && current.next()->GetValueAsUnsigned(0) != m_node_address)
216         {
217             size++;
218             current.SetEntry(current.next());
219             if (size > m_list_capping_size)
220                 break;
221         }
222         return m_count = (size-1);
223     }
224 }
225 
226 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)227 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex (size_t idx)
228 {
229     if (idx >= CalculateNumChildren())
230         return lldb::ValueObjectSP();
231 
232     if (!m_head || !m_tail || m_node_address == 0)
233         return lldb::ValueObjectSP();
234 
235     auto cached = m_children.find(idx);
236     if (cached != m_children.end())
237         return cached->second;
238 
239     ListIterator current(m_head);
240     ValueObjectSP current_sp(current.advance(idx));
241     if (!current_sp)
242         return lldb::ValueObjectSP();
243     current_sp = current_sp->GetChildMemberWithName(ConstString("__value_"), true);
244     if (!current_sp)
245         return lldb::ValueObjectSP();
246     // we need to copy current_sp into a new object otherwise we will end up with all items named __value_
247     DataExtractor data;
248     current_sp->GetData(data);
249     StreamString name;
250     name.Printf("[%zu]",idx);
251     return (m_children[idx] = ValueObject::CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type));
252 }
253 
254 bool
Update()255 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update()
256 {
257     m_head = m_tail = NULL;
258     m_node_address = 0;
259     m_count = UINT32_MAX;
260     Error err;
261     ValueObjectSP backend_addr(m_backend.AddressOf(err));
262     m_list_capping_size = 0;
263     if (m_backend.GetTargetSP())
264         m_list_capping_size = m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
265     if (m_list_capping_size == 0)
266         m_list_capping_size = 255;
267     if (err.Fail() || backend_addr.get() == NULL)
268         return false;
269     m_node_address = backend_addr->GetValueAsUnsigned(0);
270     if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS)
271         return false;
272     ValueObjectSP impl_sp(m_backend.GetChildMemberWithName(ConstString("__end_"),true));
273     if (!impl_sp)
274         return false;
275     ClangASTType list_type = m_backend.GetClangType();
276     if (list_type.IsReferenceType())
277         list_type = list_type.GetNonReferenceType();
278 
279     if (list_type.GetNumTemplateArguments() == 0)
280         return false;
281     lldb::TemplateArgumentKind kind;
282     m_element_type = list_type.GetTemplateArgument(0, kind);
283     m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
284     m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get();
285     return false;
286 }
287 
288 bool
MightHaveChildren()289 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::MightHaveChildren ()
290 {
291     return true;
292 }
293 
294 size_t
GetIndexOfChildWithName(const ConstString & name)295 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
296 {
297     return ExtractIndexFromString(name.GetCString());
298 }
299 
~LibcxxStdListSyntheticFrontEnd()300 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::~LibcxxStdListSyntheticFrontEnd ()
301 {}
302 
303 SyntheticChildrenFrontEnd*
LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)304 lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
305 {
306     if (!valobj_sp)
307         return NULL;
308     return (new LibcxxStdListSyntheticFrontEnd(valobj_sp));
309 }
310 
311