Skip to content

Commit cc7f738

Browse files
cmticesivan-shani
authored andcommitted
[LLDB] Add field member operators to DIL (llvm#138093)
Add the arrow and period operators, allowing DIL to find and access member fields.
1 parent 073b096 commit cc7f738

File tree

19 files changed

+545
-20
lines changed

19 files changed

+545
-20
lines changed

lldb/docs/dil-expr-lang.ebnf

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55
66
expression = unary_expression ;
77
8-
unary_expression = unary_operator expression
9-
| primary_expression ;
8+
unary_expression = postfix_expression
9+
| unary_operator expression ;
1010
1111
unary_operator = "*" | "&" ;
1212
13+
postfix_expresson = primary_expression
14+
| postfix_expression "." id_expression
15+
| postfix_expression "->" id_expression ;
16+
1317
primary_expression = id_expression
14-
| "(" expression ")";
18+
| "(" expression ")" ;
1519
1620
id_expression = unqualified_id
1721
| qualified_id

lldb/include/lldb/ValueObject/DILAST.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace lldb_private::dil {
2020
enum class NodeKind {
2121
eErrorNode,
2222
eIdentifierNode,
23+
eMemberOfNode,
2324
eUnaryOpNode,
2425
};
2526

@@ -88,6 +89,29 @@ class IdentifierNode : public ASTNode {
8889
std::string m_name;
8990
};
9091

92+
class MemberOfNode : public ASTNode {
93+
public:
94+
MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow,
95+
std::string name)
96+
: ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
97+
m_is_arrow(is_arrow), m_field_name(std::move(name)) {}
98+
99+
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
100+
101+
ASTNode *GetBase() const { return m_base.get(); }
102+
bool GetIsArrow() const { return m_is_arrow; }
103+
llvm::StringRef GetFieldName() const { return llvm::StringRef(m_field_name); }
104+
105+
static bool classof(const ASTNode *node) {
106+
return node->GetKind() == NodeKind::eMemberOfNode;
107+
}
108+
109+
private:
110+
ASTNodeUP m_base;
111+
bool m_is_arrow;
112+
std::string m_field_name;
113+
};
114+
91115
class UnaryOpNode : public ASTNode {
92116
public:
93117
UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP operand)
@@ -118,6 +142,8 @@ class Visitor {
118142
virtual llvm::Expected<lldb::ValueObjectSP>
119143
Visit(const IdentifierNode *node) = 0;
120144
virtual llvm::Expected<lldb::ValueObjectSP>
145+
Visit(const MemberOfNode *node) = 0;
146+
virtual llvm::Expected<lldb::ValueObjectSP>
121147
Visit(const UnaryOpNode *node) = 0;
122148
};
123149

lldb/include/lldb/ValueObject/DILEval.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,27 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref,
4141
class Interpreter : Visitor {
4242
public:
4343
Interpreter(lldb::TargetSP target, llvm::StringRef expr,
44-
lldb::DynamicValueType use_dynamic,
45-
std::shared_ptr<StackFrame> frame_sp);
44+
std::shared_ptr<StackFrame> frame_sp,
45+
lldb::DynamicValueType use_dynamic, bool use_synthetic,
46+
bool fragile_ivar, bool check_ptr_vs_member);
4647

4748
llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node);
4849

4950
private:
5051
llvm::Expected<lldb::ValueObjectSP>
5152
Visit(const IdentifierNode *node) override;
53+
llvm::Expected<lldb::ValueObjectSP> Visit(const MemberOfNode *node) override;
5254
llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
5355

5456
// Used by the interpreter to create objects, perform casts, etc.
5557
lldb::TargetSP m_target;
5658
llvm::StringRef m_expr;
5759
lldb::ValueObjectSP m_scope;
58-
lldb::DynamicValueType m_default_dynamic;
5960
std::shared_ptr<StackFrame> m_exe_ctx_scope;
61+
lldb::DynamicValueType m_use_dynamic;
62+
bool m_use_synthetic;
63+
bool m_fragile_ivar;
64+
bool m_check_ptr_vs_member;
6065
};
6166

6267
} // namespace lldb_private::dil

lldb/include/lldb/ValueObject/DILLexer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ class Token {
2525
public:
2626
enum Kind {
2727
amp,
28+
arrow,
2829
coloncolon,
2930
eof,
3031
identifier,
3132
l_paren,
33+
period,
3234
r_paren,
3335
star,
3436
};

lldb/include/lldb/ValueObject/DILParser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class DILParser {
8484

8585
ASTNodeUP ParseExpression();
8686
ASTNodeUP ParseUnaryExpression();
87+
ASTNodeUP ParsePostfixExpression();
8788
ASTNodeUP ParsePrimaryExpression();
8889

8990
std::string ParseNestedNameSpecifier();
@@ -117,6 +118,8 @@ class DILParser {
117118

118119
lldb::DynamicValueType m_use_dynamic;
119120
bool m_use_synthetic;
121+
bool m_fragile_ivar;
122+
bool m_check_ptr_vs_member;
120123
}; // class DILParser
121124

122125
} // namespace lldb_private::dil

lldb/source/Target/StackFrame.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,8 +552,9 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
552552

553553
// Evaluate the parsed expression.
554554
lldb::TargetSP target = this->CalculateTarget();
555-
dil::Interpreter interpreter(target, var_expr, use_dynamic,
556-
shared_from_this());
555+
dil::Interpreter interpreter(target, var_expr, shared_from_this(),
556+
use_dynamic, !no_synth_child, !no_fragile_ivar,
557+
check_ptr_vs_member);
557558

558559
auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get());
559560
if (!valobj_or_error) {

lldb/source/ValueObject/DILAST.cpp

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

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

lldb/source/ValueObject/DILEval.cpp

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,13 @@ lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
201201
}
202202

