LibCxxVariant.cpp revision 360784
1//===-- LibCxxVariant.cpp --------------------------------------*- C++ -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8 9#include "LibCxxVariant.h" 10#include "lldb/DataFormatters/FormattersHelpers.h" 11 12#include "llvm/ADT/Optional.h" 13#include "llvm/ADT/ScopeExit.h" 14 15using namespace lldb; 16using namespace lldb_private; 17 18// libc++ variant implementation contains two members that we care about both 19// are contained in the __impl member. 20// - __index which tells us which of the variadic template types is the active 21// type for the variant 22// - __data is a variadic union which recursively contains itself as member 23// which refers to the tailing variadic types. 24// - __head which refers to the leading non pack type 25// - __value refers to the actual value contained 26// - __tail which refers to the remaining pack types 27// 28// e.g. given std::variant<int,double,char> v1 29// 30// (lldb) frame var -R v1.__impl.__data 31//(... __union<... 0, int, double, char>) v1.__impl.__data = { 32// ... 33// __head = { 34// __value = ... 35// } 36// __tail = { 37// ... 38// __head = { 39// __value = ... 40// } 41// __tail = { 42// ... 43// __head = { 44// __value = ... 45// ... 46// 47// So given 48// - __index equal to 0 the active value is contained in 49// 50// __data.__head.__value 51// 52// - __index equal to 1 the active value is contained in 53// 54// __data.__tail.__head.__value 55// 56// - __index equal to 2 the active value is contained in 57// 58// __data.__tail.__tail.__head.__value 59// 60 61namespace { 62// libc++ std::variant index could have one of three states 63// 1) VALID, we can obtain it and its not variant_npos 64// 2) INVALID, we can't obtain it or it is not a type we expect 65// 3) NPOS, its value is variant_npos which means the variant has no value 66enum class LibcxxVariantIndexValidity { VALID, INVALID, NPOS }; 67 68LibcxxVariantIndexValidity 69LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { 70 ValueObjectSP index_sp( 71 impl_sp->GetChildMemberWithName(ConstString("__index"), true)); 72 73 if (!index_sp) 74 return LibcxxVariantIndexValidity::INVALID; 75 76 int64_t index_value = index_sp->GetValueAsSigned(0); 77 78 if (index_value == -1) 79 return LibcxxVariantIndexValidity::NPOS; 80 81 return LibcxxVariantIndexValidity::VALID; 82} 83 84llvm::Optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { 85 ValueObjectSP index_sp( 86 impl_sp->GetChildMemberWithName(ConstString("__index"), true)); 87 88 if (!index_sp) 89 return {}; 90 91 return {index_sp->GetValueAsUnsigned(0)}; 92} 93 94ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { 95 ValueObjectSP data_sp( 96 impl_sp->GetChildMemberWithName(ConstString("__data"), true)); 97 98 if (!data_sp) 99 return ValueObjectSP{}; 100 101 ValueObjectSP current_level = data_sp; 102 for (uint64_t n = index; n != 0; --n) { 103 ValueObjectSP tail_sp( 104 current_level->GetChildMemberWithName(ConstString("__tail"), true)); 105 106 if (!tail_sp) 107 return ValueObjectSP{}; 108 109 current_level = tail_sp; 110 } 111 112 return current_level->GetChildMemberWithName(ConstString("__head"), true); 113} 114} // namespace 115 116namespace lldb_private { 117namespace formatters { 118bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, 119 const TypeSummaryOptions &options) { 120 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); 121 if (!valobj_sp) 122 return false; 123 124 ValueObjectSP impl_sp( 125 valobj_sp->GetChildMemberWithName(ConstString("__impl"), true)); 126 127 if (!impl_sp) 128 return false; 129 130 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); 131 132 if (validity == LibcxxVariantIndexValidity::INVALID) 133 return false; 134 135 if (validity == LibcxxVariantIndexValidity::NPOS) { 136 stream.Printf(" No Value"); 137 return true; 138 } 139 140 auto optional_index_value = LibcxxVariantIndexValue(impl_sp); 141 142 if (!optional_index_value) 143 return false; 144 145 uint64_t index_value = *optional_index_value; 146 147 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); 148 149 if (!nth_head) 150 return false; 151 152 CompilerType head_type = nth_head->GetCompilerType(); 153 154 if (!head_type) 155 return false; 156 157 CompilerType template_type = head_type.GetTypeTemplateArgument(1); 158 159 if (!template_type) 160 return false; 161 162 stream.Printf(" Active Type = %s ", template_type.GetTypeName().GetCString()); 163 164 return true; 165} 166} // namespace formatters 167} // namespace lldb_private 168 169namespace { 170class VariantFrontEnd : public SyntheticChildrenFrontEnd { 171public: 172 VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { 173 Update(); 174 } 175 176 size_t GetIndexOfChildWithName(ConstString name) override { 177 return formatters::ExtractIndexFromString(name.GetCString()); 178 } 179 180 bool MightHaveChildren() override { return true; } 181 bool Update() override; 182 size_t CalculateNumChildren() override { return m_size; } 183 ValueObjectSP GetChildAtIndex(size_t idx) override; 184 185private: 186 size_t m_size = 0; 187}; 188} // namespace 189 190bool VariantFrontEnd::Update() { 191 m_size = 0; 192 ValueObjectSP impl_sp( 193 m_backend.GetChildMemberWithName(ConstString("__impl"), true)); 194 if (!impl_sp) 195 return false; 196 197 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); 198 199 if (validity == LibcxxVariantIndexValidity::INVALID) 200 return false; 201 202 if (validity == LibcxxVariantIndexValidity::NPOS) 203 return true; 204 205 m_size = 1; 206 207 return false; 208} 209 210ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) { 211 if (idx >= m_size) 212 return ValueObjectSP(); 213 214 ValueObjectSP impl_sp( 215 m_backend.GetChildMemberWithName(ConstString("__impl"), true)); 216 217 auto optional_index_value = LibcxxVariantIndexValue(impl_sp); 218 219 if (!optional_index_value) 220 return ValueObjectSP(); 221 222 uint64_t index_value = *optional_index_value; 223 224 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); 225 226 if (!nth_head) 227 return ValueObjectSP(); 228 229 CompilerType head_type = nth_head->GetCompilerType(); 230 231 if (!head_type) 232 return ValueObjectSP(); 233 234 CompilerType template_type = head_type.GetTypeTemplateArgument(1); 235 236 if (!template_type) 237 return ValueObjectSP(); 238 239 ValueObjectSP head_value( 240 nth_head->GetChildMemberWithName(ConstString("__value"), true)); 241 242 if (!head_value) 243 return ValueObjectSP(); 244 245 return head_value->Clone(ConstString(ConstString("Value").AsCString())); 246} 247 248SyntheticChildrenFrontEnd * 249formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, 250 lldb::ValueObjectSP valobj_sp) { 251 if (valobj_sp) 252 return new VariantFrontEnd(*valobj_sp); 253 return nullptr; 254} 255