diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 107482d03..d14cbf5c6 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -173,7 +173,15 @@ void VariableDefinition::checkTypeRequirements() { // no type declared and no previous assignment, infer the type m_value->checkTypeRequirements(); - m_variable->setType(m_value->getType()); + TypePointer type = m_value->getType(); + if (type->getCategory() == Type::Category::INTEGER_CONSTANT) + { + auto intType = dynamic_pointer_cast(type)->getIntegerType(); + if (!intType) + BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString())); + type = intType; + } + m_variable->setType(type); } } } @@ -195,13 +203,19 @@ void Assignment::checkTypeRequirements() TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), m_rightHandSide->getType()); if (!resultType || *resultType != *m_type) - BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); + BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_assigmentOperator)) + + " not compatible with types " + + m_type->toString() + " and " + + m_rightHandSide->getType()->toString())); } } void ExpressionStatement::checkTypeRequirements() { m_expression->checkTypeRequirements(); + if (m_expression->getType()->getCategory() == Type::Category::INTEGER_CONSTANT) + if (!dynamic_pointer_cast(m_expression->getType())->getIntegerType()) + BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant.")); } void Expression::expectType(Type const& _expectedType) @@ -387,7 +401,7 @@ void Literal::checkTypeRequirements() { m_type = Type::forLiteral(*this); if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Literal value too large.")); + BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); } } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index aa7406132..9ea4c28a3 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -71,12 +71,20 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) return false; } -void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation) +bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) { //@todo type checking and creating code for an operator should be in the same place: // the operator should know how to convert itself and to which types it applies, so // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that // represents the operator + if (_unaryOperation.getType()->getCategory() == Type::Category::INTEGER_CONSTANT) + { + m_context << _unaryOperation.getType()->literalValue(nullptr); + return false; + } + + _unaryOperation.getSubExpression().accept(*this); + switch (_unaryOperation.getOperator()) { case Token::NOT: // ! @@ -128,6 +136,7 @@ void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " + string(Token::toString(_unaryOperation.getOperator())))); } + return false; } bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) @@ -139,17 +148,19 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) if (op == Token::AND || op == Token::OR) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); + else if (commonType.getCategory() == Type::Category::INTEGER_CONSTANT) + m_context << commonType.literalValue(nullptr); else { - bool cleanupNeeded = false; - if (commonType.getCategory() == Type::Category::INTEGER) - if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD) - cleanupNeeded = true; + bool cleanupNeeded = commonType.getCategory() == Type::Category::INTEGER && + (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD); // for commutative operators, push the literal as late as possible to allow improved optimization - //@todo this has to be extended for literal expressions - bool swap = (m_optimize && Token::isCommutativeOp(op) && dynamic_cast(&rightExpression) - && !dynamic_cast(&leftExpression)); + auto isLiteral = [](Expression const& _e) + { + return dynamic_cast(&_e) || _e.getType()->getCategory() == Type::Category::INTEGER_CONSTANT; + }; + bool swap = m_optimize && Token::isCommutativeOp(op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) { leftExpression.accept(*this); @@ -423,10 +434,10 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { switch (_literal.getType()->getCategory()) { - case Type::Category::INTEGER: + case Type::Category::INTEGER_CONSTANT: case Type::Category::BOOL: case Type::Category::STRING: - m_context << _literal.getType()->literalValue(_literal); + m_context << _literal.getType()->literalValue(&_literal); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now.")); @@ -562,9 +573,16 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con if (_typeOnStack == _targetType && !_cleanupNeeded) return; - if (_typeOnStack.getCategory() == Type::Category::INTEGER) + Type::Category stackTypeCategory = _typeOnStack.getCategory(); + Type::Category targetTypeCategory = _targetType.getCategory(); + if (stackTypeCategory == Type::Category::INTEGER) + { + solAssert(targetTypeCategory == Type::Category::INTEGER || targetTypeCategory == Type::Category::CONTRACT, ""); appendHighBitsCleanup(dynamic_cast(_typeOnStack)); - else if (_typeOnStack.getCategory() == Type::Category::STRING) + } + else if (stackTypeCategory == Type::Category::INTEGER_CONSTANT) + solAssert(targetTypeCategory == Type::Category::INTEGER || targetTypeCategory == Type::Category::CONTRACT, ""); + else if (stackTypeCategory == Type::Category::STRING) { // nothing to do, strings are high-order-bit-aligned //@todo clear lower-order bytes if we allow explicit conversion to shorter strings diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index c0ee4ab48..c021aaff4 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -57,7 +57,7 @@ private: m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {} virtual bool visit(Assignment const& _assignment) override; - virtual void endVisit(UnaryOperation const& _unaryOperation) override; + virtual bool visit(UnaryOperation const& _unaryOperation) override; virtual bool visit(BinaryOperation const& _binaryOperation) override; virtual bool visit(FunctionCall const& _functionCall) override; virtual bool visit(NewExpression const& _newExpression) override; diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 6433b5262..6e3d04bc5 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -455,7 +455,7 @@ void Scanner::scanToken() token = Token::ADD; break; case '-': - // - -- -= Number + // - -- -= advance(); if (m_char == '-') { @@ -464,8 +464,6 @@ void Scanner::scanToken() } else if (m_char == '=') token = selectToken(Token::ASSIGN_SUB); - else if (m_char == '.' || isDecimalDigit(m_char)) - token = scanNumber('-'); else token = Token::SUB; break; @@ -650,8 +648,7 @@ Token::Value Scanner::scanNumber(char _charSeen) } else { - if (_charSeen == '-') - addLiteralChar('-'); + solAssert(_charSeen == 0, ""); // if the first character is '0' we must check for octals and hex if (m_char == '0') { diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 1e6936e61..c29b98753 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -91,7 +91,7 @@ shared_ptr Type::forLiteral(Literal const& _literal) case Token::FALSE_LITERAL: return make_shared(); case Token::NUMBER: - return IntegerType::smallestTypeForLiteral(_literal.getValue()); + return IntegerConstantType::fromLiteral(_literal.getValue()); case Token::STRING_LITERAL: //@todo put larger strings into dynamic strings return StaticStringType::smallestTypeForLiteral(_literal.getValue()); @@ -112,19 +112,6 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) const MemberList Type::EmptyMemberList = MemberList(); -shared_ptr IntegerType::smallestTypeForLiteral(string const& _literal) -{ - bigint value(_literal); - bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-'); - if (isSigned) - // convert to positive number of same bit requirements - value = ((-value) - 1) << 1; - unsigned bytes = max(bytesRequired(value), 1u); - if (bytes > 32) - return shared_ptr(); - return make_shared(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED); -} - IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) { @@ -194,15 +181,9 @@ string IntegerType::toString() const return prefix + dev::toString(m_bits); } -u256 IntegerType::literalValue(Literal const& _literal) const -{ - bigint value(_literal.getValue()); - return u256(value); -} - TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (getCategory() != _other->getCategory()) + if (_other->getCategory() != Category::INTEGER_CONSTANT && _other->getCategory() != getCategory()) return TypePointer(); auto commonType = dynamic_pointer_cast(Type::commonType(shared_from_this(), _other)); @@ -236,6 +217,141 @@ const MemberList IntegerType::AddressMemberList = make_shared(TypePointers({make_shared(256)}), TypePointers(), FunctionType::Location::SEND)}}); +shared_ptr IntegerConstantType::fromLiteral(string const& _literal) +{ + return make_shared(bigint(_literal)); +} + +bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + TypePointer integerType = getIntegerType(); + return integerType && integerType->isImplicitlyConvertibleTo(_convertTo); +} + +bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + TypePointer integerType = getIntegerType(); + return integerType && integerType->isExplicitlyConvertibleTo(_convertTo); +} + +TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) const +{ + bigint value; + switch (_operator) + { + case Token::BIT_NOT: + value = ~m_value; + break; + case Token::ADD: + value = m_value; + break; + case Token::SUB: + value = -m_value; + break; + default: + return TypePointer(); + } + return make_shared(value); +} + +TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + if (_other->getCategory() == Category::INTEGER) + { + shared_ptr integerType = getIntegerType(); + if (!integerType) + return TypePointer(); + return integerType->binaryOperatorResult(_operator, _other); + } + else if (_other->getCategory() != getCategory()) + return TypePointer(); + + IntegerConstantType const& other = dynamic_cast(*_other); + if (Token::isCompareOp(_operator)) + { + shared_ptr thisIntegerType = getIntegerType(); + shared_ptr otherIntegerType = other.getIntegerType(); + if (!thisIntegerType || !otherIntegerType) + return TypePointer(); + return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); + } + else + { + bigint value; + switch (_operator) + { + case Token::BIT_OR: + value = m_value | other.m_value; + break; + case Token::BIT_XOR: + value = m_value ^ other.m_value; + break; + case Token::BIT_AND: + value = m_value & other.m_value; + break; + case Token::ADD: + value = m_value + other.m_value; + break; + case Token::SUB: + value = m_value - other.m_value; + break; + case Token::MUL: + value = m_value * other.m_value; + break; + case Token::DIV: + if (other.m_value == 0) + return TypePointer(); + value = m_value / other.m_value; + break; + case Token::MOD: + if (other.m_value == 0) + return TypePointer(); + value = m_value % other.m_value; + break; + default: + return TypePointer(); + } + return make_shared(value); + } +} + +bool IntegerConstantType::operator==(Type const& _other) const +{ + if (_other.getCategory() != getCategory()) + return false; + return m_value == dynamic_cast(_other).m_value; +} + +string IntegerConstantType::toString() const +{ + return "int_const " + m_value.str(); +} + +u256 IntegerConstantType::literalValue(Literal const*) const +{ + // we ignore the literal and hope that the type was correctly determined + solAssert(m_value <= u256(-1), "Integer constant too large."); + solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small."); + if (m_value >= 0) + return u256(m_value); + else + return s2u(s256(m_value)); +} + +shared_ptr IntegerConstantType::getIntegerType() const +{ + bigint value = m_value; + bool negative = (value < 0); + if (negative) // convert to positive number of same bit requirements + value = ((-value) - 1) << 1; + if (value > u256(-1)) + return shared_ptr(); + else + return make_shared(max(bytesRequired(value), 1u) * 8, + negative ? IntegerType::Modifier::SIGNED + : IntegerType::Modifier::UNSIGNED); +} + shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) { if (_literal.length() <= 32) @@ -265,12 +381,13 @@ bool StaticStringType::operator==(Type const& _other) const return other.m_bytes == m_bytes; } -u256 StaticStringType::literalValue(const Literal& _literal) const +u256 StaticStringType::literalValue(const Literal* _literal) const { + solAssert(_literal, ""); u256 value = 0; - for (char c: _literal.getValue()) + for (char c: _literal->getValue()) value = (value << 8) | byte(c); - return value << ((32 - _literal.getValue().length()) * 8); + return value << ((32 - _literal->getValue().length()) * 8); } bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -286,11 +403,12 @@ bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const return isImplicitlyConvertibleTo(_convertTo); } -u256 BoolType::literalValue(Literal const& _literal) const +u256 BoolType::literalValue(Literal const* _literal) const { - if (_literal.getToken() == Token::TRUE_LITERAL) + solAssert(_literal, ""); + if (_literal->getToken() == Token::TRUE_LITERAL) return u256(1); - else if (_literal.getToken() == Token::FALSE_LITERAL) + else if (_literal->getToken() == Token::FALSE_LITERAL) return u256(0); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 35987be08..918a915cc 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -75,7 +75,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this smallestTypeForLiteral(std::string const& _literal); - explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; @@ -177,7 +173,6 @@ public: virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; } virtual std::string toString() const override; - virtual u256 literalValue(Literal const& _literal) const override; int getNumBits() const { return m_bits; } bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; } @@ -190,6 +185,40 @@ private: static const MemberList AddressMemberList; }; +/** + * Integer constants either literals or computed. Example expressions: 2, 2+10, ~10. + * There is one distinct type per value. + */ +class IntegerConstantType: public Type +{ +public: + virtual Category getCategory() const override { return Category::INTEGER_CONSTANT; } + + static std::shared_ptr fromLiteral(std::string const& _literal); + + explicit IntegerConstantType(bigint _value): m_value(_value) {} + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual bool operator==(Type const& _other) const override; + + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned getSizeOnStack() const override { return 1; } + + virtual std::string toString() const override; + virtual u256 literalValue(Literal const* _literal) const override; + + /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. + std::shared_ptr getIntegerType() const; + +private: + bigint m_value; +}; + /** * String type with fixed length, up to 32 bytes. */ @@ -211,7 +240,7 @@ public: virtual bool isValueType() const override { return true; } virtual std::string toString() const override { return "string" + dev::toString(m_bytes); } - virtual u256 literalValue(Literal const& _literal) const override; + virtual u256 literalValue(Literal const* _literal) const override; int getNumBytes() const { return m_bytes; } @@ -238,7 +267,7 @@ public: virtual bool isValueType() const override { return true; } virtual std::string toString() const override { return "bool"; } - virtual u256 literalValue(Literal const& _literal) const override; + virtual u256 literalValue(Literal const* _literal) const override; }; /** diff --git a/test/SolidityCompiler.cpp b/test/SolidityCompiler.cpp index 385a3e577..2345db64a 100644 --- a/test/SolidityCompiler.cpp +++ b/test/SolidityCompiler.cpp @@ -135,10 +135,10 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers) byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::PUSH1), 0x0, byte(Instruction::PUSH1), 0x0, // initialized e and h - byte(Instruction::PUSH1), byte(0x2a + 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(0x21 + shift), // ret address + byte(Instruction::PUSH1), 0x1, + byte(Instruction::PUSH1), 0x2, + byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), byte(0x1 + shift), // stack here: ret e h 0x20 1 2 3 0x1 byte(Instruction::JUMP), diff --git a/test/SolidityExpressionCompiler.cpp b/test/SolidityExpressionCompiler.cpp index 9c375418e..579af5bb9 100644 --- a/test/SolidityExpressionCompiler.cpp +++ b/test/SolidityExpressionCompiler.cpp @@ -174,8 +174,8 @@ BOOST_AUTO_TEST_CASE(comparison) bytes code = compileFirstExpression(sourceCode); 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::PUSH2), 0x10, 0xaa, byte(eth::Instruction::PUSH2), 0xff, 0xff, byte(eth::Instruction::AND), + byte(eth::Instruction::PUSH2), 0x11, 0xaa, + byte(eth::Instruction::PUSH2), 0x10, 0xaa, byte(eth::Instruction::LT), byte(eth::Instruction::EQ), byte(eth::Instruction::ISZERO)}); @@ -189,20 +189,18 @@ BOOST_AUTO_TEST_CASE(short_circuiting) "}\n"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(eth::Instruction::PUSH1), 0xa, - byte(eth::Instruction::PUSH1), 0x8, - 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), + bytes expectation({byte(eth::Instruction::PUSH1), 0x12, // 8 + 10 + byte(eth::Instruction::PUSH1), 0x4, byte(eth::Instruction::GT), - byte(eth::Instruction::ISZERO), // after this we have 10 + 8 >= 4 + byte(eth::Instruction::ISZERO), // after this we have 4 <= 8 + 10 byte(eth::Instruction::DUP1), - byte(eth::Instruction::PUSH1), 0x20, + byte(eth::Instruction::PUSH1), 0x11, byte(eth::Instruction::JUMPI), // short-circuit if it is true byte(eth::Instruction::POP), - 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::PUSH1), 0x2, + byte(eth::Instruction::PUSH1), 0x9, byte(eth::Instruction::EQ), - byte(eth::Instruction::ISZERO), // after this we have 2 != 9 + byte(eth::Instruction::ISZERO), // after this we have 9 != 2 byte(eth::Instruction::JUMPDEST), byte(eth::Instruction::PUSH1), 0x1, byte(eth::Instruction::EQ), @@ -213,28 +211,24 @@ BOOST_AUTO_TEST_CASE(short_circuiting) BOOST_AUTO_TEST_CASE(arithmetics) { char const* sourceCode = "contract test {\n" - " function f() { var x = ((((((((9 ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }" + " function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }" "}\n"; - bytes code = compileFirstExpression(sourceCode); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); 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, byte(eth::Instruction::PUSH1), 0x7, byte(eth::Instruction::PUSH1), 0x8, - byte(eth::Instruction::PUSH1), 0x9, + byte(eth::Instruction::DUP10), byte(eth::Instruction::XOR), byte(eth::Instruction::AND), byte(eth::Instruction::OR), byte(eth::Instruction::SUB), byte(eth::Instruction::ADD), - byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::MOD), - byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), byte(eth::Instruction::DIV), byte(eth::Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); @@ -243,15 +237,15 @@ BOOST_AUTO_TEST_CASE(arithmetics) BOOST_AUTO_TEST_CASE(unary_operators) { char const* sourceCode = "contract test {\n" - " function f() { var x = !(~+- 1 == 2); }" + " function f(int y) { var x = !(~+- y == 2); }" "}\n"; - bytes code = compileFirstExpression(sourceCode); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); - bytes expectation({byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), - byte(eth::Instruction::PUSH1), 0x1, + bytes expectation({byte(eth::Instruction::PUSH1), 0x2, + byte(eth::Instruction::DUP3), 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::NOT), byte(eth::Instruction::EQ), byte(eth::Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); @@ -315,7 +309,7 @@ BOOST_AUTO_TEST_CASE(assignment) bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - bytes expectation({byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0xff, byte(eth::Instruction::AND), + bytes expectation({byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::DUP2), byte(eth::Instruction::DUP4), byte(eth::Instruction::ADD), @@ -338,14 +332,14 @@ BOOST_AUTO_TEST_CASE(function_call) {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - 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), + bytes expectation({byte(eth::Instruction::PUSH1), 0x02, + byte(eth::Instruction::PUSH1), 0x0c, + byte(eth::Instruction::PUSH1), 0x01, byte(eth::Instruction::DUP5), byte(eth::Instruction::ADD), // Stack here: a b 2 (a+1) byte(eth::Instruction::DUP4), - byte(eth::Instruction::PUSH1), 0x19, + byte(eth::Instruction::PUSH1), 0x13, byte(eth::Instruction::JUMP), byte(eth::Instruction::JUMPDEST), // Stack here: a b 2 g(a+1, b) @@ -363,40 +357,36 @@ BOOST_AUTO_TEST_CASE(function_call) BOOST_AUTO_TEST_CASE(negative_literals_8bits) { - // these all fit in 8 bits char const* sourceCode = "contract test {\n" - " function f() { int8 x = -0 + -1 + -0x01 + -127 + -128; }\n" + " function f() { int8 x = -0x80; }\n" "}\n"; bytes code = compileFirstExpression(sourceCode); - 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)})); + bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80)); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } BOOST_AUTO_TEST_CASE(negative_literals_16bits) { - // -1 should need 8 bits, -129 should need 16 bits, how many bits are used is visible - // from the SIGNEXTEND opcodes char const* sourceCode = "contract test {\n" - " function f() { int64 x = int64(-1 + -129); }\n" + " function f() { int64 x = ~0xabc; }\n" + "}\n"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + +BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals) +{ + // first literal itself is too large for 256 bits but it fits after all constant operations + // have been applied + char const* sourceCode = "contract test {\n" + " function f() { var x = (0xffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; }\n" "}\n"; bytes code = compileFirstExpression(sourceCode); - 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::ADD), - byte(eth::Instruction::PUSH1), 0x01, - byte(eth::Instruction::SIGNEXTEND)})); + bytes expectation(bytes({byte(eth::Instruction::PUSH1), 0xbf})); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index 7e06c2e30..094702662 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(large_integers) b = 0x10000000000000000000000002; } })"; - compileBothVersions(33, sourceCode); + compileBothVersions(11, sourceCode); compareVersions(0); } @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(invariants) return int(0) | (int(1) * (int(0) ^ (0 + a))); } })"; - compileBothVersions(28, sourceCode); + compileBothVersions(16, sourceCode); compareVersions(0, u256(0x12334664)); } @@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE(unused_expressions) data; } })"; - compileBothVersions(11, sourceCode); + compileBothVersions(8, sourceCode); compareVersions(0); } @@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(constant_folding_both_sides) return 98 ^ (7 * ((1 | (x | 1000)) * 40) ^ 102); } })"; - compileBothVersions(31, sourceCode); + compileBothVersions(12, sourceCode); compareVersions(0); } diff --git a/test/SolidityScanner.cpp b/test/SolidityScanner.cpp index b7942d293..7dc9ef482 100644 --- a/test/SolidityScanner.cpp +++ b/test/SolidityScanner.cpp @@ -103,14 +103,17 @@ BOOST_AUTO_TEST_CASE(negative_numbers) BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::VAR); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "-.2"); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ".2"); BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "-0x78"); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "0x78"); BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "-7.3"); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "7.3"); BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "8.9"); @@ -130,8 +133,9 @@ BOOST_AUTO_TEST_CASE(locations) BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 24); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 25); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 26); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 27); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 32); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 45);