aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp281
1 files changed, 281 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
new file mode 100644
index 000000000000..62794318e077
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
@@ -0,0 +1,281 @@
+//===-- LibCxxVariant.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 "LibCxxVariant.h"
+#include "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "llvm/ADT/ScopeExit.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// libc++ variant implementation contains two members that we care about both
+// are contained in the __impl member.
+// - __index which tells us which of the variadic template types is the active
+// type for the variant
+// - __data is a variadic union which recursively contains itself as member
+// which refers to the tailing variadic types.
+// - __head which refers to the leading non pack type
+// - __value refers to the actual value contained
+// - __tail which refers to the remaining pack types
+//
+// e.g. given std::variant<int,double,char> v1
+//
+// (lldb) frame var -R v1.__impl.__data
+//(... __union<... 0, int, double, char>) v1.__impl.__data = {
+// ...
+// __head = {
+// __value = ...
+// }
+// __tail = {
+// ...
+// __head = {
+// __value = ...
+// }
+// __tail = {
+// ...
+// __head = {
+// __value = ...
+// ...
+//
+// So given
+// - __index equal to 0 the active value is contained in
+//
+// __data.__head.__value
+//
+// - __index equal to 1 the active value is contained in
+//
+// __data.__tail.__head.__value
+//
+// - __index equal to 2 the active value is contained in
+//
+// __data.__tail.__tail.__head.__value
+//
+
+namespace {
+// libc++ std::variant index could have one of three states
+// 1) Valid, we can obtain it and its not variant_npos
+// 2) Invalid, we can't obtain it or it is not a type we expect
+// 3) NPos, its value is variant_npos which means the variant has no value
+enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos };
+
+uint64_t VariantNposValue(uint64_t index_byte_size) {
+ switch (index_byte_size) {
+ case 1:
+ return static_cast<uint8_t>(-1);
+ case 2:
+ return static_cast<uint16_t>(-1);
+ case 4:
+ return static_cast<uint32_t>(-1);
+ }
+ lldbassert(false && "Unknown index type size");
+ return static_cast<uint32_t>(-1); // Fallback to stable ABI type.
+}
+
+LibcxxVariantIndexValidity
+LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
+ ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
+
+ if (!index_sp)
+ return LibcxxVariantIndexValidity::Invalid;
+
+ // In the stable ABI, the type of __index is just int.
+ // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is
+ // enabled, the type can either be unsigned char/short/int depending on
+ // how many variant types there are.
+ // We only need to do this here when comparing against npos, because npos is
+ // just `-1`, but that translates to different unsigned values depending on
+ // the byte size.
+ CompilerType index_type = index_sp->GetCompilerType();
+
+ std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr);
+ if (!index_type_bytes)
+ return LibcxxVariantIndexValidity::Invalid;
+
+ uint64_t npos_value = VariantNposValue(*index_type_bytes);
+ uint64_t index_value = index_sp->GetValueAsUnsigned(0);
+
+ if (index_value == npos_value)
+ return LibcxxVariantIndexValidity::NPos;
+
+ return LibcxxVariantIndexValidity::Valid;
+}
+
+std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
+ ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
+
+ if (!index_sp)
+ return {};
+
+ return {index_sp->GetValueAsUnsigned(0)};
+}
+
+ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
+ ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data"));
+
+ if (!data_sp)
+ return ValueObjectSP{};
+
+ ValueObjectSP current_level = data_sp;
+ for (uint64_t n = index; n != 0; --n) {
+ ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail"));
+
+ if (!tail_sp)
+ return ValueObjectSP{};
+
+ current_level = tail_sp;
+ }
+
+ return current_level->GetChildMemberWithName("__head");
+}
+} // namespace
+
+namespace lldb_private {
+namespace formatters {
+bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP impl_sp = GetChildMemberWithName(
+ *valobj_sp, {ConstString("__impl_"), ConstString("__impl")});
+
+ if (!impl_sp)
+ return false;
+
+ LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
+
+ if (validity == LibcxxVariantIndexValidity::Invalid)
+ return false;
+
+ if (validity == LibcxxVariantIndexValidity::NPos) {
+ stream.Printf(" No Value");
+ return true;
+ }
+
+ auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
+
+ if (!optional_index_value)
+ return false;
+
+ uint64_t index_value = *optional_index_value;
+
+ ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
+
+ if (!nth_head)
+ return false;
+
+ CompilerType head_type = nth_head->GetCompilerType();
+
+ if (!head_type)
+ return false;
+
+ CompilerType template_type = head_type.GetTypeTemplateArgument(1);
+
+ if (!template_type)
+ return false;
+
+ stream << " Active Type = " << template_type.GetDisplayTypeName() << " ";
+
+ return true;
+}
+} // namespace formatters
+} // namespace lldb_private
+
+namespace {
+class VariantFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ 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;
+ ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
+ m_backend, {ConstString("__impl_"), ConstString("__impl")});
+ if (!impl_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
+
+ if (validity == LibcxxVariantIndexValidity::Invalid)
+ return lldb::ChildCacheState::eRefetch;
+
+ if (validity == LibcxxVariantIndexValidity::NPos)
+ return lldb::ChildCacheState::eReuse;
+
+ m_size = 1;
+
+ return lldb::ChildCacheState::eRefetch;
+}
+
+ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) {
+ if (idx >= m_size)
+ return {};
+
+ ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
+ m_backend, {ConstString("__impl_"), ConstString("__impl")});
+ if (!impl_sp)
+ return {};
+
+ auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
+
+ if (!optional_index_value)
+ return {};
+
+ uint64_t index_value = *optional_index_value;
+
+ ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
+
+ if (!nth_head)
+ return {};
+
+ CompilerType head_type = nth_head->GetCompilerType();
+
+ if (!head_type)
+ return {};
+
+ CompilerType template_type = head_type.GetTypeTemplateArgument(1);
+
+ if (!template_type)
+ return {};
+
+ ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value"));
+
+ if (!head_value)
+ return {};
+
+ return head_value->Clone(ConstString("Value"));
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new VariantFrontEnd(*valobj_sp);
+ return nullptr;
+}