From 6ec55d3d5f6b3924516c91fb76d299fe14841314 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 4 Nov 2014 21:29:36 +0100 Subject: [PATCH] Type promotion fixes and tests. --- libsolidity/ExpressionCompiler.cpp | 16 ++++------- libsolidity/ExpressionCompiler.h | 4 ++- test/solidityCompiler.cpp | 8 +++--- test/solidityEndToEndTest.cpp | 42 ++++++++++++++++++++++++++++- test/solidityExpressionCompiler.cpp | 33 ++++++++++++----------- 5 files changed, 71 insertions(+), 32 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 0df741843..324cd10d0 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -136,15 +136,9 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) cleanupNeeded = true; leftExpression.accept(*this); - if (cleanupNeeded) - appendHighBitsCleanup(dynamic_cast(*leftExpression.getType())); - else - appendTypeConversion(*leftExpression.getType(), commonType); + appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); rightExpression.accept(*this); - if (cleanupNeeded) - appendHighBitsCleanup(dynamic_cast(*leftExpression.getType())); - else - appendTypeConversion(*rightExpression.getType(), commonType); + appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); if (Token::isCompareOp(op)) appendCompareOperatorCode(op, commonType); else @@ -368,20 +362,20 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) } } -void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType) +void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded) { // If the type of one of the operands is extended, we need to remove all // higher-order bits that we might have ignored in previous operations. // @todo: store in the AST whether the operand might have "dirty" higher // order bits - if (_typeOnStack == _targetType) + if (_typeOnStack == _targetType && !_cleanupNeeded) return; if (_typeOnStack.getCategory() == Type::Category::INTEGER) { appendHighBitsCleanup(dynamic_cast(_typeOnStack)); } - else + else if (_typeOnStack != _targetType) { // All other types should not be convertible to non-equal types. assert(!_typeOnStack.isExplicitlyConvertibleTo(_targetType)); diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 7c731ec7f..d67814be0 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -66,7 +66,9 @@ private: /// Appends an implicit or explicit type conversion. For now this comprises only erasing /// higher-order bits (@see appendHighBitCleanup) when widening integer types. - void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType); + /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be + /// necessary. + void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); //// Appends code that cleans higher-order bits for integer types. void appendHighBitsCleanup(IntegerType const& _typeOnStack); diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 69d331339..192fd61a4 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -119,10 +119,10 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers) byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::PUSH1), 0x0, byte(Instruction::DUP1), // initialized e and h - byte(Instruction::PUSH1), byte(0x20 + shift), // ret address - byte(Instruction::PUSH1), 0x1, - byte(Instruction::PUSH1), 0x2, - byte(Instruction::PUSH1), 0x3, + byte(Instruction::PUSH1), 0x29 + shift, // ret address + byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), + byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), + byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), byte(0x1 + shift), // stack here: ret e h 0x20 1 2 3 0x1 byte(Instruction::JUMP), diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index b4da53ba6..c60d6887e 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -192,6 +192,21 @@ BOOST_AUTO_TEST_CASE(many_local_variables) == toBigEndian(u256(0x121121))); } +BOOST_AUTO_TEST_CASE(packing_unpacking_types) +{ + char const* sourceCode = "contract test {\n" + " function run(bool a, uint32 b, uint64 c) returns(uint256 y) {\n" + " if (a) y = 1;\n" + " y = y * 0x100000000 | ~b;\n" + " y = y * 0x10000000000000000 | ~c;\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, fromHex("01""0f0f0f0f""f0f0f0f0f0f0f0f0")) + == fromHex("00000000000000000000000000000000000000""01""f0f0f0f0""0f0f0f0f0f0f0f0f")); +} + BOOST_AUTO_TEST_CASE(multiple_return_values) { char const* sourceCode = "contract test {\n" @@ -244,7 +259,32 @@ BOOST_AUTO_TEST_CASE(sign_extension) "}\n"; ExecutionFramework framework; framework.compileAndRun(sourceCode); - BOOST_CHECK(framework.callFunction(0, bytes()) == bytes(32, 0xff)); + BOOST_CHECK(framework.callFunction(0, bytes()) == toBigEndian(u256(0xff))); +} + +BOOST_AUTO_TEST_CASE(small_unsigned_types) +{ + char const* sourceCode = "contract test {\n" + " function run() returns(uint256 y) {\n" + " uint32 x = uint32(0xffffff) * 0xffffff;\n" + " return x / 0x100;" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, bytes()) == toBigEndian(u256(0xfffe0000))); +} + +BOOST_AUTO_TEST_CASE(small_signed_types) +{ + char const* sourceCode = "contract test {\n" + " function run() returns(int256 y) {\n" + " return -int32(10) * -int64(20);\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, bytes()) == toBigEndian(u256(200))); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index 561cc3bda..3be909c32 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -154,8 +154,8 @@ BOOST_AUTO_TEST_CASE(comparison) "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(eth::Instruction::PUSH2), 0x10, 0xaa, - byte(eth::Instruction::PUSH2), 0x11, 0xaa, + bytes expectation({byte(eth::Instruction::PUSH2), 0x10, 0xaa, byte(eth::Instruction::PUSH2), 0xff, 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH2), 0x11, 0xaa, byte(eth::Instruction::PUSH2), 0xff, 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::GT), byte(eth::Instruction::PUSH1), 0x1, byte(eth::Instruction::EQ), @@ -172,16 +172,16 @@ BOOST_AUTO_TEST_CASE(short_circuiting) bytes expectation({byte(eth::Instruction::PUSH1), 0xa, byte(eth::Instruction::PUSH1), 0x8, - byte(eth::Instruction::ADD), - byte(eth::Instruction::PUSH1), 0x4, + byte(eth::Instruction::ADD), byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::GT), byte(eth::Instruction::ISZERO), // after this we have 10 + 8 >= 4 byte(eth::Instruction::DUP1), - byte(eth::Instruction::PUSH1), 0x14, + byte(eth::Instruction::PUSH1), 0x20, byte(eth::Instruction::JUMPI), // short-circuit if it is true byte(eth::Instruction::POP), - byte(eth::Instruction::PUSH1), 0x2, - byte(eth::Instruction::PUSH1), 0x9, + byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH1), 0x9, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::EQ), byte(eth::Instruction::ISZERO), // after this we have 2 != 9 byte(eth::Instruction::JUMPDEST), @@ -197,10 +197,11 @@ BOOST_AUTO_TEST_CASE(arithmetics) " function f() { var x = (1 * (2 / (3 % (4 + (5 - (6 | (7 & (8 ^ 9)))))))); }" "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(eth::Instruction::PUSH1), 0x1, byte(eth::Instruction::PUSH1), 0x2, + byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::PUSH1), 0x3, + byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::PUSH1), 0x5, byte(eth::Instruction::PUSH1), 0x6, @@ -213,8 +214,10 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(eth::Instruction::SWAP1), byte(eth::Instruction::SUB), byte(eth::Instruction::ADD), + byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::SWAP1), byte(eth::Instruction::MOD), + byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::SWAP1), byte(eth::Instruction::DIV), byte(eth::Instruction::MUL)}); @@ -231,8 +234,8 @@ BOOST_AUTO_TEST_CASE(unary_operators) bytes expectation({byte(eth::Instruction::PUSH1), 0x1, byte(eth::Instruction::PUSH1), 0x0, byte(eth::Instruction::SUB), - byte(eth::Instruction::NOT), - byte(eth::Instruction::PUSH1), 0x2, + byte(eth::Instruction::NOT), byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::EQ), byte(eth::Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); @@ -305,7 +308,7 @@ BOOST_AUTO_TEST_CASE(assignment) byte(eth::Instruction::POP), byte(eth::Instruction::DUP2), // Stack here: a+b b a+b - byte(eth::Instruction::PUSH1), 0x2, + byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -320,17 +323,17 @@ BOOST_AUTO_TEST_CASE(function_call) {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - bytes expectation({byte(eth::Instruction::PUSH1), 0x0a, + bytes expectation({byte(eth::Instruction::PUSH1), 0x0d, byte(eth::Instruction::DUP3), - byte(eth::Instruction::PUSH1), 0x01, + byte(eth::Instruction::PUSH1), 0x01, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::ADD), // Stack here: a b (a+1) byte(eth::Instruction::DUP3), - byte(eth::Instruction::PUSH1), 0x14, + byte(eth::Instruction::PUSH1), 0x1a, byte(eth::Instruction::JUMP), byte(eth::Instruction::JUMPDEST), // Stack here: a b g(a+1, b) - byte(eth::Instruction::PUSH1), 0x02, + byte(eth::Instruction::PUSH1), 0x02, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::MUL), // Stack here: a b g(a+1, b)*2 byte(eth::Instruction::DUP3),