mirror of
https://github.com/intel/llvm.git
synced 2026-01-16 05:32:28 +08:00
[LLDB] Add unary operators Dereference and AddressOf to DIL (#134428)
This commit is contained in:
@@ -3,7 +3,12 @@
|
||||
(* This is currently a subset of the final DIL Language, matching the current
|
||||
DIL implementation. *)
|
||||
|
||||
expression = primary_expression ;
|
||||
expression = unary_expression ;
|
||||
|
||||
unary_expression = unary_operator expression
|
||||
| primary_expression ;
|
||||
|
||||
unary_operator = "*" | "&" ;
|
||||
|
||||
primary_expression = id_expression
|
||||
| "(" expression ")";
|
||||
|
||||
@@ -20,6 +20,13 @@ namespace lldb_private::dil {
|
||||
enum class NodeKind {
|
||||
eErrorNode,
|
||||
eIdentifierNode,
|
||||
eUnaryOpNode,
|
||||
};
|
||||
|
||||
/// The Unary operators recognized by DIL.
|
||||
enum class UnaryOpKind {
|
||||
AddrOf, // "&"
|
||||
Deref, // "*"
|
||||
};
|
||||
|
||||
/// Forward declaration, for use in DIL AST nodes. Definition is at the very
|
||||
@@ -81,6 +88,26 @@ private:
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
class UnaryOpNode : public ASTNode {
|
||||
public:
|
||||
UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP operand)
|
||||
: ASTNode(location, NodeKind::eUnaryOpNode), m_kind(kind),
|
||||
m_operand(std::move(operand)) {}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
|
||||
|
||||
UnaryOpKind kind() const { return m_kind; }
|
||||
ASTNode *operand() const { return m_operand.get(); }
|
||||
|
||||
static bool classof(const ASTNode *node) {
|
||||
return node->GetKind() == NodeKind::eUnaryOpNode;
|
||||
}
|
||||
|
||||
private:
|
||||
UnaryOpKind m_kind;
|
||||
ASTNodeUP m_operand;
|
||||
};
|
||||
|
||||
/// This class contains one Visit method for each specialized type of
|
||||
/// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
|
||||
/// the correct function in the DIL expression evaluator for evaluating that
|
||||
@@ -90,6 +117,8 @@ public:
|
||||
virtual ~Visitor() = default;
|
||||
virtual llvm::Expected<lldb::ValueObjectSP>
|
||||
Visit(const IdentifierNode *node) = 0;
|
||||
virtual llvm::Expected<lldb::ValueObjectSP>
|
||||
Visit(const UnaryOpNode *node) = 0;
|
||||
};
|
||||
|
||||
} // namespace lldb_private::dil
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
private:
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
Visit(const IdentifierNode *node) override;
|
||||
llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
|
||||
|
||||
// Used by the interpreter to create objects, perform casts, etc.
|
||||
lldb::TargetSP m_target;
|
||||
|
||||
@@ -24,11 +24,13 @@ namespace lldb_private::dil {
|
||||
class Token {
|
||||
public:
|
||||
enum Kind {
|
||||
amp,
|
||||
coloncolon,
|
||||
eof,
|
||||
identifier,
|
||||
l_paren,
|
||||
r_paren,
|
||||
star,
|
||||
};
|
||||
|
||||
Token(Kind kind, std::string spelling, uint32_t start)
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
m_detail(std::move(detail)) {}
|
||||
|
||||
DILDiagnosticError(llvm::StringRef expr, const std::string &message,
|
||||
uint32_t loc, uint16_t err_len);
|
||||
uint32_t loc, uint16_t err_len = 1);
|
||||
|
||||
std::unique_ptr<CloneableError> Clone() const override {
|
||||
return std::make_unique<DILDiagnosticError>(m_detail);
|
||||
@@ -83,6 +83,7 @@ private:
|
||||
ASTNodeUP Run();
|
||||
|
||||
ASTNodeUP ParseExpression();
|
||||
ASTNodeUP ParseUnaryExpression();
|
||||
ASTNodeUP ParsePrimaryExpression();
|
||||
|
||||
std::string ParseNestedNameSpecifier();
|
||||
|
||||
@@ -538,7 +538,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
|
||||
auto lex_or_err = dil::DILLexer::Create(var_expr);
|
||||
if (!lex_or_err) {
|
||||
error = Status::FromError(lex_or_err.takeError());
|
||||
return ValueObjectSP();
|
||||
return ValueObjectConstResult::Create(nullptr, std::move(error));
|
||||
}
|
||||
|
||||
// Parse the expression.
|
||||
@@ -547,7 +547,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
|
||||
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
|
||||
if (!tree_or_error) {
|
||||
error = Status::FromError(tree_or_error.takeError());
|
||||
return ValueObjectSP();
|
||||
return ValueObjectConstResult::Create(nullptr, std::move(error));
|
||||
}
|
||||
|
||||
// Evaluate the parsed expression.
|
||||
@@ -558,7 +558,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
|
||||
auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get());
|
||||
if (!valobj_or_error) {
|
||||
error = Status::FromError(valobj_or_error.takeError());
|
||||
return ValueObjectSP();
|
||||
return ValueObjectConstResult::Create(nullptr, std::move(error));
|
||||
}
|
||||
|
||||
return *valobj_or_error;
|
||||
|
||||
@@ -19,4 +19,8 @@ llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
|
||||
return v->Visit(this);
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const {
|
||||
return v->Visit(this);
|
||||
}
|
||||
|
||||
} // namespace lldb_private::dil
|
||||
|
||||
@@ -207,9 +207,11 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
|
||||
m_exe_ctx_scope(frame_sp) {}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) {
|
||||
|
||||
// Traverse an AST pointed by the `node`.
|
||||
return node->Accept(this);
|
||||
// Evaluate an AST.
|
||||
auto value_or_error = node->Accept(this);
|
||||
// Return the computed value-or-error. The caller is responsible for
|
||||
// checking if an error occured during the evaluation.
|
||||
return value_or_error;
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
@@ -232,4 +234,42 @@ Interpreter::Visit(const IdentifierNode *node) {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
Interpreter::Visit(const UnaryOpNode *node) {
|
||||
Status error;
|
||||
auto rhs_or_err = Evaluate(node->operand());
|
||||
if (!rhs_or_err)
|
||||
return rhs_or_err;
|
||||
|
||||
lldb::ValueObjectSP rhs = *rhs_or_err;
|
||||
|
||||
switch (node->kind()) {
|
||||
case UnaryOpKind::Deref: {
|
||||
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
|
||||
if (dynamic_rhs)
|
||||
rhs = dynamic_rhs;
|
||||
|
||||
lldb::ValueObjectSP child_sp = rhs->Dereference(error);
|
||||
if (error.Fail())
|
||||
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
|
||||
node->GetLocation());
|
||||
|
||||
return child_sp;
|
||||
}
|
||||
case UnaryOpKind::AddrOf: {
|
||||
Status error;
|
||||
lldb::ValueObjectSP value = rhs->AddressOf(error);
|
||||
if (error.Fail())
|
||||
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
|
||||
node->GetLocation());
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsupported/invalid operation.
|
||||
return llvm::make_error<DILDiagnosticError>(
|
||||
m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
|
||||
}
|
||||
|
||||
} // namespace lldb_private::dil
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace lldb_private::dil {
|
||||
|
||||
llvm::StringRef Token::GetTokenName(Kind kind) {
|
||||
switch (kind) {
|
||||
case Kind::amp:
|
||||
return "amp";
|
||||
case Kind::coloncolon:
|
||||
return "coloncolon";
|
||||
case Kind::eof:
|
||||
@@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
|
||||
return "l_paren";
|
||||
case Kind::r_paren:
|
||||
return "r_paren";
|
||||
case Token::star:
|
||||
return "star";
|
||||
}
|
||||
llvm_unreachable("Unknown token name");
|
||||
}
|
||||
@@ -82,9 +86,8 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
|
||||
return Token(Token::identifier, maybe_word->str(), position);
|
||||
|
||||
constexpr std::pair<Token::Kind, const char *> operators[] = {
|
||||
{Token::l_paren, "("},
|
||||
{Token::r_paren, ")"},
|
||||
{Token::coloncolon, "::"},
|
||||
{Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("},
|
||||
{Token::r_paren, ")"}, {Token::star, "*"},
|
||||
};
|
||||
for (auto [kind, str] : operators) {
|
||||
if (remainder.consume_front(str))
|
||||
|
||||
@@ -81,7 +81,38 @@ ASTNodeUP DILParser::Run() {
|
||||
// expression:
|
||||
// primary_expression
|
||||
//
|
||||
ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
|
||||
ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
|
||||
|
||||
// Parse an unary_expression.
|
||||
//
|
||||
// unary_expression:
|
||||
// unary_operator expression
|
||||
// primary_expression
|
||||
//
|
||||
// unary_operator:
|
||||
// "&"
|
||||
// "*"
|
||||
//
|
||||
ASTNodeUP DILParser::ParseUnaryExpression() {
|
||||
if (CurToken().IsOneOf({Token::amp, Token::star})) {
|
||||
Token token = CurToken();
|
||||
uint32_t loc = token.GetLocation();
|
||||
m_dil_lexer.Advance();
|
||||
auto rhs = ParseExpression();
|
||||
switch (token.GetKind()) {
|
||||
case Token::star:
|
||||
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Deref,
|
||||
std::move(rhs));
|
||||
case Token::amp:
|
||||
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::AddrOf,
|
||||
std::move(rhs));
|
||||
|
||||
default:
|
||||
llvm_unreachable("invalid token kind");
|
||||
}
|
||||
}
|
||||
return ParsePrimaryExpression();
|
||||
}
|
||||
|
||||
// Parse a primary_expression.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include Makefile.rules
|
||||
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
Test DIL address calculation.
|
||||
"""
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class TestFrameVarDILGlobalVariableLookup(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None):
|
||||
value_dil = super().expect_var_path(expr, value=value, type=type)
|
||||
if compare_to_framevar:
|
||||
self.runCmd("settings set target.experimental.use-DIL false")
|
||||
value_frv = super().expect_var_path(expr, value=value, type=type)
|
||||
self.runCmd("settings set target.experimental.use-DIL true")
|
||||
self.assertEqual(value_dil.GetValue(), value_frv.GetValue())
|
||||
|
||||
def test_frame_var(self):
|
||||
self.build()
|
||||
lldbutil.run_to_source_breakpoint(
|
||||
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
|
||||
)
|
||||
|
||||
self.runCmd("settings set target.experimental.use-DIL true")
|
||||
self.expect_var_path("&x", True, type="int *")
|
||||
self.expect_var_path("r", True, type="int &")
|
||||
self.expect_var_path("&r", True, type="int &*")
|
||||
self.expect_var_path("pr", True, type="int *&")
|
||||
self.expect_var_path("&pr", True, type="int *&*")
|
||||
self.expect_var_path("my_pr", True)
|
||||
self.expect_var_path("&my_pr", True, type="mypr *")
|
||||
self.expect_var_path("&globalVar", True, type="int *")
|
||||
self.expect_var_path("&s_str", True, type="const char **")
|
||||
self.expect_var_path("&argc", True, type="int *")
|
||||
@@ -0,0 +1,16 @@
|
||||
int globalVar = 0xDEADBEEF;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int x = 42;
|
||||
int &r = x;
|
||||
int *p = &x;
|
||||
int *&pr = p;
|
||||
|
||||
typedef int *&mypr;
|
||||
mypr my_pr = p;
|
||||
|
||||
const char *s_str = "hello";
|
||||
|
||||
char c = 1;
|
||||
return 0; // Set a breakpoint here
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include Makefile.rules
|
||||
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
Test DIL pointer arithmetic.
|
||||
"""
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class TestFrameVarDILGlobalVariableLookup(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None):
|
||||
value_dil = super().expect_var_path(expr, value=value, type=type)
|
||||
if compare_to_framevar:
|
||||
self.runCmd("settings set target.experimental.use-DIL false")
|
||||
value_frv = super().expect_var_path(expr, value=value, type=type)
|
||||
self.runCmd("settings set target.experimental.use-DIL true")
|
||||
self.assertEqual(value_dil.GetValue(), value_frv.GetValue())
|
||||
|
||||
def test_dereference(self):
|
||||
self.build()
|
||||
lldbutil.run_to_source_breakpoint(
|
||||
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
|
||||
)
|
||||
|
||||
self.runCmd("settings set target.experimental.use-DIL true")
|
||||
self.expect_var_path("*p_int0", True, value="0")
|
||||
self.expect_var_path("*cp_int5", True, value="5")
|
||||
self.expect_var_path("*rcp_int0", True, type="const int *")
|
||||
self.expect_var_path("*offset_p", True, value="5")
|
||||
self.expect_var_path("*offset_pref", True, type="int *")
|
||||
self.expect_var_path("**pp_int0", value="0")
|
||||
self.expect_var_path("&**pp_int0", type="int *")
|
||||
self.expect(
|
||||
"frame var '*array'",
|
||||
error=True,
|
||||
substrs=["not a pointer or reference type"],
|
||||
)
|
||||
self.expect(
|
||||
"frame var '&*p_null'",
|
||||
error=True,
|
||||
substrs=["doesn't have a valid address"],
|
||||
)
|
||||
self.expect(
|
||||
"frame var '&*p_void'",
|
||||
error=True,
|
||||
substrs=["dereference failed: (void *) p_void"],
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
int main(int argc, char **argv) {
|
||||
int *p_null = nullptr;
|
||||
const char *p_char1 = "hello";
|
||||
|
||||
typedef const char *my_char_ptr;
|
||||
my_char_ptr my_p_char1 = p_char1;
|
||||
|
||||
int offset = 5;
|
||||
int *offset_p = &offset;
|
||||
int *&offset_pref = offset_p;
|
||||
int array[10];
|
||||
array[0] = 0;
|
||||
array[offset] = offset;
|
||||
|
||||
int(&array_ref)[10] = array;
|
||||
|
||||
int *p_int0 = &array[0];
|
||||
int **pp_int0 = &p_int0;
|
||||
const int *cp_int0 = &array[0];
|
||||
const int *cp_int5 = &array[offset];
|
||||
const int *&rcp_int0 = cp_int0;
|
||||
|
||||
typedef int *td_int_ptr_t;
|
||||
td_int_ptr_t td_int_ptr0 = &array[0];
|
||||
|
||||
void *p_void = (void *)p_char1;
|
||||
void **pp_void0 = &p_void;
|
||||
void **pp_void1 = pp_void0 + 1;
|
||||
|
||||
return 0; // Set a breakpoint here
|
||||
}
|
||||
Reference in New Issue
Block a user