//===-- LibCxxList.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" #include "lldb/DataFormatters/CXXFormatterFunctions.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Error.h" #include "lldb/Core/Stream.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Host/Endian.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Target.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; class MapEntry { public: MapEntry () {} MapEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} MapEntry (const MapEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {} MapEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} ValueObjectSP left () { if (!m_entry_sp) return m_entry_sp; return m_entry_sp->GetChildMemberWithName(ConstString("__left_"), true); } ValueObjectSP right () { if (!m_entry_sp) return m_entry_sp; return m_entry_sp->GetChildMemberWithName(ConstString("__right_"), true); } ValueObjectSP parent () { if (!m_entry_sp) return m_entry_sp; return m_entry_sp->GetChildMemberWithName(ConstString("__parent_"), true); } uint64_t value () { if (!m_entry_sp) return 0; return m_entry_sp->GetValueAsUnsigned(0); } bool error () { if (!m_entry_sp) return true; return m_entry_sp->GetError().Fail(); } bool null() { return (value() == 0); } ValueObjectSP GetEntry () { return m_entry_sp; } void SetEntry (ValueObjectSP entry) { m_entry_sp = entry; } bool operator == (const MapEntry& rhs) const { return (rhs.m_entry_sp.get() == m_entry_sp.get()); } private: ValueObjectSP m_entry_sp; }; class MapIterator { public: MapIterator () {} MapIterator (MapEntry entry, size_t depth = 0) : m_entry(entry), m_max_depth(depth), m_error(false) {} MapIterator (ValueObjectSP entry, size_t depth = 0) : m_entry(entry), m_max_depth(depth), m_error(false) {} MapIterator (const MapIterator& rhs) : m_entry(rhs.m_entry),m_max_depth(rhs.m_max_depth), m_error(false) {} MapIterator (ValueObject* entry, size_t depth = 0) : m_entry(entry), m_max_depth(depth), m_error(false) {} ValueObjectSP value () { return m_entry.GetEntry(); } ValueObjectSP advance (size_t count) { if (m_error) return lldb::ValueObjectSP(); if (count == 0) return m_entry.GetEntry(); if (count == 1) { next (); return m_entry.GetEntry(); } size_t steps = 0; while (count > 0) { if (m_error) return lldb::ValueObjectSP(); next (); count--; if (m_entry.null()) return lldb::ValueObjectSP(); steps++; if (steps > m_max_depth) return lldb::ValueObjectSP(); } return m_entry.GetEntry(); } protected: void next () { m_entry.SetEntry(increment(m_entry.GetEntry())); } private: ValueObjectSP tree_min (ValueObjectSP x_sp) { MapEntry x(x_sp); if (x.null()) return ValueObjectSP(); MapEntry left(x.left()); size_t steps = 0; while (left.null() == false) { if (left.error()) { m_error = true; return lldb::ValueObjectSP(); } x.SetEntry(left.GetEntry()); left.SetEntry(x.left()); steps++; if (steps > m_max_depth) return lldb::ValueObjectSP(); } return x.GetEntry(); } ValueObjectSP tree_max (ValueObjectSP x_sp) { MapEntry x(x_sp); if (x.null()) return ValueObjectSP(); MapEntry right(x.right()); size_t steps = 0; while (right.null() == false) { if (right.error()) return lldb::ValueObjectSP(); x.SetEntry(right.GetEntry()); right.SetEntry(x.right()); steps++; if (steps > m_max_depth) return lldb::ValueObjectSP(); } return x.GetEntry(); } bool is_left_child (ValueObjectSP x_sp) { MapEntry x(x_sp); if (x.null()) return false; MapEntry rhs(x.parent()); rhs.SetEntry(rhs.left()); return x.value() == rhs.value(); } ValueObjectSP increment (ValueObjectSP x_sp) { MapEntry node(x_sp); if (node.null()) return ValueObjectSP(); MapEntry right(node.right()); if (right.null() == false) return tree_min(right.GetEntry()); size_t steps = 0; while (!is_left_child(node.GetEntry())) { if (node.error()) { m_error = true; return lldb::ValueObjectSP(); } node.SetEntry(node.parent()); steps++; if (steps > m_max_depth) return lldb::ValueObjectSP(); } return node.parent(); } MapEntry m_entry; size_t m_max_depth; bool m_error; }; lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::LibcxxStdMapSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_tree(NULL), m_root_node(NULL), m_element_type(), m_skip_size(UINT32_MAX), m_count(UINT32_MAX), m_children() { if (valobj_sp) Update(); } size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren () { if (m_count != UINT32_MAX) return m_count; if (m_tree == NULL) return 0; ValueObjectSP m_item(m_tree->GetChildMemberWithName(ConstString("__pair3_"), true)); if (!m_item) return 0; m_item = m_item->GetChildMemberWithName(ConstString("__first_"), true); if (!m_item) return 0; m_count = m_item->GetValueAsUnsigned(0); return m_count; } bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() { if (m_element_type.GetOpaqueQualType() && m_element_type.GetASTContext()) return true; m_element_type.Clear(); ValueObjectSP deref; Error error; deref = m_root_node->Dereference(error); if (!deref || error.Fail()) return false; deref = deref->GetChildMemberWithName(ConstString("__value_"), true); if (!deref) return false; m_element_type = deref->GetClangType(); return true; } void lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset (const lldb::ValueObjectSP& node) { if (m_skip_size != UINT32_MAX) return; if (!node) return; ClangASTType node_type(node->GetClangType()); uint64_t bit_offset; if (node_type.GetIndexOfFieldWithName("__value_", NULL, &bit_offset) == UINT32_MAX) return; m_skip_size = bit_offset / 8u; } lldb::ValueObjectSP lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex (size_t idx) { if (idx >= CalculateNumChildren()) return lldb::ValueObjectSP(); if (m_tree == NULL || m_root_node == NULL) return lldb::ValueObjectSP(); auto cached = m_children.find(idx); if (cached != m_children.end()) return cached->second; bool need_to_skip = (idx > 0); MapIterator iterator(m_root_node, CalculateNumChildren()); ValueObjectSP iterated_sp(iterator.advance(idx)); if (iterated_sp.get() == NULL) { // this tree is garbage - stop m_tree = NULL; // this will stop all future searches until an Update() happens return iterated_sp; } if (GetDataType()) { if (!need_to_skip) { Error error; iterated_sp = iterated_sp->Dereference(error); if (!iterated_sp || error.Fail()) { m_tree = NULL; return lldb::ValueObjectSP(); } GetValueOffset(iterated_sp); iterated_sp = iterated_sp->GetChildMemberWithName(ConstString("__value_"), true); if (!iterated_sp) { m_tree = NULL; return lldb::ValueObjectSP(); } } else { // because of the way our debug info is made, we need to read item 0 first // so that we can cache information used to generate other elements if (m_skip_size == UINT32_MAX) GetChildAtIndex(0); if (m_skip_size == UINT32_MAX) { m_tree = NULL; return lldb::ValueObjectSP(); } iterated_sp = iterated_sp->GetSyntheticChildAtOffset(m_skip_size, m_element_type, true); if (!iterated_sp) { m_tree = NULL; return lldb::ValueObjectSP(); } } } else { m_tree = NULL; return lldb::ValueObjectSP(); } // at this point we have a valid // we need to copy current_sp into a new object otherwise we will end up with all items named __value_ DataExtractor data; iterated_sp->GetData(data); StreamString name; name.Printf("[%zu]",idx); return (m_children[idx] = ValueObject::CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); } bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { m_count = UINT32_MAX; m_tree = m_root_node = NULL; m_children.clear(); m_tree = m_backend.GetChildMemberWithName(ConstString("__tree_"), true).get(); if (!m_tree) return false; m_root_node = m_tree->GetChildMemberWithName(ConstString("__begin_node_"), true).get(); return false; } bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::MightHaveChildren () { return true; } size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) { return ExtractIndexFromString(name.GetCString()); } lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::~LibcxxStdMapSyntheticFrontEnd () {} SyntheticChildrenFrontEnd* lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) { if (!valobj_sp) return NULL; return (new LibcxxStdMapSyntheticFrontEnd(valobj_sp)); }