mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 07:31:28 +00:00
Implement data formatters for LibStdC++ std::variant (#68012)
This patch implements the data formatters for LibStdC++ `std::variant`. --------- Co-authored-by: jeffreytan81 <jeffreytan@fb.com>
This commit is contained in:
parent
1493462868
commit
1ec4330f7e
@ -892,3 +892,87 @@ class StdDequeSynthProvider:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def VariantSummaryProvider(valobj, dict):
|
||||||
|
raw_obj = valobj.GetNonSyntheticValue()
|
||||||
|
index_obj = raw_obj.GetChildMemberWithName("_M_index")
|
||||||
|
data_obj = raw_obj.GetChildMemberWithName("_M_u")
|
||||||
|
if not (index_obj and index_obj.IsValid() and data_obj and data_obj.IsValid()):
|
||||||
|
return "<Can't find _M_index or _M_u>"
|
||||||
|
|
||||||
|
def get_variant_npos_value(index_byte_size):
|
||||||
|
if index_byte_size == 1:
|
||||||
|
return 0xFF
|
||||||
|
elif index_byte_size == 2:
|
||||||
|
return 0xFFFF
|
||||||
|
else:
|
||||||
|
return 0xFFFFFFFF
|
||||||
|
|
||||||
|
npos_value = get_variant_npos_value(index_obj.GetByteSize())
|
||||||
|
index = index_obj.GetValueAsUnsigned(0)
|
||||||
|
if index == npos_value:
|
||||||
|
return " No Value"
|
||||||
|
|
||||||
|
active_type = data_obj.GetType().GetTemplateArgumentType(index)
|
||||||
|
return f" Active Type = {active_type.GetDisplayTypeName()} "
|
||||||
|
|
||||||
|
|
||||||
|
class VariantSynthProvider:
|
||||||
|
def __init__(self, valobj, dict):
|
||||||
|
self.raw_obj = valobj.GetNonSyntheticValue()
|
||||||
|
self.is_valid = False
|
||||||
|
self.index = None
|
||||||
|
self.data_obj = None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
try:
|
||||||
|
self.index = self.raw_obj.GetChildMemberWithName(
|
||||||
|
"_M_index"
|
||||||
|
).GetValueAsSigned(-1)
|
||||||
|
self.is_valid = self.index != -1
|
||||||
|
self.data_obj = self.raw_obj.GetChildMemberWithName("_M_u")
|
||||||
|
except:
|
||||||
|
self.is_valid = False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_children(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
return 1 if self.is_valid else 0
|
||||||
|
|
||||||
|
def get_child_index(self, name):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
if not self.is_valid:
|
||||||
|
return None
|
||||||
|
cur = 0
|
||||||
|
node = self.data_obj
|
||||||
|
while cur < self.index:
|
||||||
|
node = node.GetChildMemberWithName("_M_rest")
|
||||||
|
cur += 1
|
||||||
|
|
||||||
|
# _M_storage's type depends on variant field's type "_Type".
|
||||||
|
# 1. if '_Type' is literal type: _Type _M_storage.
|
||||||
|
# 2. otherwise, __gnu_cxx::__aligned_membuf<_Type> _M_storage.
|
||||||
|
#
|
||||||
|
# For 2. we have to cast it to underlying template _Type.
|
||||||
|
|
||||||
|
value = node.GetChildMemberWithName("_M_first").GetChildMemberWithName(
|
||||||
|
"_M_storage"
|
||||||
|
)
|
||||||
|
template_type = value.GetType().GetTemplateArgumentType(0)
|
||||||
|
|
||||||
|
# Literal type will return None for GetTemplateArgumentType(0)
|
||||||
|
if (
|
||||||
|
template_type
|
||||||
|
and "__gnu_cxx::__aligned_membuf" in value.GetType().GetDisplayTypeName()
|
||||||
|
and template_type.IsValid()
|
||||||
|
):
|
||||||
|
value = value.Cast(template_type)
|
||||||
|
|
||||||
|
if value.IsValid():
|
||||||
|
return value.Clone("Value")
|
||||||
|
return None
|
||||||
|
@ -332,14 +332,12 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) {
|
|||||||
// If we can't parse the incoming name, then just check that it contains path.
|
// If we can't parse the incoming name, then just check that it contains path.
|
||||||
if (m_parse_error)
|
if (m_parse_error)
|
||||||
return m_full.GetStringRef().contains(path);
|
return m_full.GetStringRef().contains(path);
|
||||||
|
|
||||||
llvm::StringRef identifier;
|
llvm::StringRef identifier;
|
||||||
llvm::StringRef context;
|
llvm::StringRef context;
|
||||||
std::string path_str = path.str();
|
std::string path_str = path.str();
|
||||||
bool success
|
bool success = CPlusPlusLanguage::ExtractContextAndIdentifier(
|
||||||
= CPlusPlusLanguage::ExtractContextAndIdentifier(path_str.c_str(),
|
path_str.c_str(), context, identifier);
|
||||||
context,
|
|
||||||
identifier);
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return m_full.GetStringRef().contains(path);
|
return m_full.GetStringRef().contains(path);
|
||||||
|
|
||||||
@ -372,7 +370,7 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) {
|
|||||||
return false;
|
return false;
|
||||||
if (haystack.empty() || !isalnum(haystack.back()))
|
if (haystack.empty() || !isalnum(haystack.back()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +386,7 @@ bool CPlusPlusLanguage::IsCPPMangledName(llvm::StringRef name) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path,
|
bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path,
|
||||||
ConstString demangled) const {
|
ConstString demangled) const {
|
||||||
MethodName demangled_name(demangled);
|
MethodName demangled_name(demangled);
|
||||||
return demangled_name.ContainsPath(path);
|
return demangled_name.ContainsPath(path);
|
||||||
@ -1104,6 +1102,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
|
|||||||
SyntheticChildrenSP(new ScriptedSyntheticChildren(
|
SyntheticChildrenSP(new ScriptedSyntheticChildren(
|
||||||
stl_synth_flags,
|
stl_synth_flags,
|
||||||
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
|
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
|
||||||
|
cpp_category_sp->AddTypeSynthetic(
|
||||||
|
"^std::variant<.+>$", eFormatterMatchRegex,
|
||||||
|
SyntheticChildrenSP(new ScriptedSyntheticChildren(
|
||||||
|
stl_synth_flags,
|
||||||
|
"lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider")));
|
||||||
|
|
||||||
stl_summary_flags.SetDontShowChildren(false);
|
stl_summary_flags.SetDontShowChildren(false);
|
||||||
stl_summary_flags.SetSkipPointers(false);
|
stl_summary_flags.SetSkipPointers(false);
|
||||||
@ -1148,6 +1151,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
|
|||||||
TypeSummaryImplSP(new ScriptSummaryFormat(
|
TypeSummaryImplSP(new ScriptSummaryFormat(
|
||||||
stl_summary_flags,
|
stl_summary_flags,
|
||||||
"lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider")));
|
"lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider")));
|
||||||
|
cpp_category_sp->AddTypeSummary(
|
||||||
|
"^std::variant<.+>$", eFormatterMatchRegex,
|
||||||
|
TypeSummaryImplSP(new ScriptSummaryFormat(
|
||||||
|
stl_summary_flags,
|
||||||
|
"lldb.formatters.cpp.gnu_libstdcpp.VariantSummaryProvider")));
|
||||||
|
|
||||||
AddCXXSynthetic(
|
AddCXXSynthetic(
|
||||||
cpp_category_sp,
|
cpp_category_sp,
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
CXX_SOURCES := main.cpp
|
||||||
|
|
||||||
|
USE_LIBSTDCPP := 1
|
||||||
|
CXXFLAGS_EXTRAS := -std=c++17
|
||||||
|
include Makefile.rules
|
@ -0,0 +1,69 @@
|
|||||||
|
"""
|
||||||
|
Test lldb data formatter for LibStdC++ std::variant.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import lldb
|
||||||
|
from lldbsuite.test.decorators import *
|
||||||
|
from lldbsuite.test.lldbtest import *
|
||||||
|
from lldbsuite.test import lldbutil
|
||||||
|
|
||||||
|
|
||||||
|
class LibStdcxxVariantDataFormatterTestCase(TestBase):
|
||||||
|
@add_test_categories(["libstdcxx"])
|
||||||
|
def test_with_run_command(self):
|
||||||
|
"""Test LibStdC++ std::variant data formatter works correctly."""
|
||||||
|
self.build()
|
||||||
|
|
||||||
|
(self.target, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(
|
||||||
|
self, "// break here", lldb.SBFileSpec("main.cpp", False)
|
||||||
|
)
|
||||||
|
|
||||||
|
lldbutil.continue_to_breakpoint(self.process, bkpt)
|
||||||
|
|
||||||
|
self.expect(
|
||||||
|
"frame variable v1",
|
||||||
|
substrs=["v1 = Active Type = int {", "Value = 12", "}"],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.expect(
|
||||||
|
"frame variable v1_ref",
|
||||||
|
substrs=["v1_ref = Active Type = int : {", "Value = 12", "}"],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.expect(
|
||||||
|
"frame variable v_v1",
|
||||||
|
substrs=[
|
||||||
|
"v_v1 = Active Type = std::variant<int, double, char> {",
|
||||||
|
"Value = Active Type = int {",
|
||||||
|
"Value = 12",
|
||||||
|
"}",
|
||||||
|
"}",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
lldbutil.continue_to_breakpoint(self.process, bkpt)
|
||||||
|
|
||||||
|
self.expect(
|
||||||
|
"frame variable v1",
|
||||||
|
substrs=["v1 = Active Type = double {", "Value = 2", "}"],
|
||||||
|
)
|
||||||
|
|
||||||
|
lldbutil.continue_to_breakpoint(self.process, bkpt)
|
||||||
|
|
||||||
|
self.expect(
|
||||||
|
"frame variable v2",
|
||||||
|
substrs=["v2 = Active Type = double {", "Value = 2", "}"],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.expect(
|
||||||
|
"frame variable v3",
|
||||||
|
substrs=["v3 = Active Type = char {", "Value = 'A'", "}"],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.expect("frame variable v_no_value", substrs=["v_no_value = No Value"])
|
||||||
|
|
||||||
|
self.expect(
|
||||||
|
"frame variable v_many_types_no_value",
|
||||||
|
substrs=["v_many_types_no_value = No Value"],
|
||||||
|
)
|
@ -0,0 +1,79 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
operator int() { throw 42; }
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
bool has_variant = true;
|
||||||
|
|
||||||
|
printf("%d\n", has_variant); // break here
|
||||||
|
|
||||||
|
std::variant<int, double, char> v1;
|
||||||
|
std::variant<int, double, char> &v1_ref = v1;
|
||||||
|
std::variant<int, double, char> v2;
|
||||||
|
std::variant<int, double, char> v3;
|
||||||
|
std::variant<std::variant<int, double, char>> v_v1;
|
||||||
|
std::variant<int, double, char> v_no_value;
|
||||||
|
// The next variant has many types, meaning the type index does not fit in
|
||||||
|
// a byte and must be `unsigned short` instead of `unsigned char` when
|
||||||
|
// using the unstable libc++ ABI. With stable libc++ ABI, the type index
|
||||||
|
// is always just `unsigned int`.
|
||||||
|
std::variant<
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
|
||||||
|
int, int, int, int, int, int, int, int, int, int, int, int>
|
||||||
|
v_many_types_no_value;
|
||||||
|
|
||||||
|
v1 = 12; // v contains int
|
||||||
|
v_v1 = v1;
|
||||||
|
int i = std::get<int>(v1);
|
||||||
|
printf("%d\n", i); // break here
|
||||||
|
|
||||||
|
v2 = 2.0;
|
||||||
|
double d = std::get<double>(v2);
|
||||||
|
printf("%f\n", d);
|
||||||
|
|
||||||
|
v3 = 'A';
|
||||||
|
char c = std::get<char>(v3);
|
||||||
|
printf("%d\n", c);
|
||||||
|
|
||||||
|
// Checking v1 above and here to make sure we done maintain the incorrect
|
||||||
|
// state when we change its value.
|
||||||
|
v1 = 2.0;
|
||||||
|
d = std::get<double>(v1);
|
||||||
|
printf("%f\n", d); // break here
|
||||||
|
|
||||||
|
try {
|
||||||
|
v_no_value.emplace<0>(S());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%zu\n", v_no_value.index());
|
||||||
|
|
||||||
|
try {
|
||||||
|
v_many_types_no_value.emplace<0>(S());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%zu\n", v_many_types_no_value.index());
|
||||||
|
|
||||||
|
return 0; // break here
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user