diff options
24 files changed, 526 insertions, 32 deletions
diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 80311a63..353bb61d 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -279,7 +279,7 @@ groupings of expressions. :: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract C { uint[] data; diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index a474f905..0f8b34f8 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -173,7 +173,7 @@ situation. If you do not want to throw, you can return a pair:: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract C { uint[] counters; diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index f5d5f89e..c8a45d30 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -332,14 +332,15 @@ Transactions ============ A transaction is a message that is sent from one account to another -account (which might be the same or the special zero-account, see below). +account (which might be the same or empty, see below). It can include binary data (which is called "payload") and Ether. If the target account contains code, that code is executed and the payload is provided as input data. -If the target account is the zero-account (the account with the -address ``0``), the transaction creates a **new contract**. +If the target account is not set (the transaction does not have +a recipient or the recipient is set to ``null``), the transaction +creates a **new contract**. As already mentioned, the address of that contract is not the zero address but an address derived from the sender and its number of transactions sent (the "nonce"). The payload diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 1bbd42f8..8f58f339 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -389,7 +389,7 @@ high or low invalid bids. :: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract BlindAuction { struct Bid { diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index e410af5c..98136b44 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -25,11 +25,14 @@ #include <libdevcore/Common.h> +#include <boost/optional.hpp> + #include <vector> #include <type_traits> #include <cstring> #include <string> #include <set> +#include <functional> namespace dev { @@ -229,6 +232,36 @@ bool contains(T const& _t, V const& _v) return std::end(_t) != std::find(std::begin(_t), std::end(_t), _v); } + +/// Function that iterates over a vector, calling a function on each of its +/// elements. If that function returns a vector, the element is replaced by +/// the returned vector. During the iteration, the original vector is only valid +/// on the current element and after that. The actual replacement takes +/// place at the end, but already visited elements might be invalidated. +/// If nothing is replaced, no copy is performed. +template <class T> +void iterateReplacing(std::vector<T>& _vector, std::function<boost::optional<std::vector<T>>(T&)> _f) +{ + bool useModified = false; + std::vector<T> modifiedVector; + for (size_t i = 0; i < _vector.size(); ++i) + { + if (boost::optional<std::vector<T>> r = _f(_vector[i])) + { + if (!useModified) + { + std::move(_vector.begin(), _vector.begin() + i, back_inserter(modifiedVector)); + useModified = true; + } + modifiedVector += std::move(*r); + } + else if (useModified) + modifiedVector.emplace_back(std::move(_vector[i])); + } + if (useModified) + _vector = std::move(modifiedVector); +} + /// @returns true iff @a _str passess the hex address checksum test. /// @param _strict if false, hex strings with only uppercase or only lowercase letters /// are considered valid. diff --git a/libjulia/optimiser/ExpressionBreaker.cpp b/libjulia/optimiser/ExpressionBreaker.cpp new file mode 100644 index 00000000..2273fa98 --- /dev/null +++ b/libjulia/optimiser/ExpressionBreaker.cpp @@ -0,0 +1,105 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that turns complex expressions into multiple variable + * declarations. + */ + +#include <libjulia/optimiser/ExpressionBreaker.h> + +#include <libjulia/optimiser/ASTWalker.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::solidity; + +void ExpressionBreaker::operator()(FunctionalInstruction& _instruction) +{ + for (auto& arg: _instruction.arguments | boost::adaptors::reversed) + outlineExpression(arg); +} + +void ExpressionBreaker::operator()(FunctionCall& _funCall) +{ + for (auto& arg: _funCall.arguments | boost::adaptors::reversed) + outlineExpression(arg); +} + +void ExpressionBreaker::operator()(If& _if) +{ + outlineExpression(*_if.condition); + (*this)(_if.body); +} + +void ExpressionBreaker::operator()(Switch& _switch) +{ + outlineExpression(*_switch.expression); + for (auto& _case: _switch.cases) + // Do not visit the case expression, nothing to break there. + (*this)(_case.body); +} + +void ExpressionBreaker::operator()(ForLoop& _loop) +{ + (*this)(_loop.pre); + // Do not visit the condition because we cannot break expressions there. + (*this)(_loop.post); + (*this)(_loop.body); +} + +void ExpressionBreaker::operator()(Block& _block) +{ + vector<Statement> saved; + swap(saved, m_statementsToPrefix); + + function<boost::optional<vector<Statement>>(Statement&)> f = + [&](Statement& _statement) -> boost::optional<vector<Statement>> { + m_statementsToPrefix.clear(); + visit(_statement); + if (m_statementsToPrefix.empty()) + return {}; + m_statementsToPrefix.emplace_back(std::move(_statement)); + return std::move(m_statementsToPrefix); + }; + iterateReplacing(_block.statements, f); + + swap(saved, m_statementsToPrefix); +} + +void ExpressionBreaker::outlineExpression(Expression& _expr) +{ + if (_expr.type() != typeid(FunctionalInstruction) && _expr.type() != typeid(FunctionCall)) + return; + + visit(_expr); + + SourceLocation location = locationOf(_expr); + string var = m_nameDispenser.newName(""); + m_statementsToPrefix.emplace_back(VariableDeclaration{ + location, + {{TypedName{location, var, ""}}}, + make_shared<Expression>(std::move(_expr)) + }); + _expr = Identifier{location, var}; +} diff --git a/libjulia/optimiser/ExpressionBreaker.h b/libjulia/optimiser/ExpressionBreaker.h new file mode 100644 index 00000000..28cfdbc9 --- /dev/null +++ b/libjulia/optimiser/ExpressionBreaker.h @@ -0,0 +1,86 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that turns complex expressions into multiple variable + * declarations. + */ +#pragma once + +#include <libjulia/ASTDataForward.h> + +#include <libjulia/optimiser/ASTWalker.h> +#include <libjulia/optimiser/NameDispenser.h> + +#include <vector> + +namespace dev +{ +namespace julia +{ + +class NameCollector; + + +/** + * Optimiser component that modifies an AST in place, turning complex + * expressions into simple expressions and multiple variable declarations. + * + * Code of the form + * + * sstore(mul(x, 4), mload(y)) + * + * is transformed into + * + * let a1 := mload(y) + * let a2 := mul(x, 4) + * sstore(a2, a1) + * + * The transformation is not applied to loop conditions, because the loop control flow + * does not allow "outlining" the inner expressions in all cases. + * + * The final program should be in a form such that with the exception of a loop condition, + * function calls can only appear in the right-hand side of a variable declaration, + * assignments or expression statements and all arguments have to be constants or variables. + */ +class ExpressionBreaker: public ASTModifier +{ +public: + explicit ExpressionBreaker(NameDispenser& _nameDispenser): + m_nameDispenser(_nameDispenser) + { } + + virtual void operator()(FunctionalInstruction&) override; + virtual void operator()(FunctionCall&) override; + virtual void operator()(If&) override; + virtual void operator()(Switch&) override; + virtual void operator()(ForLoop&) override; + virtual void operator()(Block& _block) override; + +private: + /// Replaces the expression by a variable if it is a function call or functional + /// instruction. The declaration of the variable is appended to m_statementsToPrefix. + /// Recurses via visit(). + void outlineExpression(Expression& _expr); + + /// List of statements that should go in front of the currently visited AST element, + /// at the statement level. + std::vector<Statement> m_statementsToPrefix; + NameDispenser& m_nameDispenser; +}; + +} +} diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index b79fb156..ab544388 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -53,7 +53,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) SemVerVersion recommendedVersion{string(VersionString)}; if (!recommendedVersion.isPrerelease()) errorString += - "Consider adding \"pragma solidity ^" + + " Consider adding \"pragma solidity ^" + to_string(recommendedVersion.major()) + string(".") + to_string(recommendedVersion.minor()) + diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e45fc81d..195b2e2d 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -841,13 +841,8 @@ TypePointer RationalNumberType::forLiteral(Literal const& _literal) TypePointer compatibleBytesType; if (_literal.isHexNumber()) { - size_t digitCount = count_if( - _literal.value().begin() + 2, // skip "0x" - _literal.value().end(), - [](unsigned char _c) -> bool { return isxdigit(_c); } - ); - // require even number of digits - if (!(digitCount & 1)) + size_t const digitCount = _literal.valueWithoutUnderscores().length() - 2; + if (digitCount % 2 == 0 && (digitCount / 2) <= 32) compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2); } @@ -861,8 +856,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal rational value; try { - ASTString valueString = _literal.value(); - boost::erase_all(valueString, "_");// Remove underscore separators + ASTString valueString = _literal.valueWithoutUnderscores(); auto expPoint = find(valueString.begin(), valueString.end(), 'e'); if (expPoint == valueString.end()) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 2c396746..ca9a9b57 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1554,6 +1554,9 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() expression = nodeFactory.createNode<TupleExpression>(components, isArray); break; } + case Token::IllegalHex: + fatalParserError("Expected even number of hex-nibbles within double-quotes."); + break; default: if (Token::isElementaryTypeName(token)) { diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index 9a7f85cb..87d7c535 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -612,7 +612,7 @@ void Scanner::scanToken() if (m_char == '"' || m_char == '\'') token = scanHexString(); else - token = Token::Illegal; + token = Token::IllegalHex; } } else if (isDecimalDigit(m_char)) @@ -736,11 +736,11 @@ Token::Value Scanner::scanHexString() { char c = m_char; if (!scanHexByte(c)) - return Token::Illegal; + return Token::IllegalHex; addLiteralChar(c); } if (m_char != quote) - return Token::Illegal; + return Token::IllegalHex; literal.complete(); advance(); // consume quote return Token::StringLiteral; diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 73c85482..8ecc850a 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -261,6 +261,8 @@ namespace solidity \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ + /* Illegal hex token */ \ + T(IllegalHex, "ILLEGAL_HEX", 0) \ \ /* Scanner-internal use only. */ \ T(Whitespace, NULL, 0) diff --git a/test/cmdlineErrorReports/too_long_line.sol.ref b/test/cmdlineErrorReports/too_long_line.sol.ref index 7cad93ee..55cd1935 100644 --- a/test/cmdlineErrorReports/too_long_line.sol.ref +++ b/test/cmdlineErrorReports/too_long_line.sol.ref @@ -1,4 +1,3 @@ - too_long_line.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref b/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref index f2ea427a..9a5ebfba 100644 --- a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref @@ -1,4 +1,3 @@ - too_long_line_both_sides_short.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref b/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref index b6699933..ad3b7805 100644 --- a/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref @@ -1,4 +1,3 @@ - too_long_line_edge_in.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref b/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref index 76df589a..d8495c11 100644 --- a/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref @@ -1,4 +1,3 @@ - too_long_line_edge_out.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_left_short.sol.ref b/test/cmdlineErrorReports/too_long_line_left_short.sol.ref index efaa559d..00b6be5c 100644 --- a/test/cmdlineErrorReports/too_long_line_left_short.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_left_short.sol.ref @@ -1,4 +1,3 @@ - too_long_line_left_short.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_right_short.sol.ref b/test/cmdlineErrorReports/too_long_line_right_short.sol.ref index 2b0c6d8c..88072d95 100644 --- a/test/cmdlineErrorReports/too_long_line_right_short.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_right_short.sol.ref @@ -1,4 +1,3 @@ - too_long_line_right_short.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index d7b95e8a..71866bce 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -123,7 +123,8 @@ test_solc_file_input_failures() { exitCode=$? set -e - stderr=`sed 's/.*This is a pre-release compiler version, please do not use it in production.*$//' $stderr_path` + sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" + sed -i -e 's/ \?Consider adding "pragma .*$//' "$stderr_path" if [[ $exitCode -eq 0 ]]; then printError "Incorrect exit code. Expected failure (non-zero) but got success (0)." @@ -141,12 +142,12 @@ test_solc_file_input_failures() { exit 1 fi - if [[ "$stderr" != "${stderr_expected}" ]]; then + if [[ "$(cat $stderr_path)" != "${stderr_expected}" ]]; then printError "Incorrect output on stderr received. Expected:" echo -e "${stderr_expected}" printError "But got:" - echo $stderr + cat $stderr_path rm -f $stdout_path $stderr_path exit 1 fi diff --git a/test/libdevcore/IterateReplacing.cpp b/test/libdevcore/IterateReplacing.cpp new file mode 100644 index 00000000..08cd1e22 --- /dev/null +++ b/test/libdevcore/IterateReplacing.cpp @@ -0,0 +1,97 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Unit tests for the iterateReplacing function + */ + +#include <libdevcore/CommonData.h> + +#include <test/Options.h> + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(IterateReplacing) + +BOOST_AUTO_TEST_CASE(no_replacement) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string&) -> boost::optional<vector<string>> { return {}; }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "def", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(empty_input) +{ + vector<string> v; + function<boost::optional<vector<string>>(string&)> f = [](string&) -> boost::optional<vector<string>> { return {}; }; + iterateReplacing(v, f); + vector<string> expectation; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(delete_some) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "def") + return vector<string>(); + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(inject_some_start) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "abc") + return vector<string>{"x", "y"}; + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"x", "y", "def", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(inject_some_end) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "ghi") + return vector<string>{"x", "y"}; + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "def", "x", "y"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/libjulia/ExpressionBreaker.cpp b/test/libjulia/ExpressionBreaker.cpp new file mode 100644 index 00000000..de8d2251 --- /dev/null +++ b/test/libjulia/ExpressionBreaker.cpp @@ -0,0 +1,156 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Unit tests for the expression breaker. + */ + +#include <test/libjulia/Common.h> + +#include <libjulia/optimiser/ExpressionBreaker.h> +#include <libjulia/optimiser/NameCollector.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <boost/test/unit_test.hpp> + +#include <boost/range/adaptors.hpp> +#include <boost/algorithm/string/join.hpp> + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::julia::test; +using namespace dev::solidity; + + +#define CHECK(_original, _expectation)\ +do\ +{\ + auto result = parse(_original, false);\ + NameDispenser nameDispenser;\ + nameDispenser.m_usedNames = NameCollector(*result.first).names();\ + ExpressionBreaker{nameDispenser}(*result.first);\ + BOOST_CHECK_EQUAL(assembly::AsmPrinter{}(*result.first), format(_expectation, false));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(YulExpressionBreaker) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ }"); +} + +BOOST_AUTO_TEST_CASE(trivial) +{ + CHECK( + "{ mstore(add(calldataload(2), mload(3)), 8) }", + "{ let _1 := mload(3) let _2 := calldataload(2) let _3 := add(_2, _1) mstore(_3, 8) }" + ); +} + +BOOST_AUTO_TEST_CASE(control_flow) +{ + string input = R"({ + let x := calldataload(0) + if mul(add(x, 2), 3) { + for { let a := 2 } lt(a, mload(a)) { a := add(a, mul(a, 2)) } { + let b := mul(add(a, 2), 4) + sstore(b, mul(b, 2)) + } + } + })"; + string expectation = R"({ + let x := calldataload(0) + let _1 := add(x, 2) + let _2 := mul(_1, 3) + if _2 + { + for { let a := 2 } lt(a, mload(a)) + { + let _3 := mul(a, 2) + a := add(a, _3) + } + { + let _4 := add(a, 2) + let b := mul(_4, 4) + let _5 := mul(b, 2) + sstore(b, _5) + } + } + })"; + CHECK(input, expectation); +} + +BOOST_AUTO_TEST_CASE(switch_) +{ + string input = R"({ + let x := 8 + switch add(2, calldataload(0)) + case 0 { sstore(0, mload(2)) } + default { mstore(0, mload(3)) } + x := add(mload(3), 4) + })"; + string expectation = R"({ + let x := 8 + let _1 := calldataload(0) + let _2 := add(2, _1) + switch _2 + case 0 { + let _3 := mload(2) + sstore(0, _3) + } + default { + let _4 := mload(3) + mstore(0, _4) + } + let _5 := mload(3) + x := add(_5, 4) + })"; + + CHECK(input, expectation); +} + +BOOST_AUTO_TEST_CASE(inside_function) +{ + string input = R"({ + let x := mul(f(0, mload(7)), 3) + function f(a, b) -> c { + c := mul(a, mload(add(b, c))) + } + sstore(x, f(mload(2), mload(2))) + })"; + string expectation = R"({ + let _1 := mload(7) + let _2 := f(0, _1) + let x := mul(_2, 3) + function f(a, b) -> c + { + let _3 := add(b, c) + let _4 := mload(_3) + c := mul(a, _4) + } + let _5 := mload(2) + let _6 := mload(2) + let _7 := f(_6, _5) + sstore(x, _7) + })"; + + CHECK(input, expectation); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 93db236b..6965d886 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -471,6 +471,8 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } +// HEX STRING LITERAL + BOOST_AUTO_TEST_CASE(valid_hex_literal) { Scanner scanner(CharStream("{ hex\"00112233FF\"")); @@ -483,30 +485,32 @@ BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) { Scanner scanner(CharStream("{ hex\"00112233F\"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) { Scanner scanner(CharStream("{ hex\"00112233FF \"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) { Scanner scanner(CharStream("{ hex\"00112233FF'")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) { Scanner scanner(CharStream("{ hex\"hello\"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } +// COMMENTS + BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) { // This used to parse as "comment", "identifier" diff --git a/test/libsolidity/syntaxTests/types/rational_number_huge.sol b/test/libsolidity/syntaxTests/types/rational_number_huge.sol new file mode 100644 index 00000000..378de201 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_huge.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint y) public pure { + // fits FixedBytes with exactly 32-bytes + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000; // FixedBytes (32) + + // fits exactly into FixedBytes (32), ensures underscored literals won't hurt + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff_00000000; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol b/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol new file mode 100644 index 00000000..08e50656 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint y) public pure { + // one byte too long for storing in Fixedbytes (would require 33 bytes) + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff000000001; + } +} +// ---- +// TypeError: (142-209): Type int_const 1852...(71 digits omitted)...7281 is not implicitly convertible to expected type uint256. |