Author: cmtice Date: 2025-05-23T07:30:10-07:00 New Revision: 53d7b1d9e00d8cc3189808bbc26e2ee4577766e7
URL: https://github.com/llvm/llvm-project/commit/53d7b1d9e00d8cc3189808bbc26e2ee4577766e7 DIFF: https://github.com/llvm/llvm-project/commit/53d7b1d9e00d8cc3189808bbc26e2ee4577766e7.diff LOG: [LLDB] Add field member operators to DIL (#138093) Add the arrow and period operators, allowing DIL to find and access member fields. Added: lldb/test/API/commands/frame/var-dil/basics/MemberOf/Makefile lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/Makefile lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/TestFrameVarDILMemberOfAnonymousMember.py lldb/test/API/commands/frame/var-dil/basics/MemberOfAnonymousMember/main.cpp lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/Makefile lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/TestFrameVarDILMemberOfInheritance.py lldb/test/API/commands/frame/var-dil/basics/MemberOfInheritance/main.cpp Modified: lldb/docs/dil-expr-lang.ebnf lldb/include/lldb/ValueObject/DILAST.h lldb/include/lldb/ValueObject/DILEval.h lldb/include/lldb/ValueObject/DILLexer.h lldb/include/lldb/ValueObject/DILParser.h lldb/source/Target/StackFrame.cpp lldb/source/ValueObject/DILAST.cpp lldb/source/ValueObject/DILEval.cpp lldb/source/ValueObject/DILLexer.cpp lldb/source/ValueObject/DILParser.cpp Removed: ################################################################################ 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..8687316657ca9 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, + 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) @@ -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..266b6fb1a63eb 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -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 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..bd152940d28f6 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(); @@ -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 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/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..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; @@ -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 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..b74fbeb0a7a0b 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(); @@ -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: // "&" @@ -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. 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..bb16c1f82489d --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py @@ -0,0 +1,43 @@ +""" +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", type="int &") + self.expect_var_path("sr.x", value="1") + 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("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 '->'"]) + + # 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..80e8c064b1701 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/main.cpp @@ -0,0 +1,18 @@ +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; + + using SxAlias = Sx; + SxAlias sa{3, x, 4}; + + return 0; // Set a breakpoint here +} 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 +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits