Skip to content

Commit d637038

Browse files
authored
[LLDB] Add unary operators Dereference and AddressOf to DIL (#134428)
1 parent 6022a52 commit d637038

File tree

16 files changed

+269
-12
lines changed

16 files changed

+269
-12
lines changed

lldb/docs/dil-expr-lang.ebnf

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
(* This is currently a subset of the final DIL Language, matching the current
44
DIL implementation. *)
55
6-
expression = primary_expression ;
6+
expression = unary_expression ;
7+
8+
unary_expression = unary_operator expression
9+
| primary_expression ;
10+
11+
unary_operator = "*" | "&" ;
712
813
primary_expression = id_expression
914
| "(" expression ")";

lldb/include/lldb/ValueObject/DILAST.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ namespace lldb_private::dil {
2020
enum class NodeKind {
2121
eErrorNode,
2222
eIdentifierNode,
23+
eUnaryOpNode,
24+
};
25+
26+
/// The Unary operators recognized by DIL.
27+
enum class UnaryOpKind {
28+
AddrOf, // "&"
29+
Deref, // "*"
2330
};
2431

2532
/// Forward declaration, for use in DIL AST nodes. Definition is at the very
@@ -81,6 +88,26 @@ class IdentifierNode : public ASTNode {
8188
std::string m_name;
8289
};
8390

91+
class UnaryOpNode : public ASTNode {
92+
public:
93+
UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP operand)
94+
: ASTNode(location, NodeKind::eUnaryOpNode), m_kind(kind),
95+
m_operand(std::move(operand)) {}
96+
97+
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
98+
99+
UnaryOpKind kind() const { return m_kind; }
100+
ASTNode *operand() const { return m_operand.get(); }
101+
102+
static bool classof(const ASTNode *node) {
103+
return node->GetKind() == NodeKind::eUnaryOpNode;
104+
}
105+
106+
private:
107+
UnaryOpKind m_kind;
108+
ASTNodeUP m_operand;
109+
};
110+
84111
/// This class contains one Visit method for each specialized type of
85112
/// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
86113
/// the correct function in the DIL expression evaluator for evaluating that
@@ -90,6 +117,8 @@ class Visitor {
90117
virtual ~Visitor() = default;
91118
virtual llvm::Expected<lldb::ValueObjectSP>
92119
Visit(const IdentifierNode *node) = 0;
120+
virtual llvm::Expected<lldb::ValueObjectSP>
121+
Visit(const UnaryOpNode *node) = 0;
93122
};
94123

95124
} // namespace lldb_private::dil

lldb/include/lldb/ValueObject/DILEval.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class Interpreter : Visitor {
4949
private:
5050
llvm::Expected<lldb::ValueObjectSP>
5151
Visit(const IdentifierNode *node) override;
52+
llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
5253

5354
// Used by the interpreter to create objects, perform casts, etc.
5455
lldb::TargetSP m_target;

lldb/include/lldb/ValueObject/DILLexer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ namespace lldb_private::dil {
2424
class Token {
2525
public:
2626
enum Kind {
27+
amp,
2728
coloncolon,
2829
eof,
2930
identifier,
3031
l_paren,
3132
r_paren,
33+
star,
3234
};
3335

3436
Token(Kind kind, std::string spelling, uint32_t start)

lldb/include/lldb/ValueObject/DILParser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class DILDiagnosticError
4343
m_detail(std::move(detail)) {}
4444

4545
DILDiagnosticError(llvm::StringRef expr, const std::string &message,
46-
uint32_t loc, uint16_t err_len);
46+
uint32_t loc, uint16_t err_len = 1);
4747

4848
std::unique_ptr<CloneableError> Clone() const override {
4949
return std::make_unique<DILDiagnosticError>(m_detail);
@@ -83,6 +83,7 @@ class DILParser {
8383
ASTNodeUP Run();
8484

8585
ASTNodeUP ParseExpression();
86+
ASTNodeUP ParseUnaryExpression();
8687
ASTNodeUP ParsePrimaryExpression();
8788

8889
std::string ParseNestedNameSpecifier();

lldb/source/Target/StackFrame.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
538538
auto lex_or_err = dil::DILLexer::Create(var_expr);
539539
if (!lex_or_err) {
540540
error = Status::FromError(lex_or_err.takeError());
541-
return ValueObjectSP();
541+
return ValueObjectConstResult::Create(nullptr, std::move(error));
542542
}
543543

544544
// Parse the expression.
@@ -547,7 +547,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
547547
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
548548
if (!tree_or_error) {
549549
error = Status::FromError(tree_or_error.takeError());
550-
return ValueObjectSP();
550+
return ValueObjectConstResult::Create(nullptr, std::move(error));
551551
}
552552

553553
// Evaluate the parsed expression.
@@ -558,7 +558,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
558558
auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get());
559559
if (!valobj_or_error) {
560560
error = Status::FromError(valobj_or_error.takeError());
561-
return ValueObjectSP();
561+
return ValueObjectConstResult::Create(nullptr, std::move(error));
562562
}
563563

564564
return *valobj_or_error;

lldb/source/ValueObject/DILAST.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
1919
return v->Visit(this);
2020
}
2121

22+
llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const {
23+
return v->Visit(this);
24+
}
25+
2226
} // namespace lldb_private::dil

lldb/source/ValueObject/DILEval.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,11 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
207207
m_exe_ctx_scope(frame_sp) {}
208208

209209
llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) {
210-
211-
// Traverse an AST pointed by the `node`.
212-
return node->Accept(this);
210+
// Evaluate an AST.
211+
auto value_or_error = node->Accept(this);
212+
// Return the computed value-or-error. The caller is responsible for
213+
// checking if an error occured during the evaluation.
214+
return value_or_error;
213215
}
214216

