1 //===-- LibCxxVariant.cpp -------------------------------------------------===// 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 "LibCxx.h" 11 #include "lldb/DataFormatters/FormattersHelpers.h" 12 #include "lldb/Symbol/CompilerType.h" 13 #include "lldb/Utility/LLDBAssert.h" 14 15 #include "llvm/ADT/ScopeExit.h" 16 #include <optional> 17 18 using namespace lldb; 19 using namespace lldb_private; 20 21 // libc++ variant implementation contains two members that we care about both 22 // are contained in the __impl member. 23 // - __index which tells us which of the variadic template types is the active 24 // type for the variant 25 // - __data is a variadic union which recursively contains itself as member 26 // which refers to the tailing variadic types. 27 // - __head which refers to the leading non pack type 28 // - __value refers to the actual value contained 29 // - __tail which refers to the remaining pack types 30 // 31 // e.g. given std::variant<int,double,char> v1 32 // 33 // (lldb) frame var -R v1.__impl.__data 34 //(... __union<... 0, int, double, char>) v1.__impl.__data = { 35 // ... 36 // __head = { 37 // __value = ... 38 // } 39 // __tail = { 40 // ... 41 // __head = { 42 // __value = ... 43 // } 44 // __tail = { 45 // ... 46 // __head = { 47 // __value = ... 48 // ... 49 // 50 // So given 51 // - __index equal to 0 the active value is contained in 52 // 53 // __data.__head.__value 54 // 55 // - __index equal to 1 the active value is contained in 56 // 57 // __data.__tail.__head.__value 58 // 59 // - __index equal to 2 the active value is contained in 60 // 61 // __data.__tail.__tail.__head.__value 62 // 63 64 namespace { 65 // libc++ std::variant index could have one of three states 66 // 1) Valid, we can obtain it and its not variant_npos 67 // 2) Invalid, we can't obtain it or it is not a type we expect 68 // 3) NPos, its value is variant_npos which means the variant has no value 69 enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos }; 70 71 uint64_t VariantNposValue(uint64_t index_byte_size) { 72 switch (index_byte_size) { 73 case 1: 74 return static_cast<uint8_t>(-1); 75 case 2: 76 return static_cast<uint16_t>(-1); 77 case 4: 78 return static_cast<uint32_t>(-1); 79 } 80 lldbassert(false && "Unknown index type size"); 81 return static_cast<uint32_t>(-1); // Fallback to stable ABI type. 82 } 83 84 LibcxxVariantIndexValidity 85 LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { 86 ValueObjectSP index_sp( 87 impl_sp->GetChildMemberWithName(ConstString("__index"), true)); 88 89 if (!index_sp) 90 return LibcxxVariantIndexValidity::Invalid; 91 92 // In the stable ABI, the type of __index is just int. 93 // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is 94 // enabled, the type can either be unsigned char/short/int depending on 95 // how many variant types there are. 96 // We only need to do this here when comparing against npos, because npos is 97 // just `-1`, but that translates to different unsigned values depending on 98 // the byte size. 99 CompilerType index_type = index_sp->GetCompilerType(); 100 101 std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr); 102 if (!index_type_bytes) 103 return LibcxxVariantIndexValidity::Invalid; 104 105 uint64_t npos_value = VariantNposValue(*index_type_bytes); 106 uint64_t index_value = index_sp->GetValueAsUnsigned(0); 107 108 if (index_value == npos_value) 109 return LibcxxVariantIndexValidity::NPos; 110 111 return LibcxxVariantIndexValidity::Valid; 112 } 113 114 std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { 115 ValueObjectSP index_sp( 116 impl_sp->GetChildMemberWithName(ConstString("__index"), true)); 117 118 if (!index_sp) 119 return {}; 120 121 return {index_sp->GetValueAsUnsigned(0)}; 122 } 123 124 ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { 125 ValueObjectSP data_sp( 126 impl_sp->GetChildMemberWithName(ConstString("__data"), true)); 127 128 if (!data_sp) 129 return ValueObjectSP{}; 130 131 ValueObjectSP current_level = data_sp; 132 for (uint64_t n = index; n != 0; --n) { 133 ValueObjectSP tail_sp( 134 current_level->GetChildMemberWithName(ConstString("__tail"), true)); 135 136 if (!tail_sp) 137 return ValueObjectSP{}; 138 139 current_level = tail_sp; 140 } 141 142 return current_level->GetChildMemberWithName(ConstString("__head"), true); 143 } 144 } // namespace 145 146 namespace lldb_private { 147 namespace formatters { 148 bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, 149 const TypeSummaryOptions &options) { 150 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); 151 if (!valobj_sp) 152 return false; 153 154 ValueObjectSP impl_sp = GetChildMemberWithName( 155 *valobj_sp, {ConstString("__impl_"), ConstString("__impl")}); 156 157 if (!impl_sp) 158 return false; 159 160 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); 161 162 if (validity == LibcxxVariantIndexValidity::Invalid) 163 return false; 164 165 if (validity == LibcxxVariantIndexValidity::NPos) { 166 stream.Printf(" No Value"); 167 return true; 168 } 169 170 auto optional_index_value = LibcxxVariantIndexValue(impl_sp); 171 172 if (!optional_index_value) 173 return false; 174 175 uint64_t index_value = *optional_index_value; 176 177 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); 178 179 if (!nth_head) 180 return false; 181 182 CompilerType head_type = nth_head->GetCompilerType(); 183 184 if (!head_type) 185 return false; 186 187 CompilerType template_type = head_type.GetTypeTemplateArgument(1); 188 189 if (!template_type) 190 return false; 191 192 stream << " Active Type = " << template_type.GetDisplayTypeName() << " "; 193 194 return true; 195 } 196 } // namespace formatters 197 } // namespace lldb_private 198 199 namespace { 200 class VariantFrontEnd : public SyntheticChildrenFrontEnd { 201 public: 202 VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { 203 Update(); 204 } 205 206 size_t GetIndexOfChildWithName(ConstString name) override { 207 return formatters::ExtractIndexFromString(name.GetCString()); 208 } 209 210 bool MightHaveChildren() override { return true; } 211 bool Update() override; 212 size_t CalculateNumChildren() override { return m_size; } 213 ValueObjectSP GetChildAtIndex(size_t idx) override; 214 215 private: 216 size_t m_size = 0; 217 }; 218 } // namespace 219 220 bool VariantFrontEnd::Update() { 221 m_size = 0; 222 ValueObjectSP impl_sp = formatters::GetChildMemberWithName( 223 m_backend, {ConstString("__impl_"), ConstString("__impl")}); 224 if (!impl_sp) 225 return false; 226 227 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); 228 229 if (validity == LibcxxVariantIndexValidity::Invalid) 230 return false; 231 232 if (validity == LibcxxVariantIndexValidity::NPos) 233 return true; 234 235 m_size = 1; 236 237 return false; 238 } 239 240 ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) { 241 if (idx >= m_size) 242 return {}; 243 244 ValueObjectSP impl_sp = formatters::GetChildMemberWithName( 245 m_backend, {ConstString("__impl_"), ConstString("__impl")}); 246 if (!impl_sp) 247 return {}; 248 249 auto optional_index_value = LibcxxVariantIndexValue(impl_sp); 250 251 if (!optional_index_value) 252 return {}; 253 254 uint64_t index_value = *optional_index_value; 255 256 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); 257 258 if (!nth_head) 259 return {}; 260 261 CompilerType head_type = nth_head->GetCompilerType(); 262 263 if (!head_type) 264 return {}; 265 266 CompilerType template_type = head_type.GetTypeTemplateArgument(1); 267 268 if (!template_type) 269 return {}; 270 271 ValueObjectSP head_value( 272 nth_head->GetChildMemberWithName(ConstString("__value"), true)); 273 274 if (!head_value) 275 return {}; 276 277 return head_value->Clone(ConstString("Value")); 278 } 279 280 SyntheticChildrenFrontEnd * 281 formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, 282 lldb::ValueObjectSP valobj_sp) { 283 if (valobj_sp) 284 return new VariantFrontEnd(*valobj_sp); 285 return nullptr; 286 } 287