1 //===-- LibCxxList.cpp ------------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
12 // Other libraries and framework includes
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"
27 using namespace lldb_private;
28 using namespace lldb_private::formatters;
35 ListEntry() = default;
36 ListEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
37 ListEntry (const ListEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {}
38 ListEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
45 return ListEntry(m_entry_sp->GetChildAtIndexPath({0,1}));
53 return ListEntry(m_entry_sp->GetChildAtIndexPath({0,0}));
61 return m_entry_sp->GetValueAsUnsigned(0);
67 return (value() == 0);
70 explicit operator bool ()
72 return GetEntry().get() != nullptr && null() == false;
82 SetEntry (ValueObjectSP entry)
88 operator == (const ListEntry& rhs) const
90 return value() == rhs.value();
94 operator != (const ListEntry& rhs) const
96 return !(*this == rhs);
100 ValueObjectSP m_entry_sp;
106 ListIterator() = default;
107 ListIterator (ListEntry entry) : m_entry(entry) {}
108 ListIterator (ValueObjectSP entry) : m_entry(entry) {}
109 ListIterator (const ListIterator& rhs) : m_entry(rhs.m_entry) {}
110 ListIterator (ValueObject* entry) : m_entry(entry) {}
115 return m_entry.GetEntry();
119 advance (size_t count)
122 return m_entry.GetEntry();
126 return m_entry.GetEntry();
133 return lldb::ValueObjectSP();
135 return m_entry.GetEntry();
139 operator == (const ListIterator& rhs) const
141 return (rhs.m_entry == m_entry);
148 m_entry = m_entry.next();
154 m_entry = m_entry.prev();
161 } // end anonymous namespace
163 namespace lldb_private {
164 namespace formatters {
165 class LibcxxStdListSyntheticFrontEnd : public SyntheticChildrenFrontEnd
168 LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp);
170 ~LibcxxStdListSyntheticFrontEnd() override = default;
173 CalculateNumChildren() override;
176 GetChildAtIndex(size_t idx) override;
182 MightHaveChildren() override;
185 GetIndexOfChildWithName(const ConstString &name) override;
189 HasLoop(size_t count);
191 size_t m_list_capping_size;
192 static const bool g_use_loop_detect = true;
194 size_t m_loop_detected; // The number of elements that have had loop detection run over them.
195 ListEntry m_slow_runner; // Used for loop detection
196 ListEntry m_fast_runner; // Used for loop detection
198 lldb::addr_t m_node_address;
201 CompilerType m_element_type;
203 std::map<size_t,lldb::ValueObjectSP> m_children;
204 std::map<size_t, ListIterator> m_iterators;
206 } // namespace formatters
207 } // namespace lldb_private
209 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
210 SyntheticChildrenFrontEnd(*valobj_sp.get()),
211 m_list_capping_size(0),
226 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop(size_t count)
228 if (g_use_loop_detect == false)
230 // don't bother checking for a loop if we won't actually need to jump nodes
234 if (m_loop_detected == 0)
236 // This is the first time we are being run (after the last update). Set up the loop
237 // invariant for the first element.
238 m_slow_runner = ListEntry(m_head).next();
239 m_fast_runner = m_slow_runner.next();
244 // Loop detection has been run over the first m_loop_detected elements. If m_slow_runner ==
245 // m_fast_runner then the loop has been detected after m_loop_detected elements.
246 const size_t steps_to_run = std::min(count,m_count);
247 while (m_loop_detected < steps_to_run
250 && m_slow_runner != m_fast_runner) {
252 m_slow_runner = m_slow_runner.next();
253 m_fast_runner = m_fast_runner.next().next();
256 if (count <= m_loop_detected)
257 return false; // No loop in the first m_loop_detected elements.
258 if (!m_slow_runner || !m_fast_runner)
259 return false; // Reached the end of the list. Definitely no loops.
260 return m_slow_runner == m_fast_runner;
264 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::CalculateNumChildren ()
266 if (m_count != UINT32_MAX)
268 if (!m_head || !m_tail || m_node_address == 0)
270 ValueObjectSP size_alloc(m_backend.GetChildMemberWithName(ConstString("__size_alloc_"), true));
273 ValueObjectSP first(size_alloc->GetChildMemberWithName(ConstString("__first_"), true));
276 m_count = first->GetValueAsUnsigned(UINT32_MAX);
279 if (m_count != UINT32_MAX)
285 uint64_t next_val = m_head->GetValueAsUnsigned(0);
286 uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
287 if (next_val == 0 || prev_val == 0)
289 if (next_val == m_node_address)
291 if (next_val == prev_val)
294 ListEntry current(m_head);
295 while (current.next() && current.next().value() != m_node_address)
298 current = current.next();
299 if (size > m_list_capping_size)
302 return m_count = (size-1);
307 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex (size_t idx)
309 if (idx >= CalculateNumChildren())
310 return lldb::ValueObjectSP();
312 if (!m_head || !m_tail || m_node_address == 0)
313 return lldb::ValueObjectSP();
315 auto cached = m_children.find(idx);
316 if (cached != m_children.end())
317 return cached->second;
320 return lldb::ValueObjectSP();
322 size_t actual_advance = idx;
324 ListIterator current(m_head);
327 auto cached_iterator = m_iterators.find(idx-1);
328 if (cached_iterator != m_iterators.end())
330 current = cached_iterator->second;
335 ValueObjectSP current_sp(current.advance(actual_advance));
337 return lldb::ValueObjectSP();
339 m_iterators[idx] = current;
341 current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child
343 return lldb::ValueObjectSP();
344 // we need to copy current_sp into a new object otherwise we will end up with all items named __value_
347 current_sp->GetData(data, error);
349 return lldb::ValueObjectSP();
352 name.Printf("[%" PRIu64 "]", (uint64_t)idx);
353 return (m_children[idx] = CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type));
357 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update()
361 m_head = m_tail = NULL;
363 m_count = UINT32_MAX;
365 m_slow_runner.SetEntry(nullptr);
366 m_fast_runner.SetEntry(nullptr);
369 ValueObjectSP backend_addr(m_backend.AddressOf(err));
370 m_list_capping_size = 0;
371 if (m_backend.GetTargetSP())
372 m_list_capping_size = m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
373 if (m_list_capping_size == 0)
374 m_list_capping_size = 255;
375 if (err.Fail() || backend_addr.get() == NULL)
377 m_node_address = backend_addr->GetValueAsUnsigned(0);
378 if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS)
380 ValueObjectSP impl_sp(m_backend.GetChildMemberWithName(ConstString("__end_"),true));
383 CompilerType list_type = m_backend.GetCompilerType();
384 if (list_type.IsReferenceType())
385 list_type = list_type.GetNonReferenceType();
387 if (list_type.GetNumTemplateArguments() == 0)
389 lldb::TemplateArgumentKind kind;
390 m_element_type = list_type.GetTemplateArgument(0, kind);
391 m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
392 m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get();
397 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::MightHaveChildren ()
403 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
405 return ExtractIndexFromString(name.GetCString());
408 SyntheticChildrenFrontEnd*
409 lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
413 return (new LibcxxStdListSyntheticFrontEnd(valobj_sp));