#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;
namespace {
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);
}
LibcxxVariantIndexValidity
LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
if (!index_sp)
return LibcxxVariantIndexValidity::Invalid;
CompilerType index_type = index_sp->GetCompilerType();
std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr);
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 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 {
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; }
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;
};
}
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(ConstString("Value"));
}
SyntheticChildrenFrontEnd *
formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp) {
if (valobj_sp)
return new VariantFrontEnd(*valobj_sp);
return nullptr;
}