| //===-- LibCxxVariant.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 "LibCxxVariant.h" | |
| #include "LibCxx.h" | |
| #include "lldb/DataFormatters/FormattersHelpers.h" | |
| #include "lldb/Symbol/CompilerType.h" | |
| #include "lldb/Utility/LLDBAssert.h" | |
| #include "llvm/ADT/ScopeExit.h" | |
| #include <optional> | |
| 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 }; | |
| uint64_t VariantNposValue(uint64_t index_byte_size) { | |
| switch (index_byte_size) { | |
| case 1: | |
| return static_cast<uint8_t>(-1); | |
| case 2: | |
| return static_cast<uint16_t>(-1); | |
| case 4: | |
| return static_cast<uint32_t>(-1); | |
| } | |
| lldbassert(false && "Unknown index type size"); | |
| return static_cast<uint32_t>(-1); // Fallback to stable ABI type. | |
| } | |
| LibcxxVariantIndexValidity | |
| LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { | |
| ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); | |
| if (!index_sp) | |
| return LibcxxVariantIndexValidity::Invalid; | |
| // In the stable ABI, the type of __index is just int. | |
| // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is | |
| // enabled, the type can either be unsigned char/short/int depending on | |
| // how many variant types there are. | |
| // We only need to do this here when comparing against npos, because npos is | |
| // just `-1`, but that translates to different unsigned values depending on | |
| // the byte size. | |
| CompilerType index_type = index_sp->GetCompilerType(); | |
| llvm::Expected<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr); | |
| if (!index_type_bytes) { | |
| LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), | |
| index_type_bytes.takeError(), "{0}"); | |
| if (!index_type_bytes) | |
| return LibcxxVariantIndexValidity::Invalid; | |
| } | |
| uint64_t npos_value = VariantNposValue(*index_type_bytes); | |
| uint64_t index_value = index_sp->GetValueAsUnsigned(0); | |
| if (index_value == npos_value) | |
| return LibcxxVariantIndexValidity::NPos; | |
| return LibcxxVariantIndexValidity::Valid; | |
| } | |
| std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { | |
| ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); | |
| if (!index_sp) | |
| return {}; | |
| return {index_sp->GetValueAsUnsigned(0)}; | |
| } | |
| ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { | |
| ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data")); | |
| if (!data_sp) | |
| return ValueObjectSP{}; | |
| ValueObjectSP current_level = data_sp; | |
| for (uint64_t n = index; n != 0; --n) { | |
| ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail")); | |
| if (!tail_sp) | |
| return ValueObjectSP{}; | |
| current_level = tail_sp; | |
| } | |
| return current_level->GetChildMemberWithName("__head"); | |
| } | |
| } // 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 = GetChildMemberWithName( | |
| *valobj_sp, {ConstString("__impl_"), ConstString("__impl")}); | |
| 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 << " Active Type = " << template_type.GetDisplayTypeName() << " "; | |
| return true; | |
| } | |
| } // namespace formatters | |
| } // namespace lldb_private | |
| 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; | |
| ValueObjectSP impl_sp = formatters::GetChildMemberWithName( | |
| m_backend, {ConstString("__impl_"), ConstString("__impl")}); | |
| if (!impl_sp) | |
| return lldb::ChildCacheState::eRefetch; | |
| LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); | |
| if (validity == LibcxxVariantIndexValidity::Invalid) | |
| return lldb::ChildCacheState::eRefetch; | |
| if (validity == LibcxxVariantIndexValidity::NPos) | |
| return lldb::ChildCacheState::eReuse; | |
| m_size = 1; | |
| return lldb::ChildCacheState::eRefetch; | |
| } | |
| ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) { | |
| if (idx >= m_size) | |
| return {}; | |
| ValueObjectSP impl_sp = formatters::GetChildMemberWithName( | |
| m_backend, {ConstString("__impl_"), ConstString("__impl")}); | |
| if (!impl_sp) | |
| return {}; | |
| auto optional_index_value = LibcxxVariantIndexValue(impl_sp); | |
| if (!optional_index_value) | |
| return {}; | |
| uint64_t index_value = *optional_index_value; | |
| ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); | |
| if (!nth_head) | |
| return {}; | |
| CompilerType head_type = nth_head->GetCompilerType(); | |
| if (!head_type) | |
| return {}; | |
| CompilerType template_type = head_type.GetTypeTemplateArgument(1); | |
| if (!template_type) | |
| return {}; | |
| ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value")); | |
| if (!head_value) | |
| return {}; | |
| return head_value->Clone("Value"); | |
| } | |
| SyntheticChildrenFrontEnd * | |
| formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, | |
| lldb::ValueObjectSP valobj_sp) { | |
| if (valobj_sp) | |
| return new VariantFrontEnd(*valobj_sp); | |
| return nullptr; | |
| } |