blob: 951bf2896fb08fa0fe56b4040e09388dbf0b25dd [file] [log] [blame]
//===-- 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 "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);
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 << " Active Type = " << template_type.GetDisplayTypeName() << " ";
return true;
}
} // namespace formatters
} // namespace lldb_private
namespace {
class VariantFrontEnd : public SyntheticChildrenFrontEnd {
public:
VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
Update();
}
size_t GetIndexOfChildWithName(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;
};
} // 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("Value"));
}
SyntheticChildrenFrontEnd *
formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp) {
if (valobj_sp)
return new VariantFrontEnd(*valobj_sp);
return nullptr;
}