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