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(impl_sp->GetChildMemberWithName("__index")); 87 88 if (!index_sp) 89 return LibcxxVariantIndexValidity::Invalid; 90 91 // In the stable ABI, the type of __index is just int. 92 // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is 93 // enabled, the type can either be unsigned char/short/int depending on 94 // how many variant types there are. 95 // We only need to do this here when comparing against npos, because npos is 96 // just `-1`, but that translates to different unsigned values depending on 97 // the byte size. 98 CompilerType index_type = index_sp->GetCompilerType(); 99 100 std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr); 101 if (!index_type_bytes) 102 return LibcxxVariantIndexValidity::Invalid; 103 104 uint64_t npos_value = VariantNposValue(*index_type_bytes); 105 uint64_t index_value = index_sp->GetValueAsUnsigned(0); 106 107 if (index_value == npos_value) 108 return LibcxxVariantIndexValidity::NPos; 109 110 return LibcxxVariantIndexValidity::Valid; 111 } 112 113 std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { 114 ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); 115 116 if (!index_sp) 117 return {}; 118 119 return {index_sp->GetValueAsUnsigned(0)}; 120 } 121 122 ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { 123 ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data")); 124 125 if (!data_sp) 126 return ValueObjectSP{}; 127 128 ValueObjectSP current_level = data_sp; 129 for (uint64_t n = index; n != 0; --n) { 130 ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail")); 131 132 if (!tail_sp) 133 return ValueObjectSP{}; 134 135 current_level = tail_sp; 136 } 137 138 return current_level->GetChildMemberWithName("__head"); 139 } 140 } // namespace 141 142 namespace lldb_private { 143 namespace formatters { 144 bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, 145 const TypeSummaryOptions &options) { 146 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); 147 if (!valobj_sp) 148 return false; 149 150 ValueObjectSP impl_sp = GetChildMemberWithName( 151 *valobj_sp, {ConstString("__impl_"), ConstString("__impl")}); 152 153 if (!impl_sp) 154 return false; 155 156 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); 157 158 if (validity == LibcxxVariantIndexValidity::Invalid) 159 return false; 160 161 if (validity == LibcxxVariantIndexValidity::NPos) { 162 stream.Printf(" No Value"); 163 return true; 164 } 165 166 auto optional_index_value = LibcxxVariantIndexValue(impl_sp); 167 168 if (!optional_index_value) 169 return false; 170 171 uint64_t index_value = *optional_index_value; 172 173 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); 174 175 if (!nth_head) 176 return false; 177 178 CompilerType head_type = nth_head->GetCompilerType(); 179 180 if (!head_type) 181 return false; 182 183 CompilerType template_type = head_type.GetTypeTemplateArgument(1); 184 185 if (!template_type) 186 return false; 187 188 stream << " Active Type = " << template_type.GetDisplayTypeName() << " "; 189 190 return true; 191 } 192 } // namespace formatters 193 } // namespace lldb_private 194 195 namespace { 196 class VariantFrontEnd : public SyntheticChildrenFrontEnd { 197 public: 198 VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { 199 Update(); 200 } 201 202 size_t GetIndexOfChildWithName(ConstString name) override { 203 return formatters::ExtractIndexFromString(name.GetCString()); 204 } 205 206 bool MightHaveChildren() override { return true; } 207 bool Update() override; 208 size_t CalculateNumChildren() override { return m_size; } 209 ValueObjectSP GetChildAtIndex(size_t idx) override; 210 211 private: 212 size_t m_size = 0; 213 }; 214 } // namespace 215 216 bool VariantFrontEnd::Update() { 217 m_size = 0; 218 ValueObjectSP impl_sp = formatters::GetChildMemberWithName( 219 m_backend, {ConstString("__impl_"), ConstString("__impl")}); 220 if (!impl_sp) 221 return false; 222 223 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); 224 225 if (validity == LibcxxVariantIndexValidity::Invalid) 226 return false; 227 228 if (validity == LibcxxVariantIndexValidity::NPos) 229 return true; 230 231 m_size = 1; 232 233 return false; 234 } 235 236 ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) { 237 if (idx >= m_size) 238 return {}; 239 240 ValueObjectSP impl_sp = formatters::GetChildMemberWithName( 241 m_backend, {ConstString("__impl_"), ConstString("__impl")}); 242 if (!impl_sp) 243 return {}; 244 245 auto optional_index_value = LibcxxVariantIndexValue(impl_sp); 246 247 if (!optional_index_value) 248 return {}; 249 250 uint64_t index_value = *optional_index_value; 251 252 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); 253 254 if (!nth_head) 255 return {}; 256 257 CompilerType head_type = nth_head->GetCompilerType(); 258 259 if (!head_type) 260 return {}; 261 262 CompilerType template_type = head_type.GetTypeTemplateArgument(1); 263 264 if (!template_type) 265 return {}; 266 267 ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value")); 268 269 if (!head_value) 270 return {}; 271 272 return head_value->Clone(ConstString("Value")); 273 } 274 275 SyntheticChildrenFrontEnd * 276 formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, 277 lldb::ValueObjectSP valobj_sp) { 278 if (valobj_sp) 279 return new VariantFrontEnd(*valobj_sp); 280 return nullptr; 281 } 282