diff options
Diffstat (limited to 'lldb/source/Plugins')
6 files changed, 668 insertions, 22 deletions
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index ab9d991..cbc6f14 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -35,7 +35,9 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LibStdcppUniquePointer.cpp MsvcStl.cpp MsvcStlAtomic.cpp + MsvcStlDeque.cpp MsvcStlSmartPointer.cpp + MsvcStlTree.cpp MsvcStlTuple.cpp MsvcStlUnordered.cpp MsvcStlVariant.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 80dc460..e69f2f6 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1409,27 +1409,27 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::(__debug::)?map<.+> >(( )?&)?$", eFormatterMatchRegex, + "^std::__debug::map<.+> >(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::(__debug)?deque<.+>(( )?&)?$", eFormatterMatchRegex, + "^std::__debug::deque<.+>(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_deref_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::(__debug::)?set<.+> >(( )?&)?$", eFormatterMatchRegex, + "^std::__debug::set<.+> >(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_deref_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::(__debug::)?multimap<.+> >(( )?&)?$", eFormatterMatchRegex, + "^std::__debug::multimap<.+> >(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_deref_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::(__debug::)?multiset<.+> >(( )?&)?$", eFormatterMatchRegex, + "^std::__debug::multiset<.+> >(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_deref_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); @@ -1462,30 +1462,29 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "libstdc++ std::__debug::vector summary provider", "^std::__debug::vector<.+>(( )?&)?$", stl_summary_flags, true); - AddCXXSummary( - cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::map summary provider", - "^std::(__debug::)?map<.+> >(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::ContainerSizeSummaryProvider, + "libstdc++ debug std::map summary provider", + "^std::__debug::map<.+> >(( )?&)?$", stl_summary_flags, true); - AddCXXSummary( - cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::set summary provider", - "^std::(__debug::)?set<.+> >(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::ContainerSizeSummaryProvider, + "libstdc++ debug std::set summary provider", + "^std::__debug::set<.+> >(( )?&)?$", stl_summary_flags, true); - AddCXXSummary( - cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::deque summary provider", - "^std::(__debug::)?deque<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "libstdc++ debug std::deque summary provider", + "^std::__debug::deque<.+>(( )?&)?$", stl_summary_flags, true); AddCXXSummary( cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::multimap summary provider", - "^std::(__debug::)?multimap<.+> >(( )?&)?$", stl_summary_flags, true); + "libstdc++ debug std::multimap summary provider", + "^std::__debug::multimap<.+> >(( )?&)?$", stl_summary_flags, true); AddCXXSummary( cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::multiset summary provider", - "^std::(__debug::)?multiset<.+> >(( )?&)?$", stl_summary_flags, true); + "libstdc++ debug std::multiset summary provider", + "^std::__debug::multiset<.+> >(( )?&)?$", stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, @@ -1672,6 +1671,30 @@ GenericUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *children, *valobj_sp); } +static SyntheticChildrenFrontEnd * +GenericMapLikeSyntheticFrontEndCreator(CXXSyntheticChildren *children, + ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + + if (IsMsvcStlMapLike(*valobj_sp)) + return MsvcStlMapLikeSyntheticFrontEndCreator(valobj_sp); + return new ScriptedSyntheticChildren::FrontEnd( + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider", *valobj_sp); +} + +static SyntheticChildrenFrontEnd * +GenericDequeSyntheticFrontEndCreator(CXXSyntheticChildren *children, + ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + + if (IsMsvcStlDeque(*valobj_sp)) + return MsvcStlDequeSyntheticFrontEndCreator(children, valobj_sp); + return new ScriptedSyntheticChildren::FrontEnd( + "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider", *valobj_sp); +} + /// Load formatters that are formatting types from more than one STL static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) @@ -1749,6 +1772,14 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSynthetic(cpp_category_sp, GenericOptionalSyntheticFrontEndCreator, "std::optional synthetic children", "^std::optional<.+>(( )?&)?$", stl_deref_flags, true); + AddCXXSynthetic(cpp_category_sp, GenericDequeSyntheticFrontEndCreator, + "std::deque container synthetic children", + "^std::deque<.+>(( )?&)?$", stl_deref_flags, true); + + AddCXXSynthetic(cpp_category_sp, GenericMapLikeSyntheticFrontEndCreator, + "std::(multi)?map/set synthetic children", + "^std::(multi)?(map|set)<.+>(( )?&)?$", stl_synth_flags, + true); AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider, "MSVC STL/libstdc++ std::shared_ptr summary provider", @@ -1786,6 +1817,13 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "MSVC STL/libstdc++ std unordered container summary provider", "^std::unordered_(multi)?(map|set)<.+> ?>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "MSVC STL/libstdc++ std::(multi)?map/set summary provider", + "^std::(multi)?(map|set)<.+>(( )?&)?$", stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "MSVC STL/libstd++ std::deque summary provider", + "^std::deque<.+>(( )?&)?$", stl_summary_flags, true); } static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { @@ -1834,6 +1872,14 @@ static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSummary(cpp_category_sp, MsvcStlAtomicSummaryProvider, "MSVC STL std::atomic summary provider", "^std::atomic<.+>$", stl_summary_flags, true); + AddCXXSynthetic(cpp_category_sp, MsvcStlTreeIterSyntheticFrontEndCreator, + "MSVC STL tree iterator synthetic children", + "^std::_Tree(_const)?_iterator<.+>(( )?&)?$", stl_synth_flags, + true); + AddCXXSummary(cpp_category_sp, MsvcStlTreeIterSummaryProvider, + "MSVC STL tree iterator summary", + "^std::_Tree(_const)?_iterator<.+>(( )?&)?$", stl_summary_flags, + true); } static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h index e2a015a..490794c 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -91,6 +91,23 @@ bool IsMsvcStlUnordered(ValueObject &valobj); SyntheticChildrenFrontEnd * MsvcStlUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp); +bool IsMsvcStlTreeIter(ValueObject &valobj); +bool MsvcStlTreeIterSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); +lldb_private::SyntheticChildrenFrontEnd * +MsvcStlTreeIterSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +// std::map,set,multimap,multiset +bool IsMsvcStlMapLike(ValueObject &valobj); +lldb_private::SyntheticChildrenFrontEnd * +MsvcStlMapLikeSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp); + +// MSVC STL std::deque<> +bool IsMsvcStlDeque(ValueObject &valobj); +SyntheticChildrenFrontEnd * +MsvcStlDequeSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp new file mode 100644 index 0000000..8733543 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp @@ -0,0 +1,174 @@ +//===-- MsvcStlDeque.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/DataFormatters/TypeSynthetic.h" + +using namespace lldb; + +namespace lldb_private { +namespace formatters { + +class MsvcStlDequeSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + MsvcStlDequeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + 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: + ValueObject *m_map = nullptr; + ExecutionContextRef m_exe_ctx_ref; + + size_t m_block_size = 0; + size_t m_offset = 0; + size_t m_map_size = 0; + + size_t m_element_size = 0; + CompilerType m_element_type; + + uint32_t m_size = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd:: + MsvcStlDequeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + MsvcStlDequeSyntheticFrontEnd::CalculateNumChildren() { + if (!m_map) + return llvm::createStringError("Failed to read size"); + return m_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (idx >= m_size || !m_map) + return nullptr; + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (!process_sp) + return nullptr; + + // _EEN_DS = _Block_size + // _Map[(($i + _Myoff) / _EEN_DS) % _Mapsize][($i + _Myoff) % _EEN_DS] + size_t first_idx = ((idx + m_offset) / m_block_size) % m_map_size; + lldb::addr_t first_address = m_map->GetValueAsUnsigned(0) + + first_idx * process_sp->GetAddressByteSize(); + + Status err; + lldb::addr_t second_base = + process_sp->ReadPointerFromMemory(first_address, err); + if (err.Fail()) + return nullptr; + + size_t second_idx = (idx + m_offset) % m_block_size; + size_t second_address = second_base + second_idx * m_element_size; + + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), second_address, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::Update() { + m_size = 0; + m_map = nullptr; + m_element_type.Clear(); + + auto storage_sp = m_backend.GetChildAtNamePath({"_Mypair", "_Myval2"}); + if (!storage_sp) + return lldb::eRefetch; + + auto deque_type = m_backend.GetCompilerType(); + if (!deque_type) + return lldb::eRefetch; + + auto block_size_decl = deque_type.GetStaticFieldWithName("_Block_size"); + if (!block_size_decl) + return lldb::eRefetch; + auto block_size = block_size_decl.GetConstantValue(); + if (!block_size.IsValid()) + return lldb::eRefetch; + + auto element_type = deque_type.GetTypeTemplateArgument(0); + if (!element_type) + return lldb::eRefetch; + auto element_size = element_type.GetByteSize(nullptr); + if (!element_size) + return lldb::eRefetch; + + auto offset_sp = storage_sp->GetChildMemberWithName("_Myoff"); + auto map_size_sp = storage_sp->GetChildMemberWithName("_Mapsize"); + auto map_sp = storage_sp->GetChildMemberWithName("_Map"); + auto size_sp = storage_sp->GetChildMemberWithName("_Mysize"); + if (!offset_sp || !map_size_sp || !map_sp || !size_sp) + return lldb::eRefetch; + + bool ok = false; + uint64_t offset = offset_sp->GetValueAsUnsigned(0, &ok); + if (!ok) + return lldb::eRefetch; + + uint64_t map_size = map_size_sp->GetValueAsUnsigned(0, &ok); + if (!ok) + return lldb::eRefetch; + + uint64_t size = size_sp->GetValueAsUnsigned(0, &ok); + if (!ok) + return lldb::eRefetch; + + m_map = map_sp.get(); + m_exe_ctx_ref = m_backend.GetExecutionContextRef(); + m_block_size = block_size.ULongLong(); + m_offset = offset; + m_map_size = map_size; + m_element_size = *element_size; + m_element_type = element_type; + m_size = size; + return lldb::eRefetch; +} + +llvm::Expected<size_t> lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_map) + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + if (auto optional_idx = ExtractIndexFromString(name.GetCString())) + return *optional_idx; + + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); +} + +bool lldb_private::formatters::IsMsvcStlDeque(ValueObject &valobj) { + if (auto valobj_sp = valobj.GetNonSyntheticValue()) + return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr; + return false; +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::MsvcStlDequeSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return new MsvcStlDequeSyntheticFrontEnd(valobj_sp); +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp new file mode 100644 index 0000000..ddf6c27 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp @@ -0,0 +1,407 @@ +//===-- 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); +} diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index d3b758f..256952dc 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -5460,7 +5460,7 @@ CompilerType TypeSystemClang::GetBuiltinTypeByName(ConstString name) { lldb::BasicType TypeSystemClang::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) { if (type) { - clang::QualType qual_type(GetQualType(type)); + clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); if (type_class == clang::Type::Builtin) { switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) { |