Browse Source

Type promotion fixes and tests.

cl-refactor
Christian 10 years ago
parent
commit
6ec55d3d5f
  1. 16
      libsolidity/ExpressionCompiler.cpp
  2. 4
      libsolidity/ExpressionCompiler.h
  3. 8
      test/solidityCompiler.cpp
  4. 42
      test/solidityEndToEndTest.cpp
  5. 33
      test/solidityExpressionCompiler.cpp

16
libsolidity/ExpressionCompiler.cpp

@ -136,15 +136,9 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
cleanupNeeded = true; cleanupNeeded = true;
leftExpression.accept(*this); leftExpression.accept(*this);
if (cleanupNeeded) appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(*leftExpression.getType()));
else
appendTypeConversion(*leftExpression.getType(), commonType);
rightExpression.accept(*this); rightExpression.accept(*this);
if (cleanupNeeded) appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(*leftExpression.getType()));
else
appendTypeConversion(*rightExpression.getType(), commonType);
if (Token::isCompareOp(op)) if (Token::isCompareOp(op))
appendCompareOperatorCode(op, commonType); appendCompareOperatorCode(op, commonType);
else 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 // 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. // higher-order bits that we might have ignored in previous operations.
// @todo: store in the AST whether the operand might have "dirty" higher // @todo: store in the AST whether the operand might have "dirty" higher
// order bits // order bits
if (_typeOnStack == _targetType) if (_typeOnStack == _targetType && !_cleanupNeeded)
return; return;
if (_typeOnStack.getCategory() == Type::Category::INTEGER) if (_typeOnStack.getCategory() == Type::Category::INTEGER)
{ {
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack)); appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack));
} }
else else if (_typeOnStack != _targetType)
{ {
// All other types should not be convertible to non-equal types. // All other types should not be convertible to non-equal types.
assert(!_typeOnStack.isExplicitlyConvertibleTo(_targetType)); assert(!_typeOnStack.isExplicitlyConvertibleTo(_targetType));

4
libsolidity/ExpressionCompiler.h

@ -66,7 +66,9 @@ private:
/// Appends an implicit or explicit type conversion. For now this comprises only erasing /// Appends an implicit or explicit type conversion. For now this comprises only erasing
/// higher-order bits (@see appendHighBitCleanup) when widening integer types. /// 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. //// Appends code that cleans higher-order bits for integer types.
void appendHighBitsCleanup(IntegerType const& _typeOnStack); void appendHighBitsCleanup(IntegerType const& _typeOnStack);

8
test/solidityCompiler.cpp

@ -119,10 +119,10 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::JUMPDEST), // beginning of g
byte(Instruction::PUSH1), 0x0, byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1), // initialized e and h byte(Instruction::DUP1), // initialized e and h
byte(Instruction::PUSH1), byte(0x20 + shift), // ret address byte(Instruction::PUSH1), 0x29 + shift, // ret address
byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), byte(0x1 + shift), byte(Instruction::PUSH1), byte(0x1 + shift),
// stack here: ret e h 0x20 1 2 3 0x1 // stack here: ret e h 0x20 1 2 3 0x1
byte(Instruction::JUMP), byte(Instruction::JUMP),

42
test/solidityEndToEndTest.cpp

@ -192,6 +192,21 @@ BOOST_AUTO_TEST_CASE(many_local_variables)
== toBigEndian(u256(0x121121))); == 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) BOOST_AUTO_TEST_CASE(multiple_return_values)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"
@ -244,7 +259,32 @@ BOOST_AUTO_TEST_CASE(sign_extension)
"}\n"; "}\n";
ExecutionFramework framework; ExecutionFramework framework;
framework.compileAndRun(sourceCode); 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() BOOST_AUTO_TEST_SUITE_END()

33
test/solidityExpressionCompiler.cpp

