diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index bc6be6008..82d58d2a6 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -271,6 +271,20 @@ void ModifierInvocation::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation.")); } +void EventDefinition::checkTypeRequirements() +{ + int numIndexed = 0; + for (ASTPointer const& var: getParameters()) + { + if (var->isIndexed()) + numIndexed++; + if (!var->getType()->canLiveOutsideStorage()) + BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); + } + if (numIndexed > 3) + BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event.")); +} + void Block::checkTypeRequirements() { for (shared_ptr const& statement: m_statements) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 6c207290c..ab9815f60 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -202,13 +202,15 @@ public: std::vector> const& _definedStructs, std::vector> const& _stateVariables, std::vector> const& _definedFunctions, - std::vector> const& _functionModifiers): + std::vector> const& _functionModifiers, + std::vector> const& _events): Declaration(_location, _name), Documented(_documentation), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions), - m_functionModifiers(_functionModifiers) + m_functionModifiers(_functionModifiers), + m_events(_events) {} virtual void accept(ASTVisitor& _visitor) override; @@ -219,6 +221,7 @@ public: std::vector> const& getStateVariables() const { return m_stateVariables; } std::vector> const& getFunctionModifiers() const { return m_functionModifiers; } std::vector> const& getDefinedFunctions() const { return m_definedFunctions; } + std::vector> const& getEvents() const { return m_events; } virtual TypePointer getType(ContractDefinition const* m_currentContract) const override; @@ -248,6 +251,7 @@ private: std::vector> m_stateVariables; std::vector> m_definedFunctions; std::vector> m_functionModifiers; + std::vector> m_events; std::vector m_linearizedBaseContracts; mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList; @@ -380,8 +384,10 @@ class VariableDeclaration: public Declaration { public: VariableDeclaration(Location const& _location, ASTPointer const& _type, - ASTPointer const& _name, bool _isPublic, bool _isStateVar = false): - Declaration(_location, _name), m_typeName(_type), m_isPublic(_isPublic), m_isStateVariable(_isStateVar) {} + ASTPointer const& _name, bool _isPublic, bool _isStateVar = false, + bool _isIndexed = false): + Declaration(_location, _name), m_typeName(_type), + m_isPublic(_isPublic), m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -396,12 +402,13 @@ public: bool isLocalVariable() const { return !!dynamic_cast(getScope()); } bool isPublic() const { return m_isPublic; } bool isStateVariable() const { return m_isStateVariable; } - + bool isIndexed() const { return m_isIndexed; } private: ASTPointer m_typeName; ///< can be empty ("var") bool m_isPublic; ///< Whether there is an accessor for it or not bool m_isStateVariable; ///< Whether or not this is a contract state variable + bool m_isIndexed; ///< Whether this is an indexed variable (used by events). std::shared_ptr m_type; ///< derived type, initially empty }; @@ -429,7 +436,6 @@ public: virtual TypePointer getType(ContractDefinition const* = nullptr) const override; - void checkTypeRequirements(); private: @@ -460,6 +466,37 @@ private: std::vector> m_arguments; }; +/** + * Definition of a (loggable) event. + */ +class EventDefinition: public Declaration, public Documented +{ +public: + EventDefinition(Location const& _location, + ASTPointer const& _name, + ASTPointer const& _documentation, + ASTPointer const& _parameters): + Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector> const& getParameters() const { return m_parameters->getParameters(); } + ParameterList const& getParameterList() const { return *m_parameters; } + Block const& getBody() const { return *m_body; } + + virtual TypePointer getType(ContractDefinition const* = nullptr) const override + { + return std::make_shared(*this); + } + + void checkTypeRequirements(); + +private: + ASTPointer m_parameters; + ASTPointer m_body; +}; + /** * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global * functions when such an identifier is encountered. Will never have a valid location in the source code. diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index aa5cd49cd..22015f26b 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -45,6 +45,7 @@ class FunctionDefinition; class VariableDeclaration; class ModifierDefinition; class ModifierInvocation; +class EventDefinition; class MagicVariableDeclaration; class TypeName; class ElementaryTypeName; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 05b24c63a..949740e89 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -108,6 +108,13 @@ bool ASTPrinter::visit(ModifierInvocation const& _node) return goDeeper(); } +bool ASTPrinter::visit(EventDefinition const& _node) +{ + writeLine("EventDefinition \"" + _node.getName() + "\""); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(TypeName const& _node) { writeLine("TypeName"); @@ -365,6 +372,11 @@ void ASTPrinter::endVisit(ModifierInvocation const&) m_indentation--; } +void ASTPrinter::endVisit(EventDefinition const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(TypeName const&) { m_indentation--; diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 77025b2d0..ebc163e31 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -51,6 +51,7 @@ public: bool visit(VariableDeclaration const& _node) override; bool visit(ModifierDefinition const& _node) override; bool visit(ModifierInvocation const& _node) override; + bool visit(EventDefinition const& _node) override; bool visit(TypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; @@ -89,6 +90,7 @@ public: void endVisit(VariableDeclaration const&) override; void endVisit(ModifierDefinition const&) override; void endVisit(ModifierInvocation const&) override; + void endVisit(EventDefinition const&) override; void endVisit(TypeName const&) override; void endVisit(ElementaryTypeName const&) override; void endVisit(UserDefinedTypeName const&) override; diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index 53fbd1918..294902778 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -52,6 +52,7 @@ public: virtual bool visit(VariableDeclaration&) { return true; } virtual bool visit(ModifierDefinition&) { return true; } virtual bool visit(ModifierInvocation&) { return true; } + virtual bool visit(EventDefinition&) { return true; } virtual bool visit(TypeName&) { return true; } virtual bool visit(ElementaryTypeName&) { return true; } virtual bool visit(UserDefinedTypeName&) { return true; } @@ -92,6 +93,7 @@ public: virtual void endVisit(VariableDeclaration&) { } virtual void endVisit(ModifierDefinition&) { } virtual void endVisit(ModifierInvocation&) { } + virtual void endVisit(EventDefinition&) { } virtual void endVisit(TypeName&) { } virtual void endVisit(ElementaryTypeName&) { } virtual void endVisit(UserDefinedTypeName&) { } @@ -136,6 +138,7 @@ public: virtual bool visit(VariableDeclaration const&) { return true; } virtual bool visit(ModifierDefinition const&) { return true; } virtual bool visit(ModifierInvocation const&) { return true; } + virtual bool visit(EventDefinition const&) { return true; } virtual bool visit(TypeName const&) { return true; } virtual bool visit(ElementaryTypeName const&) { return true; } virtual bool visit(UserDefinedTypeName const&) { return true; } @@ -176,6 +179,7 @@ public: virtual void endVisit(VariableDeclaration const&) { } virtual void endVisit(ModifierDefinition const&) { } virtual void endVisit(ModifierInvocation const&) { } + virtual void endVisit(EventDefinition const&) { } virtual void endVisit(TypeName const&) { } virtual void endVisit(ElementaryTypeName const&) { } virtual void endVisit(UserDefinedTypeName const&) { } diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index 481b150bd..38108cd72 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -64,8 +64,9 @@ void ContractDefinition::accept(ASTVisitor& _visitor) listAccept(m_baseContracts, _visitor); listAccept(m_definedStructs, _visitor); listAccept(m_stateVariables, _visitor); - listAccept(m_definedFunctions, _visitor); + listAccept(m_events, _visitor); listAccept(m_functionModifiers, _visitor); + listAccept(m_definedFunctions, _visitor); } _visitor.endVisit(*this); } @@ -77,8 +78,9 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const listAccept(m_baseContracts, _visitor); listAccept(m_definedStructs, _visitor); listAccept(m_stateVariables, _visitor); - listAccept(m_definedFunctions, _visitor); + listAccept(m_events, _visitor); listAccept(m_functionModifiers, _visitor); + listAccept(m_definedFunctions, _visitor); } _visitor.endVisit(*this); } @@ -219,6 +221,20 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void EventDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_parameters->accept(_visitor); + _visitor.endVisit(*this); +} + +void EventDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_parameters->accept(_visitor); + _visitor.endVisit(*this); +} + void TypeName::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 5d44c86f3..7d58ea2e2 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -304,10 +305,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << eth::Instruction::SUICIDE; break; case Location::SHA3: - arguments.front()->accept(*this); - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); - // @todo move this once we actually use memory - CompilerUtils(m_context).storeInMemory(0); + appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front()); m_context << u256(32) << u256(0) << eth::Instruction::SHA3; break; case Location::LOG0: @@ -317,14 +315,41 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case Location::LOG4: { unsigned logNumber = int(function.getLocation()) - int(Location::LOG0); - for (int arg = logNumber; arg >= 0; --arg) + for (unsigned arg = logNumber; arg > 0; --arg) { arguments[arg]->accept(*this); appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true); } - // @todo move this once we actually use memory - CompilerUtils(m_context).storeInMemory(0); - m_context << u256(32) << u256(0) << eth::logInstruction(logNumber); + unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(), + *arguments.front()); + solAssert(length == 32, "Log data should be 32 bytes long (for now)."); + m_context << u256(length) << u256(0) << eth::logInstruction(logNumber); + break; + } + case Location::EVENT: + { + _functionCall.getExpression().accept(*this); + auto const& event = dynamic_cast(function.getDeclaration()); + // Copy all non-indexed arguments to memory (data) + unsigned numIndexed = 0; + unsigned memLength = 0; + for (unsigned arg = 0; arg < arguments.size(); ++arg) + if (!event.getParameters()[arg]->isIndexed()) + memLength += appendExpressionCopyToMemory(*function.getParameterTypes()[arg], + *arguments[arg], memLength); + // All indexed arguments go to the stack + for (unsigned arg = arguments.size(); arg > 0; --arg) + if (event.getParameters()[arg - 1]->isIndexed()) + { + ++numIndexed; + arguments[arg - 1]->accept(*this); + appendTypeConversion(*arguments[arg - 1]->getType(), + *function.getParameterTypes()[arg - 1], true); + } + m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); + ++numIndexed; + solAssert(numIndexed <= 4, "Too many indexed arguments."); + m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed); break; } case Location::BLOCKHASH: @@ -459,14 +484,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) { _indexAccess.getBaseExpression().accept(*this); - _indexAccess.getIndexExpression().accept(*this); - appendTypeConversion(*_indexAccess.getIndexExpression().getType(), - *dynamic_cast(*_indexAccess.getBaseExpression().getType()).getKeyType(), - true); + + TypePointer const& keyType = dynamic_cast(*_indexAccess.getBaseExpression().getType()).getKeyType(); + unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression()); + solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now)."); // @todo move this once we actually use memory - CompilerUtils(m_context).storeInMemory(0); - CompilerUtils(m_context).storeInMemory(32); - m_context << u256(64) << u256(0) << eth::Instruction::SHA3; + length += CompilerUtils(m_context).storeInMemory(length); + m_context << u256(length) << u256(0) << eth::Instruction::SHA3; m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType()); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); @@ -495,6 +519,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { // no-op } + else if (dynamic_cast(declaration)) + { + // no-op + } else { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); @@ -791,22 +819,25 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ { unsigned length = 0; for (unsigned i = 0; i < _arguments.size(); ++i) - { - _arguments[i]->accept(*this); - appendTypeConversion(*_arguments[i]->getType(), *_types[i], true); - unsigned const c_numBytes = _types[i]->getCalldataEncodedSize(); - if (c_numBytes == 0 || c_numBytes > 32) - BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(_arguments[i]->getLocation()) - << errinfo_comment("Type " + _types[i]->toString() + " not yet supported.")); - bool const c_leftAligned = _types[i]->getCategory() == Type::Category::STRING; - bool const c_padToWords = true; - length += CompilerUtils(m_context).storeInMemory(_memoryOffset + length, c_numBytes, - c_leftAligned, c_padToWords); - } + length += appendExpressionCopyToMemory(*_types[i], *_arguments[i], _memoryOffset + length); return length; } +unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, + Expression const& _expression, unsigned _memoryOffset) +{ + _expression.accept(*this); + appendTypeConversion(*_expression.getType(), _expectedType, true); + unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize()); + if (c_numBytes == 0 || c_numBytes > 32) + BOOST_THROW_EXCEPTION(CompilerError() + << errinfo_sourceLocation(_expression.getLocation()) + << errinfo_comment("Type " + _expectedType.toString() + " not yet supported.")); + bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING; + bool const c_padToWords = true; + return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords); +} + void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 748cc6c6f..caecbfe8d 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -94,8 +94,13 @@ private: bool bare = false); /// Appends code that copies the given arguments to memory (with optional offset). /// @returns the number of bytes copied to memory - unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector> const& _arguments, + unsigned appendArgumentCopyToMemory(TypePointers const& _types, + std::vector> const& _arguments, unsigned _memoryOffset = 0); + /// Appends code that evaluates a single expression and copies it to memory (with optional offset). + /// @returns the number of bytes copied to memory + unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression, + unsigned _memoryOffset = 0); /// Appends code for a State Variable accessor function void appendStateVariableAccessor(VariableDeclaration const& _varDecl); diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 43201fe12..75df637c4 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -60,6 +60,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) ReferencesResolver resolver(*structDef, *this, &_contract, nullptr); for (ASTPointer const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, &_contract, nullptr); + for (ASTPointer const& event: _contract.getEvents()) + ReferencesResolver resolver(*event, *this, &_contract, nullptr); for (ASTPointer const& modifier: _contract.getFunctionModifiers()) { m_currentScope = &m_scopes[modifier.get()]; @@ -259,6 +261,12 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) return true; } +bool DeclarationRegistrationHelper::visit(EventDefinition& _event) +{ + registerDeclaration(_event, false); + return true; +} + void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) { map::iterator iter; diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index ba327a59e..4555491f4 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -104,6 +104,7 @@ private: void endVisit(ModifierDefinition& _modifier); void endVisit(VariableDefinition& _variableDefinition); bool visit(VariableDeclaration& _declaration); + bool visit(EventDefinition& _event); void enterNewSubScope(Declaration const& _declaration); void closeCurrentScope(); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 5cfc8f462..48cca6126 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -122,6 +122,7 @@ ASTPointer Parser::parseContractDefinition() vector> stateVariables; vector> functions; vector> modifiers; + vector> events; if (m_scanner->getCurrentToken() == Token::IS) do { @@ -149,19 +150,23 @@ ASTPointer Parser::parseContractDefinition() else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || Token::isElementaryTypeName(currentToken)) { - bool const allowVar = false; - stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic, true)); + VarDeclParserOptions options; + options.isPublic = visibilityIsPublic; + options.isStateVariable = true; + stateVariables.push_back(parseVariableDeclaration(options)); expectToken(Token::SEMICOLON); } else if (currentToken == Token::MODIFIER) modifiers.push_back(parseModifierDefinition()); + else if (currentToken == Token::EVENT) + events.push_back(parseEventDefinition()); else BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected.")); } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); return nodeFactory.createNode(name, docString, baseContracts, structs, - stateVariables, functions, modifiers); + stateVariables, functions, modifiers, events); } ASTPointer Parser::parseInheritanceSpecifier() @@ -236,8 +241,7 @@ ASTPointer Parser::parseStructDefinition() expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { - bool const allowVar = false; - members.push_back(parseVariableDeclaration(allowVar)); + members.push_back(parseVariableDeclaration()); expectToken(Token::SEMICOLON); } nodeFactory.markEndPosition(); @@ -245,12 +249,20 @@ ASTPointer Parser::parseStructDefinition() return nodeFactory.createNode(name, members); } -ASTPointer Parser::parseVariableDeclaration(bool _allowVar, bool _isPublic, bool _isStateVariable) +ASTPointer Parser::parseVariableDeclaration(VarDeclParserOptions const& _options) { ASTNodeFactory nodeFactory(*this); - ASTPointer type = parseTypeName(_allowVar); + ASTPointer type = parseTypeName(_options.allowVar); + bool isIndexed = false; + if (_options.allowIndexed && m_scanner->getCurrentToken() == Token::INDEXED) + { + isIndexed = true; + m_scanner->next(); + } nodeFactory.markEndPosition(); - return nodeFactory.createNode(type, expectIdentifierToken(), _isPublic, _isStateVariable); + return nodeFactory.createNode(type, expectIdentifierToken(), + _options.isPublic, _options.isStateVariable, + isIndexed); } ASTPointer Parser::parseModifierDefinition() @@ -280,6 +292,23 @@ ASTPointer Parser::parseModifierDefinition() return nodeFactory.createNode(name, docstring, parameters, block); } +ASTPointer Parser::parseEventDefinition() +{ + ASTNodeFactory nodeFactory(*this); + ASTPointer docstring; + if (m_scanner->getCurrentCommentLiteral() != "") + docstring = make_shared(m_scanner->getCurrentCommentLiteral()); + + expectToken(Token::EVENT); + ASTPointer name(expectIdentifierToken()); + ASTPointer parameters; + if (m_scanner->getCurrentToken() == Token::LPAREN) + parameters = parseParameterList(true, true); + nodeFactory.markEndPosition(); + expectToken(Token::SEMICOLON); + return nodeFactory.createNode(name, docstring, parameters); +} + ASTPointer Parser::parseModifierInvocation() { ASTNodeFactory nodeFactory(*this); @@ -352,19 +381,20 @@ ASTPointer Parser::parseMapping() return nodeFactory.createNode(keyType, valueType); } -ASTPointer Parser::parseParameterList(bool _allowEmpty) +ASTPointer Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed) { ASTNodeFactory nodeFactory(*this); vector> parameters; + VarDeclParserOptions options; + options.allowIndexed = _allowIndexed; expectToken(Token::LPAREN); if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { - bool const allowVar = false; - parameters.push_back(parseVariableDeclaration(allowVar)); + parameters.push_back(parseVariableDeclaration(options)); while (m_scanner->getCurrentToken() != Token::RPAREN) { expectToken(Token::COMMA); - parameters.push_back(parseVariableDeclaration(allowVar)); + parameters.push_back(parseVariableDeclaration(options)); } } nodeFactory.markEndPosition(); @@ -506,8 +536,9 @@ ASTPointer Parser::parseVarDefOrExprStmt() ASTPointer Parser::parseVariableDefinition() { ASTNodeFactory nodeFactory(*this); - bool const allowVar = true; - ASTPointer variable = parseVariableDeclaration(allowVar); + VarDeclParserOptions options; + options.allowVar = true; + ASTPointer variable = parseVariableDeclaration(options); ASTPointer value; if (m_scanner->getCurrentToken() == Token::ASSIGN) { diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index d3bff67e5..69478c81e 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -45,6 +45,14 @@ private: /// End position of the current token int getEndPosition() const; + struct VarDeclParserOptions { + VarDeclParserOptions() {} + bool allowVar = false; + bool isPublic = false; + bool isStateVariable = false; + bool allowIndexed = false; + }; + ///@{ ///@name Parsing functions for the AST nodes ASTPointer parseImportDirective(); @@ -52,13 +60,14 @@ private: ASTPointer parseInheritanceSpecifier(); ASTPointer parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); ASTPointer parseStructDefinition(); - ASTPointer parseVariableDeclaration(bool _allowVar, bool _isPublic = false, bool _isStateVar = false); + ASTPointer parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); ASTPointer parseModifierDefinition(); + ASTPointer parseEventDefinition(); ASTPointer parseModifierInvocation(); ASTPointer parseIdentifier(); ASTPointer parseTypeName(bool _allowVar); ASTPointer parseMapping(); - ASTPointer parseParameterList(bool _allowEmpty = true); + ASTPointer parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false); ASTPointer parseBlock(); ASTPointer parseStatement(); ASTPointer parseIfStatement(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 55ec8e8e0..ed42f90cc 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -153,7 +153,9 @@ namespace solidity K(DEFAULT, "default", 0) \ K(DO, "do", 0) \ K(ELSE, "else", 0) \ + K(EVENT, "event", 0) \ K(IS, "is", 0) \ + K(INDEXED, "indexed", 0) \ K(FOR, "for", 0) \ K(FUNCTION, "function", 0) \ K(IF, "if", 0) \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index bebb4be17..0842e40ba 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -633,6 +633,22 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): swap(retParamNames, m_returnParameterNames); } +FunctionType::FunctionType(const EventDefinition& _event): + m_location(Location::EVENT), m_declaration(&_event) +{ + TypePointers params; + vector paramNames; + params.reserve(_event.getParameters().size()); + paramNames.reserve(_event.getParameters().size()); + for (ASTPointer const& var: _event.getParameters()) + { + paramNames.push_back(var->getName()); + params.push_back(var->getType()); + } + swap(params, m_parameterTypes); + swap(paramNames, m_parameterNames); +} + bool FunctionType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 4b4d17d0a..1f4d27a25 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -350,16 +350,18 @@ public: /// INTERNAL: jump tag, EXTERNAL: contract address + function identifier, /// BARE: contract address (non-abi contract call) /// OTHERS: special virtual function, nothing on the stack + /// @todo This documentation is outdated, and Location should rather be named "Type" enum class Location { INTERNAL, EXTERNAL, CREATION, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, - LOG0, LOG1, LOG2, LOG3, LOG4, + LOG0, LOG1, LOG2, LOG3, LOG4, EVENT, SET_GAS, SET_VALUE, BLOCKHASH, BARE }; virtual Category getCategory() const override { return Category::FUNCTION; } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(VariableDeclaration const& _varDecl); + explicit FunctionType(EventDefinition const& _event); FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, Location _location = Location::INTERNAL): FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index f29767075..b51492e3e 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1955,6 +1955,57 @@ BOOST_AUTO_TEST_CASE(super_in_constructor) BOOST_CHECK(callContractFunction("f()") == encodeArgs(1 | 2 | 4 | 8)); } +BOOST_AUTO_TEST_CASE(event) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(address indexed _from, hash indexed _id, uint _value); + function deposit(hash _id, bool _manually) { + if (_manually) { + hash s = 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20; + log3(msg.value, s, hash32(msg.sender), _id); + } else + Deposit(hash32(msg.sender), _id, msg.value); + } + } + )"; + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + for (bool manually: {true, false}) + { + callContractFunctionWithValue("deposit(hash256,bool)", value, id, manually); + 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::sha3(string("Deposit(address,hash256,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender)); + BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id)); + } +} + +BOOST_AUTO_TEST_CASE(event_lots_of_data) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(address _from, hash _id, uint _value, bool _flag); + function deposit(hash _id) { + Deposit(msg.sender, hash32(_id), msg.value, true); + } + } + )"; + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(hash256)", value, id); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(m_sender, id, value, true)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,hash256,uint256,bool)"))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 07f2d638a..88f761245 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -680,6 +680,49 @@ BOOST_AUTO_TEST_CASE(private_state_variable) BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist"); } +BOOST_AUTO_TEST_CASE(event) +{ + char const* text = R"( + contract c { + event e(uint indexed a, string3 indexed s, bool indexed b); + function f() { e(2, "abc", true); } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(event_too_many_indexed) +{ + char const* text = R"( + contract c { + event e(uint indexed a, string3 indexed b, bool indexed c, uint indexed d); + function f() { e(2, "abc", true); } + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(event_call) +{ + char const* text = R"( + contract c { + event e(uint a, string3 indexed s, bool indexed b); + function f() { e(2, "abc", true); } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(event_inheritance) +{ + char const* text = R"( + contract base { + event e(uint a, string3 indexed s, bool indexed b); + } + contract c is base { + function f() { e(2, "abc", true); } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 7bfb4c0c8..745df98d4 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -586,6 +586,33 @@ BOOST_AUTO_TEST_CASE(modifier_invocation) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(event) +{ + char const* text = R"( + contract c { + event e(); + })"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(event_arguments) +{ + char const* text = R"( + contract c { + event e(uint a, string32 s); + })"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(event_arguments_indexed) +{ + char const* text = R"( + contract c { + event e(uint a, string32 indexed s, bool indexed b); + })"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 7dad9ad44..5a6935365 100644 --- a/test/solidityExecutionFramework.h +++ b/test/solidityExecutionFramework.h @@ -67,7 +67,6 @@ public: bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) { - FixedHash<4> hash(dev::sha3(_sig)); sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); return m_output;