203203
Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
204-
lldb::DynamicValueType use_dynamic,
205-
std::shared_ptr<StackFrame> frame_sp)
206-
: m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
207-
m_exe_ctx_scope(frame_sp) {}
204+
std::shared_ptr<StackFrame> frame_sp,
205+
lldb::DynamicValueType use_dynamic, bool use_synthetic,
206+
bool fragile_ivar, bool check_ptr_vs_member)
207+
: m_target(std::move(target)), m_expr(expr), m_exe_ctx_scope(frame_sp),
208+
m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic),
209+
m_fragile_ivar(fragile_ivar), m_check_ptr_vs_member(check_ptr_vs_member) {
210+
}
208211

209212
llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) {
210213
// Evaluate an AST.
@@ -216,7 +219,7 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) {
216219

217220
llvm::Expected<lldb::ValueObjectSP>
218221
Interpreter::Visit(const IdentifierNode *node) {
219-
lldb::DynamicValueType use_dynamic = m_default_dynamic;
222+
lldb::DynamicValueType use_dynamic = m_use_dynamic;
220223

221224
lldb::ValueObjectSP identifier =
222225
LookupIdentifier(node->GetName(), m_exe_ctx_scope, use_dynamic);
@@ -245,7 +248,7 @@ Interpreter::Visit(const UnaryOpNode *node) {
245248

246249
switch (node->kind()) {
247250
case UnaryOpKind::Deref: {
248-
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
251+
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_use_dynamic);
249252
if (dynamic_rhs)
250253
rhs = dynamic_rhs;
251254

@@ -272,4 +275,112 @@ Interpreter::Visit(const UnaryOpNode *node) {
272275
m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
273276
}
274277

278+
llvm::Expected<lldb::ValueObjectSP>
279+
Interpreter::Visit(const MemberOfNode *node) {
280+
auto base_or_err = Evaluate(node->GetBase());
281+
if (!base_or_err)
282+
return base_or_err;
283+
lldb::ValueObjectSP base = *base_or_err;
284+
285+
// Perform some basic type & correctness checking.
286+
if (node->GetIsArrow()) {
287+
if (!m_fragile_ivar) {
288+
// Make sure we aren't trying to deref an objective
289+
// C ivar if this is not allowed
290+
const uint32_t pointer_type_flags =
291+
base->GetCompilerType().GetTypeInfo(nullptr);
292+
if ((pointer_type_flags & lldb::eTypeIsObjC) &&
293+
(pointer_type_flags & lldb::eTypeIsPointer)) {
294+
// This was an objective C object pointer and it was requested we
295+
// skip any fragile ivars so return nothing here
296+
return lldb::ValueObjectSP();
297+
}
298+
}
299+
300+
// If we have a non-pointer type with a synthetic value then lets check
301+
// if we have a synthetic dereference specified.
302+
if (!base->IsPointerType() && base->HasSyntheticValue()) {
303+
Status deref_error;
304+
if (lldb::ValueObjectSP synth_deref_sp =
305+
base->GetSyntheticValue()->Dereference(deref_error);
306+
synth_deref_sp && deref_error.Success()) {
307+
base = std::move(synth_deref_sp);
308+
}
309+
if (!base || deref_error.Fail()) {
310+
std::string errMsg = llvm::formatv(
311+
"Failed to dereference synthetic value: {0}", deref_error);
312+
return llvm::make_error<DILDiagnosticError>(
313+
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
314+
}
315+
316+
// Some synthetic plug-ins fail to set the error in Dereference
317+
if (!base) {
318+
std::string errMsg = "Failed to dereference synthetic value";
319+
return llvm::make_error<DILDiagnosticError>(
320+
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
321+
}
322+
}
323+
}
324+
325+
if (m_check_ptr_vs_member) {
326+
bool expr_is_ptr = node->GetIsArrow();
327+
bool base_is_ptr = base->IsPointerType();
328+
329+
if (expr_is_ptr != base_is_ptr) {
330+
if (base_is_ptr) {
331+
std::string errMsg =
332+
llvm::formatv("member reference type {0} is a pointer; "
333+
"did you mean to use '->'?",
334+
base->GetCompilerType().TypeDescription());
335+
return llvm::make_error<DILDiagnosticError>(
336+
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
337+
} else {
338+
std::string errMsg =
339+
llvm::formatv("member reference type {0} is not a pointer; "
340+
"did you mean to use '.'?",
341+
base->GetCompilerType().TypeDescription());
342+
return llvm::make_error<DILDiagnosticError>(
343+
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
344+
}
345+
}
346+
}
347+
348+
lldb::ValueObjectSP field_obj =
349+
base->GetChildMemberWithName(node->GetFieldName());
350+
if (!field_obj) {
351+
if (m_use_synthetic) {
352+
field_obj = base->GetSyntheticValue();
353+
if (field_obj)
354+
field_obj = field_obj->GetChildMemberWithName(node->GetFieldName());
355+
}
356+
357+
if (!m_use_synthetic || !field_obj) {
358+
std::string errMsg = llvm::formatv(
359+
"no member named '{0}' in {1}", node->GetFieldName(),
360+
base->GetCompilerType().GetFullyUnqualifiedType().TypeDescription());
361+
return llvm::make_error<DILDiagnosticError>(
362+
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
363+
}
364+
}
365+
366+
if (field_obj && field_obj->GetName() == node->GetFieldName()) {
367+
if (m_use_dynamic != lldb::eNoDynamicValues) {
368+
lldb::ValueObjectSP dynamic_val_sp =
369+
field_obj->GetDynamicValue(m_use_dynamic);
370+
if (dynamic_val_sp)
371+
field_obj = dynamic_val_sp;
372+
}
373+
return field_obj;
374+
}
375+
376+
CompilerType base_type = base->GetCompilerType();
377+
if (node->GetIsArrow() && base->IsPointerType())
378+
base_type = base_type.GetPointeeType();
379+
std::string errMsg =
380+
llvm::formatv("no member named '{0}' in {1}", node->GetFieldName(),
381+
base_type.GetFullyUnqualifiedType().TypeDescription());
382+
return llvm::make_error<DILDiagnosticError>(
383+
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
384+
}
385+
275386
} // namespace lldb_private::dil

lldb/source/ValueObject/DILLexer.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
2121
switch (kind) {
2222
case Kind::amp:
2323
return "amp";
24+
case Kind::arrow:
25+
return "arrow";
2426
case Kind::coloncolon:
2527
return "coloncolon";
2628
case Kind::eof:
@@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
2931
return "identifier";
3032
case Kind::l_paren:
3133
return "l_paren";
34+
case Kind::period:
35+
return "period";
3236
case Kind::r_paren:
3337
return "r_paren";
3438
case Token::star:
@@ -86,8 +90,9 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
8690
return Token(Token::identifier, maybe_word->str(), position);
8791

8892
constexpr std::pair<Token::Kind, const char *> operators[] = {
89-
{Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("},
90-
{Token::r_paren, ")"}, {Token::star, "*"},
93+
{Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"},
94+
{Token::l_paren, "("}, {Token::period, "."}, {Token::r_paren, ")"},
95+
{Token::star, "*"},
9196
};
9297
for (auto [kind, str] : operators) {
9398
if (remainder.consume_front(str))

lldb/source/ValueObject/DILParser.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ DILParser::DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
6666
llvm::Error &error)
6767
: m_ctx_scope(frame_sp), m_input_expr(dil_input_expr),
6868
m_dil_lexer(std::move(lexer)), m_error(error), m_use_dynamic(use_dynamic),
69-
m_use_synthetic(use_synthetic) {}
69+
m_use_synthetic(use_synthetic), m_fragile_ivar(fragile_ivar),
70+
m_check_ptr_vs_member(check_ptr_vs_member) {}
7071

7172
ASTNodeUP DILParser::Run() {
7273
ASTNodeUP expr = ParseExpression();
@@ -79,15 +80,15 @@ ASTNodeUP DILParser::Run() {
7980
// Parse an expression.
8081
//
8182
// expression:
82-
// primary_expression
83+
// unary_expression
8384
//
8485
ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
8586

8687
// Parse an unary_expression.
8788
//
8889
// unary_expression:
90+
// postfix_expression
8991
// unary_operator expression
90-
// primary_expression
9192
//
9293
// unary_operator:
9394
// "&"
@@ -111,7 +112,28 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
111112
llvm_unreachable("invalid token kind");
112113
}
113114
}
114-
return ParsePrimaryExpression();
115+
return ParsePostfixExpression();
116+
}
117+
118+
// Parse a postfix_expression.
119+
//
120+
// postfix_expression:
121+
// primary_expression
122+
// postfix_expression "." id_expression
123+
// postfix_expression "->" id_expression
124+
//
125+
ASTNodeUP DILParser::ParsePostfixExpression() {
126+
ASTNodeUP lhs = ParsePrimaryExpression();
127+
while (CurToken().IsOneOf({Token::period, Token::arrow})) {
128+
Token token = CurToken();
129+
m_dil_lexer.Advance();
130+
Token member_token = CurToken();
131+
std::string member_id = ParseIdExpression();
132+
lhs = std::make_unique<MemberOfNode>(
133+
member_token.GetLocation(), std::move(lhs),
134+
token.GetKind() == Token::arrow, member_id);
135+
}
136+
return lhs;
115137
}
116138

117139
// Parse a primary_expression.
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

0 commit comments

Comments
 (0)