215217
llvm::Expected<lldb::ValueObjectSP>
@@ -232,4 +234,42 @@ Interpreter::Visit(const IdentifierNode *node) {
232234
return identifier;
233235
}
234236

237+
llvm::Expected<lldb::ValueObjectSP>
238+
Interpreter::Visit(const UnaryOpNode *node) {
239+
Status error;
240+
auto rhs_or_err = Evaluate(node->operand());
241+
if (!rhs_or_err)
242+
return rhs_or_err;
243+
244+
lldb::ValueObjectSP rhs = *rhs_or_err;
245+
246+
switch (node->kind()) {
247+
case UnaryOpKind::Deref: {
248+
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
249+
if (dynamic_rhs)
250+
rhs = dynamic_rhs;
251+
252+
lldb::ValueObjectSP child_sp = rhs->Dereference(error);
253+
if (error.Fail())
254+
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
255+
node->GetLocation());
256+
257+
return child_sp;
258+
}
259+
case UnaryOpKind::AddrOf: {
260+
Status error;
261+
lldb::ValueObjectSP value = rhs->AddressOf(error);
262+
if (error.Fail())
263+
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
264+
node->GetLocation());
265+
266+
return value;
267+
}
268+
}
269+
270+
// Unsupported/invalid operation.
271+
return llvm::make_error<DILDiagnosticError>(
272+
m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
273+
}
274+
235275
} // namespace lldb_private::dil

