Skip to content

[LLDB] Add field member operators to DIL #138093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions lldb/docs/dil-expr-lang.ebnf
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@

expression = unary_expression ;

unary_expression = unary_operator expression
| primary_expression ;
unary_expression = postfix_expression
| unary_operator expression ;

unary_operator = "*" | "&" ;

postfix_expresson = primary_expression
| postfix_expression "." id_expression
| postfix_expression "->" id_expression ;

primary_expression = id_expression
| "(" expression ")";
| "(" expression ")" ;

id_expression = unqualified_id
| qualified_id
Expand Down
26 changes: 26 additions & 0 deletions lldb/include/lldb/ValueObject/DILAST.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace lldb_private::dil {
enum class NodeKind {
eErrorNode,
eIdentifierNode,
eMemberOfNode,
eUnaryOpNode,
};

Expand Down Expand Up @@ -88,6 +89,29 @@ class IdentifierNode : public ASTNode {
std::string m_name;
};

class MemberOfNode : public ASTNode {
public:
MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow,
std::string name)
: ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
m_is_arrow(is_arrow), m_field_name(std::move(name)) {}

llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;

ASTNode *GetBase() const { return m_base.get(); }
bool GetIsArrow() const { return m_is_arrow; }
llvm::StringRef GetFieldName() const { return llvm::StringRef(m_field_name); }

static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eMemberOfNode;
}

private:
ASTNodeUP m_base;
bool m_is_arrow;
std::string m_field_name;
};

class UnaryOpNode : public ASTNode {
public:
UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP operand)
Expand Down Expand Up @@ -118,6 +142,8 @@ class Visitor {
virtual llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) = 0;
virtual llvm::Expected<lldb::ValueObjectSP>
Visit(const MemberOfNode *node) = 0;
virtual llvm::Expected<lldb::ValueObjectSP>
Visit(const UnaryOpNode *node) = 0;
};

Expand Down
11 changes: 8 additions & 3 deletions lldb/include/lldb/ValueObject/DILEval.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,27 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref,
class Interpreter : Visitor {
public:
Interpreter(lldb::TargetSP target, llvm::StringRef expr,
lldb::DynamicValueType use_dynamic,
std::shared_ptr<StackFrame> frame_sp);
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member);

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

private:
llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) override;
llvm::Expected<lldb::ValueObjectSP> Visit(const MemberOfNode *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;
llvm::StringRef m_expr;
lldb::ValueObjectSP m_scope;
lldb::DynamicValueType m_default_dynamic;
std::shared_ptr<StackFrame> m_exe_ctx_scope;
lldb::DynamicValueType m_use_dynamic;
bool m_use_synthetic;
bool m_fragile_ivar;
bool m_check_ptr_vs_member;
};

} // namespace lldb_private::dil
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/ValueObject/DILLexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ class Token {
public:
enum Kind {
amp,
arrow,
coloncolon,
eof,
identifier,
l_paren,
period,
r_paren,
star,
};
Expand Down
3 changes: 3 additions & 0 deletions lldb/include/lldb/ValueObject/DILParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class DILParser {

ASTNodeUP ParseExpression();
ASTNodeUP ParseUnaryExpression();
ASTNodeUP ParsePostfixExpression();
ASTNodeUP ParsePrimaryExpression();

std::string ParseNestedNameSpecifier();
Expand Down Expand Up @@ -117,6 +118,8 @@ class DILParser {

lldb::DynamicValueType m_use_dynamic;
bool m_use_synthetic;
bool m_fragile_ivar;
bool m_check_ptr_vs_member;
}; // class DILParser

} // namespace lldb_private::dil
Expand Down
5 changes: 3 additions & 2 deletions lldb/source/Target/StackFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,9 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(

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

auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get());
if (!valobj_or_error) {
Expand Down
4 changes: 4 additions & 0 deletions lldb/source/ValueObject/DILAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
return v->Visit(this);
}

llvm::Expected<lldb::ValueObjectSP> MemberOfNode::Accept(Visitor *v) const {
return v->Visit(this);
}

llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const {
return v->Visit(this);
}
Expand Down
123 changes: 117 additions & 6 deletions lldb/source/ValueObject/DILEval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,13 @@ lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
}

Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
lldb::DynamicValueType use_dynamic,
std::shared_ptr<StackFrame> frame_sp)
: m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
m_exe_ctx_scope(frame_sp) {}
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member)
: m_target(std::move(target)), m_expr(expr), m_exe_ctx_scope(frame_sp),
m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic),
m_fragile_ivar(fragile_ivar), m_check_ptr_vs_member(check_ptr_vs_member) {
}

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

llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const IdentifierNode *node) {
lldb::DynamicValueType use_dynamic = m_default_dynamic;
lldb::DynamicValueType use_dynamic = m_use_dynamic;

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

switch (node->kind()) {
case UnaryOpKind::Deref: {
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_use_dynamic);
if (dynamic_rhs)
rhs = dynamic_rhs;

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

llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const MemberOfNode *node) {
auto base_or_err = Evaluate(node->GetBase());
if (!base_or_err)
return base_or_err;
lldb::ValueObjectSP base = *base_or_err;

// Perform some basic type & correctness checking.
if (node->GetIsArrow()) {
if (!m_fragile_ivar) {
// Make sure we aren't trying to deref an objective
// C ivar if this is not allowed
const uint32_t pointer_type_flags =
base->GetCompilerType().GetTypeInfo(nullptr);
if ((pointer_type_flags & lldb::eTypeIsObjC) &&
(pointer_type_flags & lldb::eTypeIsPointer)) {
// This was an objective C object pointer and it was requested we
// skip any fragile ivars so return nothing here
return lldb::ValueObjectSP();
}
}

// If we have a non-pointer type with a synthetic value then lets check
// if we have a synthetic dereference specified.
if (!base->IsPointerType() && base->HasSyntheticValue()) {
Status deref_error;
if (lldb::ValueObjectSP synth_deref_sp =
base->GetSyntheticValue()->Dereference(deref_error);
synth_deref_sp && deref_error.Success()) {
base = std::move(synth_deref_sp);
}
if (!base || deref_error.Fail()) {
std::string errMsg = llvm::formatv(
"Failed to dereference synthetic value: {0}", deref_error);
return llvm::make_error<DILDiagnosticError>(
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}

// Some synthetic plug-ins fail to set the error in Dereference
if (!base) {
std::string errMsg = "Failed to dereference synthetic value";
return llvm::make_error<DILDiagnosticError>(
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}
}
}

if (m_check_ptr_vs_member) {
bool expr_is_ptr = node->GetIsArrow();
bool base_is_ptr = base->IsPointerType();

if (expr_is_ptr != base_is_ptr) {
if (base_is_ptr) {
std::string errMsg =
llvm::formatv("member reference type {0} is a pointer; "
"did you mean to use '->'?",
base->GetCompilerType().TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
} else {
std::string errMsg =
llvm::formatv("member reference type {0} is not a pointer; "
"did you mean to use '.'?",
base->GetCompilerType().TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}
}
}

lldb::ValueObjectSP field_obj =
base->GetChildMemberWithName(node->GetFieldName());
if (!field_obj) {
if (m_use_synthetic) {
field_obj = base->GetSyntheticValue();
if (field_obj)
field_obj = field_obj->GetChildMemberWithName(node->GetFieldName());
}

if (!m_use_synthetic || !field_obj) {
std::string errMsg = llvm::formatv(
"no member named '{0}' in {1}", node->GetFieldName(),
base->GetCompilerType().GetFullyUnqualifiedType().TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}
}

if (field_obj && field_obj->GetName() == node->GetFieldName()) {
if (m_use_dynamic != lldb::eNoDynamicValues) {
lldb::ValueObjectSP dynamic_val_sp =
field_obj->GetDynamicValue(m_use_dynamic);
if (dynamic_val_sp)
field_obj = dynamic_val_sp;
}
return field_obj;
}

CompilerType base_type = base->GetCompilerType();
if (node->GetIsArrow() && base->IsPointerType())
base_type = base_type.GetPointeeType();
std::string errMsg =
llvm::formatv("no member named '{0}' in {1}", node->GetFieldName(),
base_type.GetFullyUnqualifiedType().TypeDescription());
return llvm::make_error<DILDiagnosticError>(
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}

} // namespace lldb_private::dil
9 changes: 7 additions & 2 deletions lldb/source/ValueObject/DILLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
switch (kind) {
case Kind::amp:
return "amp";
case Kind::arrow:
return "arrow";
case Kind::coloncolon:
return "coloncolon";
case Kind::eof:
Expand All @@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "identifier";
case Kind::l_paren:
return "l_paren";
case Kind::period:
return "period";
case Kind::r_paren:
return "r_paren";
case Token::star:
Expand Down Expand Up @@ -86,8 +90,9 @@ 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::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("},
{Token::r_paren, ")"}, {Token::star, "*"},
{Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"},
{Token::l_paren, "("}, {Token::period, "."}, {Token::r_paren, ")"},
{Token::star, "*"},
};
for (auto [kind, str] : operators) {
if (remainder.consume_front(str))
Expand Down
30 changes: 26 additions & 4 deletions lldb/source/ValueObject/DILParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ DILParser::DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
llvm::Error &error)
: m_ctx_scope(frame_sp), m_input_expr(dil_input_expr),
m_dil_lexer(std::move(lexer)), m_error(error), m_use_dynamic(use_dynamic),
m_use_synthetic(use_synthetic) {}
m_use_synthetic(use_synthetic), m_fragile_ivar(fragile_ivar),
m_check_ptr_vs_member(check_ptr_vs_member) {}

ASTNodeUP DILParser::Run() {
ASTNodeUP expr = ParseExpression();
Expand All @@ -79,15 +80,15 @@ ASTNodeUP DILParser::Run() {
// Parse an expression.
//
// expression:
// primary_expression
// unary_expression
//
ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }

// Parse an unary_expression.
//
// unary_expression:
// postfix_expression
// unary_operator expression
// primary_expression
//
// unary_operator:
// "&"
Expand All @@ -111,7 +112,28 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
llvm_unreachable("invalid token kind");
}
}
return ParsePrimaryExpression();
return ParsePostfixExpression();
}

// Parse a postfix_expression.
//
// postfix_expression:
// primary_expression
// postfix_expression "." id_expression
// postfix_expression "->" id_expression
//
ASTNodeUP DILParser::ParsePostfixExpression() {
ASTNodeUP lhs = ParsePrimaryExpression();
while (CurToken().IsOneOf({Token::period, Token::arrow})) {
Token token = CurToken();
m_dil_lexer.Advance();
Token member_token = CurToken();
std::string member_id = ParseIdExpression();
lhs = std::make_unique<MemberOfNode>(
member_token.GetLocation(), std::move(lhs),
token.GetKind() == Token::arrow, member_id);
}
return lhs;
}

// Parse a primary_expression.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Loading
Loading