aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVariant.cpp
blob: 52a3d98d2af4b11479694ef8dd6f6c55cc1177a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//===-- MsvcStlVariant.cpp-------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "MsvcStl.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/Symbol/CompilerType.h"
#include <optional>

using namespace lldb;
using namespace lldb_private;

namespace {

// A variant when using DWARF looks as follows:
// (lldb) fr v -R v1
// (std::variant<int, double, char>) v1 = {
//   std::_SMF_control<std::_Variant_base<int, double, char>, int, double, char>
//   = {
//     std::_Variant_storage<int, double, char> = {
//        = {
//         _Head = 0
//         _Tail = {
//            = {
//             _Head = 2
//             _Tail = {
//                = {
//                 _Head = '\0'
//                 _Tail = {}
//               }
//             }
//           }
//         }
//       }
//     }
//     _Which = '\x01'
//   }
// }

ValueObjectSP GetStorageMember(ValueObject &valobj, llvm::StringRef name) {
  // Find the union
  ValueObjectSP union_sp = valobj.GetChildAtIndex(0);
  if (!union_sp)
    return nullptr;
  return union_sp->GetChildMemberWithName(name);
}

ValueObjectSP GetHead(ValueObject &valobj) {
  return GetStorageMember(valobj, "_Head");
}
ValueObjectSP GetTail(ValueObject &valobj) {
  return GetStorageMember(valobj, "_Tail");
}

std::optional<int64_t> GetIndexValue(ValueObject &valobj) {
  ValueObjectSP index_sp = valobj.GetChildMemberWithName("_Which");
  if (!index_sp)
    return std::nullopt;

  return {index_sp->GetValueAsSigned(-1)};
}

ValueObjectSP GetNthStorage(ValueObject &outer, int64_t index) {
  // We need to find the std::_Variant_storage base class.

  // -> std::_SMF_control (typedef to std::_Variant_base)
  ValueObjectSP container_sp = outer.GetSP()->GetChildAtIndex(0);
  if (!container_sp)
    return nullptr;
  // -> std::_Variant_storage
  container_sp = container_sp->GetChildAtIndex(0);
  if (!container_sp)
    return nullptr;

  for (int64_t i = 0; i < index; i++) {
    container_sp = GetTail(*container_sp);
    if (!container_sp)
      return nullptr;
  }
  return container_sp;
}

} // namespace

bool formatters::IsMsvcStlVariant(ValueObject &valobj) {
  if (auto valobj_sp = valobj.GetNonSyntheticValue()) {
    return valobj_sp->GetChildMemberWithName("_Which") != nullptr;
  }
  return false;
}

bool formatters::MsvcStlVariantSummaryProvider(
    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
  ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
  if (!valobj_sp)
    return false;

  auto index = GetIndexValue(*valobj_sp);
  if (!index)
    return false;

  if (*index < 0) {
    stream.Printf(" No Value");
    return true;
  }

  ValueObjectSP storage = GetNthStorage(*valobj_sp, *index);
  if (!storage)
    return false;
  CompilerType storage_type = storage->GetCompilerType();
  if (!storage_type)
    return false;
  // Resolve the typedef
  if (storage_type.IsTypedefType())
    storage_type = storage_type.GetTypedefedType();

  CompilerType active_type = storage_type.GetTypeTemplateArgument(1, true);
  if (!active_type)
    return false;

  stream << " Active Type = " << active_type.GetDisplayTypeName() << " ";
  return true;
}

namespace {
class VariantFrontEnd : public SyntheticChildrenFrontEnd {
public:
  VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
    Update();
  }

  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
    auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
    if (!optional_idx) {
      return llvm::createStringError("Type has no child named '%s'",
                                     name.AsCString());
    }
    return *optional_idx;
  }

  lldb::ChildCacheState Update() override;
  llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }
  ValueObjectSP GetChildAtIndex(uint32_t idx) override;

private:
  size_t m_size = 0;
};
} // namespace

lldb::ChildCacheState VariantFrontEnd::Update() {
  m_size = 0;

  auto index = GetIndexValue(m_backend);
  if (index && *index >= 0)
    m_size = 1;

  return lldb::ChildCacheState::eRefetch;
}

ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) {
  if (idx >= m_size)
    return nullptr;

  auto index = GetIndexValue(m_backend);
  if (!index)
    return nullptr;

  ValueObjectSP storage_sp = GetNthStorage(m_backend, *index);
  if (!storage_sp)
    return nullptr;

  ValueObjectSP head_sp = GetHead(*storage_sp);
  if (!head_sp)
    return nullptr;

  return head_sp->Clone(ConstString("Value"));
}

SyntheticChildrenFrontEnd *formatters::MsvcStlVariantSyntheticFrontEndCreator(
    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
  if (valobj_sp)
    return new VariantFrontEnd(*valobj_sp);
  return nullptr;
}