lldb/source/ValueObject/DILLexer.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ namespace lldb_private::dil {
1919

2020
llvm::StringRef Token::GetTokenName(Kind kind) {
2121
switch (kind) {
22+
case Kind::amp:
23+
return "amp";
2224
case Kind::coloncolon:
2325
return "coloncolon";
2426
case Kind::eof:
@@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
2931
return "l_paren";
3032
case Kind::r_paren:
3133
return "r_paren";
34+
case Token::star:
35+
return "star";
3236
}
3337
llvm_unreachable("Unknown token name");
3438
}
@@ -82,9 +86,8 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
8286
return Token(Token::identifier, maybe_word->str(), position);
8387

8488
constexpr std::pair<Token::Kind, const char *> operators[] = {
85-
{Token::l_paren, "("},
86-
{Token::r_paren, ")"},
87-
{Token::coloncolon, "::"},
89+
{Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("},
90+
{Token::r_paren, ")"}, {Token::star, "*"},
8891
};
8992
for (auto [kind, str] : operators) {
9093
if (remainder.consume_front(str))

lldb/source/ValueObject/DILParser.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,38 @@ ASTNodeUP DILParser::Run() {
8181
// expression:
8282
// primary_expression
8383
//
84-
ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
84+
ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
85+
86+
// Parse an unary_expression.
87+
//
88+
// unary_expression:
89+
// unary_operator expression
90+
// primary_expression
91+
//
92+
// unary_operator:
93+
// "&"
94+
// "*"
95+
//
96+
ASTNodeUP DILParser::ParseUnaryExpression() {
97+
if (CurToken().IsOneOf({Token::amp, Token::star})) {
98+
Token token = CurToken();
99+
uint32_t loc = token.GetLocation();
100+
m_dil_lexer.Advance();
101+
auto rhs = ParseExpression();
102+
switch (token.GetKind()) {
103+
case Token::star:
104+
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Deref,
105+
std::move(rhs));
106+
case Token::amp:
107+
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::AddrOf,
108+
std::move(rhs));
109+
110+
default:
111+
llvm_unreachable("invalid token kind");
112+
}
113+
}
114+
return ParsePrimaryExpression();
115+
}
85116

86117
// Parse a primary_expression.
87118
//
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
Test DIL address calculation.
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.lldbtest import *
7+
from lldbsuite.test.decorators import *
8+
from lldbsuite.test import lldbutil
9+
10+
11+
class TestFrameVarDILGlobalVariableLookup(TestBase):
12+
NO_DEBUG_INFO_TESTCASE = True
13+
14+
def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None):
15+
value_dil = super().expect_var_path(expr, value=value, type=type)
16+
if compare_to_framevar:
17+
self.runCmd("settings set target.experimental.use-DIL false")
18+
value_frv = super().expect_var_path(expr, value=value, type=type)
19+
self.runCmd("settings set target.experimental.use-DIL true")
20+
self.assertEqual(value_dil.GetValue(), value_frv.GetValue())
21+
22+
def test_frame_var(self):
23+
self.build()
24+
lldbutil.run_to_source_breakpoint(
25+
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
26+
)
27+
28+
self.runCmd("settings set target.experimental.use-DIL true")
29+
self.expect_var_path("&x", True, type="int *")
30+
self.expect_var_path("r", True, type="int &")
31+
self.expect_var_path("&r", True, type="int &*")
32+
self.expect_var_path("pr", True, type="int *&")
33+
self.expect_var_path("&pr", True, type="int *&*")
34+
self.expect_var_path("my_pr", True)
35+
self.expect_var_path("&my_pr", True, type="mypr *")
36+
self.expect_var_path("&globalVar", True, type="int *")
37+
self.expect_var_path("&s_str", True, type="const char **")
38+
self.expect_var_path("&argc", True, type="int *")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
int globalVar = 0xDEADBEEF;
2+
3+
int main(int argc, char **argv) {
4+
int x = 42;
5+
int &r = x;
6+
int *p = &x;
7+
int *&pr = p;
8+
9+
typedef int *&mypr;
10+
mypr my_pr = p;
11+
12+
const char *s_str = "hello";
13+
14+
char c = 1;
15+
return 0; // Set a breakpoint here
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""
2+
Test DIL pointer arithmetic.
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.lldbtest import *
7+
from lldbsuite.test.decorators import *
8+
from lldbsuite.test import lldbutil
9+
10+
11+
class TestFrameVarDILGlobalVariableLookup(TestBase):
12+
NO_DEBUG_INFO_TESTCASE = True
13+
14+
def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None):
15+
value_dil = super().expect_var_path(expr, value=value, type=type)
16+
if compare_to_framevar:
17+
self.runCmd("settings set target.experimental.use-DIL false")
18+
value_frv = super().expect_var_path(expr, value=value, type=type)
19+
self.runCmd("settings set target.experimental.use-DIL true")
20+
self.assertEqual(value_dil.GetValue(), value_frv.GetValue())
21+
22+
def test_dereference(self):
23+
self.build()
24+
lldbutil.run_to_source_breakpoint(
25+
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
26+
)
27+
28+
self.runCmd("settings set target.experimental.use-DIL true")
29+
self.expect_var_path("*p_int0", True, value="0")
30+
self.expect_var_path("*cp_int5", True, value="5")
31+
self.expect_var_path("*rcp_int0", True, type="const int *")
32+
self.expect_var_path("*offset_p", True, value="5")
33+
self.expect_var_path("*offset_pref", True, type="int *")
34+
self.expect_var_path("**pp_int0", value="0")
35+
self.expect_var_path("&**pp_int0", type="int *")
36+
self.expect(
37+
"frame var '*array'",
38+
error=True,
39+
substrs=["not a pointer or reference type"],
40+
)
41+
self.expect(
42+
"frame var '&*p_null'",
43+
error=True,
44+
substrs=["doesn't have a valid address"],
45+
)
46+
self.expect(
47+
"frame var '&*p_void'",
48+
error=True,
49+
substrs=["dereference failed: (void *) p_void"],
50+
)

0 commit comments

Comments
 (0)