Skip to content

Commit 388718b

Browse files
committed
Introduce emit statement.
1 parent 8fc9535 commit 388718b

17 files changed

+252
-3
lines changed

docs/grammar.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ StateMutability = 'pure' | 'constant' | 'view' | 'payable'
6363
Block = '{' Statement* '}'
6464
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
6565
( DoWhileStatement | PlaceholderStatement | Continue | Break | Return |
66-
Throw | SimpleStatement ) ';'
66+
Throw | EmitStatement | SimpleStatement ) ';'
6767

6868
ExpressionStatement = Expression
6969
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
@@ -77,6 +77,7 @@ Continue = 'continue'
7777
Break = 'break'
7878
Return = 'return' Expression?
7979
Throw = 'throw'
80+
EmitStatement = 'emit' FunctionCall
8081
VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )?
8182
IdentifierList = '(' ( Identifier? ',' )* Identifier? ')'
8283

libsolidity/analysis/TypeChecker.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,15 @@ void TypeChecker::endVisit(Return const& _return)
955955
}
956956
}
957957

958+
void TypeChecker::endVisit(EmitStatement const& _emit)
959+
{
960+
if (
961+
_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall ||
962+
dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event
963+
)
964+
m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event.");
965+
}
966+
958967
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
959968
{
960969
if (!_statement.initialValue())

libsolidity/analysis/TypeChecker.h

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class TypeChecker: private ASTConstVisitor
9494
virtual bool visit(WhileStatement const& _whileStatement) override;
9595
virtual bool visit(ForStatement const& _forStatement) override;
9696
virtual void endVisit(Return const& _return) override;
97+
virtual void endVisit(EmitStatement const& _emit) override;
9798
virtual bool visit(VariableDeclarationStatement const& _variable) override;
9899
virtual void endVisit(ExpressionStatement const& _statement) override;
99100
virtual bool visit(Conditional const& _conditional) override;

libsolidity/ast/AST.h

+21
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,27 @@ class Throw: public Statement
11961196
virtual void accept(ASTConstVisitor& _visitor) const override;
11971197
};
11981198

