From 58c4f082452bc0600dcf72ba89cbfeafc109cc54 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Fri, 4 Apr 2025 22:13:02 +0500 Subject: [PATCH 1/6] Add unary operators Dereference and AddressOf --- lldb/docs/dil-expr-lang.ebnf | 4 +- lldb/include/lldb/ValueObject/DILAST.h | 32 +++++ lldb/include/lldb/ValueObject/DILEval.h | 29 +++++ lldb/include/lldb/ValueObject/DILLexer.h | 2 + lldb/include/lldb/ValueObject/DILParser.h | 3 +- lldb/source/ValueObject/DILAST.cpp | 4 + lldb/source/ValueObject/DILEval.cpp | 139 +++++++++++++++++++++- lldb/source/ValueObject/DILLexer.cpp | 9 +- lldb/source/ValueObject/DILParser.cpp | 32 ++++- 9 files changed, 245 insertions(+), 9 deletions(-) diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 0bbbecbdc78c1..13b9cc22e6000 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -3,7 +3,9 @@ (* This is currently a subset of the final DIL Language, matching the current DIL implementation. *) -expression = primary_expression ; +expression = unary_expression ; + +unary_expression = unary_operator primary_expression ; primary_expression = id_expression | "(" expression ")"; diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 05d87e9cc4b6b..323ebe8dd49ec 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -20,6 +20,13 @@ namespace lldb_private::dil { enum class NodeKind { eErrorNode, eIdentifierNode, + eUnaryOpNode, +}; + +/// The Unary operators recognized by DIL. +enum class UnaryOpKind { + AddrOf, // "&" + Deref, // "*" }; /// Forward declaration, for use in DIL AST nodes. Definition is at the very @@ -44,6 +51,8 @@ class ASTNode { virtual llvm::Expected Accept(Visitor *v) const = 0; + virtual bool is_rvalue() const { return false; } + uint32_t GetLocation() const { return m_location; } NodeKind GetKind() const { return m_kind; } @@ -81,6 +90,27 @@ class IdentifierNode : public ASTNode { std::string m_name; }; +class UnaryOpNode : public ASTNode { +public: + UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP rhs) + : ASTNode(location, NodeKind::eUnaryOpNode), m_kind(kind), + m_rhs(std::move(rhs)) {} + + llvm::Expected Accept(Visitor *v) const override; + bool is_rvalue() const override { return m_kind != UnaryOpKind::Deref; } + + UnaryOpKind kind() const { return m_kind; } + ASTNode *rhs() const { return m_rhs.get(); } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eUnaryOpNode; + } + +private: + UnaryOpKind m_kind; + ASTNodeUP m_rhs; +}; + /// This class contains one Visit method for each specialized type of /// DIL AST node. The Visit methods are used to dispatch a DIL AST node to /// the correct function in the DIL expression evaluator for evaluating that @@ -90,6 +120,8 @@ class Visitor { virtual ~Visitor() = default; virtual llvm::Expected Visit(const IdentifierNode *node) = 0; + virtual llvm::Expected + Visit(const UnaryOpNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 335035d3f9248..0080f4dba9291 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -38,6 +38,18 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref, lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr = nullptr); +class FlowAnalysis { +public: + FlowAnalysis(bool address_of_is_pending) + : m_address_of_is_pending(address_of_is_pending) {} + + bool AddressOfIsPending() const { return m_address_of_is_pending; } + void DiscardAddressOf() { m_address_of_is_pending = false; } + +private: + bool m_address_of_is_pending; +}; + class Interpreter : Visitor { public: Interpreter(lldb::TargetSP target, llvm::StringRef expr, @@ -47,12 +59,29 @@ class Interpreter : Visitor { llvm::Expected Evaluate(const ASTNode *node); private: + llvm::Expected + EvaluateNode(const ASTNode *node, FlowAnalysis *flow = nullptr); + llvm::Expected Visit(const IdentifierNode *node) override; + llvm::Expected Visit(const UnaryOpNode *node) override; + + lldb::ValueObjectSP EvaluateDereference(lldb::ValueObjectSP rhs); + + FlowAnalysis *flow_analysis() { return m_flow_analysis_chain.back(); } // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; llvm::StringRef m_expr; + // Flow analysis chain represents the expression evaluation flow for the + // current code branch. Each node in the chain corresponds to an AST node, + // describing the semantics of the evaluation for it. Currently, flow analysis + // propagates the information about the pending address-of operator, so that + // combination of address-of and a subsequent dereference can be eliminated. + // End of the chain (i.e. `back()`) contains the flow analysis instance for + // the current node. It may be `nullptr` if no relevant information is + // available, the caller/user is supposed to check. + std::vector m_flow_analysis_chain; lldb::ValueObjectSP m_scope; lldb::DynamicValueType m_default_dynamic; std::shared_ptr m_exe_ctx_scope; diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h index d15fc382d1623..3508b8b7a85c6 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -24,11 +24,13 @@ namespace lldb_private::dil { class Token { public: enum Kind { + amp, coloncolon, eof, identifier, l_paren, r_paren, + star, }; Token(Kind kind, std::string spelling, uint32_t start) diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 9b7a6cd487939..b755f7eeeac5a 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -43,7 +43,7 @@ class DILDiagnosticError m_detail(std::move(detail)) {} DILDiagnosticError(llvm::StringRef expr, const std::string &message, - uint32_t loc, uint16_t err_len); + uint32_t loc, uint16_t err_len = 1); std::unique_ptr Clone() const override { return std::make_unique(m_detail); @@ -83,6 +83,7 @@ class DILParser { ASTNodeUP Run(); ASTNodeUP ParseExpression(); + ASTNodeUP ParseUnaryExpression(); ASTNodeUP ParsePrimaryExpression(); std::string ParseNestedNameSpecifier(); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index e75958d784627..ea847587501ee 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -19,4 +19,8 @@ llvm::Expected IdentifierNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected UnaryOpNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 4889834c7a3c1..743b024b44b2e 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,6 +18,22 @@ namespace lldb_private::dil { +static lldb::ValueObjectSP +ArrayToPointerConversion(lldb::ValueObjectSP valobj, + std::shared_ptr ctx) { + assert(valobj->IsArrayType() && + "an argument to array-to-pointer conversion must be an array"); + + uint64_t addr = valobj->GetLoadAddress(); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx; + ctx->CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + name, addr, exe_ctx, + valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), + /* do_deref */ false); +} + static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -206,10 +222,25 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr, : m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic), m_exe_ctx_scope(frame_sp) {} -llvm::Expected Interpreter::Evaluate(const ASTNode *node) { +llvm::Expected Interpreter::Evaluate(const ASTNode *tree) { + // Evaluate an AST. + auto value_or_error = EvaluateNode(tree); + + // Return the computed result-or-error. + return value_or_error; +} +llvm::Expected +Interpreter::EvaluateNode(const ASTNode *node, FlowAnalysis *flow) { + // Set up the evaluation context for the current node. + m_flow_analysis_chain.push_back(flow); // Traverse an AST pointed by the `node`. - return node->Accept(this); + auto value_or_error = node->Accept(this); + // Cleanup the context. + m_flow_analysis_chain.pop_back(); + // Return the computed value-or-error. The caller is responsible for + // checking if an error occured during the evaluation. + return value_or_error; } llvm::Expected @@ -232,4 +263,106 @@ Interpreter::Visit(const IdentifierNode *node) { return identifier; } -} // namespace lldb_private::dil +llvm::Expected +Interpreter::Visit(const UnaryOpNode *node) { + FlowAnalysis rhs_flow( + /* address_of_is_pending */ node->kind() == UnaryOpKind::AddrOf); + + Status error; + auto rhs_or_err = EvaluateNode(node->rhs(), &rhs_flow); + if (!rhs_or_err) { + return rhs_or_err; + } + lldb::ValueObjectSP rhs = *rhs_or_err; + + if (rhs->GetCompilerType().IsReferenceType()) { + rhs = rhs->Dereference(error); + if (error.Fail()) { + return llvm::make_error(m_expr, error.AsCString(), + node->GetLocation()); + } + } + CompilerType rhs_type = rhs->GetCompilerType(); + switch (node->kind()) { + case UnaryOpKind::Deref: { + if (rhs_type.IsArrayType()) + rhs = ArrayToPointerConversion(rhs, m_exe_ctx_scope); + + lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic); + if (dynamic_rhs) + rhs = dynamic_rhs; + + if (rhs->GetCompilerType().IsPointerType()) + return EvaluateDereference(rhs); + lldb::ValueObjectSP child_sp = rhs->Dereference(error); + if (error.Success()) + rhs = child_sp; + + return rhs; + } + case UnaryOpKind::AddrOf: { + if (node->rhs()->is_rvalue()) { + std::string errMsg = + llvm::formatv("cannot take the address of an rvalue of type {0}", + rhs_type.TypeDescription()); + return llvm::make_error(m_expr, errMsg, + node->GetLocation()); + } + if (rhs->IsBitfield()) { + return llvm::make_error( + m_expr, "address of bit-field requested", node->GetLocation()); + } + // If the address-of operation wasn't cancelled during the evaluation of + // RHS (e.g. because of the address-of-a-dereference elision), apply it + // here. + if (rhs_flow.AddressOfIsPending()) { + Status error; + lldb::ValueObjectSP value = rhs->AddressOf(error); + if (error.Fail()) { + return llvm::make_error(m_expr, error.AsCString(), + node->GetLocation()); + } + return value; + } + return rhs; + } + } + + // Unsupported/invalid operation. + return llvm::make_error( + m_expr, "invalid ast: unexpected binary operator", node->GetLocation(), + 1); +} + +lldb::ValueObjectSP Interpreter::EvaluateDereference(lldb::ValueObjectSP rhs) { + // If rhs is a reference, dereference it first. + Status error; + if (rhs->GetCompilerType().IsReferenceType()) + rhs = rhs->Dereference(error); + + assert(rhs->GetCompilerType().IsPointerType() && + "invalid ast: must be a pointer type"); + + if (rhs->GetDerefValobj()) + return rhs->GetDerefValobj()->GetSP(); + + CompilerType pointer_type = rhs->GetCompilerType(); + lldb::addr_t base_addr = rhs->GetValueAsUnsigned(0); + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress( + name, base_addr, exe_ctx, pointer_type, + /* do_deref */ false); + + // If we're in the address-of context, skip the dereference and cancel the + // pending address-of operation as well. + if (flow_analysis() && flow_analysis()->AddressOfIsPending()) { + flow_analysis()->DiscardAddressOf(); + return value; + } + + return value->Dereference(error); +} + +} // namespace lldb_private::dil \ No newline at end of file diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index 1f013288c839b..b9c2e7971e3b4 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -19,6 +19,8 @@ namespace lldb_private::dil { llvm::StringRef Token::GetTokenName(Kind kind) { switch (kind) { + case Kind::amp: + return "amp"; case Kind::coloncolon: return "coloncolon"; case Kind::eof: @@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "l_paren"; case Kind::r_paren: return "r_paren"; + case Token::star: + return "star"; } llvm_unreachable("Unknown token name"); } @@ -82,9 +86,8 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair operators[] = { - {Token::l_paren, "("}, - {Token::r_paren, ")"}, - {Token::coloncolon, "::"}, + {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, + {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 a8baba2c06e7a..c233c535c0355 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -82,7 +82,37 @@ ASTNodeUP DILParser::Run() { // expression: // primary_expression // -ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); } +ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); } + +// Parse an unary_expression. +// +// unary_expression: +// unary_operator primary_expression +// +// unary_operator: +// "&" +// "*" +// +ASTNodeUP DILParser::ParseUnaryExpression() { + if (CurToken().IsOneOf({Token::amp, Token::star})) { + Token token = CurToken(); + uint32_t loc = token.GetLocation(); + m_dil_lexer.Advance(); + auto rhs = ParsePrimaryExpression(); + switch (token.GetKind()) { + case Token::star: + return std::make_unique(loc, UnaryOpKind::Deref, + std::move(rhs)); + case Token::amp: + return std::make_unique(loc, UnaryOpKind::AddrOf, + std::move(rhs)); + + default: + llvm_unreachable("invalid token kind"); + } + } + return ParsePrimaryExpression(); +} // Parse a primary_expression. // From 36d4f95784756fb38305697e88ceabd22cf5370f Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Tue, 8 Apr 2025 00:25:57 +0500 Subject: [PATCH 2/6] Fix parsing, remove dereferencing, add unit tests --- lldb/docs/dil-expr-lang.ebnf | 5 +- lldb/include/lldb/API/SBFrame.h | 3 + lldb/include/lldb/Target/StackFrame.h | 3 + lldb/source/API/SBFrame.cpp | 35 + lldb/source/Target/StackFrame.cpp | 17 +- lldb/source/ValueObject/DILEval.cpp | 15 +- lldb/source/ValueObject/DILParser.cpp | 5 +- lldb/unittests/CMakeLists.txt | 1 + lldb/unittests/DIL/CMakeLists.txt | 15 + .../{ValueObject => DIL}/DILLexerTests.cpp | 0 lldb/unittests/DIL/DILTests.cpp | 303 ++++ lldb/unittests/DIL/Inputs/CMakeLists.txt | 36 + lldb/unittests/DIL/Inputs/test_binary.cpp | 1255 +++++++++++++++++ lldb/unittests/DIL/Inputs/test_extern.cpp | 9 + lldb/unittests/DIL/Runner.cpp | 146 ++ lldb/unittests/DIL/Runner.h | 22 + lldb/unittests/ValueObject/CMakeLists.txt | 1 - 17 files changed, 1856 insertions(+), 15 deletions(-) create mode 100644 lldb/unittests/DIL/CMakeLists.txt rename lldb/unittests/{ValueObject => DIL}/DILLexerTests.cpp (100%) create mode 100644 lldb/unittests/DIL/DILTests.cpp create mode 100644 lldb/unittests/DIL/Inputs/CMakeLists.txt create mode 100644 lldb/unittests/DIL/Inputs/test_binary.cpp create mode 100644 lldb/unittests/DIL/Inputs/test_extern.cpp create mode 100644 lldb/unittests/DIL/Runner.cpp create mode 100644 lldb/unittests/DIL/Runner.h diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 13b9cc22e6000..c8bf4231b3e4a 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -5,7 +5,10 @@ expression = unary_expression ; -unary_expression = unary_operator primary_expression ; +unary_expression = unary_operator expression + | primary_expression ; + +unary_operator = "*" | "&" ; primary_expression = id_expression | "(" expression ")"; diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 3635ee5a537ad..e0778623d503d 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -182,6 +182,9 @@ class LLDB_API SBFrame { // variable value. lldb::SBValue GetValueForVariablePath(const char *var_expr_cstr, DynamicValueType use_dynamic); + lldb::SBValue TestGetValueForVariablePath(const char *var_expr_cstr, + DynamicValueType use_dynamic, + bool use_DIL = false); /// The version that doesn't supply a 'use_dynamic' value will use the /// target's default. diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index 3f51c9a7f22f0..3673040335140 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -308,6 +308,9 @@ class StackFrame : public ExecutionContextScope, lldb::ValueObjectSP GetValueForVariableExpressionPath( llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, uint32_t options, lldb::VariableSP &var_sp, Status &error); + lldb::ValueObjectSP GetValueForVariableExpressionPath( + llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, + uint32_t options, lldb::VariableSP &var_sp, Status &error, bool use_DIL); /// Determine whether this StackFrame has debug information available or not. /// diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index 5b69cf1ee2641..c77ab4f335c68 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -492,6 +492,41 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path, return sb_value; } +lldb::SBValue SBFrame::TestGetValueForVariablePath(const char *var_path, + DynamicValueType use_dynamic, + bool use_DIL) { + LLDB_INSTRUMENT_VA(this, var_path, use_dynamic); + + SBValue sb_value; + if (var_path == nullptr || var_path[0] == '\0') { + return sb_value; + } + + std::unique_lock lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + + StackFrame *frame = nullptr; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) { + frame = exe_ctx.GetFramePtr(); + if (frame) { + VariableSP var_sp; + Status error; + ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath( + var_path, eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, + var_sp, error, use_DIL)); + sb_value.SetSP(value_sp, use_dynamic); + } + } + } + return sb_value; +} + SBValue SBFrame::FindVariable(const char *name) { LLDB_INSTRUMENT_VA(this, name); diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 0306f68169a98..05603cde27a65 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -523,6 +523,17 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath( var_sp, error); } +ValueObjectSP StackFrame::GetValueForVariableExpressionPath( + llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options, + VariableSP &var_sp, Status &error, bool use_DIL) { + if (use_DIL) + return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options, + var_sp, error); + + return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options, + var_sp, error); +} + ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath( llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, uint32_t options, lldb::VariableSP &var_sp, Status &error) { @@ -538,7 +549,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath( auto lex_or_err = dil::DILLexer::Create(var_expr); if (!lex_or_err) { error = Status::FromError(lex_or_err.takeError()); - return ValueObjectSP(); + return ValueObjectConstResult::Create(nullptr, std::move(error)); } // Parse the expression. @@ -547,7 +558,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath( !no_synth_child, !no_fragile_ivar, check_ptr_vs_member); if (!tree_or_error) { error = Status::FromError(tree_or_error.takeError()); - return ValueObjectSP(); + return ValueObjectConstResult::Create(nullptr, std::move(error)); } // Evaluate the parsed expression. @@ -558,7 +569,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath( auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get()); if (!valobj_or_error) { error = Status::FromError(valobj_or_error.takeError()); - return ValueObjectSP(); + return ValueObjectConstResult::Create(nullptr, std::move(error)); } return *valobj_or_error; diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 743b024b44b2e..8db20da4132a9 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -275,13 +275,6 @@ Interpreter::Visit(const UnaryOpNode *node) { } lldb::ValueObjectSP rhs = *rhs_or_err; - if (rhs->GetCompilerType().IsReferenceType()) { - rhs = rhs->Dereference(error); - if (error.Fail()) { - return llvm::make_error(m_expr, error.AsCString(), - node->GetLocation()); - } - } CompilerType rhs_type = rhs->GetCompilerType(); switch (node->kind()) { case UnaryOpKind::Deref: { @@ -292,8 +285,14 @@ Interpreter::Visit(const UnaryOpNode *node) { if (dynamic_rhs) rhs = dynamic_rhs; - if (rhs->GetCompilerType().IsPointerType()) + if (rhs->GetCompilerType().IsPointerType()) { + if (rhs->GetCompilerType().IsPointerToVoid()) { + return llvm::make_error( + m_expr, "indirection not permitted on operand of type 'void *'", + node->GetLocation(), 1); + } return EvaluateDereference(rhs); + } lldb::ValueObjectSP child_sp = rhs->Dereference(error); if (error.Success()) rhs = child_sp; diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index c233c535c0355..553b4dc31d9c0 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -87,7 +87,8 @@ ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); } // Parse an unary_expression. // // unary_expression: -// unary_operator primary_expression +// unary_operator expression +// primary_expression // // unary_operator: // "&" @@ -98,7 +99,7 @@ ASTNodeUP DILParser::ParseUnaryExpression() { Token token = CurToken(); uint32_t loc = token.GetLocation(); m_dil_lexer.Advance(); - auto rhs = ParsePrimaryExpression(); + auto rhs = ParseExpression(); switch (token.GetKind()) { case Token::star: return std::make_unique(loc, UnaryOpKind::Deref, diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt index cc9d45ebf981d..dc8b4c84e22f7 100644 --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -54,6 +54,7 @@ endif() add_subdirectory(Breakpoint) add_subdirectory(Callback) add_subdirectory(Core) +add_subdirectory(DIL) add_subdirectory(DataFormatter) add_subdirectory(Disassembler) add_subdirectory(Editline) diff --git a/lldb/unittests/DIL/CMakeLists.txt b/lldb/unittests/DIL/CMakeLists.txt new file mode 100644 index 0000000000000..07fb7172a2b4c --- /dev/null +++ b/lldb/unittests/DIL/CMakeLists.txt @@ -0,0 +1,15 @@ +add_lldb_unittest(DILTests + DILTests.cpp + DILLexerTests.cpp + Runner.cpp + + LINK_LIBS + liblldb + lldbUtilityHelpers + lldbValueObject + LLVMTestingSupport + ) +add_subdirectory(Inputs) +add_dependencies(DILTests test_binary) + +add_unittest_inputs(DILTests "test_binary.cpp") diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/DIL/DILLexerTests.cpp similarity index 100% rename from lldb/unittests/ValueObject/DILLexerTests.cpp rename to lldb/unittests/DIL/DILLexerTests.cpp diff --git a/lldb/unittests/DIL/DILTests.cpp b/lldb/unittests/DIL/DILTests.cpp new file mode 100644 index 0000000000000..c9c3620cf3162 --- /dev/null +++ b/lldb/unittests/DIL/DILTests.cpp @@ -0,0 +1,303 @@ +//===-- DILTests.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Runner.h" +#include "TestingSupport/TestUtilities.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBType.h" +#include "lldb/lldb-enumerations.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::testing::MakeMatcher; +using ::testing::Matcher; +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +struct EvalResult { + lldb::SBError lldb_DIL_error; + mutable lldb::SBValue lldb_DIL_value; + mutable std::optional lldb_value; +}; + +class EvaluatorHelper { +public: + EvaluatorHelper(lldb::SBFrame frame, bool compare_with_frame_var) + : frame_(frame), compare_with_frame_var_(compare_with_frame_var) {} + +public: + EvalResult Eval(const std::string &expr) { + EvalResult ret; + ret.lldb_DIL_value = frame_.TestGetValueForVariablePath( + expr.c_str(), lldb::eNoDynamicValues, true); + if (!ret.lldb_DIL_value.GetError().Success()) + ret.lldb_DIL_error = ret.lldb_DIL_value.GetError(); + if (compare_with_frame_var_) { + ret.lldb_value = frame_.TestGetValueForVariablePath( + expr.c_str(), lldb::eNoDynamicValues, false); + } + return ret; + } + +private: + lldb::SBFrame frame_; + bool compare_with_frame_var_; +}; + +void PrintError(::testing::MatchResultListener *listener, + const std::string &error) { + *listener << "error:"; + // Print multiline errors on a separate line. + if (error.find('\n') != std::string::npos) { + *listener << "\n"; + } else { + *listener << " "; + } + *listener << error; +} + +class IsOkMatcher : public MatcherInterface { +public: + explicit IsOkMatcher(bool compare_types) : compare_types_(compare_types) {} + + bool MatchAndExplain(EvalResult result, + MatchResultListener *listener) const override { + if (result.lldb_DIL_error.GetError()) { + PrintError(listener, result.lldb_DIL_error.GetCString()); + return false; + } + + std::string actual = result.lldb_DIL_value.GetValue(); + // Compare only if we tried to evaluate with LLDB. + if (result.lldb_value.has_value()) { + if (result.lldb_value.value().GetError().GetError()) { + *listener << "values produced by DIL and 'frame var' don't match\n" + << "DIL : " << actual << "\n" + << "frame var: " + << result.lldb_value.value().GetError().GetCString(); + return false; + + } else if (actual != result.lldb_value.value().GetValue()) { + *listener << "values produced by DIL and 'frame var' don't match\n" + << "DIL : " << actual << "\n" + << "frame var: " << result.lldb_value.value().GetValue(); + return false; + } + + if (compare_types_) { + const char *lldb_DIL_type = + result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName(); + const char *lldb_type = + result.lldb_value.value().GetType().GetUnqualifiedType().GetName(); + if (strcmp(lldb_DIL_type, lldb_type) != 0) { + *listener << "types produced by DIL and 'frame var' don't match\n" + << "DIL : " << lldb_DIL_type << "\n" + << "frame var: " << lldb_type; + return false; + } + } + } + + return true; + } + + void DescribeTo(std::ostream *os) const override { + *os << "evaluates without an error and equals to LLDB"; + } + +private: + bool compare_types_; +}; + +Matcher IsOk(bool compare_types = true) { + return MakeMatcher(new IsOkMatcher(compare_types)); +} + +class IsEqualMatcher : public MatcherInterface { +public: + IsEqualMatcher(std::string value, bool compare_types) + : value_(std::move(value)), compare_types_(compare_types) {} + +public: + bool MatchAndExplain(EvalResult result, + MatchResultListener *listener) const override { + if (result.lldb_DIL_error.GetError()) { + PrintError(listener, result.lldb_DIL_error.GetCString()); + return false; + } + + std::string actual = result.lldb_DIL_value.GetValue(); + if (actual != value_) { + *listener << "evaluated to '" << actual << "'"; + return false; + } + + // Compare only if we tried to evaluate with LLDB. + if (result.lldb_value.has_value()) { + if (result.lldb_value.value().GetError().GetError()) { + *listener << "values produced by DIL and 'frame var' don't match\n" + << "DIL : " << actual << "\n" + << "frame var: " + << result.lldb_value.value().GetError().GetCString(); + return false; + + } else if (actual != result.lldb_value.value().GetValue()) { + *listener << "values produced by DIL and 'frame var' don't match\n" + << "DIL : " << actual << "\n" + << "frame var: " << result.lldb_value.value().GetValue(); + return false; + } + + if (compare_types_) { + const char *lldb_DIL_type = + result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName(); + const char *lldb_type = + result.lldb_value.value().GetType().GetUnqualifiedType().GetName(); + if (strcmp(lldb_DIL_type, lldb_type) != 0) { + *listener << "types produced by DIL and 'frame var' don't match\n" + << "DIL : " << lldb_DIL_type << "\n" + << "frame var: " << lldb_type; + return false; + } + } + } + return true; + } + + void DescribeTo(std::ostream *os) const override { + *os << "evaluates to '" << value_ << "'"; + } + +private: + std::string value_; + bool compare_types_; +}; + +Matcher IsEqual(std::string value, bool compare_types = true) { + return MakeMatcher(new IsEqualMatcher(std::move(value), compare_types)); +} + +class IsErrorMatcher : public MatcherInterface { +public: + explicit IsErrorMatcher(std::string value) : value_(std::move(value)) {} + +public: + bool MatchAndExplain(EvalResult result, + MatchResultListener *listener) const override { + if (!result.lldb_DIL_error.GetError()) { + *listener << "evaluated to '" << result.lldb_DIL_value.GetValue() << "'"; + return false; + } + std::string message = result.lldb_DIL_error.GetCString(); + if (message.find(value_) == std::string::npos) { + PrintError(listener, message); + return false; + } + + return true; + } + + void DescribeTo(std::ostream *os) const override { + *os << "evaluates with an error: '" << value_ << "'"; + } + +private: + std::string value_; +}; + +Matcher IsError(std::string value) { + return MakeMatcher(new IsErrorMatcher(std::move(value))); +} + +class EvalTest : public ::testing::Test { +protected: + static void SetUpTestSuite() { lldb::SBDebugger::Initialize(); } + + static void TearDownTestSuite() { lldb::SBDebugger::Terminate(); } + + void SetUp() override { + std::string test_name = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + std::string break_line = "// BREAK(" + test_name + ")"; + + std::string binary_path = + lldb_private::GetInputFilePath("test_binary.bin"); + std::string source_path = lldb_private::GetInputFilePath("test_binary.cpp"); + + debugger_ = lldb::SBDebugger::Create(false); + process_ = + LaunchTestProgram(debugger_, source_path, binary_path, break_line); + frame_ = process_.GetSelectedThread().GetSelectedFrame(); + } + + void TearDown() override { + process_.Destroy(); + lldb::SBDebugger::Destroy(debugger_); + } + + EvalResult Eval(const std::string &expr) { + return EvaluatorHelper(frame_, compare_with_frame_var_).Eval(expr); + } + + bool Is32Bit() const { + if (process_.GetAddressByteSize() == 4) { + return true; + } + return false; + } + +protected: + lldb::SBDebugger debugger_; + lldb::SBProcess process_; + lldb::SBFrame frame_; + + // Evaluate with both DIL and LLDB by default. + bool compare_with_frame_var_ = true; +}; + +TEST_F(EvalTest, TestSymbols) { + EXPECT_GT(frame_.GetModule().GetNumSymbols(), (size_t)0) + << "No symbols might indicate that the test binary was built incorrectly"; +} + +TEST_F(EvalTest, TestPointerDereference) { + EXPECT_THAT(Eval("*p_int0"), IsEqual("0")); + EXPECT_THAT(Eval("*cp_int5"), IsEqual("5")); + EXPECT_THAT(Eval("*rcp_int0"), IsOk()); + + EXPECT_THAT(Eval("&*p_void"), + IsError("indirection not permitted on operand of type" + " 'void *'")); + + this->compare_with_frame_var_ = false; + EXPECT_THAT(Eval("*array"), IsEqual("0")); + EXPECT_THAT(Eval("&*p_null"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("**pp_int0"), IsEqual("0")); + EXPECT_THAT(Eval("&**pp_int0"), IsOk()); +} + +TEST_F(EvalTest, TestAddressOf) { + EXPECT_THAT(Eval("&x"), IsOk()); + EXPECT_THAT(Eval("r"), IsOk()); + EXPECT_THAT(Eval("&r"), IsOk()); + EXPECT_THAT(Eval("pr"), IsOk()); + EXPECT_THAT(Eval("&pr"), IsOk()); + EXPECT_THAT(Eval("my_pr"), IsOk()); + EXPECT_THAT(Eval("&my_pr"), IsOk()); + + EXPECT_THAT(Eval("&globalVar"), IsOk()); + EXPECT_THAT(Eval("&s_str"), IsOk()); + EXPECT_THAT(Eval("¶m"), IsOk()); +} diff --git a/lldb/unittests/DIL/Inputs/CMakeLists.txt b/lldb/unittests/DIL/Inputs/CMakeLists.txt new file mode 100644 index 0000000000000..73e179d08d059 --- /dev/null +++ b/lldb/unittests/DIL/Inputs/CMakeLists.txt @@ -0,0 +1,36 @@ +# Build `test_binary.cc` and put the binary in the Inputs folder, +# allowing `lldb_private::GetInputFilePath` to find it. +# Projects that must be enabled: clang;lldb;lld +# Runtimes that must be enabled: libcxx;libcxxabi;libunwind +if ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES) + if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE) + set(LIBCXX_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE}) + set(LIBCXX_GENERATED_INCLUDE_DIR "${LLVM_BINARY_DIR}/include/c++/v1") + set(LIBCXX_GENERATED_INCLUDE_TARGET_DIR "${LLVM_BINARY_DIR}/include/${LLVM_DEFAULT_TARGET_TRIPLE}/c++/v1") + else() + set(LIBCXX_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBCXX_LIBDIR_SUFFIX}) + set(LIBCXX_GENERATED_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include/c++/v1") + endif() + + if(DEFINED LIBCXX_GENERATED_INCLUDE_TARGET_DIR) + set(INCLUDE_TARGET_DIR_OPTION "-cxx-isystem" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}") + endif() + + get_target_property(EXE_PATH DILTests RUNTIME_OUTPUT_DIRECTORY) + add_custom_command( + OUTPUT test_binary.bin + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/test_binary.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_extern.cpp + -O0 -g -std=c++17 -fuse-ld=lld -B$ + -nostdlib++ -nostdinc++ -cxx-isystem ${LIBCXX_GENERATED_INCLUDE_DIR} + ${INCLUDE_TARGET_DIR_OPTION} + -L${LIBCXX_LIBRARY_DIR} -Wl,-rpath,${LIBCXX_LIBRARY_DIR} -lc++ + -o ${EXE_PATH}/Inputs/test_binary.bin + DEPENDS test_binary.cpp test_extern.cpp clang lld + ) + add_custom_target(test_binary + DEPENDS test_binary.bin + ) +else() + message(FATAL_ERROR "libcxx runtime must be enabled.") +endif() diff --git a/lldb/unittests/DIL/Inputs/test_binary.cpp b/lldb/unittests/DIL/Inputs/test_binary.cpp new file mode 100644 index 0000000000000..115add3129bcd --- /dev/null +++ b/lldb/unittests/DIL/Inputs/test_binary.cpp @@ -0,0 +1,1255 @@ +//===-- test_binary.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +static void TestArithmetic() { + char c = 10; + unsigned char uc = 1; + int a = 1; + int int_max = std::numeric_limits::max(); + int int_min = std::numeric_limits::min(); + unsigned int uint_max = std::numeric_limits::max(); + unsigned int uint_zero = 0; + long long ll_max = std::numeric_limits::max(); + long long ll_min = std::numeric_limits::min(); + unsigned long long ull_max = std::numeric_limits::max(); + unsigned long long ull_zero = 0; + + int x = 2; + int &r = x; + int *p = &x; + + typedef int &myr; + myr my_r = x; + + auto fnan = std::numeric_limits::quiet_NaN(); + auto fsnan = std::numeric_limits::signaling_NaN(); + // Smallest positive non-zero float denormal + auto fdenorm = 0x0.1p-145f; + + // BREAK(TestArithmetic) + // BREAK(TestZeroDivision) +} + +static void TestBitwiseOperators() { + bool var_true = true; + bool var_false = false; + + unsigned long long ull_max = std::numeric_limits::max(); + unsigned long long ull_zero = 0; + + struct S { + } s; + + const char *p = nullptr; + + uint32_t mask_ff = 0xFF; + + // BREAK(TestBitwiseOperators) +} + +static void TestPointerArithmetic() { + int *p_null = nullptr; + const char *p_char1 = "hello"; + + typedef const char *my_char_ptr; + my_char_ptr my_p_char1 = p_char1; + + int offset = 5; + int array[10]; + array[0] = 0; + array[offset] = offset; + + int(&array_ref)[10] = array; + + int *p_int0 = &array[0]; + int **pp_int0 = &p_int0; + const int *cp_int0 = &array[0]; + const int *cp_int5 = &array[offset]; + const int *&rcp_int0 = cp_int0; + + typedef int *td_int_ptr_t; + td_int_ptr_t td_int_ptr0 = &array[0]; + + void *p_void = (void *)p_char1; + void **pp_void0 = &p_void; + void **pp_void1 = pp_void0 + 1; + + std::nullptr_t std_nullptr_t = nullptr; + + // BREAK(TestPointerArithmetic) + // BREAK(PointerPointerArithmeticFloat) + // BREAK(PointerPointerComparison) + // BREAK(PointerIntegerComparison) + // BREAK(TestPointerDereference) +} + +static void TestLogicalOperators() { + bool trueVar = true; + bool falseVar = false; + + const char *p_ptr = "🦊"; + const char *p_nullptr = nullptr; + + int array[2] = {1, 2}; + + struct S { + } s; + + // BREAK(TestLogicalOperators) +} + +static void TestLocalVariables() { + int a = 1; + int b = 2; + + char c = -3; + unsigned short s = 4; + + // BREAK(TestLocalVariables) +} + +static void TestMemberOf() { + 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}; + + // BREAK(TestMemberOf) +} + +static void TestMemberOfInheritance() { + struct A { + int a_; + } a{1}; + + struct B { + int b_; + } b{2}; + + struct C : A, B { + int c_; + } c{{1}, {2}, 3}; + + struct D : C { + int d_; + A fa_; + } d{{{1}, {2}, 3}, 4, {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; + + // BREAK(TestMemberOfInheritance) +} + +static void TestMemberOfAnonymousMember() { + 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; + + // BREAK(TestMemberOfAnonymousMember) +} + +static void TestIndirection() { + int val = 1; + int *p = &val; + + typedef int *myp; + myp my_p = &val; + + typedef int *&mypr; + mypr my_pr = p; + + // BREAK(TestIndirection) +} + +// Referenced by TestInstanceVariables +class C { +public: + int field_ = 1337; +}; + +// Referenced by TestAddressOf +int globalVar = 0xDEADBEEF; +extern int externGlobalVar; + +int *globalPtr = &globalVar; +int &globalRef = globalVar; + +namespace ns { +int globalVar = 13; +int *globalPtr = &globalVar; +int &globalRef = globalVar; +} // namespace ns + +void TestGlobalVariableLookup() { + // BREAK(TestGlobalVariableLookup) +} + +class TestMethods { +public: + void TestInstanceVariables() { + C c; + c.field_ = -1; + + C &c_ref = c; + C *c_ptr = &c; + + // BREAK(TestInstanceVariables) + } + + void TestAddressOf(int param) { + int x = 42; + int &r = x; + int *p = &x; + int *&pr = p; + + typedef int *&mypr; + mypr my_pr = p; + + std::string s = "hello"; + const char *s_str = s.c_str(); + + char c = 1; + + // BREAK(TestAddressOf) + } + +private: + int field_ = 1; +}; + +static void TestSubscript() { + const char *char_ptr = "lorem"; + const char char_arr[] = "ipsum"; + + int int_arr[] = {1, 2, 3}; + + C c_arr[2]; + c_arr[0].field_ = 0; + c_arr[1].field_ = 1; + + C(&c_arr_ref)[2] = c_arr; + + int idx_1 = 1; + const int &idx_1_ref = idx_1; + + typedef int td_int_t; + typedef td_int_t td_td_int_t; + typedef int *td_int_ptr_t; + typedef int &td_int_ref_t; + + td_int_t td_int_idx_1 = 1; + td_td_int_t td_td_int_idx_2 = 2; + + td_int_t td_int_arr[3] = {1, 2, 3}; + td_int_ptr_t td_int_ptr = td_int_arr; + + td_int_ref_t td_int_idx_1_ref = td_int_idx_1; + td_int_t(&td_int_arr_ref)[3] = td_int_arr; + + unsigned char uchar_idx = std::numeric_limits::max(); + uint8_t uint8_arr[256]; + uint8_arr[255] = 0xAB; + uint8_t *uint8_ptr = uint8_arr; + + enum Enum { kZero, kOne } enum_one = kOne; + Enum &enum_ref = enum_one; + + // BREAK(TestSubscript) +} + +static void TestArrayDereference() { + int arr_1d[2] = {1, 2}; + int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}}; + + // BREAK(TestArrayDereference) +} + +// Referenced by TestCStyleCast +namespace ns { + +typedef int myint; + +class Foo {}; + +namespace inner { + +using mydouble = double; + +class Foo {}; + +} // namespace inner + +} // namespace ns + +static void TestCStyleCast() { + int a = 1; + int *ap = &a; + void *vp = &a; + int arr[2] = {1, 2}; + + int na = -1; + float f = 1.1; + + typedef int myint; + + myint myint_ = 1; + ns::myint ns_myint_ = 2; + ns::Foo ns_foo_; + ns::Foo *ns_foo_ptr_ = &ns_foo_; + + ns::inner::mydouble ns_inner_mydouble_ = 1.2; + ns::inner::Foo ns_inner_foo_; + ns::inner::Foo *ns_inner_foo_ptr_ = &ns_inner_foo_; + + float finf = std::numeric_limits::infinity(); + float fnan = std::numeric_limits::quiet_NaN(); + float fsnan = std::numeric_limits::signaling_NaN(); + float fmax = std::numeric_limits::max(); + float fdenorm = std::numeric_limits::denorm_min(); + + // BREAK(TestCStyleCastBuiltins) + // BREAK(TestCStyleCastBasicType) + // BREAK(TestCStyleCastPointer) + // BREAK(TestCStyleCastNullptrType) + + struct InnerFoo { + int a; + int b; + }; + + InnerFoo ifoo; + (void)ifoo; + + int arr_1d[] = {1, 2, 3, 4}; + int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}}; + + // BREAK(TestCStyleCastArray) + // BREAK(TestCStyleCastReference) +} + +// Referenced by TestCxxCast +struct CxxVirtualBase { + int a; + virtual ~CxxVirtualBase(){}; +}; +struct CxxVirtualParent : CxxVirtualBase { + int b; +}; + +static void TestCxxCast() { + struct CxxBase { + int a; + int b; + }; + struct CxxParent : CxxBase { + long long c; + short d; + }; + + enum UEnum { kUZero, kUOne, kUTwo }; + enum class SEnum { kSZero, kSOne }; + + UEnum u_enum = kUTwo; + SEnum s_enum = SEnum::kSOne; + + typedef int td_int_t; + typedef int *td_int_ptr_t; + typedef int &td_int_ref_t; + typedef SEnum td_senum_t; + td_int_t td_int = 13; + td_int_ptr_t td_int_ptr = &td_int; + td_int_ref_t td_int_ref = td_int; + td_senum_t td_senum = s_enum; + + CxxParent parent; + parent.a = 1; + parent.b = 2; + parent.c = 3; + parent.d = 4; + + CxxBase *base = &parent; + + int arr[] = {1, 2, 3, 4, 5}; + int *ptr = arr; + + // BREAK(TestCxxStaticCast) + // BREAK(TestCxxReinterpretCast) + + CxxVirtualParent v_parent; + v_parent.a = 1; + v_parent.b = 2; + CxxVirtualBase *v_base = &v_parent; + + // BREAK(TestCxxDynamicCast) +} + +void TestCastInheritedTypes() { + struct CxxEmpty {}; + struct CxxA { + short a; + }; + struct CxxB { + long long b; + }; + struct CxxC : CxxEmpty, CxxA, CxxB { + int c; + }; + struct CxxD { + long long d; + }; + struct CxxE : CxxD, CxxC { + int e; + }; + + CxxA a{1}; + CxxB b{2}; + CxxC c; + c.a = 3; + c.b = 4; + c.c = 5; + CxxD d{6}; + CxxE e; + e.a = 7; + e.b = 8; + e.c = 9; + e.d = 10; + e.e = 11; + + struct CxxVC : virtual CxxA, virtual CxxB { + int c; + }; + struct CxxVE : CxxD, CxxVC { + int e; + }; + + CxxVC vc; + vc.a = 12; + vc.b = 13; + vc.c = 14; + CxxVE ve; + ve.a = 15; + ve.b = 16; + ve.c = 17; + ve.d = 18; + ve.e = 19; + + CxxB *e_as_b = &e; + CxxB *ve_as_b = &ve; + + // BREAK(TestCastBaseToDerived) + // BREAK(TestCastDerivedToBase) +} + +// Referenced by TestQualifiedId. +namespace ns { + +int i = 1; + +namespace ns { + +int i = 2; + +} // namespace ns + +} // namespace ns + +static void TestQualifiedId() { + // BREAK(TestQualifiedId) +} + +namespace outer { + +namespace inner { + +class Vars { +public: + inline static double inline_static = 1.5; + static constexpr int static_constexpr = 2; + static const unsigned int static_const; + + struct Nested { + static const int static_const; + }; +}; + +const unsigned int Vars::static_const = 3; +const int Vars::Nested::static_const = 10; + +using MyVars = Vars; + +} // namespace inner + +class Vars { +public: + inline static double inline_static = 4.5; + static constexpr int static_constexpr = 5; + static const unsigned int static_const; + + struct Nested { + static const int static_const; + }; +}; + +const unsigned int Vars::static_const = 6; +const int Vars::Nested::static_const = 20; + +} // namespace outer + +class Vars { +public: + inline static double inline_static = 7.5; + static constexpr int static_constexpr = 8; + static const unsigned int static_const; + + struct Nested { + static const int static_const; + }; +}; + +const unsigned int Vars::static_const = 9; +const int Vars::Nested::static_const = 30; + +static void TestStaticConst() { + Vars vars; + outer::Vars outer_vars; + outer::inner::Vars outer_inner_vars; + + using MyVars = Vars; + using MyOuterVars = outer::Vars; + + MyVars my_vars; + MyOuterVars my_outer_vars; + outer::inner::MyVars my_outer_inner_vars; + + // BREAK(TestStaticConstDeclaredInline) + // BREAK(TestStaticConstDeclaredInlineScoped) + // BREAK(TestStaticConstDeclaredOutsideTheClass) + // BREAK(TestStaticConstDeclaredOutsideTheClassScoped) +} + +// Referenced by TestTemplateTypes. +template struct T_1 { + static const int cx; + typedef double myint; + + T_1() {} + T_1(T x) : x(x) {} + T x; +}; + +template const int T_1::cx = 42; + +template <> const int T_1::cx = 24; + +template struct T_2 { + typedef float myint; + + T_2() {} + T1 x; + T2 y; +}; + +namespace ns { + +template struct T_1 { + static const int cx; + typedef int myint; + + T_1() {} + T_1(T x) : x(x) {} + T x; +}; + +template const int T_1::cx = 46; + +template <> const int T_1::cx = 64; + +} // namespace ns + +static void TestTemplateTypes() { + int i; + int *p = &i; + + { T_1 _; } + { T_1 _; } + { T_1 _; } + { T_1 _(i); } + { T_1 _(p); } + { T_1 _; } + { T_2 _; } + { T_2 _; } + { T_2, T_1> _; } + { T_2>, T_1> _; } + + { ns::T_1 _; } + { ns::T_1 _; } + { ns::T_1 _; } + { ns::T_1 _(i); } + { ns::T_1 _(p); } + { ns::T_1> _; } + { ns::T_1> _; } + { ns::T_1> _; } + { ns::T_1> _(i); } + { ns::T_1> _(p); } + + { T_1::myint _ = 0; } + { T_1::myint _ = 0; } + { T_1::myint _ = 0; } + { T_1::myint _ = 0; } + { T_1::myint _ = 0; } + { T_1>::myint _ = 0; } + { T_1>>::myint _ = 0; } + { T_1>::myint _ = 0; } + { T_1>::myint _ = 0; } + { T_1>::myint _ = 0; } + { T_1>::myint _ = 0; } + + { T_2::myint _ = 0; } + { T_2::myint _ = 0; } + { T_2::myint _ = 0; } + { T_2>, T_1>::myint _ = 0; } + + { ns::T_1::myint _ = 0; } + { ns::T_1::myint _ = 0; } + { ns::T_1::myint _ = 0; } + { ns::T_1::myint _ = 0; } + { ns::T_1::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + + (void)T_1::cx; + (void)ns::T_1::cx; + (void)ns::T_1>::cx; + + int T_1 = 2; + + // BREAK(TestTemplateTypes) + // BREAK(TestTemplateCpp11) +} + +template struct TArray { + using ElementType = T; + T t_; + TAllocator a_; +}; + +template struct Allocator { + int size = Size; +}; + +void TestTemplateWithNumericArguments() { + Allocator<4> a4; + Allocator<8> a8; + TArray> arr; + decltype(arr)::ElementType *el = 0; + + // BREAK(TestTemplateWithNumericArguments) +} + +namespace test_scope { + +class Value { +public: + Value(int x, float y) : x_(x), y_(y) {} + + // Static members + enum ValueEnum { A, B }; + static double static_var; + +private: + int x_; + float y_; +}; + +double Value::static_var = 3.5; + +} // namespace test_scope + +void TestValueScope() { + test_scope::Value var(1, 2.5f); + test_scope::Value &var_ref = var; + uint64_t z_ = 3; + + // "raw" representation of the Value. + int bytes[] = {1, 0x40200000}; + + auto val_enum = test_scope::Value::A; + (void)val_enum; + (void)test_scope::Value::static_var; + + // BREAK(TestValueScope) + // BREAK(TestReferenceScope) +} + +void TestBitField() { + enum BitFieldEnum : uint32_t { kZero, kOne }; + + struct BitFieldStruct { + uint16_t a : 10; + uint32_t b : 4; + bool c : 1; + bool d : 1; + int32_t e : 32; + uint32_t f : 32; + uint32_t g : 31; + uint64_t h : 31; + uint64_t i : 33; + BitFieldEnum j : 10; + }; + + BitFieldStruct bf; + bf.a = 0b1111111111; + bf.b = 0b1001; + bf.c = 0b0; + bf.d = 0b1; + bf.e = 0b1; + bf.f = 0b1; + bf.g = 0b1; + bf.h = 0b1; + bf.i = 0b1; + bf.j = BitFieldEnum::kOne; + + struct AlignedBitFieldStruct { + uint16_t a : 10; + uint8_t b : 4; + unsigned char : 0; + uint16_t c : 2; + }; + + uint32_t data = ~0; + AlignedBitFieldStruct abf = (AlignedBitFieldStruct &)data; + + // BREAK(TestBitField) + // BREAK(TestBitFieldScoped) + // BREAK(TestBitFieldPromotion) + // BREAK(TestBitFieldWithSideEffects) +} + +void TestContextVariables() { + struct Scope { + int a = 10; + const char *ptr = "hello"; + }; + + Scope s; + + // BREAK(TestContextVariables) + // BREAK(TestContextVariablesSubset) +} + +// Referenced by TestScopedEnum. +enum class ScopedEnum { kFoo, kBar }; +enum class ScopedEnumUInt8 : uint8_t { kFoo, kBar }; + +void TestScopedEnum() { + auto enum_foo = ScopedEnum::kFoo; + auto enum_bar = ScopedEnum::kBar; + auto enum_neg = (ScopedEnum)-1; + + auto enum_u8_foo = ScopedEnumUInt8::kFoo; + auto enum_u8_bar = ScopedEnumUInt8::kBar; + + // BREAK(TestScopedEnum) + // BREAK(TestScopedEnumArithmetic) + // BREAK(TestScopedEnumWithUnderlyingType) +} + +enum UnscopedEnum { kZero, kOne, kTwo }; +enum UnscopedEnumUInt8 : uint8_t { kZeroU8, kOneU8, kTwoU8 }; +enum UnscopedEnumInt8 : int8_t { kZero8, kOne8, kTwo8 }; +enum UnscopedEnumEmpty : uint8_t {}; + +// UnscopedEnum global_enum = UnscopedEnum::kOne; + +void TestUnscopedEnum() { + auto enum_zero = UnscopedEnum::kZero; + auto enum_one = UnscopedEnum::kOne; + auto enum_two = UnscopedEnum::kTwo; + + auto &enum_one_ref = enum_one; + auto &enum_two_ref = enum_two; + + auto enum_zero_u8 = UnscopedEnumUInt8::kZeroU8; + auto enum_one_u8 = UnscopedEnumUInt8::kOneU8; + auto enum_two_u8 = UnscopedEnumUInt8::kTwoU8; + + UnscopedEnumEmpty enum_empty{}; + + auto enum_one_8 = UnscopedEnumInt8::kOne8; + auto enum_neg_8 = (UnscopedEnumInt8)-1; + + // BREAK(TestUnscopedEnum) + // BREAK(TestUnscopedEnumNegation) + // BREAK(TestUnscopedEnumWithUnderlyingType) + // BREAK(TestUnscopedEnumEmpty) +} + +void TestTernaryOperator() { + int i = 1; + int *pi = &i; + char c = 2; + int arr2[2] = {1, 2}; + int arr3[3] = {1, 2, 3}; + double dbl_arr[2] = {1.0, 2.0}; + struct T { + } t; + enum EnumA { kOneA = 1, kTwoA } a_enum = kTwoA; + enum EnumB { kOneB = 1 } b_enum = kOneB; + // BREAK(TestTernaryOperator) +} + +void TestSizeOf() { + int i = 1; + int *p = &i; + int arr[] = {1, 2, 3}; + + struct SizeOfFoo { + int x, y; + } foo; + + // BREAK(TestSizeOf) +} + +void TestBuiltinFunction_Log2() { + struct Foo { + } foo; + + enum CEnum { kFoo = 129 } c_enum = kFoo; + enum class CxxEnum { kFoo = 129 } cxx_enum = CxxEnum::kFoo; + + CEnum &c_enum_ref = c_enum; + CxxEnum &cxx_enum_ref = cxx_enum; + + // BREAK(TestBuiltinFunction_Log2) +} + +void TestBuiltinFunction_findnonnull() { + uint8_t array_of_uint8[] = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}; + uint8_t *pointer_to_uint8 = array_of_uint8; + + int *array_of_pointers[] = {(int *)1, (int *)1, (int *)0, (int *)0, (int *)1}; + int **pointer_to_pointers = array_of_pointers; + + // BREAK(TestBuiltinFunction_findnonnull) +} + +void TestPrefixIncDec() { + auto enum_foo = ScopedEnum::kFoo; + int i = 1; + + // BREAK(TestPrefixIncDec) + // BREAK(TestPostfixIncDec) +} + +void TestDereferencedType() { + struct TTuple { + int x = 1; + }; + using TPair = TTuple; + + TPair p{}; + const TPair &p_ref = p; + const TPair *p_ptr = &p; + + // BREAK(TestDereferencedType) +} + +void TestMemberFunctionCall() { + struct C { + int m() { return 1; } + }; + + C c; + c.m(); + + // BREAK(TestMemberFunctionCall) +} + +void TestCompositeAssignment() { + int i = 10; + float f = 1.5f; + float *p = &f; + + enum Enum { ONE, TWO }; + Enum eOne = ONE; + Enum eTwo = TWO; + + // BREAK(TestAssignment) + // BREAK(TestCompositeAssignmentInvalid) + // BREAK(TestCompositeAssignmentAdd) + // BREAK(TestCompositeAssignmentSub) + // BREAK(TestCompositeAssignmentMul) + // BREAK(TestCompositeAssignmentDiv) + // BREAK(TestCompositeAssignmentRem) + // BREAK(TestCompositeAssignmentBitwise) +} + +void TestSideEffects() { + int x = 1; + int xa[] = {1, 2}; + int *p = &x; + + // BREAK(TestSideEffects) +} + +void TestUniquePtr() { + struct NodeU { + std::unique_ptr next; + int value; + }; + auto ptr_node = std::unique_ptr(new NodeU{nullptr, 2}); + ptr_node = std::unique_ptr(new NodeU{std::move(ptr_node), 1}); + + std::unique_ptr ptr_null; + auto ptr_int = std::make_unique(1); + auto ptr_float = std::make_unique(1.1f); + + auto deleter = [](void const *data) { + delete static_cast(data); + }; + std::unique_ptr ptr_void(new int(42), deleter); + + // BREAK(TestUniquePtr) + // BREAK(TestUniquePtrDeref) + // BREAK(TestUniquePtrCompare) +} + +void TestSharedPtr() { + struct NodeS { + std::shared_ptr next; + int value; + }; + auto ptr_node = std::shared_ptr(new NodeS{nullptr, 2}); + ptr_node = std::shared_ptr(new NodeS{std::move(ptr_node), 1}); + + std::shared_ptr ptr_null; + auto ptr_int = std::make_shared(1); + auto ptr_float = std::make_shared(1.1f); + + std::weak_ptr ptr_int_weak = ptr_int; + + std::shared_ptr ptr_void = ptr_int; + + // BREAK(TestSharedPtr) + // BREAK(TestSharedPtrDeref) + // BREAK(TestSharedPtrCompare) +} + +void TestTypeComparison() { + int i = 1; + int const *const icpc = &i; + int *ip = &i; + int const *const *const icpcpc = &icpc; + int **ipp = &ip; + + using MyInt = int; + using MyPtr = MyInt *; + MyInt mi = 2; + MyPtr *mipp = ipp; + + using MyConstInt = const int; + using MyConstPtr = MyConstInt *const; + MyConstPtr *const micpcpc = icpcpc; + + char c = 2; + signed char sc = 65; + const char cc = 66; + using mychar = char; + mychar mc = 67; + + // BREAK(TestTypeComparison) +} + +static void TestTypeDeclaration() { + wchar_t wchar = 0; + char16_t char16 = 0; + char32_t char32 = 0; + + using mylong = long; + mylong my_long = 1; + + // BREAK(TestBasicTypeDeclaration) + // BREAK(TestUserTypeDeclaration) +} + +static void TestTypeVsIdentifier() { + struct StructOrVar { + int x = 1; + } s; + short StructOrVar = 2; + + class ClassOrVar { + public: + int x = 3; + }; + ClassOrVar ClassOrVar; + + union UnionOrVar { + int x; + } u; + int UnionOrVar[2] = {1, 2}; + + enum EnumOrVar { kFoo, kBar }; + EnumOrVar EnumOrVar = kFoo; + + enum class CxxEnumOrVar { kCxxFoo, kCxxBar }; + CxxEnumOrVar CxxEnumOrVar = CxxEnumOrVar::kCxxFoo; + + int OnlyVar = 4; + + // BREAK(TestTypeVsIdentifier) +} + +static void TestSeparateParsing() { + struct StructA { + int a_; + } a{1}; + + struct StructB { + int b_; + } b{2}; + + struct StructC : public StructA, public StructB { + int c_; + } c{{3}, {4}, 5}; + + struct StructD : public StructC { + int d_; + } d{{{6}, {7}, 8}, 9}; + + // BREAK(TestSeparateParsing) + // BREAK(TestSeparateParsingWithContextVars) +} + +// Used by TestRegistersNoDollar +int rcx = 42; + +struct RegisterCtx { + int rbx = 42; + + void TestRegisters() { + int rax = 42; + + // BREAK(TestRegisters) + // BREAK(TestRegistersNoDollar) + } +}; + +static void TestCharParsing() { + // BREAK(TestCharParsing) +} + +static void TestStringParsing() { + // BREAK(TestStringParsing) +} + +namespace test_binary { + +void main() { + // BREAK(TestSymbols) + + TestMethods tm; + + TestArithmetic(); + TestBitwiseOperators(); + TestPointerArithmetic(); + TestLogicalOperators(); + TestLocalVariables(); + TestMemberOf(); + TestMemberOfInheritance(); + TestMemberOfAnonymousMember(); + TestGlobalVariableLookup(); + tm.TestInstanceVariables(); + TestIndirection(); + tm.TestAddressOf(42); + TestSubscript(); + TestCStyleCast(); + TestCxxCast(); + TestCastInheritedTypes(); + TestQualifiedId(); + TestStaticConst(); + TestTypeDeclaration(); + TestTemplateTypes(); + TestTemplateWithNumericArguments(); + TestValueScope(); + TestBitField(); + TestContextVariables(); + TestPrefixIncDec(); + TestScopedEnum(); + TestUnscopedEnum(); + TestTernaryOperator(); + TestSizeOf(); + TestBuiltinFunction_Log2(); + TestBuiltinFunction_findnonnull(); + TestArrayDereference(); + TestDereferencedType(); + TestMemberFunctionCall(); + TestCompositeAssignment(); + TestSideEffects(); + TestUniquePtr(); + TestSharedPtr(); + TestTypeComparison(); + TestTypeVsIdentifier(); + TestSeparateParsing(); + + RegisterCtx rc; + rc.TestRegisters(); + + TestCharParsing(); + TestStringParsing(); + + // BREAK HERE +} + +} // namespace test_binary + +int main() { test_binary::main(); } diff --git a/lldb/unittests/DIL/Inputs/test_extern.cpp b/lldb/unittests/DIL/Inputs/test_extern.cpp new file mode 100644 index 0000000000000..d97c06f51f44e --- /dev/null +++ b/lldb/unittests/DIL/Inputs/test_extern.cpp @@ -0,0 +1,9 @@ +//===-- test_extern.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +int externGlobalVar = 0x00C0FFEE; diff --git a/lldb/unittests/DIL/Runner.cpp b/lldb/unittests/DIL/Runner.cpp new file mode 100644 index 0000000000000..5524caab43ad2 --- /dev/null +++ b/lldb/unittests/DIL/Runner.cpp @@ -0,0 +1,146 @@ +//===-- Runner.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Runner.h" + +#include +#include +#include + +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBValue.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +#ifdef CONFIG_VALGRIND +// Running a process under Valgrind can be extremely slow. +const uint32_t kWaitForEventTimeout = 30; +#else +// Running a process can be slow when built with sanitizers. +const uint32_t kWaitForEventTimeout = 5; +#endif + +int FindBreakpointLine(const std::string &file_path, + const std::string &break_line) { + // Read the source file to find the breakpoint location. + std::ifstream infile(file_path); + std::string line; + int line_num = 1; + while (std::getline(infile, line)) { + if (line.find(break_line) != std::string::npos) { + return line_num; + } + ++line_num; + } + + std::cerr << "Can't find the breakpoint location." << std::endl; + exit(1); +} + +std::string filename_of_source_path(const std::string &source_path) { + auto idx = source_path.find_last_of("/\\"); + if (idx == std::string::npos) { + idx = 0; + } else { + idx++; + } + + return source_path.substr(idx); +} + +lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger, + const std::string &source_path, + const std::string &binary_path, + const std::string &break_line) { + auto target = debugger.CreateTarget(binary_path.c_str()); + + auto source_file = filename_of_source_path(source_path); + + const char *argv[] = {binary_path.c_str(), nullptr}; + + auto bp = target.BreakpointCreateByLocation( + source_file.c_str(), FindBreakpointLine(source_path.c_str(), break_line)); + // Test programs don't perform any I/O, so current directory doesn't + // matter. + if (bp.GetNumLocations() == 0) + std::cerr + << "WARNING: Unable to resolve breakpoint to any actual locations." + << std::endl; + auto process = target.LaunchSimple(argv, nullptr, "."); + if (!process.IsValid()) { + std::cerr << "ERROR: Unable to launch process. Check that the path to the " + "binary is valid." + << std::endl; + return process; + } + lldb::SBEvent event; + auto listener = debugger.GetListener(); + + while (true) { + if (!listener.WaitForEvent(kWaitForEventTimeout, event)) { + std::cerr + << "Timeout while waiting for the event, kill the process and exit." + << std::endl; + process.Destroy(); + exit(1); + } + + if (!lldb::SBProcess::EventIsProcessEvent(event)) { + std::cerr << "Got some random event: " + << lldb::SBEvent::GetCStringFromEvent(event) << std::endl; + continue; + } + + auto state = lldb::SBProcess::GetStateFromEvent(event); + if (state == lldb::eStateInvalid) { + std::cerr << "process event: " + << lldb::SBEvent::GetCStringFromEvent(event) << std::endl; + continue; + } + + if (state == lldb::eStateExited) { + std::cerr << "Process exited: " << process.GetExitStatus() << std::endl; + process.Destroy(); + exit(1); + } + + if (state != lldb::eStateStopped) { + continue; + } + + auto thread = process.GetSelectedThread(); + auto stopReason = thread.GetStopReason(); + + if (stopReason != lldb::eStopReasonBreakpoint) { + continue; + } + + auto bpId = + static_cast(thread.GetStopReasonDataAtIndex(0)); + if (bpId != bp.GetID()) { + std::cerr << "Stopped at unknown breakpoint: " << bpId << std::endl + << "Now killing process and exiting" << std::endl; + process.Destroy(); + exit(1); + } + + return process; + } +} diff --git a/lldb/unittests/DIL/Runner.h b/lldb/unittests/DIL/Runner.h new file mode 100644 index 0000000000000..f2723518e8ac1 --- /dev/null +++ b/lldb/unittests/DIL/Runner.h @@ -0,0 +1,22 @@ +//===-- Runner.h --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_DIL_RUNNER_H_ +#define LLDB_DIL_RUNNER_H_ + +#include + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBProcess.h" + +lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger, + const std::string &source_path, + const std::string &binary_path, + const std::string &break_line); + +#endif // LLDB_DIL_RUNNER_H_ \ No newline at end of file diff --git a/lldb/unittests/ValueObject/CMakeLists.txt b/lldb/unittests/ValueObject/CMakeLists.txt index 6ef0091647a59..20c10532ec14c 100644 --- a/lldb/unittests/ValueObject/CMakeLists.txt +++ b/lldb/unittests/ValueObject/CMakeLists.txt @@ -1,6 +1,5 @@ add_lldb_unittest(LLDBValueObjectTests DumpValueObjectOptionsTests.cpp - DILLexerTests.cpp DynamicValueObjectLocalBuffer.cpp LINK_LIBS From d7fdf3f675199947ab57a3e6c84172d83268804b Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Wed, 9 Apr 2025 00:05:19 +0500 Subject: [PATCH 3/6] Remove unit tests, add Python tests --- lldb/include/lldb/API/SBFrame.h | 3 - lldb/include/lldb/Target/StackFrame.h | 3 - lldb/source/API/SBFrame.cpp | 35 - lldb/source/Target/StackFrame.cpp | 11 - .../frame/var-dil/basics/AddressOf/Makefile | 3 + .../AddressOf/TestFrameVarDILAddressOf.py | 38 + .../frame/var-dil/basics/AddressOf/main.cpp | 16 + .../var-dil/basics/PointerArithmetic/Makefile | 3 + .../TestFrameVarDILPointerArithmetic.py | 43 + .../var-dil/basics/PointerArithmetic/main.cpp | 29 + lldb/unittests/CMakeLists.txt | 1 - lldb/unittests/DIL/CMakeLists.txt | 15 - lldb/unittests/DIL/DILTests.cpp | 303 ---- lldb/unittests/DIL/Inputs/CMakeLists.txt | 36 - lldb/unittests/DIL/Inputs/test_binary.cpp | 1255 ----------------- lldb/unittests/DIL/Inputs/test_extern.cpp | 9 - lldb/unittests/DIL/Runner.cpp | 146 -- lldb/unittests/DIL/Runner.h | 22 - lldb/unittests/ValueObject/CMakeLists.txt | 1 + .../{DIL => ValueObject}/DILLexerTests.cpp | 0 20 files changed, 133 insertions(+), 1839 deletions(-) create mode 100644 lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile create mode 100644 lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py create mode 100644 lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp create mode 100644 lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile create mode 100644 lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py create mode 100644 lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp delete mode 100644 lldb/unittests/DIL/CMakeLists.txt delete mode 100644 lldb/unittests/DIL/DILTests.cpp delete mode 100644 lldb/unittests/DIL/Inputs/CMakeLists.txt delete mode 100644 lldb/unittests/DIL/Inputs/test_binary.cpp delete mode 100644 lldb/unittests/DIL/Inputs/test_extern.cpp delete mode 100644 lldb/unittests/DIL/Runner.cpp delete mode 100644 lldb/unittests/DIL/Runner.h rename lldb/unittests/{DIL => ValueObject}/DILLexerTests.cpp (100%) diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index e0778623d503d..3635ee5a537ad 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -182,9 +182,6 @@ class LLDB_API SBFrame { // variable value. lldb::SBValue GetValueForVariablePath(const char *var_expr_cstr, DynamicValueType use_dynamic); - lldb::SBValue TestGetValueForVariablePath(const char *var_expr_cstr, - DynamicValueType use_dynamic, - bool use_DIL = false); /// The version that doesn't supply a 'use_dynamic' value will use the /// target's default. diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index 3673040335140..3f51c9a7f22f0 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -308,9 +308,6 @@ class StackFrame : public ExecutionContextScope, lldb::ValueObjectSP GetValueForVariableExpressionPath( llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, uint32_t options, lldb::VariableSP &var_sp, Status &error); - lldb::ValueObjectSP GetValueForVariableExpressionPath( - llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, - uint32_t options, lldb::VariableSP &var_sp, Status &error, bool use_DIL); /// Determine whether this StackFrame has debug information available or not. /// diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index c77ab4f335c68..5b69cf1ee2641 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -492,41 +492,6 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path, return sb_value; } -lldb::SBValue SBFrame::TestGetValueForVariablePath(const char *var_path, - DynamicValueType use_dynamic, - bool use_DIL) { - LLDB_INSTRUMENT_VA(this, var_path, use_dynamic); - - SBValue sb_value; - if (var_path == nullptr || var_path[0] == '\0') { - return sb_value; - } - - std::unique_lock lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - VariableSP var_sp; - Status error; - ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath( - var_path, eNoDynamicValues, - StackFrame::eExpressionPathOptionCheckPtrVsMember | - StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, - var_sp, error, use_DIL)); - sb_value.SetSP(value_sp, use_dynamic); - } - } - } - return sb_value; -} - SBValue SBFrame::FindVariable(const char *name) { LLDB_INSTRUMENT_VA(this, name); diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 05603cde27a65..97da16c3647a5 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -523,17 +523,6 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath( var_sp, error); } -ValueObjectSP StackFrame::GetValueForVariableExpressionPath( - llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options, - VariableSP &var_sp, Status &error, bool use_DIL) { - if (use_DIL) - return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options, - var_sp, error); - - return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options, - var_sp, error); -} - ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath( llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, uint32_t options, lldb::VariableSP &var_sp, Status &error) { diff --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py new file mode 100644 index 0000000000000..8eab75949047d --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py @@ -0,0 +1,38 @@ +""" +Test DIL address calculation. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILGlobalVariableLookup(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None): + value_dil = super().expect_var_path(expr, value=value, type=type) + if compare_to_framevar: + self.runCmd("settings set target.experimental.use-DIL false") + value_frv = super().expect_var_path(expr, value=value, type=type) + self.runCmd("settings set target.experimental.use-DIL true") + self.assertEqual(value_dil.GetValue(), value_frv.GetValue()) + + def test_frame_var(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + self.expect_var_path("&x", True, type="int *") + self.expect_var_path("r", True, type="int &") + self.expect_var_path("&r", True, type="int &*") + self.expect_var_path("pr", True, type="int *&") + self.expect_var_path("&pr", True, type="int *&*") + self.expect_var_path("my_pr", True) + self.expect_var_path("&my_pr", True, type="mypr *") + self.expect_var_path("&globalVar", True, type="int *") + self.expect_var_path("&s_str", True, type="const char **") + self.expect_var_path("&argc", True, type="int *") diff --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp new file mode 100644 index 0000000000000..29341e1d8e478 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp @@ -0,0 +1,16 @@ +int globalVar = 0xDEADBEEF; + +int main(int argc, char **argv) { + int x = 42; + int &r = x; + int *p = &x; + int *&pr = p; + + typedef int *&mypr; + mypr my_pr = p; + + const char *s_str = "hello"; + + char c = 1; + return 0; // Set a breakpoint here +} diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py new file mode 100644 index 0000000000000..1e90433e331f5 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py @@ -0,0 +1,43 @@ +""" +Test DIL pointer arithmetic. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILGlobalVariableLookup(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None): + value_dil = super().expect_var_path(expr, value=value, type=type) + if compare_to_framevar: + self.runCmd("settings set target.experimental.use-DIL false") + value_frv = super().expect_var_path(expr, value=value, type=type) + self.runCmd("settings set target.experimental.use-DIL true") + self.assertEqual(value_dil.GetValue(), value_frv.GetValue()) + + def test_dereference(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + is_32bit = self.process().GetAddressByteSize() == 4 + + self.runCmd("settings set target.experimental.use-DIL true") + self.expect_var_path("*p_int0", True, value="0") + self.expect_var_path("*cp_int5", True, value="5") + self.expect_var_path("*rcp_int0", True, type="const int *") + self.expect_var_path("*array", value="0") + self.expect_var_path( + "&*p_null", value="0x00000000" if is_32bit else "0x0000000000000000" + ) + self.expect_var_path("**pp_int0", value="0") + self.expect_var_path("&**pp_int0", type="int *") + self.expect( + "frame var '&*p_void'", + error=True, + substrs=["indirection not permitted on operand of type 'void *'"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp new file mode 100644 index 0000000000000..1f2ac522ef0ef --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp @@ -0,0 +1,29 @@ +int main(int argc, char **argv) { + int *p_null = nullptr; + const char *p_char1 = "hello"; + + typedef const char *my_char_ptr; + my_char_ptr my_p_char1 = p_char1; + + int offset = 5; + int array[10]; + array[0] = 0; + array[offset] = offset; + + int(&array_ref)[10] = array; + + int *p_int0 = &array[0]; + int **pp_int0 = &p_int0; + const int *cp_int0 = &array[0]; + const int *cp_int5 = &array[offset]; + const int *&rcp_int0 = cp_int0; + + typedef int *td_int_ptr_t; + td_int_ptr_t td_int_ptr0 = &array[0]; + + void *p_void = (void *)p_char1; + void **pp_void0 = &p_void; + void **pp_void1 = pp_void0 + 1; + + return 0; // Set a breakpoint here +} diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt index dc8b4c84e22f7..cc9d45ebf981d 100644 --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -54,7 +54,6 @@ endif() add_subdirectory(Breakpoint) add_subdirectory(Callback) add_subdirectory(Core) -add_subdirectory(DIL) add_subdirectory(DataFormatter) add_subdirectory(Disassembler) add_subdirectory(Editline) diff --git a/lldb/unittests/DIL/CMakeLists.txt b/lldb/unittests/DIL/CMakeLists.txt deleted file mode 100644 index 07fb7172a2b4c..0000000000000 --- a/lldb/unittests/DIL/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -add_lldb_unittest(DILTests - DILTests.cpp - DILLexerTests.cpp - Runner.cpp - - LINK_LIBS - liblldb - lldbUtilityHelpers - lldbValueObject - LLVMTestingSupport - ) -add_subdirectory(Inputs) -add_dependencies(DILTests test_binary) - -add_unittest_inputs(DILTests "test_binary.cpp") diff --git a/lldb/unittests/DIL/DILTests.cpp b/lldb/unittests/DIL/DILTests.cpp deleted file mode 100644 index c9c3620cf3162..0000000000000 --- a/lldb/unittests/DIL/DILTests.cpp +++ /dev/null @@ -1,303 +0,0 @@ -//===-- DILTests.cpp --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Runner.h" -#include "TestingSupport/TestUtilities.h" -#include "lldb/API/SBDebugger.h" -#include "lldb/API/SBError.h" -#include "lldb/API/SBFrame.h" -#include "lldb/API/SBProcess.h" -#include "lldb/API/SBTarget.h" -#include "lldb/API/SBThread.h" -#include "lldb/API/SBType.h" -#include "lldb/lldb-enumerations.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using ::testing::MakeMatcher; -using ::testing::Matcher; -using ::testing::MatcherInterface; -using ::testing::MatchResultListener; - -struct EvalResult { - lldb::SBError lldb_DIL_error; - mutable lldb::SBValue lldb_DIL_value; - mutable std::optional lldb_value; -}; - -class EvaluatorHelper { -public: - EvaluatorHelper(lldb::SBFrame frame, bool compare_with_frame_var) - : frame_(frame), compare_with_frame_var_(compare_with_frame_var) {} - -public: - EvalResult Eval(const std::string &expr) { - EvalResult ret; - ret.lldb_DIL_value = frame_.TestGetValueForVariablePath( - expr.c_str(), lldb::eNoDynamicValues, true); - if (!ret.lldb_DIL_value.GetError().Success()) - ret.lldb_DIL_error = ret.lldb_DIL_value.GetError(); - if (compare_with_frame_var_) { - ret.lldb_value = frame_.TestGetValueForVariablePath( - expr.c_str(), lldb::eNoDynamicValues, false); - } - return ret; - } - -private: - lldb::SBFrame frame_; - bool compare_with_frame_var_; -}; - -void PrintError(::testing::MatchResultListener *listener, - const std::string &error) { - *listener << "error:"; - // Print multiline errors on a separate line. - if (error.find('\n') != std::string::npos) { - *listener << "\n"; - } else { - *listener << " "; - } - *listener << error; -} - -class IsOkMatcher : public MatcherInterface { -public: - explicit IsOkMatcher(bool compare_types) : compare_types_(compare_types) {} - - bool MatchAndExplain(EvalResult result, - MatchResultListener *listener) const override { - if (result.lldb_DIL_error.GetError()) { - PrintError(listener, result.lldb_DIL_error.GetCString()); - return false; - } - - std::string actual = result.lldb_DIL_value.GetValue(); - // Compare only if we tried to evaluate with LLDB. - if (result.lldb_value.has_value()) { - if (result.lldb_value.value().GetError().GetError()) { - *listener << "values produced by DIL and 'frame var' don't match\n" - << "DIL : " << actual << "\n" - << "frame var: " - << result.lldb_value.value().GetError().GetCString(); - return false; - - } else if (actual != result.lldb_value.value().GetValue()) { - *listener << "values produced by DIL and 'frame var' don't match\n" - << "DIL : " << actual << "\n" - << "frame var: " << result.lldb_value.value().GetValue(); - return false; - } - - if (compare_types_) { - const char *lldb_DIL_type = - result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName(); - const char *lldb_type = - result.lldb_value.value().GetType().GetUnqualifiedType().GetName(); - if (strcmp(lldb_DIL_type, lldb_type) != 0) { - *listener << "types produced by DIL and 'frame var' don't match\n" - << "DIL : " << lldb_DIL_type << "\n" - << "frame var: " << lldb_type; - return false; - } - } - } - - return true; - } - - void DescribeTo(std::ostream *os) const override { - *os << "evaluates without an error and equals to LLDB"; - } - -private: - bool compare_types_; -}; - -Matcher IsOk(bool compare_types = true) { - return MakeMatcher(new IsOkMatcher(compare_types)); -} - -class IsEqualMatcher : public MatcherInterface { -public: - IsEqualMatcher(std::string value, bool compare_types) - : value_(std::move(value)), compare_types_(compare_types) {} - -public: - bool MatchAndExplain(EvalResult result, - MatchResultListener *listener) const override { - if (result.lldb_DIL_error.GetError()) { - PrintError(listener, result.lldb_DIL_error.GetCString()); - return false; - } - - std::string actual = result.lldb_DIL_value.GetValue(); - if (actual != value_) { - *listener << "evaluated to '" << actual << "'"; - return false; - } - - // Compare only if we tried to evaluate with LLDB. - if (result.lldb_value.has_value()) { - if (result.lldb_value.value().GetError().GetError()) { - *listener << "values produced by DIL and 'frame var' don't match\n" - << "DIL : " << actual << "\n" - << "frame var: " - << result.lldb_value.value().GetError().GetCString(); - return false; - - } else if (actual != result.lldb_value.value().GetValue()) { - *listener << "values produced by DIL and 'frame var' don't match\n" - << "DIL : " << actual << "\n" - << "frame var: " << result.lldb_value.value().GetValue(); - return false; - } - - if (compare_types_) { - const char *lldb_DIL_type = - result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName(); - const char *lldb_type = - result.lldb_value.value().GetType().GetUnqualifiedType().GetName(); - if (strcmp(lldb_DIL_type, lldb_type) != 0) { - *listener << "types produced by DIL and 'frame var' don't match\n" - << "DIL : " << lldb_DIL_type << "\n" - << "frame var: " << lldb_type; - return false; - } - } - } - return true; - } - - void DescribeTo(std::ostream *os) const override { - *os << "evaluates to '" << value_ << "'"; - } - -private: - std::string value_; - bool compare_types_; -}; - -Matcher IsEqual(std::string value, bool compare_types = true) { - return MakeMatcher(new IsEqualMatcher(std::move(value), compare_types)); -} - -class IsErrorMatcher : public MatcherInterface { -public: - explicit IsErrorMatcher(std::string value) : value_(std::move(value)) {} - -public: - bool MatchAndExplain(EvalResult result, - MatchResultListener *listener) const override { - if (!result.lldb_DIL_error.GetError()) { - *listener << "evaluated to '" << result.lldb_DIL_value.GetValue() << "'"; - return false; - } - std::string message = result.lldb_DIL_error.GetCString(); - if (message.find(value_) == std::string::npos) { - PrintError(listener, message); - return false; - } - - return true; - } - - void DescribeTo(std::ostream *os) const override { - *os << "evaluates with an error: '" << value_ << "'"; - } - -private: - std::string value_; -}; - -Matcher IsError(std::string value) { - return MakeMatcher(new IsErrorMatcher(std::move(value))); -} - -class EvalTest : public ::testing::Test { -protected: - static void SetUpTestSuite() { lldb::SBDebugger::Initialize(); } - - static void TearDownTestSuite() { lldb::SBDebugger::Terminate(); } - - void SetUp() override { - std::string test_name = - ::testing::UnitTest::GetInstance()->current_test_info()->name(); - std::string break_line = "// BREAK(" + test_name + ")"; - - std::string binary_path = - lldb_private::GetInputFilePath("test_binary.bin"); - std::string source_path = lldb_private::GetInputFilePath("test_binary.cpp"); - - debugger_ = lldb::SBDebugger::Create(false); - process_ = - LaunchTestProgram(debugger_, source_path, binary_path, break_line); - frame_ = process_.GetSelectedThread().GetSelectedFrame(); - } - - void TearDown() override { - process_.Destroy(); - lldb::SBDebugger::Destroy(debugger_); - } - - EvalResult Eval(const std::string &expr) { - return EvaluatorHelper(frame_, compare_with_frame_var_).Eval(expr); - } - - bool Is32Bit() const { - if (process_.GetAddressByteSize() == 4) { - return true; - } - return false; - } - -protected: - lldb::SBDebugger debugger_; - lldb::SBProcess process_; - lldb::SBFrame frame_; - - // Evaluate with both DIL and LLDB by default. - bool compare_with_frame_var_ = true; -}; - -TEST_F(EvalTest, TestSymbols) { - EXPECT_GT(frame_.GetModule().GetNumSymbols(), (size_t)0) - << "No symbols might indicate that the test binary was built incorrectly"; -} - -TEST_F(EvalTest, TestPointerDereference) { - EXPECT_THAT(Eval("*p_int0"), IsEqual("0")); - EXPECT_THAT(Eval("*cp_int5"), IsEqual("5")); - EXPECT_THAT(Eval("*rcp_int0"), IsOk()); - - EXPECT_THAT(Eval("&*p_void"), - IsError("indirection not permitted on operand of type" - " 'void *'")); - - this->compare_with_frame_var_ = false; - EXPECT_THAT(Eval("*array"), IsEqual("0")); - EXPECT_THAT(Eval("&*p_null"), - IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); - EXPECT_THAT(Eval("**pp_int0"), IsEqual("0")); - EXPECT_THAT(Eval("&**pp_int0"), IsOk()); -} - -TEST_F(EvalTest, TestAddressOf) { - EXPECT_THAT(Eval("&x"), IsOk()); - EXPECT_THAT(Eval("r"), IsOk()); - EXPECT_THAT(Eval("&r"), IsOk()); - EXPECT_THAT(Eval("pr"), IsOk()); - EXPECT_THAT(Eval("&pr"), IsOk()); - EXPECT_THAT(Eval("my_pr"), IsOk()); - EXPECT_THAT(Eval("&my_pr"), IsOk()); - - EXPECT_THAT(Eval("&globalVar"), IsOk()); - EXPECT_THAT(Eval("&s_str"), IsOk()); - EXPECT_THAT(Eval("¶m"), IsOk()); -} diff --git a/lldb/unittests/DIL/Inputs/CMakeLists.txt b/lldb/unittests/DIL/Inputs/CMakeLists.txt deleted file mode 100644 index 73e179d08d059..0000000000000 --- a/lldb/unittests/DIL/Inputs/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -# Build `test_binary.cc` and put the binary in the Inputs folder, -# allowing `lldb_private::GetInputFilePath` to find it. -# Projects that must be enabled: clang;lldb;lld -# Runtimes that must be enabled: libcxx;libcxxabi;libunwind -if ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES) - if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE) - set(LIBCXX_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE}) - set(LIBCXX_GENERATED_INCLUDE_DIR "${LLVM_BINARY_DIR}/include/c++/v1") - set(LIBCXX_GENERATED_INCLUDE_TARGET_DIR "${LLVM_BINARY_DIR}/include/${LLVM_DEFAULT_TARGET_TRIPLE}/c++/v1") - else() - set(LIBCXX_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBCXX_LIBDIR_SUFFIX}) - set(LIBCXX_GENERATED_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include/c++/v1") - endif() - - if(DEFINED LIBCXX_GENERATED_INCLUDE_TARGET_DIR) - set(INCLUDE_TARGET_DIR_OPTION "-cxx-isystem" "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}") - endif() - - get_target_property(EXE_PATH DILTests RUNTIME_OUTPUT_DIRECTORY) - add_custom_command( - OUTPUT test_binary.bin - COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/test_binary.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/test_extern.cpp - -O0 -g -std=c++17 -fuse-ld=lld -B$ - -nostdlib++ -nostdinc++ -cxx-isystem ${LIBCXX_GENERATED_INCLUDE_DIR} - ${INCLUDE_TARGET_DIR_OPTION} - -L${LIBCXX_LIBRARY_DIR} -Wl,-rpath,${LIBCXX_LIBRARY_DIR} -lc++ - -o ${EXE_PATH}/Inputs/test_binary.bin - DEPENDS test_binary.cpp test_extern.cpp clang lld - ) - add_custom_target(test_binary - DEPENDS test_binary.bin - ) -else() - message(FATAL_ERROR "libcxx runtime must be enabled.") -endif() diff --git a/lldb/unittests/DIL/Inputs/test_binary.cpp b/lldb/unittests/DIL/Inputs/test_binary.cpp deleted file mode 100644 index 115add3129bcd..0000000000000 --- a/lldb/unittests/DIL/Inputs/test_binary.cpp +++ /dev/null @@ -1,1255 +0,0 @@ -//===-- test_binary.cpp --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include -#include -#include -#include - -static void TestArithmetic() { - char c = 10; - unsigned char uc = 1; - int a = 1; - int int_max = std::numeric_limits::max(); - int int_min = std::numeric_limits::min(); - unsigned int uint_max = std::numeric_limits::max(); - unsigned int uint_zero = 0; - long long ll_max = std::numeric_limits::max(); - long long ll_min = std::numeric_limits::min(); - unsigned long long ull_max = std::numeric_limits::max(); - unsigned long long ull_zero = 0; - - int x = 2; - int &r = x; - int *p = &x; - - typedef int &myr; - myr my_r = x; - - auto fnan = std::numeric_limits::quiet_NaN(); - auto fsnan = std::numeric_limits::signaling_NaN(); - // Smallest positive non-zero float denormal - auto fdenorm = 0x0.1p-145f; - - // BREAK(TestArithmetic) - // BREAK(TestZeroDivision) -} - -static void TestBitwiseOperators() { - bool var_true = true; - bool var_false = false; - - unsigned long long ull_max = std::numeric_limits::max(); - unsigned long long ull_zero = 0; - - struct S { - } s; - - const char *p = nullptr; - - uint32_t mask_ff = 0xFF; - - // BREAK(TestBitwiseOperators) -} - -static void TestPointerArithmetic() { - int *p_null = nullptr; - const char *p_char1 = "hello"; - - typedef const char *my_char_ptr; - my_char_ptr my_p_char1 = p_char1; - - int offset = 5; - int array[10]; - array[0] = 0; - array[offset] = offset; - - int(&array_ref)[10] = array; - - int *p_int0 = &array[0]; - int **pp_int0 = &p_int0; - const int *cp_int0 = &array[0]; - const int *cp_int5 = &array[offset]; - const int *&rcp_int0 = cp_int0; - - typedef int *td_int_ptr_t; - td_int_ptr_t td_int_ptr0 = &array[0]; - - void *p_void = (void *)p_char1; - void **pp_void0 = &p_void; - void **pp_void1 = pp_void0 + 1; - - std::nullptr_t std_nullptr_t = nullptr; - - // BREAK(TestPointerArithmetic) - // BREAK(PointerPointerArithmeticFloat) - // BREAK(PointerPointerComparison) - // BREAK(PointerIntegerComparison) - // BREAK(TestPointerDereference) -} - -static void TestLogicalOperators() { - bool trueVar = true; - bool falseVar = false; - - const char *p_ptr = "🦊"; - const char *p_nullptr = nullptr; - - int array[2] = {1, 2}; - - struct S { - } s; - - // BREAK(TestLogicalOperators) -} - -static void TestLocalVariables() { - int a = 1; - int b = 2; - - char c = -3; - unsigned short s = 4; - - // BREAK(TestLocalVariables) -} - -static void TestMemberOf() { - 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}; - - // BREAK(TestMemberOf) -} - -static void TestMemberOfInheritance() { - struct A { - int a_; - } a{1}; - - struct B { - int b_; - } b{2}; - - struct C : A, B { - int c_; - } c{{1}, {2}, 3}; - - struct D : C { - int d_; - A fa_; - } d{{{1}, {2}, 3}, 4, {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; - - // BREAK(TestMemberOfInheritance) -} - -static void TestMemberOfAnonymousMember() { - 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; - - // BREAK(TestMemberOfAnonymousMember) -} - -static void TestIndirection() { - int val = 1; - int *p = &val; - - typedef int *myp; - myp my_p = &val; - - typedef int *&mypr; - mypr my_pr = p; - - // BREAK(TestIndirection) -} - -// Referenced by TestInstanceVariables -class C { -public: - int field_ = 1337; -}; - -// Referenced by TestAddressOf -int globalVar = 0xDEADBEEF; -extern int externGlobalVar; - -int *globalPtr = &globalVar; -int &globalRef = globalVar; - -namespace ns { -int globalVar = 13; -int *globalPtr = &globalVar; -int &globalRef = globalVar; -} // namespace ns - -void TestGlobalVariableLookup() { - // BREAK(TestGlobalVariableLookup) -} - -class TestMethods { -public: - void TestInstanceVariables() { - C c; - c.field_ = -1; - - C &c_ref = c; - C *c_ptr = &c; - - // BREAK(TestInstanceVariables) - } - - void TestAddressOf(int param) { - int x = 42; - int &r = x; - int *p = &x; - int *&pr = p; - - typedef int *&mypr; - mypr my_pr = p; - - std::string s = "hello"; - const char *s_str = s.c_str(); - - char c = 1; - - // BREAK(TestAddressOf) - } - -private: - int field_ = 1; -}; - -static void TestSubscript() { - const char *char_ptr = "lorem"; - const char char_arr[] = "ipsum"; - - int int_arr[] = {1, 2, 3}; - - C c_arr[2]; - c_arr[0].field_ = 0; - c_arr[1].field_ = 1; - - C(&c_arr_ref)[2] = c_arr; - - int idx_1 = 1; - const int &idx_1_ref = idx_1; - - typedef int td_int_t; - typedef td_int_t td_td_int_t; - typedef int *td_int_ptr_t; - typedef int &td_int_ref_t; - - td_int_t td_int_idx_1 = 1; - td_td_int_t td_td_int_idx_2 = 2; - - td_int_t td_int_arr[3] = {1, 2, 3}; - td_int_ptr_t td_int_ptr = td_int_arr; - - td_int_ref_t td_int_idx_1_ref = td_int_idx_1; - td_int_t(&td_int_arr_ref)[3] = td_int_arr; - - unsigned char uchar_idx = std::numeric_limits::max(); - uint8_t uint8_arr[256]; - uint8_arr[255] = 0xAB; - uint8_t *uint8_ptr = uint8_arr; - - enum Enum { kZero, kOne } enum_one = kOne; - Enum &enum_ref = enum_one; - - // BREAK(TestSubscript) -} - -static void TestArrayDereference() { - int arr_1d[2] = {1, 2}; - int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}}; - - // BREAK(TestArrayDereference) -} - -// Referenced by TestCStyleCast -namespace ns { - -typedef int myint; - -class Foo {}; - -namespace inner { - -using mydouble = double; - -class Foo {}; - -} // namespace inner - -} // namespace ns - -static void TestCStyleCast() { - int a = 1; - int *ap = &a; - void *vp = &a; - int arr[2] = {1, 2}; - - int na = -1; - float f = 1.1; - - typedef int myint; - - myint myint_ = 1; - ns::myint ns_myint_ = 2; - ns::Foo ns_foo_; - ns::Foo *ns_foo_ptr_ = &ns_foo_; - - ns::inner::mydouble ns_inner_mydouble_ = 1.2; - ns::inner::Foo ns_inner_foo_; - ns::inner::Foo *ns_inner_foo_ptr_ = &ns_inner_foo_; - - float finf = std::numeric_limits::infinity(); - float fnan = std::numeric_limits::quiet_NaN(); - float fsnan = std::numeric_limits::signaling_NaN(); - float fmax = std::numeric_limits::max(); - float fdenorm = std::numeric_limits::denorm_min(); - - // BREAK(TestCStyleCastBuiltins) - // BREAK(TestCStyleCastBasicType) - // BREAK(TestCStyleCastPointer) - // BREAK(TestCStyleCastNullptrType) - - struct InnerFoo { - int a; - int b; - }; - - InnerFoo ifoo; - (void)ifoo; - - int arr_1d[] = {1, 2, 3, 4}; - int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}}; - - // BREAK(TestCStyleCastArray) - // BREAK(TestCStyleCastReference) -} - -// Referenced by TestCxxCast -struct CxxVirtualBase { - int a; - virtual ~CxxVirtualBase(){}; -}; -struct CxxVirtualParent : CxxVirtualBase { - int b; -}; - -static void TestCxxCast() { - struct CxxBase { - int a; - int b; - }; - struct CxxParent : CxxBase { - long long c; - short d; - }; - - enum UEnum { kUZero, kUOne, kUTwo }; - enum class SEnum { kSZero, kSOne }; - - UEnum u_enum = kUTwo; - SEnum s_enum = SEnum::kSOne; - - typedef int td_int_t; - typedef int *td_int_ptr_t; - typedef int &td_int_ref_t; - typedef SEnum td_senum_t; - td_int_t td_int = 13; - td_int_ptr_t td_int_ptr = &td_int; - td_int_ref_t td_int_ref = td_int; - td_senum_t td_senum = s_enum; - - CxxParent parent; - parent.a = 1; - parent.b = 2; - parent.c = 3; - parent.d = 4; - - CxxBase *base = &parent; - - int arr[] = {1, 2, 3, 4, 5}; - int *ptr = arr; - - // BREAK(TestCxxStaticCast) - // BREAK(TestCxxReinterpretCast) - - CxxVirtualParent v_parent; - v_parent.a = 1; - v_parent.b = 2; - CxxVirtualBase *v_base = &v_parent; - - // BREAK(TestCxxDynamicCast) -} - -void TestCastInheritedTypes() { - struct CxxEmpty {}; - struct CxxA { - short a; - }; - struct CxxB { - long long b; - }; - struct CxxC : CxxEmpty, CxxA, CxxB { - int c; - }; - struct CxxD { - long long d; - }; - struct CxxE : CxxD, CxxC { - int e; - }; - - CxxA a{1}; - CxxB b{2}; - CxxC c; - c.a = 3; - c.b = 4; - c.c = 5; - CxxD d{6}; - CxxE e; - e.a = 7; - e.b = 8; - e.c = 9; - e.d = 10; - e.e = 11; - - struct CxxVC : virtual CxxA, virtual CxxB { - int c; - }; - struct CxxVE : CxxD, CxxVC { - int e; - }; - - CxxVC vc; - vc.a = 12; - vc.b = 13; - vc.c = 14; - CxxVE ve; - ve.a = 15; - ve.b = 16; - ve.c = 17; - ve.d = 18; - ve.e = 19; - - CxxB *e_as_b = &e; - CxxB *ve_as_b = &ve; - - // BREAK(TestCastBaseToDerived) - // BREAK(TestCastDerivedToBase) -} - -// Referenced by TestQualifiedId. -namespace ns { - -int i = 1; - -namespace ns { - -int i = 2; - -} // namespace ns - -} // namespace ns - -static void TestQualifiedId() { - // BREAK(TestQualifiedId) -} - -namespace outer { - -namespace inner { - -class Vars { -public: - inline static double inline_static = 1.5; - static constexpr int static_constexpr = 2; - static const unsigned int static_const; - - struct Nested { - static const int static_const; - }; -}; - -const unsigned int Vars::static_const = 3; -const int Vars::Nested::static_const = 10; - -using MyVars = Vars; - -} // namespace inner - -class Vars { -public: - inline static double inline_static = 4.5; - static constexpr int static_constexpr = 5; - static const unsigned int static_const; - - struct Nested { - static const int static_const; - }; -}; - -const unsigned int Vars::static_const = 6; -const int Vars::Nested::static_const = 20; - -} // namespace outer - -class Vars { -public: - inline static double inline_static = 7.5; - static constexpr int static_constexpr = 8; - static const unsigned int static_const; - - struct Nested { - static const int static_const; - }; -}; - -const unsigned int Vars::static_const = 9; -const int Vars::Nested::static_const = 30; - -static void TestStaticConst() { - Vars vars; - outer::Vars outer_vars; - outer::inner::Vars outer_inner_vars; - - using MyVars = Vars; - using MyOuterVars = outer::Vars; - - MyVars my_vars; - MyOuterVars my_outer_vars; - outer::inner::MyVars my_outer_inner_vars; - - // BREAK(TestStaticConstDeclaredInline) - // BREAK(TestStaticConstDeclaredInlineScoped) - // BREAK(TestStaticConstDeclaredOutsideTheClass) - // BREAK(TestStaticConstDeclaredOutsideTheClassScoped) -} - -// Referenced by TestTemplateTypes. -template struct T_1 { - static const int cx; - typedef double myint; - - T_1() {} - T_1(T x) : x(x) {} - T x; -}; - -template const int T_1::cx = 42; - -template <> const int T_1::cx = 24; - -template struct T_2 { - typedef float myint; - - T_2() {} - T1 x; - T2 y; -}; - -namespace ns { - -template struct T_1 { - static const int cx; - typedef int myint; - - T_1() {} - T_1(T x) : x(x) {} - T x; -}; - -template const int T_1::cx = 46; - -template <> const int T_1::cx = 64; - -} // namespace ns - -static void TestTemplateTypes() { - int i; - int *p = &i; - - { T_1 _; } - { T_1 _; } - { T_1 _; } - { T_1 _(i); } - { T_1 _(p); } - { T_1 _; } - { T_2 _; } - { T_2 _; } - { T_2, T_1> _; } - { T_2>, T_1> _; } - - { ns::T_1 _; } - { ns::T_1 _; } - { ns::T_1 _; } - { ns::T_1 _(i); } - { ns::T_1 _(p); } - { ns::T_1> _; } - { ns::T_1> _; } - { ns::T_1> _; } - { ns::T_1> _(i); } - { ns::T_1> _(p); } - - { T_1::myint _ = 0; } - { T_1::myint _ = 0; } - { T_1::myint _ = 0; } - { T_1::myint _ = 0; } - { T_1::myint _ = 0; } - { T_1>::myint _ = 0; } - { T_1>>::myint _ = 0; } - { T_1>::myint _ = 0; } - { T_1>::myint _ = 0; } - { T_1>::myint _ = 0; } - { T_1>::myint _ = 0; } - - { T_2::myint _ = 0; } - { T_2::myint _ = 0; } - { T_2::myint _ = 0; } - { T_2>, T_1>::myint _ = 0; } - - { ns::T_1::myint _ = 0; } - { ns::T_1::myint _ = 0; } - { ns::T_1::myint _ = 0; } - { ns::T_1::myint _ = 0; } - { ns::T_1::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - { ns::T_1>::myint _ = 0; } - - (void)T_1::cx; - (void)ns::T_1::cx; - (void)ns::T_1>::cx; - - int T_1 = 2; - - // BREAK(TestTemplateTypes) - // BREAK(TestTemplateCpp11) -} - -template struct TArray { - using ElementType = T; - T t_; - TAllocator a_; -}; - -template struct Allocator { - int size = Size; -}; - -void TestTemplateWithNumericArguments() { - Allocator<4> a4; - Allocator<8> a8; - TArray> arr; - decltype(arr)::ElementType *el = 0; - - // BREAK(TestTemplateWithNumericArguments) -} - -namespace test_scope { - -class Value { -public: - Value(int x, float y) : x_(x), y_(y) {} - - // Static members - enum ValueEnum { A, B }; - static double static_var; - -private: - int x_; - float y_; -}; - -double Value::static_var = 3.5; - -} // namespace test_scope - -void TestValueScope() { - test_scope::Value var(1, 2.5f); - test_scope::Value &var_ref = var; - uint64_t z_ = 3; - - // "raw" representation of the Value. - int bytes[] = {1, 0x40200000}; - - auto val_enum = test_scope::Value::A; - (void)val_enum; - (void)test_scope::Value::static_var; - - // BREAK(TestValueScope) - // BREAK(TestReferenceScope) -} - -void TestBitField() { - enum BitFieldEnum : uint32_t { kZero, kOne }; - - struct BitFieldStruct { - uint16_t a : 10; - uint32_t b : 4; - bool c : 1; - bool d : 1; - int32_t e : 32; - uint32_t f : 32; - uint32_t g : 31; - uint64_t h : 31; - uint64_t i : 33; - BitFieldEnum j : 10; - }; - - BitFieldStruct bf; - bf.a = 0b1111111111; - bf.b = 0b1001; - bf.c = 0b0; - bf.d = 0b1; - bf.e = 0b1; - bf.f = 0b1; - bf.g = 0b1; - bf.h = 0b1; - bf.i = 0b1; - bf.j = BitFieldEnum::kOne; - - struct AlignedBitFieldStruct { - uint16_t a : 10; - uint8_t b : 4; - unsigned char : 0; - uint16_t c : 2; - }; - - uint32_t data = ~0; - AlignedBitFieldStruct abf = (AlignedBitFieldStruct &)data; - - // BREAK(TestBitField) - // BREAK(TestBitFieldScoped) - // BREAK(TestBitFieldPromotion) - // BREAK(TestBitFieldWithSideEffects) -} - -void TestContextVariables() { - struct Scope { - int a = 10; - const char *ptr = "hello"; - }; - - Scope s; - - // BREAK(TestContextVariables) - // BREAK(TestContextVariablesSubset) -} - -// Referenced by TestScopedEnum. -enum class ScopedEnum { kFoo, kBar }; -enum class ScopedEnumUInt8 : uint8_t { kFoo, kBar }; - -void TestScopedEnum() { - auto enum_foo = ScopedEnum::kFoo; - auto enum_bar = ScopedEnum::kBar; - auto enum_neg = (ScopedEnum)-1; - - auto enum_u8_foo = ScopedEnumUInt8::kFoo; - auto enum_u8_bar = ScopedEnumUInt8::kBar; - - // BREAK(TestScopedEnum) - // BREAK(TestScopedEnumArithmetic) - // BREAK(TestScopedEnumWithUnderlyingType) -} - -enum UnscopedEnum { kZero, kOne, kTwo }; -enum UnscopedEnumUInt8 : uint8_t { kZeroU8, kOneU8, kTwoU8 }; -enum UnscopedEnumInt8 : int8_t { kZero8, kOne8, kTwo8 }; -enum UnscopedEnumEmpty : uint8_t {}; - -// UnscopedEnum global_enum = UnscopedEnum::kOne; - -void TestUnscopedEnum() { - auto enum_zero = UnscopedEnum::kZero; - auto enum_one = UnscopedEnum::kOne; - auto enum_two = UnscopedEnum::kTwo; - - auto &enum_one_ref = enum_one; - auto &enum_two_ref = enum_two; - - auto enum_zero_u8 = UnscopedEnumUInt8::kZeroU8; - auto enum_one_u8 = UnscopedEnumUInt8::kOneU8; - auto enum_two_u8 = UnscopedEnumUInt8::kTwoU8; - - UnscopedEnumEmpty enum_empty{}; - - auto enum_one_8 = UnscopedEnumInt8::kOne8; - auto enum_neg_8 = (UnscopedEnumInt8)-1; - - // BREAK(TestUnscopedEnum) - // BREAK(TestUnscopedEnumNegation) - // BREAK(TestUnscopedEnumWithUnderlyingType) - // BREAK(TestUnscopedEnumEmpty) -} - -void TestTernaryOperator() { - int i = 1; - int *pi = &i; - char c = 2; - int arr2[2] = {1, 2}; - int arr3[3] = {1, 2, 3}; - double dbl_arr[2] = {1.0, 2.0}; - struct T { - } t; - enum EnumA { kOneA = 1, kTwoA } a_enum = kTwoA; - enum EnumB { kOneB = 1 } b_enum = kOneB; - // BREAK(TestTernaryOperator) -} - -void TestSizeOf() { - int i = 1; - int *p = &i; - int arr[] = {1, 2, 3}; - - struct SizeOfFoo { - int x, y; - } foo; - - // BREAK(TestSizeOf) -} - -void TestBuiltinFunction_Log2() { - struct Foo { - } foo; - - enum CEnum { kFoo = 129 } c_enum = kFoo; - enum class CxxEnum { kFoo = 129 } cxx_enum = CxxEnum::kFoo; - - CEnum &c_enum_ref = c_enum; - CxxEnum &cxx_enum_ref = cxx_enum; - - // BREAK(TestBuiltinFunction_Log2) -} - -void TestBuiltinFunction_findnonnull() { - uint8_t array_of_uint8[] = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}; - uint8_t *pointer_to_uint8 = array_of_uint8; - - int *array_of_pointers[] = {(int *)1, (int *)1, (int *)0, (int *)0, (int *)1}; - int **pointer_to_pointers = array_of_pointers; - - // BREAK(TestBuiltinFunction_findnonnull) -} - -void TestPrefixIncDec() { - auto enum_foo = ScopedEnum::kFoo; - int i = 1; - - // BREAK(TestPrefixIncDec) - // BREAK(TestPostfixIncDec) -} - -void TestDereferencedType() { - struct TTuple { - int x = 1; - }; - using TPair = TTuple; - - TPair p{}; - const TPair &p_ref = p; - const TPair *p_ptr = &p; - - // BREAK(TestDereferencedType) -} - -void TestMemberFunctionCall() { - struct C { - int m() { return 1; } - }; - - C c; - c.m(); - - // BREAK(TestMemberFunctionCall) -} - -void TestCompositeAssignment() { - int i = 10; - float f = 1.5f; - float *p = &f; - - enum Enum { ONE, TWO }; - Enum eOne = ONE; - Enum eTwo = TWO; - - // BREAK(TestAssignment) - // BREAK(TestCompositeAssignmentInvalid) - // BREAK(TestCompositeAssignmentAdd) - // BREAK(TestCompositeAssignmentSub) - // BREAK(TestCompositeAssignmentMul) - // BREAK(TestCompositeAssignmentDiv) - // BREAK(TestCompositeAssignmentRem) - // BREAK(TestCompositeAssignmentBitwise) -} - -void TestSideEffects() { - int x = 1; - int xa[] = {1, 2}; - int *p = &x; - - // BREAK(TestSideEffects) -} - -void TestUniquePtr() { - struct NodeU { - std::unique_ptr next; - int value; - }; - auto ptr_node = std::unique_ptr(new NodeU{nullptr, 2}); - ptr_node = std::unique_ptr(new NodeU{std::move(ptr_node), 1}); - - std::unique_ptr ptr_null; - auto ptr_int = std::make_unique(1); - auto ptr_float = std::make_unique(1.1f); - - auto deleter = [](void const *data) { - delete static_cast(data); - }; - std::unique_ptr ptr_void(new int(42), deleter); - - // BREAK(TestUniquePtr) - // BREAK(TestUniquePtrDeref) - // BREAK(TestUniquePtrCompare) -} - -void TestSharedPtr() { - struct NodeS { - std::shared_ptr next; - int value; - }; - auto ptr_node = std::shared_ptr(new NodeS{nullptr, 2}); - ptr_node = std::shared_ptr(new NodeS{std::move(ptr_node), 1}); - - std::shared_ptr ptr_null; - auto ptr_int = std::make_shared(1); - auto ptr_float = std::make_shared(1.1f); - - std::weak_ptr ptr_int_weak = ptr_int; - - std::shared_ptr ptr_void = ptr_int; - - // BREAK(TestSharedPtr) - // BREAK(TestSharedPtrDeref) - // BREAK(TestSharedPtrCompare) -} - -void TestTypeComparison() { - int i = 1; - int const *const icpc = &i; - int *ip = &i; - int const *const *const icpcpc = &icpc; - int **ipp = &ip; - - using MyInt = int; - using MyPtr = MyInt *; - MyInt mi = 2; - MyPtr *mipp = ipp; - - using MyConstInt = const int; - using MyConstPtr = MyConstInt *const; - MyConstPtr *const micpcpc = icpcpc; - - char c = 2; - signed char sc = 65; - const char cc = 66; - using mychar = char; - mychar mc = 67; - - // BREAK(TestTypeComparison) -} - -static void TestTypeDeclaration() { - wchar_t wchar = 0; - char16_t char16 = 0; - char32_t char32 = 0; - - using mylong = long; - mylong my_long = 1; - - // BREAK(TestBasicTypeDeclaration) - // BREAK(TestUserTypeDeclaration) -} - -static void TestTypeVsIdentifier() { - struct StructOrVar { - int x = 1; - } s; - short StructOrVar = 2; - - class ClassOrVar { - public: - int x = 3; - }; - ClassOrVar ClassOrVar; - - union UnionOrVar { - int x; - } u; - int UnionOrVar[2] = {1, 2}; - - enum EnumOrVar { kFoo, kBar }; - EnumOrVar EnumOrVar = kFoo; - - enum class CxxEnumOrVar { kCxxFoo, kCxxBar }; - CxxEnumOrVar CxxEnumOrVar = CxxEnumOrVar::kCxxFoo; - - int OnlyVar = 4; - - // BREAK(TestTypeVsIdentifier) -} - -static void TestSeparateParsing() { - struct StructA { - int a_; - } a{1}; - - struct StructB { - int b_; - } b{2}; - - struct StructC : public StructA, public StructB { - int c_; - } c{{3}, {4}, 5}; - - struct StructD : public StructC { - int d_; - } d{{{6}, {7}, 8}, 9}; - - // BREAK(TestSeparateParsing) - // BREAK(TestSeparateParsingWithContextVars) -} - -// Used by TestRegistersNoDollar -int rcx = 42; - -struct RegisterCtx { - int rbx = 42; - - void TestRegisters() { - int rax = 42; - - // BREAK(TestRegisters) - // BREAK(TestRegistersNoDollar) - } -}; - -static void TestCharParsing() { - // BREAK(TestCharParsing) -} - -static void TestStringParsing() { - // BREAK(TestStringParsing) -} - -namespace test_binary { - -void main() { - // BREAK(TestSymbols) - - TestMethods tm; - - TestArithmetic(); - TestBitwiseOperators(); - TestPointerArithmetic(); - TestLogicalOperators(); - TestLocalVariables(); - TestMemberOf(); - TestMemberOfInheritance(); - TestMemberOfAnonymousMember(); - TestGlobalVariableLookup(); - tm.TestInstanceVariables(); - TestIndirection(); - tm.TestAddressOf(42); - TestSubscript(); - TestCStyleCast(); - TestCxxCast(); - TestCastInheritedTypes(); - TestQualifiedId(); - TestStaticConst(); - TestTypeDeclaration(); - TestTemplateTypes(); - TestTemplateWithNumericArguments(); - TestValueScope(); - TestBitField(); - TestContextVariables(); - TestPrefixIncDec(); - TestScopedEnum(); - TestUnscopedEnum(); - TestTernaryOperator(); - TestSizeOf(); - TestBuiltinFunction_Log2(); - TestBuiltinFunction_findnonnull(); - TestArrayDereference(); - TestDereferencedType(); - TestMemberFunctionCall(); - TestCompositeAssignment(); - TestSideEffects(); - TestUniquePtr(); - TestSharedPtr(); - TestTypeComparison(); - TestTypeVsIdentifier(); - TestSeparateParsing(); - - RegisterCtx rc; - rc.TestRegisters(); - - TestCharParsing(); - TestStringParsing(); - - // BREAK HERE -} - -} // namespace test_binary - -int main() { test_binary::main(); } diff --git a/lldb/unittests/DIL/Inputs/test_extern.cpp b/lldb/unittests/DIL/Inputs/test_extern.cpp deleted file mode 100644 index d97c06f51f44e..0000000000000 --- a/lldb/unittests/DIL/Inputs/test_extern.cpp +++ /dev/null @@ -1,9 +0,0 @@ -//===-- test_extern.cpp --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -int externGlobalVar = 0x00C0FFEE; diff --git a/lldb/unittests/DIL/Runner.cpp b/lldb/unittests/DIL/Runner.cpp deleted file mode 100644 index 5524caab43ad2..0000000000000 --- a/lldb/unittests/DIL/Runner.cpp +++ /dev/null @@ -1,146 +0,0 @@ -//===-- Runner.cpp --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Runner.h" - -#include -#include -#include - -#include "lldb/API/SBBreakpoint.h" -#include "lldb/API/SBBreakpointLocation.h" -#include "lldb/API/SBCommandInterpreter.h" -#include "lldb/API/SBCommandReturnObject.h" -#include "lldb/API/SBDebugger.h" -#include "lldb/API/SBDefines.h" -#include "lldb/API/SBEvent.h" -#include "lldb/API/SBFileSpec.h" -#include "lldb/API/SBFrame.h" -#include "lldb/API/SBListener.h" -#include "lldb/API/SBProcess.h" -#include "lldb/API/SBTarget.h" -#include "lldb/API/SBThread.h" -#include "lldb/API/SBValue.h" -#include "lldb/lldb-enumerations.h" -#include "lldb/lldb-types.h" - -#ifdef CONFIG_VALGRIND -// Running a process under Valgrind can be extremely slow. -const uint32_t kWaitForEventTimeout = 30; -#else -// Running a process can be slow when built with sanitizers. -const uint32_t kWaitForEventTimeout = 5; -#endif - -int FindBreakpointLine(const std::string &file_path, - const std::string &break_line) { - // Read the source file to find the breakpoint location. - std::ifstream infile(file_path); - std::string line; - int line_num = 1; - while (std::getline(infile, line)) { - if (line.find(break_line) != std::string::npos) { - return line_num; - } - ++line_num; - } - - std::cerr << "Can't find the breakpoint location." << std::endl; - exit(1); -} - -std::string filename_of_source_path(const std::string &source_path) { - auto idx = source_path.find_last_of("/\\"); - if (idx == std::string::npos) { - idx = 0; - } else { - idx++; - } - - return source_path.substr(idx); -} - -lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger, - const std::string &source_path, - const std::string &binary_path, - const std::string &break_line) { - auto target = debugger.CreateTarget(binary_path.c_str()); - - auto source_file = filename_of_source_path(source_path); - - const char *argv[] = {binary_path.c_str(), nullptr}; - - auto bp = target.BreakpointCreateByLocation( - source_file.c_str(), FindBreakpointLine(source_path.c_str(), break_line)); - // Test programs don't perform any I/O, so current directory doesn't - // matter. - if (bp.GetNumLocations() == 0) - std::cerr - << "WARNING: Unable to resolve breakpoint to any actual locations." - << std::endl; - auto process = target.LaunchSimple(argv, nullptr, "."); - if (!process.IsValid()) { - std::cerr << "ERROR: Unable to launch process. Check that the path to the " - "binary is valid." - << std::endl; - return process; - } - lldb::SBEvent event; - auto listener = debugger.GetListener(); - - while (true) { - if (!listener.WaitForEvent(kWaitForEventTimeout, event)) { - std::cerr - << "Timeout while waiting for the event, kill the process and exit." - << std::endl; - process.Destroy(); - exit(1); - } - - if (!lldb::SBProcess::EventIsProcessEvent(event)) { - std::cerr << "Got some random event: " - << lldb::SBEvent::GetCStringFromEvent(event) << std::endl; - continue; - } - - auto state = lldb::SBProcess::GetStateFromEvent(event); - if (state == lldb::eStateInvalid) { - std::cerr << "process event: " - << lldb::SBEvent::GetCStringFromEvent(event) << std::endl; - continue; - } - - if (state == lldb::eStateExited) { - std::cerr << "Process exited: " << process.GetExitStatus() << std::endl; - process.Destroy(); - exit(1); - } - - if (state != lldb::eStateStopped) { - continue; - } - - auto thread = process.GetSelectedThread(); - auto stopReason = thread.GetStopReason(); - - if (stopReason != lldb::eStopReasonBreakpoint) { - continue; - } - - auto bpId = - static_cast(thread.GetStopReasonDataAtIndex(0)); - if (bpId != bp.GetID()) { - std::cerr << "Stopped at unknown breakpoint: " << bpId << std::endl - << "Now killing process and exiting" << std::endl; - process.Destroy(); - exit(1); - } - - return process; - } -} diff --git a/lldb/unittests/DIL/Runner.h b/lldb/unittests/DIL/Runner.h deleted file mode 100644 index f2723518e8ac1..0000000000000 --- a/lldb/unittests/DIL/Runner.h +++ /dev/null @@ -1,22 +0,0 @@ -//===-- Runner.h --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLDB_DIL_RUNNER_H_ -#define LLDB_DIL_RUNNER_H_ - -#include - -#include "lldb/API/SBDebugger.h" -#include "lldb/API/SBProcess.h" - -lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger, - const std::string &source_path, - const std::string &binary_path, - const std::string &break_line); - -#endif // LLDB_DIL_RUNNER_H_ \ No newline at end of file diff --git a/lldb/unittests/ValueObject/CMakeLists.txt b/lldb/unittests/ValueObject/CMakeLists.txt index 20c10532ec14c..6ef0091647a59 100644 --- a/lldb/unittests/ValueObject/CMakeLists.txt +++ b/lldb/unittests/ValueObject/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_unittest(LLDBValueObjectTests DumpValueObjectOptionsTests.cpp + DILLexerTests.cpp DynamicValueObjectLocalBuffer.cpp LINK_LIBS diff --git a/lldb/unittests/DIL/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp similarity index 100% rename from lldb/unittests/DIL/DILLexerTests.cpp rename to lldb/unittests/ValueObject/DILLexerTests.cpp From 336d59faeda26e9038c42267ee1e1330009cacbd Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Thu, 10 Apr 2025 22:08:35 +0500 Subject: [PATCH 4/6] Remove array conversion, cut down evaluation of & and *. --- lldb/include/lldb/ValueObject/DILAST.h | 3 - lldb/include/lldb/ValueObject/DILEval.h | 28 ----- lldb/source/ValueObject/DILEval.cpp | 118 +++--------------- .../TestFrameVarDILPointerArithmetic.py | 17 ++- .../var-dil/basics/PointerArithmetic/main.cpp | 2 + 5 files changed, 32 insertions(+), 136 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 323ebe8dd49ec..d00f726115757 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -51,8 +51,6 @@ class ASTNode { virtual llvm::Expected Accept(Visitor *v) const = 0; - virtual bool is_rvalue() const { return false; } - uint32_t GetLocation() const { return m_location; } NodeKind GetKind() const { return m_kind; } @@ -97,7 +95,6 @@ class UnaryOpNode : public ASTNode { m_rhs(std::move(rhs)) {} llvm::Expected Accept(Visitor *v) const override; - bool is_rvalue() const override { return m_kind != UnaryOpKind::Deref; } UnaryOpKind kind() const { return m_kind; } ASTNode *rhs() const { return m_rhs.get(); } diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 0080f4dba9291..b1dd3fdb49739 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -38,18 +38,6 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref, lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr = nullptr); -class FlowAnalysis { -public: - FlowAnalysis(bool address_of_is_pending) - : m_address_of_is_pending(address_of_is_pending) {} - - bool AddressOfIsPending() const { return m_address_of_is_pending; } - void DiscardAddressOf() { m_address_of_is_pending = false; } - -private: - bool m_address_of_is_pending; -}; - class Interpreter : Visitor { public: Interpreter(lldb::TargetSP target, llvm::StringRef expr, @@ -59,29 +47,13 @@ class Interpreter : Visitor { llvm::Expected Evaluate(const ASTNode *node); private: - llvm::Expected - EvaluateNode(const ASTNode *node, FlowAnalysis *flow = nullptr); - llvm::Expected Visit(const IdentifierNode *node) override; llvm::Expected Visit(const UnaryOpNode *node) override; - lldb::ValueObjectSP EvaluateDereference(lldb::ValueObjectSP rhs); - - FlowAnalysis *flow_analysis() { return m_flow_analysis_chain.back(); } - // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; llvm::StringRef m_expr; - // Flow analysis chain represents the expression evaluation flow for the - // current code branch. Each node in the chain corresponds to an AST node, - // describing the semantics of the evaluation for it. Currently, flow analysis - // propagates the information about the pending address-of operator, so that - // combination of address-of and a subsequent dereference can be eliminated. - // End of the chain (i.e. `back()`) contains the flow analysis instance for - // the current node. It may be `nullptr` if no relevant information is - // available, the caller/user is supposed to check. - std::vector m_flow_analysis_chain; lldb::ValueObjectSP m_scope; lldb::DynamicValueType m_default_dynamic; std::shared_ptr m_exe_ctx_scope; diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 8db20da4132a9..b8095f1169651 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,22 +18,6 @@ namespace lldb_private::dil { -static lldb::ValueObjectSP -ArrayToPointerConversion(lldb::ValueObjectSP valobj, - std::shared_ptr ctx) { - assert(valobj->IsArrayType() && - "an argument to array-to-pointer conversion must be an array"); - - uint64_t addr = valobj->GetLoadAddress(); - llvm::StringRef name = "result"; - ExecutionContext exe_ctx; - ctx->CalculateExecutionContext(exe_ctx); - return ValueObject::CreateValueObjectFromAddress( - name, addr, exe_ctx, - valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), - /* do_deref */ false); -} - static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -222,22 +206,9 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr, : m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic), m_exe_ctx_scope(frame_sp) {} -llvm::Expected Interpreter::Evaluate(const ASTNode *tree) { +llvm::Expected Interpreter::Evaluate(const ASTNode *node) { // Evaluate an AST. - auto value_or_error = EvaluateNode(tree); - - // Return the computed result-or-error. - return value_or_error; -} - -llvm::Expected -Interpreter::EvaluateNode(const ASTNode *node, FlowAnalysis *flow) { - // Set up the evaluation context for the current node. - m_flow_analysis_chain.push_back(flow); - // Traverse an AST pointed by the `node`. auto value_or_error = node->Accept(this); - // Cleanup the context. - m_flow_analysis_chain.pop_back(); // Return the computed value-or-error. The caller is responsible for // checking if an error occured during the evaluation. return value_or_error; @@ -265,33 +236,29 @@ Interpreter::Visit(const IdentifierNode *node) { llvm::Expected Interpreter::Visit(const UnaryOpNode *node) { - FlowAnalysis rhs_flow( - /* address_of_is_pending */ node->kind() == UnaryOpKind::AddrOf); - Status error; - auto rhs_or_err = EvaluateNode(node->rhs(), &rhs_flow); + auto rhs_or_err = Evaluate(node->rhs()); if (!rhs_or_err) { return rhs_or_err; } lldb::ValueObjectSP rhs = *rhs_or_err; - CompilerType rhs_type = rhs->GetCompilerType(); switch (node->kind()) { case UnaryOpKind::Deref: { - if (rhs_type.IsArrayType()) - rhs = ArrayToPointerConversion(rhs, m_exe_ctx_scope); - lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic); if (dynamic_rhs) rhs = dynamic_rhs; - if (rhs->GetCompilerType().IsPointerType()) { - if (rhs->GetCompilerType().IsPointerToVoid()) { - return llvm::make_error( - m_expr, "indirection not permitted on operand of type 'void *'", - node->GetLocation(), 1); - } - return EvaluateDereference(rhs); + CompilerType rhs_type = rhs->GetCompilerType(); + if (!rhs_type.IsReferenceType() && !rhs_type.IsPointerType()) + return llvm::make_error( + m_expr, "not a pointer or reference type", + node->rhs()->GetLocation()); + + if (rhs_type.IsPointerToVoid()) { + return llvm::make_error( + m_expr, "indirection not permitted on operand of type 'void *'", + node->GetLocation()); } lldb::ValueObjectSP child_sp = rhs->Dereference(error); if (error.Success()) @@ -300,68 +267,19 @@ Interpreter::Visit(const UnaryOpNode *node) { return rhs; } case UnaryOpKind::AddrOf: { - if (node->rhs()->is_rvalue()) { - std::string errMsg = - llvm::formatv("cannot take the address of an rvalue of type {0}", - rhs_type.TypeDescription()); - return llvm::make_error(m_expr, errMsg, + Status error; + lldb::ValueObjectSP value = rhs->AddressOf(error); + if (error.Fail()) { + return llvm::make_error(m_expr, error.AsCString(), node->GetLocation()); } - if (rhs->IsBitfield()) { - return llvm::make_error( - m_expr, "address of bit-field requested", node->GetLocation()); - } - // If the address-of operation wasn't cancelled during the evaluation of - // RHS (e.g. because of the address-of-a-dereference elision), apply it - // here. - if (rhs_flow.AddressOfIsPending()) { - Status error; - lldb::ValueObjectSP value = rhs->AddressOf(error); - if (error.Fail()) { - return llvm::make_error(m_expr, error.AsCString(), - node->GetLocation()); - } - return value; - } - return rhs; + return value; } } // Unsupported/invalid operation. return llvm::make_error( - m_expr, "invalid ast: unexpected binary operator", node->GetLocation(), - 1); -} - -lldb::ValueObjectSP Interpreter::EvaluateDereference(lldb::ValueObjectSP rhs) { - // If rhs is a reference, dereference it first. - Status error; - if (rhs->GetCompilerType().IsReferenceType()) - rhs = rhs->Dereference(error); - - assert(rhs->GetCompilerType().IsPointerType() && - "invalid ast: must be a pointer type"); - - if (rhs->GetDerefValobj()) - return rhs->GetDerefValobj()->GetSP(); - - CompilerType pointer_type = rhs->GetCompilerType(); - lldb::addr_t base_addr = rhs->GetValueAsUnsigned(0); - - llvm::StringRef name = "result"; - ExecutionContext exe_ctx(m_target.get(), false); - lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress( - name, base_addr, exe_ctx, pointer_type, - /* do_deref */ false); - - // If we're in the address-of context, skip the dereference and cancel the - // pending address-of operation as well. - if (flow_analysis() && flow_analysis()->AddressOfIsPending()) { - flow_analysis()->DiscardAddressOf(); - return value; - } - - return value->Dereference(error); + m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } } // namespace lldb_private::dil \ No newline at end of file diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py index 1e90433e331f5..79008dab2131d 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py @@ -24,18 +24,25 @@ def test_dereference(self): lldbutil.run_to_source_breakpoint( self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") ) - is_32bit = self.process().GetAddressByteSize() == 4 self.runCmd("settings set target.experimental.use-DIL true") self.expect_var_path("*p_int0", True, value="0") self.expect_var_path("*cp_int5", True, value="5") self.expect_var_path("*rcp_int0", True, type="const int *") - self.expect_var_path("*array", value="0") - self.expect_var_path( - "&*p_null", value="0x00000000" if is_32bit else "0x0000000000000000" - ) + self.expect_var_path("*offset_p", True, value="5") + self.expect_var_path("*offset_pref", True, type="int *") self.expect_var_path("**pp_int0", value="0") self.expect_var_path("&**pp_int0", type="int *") + self.expect( + "frame var '*array'", + error=True, + substrs=["not a pointer or reference type"], + ) + self.expect( + "frame var '&*p_null'", + error=True, + substrs=["doesn't have a valid address"], + ) self.expect( "frame var '&*p_void'", error=True, diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp index 1f2ac522ef0ef..b43b030fba049 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp @@ -6,6 +6,8 @@ int main(int argc, char **argv) { my_char_ptr my_p_char1 = p_char1; int offset = 5; + int *offset_p = &offset; + int *&offset_pref = offset_p; int array[10]; array[0] = 0; array[offset] = offset; From 8b63dfd61f407b0e3cdc6510557b40d57d7a0060 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Fri, 25 Apr 2025 20:00:31 +0500 Subject: [PATCH 5/6] Rename rhs to operand, remove extra check and return `ValueObject::Dereference` error, cleanup code. --- lldb/include/lldb/ValueObject/DILAST.h | 8 ++++---- lldb/source/ValueObject/DILEval.cpp | 28 +++++++++++--------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index d00f726115757..fe3827ef0516a 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -90,14 +90,14 @@ class IdentifierNode : public ASTNode { class UnaryOpNode : public ASTNode { public: - UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP rhs) + UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP operand) : ASTNode(location, NodeKind::eUnaryOpNode), m_kind(kind), - m_rhs(std::move(rhs)) {} + m_operand(std::move(operand)) {} llvm::Expected Accept(Visitor *v) const override; UnaryOpKind kind() const { return m_kind; } - ASTNode *rhs() const { return m_rhs.get(); } + ASTNode *operand() const { return m_operand.get(); } static bool classof(const ASTNode *node) { return node->GetKind() == NodeKind::eUnaryOpNode; @@ -105,7 +105,7 @@ class UnaryOpNode : public ASTNode { private: UnaryOpKind m_kind; - ASTNodeUP m_rhs; + ASTNodeUP m_operand; }; /// This class contains one Visit method for each specialized type of diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index b8095f1169651..f54acfa033ff5 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -237,10 +237,10 @@ Interpreter::Visit(const IdentifierNode *node) { llvm::Expected Interpreter::Visit(const UnaryOpNode *node) { Status error; - auto rhs_or_err = Evaluate(node->rhs()); - if (!rhs_or_err) { + auto rhs_or_err = Evaluate(node->operand()); + if (!rhs_or_err) return rhs_or_err; - } + lldb::ValueObjectSP rhs = *rhs_or_err; switch (node->kind()) { @@ -249,30 +249,26 @@ Interpreter::Visit(const UnaryOpNode *node) { if (dynamic_rhs) rhs = dynamic_rhs; - CompilerType rhs_type = rhs->GetCompilerType(); - if (!rhs_type.IsReferenceType() && !rhs_type.IsPointerType()) - return llvm::make_error( - m_expr, "not a pointer or reference type", - node->rhs()->GetLocation()); - - if (rhs_type.IsPointerToVoid()) { + if (rhs->GetCompilerType().IsPointerToVoid()) { return llvm::make_error( m_expr, "indirection not permitted on operand of type 'void *'", node->GetLocation()); } + lldb::ValueObjectSP child_sp = rhs->Dereference(error); - if (error.Success()) - rhs = child_sp; + if (error.Fail()) + return llvm::make_error(m_expr, error.AsCString(), + node->GetLocation()); - return rhs; + return child_sp; } case UnaryOpKind::AddrOf: { Status error; lldb::ValueObjectSP value = rhs->AddressOf(error); - if (error.Fail()) { + if (error.Fail()) return llvm::make_error(m_expr, error.AsCString(), node->GetLocation()); - } + return value; } } @@ -282,4 +278,4 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } -} // namespace lldb_private::dil \ No newline at end of file +} // namespace lldb_private::dil From b183a3b3e01b39c5f58e959bdb9441b2c050b041 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Mon, 28 Apr 2025 22:19:28 +0500 Subject: [PATCH 6/6] Remove a pointer to void check --- lldb/source/ValueObject/DILEval.cpp | 6 ------ .../PointerArithmetic/TestFrameVarDILPointerArithmetic.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index f54acfa033ff5..15a66d4866305 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -249,12 +249,6 @@ Interpreter::Visit(const UnaryOpNode *node) { if (dynamic_rhs) rhs = dynamic_rhs; - if (rhs->GetCompilerType().IsPointerToVoid()) { - return llvm::make_error( - m_expr, "indirection not permitted on operand of type 'void *'", - node->GetLocation()); - } - lldb::ValueObjectSP child_sp = rhs->Dereference(error); if (error.Fail()) return llvm::make_error(m_expr, error.AsCString(), diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py index 79008dab2131d..d36c5fce6d43d 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py @@ -46,5 +46,5 @@ def test_dereference(self): self.expect( "frame var '&*p_void'", error=True, - substrs=["indirection not permitted on operand of type 'void *'"], + substrs=["dereference failed: (void *) p_void"], )