Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
39644 views
//===-- LibCxxVariant.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "LibCxxVariant.h"9#include "LibCxx.h"10#include "lldb/DataFormatters/FormattersHelpers.h"11#include "lldb/Symbol/CompilerType.h"12#include "lldb/Utility/LLDBAssert.h"1314#include "llvm/ADT/ScopeExit.h"15#include <optional>1617using namespace lldb;18using namespace lldb_private;1920// libc++ variant implementation contains two members that we care about both21// are contained in the __impl member.22// - __index which tells us which of the variadic template types is the active23// type for the variant24// - __data is a variadic union which recursively contains itself as member25// which refers to the tailing variadic types.26// - __head which refers to the leading non pack type27// - __value refers to the actual value contained28// - __tail which refers to the remaining pack types29//30// e.g. given std::variant<int,double,char> v131//32// (lldb) frame var -R v1.__impl.__data33//(... __union<... 0, int, double, char>) v1.__impl.__data = {34// ...35// __head = {36// __value = ...37// }38// __tail = {39// ...40// __head = {41// __value = ...42// }43// __tail = {44// ...45// __head = {46// __value = ...47// ...48//49// So given50// - __index equal to 0 the active value is contained in51//52// __data.__head.__value53//54// - __index equal to 1 the active value is contained in55//56// __data.__tail.__head.__value57//58// - __index equal to 2 the active value is contained in59//60// __data.__tail.__tail.__head.__value61//6263namespace {64// libc++ std::variant index could have one of three states65// 1) Valid, we can obtain it and its not variant_npos66// 2) Invalid, we can't obtain it or it is not a type we expect67// 3) NPos, its value is variant_npos which means the variant has no value68enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos };6970uint64_t VariantNposValue(uint64_t index_byte_size) {71switch (index_byte_size) {72case 1:73return static_cast<uint8_t>(-1);74case 2:75return static_cast<uint16_t>(-1);76case 4:77return static_cast<uint32_t>(-1);78}79lldbassert(false && "Unknown index type size");80return static_cast<uint32_t>(-1); // Fallback to stable ABI type.81}8283LibcxxVariantIndexValidity84LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {85ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));8687if (!index_sp)88return LibcxxVariantIndexValidity::Invalid;8990// In the stable ABI, the type of __index is just int.91// In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is92// enabled, the type can either be unsigned char/short/int depending on93// how many variant types there are.94// We only need to do this here when comparing against npos, because npos is95// just `-1`, but that translates to different unsigned values depending on96// the byte size.97CompilerType index_type = index_sp->GetCompilerType();9899std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr);100if (!index_type_bytes)101return LibcxxVariantIndexValidity::Invalid;102103uint64_t npos_value = VariantNposValue(*index_type_bytes);104uint64_t index_value = index_sp->GetValueAsUnsigned(0);105106if (index_value == npos_value)107return LibcxxVariantIndexValidity::NPos;108109return LibcxxVariantIndexValidity::Valid;110}111112std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {113ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));114115if (!index_sp)116return {};117118return {index_sp->GetValueAsUnsigned(0)};119}120121ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {122ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data"));123124if (!data_sp)125return ValueObjectSP{};126127ValueObjectSP current_level = data_sp;128for (uint64_t n = index; n != 0; --n) {129ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail"));130131if (!tail_sp)132return ValueObjectSP{};133134current_level = tail_sp;135}136137return current_level->GetChildMemberWithName("__head");138}139} // namespace140141namespace lldb_private {142namespace formatters {143bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,144const TypeSummaryOptions &options) {145ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());146if (!valobj_sp)147return false;148149ValueObjectSP impl_sp = GetChildMemberWithName(150*valobj_sp, {ConstString("__impl_"), ConstString("__impl")});151152if (!impl_sp)153return false;154155LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);156157if (validity == LibcxxVariantIndexValidity::Invalid)158return false;159160if (validity == LibcxxVariantIndexValidity::NPos) {161stream.Printf(" No Value");162return true;163}164165auto optional_index_value = LibcxxVariantIndexValue(impl_sp);166167if (!optional_index_value)168return false;169170uint64_t index_value = *optional_index_value;171172ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);173174if (!nth_head)175return false;176177CompilerType head_type = nth_head->GetCompilerType();178179if (!head_type)180return false;181182CompilerType template_type = head_type.GetTypeTemplateArgument(1);183184if (!template_type)185return false;186187stream << " Active Type = " << template_type.GetDisplayTypeName() << " ";188189return true;190}191} // namespace formatters192} // namespace lldb_private193194namespace {195class VariantFrontEnd : public SyntheticChildrenFrontEnd {196public:197VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {198Update();199}200201size_t GetIndexOfChildWithName(ConstString name) override {202return formatters::ExtractIndexFromString(name.GetCString());203}204205bool MightHaveChildren() override { return true; }206lldb::ChildCacheState Update() override;207llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }208ValueObjectSP GetChildAtIndex(uint32_t idx) override;209210private:211size_t m_size = 0;212};213} // namespace214215lldb::ChildCacheState VariantFrontEnd::Update() {216m_size = 0;217ValueObjectSP impl_sp = formatters::GetChildMemberWithName(218m_backend, {ConstString("__impl_"), ConstString("__impl")});219if (!impl_sp)220return lldb::ChildCacheState::eRefetch;221222LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);223224if (validity == LibcxxVariantIndexValidity::Invalid)225return lldb::ChildCacheState::eRefetch;226227if (validity == LibcxxVariantIndexValidity::NPos)228return lldb::ChildCacheState::eReuse;229230m_size = 1;231232return lldb::ChildCacheState::eRefetch;233}234235ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) {236if (idx >= m_size)237return {};238239ValueObjectSP impl_sp = formatters::GetChildMemberWithName(240m_backend, {ConstString("__impl_"), ConstString("__impl")});241if (!impl_sp)242return {};243244auto optional_index_value = LibcxxVariantIndexValue(impl_sp);245246if (!optional_index_value)247return {};248249uint64_t index_value = *optional_index_value;250251ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);252253if (!nth_head)254return {};255256CompilerType head_type = nth_head->GetCompilerType();257258if (!head_type)259return {};260261CompilerType template_type = head_type.GetTypeTemplateArgument(1);262263if (!template_type)264return {};265266ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value"));267268if (!head_value)269return {};270271return head_value->Clone(ConstString("Value"));272}273274SyntheticChildrenFrontEnd *275formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,276lldb::ValueObjectSP valobj_sp) {277if (valobj_sp)278return new VariantFrontEnd(*valobj_sp);279return nullptr;280}281282283