diff --git a/libsolidity/AST.h b/libsolidity/AST.h index f0d6cb331..fb046f199 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -404,7 +404,6 @@ public: 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 diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 5d44c86f3..2b2990117 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 have length 32."); + 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 length in memory has to be 32."); // @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 = _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..18c3817ac 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 _memoryOffset = 0); + 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/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index f29767075..0df2d9146 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, hash(msg.sender), _id); + } else + Deposit(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, _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/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;