| //===-- MsvcStlVariant.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/Symbol/CompilerType.h" | |
| #include <optional> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| namespace { | |
| // A variant when using DWARF looks as follows: | |
| // (lldb) fr v -R v1 | |
| // (std::variant<int, double, char>) v1 = { | |
| // std::_SMF_control<std::_Variant_base<int, double, char>, int, double, char> | |
| // = { | |
| // std::_Variant_storage<int, double, char> = { | |
| // = { | |
| // _Head = 0 | |
| // _Tail = { | |
| // = { | |
| // _Head = 2 | |
| // _Tail = { | |
| // = { | |
| // _Head = '\0' | |
| // _Tail = {} | |
| // } | |
| // } | |
| // } | |
| // } | |
| // } | |
| // } | |
| // _Which = '\x01' | |
| // } | |
| // } | |
| ValueObjectSP GetStorageMember(ValueObject &valobj, llvm::StringRef name) { | |
| // Find the union | |
| ValueObjectSP union_sp = valobj.GetChildAtIndex(0); | |
| if (!union_sp) | |
| return nullptr; | |
| return union_sp->GetChildMemberWithName(name); | |
| } | |
| ValueObjectSP GetHead(ValueObject &valobj) { | |
| return GetStorageMember(valobj, "_Head"); | |
| } | |
| ValueObjectSP GetTail(ValueObject &valobj) { | |
| return GetStorageMember(valobj, "_Tail"); | |
| } | |
| std::optional<int64_t> GetIndexValue(ValueObject &valobj) { | |
| ValueObjectSP index_sp = valobj.GetChildMemberWithName("_Which"); | |
| if (!index_sp) | |
| return std::nullopt; | |
| return {index_sp->GetValueAsSigned(-1)}; | |
| } | |
| ValueObjectSP GetNthStorage(ValueObject &outer, int64_t index) { | |
| // We need to find the std::_Variant_storage base class. | |
| // Navigate "down" to std::_Variant_base by finding the holder of "_Which". | |
| // This might be down a few levels if a variant member isn't trivially | |
| // destructible/copyable/etc. | |
| ValueObjectSP which_sp = outer.GetChildMemberWithName("_Which"); | |
| if (!which_sp) | |
| return nullptr; | |
| ValueObject *parent = which_sp->GetParent(); | |
| if (!parent) | |
| return nullptr; | |
| // Now go to std::_Variant_storage. | |
| ValueObjectSP container_sp = parent->GetChildAtIndex(0); | |
| if (!container_sp) | |
| return nullptr; | |
| for (int64_t i = 0; i < index; i++) { | |
| container_sp = GetTail(*container_sp); | |
| if (!container_sp) | |
| return nullptr; | |
| } | |
| return container_sp; | |
| } | |
| } // namespace | |
| bool formatters::IsMsvcStlVariant(ValueObject &valobj) { | |
| if (auto valobj_sp = valobj.GetNonSyntheticValue()) { | |
| return valobj_sp->GetChildMemberWithName("_Which") != nullptr; | |
| } | |
| return false; | |
| } | |
| bool formatters::MsvcStlVariantSummaryProvider( | |
| ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { | |
| ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); | |
| if (!valobj_sp) | |
| return false; | |
| auto index = GetIndexValue(*valobj_sp); | |
| if (!index) | |
| return false; | |
| if (*index < 0) { | |
| stream.Printf(" No Value"); | |
| return true; | |
| } | |
| ValueObjectSP storage = GetNthStorage(*valobj_sp, *index); | |
| if (!storage) | |
| return false; | |
| CompilerType storage_type = storage->GetCompilerType(); | |
| if (!storage_type) | |
| return false; | |
| // Resolve the typedef | |
| if (storage_type.IsTypedefType()) | |
| storage_type = storage_type.GetTypedefedType(); | |
| CompilerType active_type = storage_type.GetTypeTemplateArgument(1, true); | |
| if (!active_type) { | |
| // PDB: get the type from the head as we don't have template arguments | |
| // there. | |
| ValueObjectSP head = GetHead(*storage); | |
| if (!head) | |
| return false; | |
| active_type = head->GetCompilerType(); | |
| if (!active_type) | |
| return false; | |
| } | |
| stream << " Active Type = " << active_type.GetDisplayTypeName() << " "; | |
| return true; | |
| } | |
| namespace { | |
| class VariantFrontEnd : public SyntheticChildrenFrontEnd { | |
| public: | |
| VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { | |
| Update(); | |
| } | |
| lldb::ChildCacheState Update() override; | |
| llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; } | |
| ValueObjectSP GetChildAtIndex(uint32_t idx) override; | |
| private: | |
| size_t m_size = 0; | |
| }; | |
| } // namespace | |
| lldb::ChildCacheState VariantFrontEnd::Update() { | |
| m_size = 0; | |
| auto index = GetIndexValue(m_backend); | |
| if (index && *index >= 0) | |
| m_size = 1; | |
| return lldb::ChildCacheState::eRefetch; | |
| } | |
| ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) { | |
| if (idx >= m_size) | |
| return nullptr; | |
| auto index = GetIndexValue(m_backend); | |
| if (!index) | |
| return nullptr; | |
| ValueObjectSP storage_sp = GetNthStorage(m_backend, *index); | |
| if (!storage_sp) | |
| return nullptr; | |
| ValueObjectSP head_sp = GetHead(*storage_sp); | |
| if (!head_sp) | |
| return nullptr; | |
| return head_sp->Clone("Value"); | |
| } | |
| SyntheticChildrenFrontEnd *formatters::MsvcStlVariantSyntheticFrontEndCreator( | |
| CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { | |
| if (valobj_sp) | |
| return new VariantFrontEnd(*valobj_sp); | |
| return nullptr; | |
| } |