|  | 
|  | 1 | +//===-- MsvcStlVariant.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 "MsvcStl.h" | 
|  | 10 | +#include "lldb/DataFormatters/FormattersHelpers.h" | 
|  | 11 | +#include "lldb/Symbol/CompilerType.h" | 
|  | 12 | +#include <optional> | 
|  | 13 | + | 
|  | 14 | +using namespace lldb; | 
|  | 15 | +using namespace lldb_private; | 
|  | 16 | + | 
|  | 17 | +namespace { | 
|  | 18 | + | 
|  | 19 | +// A variant when using DWARF looks as follows: | 
|  | 20 | +// (lldb) fr v -R v1 | 
|  | 21 | +// (std::variant<int, double, char>) v1 = { | 
|  | 22 | +//   std::_SMF_control<std::_Variant_base<int, double, char>, int, double, char> | 
|  | 23 | +//   = { | 
|  | 24 | +//     std::_Variant_storage<int, double, char> = { | 
|  | 25 | +//        = { | 
|  | 26 | +//         _Head = 0 | 
|  | 27 | +//         _Tail = { | 
|  | 28 | +//            = { | 
|  | 29 | +//             _Head = 2 | 
|  | 30 | +//             _Tail = { | 
|  | 31 | +//                = { | 
|  | 32 | +//                 _Head = '\0' | 
|  | 33 | +//                 _Tail = {} | 
|  | 34 | +//               } | 
|  | 35 | +//             } | 
|  | 36 | +//           } | 
|  | 37 | +//         } | 
|  | 38 | +//       } | 
|  | 39 | +//     } | 
|  | 40 | +//     _Which = '\x01' | 
|  | 41 | +//   } | 
|  | 42 | +// } | 
|  | 43 | + | 
|  | 44 | +ValueObjectSP GetStorageMember(ValueObject &valobj, llvm::StringRef name) { | 
|  | 45 | +  // Find the union | 
|  | 46 | +  ValueObjectSP union_sp = valobj.GetChildAtIndex(0); | 
|  | 47 | +  if (!union_sp) | 
|  | 48 | +    return nullptr; | 
|  | 49 | +  return union_sp->GetChildMemberWithName(name); | 
|  | 50 | +} | 
|  | 51 | + | 
|  | 52 | +ValueObjectSP GetHead(ValueObject &valobj) { | 
|  | 53 | +  return GetStorageMember(valobj, "_Head"); | 
|  | 54 | +} | 
|  | 55 | +ValueObjectSP GetTail(ValueObject &valobj) { | 
|  | 56 | +  return GetStorageMember(valobj, "_Tail"); | 
|  | 57 | +} | 
|  | 58 | + | 
|  | 59 | +std::optional<int64_t> GetIndexValue(ValueObject &valobj) { | 
|  | 60 | +  ValueObjectSP index_sp = valobj.GetChildMemberWithName("_Which"); | 
|  | 61 | +  if (!index_sp) | 
|  | 62 | +    return std::nullopt; | 
|  | 63 | + | 
|  | 64 | +  return {index_sp->GetValueAsSigned(-1)}; | 
|  | 65 | +} | 
|  | 66 | + | 
|  | 67 | +ValueObjectSP GetNthStorage(ValueObject &outer, int64_t index) { | 
|  | 68 | +  // We need to find the std::_Variant_storage base class. | 
|  | 69 | + | 
|  | 70 | +  // -> std::_SMF_control (typedef to std::_Variant_base) | 
|  | 71 | +  ValueObjectSP container_sp = outer.GetSP()->GetChildAtIndex(0); | 
|  | 72 | +  if (!container_sp) | 
|  | 73 | +    return nullptr; | 
|  | 74 | +  // -> std::_Variant_storage | 
|  | 75 | +  container_sp = container_sp->GetChildAtIndex(0); | 
|  | 76 | +  if (!container_sp) | 
|  | 77 | +    return nullptr; | 
|  | 78 | + | 
|  | 79 | +  for (int64_t i = 0; i < index; i++) { | 
|  | 80 | +    container_sp = GetTail(*container_sp); | 
|  | 81 | +    if (!container_sp) | 
|  | 82 | +      return nullptr; | 
|  | 83 | +  } | 
|  | 84 | +  return container_sp; | 
|  | 85 | +} | 
|  | 86 | + | 
|  | 87 | +} // namespace | 
|  | 88 | + | 
|  | 89 | +bool formatters::IsMsvcStlVariant(ValueObject &valobj) { | 
|  | 90 | +  if (auto valobj_sp = valobj.GetNonSyntheticValue()) { | 
|  | 91 | +    return valobj_sp->GetChildMemberWithName("_Which") != nullptr; | 
|  | 92 | +  } | 
|  | 93 | +  return false; | 
|  | 94 | +} | 
|  | 95 | + | 
|  | 96 | +bool formatters::MsvcStlVariantSummaryProvider( | 
|  | 97 | +    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { | 
|  | 98 | +  ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); | 
|  | 99 | +  if (!valobj_sp) | 
|  | 100 | +    return false; | 
|  | 101 | + | 
|  | 102 | +  auto index = GetIndexValue(*valobj_sp); | 
|  | 103 | +  if (!index) | 
|  | 104 | +    return false; | 
|  | 105 | + | 
|  | 106 | +  if (*index < 0) { | 
|  | 107 | +    stream.Printf(" No Value"); | 
|  | 108 | +    return true; | 
|  | 109 | +  } | 
|  | 110 | + | 
|  | 111 | +  ValueObjectSP storage = GetNthStorage(*valobj_sp, *index); | 
|  | 112 | +  if (!storage) | 
|  | 113 | +    return false; | 
|  | 114 | +  CompilerType storage_type = storage->GetCompilerType(); | 
|  | 115 | +  if (!storage_type) | 
|  | 116 | +    return false; | 
|  | 117 | +  // Resolve the typedef | 
|  | 118 | +  if (storage_type.IsTypedefType()) | 
|  | 119 | +    storage_type = storage_type.GetTypedefedType(); | 
|  | 120 | + | 
|  | 121 | +  CompilerType active_type = storage_type.GetTypeTemplateArgument(1, true); | 
|  | 122 | +  if (!active_type) | 
|  | 123 | +    return false; | 
|  | 124 | + | 
|  | 125 | +  stream << " Active Type = " << active_type.GetDisplayTypeName() << " "; | 
|  | 126 | +  return true; | 
|  | 127 | +} | 
|  | 128 | + | 
|  | 129 | +namespace { | 
|  | 130 | +class VariantFrontEnd : public SyntheticChildrenFrontEnd { | 
|  | 131 | +public: | 
|  | 132 | +  VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { | 
|  | 133 | +    Update(); | 
|  | 134 | +  } | 
|  | 135 | + | 
|  | 136 | +  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { | 
|  | 137 | +    auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); | 
|  | 138 | +    if (!optional_idx) { | 
|  | 139 | +      return llvm::createStringError("Type has no child named '%s'", | 
|  | 140 | +                                     name.AsCString()); | 
|  | 141 | +    } | 
|  | 142 | +    return *optional_idx; | 
|  | 143 | +  } | 
|  | 144 | + | 
|  | 145 | +  lldb::ChildCacheState Update() override; | 
|  | 146 | +  llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; } | 
|  | 147 | +  ValueObjectSP GetChildAtIndex(uint32_t idx) override; | 
|  | 148 | + | 
|  | 149 | +private: | 
|  | 150 | +  size_t m_size = 0; | 
|  | 151 | +}; | 
|  | 152 | +} // namespace | 
|  | 153 | + | 
|  | 154 | +lldb::ChildCacheState VariantFrontEnd::Update() { | 
|  | 155 | +  m_size = 0; | 
|  | 156 | + | 
|  | 157 | +  auto index = GetIndexValue(m_backend); | 
|  | 158 | +  if (index && *index >= 0) | 
|  | 159 | +    m_size = 1; | 
|  | 160 | + | 
|  | 161 | +  return lldb::ChildCacheState::eRefetch; | 
|  | 162 | +} | 
|  | 163 | + | 
|  | 164 | +ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) { | 
|  | 165 | +  if (idx >= m_size) | 
|  | 166 | +    return nullptr; | 
|  | 167 | + | 
|  | 168 | +  auto index = GetIndexValue(m_backend); | 
|  | 169 | +  if (!index) | 
|  | 170 | +    return nullptr; | 
|  | 171 | + | 
|  | 172 | +  ValueObjectSP storage_sp = GetNthStorage(m_backend, *index); | 
|  | 173 | +  if (!storage_sp) | 
|  | 174 | +    return nullptr; | 
|  | 175 | + | 
|  | 176 | +  ValueObjectSP head_sp = GetHead(*storage_sp); | 
|  | 177 | +  if (!head_sp) | 
|  | 178 | +    return nullptr; | 
|  | 179 | + | 
|  | 180 | +  return head_sp->Clone(ConstString("Value")); | 
|  | 181 | +} | 
|  | 182 | + | 
|  | 183 | +SyntheticChildrenFrontEnd *formatters::MsvcStlVariantSyntheticFrontEndCreator( | 
|  | 184 | +    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { | 
|  | 185 | +  if (valobj_sp) | 
|  | 186 | +    return new VariantFrontEnd(*valobj_sp); | 
|  | 187 | +  return nullptr; | 
|  | 188 | +} | 
0 commit comments