From 11f65d50b8b1c5eab725195581a34f8e4f6ca656 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 6 Nov 2014 22:39:02 +0100 Subject: [PATCH] Evaluate expressions in convenient order. --- libsolidity/ExpressionCompiler.cpp | 51 ++++++++------------ test/solidityExpressionCompiler.cpp | 73 ++++++++++++++--------------- 2 files changed, 53 insertions(+), 71 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 6efb8b20c..015b9ab94 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -48,21 +48,15 @@ bool ExpressionCompiler::visit(Assignment& _assignment) { m_currentLValue = nullptr; - Expression& rightHandSide = _assignment.getRightHandSide(); - rightHandSide.accept(*this); - Type const& resultType = *_assignment.getType(); - appendTypeConversion(*rightHandSide.getType(), resultType); + _assignment.getRightHandSide().accept(*this); + appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); _assignment.getLeftHandSide().accept(*this); Token::Value op = _assignment.getAssignmentOperator(); - if (op != Token::ASSIGN) - { - // compound assignment - m_context << eth::Instruction::SWAP1; - appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType); - } + if (op != Token::ASSIGN) // compound assignment + appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); else - m_context << eth::Instruction::POP; //@todo do not retrieve the value in the first place + m_context << eth::Instruction::POP; storeInLValue(_assignment); return false; @@ -123,11 +117,8 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) Type const& commonType = _binaryOperation.getCommonType(); Token::Value const op = _binaryOperation.getOperator(); - if (op == Token::AND || op == Token::OR) - { - // special case: short-circuiting + if (op == Token::AND || op == Token::OR) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); - } else { bool cleanupNeeded = false; @@ -135,10 +126,10 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD) cleanupNeeded = true; - leftExpression.accept(*this); - appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); rightExpression.accept(*this); appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); + leftExpression.accept(*this); + appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); if (Token::isCompareOp(op)) appendCompareOperatorCode(op, commonType); else @@ -175,8 +166,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) for (unsigned i = 0; i < arguments.size(); ++i) { arguments[i]->accept(*this); - appendTypeConversion(*arguments[i]->getType(), - *function.getParameters()[i]->getType()); + appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType()); } m_context.appendJumpTo(m_context.getFunctionEntryLabel(function)); @@ -267,23 +257,21 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type IntegerType const& type = dynamic_cast(_type); bool const isSigned = type.isSigned(); - // note that EVM opcodes compare like "stack[0] < stack[1]", - // but our left value is at stack[1], so everyhing is reversed. switch (_operator) { case Token::GTE: - m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT) + m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT) << eth::Instruction::ISZERO; break; case Token::LTE: - m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT) + m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT) << eth::Instruction::ISZERO; break; case Token::GT: - m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT); + m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT); break; case Token::LT: - m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT); + m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator.")); @@ -314,16 +302,16 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty m_context << eth::Instruction::ADD; break; case Token::SUB: - m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; + m_context << eth::Instruction::SUB; break; case Token::MUL: m_context << eth::Instruction::MUL; break; case Token::DIV: - m_context << eth::Instruction::SWAP1 << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV); + m_context << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV); break; case Token::MOD: - m_context << eth::Instruction::SWAP1 << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); + m_context << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator.")); @@ -364,10 +352,9 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) 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 + // For a type extension, 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 && !_cleanupNeeded) return; diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index 8fc4a1a22..59a9e9336 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -154,10 +154,10 @@ BOOST_AUTO_TEST_CASE(comparison) "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(eth::Instruction::PUSH2), 0x10, 0xaa, byte(eth::Instruction::PUSH2), 0xff, 0xff, byte(eth::Instruction::AND), + bytes expectation({byte(eth::Instruction::PUSH1), 0x1, 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::PUSH2), 0x10, 0xaa, byte(eth::Instruction::PUSH2), 0xff, 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::LT), byte(eth::Instruction::EQ), byte(eth::Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); @@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE(comparison) BOOST_AUTO_TEST_CASE(short_circuiting) { char const* sourceCode = "contract test {\n" - " function f() { var x = (10 + 8 >= 4 || 2 != 9) != true; }" + " function f() { var x = true != (4 <= 8 + 10 || 9 != 2); }" "}\n"; bytes code = compileFirstExpression(sourceCode); @@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(short_circuiting) BOOST_AUTO_TEST_CASE(arithmetics) { char const* sourceCode = "contract test {\n" - " function f() { var x = (1 * (2 / (3 % (4 + (5 - (6 | (7 & (8 ^ 9)))))))); }" + " function f() { var x = ((((((((9 ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }" "}\n"; bytes code = compileFirstExpression(sourceCode); bytes expectation({byte(eth::Instruction::PUSH1), 0x1, @@ -211,14 +211,11 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(eth::Instruction::XOR), byte(eth::Instruction::AND), byte(eth::Instruction::OR), - 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)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); @@ -231,11 +228,11 @@ BOOST_AUTO_TEST_CASE(unary_operators) "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(eth::Instruction::PUSH1), 0x1, + bytes expectation({byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH1), 0x1, byte(eth::Instruction::PUSH1), 0x0, byte(eth::Instruction::SUB), 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()); @@ -244,7 +241,7 @@ BOOST_AUTO_TEST_CASE(unary_operators) BOOST_AUTO_TEST_CASE(unary_inc_dec) { char const* sourceCode = "contract test {\n" - " function f(uint a) { var x = ((a++ ^ ++a) ^ a--) ^ --a; }" + " function f(uint a) { var x = --a ^ (a-- ^ (++a ^ a++)); }" "}\n"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); @@ -299,16 +296,15 @@ BOOST_AUTO_TEST_CASE(assignment) bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - bytes expectation({byte(eth::Instruction::DUP1), - byte(eth::Instruction::DUP3), - byte(eth::Instruction::SWAP1), + bytes expectation({byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::DUP2), + byte(eth::Instruction::DUP4), byte(eth::Instruction::ADD), - // Stack here: a b a+b - byte(eth::Instruction::SWAP2), + // Stack here: a b 2 a+b + byte(eth::Instruction::SWAP3), byte(eth::Instruction::POP), - byte(eth::Instruction::DUP2), - // Stack here: a+b b a+b - byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::DUP3), + // Stack here: a+b b 2 a+b byte(eth::Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -323,21 +319,20 @@ BOOST_AUTO_TEST_CASE(function_call) {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - bytes expectation({byte(eth::Instruction::PUSH1), 0x0d, - byte(eth::Instruction::DUP3), + bytes expectation({byte(eth::Instruction::PUSH1), 0x02, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH1), 0x12, byte(eth::Instruction::PUSH1), 0x01, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::DUP5), byte(eth::Instruction::ADD), - // Stack here: a b (a+1) - byte(eth::Instruction::DUP3), - byte(eth::Instruction::PUSH1), 0x1a, + // Stack here: a b 2 (a+1) + byte(eth::Instruction::DUP4), + byte(eth::Instruction::PUSH1), 0x19, 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), 0xff, byte(eth::Instruction::AND), + // Stack here: a b 2 g(a+1, b) byte(eth::Instruction::MUL), // Stack here: a b g(a+1, b)*2 byte(eth::Instruction::DUP3), - byte(eth::Instruction::SWAP1), byte(eth::Instruction::ADD), // Stack here: a b a+g(a+1, b)*2 byte(eth::Instruction::SWAP2), @@ -355,15 +350,15 @@ BOOST_AUTO_TEST_CASE(negative_literals_8bits) "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(eth::Instruction::PUSH1), 0x00, - byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + - bytes({byte(eth::Instruction::ADD), - byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + - bytes({byte(eth::Instruction::ADD), - byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x81) + - bytes({byte(eth::Instruction::ADD), - byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80) + - bytes({byte(eth::Instruction::ADD)})); + bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80) + + bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x81) + + bytes({byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + + bytes({byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + + bytes({byte(eth::Instruction::PUSH1), 0x00, + byte(eth::Instruction::ADD), + byte(eth::Instruction::ADD), + byte(eth::Instruction::ADD), + byte(eth::Instruction::ADD)})); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -376,11 +371,11 @@ BOOST_AUTO_TEST_CASE(negative_literals_16bits) "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + + bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x7f) + + bytes({byte(eth::Instruction::PUSH32)}) + bytes(32, 0xff) + bytes({byte(eth::Instruction::PUSH1), 0x00, byte(eth::Instruction::SIGNEXTEND), - byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x7f) + - bytes({byte(eth::Instruction::ADD), + byte(eth::Instruction::ADD), byte(eth::Instruction::PUSH1), 0x01, byte(eth::Instruction::SIGNEXTEND)})); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());