diff options
Diffstat (limited to 'lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp')
-rw-r--r-- | lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp new file mode 100644 index 00000000000..a7da282f778 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp @@ -0,0 +1,273 @@ +//===-- LibCxxVariant.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LibCxxVariant.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/ScopeExit.h" + +using namespace lldb; +using namespace lldb_private; + +// libc++ variant implementation contains two members that we care about both +// are contained in the __impl member. +// - __index which tells us which of the variadic template types is the active +// type for the variant +// - __data is a variadic union which recursively contains itself as member +// which refers to the tailing variadic types. +// - __head which refers to the leading non pack type +// - __value refers to the actual value contained +// - __tail which refers to the remaining pack types +// +// e.g. given std::variant<int,double,char> v1 +// +// (lldb) frame var -R v1.__impl.__data +//(... __union<... 0, int, double, char>) v1.__impl.__data = { +// ... +// __head = { +// __value = ... +// } +// __tail = { +// ... +// __head = { +// __value = ... +// } +// __tail = { +// ... +// __head = { +// __value = ... +// ... +// +// So given +// - __index equal to 0 the active value is contained in +// +// __data.__head.__value +// +// - __index equal to 1 the active value is contained in +// +// __data.__tail.__head.__value +// +// - __index equal to 2 the active value is contained in +// +// __data.__tail.__tail.__head.__value +// + +namespace { +// libc++ std::variant index could have one of three states +// 1) VALID, we can obtain it and its not variant_npos +// 2) INVALID, we can't obtain it or it is not a type we expect +// 3) NPOS, its value is variant_npos which means the variant has no value +enum class LibcxxVariantIndexValidity { VALID, INVALID, NPOS }; + +LibcxxVariantIndexValidity +LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { + ValueObjectSP index_sp( + impl_sp->GetChildMemberWithName(ConstString("__index"), true)); + + if (!index_sp) + return LibcxxVariantIndexValidity::INVALID; + + int64_t index_value = index_sp->GetValueAsSigned(0); + + CompilerType index_compiler_type = index_sp->GetCompilerType(); + + // We are expecting two layers of typedefs before we obtain the basic type + // The next two checks verify this. + if (!index_compiler_type.IsTypedefType()) + return LibcxxVariantIndexValidity::INVALID; + + if (!index_compiler_type.GetTypedefedType().IsTypedefType()) + return LibcxxVariantIndexValidity::INVALID; + + lldb::BasicType index_basic_type = index_compiler_type.GetTypedefedType() + .GetTypedefedType() + .GetBasicTypeEnumeration(); + + if (index_basic_type == eBasicTypeInvalid) + return LibcxxVariantIndexValidity::INVALID; + + if (index_value == -1) + return LibcxxVariantIndexValidity::NPOS; + + return LibcxxVariantIndexValidity::VALID; +} + +llvm::Optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { + ValueObjectSP index_sp( + impl_sp->GetChildMemberWithName(ConstString("__index"), true)); + + if (!index_sp) + return {}; + + return {index_sp->GetValueAsUnsigned(0)}; +} + +ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { + ValueObjectSP data_sp( + impl_sp->GetChildMemberWithName(ConstString("__data"), true)); + + if (!data_sp) + return ValueObjectSP{}; + + ValueObjectSP current_level = data_sp; + for (uint64_t n = index; n != 0; --n) { + ValueObjectSP tail_sp( + current_level->GetChildMemberWithName(ConstString("__tail"), true)); + + if (!tail_sp) + return ValueObjectSP{}; + + current_level = tail_sp; + } + + return current_level->GetChildMemberWithName(ConstString("__head"), true); +}; +} // namespace + +namespace lldb_private { +namespace formatters { +bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + ValueObjectSP impl_sp( + valobj_sp->GetChildMemberWithName(ConstString("__impl"), true)); + + if (!impl_sp) + return false; + + LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); + + if (validity == LibcxxVariantIndexValidity::INVALID) + return false; + + if (validity == LibcxxVariantIndexValidity::NPOS) { + stream.Printf(" No Value"); + return true; + } + + auto optional_index_value = LibcxxVariantIndexValue(impl_sp); + + if (!optional_index_value) + return false; + + uint64_t index_value = *optional_index_value; + + ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); + + if (!nth_head) + return false; + + CompilerType head_type = nth_head->GetCompilerType(); + + if (!head_type) + return false; + + CompilerType template_type = head_type.GetTypeTemplateArgument(1); + + if (!template_type) + return false; + + stream.Printf(" Active Type = %s ", template_type.GetTypeName().GetCString()); + + return true; +} +} // namespace formatters +} // namespace lldb_private + +namespace { +class VariantFrontEnd : public SyntheticChildrenFrontEnd { +public: + VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(const ConstString &name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + bool Update() override; + size_t CalculateNumChildren() override { return m_size; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + +private: + size_t m_size = 0; + ValueObjectSP m_base_sp; +}; +} // namespace + +bool VariantFrontEnd::Update() { + m_size = 0; + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__impl"), true)); + if (!impl_sp) + return false; + + LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); + + if (validity == LibcxxVariantIndexValidity::INVALID) + return false; + + if (validity == LibcxxVariantIndexValidity::NPOS) + return true; + + m_size = 1; + + return false; +} + +ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= m_size) + return ValueObjectSP(); + + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__impl"), true)); + + auto optional_index_value = LibcxxVariantIndexValue(impl_sp); + + if (!optional_index_value) + return ValueObjectSP(); + + uint64_t index_value = *optional_index_value; + + ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); + + if (!nth_head) + return ValueObjectSP(); + + CompilerType head_type = nth_head->GetCompilerType(); + + if (!head_type) + return ValueObjectSP(); + + CompilerType template_type = head_type.GetTypeTemplateArgument(1); + + if (!template_type) + return ValueObjectSP(); + + ValueObjectSP head_value( + nth_head->GetChildMemberWithName(ConstString("__value"), true)); + + if (!head_value) + return ValueObjectSP(); + + return head_value->Clone(ConstString(ConstString("Value").AsCString())); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new VariantFrontEnd(*valobj_sp); + return nullptr; +} |