1199+
/**
1200+
* The emit statement is used to emit events: emit EventName(arg1, ..., argn)
1201+
*/
1202+
class EmitStatement: public Statement
1203+
{
1204+
public:
1205+
explicit EmitStatement(
1206+
SourceLocation const& _location,
1207+
ASTPointer<ASTString> const& _docString,
1208+
ASTPointer<FunctionCall> const& _functionCall
1209+
):
1210+
Statement(_location, _docString), m_eventCall(_functionCall) {}
1211+
virtual void accept(ASTVisitor& _visitor) override;
1212+
virtual void accept(ASTConstVisitor& _visitor) const override;
1213+
1214+
FunctionCall const& eventCall() const { return *m_eventCall; }
1215+
1216+
private:
1217+
ASTPointer<FunctionCall> m_eventCall;
1218+
};
1219+
11991220
/**
12001221
* Definition of a variable as a statement inside a function. It requires a type name (which can
12011222
* also be "var") but the actual assignment can be missing.

libsolidity/ast/ASTForward.h

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class Continue;
6969
class Break;
7070
class Return;
7171
class Throw;
72+
class EmitStatement;
7273
class VariableDeclarationStatement;
7374
class ExpressionStatement;
7475
class Expression;

libsolidity/ast/ASTJsonConverter.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,15 @@ bool ASTJsonConverter::visit(Return const& _node)
540540

541541
bool ASTJsonConverter::visit(Throw const& _node)
542542
{
543-
setJsonNode(_node, "Throw", {});;
543+
setJsonNode(_node, "Throw", {});
544+
return false;
545+
}
546+
547+
bool ASTJsonConverter::visit(EmitStatement const& _node)
548+
{
549+
setJsonNode(_node, "EmitStatement", {
550+
make_pair("eventCall", toJson(_node.eventCall()))
551+
});
544552
return false;
545553
}
546554

libsolidity/ast/ASTJsonConverter.h

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class ASTJsonConverter: public ASTConstVisitor
9191
bool visit(Break const& _node) override;
9292
bool visit(Return const& _node) override;
9393
bool visit(Throw const& _node) override;
94+
bool visit(EmitStatement const& _node) override;
9495
bool visit(VariableDeclarationStatement const& _node) override;
9596
bool visit(ExpressionStatement const& _node) override;
9697
bool visit(Conditional const& _node) override;

libsolidity/ast/ASTPrinter.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,13 @@ bool ASTPrinter::visit(Throw const& _node)
258258
return goDeeper();
259259
}
260260

261+
bool ASTPrinter::visit(EmitStatement const& _node)
262+
{
263+
writeLine("EmitStatement");
264+
printSourcePart(_node);
265+
return goDeeper();
266+
}
267+
261268
bool ASTPrinter::visit(VariableDeclarationStatement const& _node)
262269
{
263270
writeLine("VariableDeclarationStatement");
@@ -517,6 +524,11 @@ void ASTPrinter::endVisit(Throw const&)
517524
m_indentation--;
518525
}
519526

527+
void ASTPrinter::endVisit(EmitStatement const&)
528+
{
529+
m_indentation--;
530+
}
531+
520532
void ASTPrinter::endVisit(VariableDeclarationStatement const&)
521533
{
522534
m_indentation--;

libsolidity/ast/ASTPrinter.h

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class ASTPrinter: public ASTConstVisitor
7676
bool visit(Break const& _node) override;
7777
bool visit(Return const& _node) override;
7878
bool visit(Throw const& _node) override;
79+
bool visit(EmitStatement const& _node) override;
7980
bool visit(VariableDeclarationStatement const& _node) override;
8081
bool visit(ExpressionStatement const& _node) override;
8182
bool visit(Conditional const& _node) override;
@@ -120,6 +121,7 @@ class ASTPrinter: public ASTConstVisitor
120121
void endVisit(Break const&) override;
121122
void endVisit(Return const&) override;
122123
void endVisit(Throw const&) override;
124+
void endVisit(EmitStatement const&) override;
123125
void endVisit(VariableDeclarationStatement const&) override;
124126
void endVisit(ExpressionStatement const&) override;
125127
void endVisit(Conditional const&) override;

libsolidity/ast/ASTVisitor.h

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class ASTVisitor
7373
virtual bool visit(Break& _node) { return visitNode(_node); }
7474
virtual bool visit(Return& _node) { return visitNode(_node); }
7575
virtual bool visit(Throw& _node) { return visitNode(_node); }
76+
virtual bool visit(EmitStatement& _node) { return visitNode(_node); }
7677
virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
7778
virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); }
7879
virtual bool visit(Conditional& _node) { return visitNode(_node); }
@@ -118,6 +119,7 @@ class ASTVisitor
118119
virtual void endVisit(Break& _node) { endVisitNode(_node); }
119120
virtual void endVisit(Return& _node) { endVisitNode(_node); }
120121
virtual void endVisit(Throw& _node) { endVisitNode(_node); }
122+
virtual void endVisit(EmitStatement& _node) { endVisitNode(_node); }
121123
virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); }
122124
virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); }
123125
virtual void endVisit(Conditional& _node) { endVisitNode(_node); }
@@ -175,6 +177,7 @@ class ASTConstVisitor
175177
virtual bool visit(Break const& _node) { return visitNode(_node); }
176178
virtual bool visit(Return const& _node) { return visitNode(_node); }
177179
virtual bool visit(Throw const& _node) { return visitNode(_node); }
180+
virtual bool visit(EmitStatement const& _node) { return visitNode(_node); }
178181
virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); }
179182
virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); }
180183
virtual bool visit(Conditional const& _node) { return visitNode(_node); }
@@ -220,6 +223,7 @@ class ASTConstVisitor
220223
virtual void endVisit(Break const& _node) { endVisitNode(_node); }
221224
virtual void endVisit(Return const& _node) { endVisitNode(_node); }
222225
virtual void endVisit(Throw const& _node) { endVisitNode(_node); }
226+
virtual void endVisit(EmitStatement const& _node) { endVisitNode(_node); }
223227
virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); }
224228
virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); }
225229
virtual void endVisit(Conditional const& _node) { endVisitNode(_node); }

libsolidity/ast/AST_accept.h

+14
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,20 @@ void Throw::accept(ASTConstVisitor& _visitor) const
541541
_visitor.endVisit(*this);
542542
}
543543

544+
void EmitStatement::accept(ASTVisitor& _visitor)
545+
{
546+
if (_visitor.visit(*this))
547+
m_eventCall->accept(_visitor);
548+
_visitor.endVisit(*this);
549+
}
550+
551+
void EmitStatement::accept(ASTConstVisitor& _visitor) const
552+
{
553+
if (_visitor.visit(*this))
554+
m_eventCall->accept(_visitor);
555+
_visitor.endVisit(*this);
556+
}
557+
544558
void ExpressionStatement::accept(ASTVisitor& _visitor)
545559
{
546560
if (_visitor.visit(*this))

libsolidity/codegen/ContractCompiler.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,15 @@ bool ContractCompiler::visit(Throw const& _throw)
903903
return false;
904904
}
905905

906+
bool ContractCompiler::visit(EmitStatement const& _emit)
907+
{
908+
CompilerContext::LocationSetter locationSetter(m_context, _emit);
909+
StackHeightChecker checker(m_context);
910+
compileExpression(_emit.eventCall());
911+
checker.check();
912+
return false;
913+
}
914+
906915
bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
907916
{
908917
StackHeightChecker checker(m_context);

libsolidity/codegen/ContractCompiler.h

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class ContractCompiler: private ASTConstVisitor
109109
virtual bool visit(Break const& _breakStatement) override;
110110
virtual bool visit(Return const& _return) override;
111111
virtual bool visit(Throw const& _throw) override;
112+
virtual bool visit(EmitStatement const& _emit) override;
112113
virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
113114
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
114115
virtual bool visit(PlaceholderStatement const&) override;

libsolidity/parsing/Parser.cpp

+35-1
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,9 @@ ASTPointer<Statement> Parser::parseStatement()
897897
case Token::Assembly:
898898
return parseInlineAssembly(docString);
899899
case Token::Identifier:
900-
if (m_insideModifier && m_scanner->currentLiteral() == "_")
900+
if (m_scanner->currentLiteral() == "emit")
901+
statement = parseEmitStatement(docString);
902+
else if (m_insideModifier && m_scanner->currentLiteral() == "_")
901903
{
902904
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
903905
m_scanner->next();
@@ -1015,6 +1017,38 @@ ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const&
10151017
);
10161018
}
10171019

1020+
ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const& _docString)
1021+
{
1022+
ASTNodeFactory nodeFactory(*this);
1023+
m_scanner->next();
1024+
ASTNodeFactory eventCallNodeFactory(*this);
1025+
1026+
if (m_scanner->currentToken() != Token::Identifier)
1027+
fatalParserError("Expected event name or path.");
1028+
1029+
vector<ASTPointer<PrimaryExpression>> path;
1030+
while (true)
1031+
{
1032+
path.push_back(parseIdentifier());
1033+
if (m_scanner->currentToken() != Token::Period)
1034+
break;
1035+
m_scanner->next();
1036+
};
1037+
1038+
auto eventName = expressionFromIndexAccessStructure(path, {});
1039+
expectToken(Token::LParen);
1040+
1041+
vector<ASTPointer<Expression>> arguments;
1042+
vector<ASTPointer<ASTString>> names;
1043+
std::tie(arguments, names) = parseFunctionCallArguments();
1044+
eventCallNodeFactory.markEndPosition();
1045+
nodeFactory.markEndPosition();
1046+
expectToken(Token::RParen);
1047+
auto eventCall = eventCallNodeFactory.createNode<FunctionCall>(eventName, arguments, names);
1048+
auto statement = nodeFactory.createNode<EmitStatement>(_docString, eventCall);
1049+
return statement;
1050+
}
1051+
10181052
ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
10191053
{
10201054
RecursionGuard recursionGuard(*this);

libsolidity/parsing/Parser.h

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class Parser: public ParserBase
104104
ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString);
105105
ASTPointer<WhileStatement> parseDoWhileStatement(ASTPointer<ASTString> const& _docString);
106106
ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
107+
ASTPointer<EmitStatement> parseEmitStatement(ASTPointer<ASTString> const& docString);
107108
/// A "simple statement" can be a variable declaration statement or an expression statement.
108109
ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString);
109110
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(

test/libsolidity/SolidityEndToEndTest.cpp

+97
Original file line numberDiff line numberDiff line change
@@ -2967,6 +2967,29 @@ BOOST_AUTO_TEST_CASE(event)
29672967
}
29682968
}
29692969

2970+
BOOST_AUTO_TEST_CASE(event_emit)
2971+
{
2972+
char const* sourceCode = R"(
2973+
contract ClientReceipt {
2974+
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
2975+
function deposit(bytes32 _id) payable {
2976+
emit Deposit(msg.sender, _id, msg.value);
2977+
}
2978+
}
2979+
)";
2980+
compileAndRun(sourceCode);
2981+
u256 value(18);
2982+
u256 id(0x1234);
2983+
callContractFunctionWithValue("deposit(bytes32)", value, id);
2984+
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
2985+
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
2986+
BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value)));
2987+
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3);
2988+
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256)")));
2989+
BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender, h256::AlignRight));
2990+
BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id));
2991+
}
2992+
29702993
BOOST_AUTO_TEST_CASE(event_no_arguments)
29712994
{
29722995
char const* sourceCode = R"(
@@ -3009,6 +3032,28 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name)
30093032
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()")));
30103033
}
30113034

3035+
BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit)
3036+
{
3037+
char const* sourceCode = R"(
3038+
contract A {
3039+
event x();
3040+
}
3041+
contract B is A {
3042+
function f() returns (uint) {
3043+
emit A.x();
3044+
return 1;
3045+
}
3046+
}
3047+
)";
3048+
compileAndRun(sourceCode);
3049+
callContractFunction("f()");
3050+
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
3051+
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
3052+
BOOST_CHECK(m_logs[0].data.empty());
3053+
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
3054+
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()")));
3055+
}
3056+
30123057
BOOST_AUTO_TEST_CASE(events_with_same_name)
30133058
{
30143059
char const* sourceCode = R"(
@@ -3107,6 +3152,58 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited)
31073152
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)")));
31083153
}
31093154

3155+
BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit)
3156+
{
3157+
char const* sourceCode = R"(
3158+
contract A {
3159+
event Deposit();
3160+
}
3161+
3162+
contract B {
3163+
event Deposit(address _addr);
3164+
}
3165+
3166+
contract ClientReceipt is A, B {
3167+
event Deposit(address _addr, uint _amount);
3168+
function deposit() returns (uint) {
3169+
emit Deposit();
3170+
return 1;
3171+
}
3172+
function deposit(address _addr) returns (uint) {
3173+
emit Deposit(_addr);
3174+
return 1;
3175+
}
3176+
function deposit(address _addr, uint _amount) returns (uint) {
3177+
emit Deposit(_addr, _amount);
3178+
return 1;
3179+
}
3180+
}
3181+
)";
3182+
u160 const c_loggedAddress = m_contractAddress;
3183+
3184+
compileAndRun(sourceCode);
3185+
ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1)));
3186+
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
3187+
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
3188+
BOOST_CHECK(m_logs[0].data.empty());
3189+
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
3190+
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()")));
3191+
3192+
ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1)));
3193+
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
3194+
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
3195+
BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress));
3196+
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
3197+
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)")));
3198+
3199+
ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1)));
3200+
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
3201+
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
3202+
BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100));
3203+
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
3204+
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)")));
3205+
}
3206+
31103207
BOOST_AUTO_TEST_CASE(event_anonymous)
31113208
{
31123209
char const* sourceCode = R"(

0 commit comments

Comments
 (0)