diff options
-rw-r--r-- | docs/grammar.txt | 3 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 9 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.h | 1 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 21 | ||||
-rw-r--r-- | libsolidity/ast/ASTForward.h | 1 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 10 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.h | 1 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.cpp | 12 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.h | 2 | ||||
-rw-r--r-- | libsolidity/ast/ASTVisitor.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/AST_accept.h | 14 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 9 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.h | 1 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 36 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.h | 1 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 97 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 33 |
17 files changed, 252 insertions, 3 deletions
diff --git a/docs/grammar.txt b/docs/grammar.txt index e700c946..a5c2acf3 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -63,7 +63,7 @@ StateMutability = 'pure' | 'constant' | 'view' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | - Throw | SimpleStatement ) ';' + Throw | EmitStatement | SimpleStatement ) ';' ExpressionStatement = Expression IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? @@ -77,6 +77,7 @@ Continue = 'continue' Break = 'break' Return = 'return' Expression? Throw = 'throw' +EmitStatement = 'emit' FunctionCall VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )? IdentifierList = '(' ( Identifier? ',' )* Identifier? ')' diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0ee16c89..d93ebbd1 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -955,6 +955,15 @@ void TypeChecker::endVisit(Return const& _return) } } +void TypeChecker::endVisit(EmitStatement const& _emit) +{ + if ( + _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || + dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event + ) + m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event."); +} + bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (!_statement.initialValue()) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 344b019d..fc4ec6f0 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -94,6 +94,7 @@ private: virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; virtual void endVisit(Return const& _return) override; + virtual void endVisit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variable) override; virtual void endVisit(ExpressionStatement const& _statement) override; virtual bool visit(Conditional const& _conditional) override; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index feffde64..c0d55aec 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1197,6 +1197,27 @@ public: }; /** + * The emit statement is used to emit events: emit EventName(arg1, ..., argn) + */ +class EmitStatement: public Statement +{ +public: + explicit EmitStatement( + SourceLocation const& _location, + ASTPointer<ASTString> const& _docString, + ASTPointer<FunctionCall> const& _functionCall + ): + Statement(_location, _docString), m_eventCall(_functionCall) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + FunctionCall const& eventCall() const { return *m_eventCall; } + +private: + ASTPointer<FunctionCall> m_eventCall; +}; + +/** * Definition of a variable as a statement inside a function. It requires a type name (which can * also be "var") but the actual assignment can be missing. * Examples: var a = 2; uint256 a; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 46675e51..992fe4cd 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -69,6 +69,7 @@ class Continue; class Break; class Return; class Throw; +class EmitStatement; class VariableDeclarationStatement; class ExpressionStatement; class Expression; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index cd9f7eca..4fef67c3 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -540,7 +540,15 @@ bool ASTJsonConverter::visit(Return const& _node) bool ASTJsonConverter::visit(Throw const& _node) { - setJsonNode(_node, "Throw", {});; + setJsonNode(_node, "Throw", {}); + return false; +} + +bool ASTJsonConverter::visit(EmitStatement const& _node) +{ + setJsonNode(_node, "EmitStatement", { + make_pair("eventCall", toJson(_node.eventCall())) + }); return false; } diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 9a886220..88b93699 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -91,6 +91,7 @@ public: bool visit(Break const& _node) override; bool visit(Return const& _node) override; bool visit(Throw const& _node) override; + bool visit(EmitStatement const& _node) override; bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 23c3cbe1..4273f225 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -258,6 +258,13 @@ bool ASTPrinter::visit(Throw const& _node) return goDeeper(); } +bool ASTPrinter::visit(EmitStatement const& _node) +{ + writeLine("EmitStatement"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(VariableDeclarationStatement const& _node) { writeLine("VariableDeclarationStatement"); @@ -517,6 +524,11 @@ void ASTPrinter::endVisit(Throw const&) m_indentation--; } +void ASTPrinter::endVisit(EmitStatement const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(VariableDeclarationStatement const&) { m_indentation--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index 01e4f7fc..de3bf8a2 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -76,6 +76,7 @@ public: bool visit(Break const& _node) override; bool visit(Return const& _node) override; bool visit(Throw const& _node) override; + bool visit(EmitStatement const& _node) override; bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; @@ -120,6 +121,7 @@ public: void endVisit(Break const&) override; void endVisit(Return const&) override; void endVisit(Throw const&) override; + void endVisit(EmitStatement const&) override; void endVisit(VariableDeclarationStatement const&) override; void endVisit(ExpressionStatement const&) override; void endVisit(Conditional const&) override; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index b726d592..b1389f0f 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -73,6 +73,7 @@ public: virtual bool visit(Break& _node) { return visitNode(_node); } virtual bool visit(Return& _node) { return visitNode(_node); } virtual bool visit(Throw& _node) { return visitNode(_node); } + virtual bool visit(EmitStatement& _node) { return visitNode(_node); } virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); } virtual bool visit(Conditional& _node) { return visitNode(_node); } @@ -118,6 +119,7 @@ public: virtual void endVisit(Break& _node) { endVisitNode(_node); } virtual void endVisit(Return& _node) { endVisitNode(_node); } virtual void endVisit(Throw& _node) { endVisitNode(_node); } + virtual void endVisit(EmitStatement& _node) { endVisitNode(_node); } virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); } virtual void endVisit(Conditional& _node) { endVisitNode(_node); } @@ -175,6 +177,7 @@ public: virtual bool visit(Break const& _node) { return visitNode(_node); } virtual bool visit(Return const& _node) { return visitNode(_node); } virtual bool visit(Throw const& _node) { return visitNode(_node); } + virtual bool visit(EmitStatement const& _node) { return visitNode(_node); } virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); } virtual bool visit(Conditional const& _node) { return visitNode(_node); } @@ -220,6 +223,7 @@ public: virtual void endVisit(Break const& _node) { endVisitNode(_node); } virtual void endVisit(Return const& _node) { endVisitNode(_node); } virtual void endVisit(Throw const& _node) { endVisitNode(_node); } + virtual void endVisit(EmitStatement const& _node) { endVisitNode(_node); } virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); } virtual void endVisit(Conditional const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 904d9ff6..70ee997e 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -541,6 +541,20 @@ void Throw::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void EmitStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_eventCall->accept(_visitor); + _visitor.endVisit(*this); +} + +void EmitStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_eventCall->accept(_visitor); + _visitor.endVisit(*this); +} + void ExpressionStatement::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index f463db94..ebb718a5 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -903,6 +903,15 @@ bool ContractCompiler::visit(Throw const& _throw) return false; } +bool ContractCompiler::visit(EmitStatement const& _emit) +{ + CompilerContext::LocationSetter locationSetter(m_context, _emit); + StackHeightChecker checker(m_context); + compileExpression(_emit.eventCall()); + checker.check(); + return false; +} + bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { StackHeightChecker checker(m_context); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 1fd80d05..d698dc71 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -109,6 +109,7 @@ private: virtual bool visit(Break const& _breakStatement) override; virtual bool visit(Return const& _return) override; virtual bool visit(Throw const& _throw) override; + virtual bool visit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override; virtual bool visit(PlaceholderStatement const&) override; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e306e21b..8c97f55f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -897,7 +897,9 @@ ASTPointer<Statement> Parser::parseStatement() case Token::Assembly: return parseInlineAssembly(docString); case Token::Identifier: - if (m_insideModifier && m_scanner->currentLiteral() == "_") + if (m_scanner->currentLiteral() == "emit") + statement = parseEmitStatement(docString); + else if (m_insideModifier && m_scanner->currentLiteral() == "_") { statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString); m_scanner->next(); @@ -1015,6 +1017,38 @@ ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& ); } +ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const& _docString) +{ + ASTNodeFactory nodeFactory(*this); + m_scanner->next(); + ASTNodeFactory eventCallNodeFactory(*this); + + if (m_scanner->currentToken() != Token::Identifier) + fatalParserError("Expected event name or path."); + + vector<ASTPointer<PrimaryExpression>> path; + while (true) + { + path.push_back(parseIdentifier()); + if (m_scanner->currentToken() != Token::Period) + break; + m_scanner->next(); + }; + + auto eventName = expressionFromIndexAccessStructure(path, {}); + expectToken(Token::LParen); + + vector<ASTPointer<Expression>> arguments; + vector<ASTPointer<ASTString>> names; + std::tie(arguments, names) = parseFunctionCallArguments(); + eventCallNodeFactory.markEndPosition(); + nodeFactory.markEndPosition(); + expectToken(Token::RParen); + auto eventCall = eventCallNodeFactory.createNode<FunctionCall>(eventName, arguments, names); + auto statement = nodeFactory.createNode<EmitStatement>(_docString, eventCall); + return statement; +} + ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString) { RecursionGuard recursionGuard(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index cfdfea7e..3f780af9 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -104,6 +104,7 @@ private: ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString); ASTPointer<WhileStatement> parseDoWhileStatement(ASTPointer<ASTString> const& _docString); ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString); + ASTPointer<EmitStatement> parseEmitStatement(ASTPointer<ASTString> const& docString); /// A "simple statement" can be a variable declaration statement or an expression statement. ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString); ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d2528a67..3882e4ea 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2967,6 +2967,29 @@ BOOST_AUTO_TEST_CASE(event) } } +BOOST_AUTO_TEST_CASE(event_emit) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint _value); + function deposit(bytes32 _id) payable { + emit Deposit(msg.sender, _id, msg.value); + } + } + )"; + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(bytes32)", value, id); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value))); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id)); +} + BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( @@ -3009,6 +3032,28 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); } +BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) +{ + char const* sourceCode = R"( + contract A { + event x(); + } + contract B is A { + function f() returns (uint) { + emit A.x(); + return 1; + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); +} + BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( @@ -3107,6 +3152,58 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); } +BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) +{ + char const* sourceCode = R"( + contract A { + event Deposit(); + } + + contract B { + event Deposit(address _addr); + } + + contract ClientReceipt is A, B { + event Deposit(address _addr, uint _amount); + function deposit() returns (uint) { + emit Deposit(); + return 1; + } + function deposit(address _addr) returns (uint) { + emit Deposit(_addr); + return 1; + } + function deposit(address _addr, uint _amount) returns (uint) { + emit Deposit(_addr, _amount); + return 1; + } + } + )"; + u160 const c_loggedAddress = m_contractAddress; + + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); + + ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)"))); + + ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); +} + BOOST_AUTO_TEST_CASE(event_anonymous) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7c03d7cb..ce80ffda 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7785,6 +7785,39 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) CHECK_ERROR(text, TypeError, "Member \"delegatecall\" not found or not visible after argument-dependent lookup in contract"); } +BOOST_AUTO_TEST_CASE(emit_events) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + emit e(); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + event e(uint a, string b); + function f() public { + emit e(2, "abc"); + emit e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract A { event e(uint a, string b); } + contract C is A { + function f() public { + emit A.e(2, "abc"); + emit A.e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_SUITE_END() } |