@ -154,8 +154,8 @@ BOOST_AUTO_TEST_CASE(comparison)
"}\n"; "}\n";
bytes code = compileFirstExpression(sourceCode); bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH2), 0x10, 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), 0x11, 0xaa, byte(eth::Instruction::PUSH2), 0xff, 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::GT), byte(eth::Instruction::GT),
byte(eth::Instruction::PUSH1), 0x1, byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::EQ), byte(eth::Instruction::EQ),
@ -172,16 +172,16 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
bytes expectation({byte(eth::Instruction::PUSH1), 0xa, bytes expectation({byte(eth::Instruction::PUSH1), 0xa,
byte(eth::Instruction::PUSH1), 0x8, byte(eth::Instruction::PUSH1), 0x8,
byte(eth::Instruction::ADD), byte(eth::Instruction::ADD), byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::GT), byte(eth::Instruction::GT),
byte(eth::Instruction::ISZERO), // after this we have 10 + 8 >= 4 byte(eth::Instruction::ISZERO), // after this we have 10 + 8 >= 4
byte(eth::Instruction::DUP1), 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::JUMPI), // short-circuit if it is true
byte(eth::Instruction::POP), byte(eth::Instruction::POP),
byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::PUSH1), 0x9, byte(eth::Instruction::PUSH1), 0x9, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::EQ), byte(eth::Instruction::EQ),
byte(eth::Instruction::ISZERO), // after this we have 2 != 9 byte(eth::Instruction::ISZERO), // after this we have 2 != 9
byte(eth::Instruction::JUMPDEST), 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)))))))); }" " function f() { var x = (1 * (2 / (3 % (4 + (5 - (6 | (7 & (8 ^ 9)))))))); }"
"}\n"; "}\n";
bytes code = compileFirstExpression(sourceCode); bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH1), 0x1, bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0x2,
byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::PUSH1), 0x3, byte(eth::Instruction::PUSH1), 0x3,
byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::PUSH1), 0x4,
byte(eth::Instruction::PUSH1), 0x5, byte(eth::Instruction::PUSH1), 0x5,
byte(eth::Instruction::PUSH1), 0x6, byte(eth::Instruction::PUSH1), 0x6,
@ -213,8 +214,10 @@ BOOST_AUTO_TEST_CASE(arithmetics)
byte(eth::Instruction::SWAP1), byte(eth::Instruction::SWAP1),
byte(eth::Instruction::SUB), byte(eth::Instruction::SUB),
byte(eth::Instruction::ADD), byte(eth::Instruction::ADD),
byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::SWAP1), byte(eth::Instruction::SWAP1),
byte(eth::Instruction::MOD), byte(eth::Instruction::MOD),
byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::SWAP1), byte(eth::Instruction::SWAP1),
byte(eth::Instruction::DIV), byte(eth::Instruction::DIV),
byte(eth::Instruction::MUL)}); byte(eth::Instruction::MUL)});
@ -231,8 +234,8 @@ BOOST_AUTO_TEST_CASE(unary_operators)
bytes expectation({byte(eth::Instruction::PUSH1), 0x1, bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::PUSH1), 0x0, byte(eth::Instruction::PUSH1), 0x0,
byte(eth::Instruction::SUB), byte(eth::Instruction::SUB),
byte(eth::Instruction::NOT), byte(eth::Instruction::NOT), byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::EQ), byte(eth::Instruction::EQ),
byte(eth::Instruction::ISZERO)}); byte(eth::Instruction::ISZERO)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); 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::POP),
byte(eth::Instruction::DUP2), byte(eth::Instruction::DUP2),
// Stack here: a+b b a+b // 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)}); byte(eth::Instruction::MUL)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); 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"}}); {{"test", "f", "a"}, {"test", "f", "b"}});
// Stack: a, 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::DUP3),
byte(eth::Instruction::PUSH1), 0x01, byte(eth::Instruction::PUSH1), 0x01, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND),
byte(eth::Instruction::ADD), byte(eth::Instruction::ADD),
// Stack here: a b <ret label> (a+1) // Stack here: a b <ret label> (a+1)
byte(eth::Instruction::DUP3), byte(eth::Instruction::DUP3),
byte(eth::Instruction::PUSH1), 0x14, byte(eth::Instruction::PUSH1), 0x1a,
byte(eth::Instruction::JUMP), byte(eth::Instruction::JUMP),
byte(eth::Instruction::JUMPDEST), byte(eth::Instruction::JUMPDEST),
// Stack here: a b g(a+1, b) // 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), byte(eth::Instruction::MUL),
// Stack here: a b g(a+1, b)*2 // Stack here: a b g(a+1, b)*2
byte(eth::Instruction::DUP3), byte(eth::Instruction::DUP3),

Loading…
Cancel
Save