blob: ddf6c27a3e003ac75834d94a3e44fa99f8c4e581 [file] [log] [blame]
//===-- MsvcStlTree.cpp ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "MsvcStl.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/Utility/Status.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include <cstdint>
#include <optional>
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::formatters;
// A Node looks as follows:
// struct _Tree_node {
// _Tree_node *_Left;
// _Tree_node *_Parent;
// _Tree_node *_Right;
// char _Color;
// char _Isnil; // true (!= 0) if head or nil node
// value_type _Myval;
// };
namespace {
class MapEntry {
public:
MapEntry() = default;
explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
explicit MapEntry(ValueObject *entry)
: m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
ValueObjectSP left() const {
if (!m_entry_sp)
return m_entry_sp;
return m_entry_sp->GetSyntheticChildAtOffset(
0, m_entry_sp->GetCompilerType(), true);
}
ValueObjectSP right() const {
if (!m_entry_sp)
return m_entry_sp;
return m_entry_sp->GetSyntheticChildAtOffset(
2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(),
m_entry_sp->GetCompilerType(), true);
}
ValueObjectSP parent() const {
if (!m_entry_sp)
return m_entry_sp;
return m_entry_sp->GetSyntheticChildAtOffset(
m_entry_sp->GetProcessSP()->GetAddressByteSize(),
m_entry_sp->GetCompilerType(), true);
}
uint64_t value() const {
if (!m_entry_sp)
return 0;
return m_entry_sp->GetValueAsUnsigned(0);
}
bool is_nil() const {
if (!m_entry_sp)
return true;
auto isnil_sp = m_entry_sp->GetChildMemberWithName("_Isnil");
if (!isnil_sp)
return true;
return isnil_sp->GetValueAsUnsigned(1) != 0;
}
bool error() const {
if (!m_entry_sp)
return true;
return m_entry_sp->GetError().Fail();
}
bool is_nullptr() const { return (value() == 0); }
ValueObjectSP GetEntry() const { 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(ValueObject *entry, size_t depth = 0)
: m_entry(entry), m_max_depth(depth) {}
MapIterator() = default;
ValueObjectSP value() { return m_entry.GetEntry(); }
ValueObjectSP advance(size_t count) {
ValueObjectSP fail;
if (m_error)
return fail;
size_t steps = 0;
while (count > 0) {
next();
count--, steps++;
if (m_error || m_entry.is_nullptr() || (steps > m_max_depth))
return fail;
}
return m_entry.GetEntry();
}
private:
/// Mimicks _Tree_unchecked_const_iterator::operator++()
void next() {
if (m_entry.is_nullptr())
return;
MapEntry right(m_entry.right());
if (!right.is_nil()) {
m_entry = tree_min(std::move(right));
return;
}
size_t steps = 0;
MapEntry pnode(m_entry.parent());
while (!pnode.is_nil() &&
m_entry.value() == MapEntry(pnode.right()).value()) {
m_entry = pnode;
steps++;
if (steps > m_max_depth) {
m_entry = MapEntry();
return;
}
pnode.SetEntry(m_entry.parent());
}
m_entry = std::move(pnode);
}
/// Mimicks MSVC STL's _Min() algorithm (finding the leftmost node in the
/// subtree).
MapEntry tree_min(MapEntry pnode) {
if (pnode.is_nullptr())
return MapEntry();
MapEntry left(pnode.left());
size_t steps = 0;
while (!left.is_nil()) {
if (left.error()) {
m_error = true;
return MapEntry();
}
pnode = left;
left.SetEntry(pnode.left());
steps++;
if (steps > m_max_depth)
return MapEntry();
}
return pnode;
}
MapEntry m_entry;
size_t m_max_depth = 0;
bool m_error = false;
};
} // namespace
namespace lldb_private {
namespace formatters {
class MsvcStlTreeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
MsvcStlTreeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
~MsvcStlTreeSyntheticFrontEnd() override = default;
llvm::Expected<uint32_t> CalculateNumChildren() override;
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
lldb::ChildCacheState Update() override;
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
private:
/// Returns the ValueObject for the _Tree_node at index \ref idx.
///
/// \param[in] idx The child index that we're looking to get the value for.
///
/// \param[in] max_depth The maximum search depth after which we stop trying
/// to find the node for.
///
/// \returns On success, returns the ValueObjectSP corresponding to the
/// _Tree_node's _Myval member.
/// On failure, nullptr is returned.
ValueObjectSP GetValueAt(size_t idx, size_t max_depth);
ValueObject *m_tree = nullptr;
ValueObject *m_begin_node = nullptr;
size_t m_count = UINT32_MAX;
std::map<size_t, MapIterator> m_iterators;
};
class MsvcStlTreeIterSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
MsvcStlTreeIterSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp) {}
llvm::Expected<uint32_t> CalculateNumChildren() override {
if (!m_inner_sp)
return 0;
return m_inner_sp->GetNumChildren();
}
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
if (!m_inner_sp)
return nullptr;
return m_inner_sp->GetChildAtIndex(idx);
}
lldb::ChildCacheState Update() override;
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
if (!m_inner_sp)
return llvm::createStringError("There are no children.");
return m_inner_sp->GetIndexOfChildWithName(name);
}
lldb::ValueObjectSP GetSyntheticValue() override { return m_inner_sp; }
private:
ValueObjectSP m_inner_sp;
};
} // namespace formatters
} // namespace lldb_private
lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::
MsvcStlTreeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp) {
if (valobj_sp)
Update();
}
llvm::Expected<uint32_t>
lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::CalculateNumChildren() {
if (m_count != UINT32_MAX)
return m_count;
if (m_tree == nullptr)
return 0;
if (auto node_sp = m_tree->GetChildMemberWithName("_Mysize")) {
m_count = node_sp->GetValueAsUnsigned(0);
return m_count;
}
return llvm::createStringError("Failed to read size.");
}
ValueObjectSP
lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetValueAt(
size_t idx, size_t max_depth) {
MapIterator iterator(m_begin_node, max_depth);
size_t advance_by = idx;
if (idx > 0) {
// If we have already created the iterator for the previous
// index, we can start from there and advance by 1.
auto cached_iterator = m_iterators.find(idx - 1);
if (cached_iterator != m_iterators.end()) {
iterator = cached_iterator->second;
advance_by = 1;
}
}
ValueObjectSP iterated_sp(iterator.advance(advance_by));
if (!iterated_sp)
// this tree is garbage - stop
return nullptr;
ValueObjectSP value_sp = iterated_sp->GetChildMemberWithName("_Myval");
if (!value_sp)
return nullptr;
m_iterators[idx] = iterator;
return value_sp;
}
lldb::ValueObjectSP
lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
uint32_t num_children = CalculateNumChildrenIgnoringErrors();
if (idx >= num_children)
return nullptr;
if (m_tree == nullptr || m_begin_node == nullptr)
return nullptr;
ValueObjectSP val_sp = GetValueAt(idx, /*max_depth=*/num_children);
if (!val_sp) {
// this will stop all future searches until an Update() happens
m_tree = nullptr;
return nullptr;
}
// at this point we have a valid pair
// we need to copy current_sp into a new object otherwise we will end up with
// all items named _Myval
StreamString name;
name.Printf("[%" PRIu64 "]", (uint64_t)idx);
return val_sp->Clone(ConstString(name.GetString()));
}
lldb::ChildCacheState
lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::Update() {
m_count = UINT32_MAX;
m_tree = m_begin_node = nullptr;
m_iterators.clear();
m_tree =
m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myval2"}).get();
if (!m_tree)
return lldb::ChildCacheState::eRefetch;
m_begin_node = m_tree->GetChildAtNamePath({"_Myhead", "_Left"}).get();
return lldb::ChildCacheState::eRefetch;
}
llvm::Expected<size_t>
lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetIndexOfChildWithName(
ConstString name) {
auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
if (!optional_idx) {
return llvm::createStringError("Type has no child named '%s'",
name.AsCString());
}
return *optional_idx;
}
lldb::ChildCacheState MsvcStlTreeIterSyntheticFrontEnd::Update() {
m_inner_sp = nullptr;
auto node_sp = m_backend.GetChildMemberWithName("_Ptr");
if (!node_sp)
return lldb::eRefetch;
MapEntry entry(node_sp.get());
if (entry.is_nil())
return lldb::eRefetch; // end
m_inner_sp = node_sp->GetChildMemberWithName("_Myval");
return lldb::eRefetch;
}
bool formatters::IsMsvcStlTreeIter(ValueObject &valobj) {
return valobj.GetChildMemberWithName("_Ptr") != nullptr;
}
bool formatters::MsvcStlTreeIterSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
auto valobj_sp = valobj.GetNonSyntheticValue();
if (!valobj_sp)
return false;
auto node_sp = valobj_sp->GetChildMemberWithName("_Ptr");
if (!node_sp)
return false;
MapEntry entry(node_sp.get());
if (entry.is_nil()) {
stream.Printf("end");
return true;
}
auto value_sp = node_sp->GetChildMemberWithName("_Myval");
if (!value_sp)
return false;
auto *summary = value_sp->GetSummaryAsCString();
if (summary)
stream << summary;
return true;
}
SyntheticChildrenFrontEnd *
lldb_private::formatters::MsvcStlTreeIterSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
return (valobj_sp ? new MsvcStlTreeIterSyntheticFrontEnd(valobj_sp)
: nullptr);
}
bool formatters::IsMsvcStlMapLike(ValueObject &valobj) {
return valobj.GetChildMemberWithName("_Mypair") != nullptr;
}
SyntheticChildrenFrontEnd *
lldb_private::formatters::MsvcStlMapLikeSyntheticFrontEndCreator(
lldb::ValueObjectSP valobj_sp) {
return (valobj_sp ? new MsvcStlTreeSyntheticFrontEnd(valobj_sp) : nullptr);
}