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 "lldb/DataFormatters/FormattersHelpers.h" 11 12 #include "llvm/ADT/Optional.h" 13 #include "llvm/ADT/ScopeExit.h" 14 15 using namespace lldb; 16 using 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 61 namespace { 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 66 enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos }; 67 68 LibcxxVariantIndexValidity 69 LibcxxVariantGetIndexValidity(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 84 llvm::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 94 ValueObjectSP 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 116 namespace lldb_private { 117 namespace formatters { 118 bool 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 << " Active Type = " << template_type.GetDisplayTypeName() << " "; 163 164 return true; 165 } 166 } // namespace formatters 167 } // namespace lldb_private 168 169 namespace { 170 class VariantFrontEnd : public SyntheticChildrenFrontEnd { 171 public: 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 185 private: 186 size_t m_size = 0; 187 }; 188 } // namespace 189 190 bool 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 210 ValueObjectSP 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("Value")); 246 } 247 248 SyntheticChildrenFrontEnd * 249 formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, 250 lldb::ValueObjectSP valobj_sp) { 251 if (valobj_sp) 252 return new VariantFrontEnd(*valobj_sp); 253 return nullptr; 254 } 255