[Lldb-commits] [lldb] [LLDB] Add field member operators to DIL (PR #138093)
via lldb-commits
lldb-commits at lists.llvm.org
Thu May 22 13:05:21 PDT 2025
https://github.com/cmtice updated https://github.com/llvm/llvm-project/pull/138093
>From fe9ac0fa05bb43ea718214746f0ea9b7eefc929a Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 1 May 2025 00:05:57 -0700
Subject: [PATCH 1/5] [LLDB] Add field member operators to DIL
Add the arrow and period operators, allowing DIL to find and access
member fields.
---
lldb/docs/dil-expr-lang.ebnf | 10 +-
lldb/include/lldb/ValueObject/DILAST.h | 26 +++
lldb/include/lldb/ValueObject/DILEval.h | 8 +
lldb/include/lldb/ValueObject/DILLexer.h | 2 +
lldb/include/lldb/ValueObject/DILParser.h | 1 +
lldb/source/ValueObject/DILAST.cpp | 4 +
lldb/source/ValueObject/DILEval.cpp | 200 ++++++++++++++++++
lldb/source/ValueObject/DILLexer.cpp | 9 +-
lldb/source/ValueObject/DILParser.cpp | 27 ++-
.../frame/var-dil/basics/MemberOf/Makefile | 3 +
.../MemberOf/TestFrameVarDILMemberOf.py | 47 ++++
.../frame/var-dil/basics/MemberOf/main.cpp | 59 ++++++
.../basics/MemberOfAnonymousMember/Makefile | 3 +
.../TestFrameVarDILMemberOfAnonymousMember.py | 62 ++++++
.../basics/MemberOfAnonymousMember/main.cpp | 74 +++++++
.../basics/MemberOfInheritance/Makefile | 3 +
.../TestFrameVarDILMemberOfInheritance.py | 49 +++++
.../basics/MemberOfInheritance/main.cpp | 87 ++++++++
18 files changed, 666 insertions(+), 8 deletions(-)
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index c8bf4231b3e4a..580626862c005 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -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
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index fe3827ef0516a..a74a12bd8be9d 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -20,6 +20,7 @@ namespace lldb_private::dil {
enum class NodeKind {
eErrorNode,
eIdentifierNode,
+ eMemberOfNode,
eUnaryOpNode,
};
@@ -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,
+ ConstString name)
+ : ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
+ m_is_arrow(is_arrow), m_field_name(name) { }
+
+ llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+ ASTNode *base() const { return m_base.get(); }
+ bool IsArrow() const { return m_is_arrow; }
+ ConstString FieldName() const { return m_field_name; }
+
+ static bool classof(const ASTNode *node) {
+ return node->GetKind() == NodeKind::eMemberOfNode;
+ }
+
+private:
+ ASTNodeUP m_base;
+ bool m_is_arrow;
+ ConstString m_field_name;
+};
+
class UnaryOpNode : public ASTNode {
public:
UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP operand)
@@ -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;
};
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index b1dd3fdb49739..053daffaa41f2 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -49,8 +49,16 @@ class Interpreter : Visitor {
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;
+ lldb::ValueObjectSP EvaluateMemberOf(lldb::ValueObjectSP value,
+ const std::vector<uint32_t> &path,
+ bool use_synthetic, bool is_dynamic);
+
+ lldb::ValueObjectSP FindMemberWithName(lldb::ValueObjectSP base,
+ ConstString name, bool is_arrow);
+
// Used by the interpreter to create objects, perform casts, etc.
lldb::TargetSP m_target;
llvm::StringRef m_expr;
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index 3508b8b7a85c6..9475519a43d2a 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -25,10 +25,12 @@ class Token {
public:
enum Kind {
amp,
+ arrow,
coloncolon,
eof,
identifier,
l_paren,
+ period,
r_paren,
star,
};
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index f5c00b1040ef4..c62f8908290f5 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -84,6 +84,7 @@ class DILParser {
ASTNodeUP ParseExpression();
ASTNodeUP ParseUnaryExpression();
+ ASTNodeUP ParsePostfixExpression();
ASTNodeUP ParsePrimaryExpression();
std::string ParseNestedNameSpecifier();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index ea847587501ee..2814d96b94e6e 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -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);
}
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 15a66d4866305..1f1ad7161f42e 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -272,4 +272,204 @@ Interpreter::Visit(const UnaryOpNode *node) {
m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
}
+lldb::ValueObjectSP
+Interpreter::EvaluateMemberOf(lldb::ValueObjectSP value,
+ const std::vector<uint32_t> &path,
+ bool use_synthetic, bool is_dynamic) {
+ lldb::ValueObjectSP member_val_sp = value;
+
+ lldb::DynamicValueType use_dynamic =
+ (!is_dynamic) ? lldb::eNoDynamicValues : lldb::eDynamicDontRunTarget;
+ // Walk the path from the base value to the value that contains the requested field.
+ for (uint32_t idx : path) {
+ member_val_sp = member_val_sp->GetChildAtIndex(idx, /*can_create*/ true);
+ }
+ // If that didn't work, try it with the dynamic value.
+ if (!member_val_sp && is_dynamic) {
+ lldb::ValueObjectSP dyn_val_sp = value->GetDynamicValue(use_dynamic);
+ if (dyn_val_sp) {
+ for (uint32_t idx : path) {
+ dyn_val_sp = dyn_val_sp->GetChildAtIndex(idx, true);
+ }
+ member_val_sp = dyn_val_sp;
+ }
+ }
+ assert(member_val_sp && "invalid ast: invalid member access");
+
+ return member_val_sp;
+}
+
+static bool GetFieldIndex(CompilerType type, const std::string &name,
+ std::vector<uint32_t> *idx_path) {
+ bool found = false;
+ uint32_t num_fields = type.GetNumFields();
+ for (uint32_t i = 0; i < num_fields; ++i) {
+ uint64_t bit_offset = 0;
+ uint32_t bitfield_bit_size = 0;
+ bool is_bitfield = false;
+ std::string name_sstr;
+ CompilerType field_type(type.GetFieldAtIndex(
+ i, name_sstr, &bit_offset, &bitfield_bit_size, &is_bitfield));
+ auto field_name =
+ name_sstr.length() == 0 ? std::optional<std::string>() : name_sstr;
+ if (field_type.IsValid() && name_sstr == name) {
+ idx_path->push_back(i + type.GetNumberOfNonEmptyBaseClasses());
+ found = true;
+ break;
+ } else if (field_type.IsAnonymousType()) {
+ found = GetFieldIndex(field_type, name, idx_path);
+ if (found) {
+ idx_path->push_back(i + type.GetNumberOfNonEmptyBaseClasses());
+ break;
+ }
+ }
+ }
+ return found;
+}
+
+static bool SearchBaseClassesForField(lldb::ValueObjectSP base_sp,
+ CompilerType base_type,
+ const std::string &name,
+ std::vector<uint32_t> *idx_path,
+ bool use_synthetic, bool is_dynamic) {
+ bool found = false;
+ uint32_t num_non_empty_bases = 0;
+ uint32_t num_direct_bases = base_type.GetNumDirectBaseClasses();
+ for (uint32_t i = 0; i < num_direct_bases; ++i) {
+ uint32_t bit_offset;
+ CompilerType base_class =
+ base_type.GetDirectBaseClassAtIndex(i, &bit_offset);
+ std::vector<uint32_t> field_idx_path;
+ if (GetFieldIndex(base_class, name, &field_idx_path)) {
+ for (uint32_t j : field_idx_path)
+ idx_path->push_back(j + base_class.GetNumberOfNonEmptyBaseClasses());
+ idx_path->push_back(i);
+ return true;
+ }
+
+ found = SearchBaseClassesForField(base_sp, base_class, name, idx_path,
+ use_synthetic, is_dynamic);
+ if (found) {
+ idx_path->push_back(i);
+ return true;
+ }
+
+ if (base_class.GetNumFields() > 0)
+ num_non_empty_bases += 1;
+ }
+ return false;
+}
+
+lldb::ValueObjectSP Interpreter::FindMemberWithName(lldb::ValueObjectSP base,
+ ConstString name,
+ bool is_arrow) {
+ bool is_synthetic = false;
+ bool is_dynamic = true;
+ // See if GetChildMemberWithName works.
+ lldb::ValueObjectSP field_obj =
+ base->GetChildMemberWithName(name.GetStringRef());
+ if (field_obj && field_obj->GetName() == name)
+ return field_obj;
+
+ // Check for synthetic member.
+ lldb::ValueObjectSP child_sp = base->GetSyntheticValue();
+ if (child_sp) {
+ is_synthetic = true;
+ field_obj = child_sp->GetChildMemberWithName(name);
+ if (field_obj && field_obj->GetName() == name)
+ return field_obj;
+ }
+
+ // Check indices of immediate member fields of base's type.
+ CompilerType base_type = base->GetCompilerType();
+ std::vector<uint32_t> field_idx_path;
+ if (GetFieldIndex(base_type, name.GetString(), &field_idx_path)) {
+ std::reverse(field_idx_path.begin(), field_idx_path.end());
+ // Traverse the path & verify the final object is correct.
+ field_obj = base;
+ for (uint32_t i : field_idx_path)
+ field_obj = field_obj->GetChildAtIndex(i, true);
+ if (field_obj && field_obj->GetName() == name)
+ return field_obj;
+ }
+
+ // Go through base classes and look for field there.
+ std::vector<uint32_t> base_class_idx_path;
+ bool found =
+ SearchBaseClassesForField(base, base_type, name.GetString(),
+ &base_class_idx_path, is_synthetic, is_dynamic);
+ if (found && !base_class_idx_path.empty()) {
+ std::reverse(base_class_idx_path.begin(), base_class_idx_path.end());
+ field_obj =
+ EvaluateMemberOf(base, base_class_idx_path, is_synthetic, is_dynamic);
+ if (field_obj && field_obj->GetName() == name)
+ return field_obj;
+ }
+
+ // Field not found.
+ return lldb::ValueObjectSP();
+}
+
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const MemberOfNode *node) {
+ Status error;
+ auto base_or_err = Evaluate(node->base());
+ if (!base_or_err) {
+ return base_or_err;
+ }
+ lldb::ValueObjectSP base = *base_or_err;
+
+ // Perform basic type checking.
+ CompilerType base_type = base->GetCompilerType();
+ // When using an arrow, make sure the base is a pointer or array type.
+ // When using a period, make sure the base type is NOT a pointer type.
+ if (node->IsArrow() && !base_type.IsPointerType() &&
+ !base_type.IsArrayType()) {
+ lldb::ValueObjectSP deref_sp = base->Dereference(error);
+ if (error.Success()) {
+ base = deref_sp;
+ base_type = deref_sp->GetCompilerType().GetPointerType();
+ } else {
+ std::string errMsg =
+ llvm::formatv("member reference type {0} is not a pointer; "
+ "did you mean to use '.'?",
+ base_type.TypeDescription());
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, errMsg, node->GetLocation(), node->FieldName().GetLength());
+ }
+ } else if (!node->IsArrow() && base_type.IsPointerType()) {
+ std::string errMsg =
+ llvm::formatv("member reference type {0} is a pointer; "
+ "did you mean to use '->'?",
+ base_type.TypeDescription());
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, errMsg, node->GetLocation(), node->FieldName().GetLength());
+ }
+
+ // User specified array->elem; need to get to element[0] to look for fields.
+ if (node->IsArrow() && base_type.IsArrayType())
+ base = base->GetChildAtIndex(0);
+
+ // Now look for the member with the specified name.
+ lldb::ValueObjectSP field_obj =
+ FindMemberWithName(base, node->FieldName(), node->IsArrow());
+ if (field_obj) {
+ if (field_obj->GetCompilerType().IsReferenceType()) {
+ lldb::ValueObjectSP tmp_obj = field_obj->Dereference(error);
+ if (error.Fail())
+ return error.ToError();
+ return tmp_obj;
+ }
+ return field_obj;
+ }
+
+ if (node->IsArrow() && base_type.IsPointerType())
+ base_type = base_type.GetPointeeType();
+ std::string errMsg = llvm::formatv(
+ "no member named '{0}' in {1}", node->FieldName().GetStringRef(),
+ base_type.GetFullyUnqualifiedType().TypeDescription());
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, errMsg, node->GetLocation(), node->FieldName().GetLength());
+}
+
} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index b9c2e7971e3b4..1610b4e87d7fb 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -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:
@@ -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:
@@ -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))
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 2c78eae8cf6bf..9c5bc71775fb2 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -79,15 +79,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:
// "&"
@@ -111,7 +111,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,
+ ConstString(member_id));
+ }
+ return lhs;
}
// Parse a primary_expression.
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
new file mode 100644
index 0000000000000..ff942c88bf183
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
@@ -0,0 +1,47 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+class TestFrameVarDILMemberOf(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
+ lldb.SBFileSpec("main.cpp"))
+
+ self.expect("settings set target.experimental.use-DIL true",
+ substrs=[""])
+ self.expect_var_path("s.x", value="1")
+ self.expect_var_path("s.r", value="2")
+ self.expect_var_path("sr.x", value="1")
+ self.expect_var_path("sr.r", value="2")
+ self.expect_var_path("sp->x", value="1")
+ self.expect_var_path("sp->r", value="2")
+ self.expect_var_path("sarr->x", value="5");
+ self.expect_var_path("sarr->r", value="2")
+
+ self.expect("frame variable 'sp->foo'", error=True,
+ substrs=["no member named 'foo' in 'Sx'"])
+
+ self.expect("frame variable 'sp.x'", error=True,
+ substrs=["member reference type 'Sx *' is a "
+ "pointer; did you mean to use '->'"])
+ self.expect("frame variable 'sarr.x'", error=True,
+ substrs=["no member named 'x' in 'Sx[2]'"])
+
+ # Test for record typedefs.
+ self.expect_var_path("sa.x", value="3")
+ self.expect_var_path("sa.y", value="'\\x04'")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
new file mode 100644
index 0000000000000..dace888bef4dc
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
@@ -0,0 +1,59 @@
+int
+main(int argc, char**argv)
+{
+ int x = 2;
+ struct Sx {
+ int x;
+ int& r;
+ char y;
+ } s{1, x, 2};
+
+ Sx& sr = s;
+ Sx* sp = &s;
+
+ Sx sarr[2] = {{5, x, 2}, {1, x, 3}};
+
+ using SxAlias = Sx;
+ SxAlias sa{3, x, 4};
+
+ return 0; // Set a breakpoint here
+}
+
+/*
+ EXPECT_THAT(Eval("s.x"), IsEqual("1"));
+ EXPECT_THAT(Eval("s.r"), IsEqual("2"));
+ EXPECT_THAT(Eval("s.r + 1"), IsEqual("3"));
+ EXPECT_THAT(Eval("sr.x"), IsEqual("1"));
+ EXPECT_THAT(Eval("sr.r"), IsEqual("2"));
+ EXPECT_THAT(Eval("sr.r + 1"), IsEqual("3"));
+ EXPECT_THAT(Eval("sp->x"), IsEqual("1"));
+ EXPECT_THAT(Eval("sp->r"), IsEqual("2"));
+ EXPECT_THAT(Eval("sp->r + 1"), IsEqual("3"));
+ EXPECT_THAT(Eval("sarr->x"), IsEqual("5"));
+ EXPECT_THAT(Eval("sarr->r"), IsEqual("2"));
+ EXPECT_THAT(Eval("sarr->r + 1"), IsEqual("3"));
+ EXPECT_THAT(Eval("(sarr + 1)->x"), IsEqual("1"));
+
+ EXPECT_THAT(
+ Eval("sp->4"),
+ IsError(
+ "<expr>:1:5: expected 'identifier', got: <'4' (numeric_constant)>\n"
+ "sp->4\n"
+ " ^"));
+ EXPECT_THAT(Eval("sp->foo"), IsError("no member named 'foo' in 'Sx'"));
+ EXPECT_THAT(
+ Eval("sp->r / (void*)0"),
+ IsError("invalid operands to binary expression ('int' and 'void *')"));
+
+ EXPECT_THAT(Eval("sp.x"), IsError("member reference type 'Sx *' is a "
+ "pointer; did you mean to use '->'"));
+ EXPECT_THAT(
+ Eval("sarr.x"),
+ IsError(
+ "member reference base type 'Sx[2]' is not a structure or union"));
+
+ // Test for record typedefs.
+ EXPECT_THAT(Eval("sa.x"), IsEqual("3"));
+ EXPECT_THAT(Eval("sa.y"), IsEqual("'\\x04'"));
+
+*/
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py
new file mode 100644
index 0000000000000..1bde4706da90f
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py
@@ -0,0 +1,62 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+class TestFrameVarDILMemberOfAnonymousMember(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
+ lldb.SBFileSpec("main.cpp"))
+
+ self.expect("settings set target.experimental.use-DIL true",
+ substrs=[""])
+ self.expect_var_path("a.x", value="1")
+ self.expect_var_path("a.y", value="2")
+
+ self.expect("frame variable 'b.x'", error=True,
+ substrs=["no member named 'x' in 'B'"])
+ #self.expect_var_path("b.y", value="0")
+ self.expect_var_path("b.z", value="3")
+ self.expect_var_path("b.w", value="4")
+ self.expect_var_path("b.a.x", value="1")
+ self.expect_var_path("b.a.y", value="2")
+
+ self.expect_var_path("c.x", value="5")
+ self.expect_var_path("c.y", value="6")
+
+ self.expect_var_path("d.x", value="7")
+ self.expect_var_path("d.y", value="8")
+ self.expect_var_path("d.z", value="9")
+ self.expect_var_path("d.w", value="10")
+
+ self.expect("frame variable 'e.x'", error=True,
+ substrs=["no member named 'x' in 'E'"])
+ self.expect("frame variable 'f.x'", error=True,
+ substrs=["no member named 'x' in 'F'"])
+ self.expect_var_path("f.named_field.x", value="12")
+
+ self.expect_var_path("unnamed_derived.y", value="2")
+ self.expect_var_path("unnamed_derived.z", value="13")
+
+ self.expect("frame variable 'derb.x'", error=True,
+ substrs=["no member named 'x' in 'DerivedB'"])
+ self.expect("frame variable 'derb.y'", error=True,
+ substrs=["no member named 'y' in 'DerivedB'"])
+ self.expect_var_path("derb.w", value="14")
+ self.expect_var_path("derb.k", value="15")
+ self.expect_var_path("derb.a.x", value="1")
+ self.expect_var_path("derb.a.y", value="2")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp
new file mode 100644
index 0000000000000..6237523ac6bf3
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp
@@ -0,0 +1,74 @@
+int main(int argc, char** argv)
+{
+ struct A {
+ struct {
+ int x = 1;
+ };
+ int y = 2;
+ } a;
+
+ struct B {
+ // Anonymous struct inherits another struct.
+ struct : public A {
+ int z = 3;
+ };
+ int w = 4;
+ A a;
+ } b;
+
+ // Anonymous classes and unions.
+ struct C {
+ union {
+ int x = 5;
+ };
+ class {
+ public:
+ int y = 6;
+ };
+ } c;
+
+ // Multiple levels of anonymous structs.
+ struct D {
+ struct {
+ struct {
+ int x = 7;
+ struct {
+ int y = 8;
+ };
+ };
+ int z = 9;
+ struct {
+ int w = 10;
+ };
+ };
+ } d;
+
+ struct E {
+ struct IsNotAnon {
+ int x = 11;
+ };
+ } e;
+
+ struct F {
+ struct {
+ int x = 12;
+ } named_field;
+ } f;
+
+ // Inherited unnamed struct without an enclosing parent class.
+ struct : public A {
+ struct {
+ int z = 13;
+ };
+ } unnamed_derived;
+
+ struct DerivedB : public B {
+ struct {
+ // `w` in anonymous struct overrides `w` from `B`.
+ int w = 14;
+ int k = 15;
+ };
+ } derb;
+
+ return 0; // Set a breakpoint here
+}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py
new file mode 100644
index 0000000000000..c38ab5d30b238
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py
@@ -0,0 +1,49 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+class TestFrameVarDILMemberOfInheritance(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
+ lldb.SBFileSpec("main.cpp"))
+
+ self.expect("settings set target.experimental.use-DIL true",
+ substrs=[""])
+ self.expect_var_path("a.a_", value="1")
+ self.expect_var_path("b.b_", value="2")
+ self.expect_var_path("c.a_", value="1")
+ self.expect_var_path("c.b_", value="2")
+ self.expect_var_path("c.c_", value="3")
+ self.expect_var_path("d.a_", value="1")
+ self.expect_var_path("d.b_", value="2")
+ self.expect_var_path("d.c_", value="3")
+ self.expect_var_path("d.d_", value="4")
+ self.expect_var_path("d.fa_.a_", value="5")
+
+ self.expect_var_path("plugin.x", value="1")
+ self.expect_var_path("plugin.y", value="2")
+
+ self.expect_var_path("engine.x", value="1")
+ self.expect_var_path("engine.y", value="2")
+ self.expect_var_path("engine.z", value="3")
+
+ self.expect_var_path("parent_base->x", value="1")
+ self.expect_var_path("parent_base->y", value="2")
+ self.expect_var_path("parent->x", value="1")
+ self.expect_var_path("parent->y", value="2")
+ self.expect_var_path("parent->z", value="3")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp
new file mode 100644
index 0000000000000..4d29af1b7f15b
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp
@@ -0,0 +1,87 @@
+int main(int argc, char** argv)
+{
+ struct A {
+ int a_;
+ } a{1};
+
+ struct B {
+ int b_;
+ } b{2};
+
+ struct C : A, B {
+ int c_;
+ } c;
+ // } c{{1}, {2}, 3};
+ c.a_ = 1;
+ c.b_ = 2;
+ c.c_ = 3;
+
+ struct D : C {
+ int d_;
+ A fa_;
+ } d;
+ // } d{{{1}, {2}, 3}, 4, {5}};
+ d.a_ = 1;
+ d.b_ = 2;
+ d.c_ = 3;
+ d.d_ = 4;
+ d.fa_.a_ = 5;
+
+ // Virtual inheritance example.
+ struct Animal {
+ virtual ~Animal() = default;
+ int weight_;
+ };
+ struct Mammal : virtual Animal {};
+ struct WingedAnimal : virtual Animal {};
+ struct Bat : Mammal, WingedAnimal {
+ } bat;
+ bat.weight_ = 10;
+
+ // Empty bases example.
+ struct IPlugin {
+ virtual ~IPlugin() {}
+ };
+ struct Plugin : public IPlugin {
+ int x;
+ int y;
+ };
+ Plugin plugin;
+ plugin.x = 1;
+ plugin.y = 2;
+
+ struct ObjectBase {
+ int x;
+ };
+ struct Object : ObjectBase {};
+ struct Engine : Object {
+ int y;
+ int z;
+ };
+
+ Engine engine;
+ engine.x = 1;
+ engine.y = 2;
+ engine.z = 3;
+
+ // Empty multiple inheritance with empty base.
+ struct Base {
+ int x;
+ int y;
+ virtual void Do() = 0;
+ virtual ~Base() {}
+ };
+ struct Mixin {};
+ struct Parent : private Mixin, public Base {
+ int z;
+ virtual void Do(){};
+ };
+ Parent obj;
+ obj.x = 1;
+ obj.y = 2;
+ obj.z = 3;
+ Base* parent_base = &obj;
+ Parent* parent = &obj;
+
+ return 0; // Set a breakpoint here
+}
>From b3bf9213cafb3bc8e5b333b37b83f9af294782f2 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Wed, 7 May 2025 23:05:58 -0700
Subject: [PATCH 2/5] Remove special code for finding field members and rely
entirely on ValueObject::GetChildMemberWithName. Also minor code cleanups
requested by reviewer.
---
lldb/include/lldb/ValueObject/DILAST.h | 12 +-
lldb/include/lldb/ValueObject/DILEval.h | 7 -
lldb/source/ValueObject/DILEval.cpp | 164 ++----------------------
lldb/source/ValueObject/DILParser.cpp | 7 +-
4 files changed, 22 insertions(+), 168 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index a74a12bd8be9d..f3cda4fbece9b 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -92,15 +92,15 @@ class IdentifierNode : public ASTNode {
class MemberOfNode : public ASTNode {
public:
MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow,
- ConstString name)
+ std::string name)
: ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
- m_is_arrow(is_arrow), m_field_name(name) { }
+ m_is_arrow(is_arrow), m_field_name(std::move(name)) {}
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
- ASTNode *base() const { return m_base.get(); }
- bool IsArrow() const { return m_is_arrow; }
- ConstString FieldName() const { return m_field_name; }
+ ASTNode *GetBase() const { return m_base.get(); }
+ bool GetIsArrow() const { return m_is_arrow; }
+ std::string GetFieldName() const { return m_field_name; }
static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eMemberOfNode;
@@ -109,7 +109,7 @@ class MemberOfNode : public ASTNode {
private:
ASTNodeUP m_base;
bool m_is_arrow;
- ConstString m_field_name;
+ std::string m_field_name;
};
class UnaryOpNode : public ASTNode {
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 053daffaa41f2..04c21ab77b6b4 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -52,13 +52,6 @@ class Interpreter : Visitor {
llvm::Expected<lldb::ValueObjectSP> Visit(const MemberOfNode *node) override;
llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
- lldb::ValueObjectSP EvaluateMemberOf(lldb::ValueObjectSP value,
- const std::vector<uint32_t> &path,
- bool use_synthetic, bool is_dynamic);
-
- lldb::ValueObjectSP FindMemberWithName(lldb::ValueObjectSP base,
- ConstString name, bool is_arrow);
-
// Used by the interpreter to create objects, perform casts, etc.
lldb::TargetSP m_target;
llvm::StringRef m_expr;
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 1f1ad7161f42e..8630bdd0a4ed1 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -272,148 +272,10 @@ Interpreter::Visit(const UnaryOpNode *node) {
m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
}
-lldb::ValueObjectSP
-Interpreter::EvaluateMemberOf(lldb::ValueObjectSP value,
- const std::vector<uint32_t> &path,
- bool use_synthetic, bool is_dynamic) {
- lldb::ValueObjectSP member_val_sp = value;
-
- lldb::DynamicValueType use_dynamic =
- (!is_dynamic) ? lldb::eNoDynamicValues : lldb::eDynamicDontRunTarget;
- // Walk the path from the base value to the value that contains the requested field.
- for (uint32_t idx : path) {
- member_val_sp = member_val_sp->GetChildAtIndex(idx, /*can_create*/ true);
- }
- // If that didn't work, try it with the dynamic value.
- if (!member_val_sp && is_dynamic) {
- lldb::ValueObjectSP dyn_val_sp = value->GetDynamicValue(use_dynamic);
- if (dyn_val_sp) {
- for (uint32_t idx : path) {
- dyn_val_sp = dyn_val_sp->GetChildAtIndex(idx, true);
- }
- member_val_sp = dyn_val_sp;
- }
- }
- assert(member_val_sp && "invalid ast: invalid member access");
-
- return member_val_sp;
-}
-
-static bool GetFieldIndex(CompilerType type, const std::string &name,
- std::vector<uint32_t> *idx_path) {
- bool found = false;
- uint32_t num_fields = type.GetNumFields();
- for (uint32_t i = 0; i < num_fields; ++i) {
- uint64_t bit_offset = 0;
- uint32_t bitfield_bit_size = 0;
- bool is_bitfield = false;
- std::string name_sstr;
- CompilerType field_type(type.GetFieldAtIndex(
- i, name_sstr, &bit_offset, &bitfield_bit_size, &is_bitfield));
- auto field_name =
- name_sstr.length() == 0 ? std::optional<std::string>() : name_sstr;
- if (field_type.IsValid() && name_sstr == name) {
- idx_path->push_back(i + type.GetNumberOfNonEmptyBaseClasses());
- found = true;
- break;
- } else if (field_type.IsAnonymousType()) {
- found = GetFieldIndex(field_type, name, idx_path);
- if (found) {
- idx_path->push_back(i + type.GetNumberOfNonEmptyBaseClasses());
- break;
- }
- }
- }
- return found;
-}
-
-static bool SearchBaseClassesForField(lldb::ValueObjectSP base_sp,
- CompilerType base_type,
- const std::string &name,
- std::vector<uint32_t> *idx_path,
- bool use_synthetic, bool is_dynamic) {
- bool found = false;
- uint32_t num_non_empty_bases = 0;
- uint32_t num_direct_bases = base_type.GetNumDirectBaseClasses();
- for (uint32_t i = 0; i < num_direct_bases; ++i) {
- uint32_t bit_offset;
- CompilerType base_class =
- base_type.GetDirectBaseClassAtIndex(i, &bit_offset);
- std::vector<uint32_t> field_idx_path;
- if (GetFieldIndex(base_class, name, &field_idx_path)) {
- for (uint32_t j : field_idx_path)
- idx_path->push_back(j + base_class.GetNumberOfNonEmptyBaseClasses());
- idx_path->push_back(i);
- return true;
- }
-
- found = SearchBaseClassesForField(base_sp, base_class, name, idx_path,
- use_synthetic, is_dynamic);
- if (found) {
- idx_path->push_back(i);
- return true;
- }
-
- if (base_class.GetNumFields() > 0)
- num_non_empty_bases += 1;
- }
- return false;
-}
-
-lldb::ValueObjectSP Interpreter::FindMemberWithName(lldb::ValueObjectSP base,
- ConstString name,
- bool is_arrow) {
- bool is_synthetic = false;
- bool is_dynamic = true;
- // See if GetChildMemberWithName works.
- lldb::ValueObjectSP field_obj =
- base->GetChildMemberWithName(name.GetStringRef());
- if (field_obj && field_obj->GetName() == name)
- return field_obj;
-
- // Check for synthetic member.
- lldb::ValueObjectSP child_sp = base->GetSyntheticValue();
- if (child_sp) {
- is_synthetic = true;
- field_obj = child_sp->GetChildMemberWithName(name);
- if (field_obj && field_obj->GetName() == name)
- return field_obj;
- }
-
- // Check indices of immediate member fields of base's type.
- CompilerType base_type = base->GetCompilerType();
- std::vector<uint32_t> field_idx_path;
- if (GetFieldIndex(base_type, name.GetString(), &field_idx_path)) {
- std::reverse(field_idx_path.begin(), field_idx_path.end());
- // Traverse the path & verify the final object is correct.
- field_obj = base;
- for (uint32_t i : field_idx_path)
- field_obj = field_obj->GetChildAtIndex(i, true);
- if (field_obj && field_obj->GetName() == name)
- return field_obj;
- }
-
- // Go through base classes and look for field there.
- std::vector<uint32_t> base_class_idx_path;
- bool found =
- SearchBaseClassesForField(base, base_type, name.GetString(),
- &base_class_idx_path, is_synthetic, is_dynamic);
- if (found && !base_class_idx_path.empty()) {
- std::reverse(base_class_idx_path.begin(), base_class_idx_path.end());
- field_obj =
- EvaluateMemberOf(base, base_class_idx_path, is_synthetic, is_dynamic);
- if (field_obj && field_obj->GetName() == name)
- return field_obj;
- }
-
- // Field not found.
- return lldb::ValueObjectSP();
-}
-
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const MemberOfNode *node) {
Status error;
- auto base_or_err = Evaluate(node->base());
+ auto base_or_err = Evaluate(node->GetBase());
if (!base_or_err) {
return base_or_err;
}
@@ -423,7 +285,7 @@ Interpreter::Visit(const MemberOfNode *node) {
CompilerType base_type = base->GetCompilerType();
// When using an arrow, make sure the base is a pointer or array type.
// When using a period, make sure the base type is NOT a pointer type.
- if (node->IsArrow() && !base_type.IsPointerType() &&
+ if (node->GetIsArrow() && !base_type.IsPointerType() &&
!base_type.IsArrayType()) {
lldb::ValueObjectSP deref_sp = base->Dereference(error);
if (error.Success()) {
@@ -435,25 +297,25 @@ Interpreter::Visit(const MemberOfNode *node) {
"did you mean to use '.'?",
base_type.TypeDescription());
return llvm::make_error<DILDiagnosticError>(
- m_expr, errMsg, node->GetLocation(), node->FieldName().GetLength());
+ m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}
- } else if (!node->IsArrow() && base_type.IsPointerType()) {
+ } else if (!node->GetIsArrow() && base_type.IsPointerType()) {
std::string errMsg =
llvm::formatv("member reference type {0} is a pointer; "
"did you mean to use '->'?",
base_type.TypeDescription());
return llvm::make_error<DILDiagnosticError>(
- m_expr, errMsg, node->GetLocation(), node->FieldName().GetLength());
+ m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}
// User specified array->elem; need to get to element[0] to look for fields.
- if (node->IsArrow() && base_type.IsArrayType())
+ if (node->GetIsArrow() && base_type.IsArrayType())
base = base->GetChildAtIndex(0);
// Now look for the member with the specified name.
lldb::ValueObjectSP field_obj =
- FindMemberWithName(base, node->FieldName(), node->IsArrow());
- if (field_obj) {
+ base->GetChildMemberWithName(llvm::StringRef(node->GetFieldName()));
+ if (field_obj && field_obj->GetName().GetString() == node->GetFieldName()) {
if (field_obj->GetCompilerType().IsReferenceType()) {
lldb::ValueObjectSP tmp_obj = field_obj->Dereference(error);
if (error.Fail())
@@ -463,13 +325,13 @@ Interpreter::Visit(const MemberOfNode *node) {
return field_obj;
}
- if (node->IsArrow() && base_type.IsPointerType())
+ if (node->GetIsArrow() && base_type.IsPointerType())
base_type = base_type.GetPointeeType();
- std::string errMsg = llvm::formatv(
- "no member named '{0}' in {1}", node->FieldName().GetStringRef(),
- base_type.GetFullyUnqualifiedType().TypeDescription());
+ 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->FieldName().GetLength());
+ m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}
} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 9c5bc71775fb2..b147ca61df73b 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -127,10 +127,9 @@ ASTNodeUP DILParser::ParsePostfixExpression() {
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,
- ConstString(member_id));
+ lhs = std::make_unique<MemberOfNode>(
+ member_token.GetLocation(), std::move(lhs),
+ token.GetKind() == Token::arrow, member_id);
}
return lhs;
}
>From 4afffe9379ed90e1000c8c0c187eb2e703024d70 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 12 May 2025 20:52:34 -0700
Subject: [PATCH 3/5] Minor code cleanups: - Formatting issues. - Update
the evalution of MemberOf to NOT automatically deref references - Update
tests to match. - Change GetFieldName to return a StringRef instead of a
std::string.
---
lldb/include/lldb/ValueObject/DILAST.h | 2 +-
lldb/source/ValueObject/DILEval.cpp | 15 ++-----
lldb/source/ValueObject/DILParser.cpp | 1 +
.../MemberOf/TestFrameVarDILMemberOf.py | 8 ++--
.../frame/var-dil/basics/MemberOf/main.cpp | 39 -------------------
5 files changed, 10 insertions(+), 55 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index f3cda4fbece9b..8687316657ca9 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -100,7 +100,7 @@ class MemberOfNode : public ASTNode {
ASTNode *GetBase() const { return m_base.get(); }
bool GetIsArrow() const { return m_is_arrow; }
- std::string GetFieldName() const { return m_field_name; }
+ llvm::StringRef GetFieldName() const { return llvm::StringRef(m_field_name); }
static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eMemberOfNode;
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 8630bdd0a4ed1..7eb7591a0a6b2 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -274,11 +274,9 @@ Interpreter::Visit(const UnaryOpNode *node) {
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const MemberOfNode *node) {
- Status error;
auto base_or_err = Evaluate(node->GetBase());
- if (!base_or_err) {
+ if (!base_or_err)
return base_or_err;
- }
lldb::ValueObjectSP base = *base_or_err;
// Perform basic type checking.
@@ -287,6 +285,7 @@ Interpreter::Visit(const MemberOfNode *node) {
// When using a period, make sure the base type is NOT a pointer type.
if (node->GetIsArrow() && !base_type.IsPointerType() &&
!base_type.IsArrayType()) {
+ Status error;
lldb::ValueObjectSP deref_sp = base->Dereference(error);
if (error.Success()) {
base = deref_sp;
@@ -314,14 +313,8 @@ Interpreter::Visit(const MemberOfNode *node) {
// Now look for the member with the specified name.
lldb::ValueObjectSP field_obj =
- base->GetChildMemberWithName(llvm::StringRef(node->GetFieldName()));
- if (field_obj && field_obj->GetName().GetString() == node->GetFieldName()) {
- if (field_obj->GetCompilerType().IsReferenceType()) {
- lldb::ValueObjectSP tmp_obj = field_obj->Dereference(error);
- if (error.Fail())
- return error.ToError();
- return tmp_obj;
- }
+ base->GetChildMemberWithName(node->GetFieldName());
+ if (field_obj && field_obj->GetName() == node->GetFieldName()) {
return field_obj;
}
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index b147ca61df73b..982fbf40e6b39 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -113,6 +113,7 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
}
return ParsePostfixExpression();
}
+
// Parse a postfix_expression.
//
// postfix_expression:
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
index ff942c88bf183..76918b872ce77 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
@@ -25,13 +25,13 @@ def test_frame_var(self):
self.expect("settings set target.experimental.use-DIL true",
substrs=[""])
self.expect_var_path("s.x", value="1")
- self.expect_var_path("s.r", value="2")
+ self.expect_var_path("s.r", type="int &")
self.expect_var_path("sr.x", value="1")
- self.expect_var_path("sr.r", value="2")
+ self.expect_var_path("sr.r", type="int &")
self.expect_var_path("sp->x", value="1")
- self.expect_var_path("sp->r", value="2")
+ self.expect_var_path("sp->r", type="int &")
self.expect_var_path("sarr->x", value="5");
- self.expect_var_path("sarr->r", value="2")
+ self.expect_var_path("sarr->r", type="int &")
self.expect("frame variable 'sp->foo'", error=True,
substrs=["no member named 'foo' in 'Sx'"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
index dace888bef4dc..b859c00917f0e 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
@@ -18,42 +18,3 @@ main(int argc, char**argv)
return 0; // Set a breakpoint here
}
-
-/*
- EXPECT_THAT(Eval("s.x"), IsEqual("1"));
- EXPECT_THAT(Eval("s.r"), IsEqual("2"));
- EXPECT_THAT(Eval("s.r + 1"), IsEqual("3"));
- EXPECT_THAT(Eval("sr.x"), IsEqual("1"));
- EXPECT_THAT(Eval("sr.r"), IsEqual("2"));
- EXPECT_THAT(Eval("sr.r + 1"), IsEqual("3"));
- EXPECT_THAT(Eval("sp->x"), IsEqual("1"));
- EXPECT_THAT(Eval("sp->r"), IsEqual("2"));
- EXPECT_THAT(Eval("sp->r + 1"), IsEqual("3"));
- EXPECT_THAT(Eval("sarr->x"), IsEqual("5"));
- EXPECT_THAT(Eval("sarr->r"), IsEqual("2"));
- EXPECT_THAT(Eval("sarr->r + 1"), IsEqual("3"));
- EXPECT_THAT(Eval("(sarr + 1)->x"), IsEqual("1"));
-
- EXPECT_THAT(
- Eval("sp->4"),
- IsError(
- "<expr>:1:5: expected 'identifier', got: <'4' (numeric_constant)>\n"
- "sp->4\n"
- " ^"));
- EXPECT_THAT(Eval("sp->foo"), IsError("no member named 'foo' in 'Sx'"));
- EXPECT_THAT(
- Eval("sp->r / (void*)0"),
- IsError("invalid operands to binary expression ('int' and 'void *')"));
-
- EXPECT_THAT(Eval("sp.x"), IsError("member reference type 'Sx *' is a "
- "pointer; did you mean to use '->'"));
- EXPECT_THAT(
- Eval("sarr.x"),
- IsError(
- "member reference base type 'Sx[2]' is not a structure or union"));
-
- // Test for record typedefs.
- EXPECT_THAT(Eval("sa.x"), IsEqual("3"));
- EXPECT_THAT(Eval("sa.y"), IsEqual("'\\x04'"));
-
-*/
>From a8eea48b2c63a4a7a4ac55feb9364fa3ff47dc1c Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Sat, 17 May 2025 12:58:11 -0700
Subject: [PATCH 4/5] - Remove code treating arrays as pointers. - Remove code
that automatically dereferenced result. - Update member-of logic to more
closely match current frame var implementation.
---
lldb/include/lldb/ValueObject/DILAST.h | 17 ++-
lldb/include/lldb/ValueObject/DILParser.h | 2 +
lldb/source/ValueObject/DILEval.cpp | 117 +++++++++++++-----
lldb/source/ValueObject/DILParser.cpp | 6 +-
.../MemberOf/TestFrameVarDILMemberOf.py | 6 +-
.../frame/var-dil/basics/MemberOf/main.cpp | 2 -
6 files changed, 109 insertions(+), 41 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 8687316657ca9..05d9ebd26a3b0 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -92,15 +92,24 @@ class IdentifierNode : public ASTNode {
class MemberOfNode : public ASTNode {
public:
MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow,
- std::string name)
+ std::string name, lldb::DynamicValueType use_dynamic,
+ bool fragile_ivar, bool use_synth_child,
+ bool check_ptr_vs_member)
: ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
- m_is_arrow(is_arrow), m_field_name(std::move(name)) {}
+ m_is_arrow(is_arrow), m_field_name(std::move(name)),
+ m_use_dynamic(use_dynamic), m_fragile_ivar(fragile_ivar),
+ m_use_synth_child(use_synth_child),
+ m_check_ptr_vs_member(check_ptr_vs_member) {}
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); }
+ bool GetCheckPtrVsMember() const { return m_check_ptr_vs_member; }
+ bool GetFragileIvar() const { return m_fragile_ivar; }
+ bool GetSynthChild() const { return m_use_synth_child; }
+ lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; }
static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eMemberOfNode;
@@ -110,6 +119,10 @@ class MemberOfNode : public ASTNode {
ASTNodeUP m_base;
bool m_is_arrow;
std::string m_field_name;
+ lldb::DynamicValueType m_use_dynamic;
+ bool m_fragile_ivar;
+ bool m_use_synth_child;
+ bool m_check_ptr_vs_member;
};
class UnaryOpNode : public ASTNode {
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index c62f8908290f5..bd152940d28f6 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -118,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
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 7eb7591a0a6b2..299916f69c041 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -278,47 +278,104 @@ Interpreter::Visit(const MemberOfNode *node) {
if (!base_or_err)
return base_or_err;
lldb::ValueObjectSP base = *base_or_err;
+ bool check_ptr_vs_member = node->GetCheckPtrVsMember();
+ bool fragile_ivar = node->GetFragileIvar();
+ bool synth_child = node->GetSynthChild();
+ lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
+
+ // Perform some basic type & correctness checking.
+ if (node->GetIsArrow()) {
+ if (!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();
+ }
+ }
- // Perform basic type checking.
- CompilerType base_type = base->GetCompilerType();
- // When using an arrow, make sure the base is a pointer or array type.
- // When using a period, make sure the base type is NOT a pointer type.
- if (node->GetIsArrow() && !base_type.IsPointerType() &&
- !base_type.IsArrayType()) {
- Status error;
- lldb::ValueObjectSP deref_sp = base->Dereference(error);
- if (error.Success()) {
- base = deref_sp;
- base_type = deref_sp->GetCompilerType().GetPointerType();
- } else {
- std::string errMsg =
- llvm::formatv("member reference type {0} is not a pointer; "
- "did you mean to use '.'?",
- base_type.TypeDescription());
- return llvm::make_error<DILDiagnosticError>(
- m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
+ // 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());
+ }
}
- } else if (!node->GetIsArrow() && base_type.IsPointerType()) {
- std::string errMsg =
- llvm::formatv("member reference type {0} is a pointer; "
- "did you mean to use '->'?",
- base_type.TypeDescription());
- return llvm::make_error<DILDiagnosticError>(
- m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}
- // User specified array->elem; need to get to element[0] to look for fields.
- if (node->GetIsArrow() && base_type.IsArrayType())
- base = base->GetChildAtIndex(0);
+ if (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());
+ }
+ }
+ }
- // Now look for the member with the specified name.
lldb::ValueObjectSP field_obj =
base->GetChildMemberWithName(node->GetFieldName());
+ if (!field_obj) {
+ if (synth_child) {
+ field_obj = base->GetSyntheticValue();
+ if (field_obj)
+ field_obj = field_obj->GetChildMemberWithName(node->GetFieldName());
+ }
+
+ if (!synth_child || !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 (use_dynamic != lldb::eNoDynamicValues) {
+ lldb::ValueObjectSP dynamic_val_sp =
+ field_obj->GetDynamicValue(use_dynamic);
+ if (dynamic_val_sp)
+ field_obj = dynamic_val_sp;
+ }
return field_obj;
}
- if (node->GetIsArrow() && base_type.IsPointerType())
+ 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(),
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 982fbf40e6b39..5c0051dd3eb1d 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -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();
@@ -130,7 +131,8 @@ ASTNodeUP DILParser::ParsePostfixExpression() {
std::string member_id = ParseIdExpression();
lhs = std::make_unique<MemberOfNode>(
member_token.GetLocation(), std::move(lhs),
- token.GetKind() == Token::arrow, member_id);
+ token.GetKind() == Token::arrow, member_id, UseDynamic(),
+ m_fragile_ivar, m_use_synthetic, m_check_ptr_vs_member);
}
return lhs;
}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
index 76918b872ce77..bb16c1f82489d 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
@@ -30,17 +30,13 @@ def test_frame_var(self):
self.expect_var_path("sr.r", type="int &")
self.expect_var_path("sp->x", value="1")
self.expect_var_path("sp->r", type="int &")
- self.expect_var_path("sarr->x", value="5");
- self.expect_var_path("sarr->r", type="int &")
self.expect("frame variable 'sp->foo'", error=True,
- substrs=["no member named 'foo' in 'Sx'"])
+ substrs=["no member named 'foo' in 'Sx *'"])
self.expect("frame variable 'sp.x'", error=True,
substrs=["member reference type 'Sx *' is a "
"pointer; did you mean to use '->'"])
- self.expect("frame variable 'sarr.x'", error=True,
- substrs=["no member named 'x' in 'Sx[2]'"])
# Test for record typedefs.
self.expect_var_path("sa.x", value="3")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
index b859c00917f0e..80e8c064b1701 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp
@@ -11,8 +11,6 @@ main(int argc, char**argv)
Sx& sr = s;
Sx* sp = &s;
- Sx sarr[2] = {{5, x, 2}, {1, x, 3}};
-
using SxAlias = Sx;
SxAlias sa{3, x, 4};
>From 9e397773d9ddafb66a70974276a603e433a09f00 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 22 May 2025 13:03:49 -0700
Subject: [PATCH 5/5] Make fragile_ivar, check_ptr_vs_member and use_synthetic
members of the Interpreter.
---
lldb/include/lldb/ValueObject/DILAST.h | 17 ++------------
lldb/include/lldb/ValueObject/DILEval.h | 10 +++++---
lldb/source/Target/StackFrame.cpp | 5 ++--
lldb/source/ValueObject/DILEval.cpp | 31 ++++++++++++-------------
lldb/source/ValueObject/DILParser.cpp | 3 +--
5 files changed, 28 insertions(+), 38 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 05d9ebd26a3b0..8687316657ca9 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -92,24 +92,15 @@ class IdentifierNode : public ASTNode {
class MemberOfNode : public ASTNode {
public:
MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow,
- std::string name, lldb::DynamicValueType use_dynamic,
- bool fragile_ivar, bool use_synth_child,
- bool check_ptr_vs_member)
+ std::string name)
: ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)),
- m_is_arrow(is_arrow), m_field_name(std::move(name)),
- m_use_dynamic(use_dynamic), m_fragile_ivar(fragile_ivar),
- m_use_synth_child(use_synth_child),
- m_check_ptr_vs_member(check_ptr_vs_member) {}
+ 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); }
- bool GetCheckPtrVsMember() const { return m_check_ptr_vs_member; }
- bool GetFragileIvar() const { return m_fragile_ivar; }
- bool GetSynthChild() const { return m_use_synth_child; }
- lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; }
static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eMemberOfNode;
@@ -119,10 +110,6 @@ class MemberOfNode : public ASTNode {
ASTNodeUP m_base;
bool m_is_arrow;
std::string m_field_name;
- lldb::DynamicValueType m_use_dynamic;
- bool m_fragile_ivar;
- bool m_use_synth_child;
- bool m_check_ptr_vs_member;
};
class UnaryOpNode : public ASTNode {
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 04c21ab77b6b4..266b6fb1a63eb 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -41,8 +41,9 @@ 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);
@@ -56,8 +57,11 @@ class Interpreter : Visitor {
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
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index df9f1eae32bd1..ab5cd0b27c789 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -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) {
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 299916f69c041..07a41e8602369 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -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.
@@ -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);
@@ -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;
@@ -278,14 +281,10 @@ Interpreter::Visit(const MemberOfNode *node) {
if (!base_or_err)
return base_or_err;
lldb::ValueObjectSP base = *base_or_err;
- bool check_ptr_vs_member = node->GetCheckPtrVsMember();
- bool fragile_ivar = node->GetFragileIvar();
- bool synth_child = node->GetSynthChild();
- lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
// Perform some basic type & correctness checking.
if (node->GetIsArrow()) {
- if (!fragile_ivar) {
+ 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 =
@@ -323,7 +322,7 @@ Interpreter::Visit(const MemberOfNode *node) {
}
}
- if (check_ptr_vs_member) {
+ if (m_check_ptr_vs_member) {
bool expr_is_ptr = node->GetIsArrow();
bool base_is_ptr = base->IsPointerType();
@@ -349,13 +348,13 @@ Interpreter::Visit(const MemberOfNode *node) {
lldb::ValueObjectSP field_obj =
base->GetChildMemberWithName(node->GetFieldName());
if (!field_obj) {
- if (synth_child) {
+ if (m_use_synthetic) {
field_obj = base->GetSyntheticValue();
if (field_obj)
field_obj = field_obj->GetChildMemberWithName(node->GetFieldName());
}
- if (!synth_child || !field_obj) {
+ if (!m_use_synthetic || !field_obj) {
std::string errMsg = llvm::formatv(
"no member named '{0}' in {1}", node->GetFieldName(),
base->GetCompilerType().GetFullyUnqualifiedType().TypeDescription());
@@ -365,9 +364,9 @@ Interpreter::Visit(const MemberOfNode *node) {
}
if (field_obj && field_obj->GetName() == node->GetFieldName()) {
- if (use_dynamic != lldb::eNoDynamicValues) {
+ if (m_use_dynamic != lldb::eNoDynamicValues) {
lldb::ValueObjectSP dynamic_val_sp =
- field_obj->GetDynamicValue(use_dynamic);
+ field_obj->GetDynamicValue(m_use_dynamic);
if (dynamic_val_sp)
field_obj = dynamic_val_sp;
}
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 5c0051dd3eb1d..b74fbeb0a7a0b 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -131,8 +131,7 @@ ASTNodeUP DILParser::ParsePostfixExpression() {
std::string member_id = ParseIdExpression();
lhs = std::make_unique<MemberOfNode>(
member_token.GetLocation(), std::move(lhs),
- token.GetKind() == Token::arrow, member_id, UseDynamic(),
- m_fragile_ivar, m_use_synthetic, m_check_ptr_vs_member);
+ token.GetKind() == Token::arrow, member_id);
}
return lhs;
}
More information about the lldb-commits
mailing list