diff options
57 files changed, 1033 insertions, 389 deletions
diff --git a/.travis.yml b/.travis.yml index c30e3e0f..708d3620 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,13 +49,6 @@ env: matrix: include: - # Ubuntu 14.04 LTS "Trusty Tahr" - # https://en.wikipedia.org/wiki/List_of_Ubuntu_releases#Ubuntu_14.04_LTS_.28Trusty_Tahr.29 - # - # TravisCI doesn't directly support any new Ubuntu releases. These is - # some Docker support, which we should probably investigate, at least for - # Ubuntu 16.04 LTS "Xenial Xerus" - # See https://en.wikipedia.org/wiki/List_of_Ubuntu_releases#Ubuntu_16.04_LTS_.28Xenial_Xerus.29. - os: linux dist: trusty sudo: required diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f71cedc..537a9521 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ string(REGEX MATCHALL ".." LICENSE_TEXT "${LICENSE_TEXT}") string(REGEX REPLACE ";" ",\n\t0x" LICENSE_TEXT "${LICENSE_TEXT}") set(LICENSE_TEXT "0x${LICENSE_TEXT}") -configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" "license.h") +configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/license.h) include(EthOptions) configure_project(TESTS) diff --git a/Changelog.md b/Changelog.md index 9755d2d3..8ebc30d0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,8 +2,15 @@ Features: * Parser: Better error message for unexpected trailing comma in parameter lists. + * Syntax Checker: Unary ``+`` is now a syntax error as experimental 0.5.0 feature. + * Type Checker: Disallow non-pure constant state variables as experimental 0.5.0 feature. Bugfixes: + * Parser: Fix source location of VariableDeclarationStatement. + * Type Checker: Properly check array length and don't rely on an assertion in code generation. + * Type Checker: Properly support overwriting members inherited from ``address`` in a contract + (such as ``balance``, ``transfer``, etc.) + * Type Checker: Prevent duplicate event declarations. ### 0.4.17 (2017-09-21) diff --git a/appveyor.yml b/appveyor.yml index 3d4d65bb..b50681b9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -71,7 +71,7 @@ build_script: test_script: - cd %APPVEYOR_BUILD_FOLDER% - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% - - soltest.exe --show-progress -- --no-ipc + - soltest.exe --show-progress -- --no-ipc --no-smt artifacts: - path: solidity-windows.zip diff --git a/cmake/EthBuildInfo.cmake b/cmake/EthBuildInfo.cmake index 1f70d371..cae3e5ce 100644 --- a/cmake/EthBuildInfo.cmake +++ b/cmake/EthBuildInfo.cmake @@ -39,5 +39,5 @@ function(create_build_info NAME) -DPROJECT_VERSION="${PROJECT_VERSION}" -P "${ETH_SCRIPTS_DIR}/buildinfo.cmake" ) - include_directories(BEFORE ${PROJECT_BINARY_DIR}) + include_directories("${PROJECT_BINARY_DIR}/include") endfunction() diff --git a/cmake/EthCheckCXXCompilerFlag.cmake b/cmake/EthCheckCXXCompilerFlag.cmake new file mode 100644 index 00000000..c6ed35b4 --- /dev/null +++ b/cmake/EthCheckCXXCompilerFlag.cmake @@ -0,0 +1,23 @@ +include(CheckCXXCompilerFlag) + +# Adds CXX compiler flag if the flag is supported by the compiler. +# +# This is effectively a combination of CMake's check_cxx_compiler_flag() +# and add_compile_options(): +# +# if(check_cxx_compiler_flag(flag)) +# add_compile_options(flag) +# +function(eth_add_cxx_compiler_flag_if_supported FLAG) + # Remove leading - or / from the flag name. + string(REGEX REPLACE "^-|/" "" name ${FLAG}) + check_cxx_compiler_flag(${FLAG} ${name}) + if(${name}) + add_compile_options(${FLAG}) + endif() + + # If the optional argument passed, store the result there. + if(ARGV1) + set(${ARGV1} ${name} PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 1a00ae70..6d4dadeb 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -4,7 +4,7 @@ # CMake file for cpp-ethereum project which specifies our compiler settings # for each supported platform and build configuration. # -# See http://www.ethdocs.org/en/latest/ethereum-clients/cpp-ethereum/. +# The documentation for cpp-ethereum is hosted at http://cpp-ethereum.org # # Copyright (c) 2014-2016 cpp-ethereum contributors. #------------------------------------------------------------------------------ @@ -14,18 +14,15 @@ # # These settings then end up spanning all POSIX platforms (Linux, OS X, BSD, etc) -include(CheckCXXCompilerFlag) +include(EthCheckCXXCompilerFlag) -check_cxx_compiler_flag(-fstack-protector-strong have_stack_protector_strong) -if (have_stack_protector_strong) - add_compile_options(-fstack-protector-strong) -else() - check_cxx_compiler_flag(-fstack-protector have_stack_protector) - if(have_stack_protector) - add_compile_options(-fstack-protector) - endif() +eth_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support) +if(NOT have_stack_protector_strong_support) + eth_add_cxx_compiler_flag_if_supported(-fstack-protector) endif() +eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) # Use ISO C++11 standard language. @@ -83,12 +80,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.") endif () - # Until https://github.com/ethereum/solidity/issues/2479 is handled - # disable all implicit fallthrough warnings in the codebase for GCC > 7.0 - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) - add_compile_options(-Wno-implicit-fallthrough) - endif() - # Additional Clang-specific compiler settings. elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 1204f186..233ac22a 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -48,10 +48,3 @@ option(Boost_USE_STATIC_LIBS "Link Boost statically" ON) find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options system) eth_show_dependency(Boost boost) - -if (APPLE) - link_directories(/usr/local/lib) - include_directories(/usr/local/include) -endif() - -include_directories(BEFORE "${PROJECT_BINARY_DIR}/include") diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 29d98645..0361458f 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -442,3 +442,22 @@ would result in the JSON: "outputs": [] } ] + +.. _abi_packed_mode: + +Non-standard Packed Mode +======================== + +Solidity supports a non-standard packed mode where: + +- no :ref:`function selector <abi_function_selector>` is encoded, +- short types are not zero padded and +- dynamic types are encoded in-place and without the length. + +As an example encoding ``uint1, bytes1, uint8, string`` with values ``1, 0x42, 0x2424, "Hello, world!"`` results in :: + + 0x0142242448656c6c6f2c20776f726c6421 + ^^ uint1(1) + ^^ bytes1(0x42) + ^^^^ uint8(0x2424) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index acef13b7..52319be0 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -93,7 +93,7 @@ Notice that, in this example, an attacker could trap the contract into an unusable state by causing ``richest`` to be the address of a contract that has a fallback function which fails (e.g. by using ``revert()`` or by just -conssuming more than the 2300 gas stipend). That way, +consuming more than the 2300 gas stipend). That way, whenever ``transfer`` is called to deliver funds to the "poisoned" contract, it will fail and thus also ``becomeRichest`` will fail, with the contract being stuck forever. @@ -121,7 +121,7 @@ unless you declare make your state variables ``public``. Furthermore, you can restrict who can make modifications to your contract's state or call your contract's -functions and this is what this page is about. +functions and this is what this section is about. .. index:: function;modifier diff --git a/docs/contracts.rst b/docs/contracts.rst index 69600fc1..cdc92315 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -20,7 +20,7 @@ Contracts can be created "from outside" via Ethereum transactions or from within IDEs, such as `Remix <https://remix.ethereum.org/>`_, make the creation process seamless using UI elements. -Creating contracts programatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/etherem/web3.js>`_. +Creating contracts programatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/ethereum/web3.js>`_. As of today it has a method called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_ to facilitate contract creation. diff --git a/docs/contributing.rst b/docs/contributing.rst index 01caa5b1..0f7c3e72 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -64,9 +64,11 @@ Running the compiler tests ========================== Solidity includes different types of tests. They are included in the application -called ``soltest``. Some of them require the ``cpp-ethereum`` client in testing mode. +called ``soltest``. Some of them require the ``cpp-ethereum`` client in testing mode, +some others require ``libz3`` to be installed. -To run a subset of the tests that do not require ``cpp-ethereum``, use ``./build/test/soltest -- --no-ipc``. +To disable the z3 tests, use ``./build/test/soltest -- --no-smt`` and +to run a subset of the tests that do not require ``cpp-ethereum``, use ``./build/test/soltest -- --no-ipc``. For all other tests, you need to install `cpp-ethereum <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth>`_ and run it in testing mode: ``eth --test -d /tmp/testeth``. diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 93dcfbb8..5c427c69 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -432,12 +432,12 @@ What happens to a ``struct``'s mapping when copying over a ``struct``? This is a very interesting question. Suppose that we have a contract field set up like such:: struct user { - mapping(string => address) usedContracts; + mapping(string => string) comments; } function somefunction { user user1; - user1.usedContracts["Hello"] = "World"; + user1.comments["Hello"] = "World"; user user2 = user1; } diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 6d6c25ac..6c0efa9e 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -322,10 +322,10 @@ Global Variables - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) - ``revert()``: abort execution and revert state changes -- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments +- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` - ``sha3(...) returns (bytes32)``: an alias to ``keccak256`` -- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments -- ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the (tightly packed) arguments +- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` +- ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error - ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256`` - ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256`` diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 887535da..7af97376 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -116,13 +116,13 @@ Mathematical and Cryptographic Functions ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. ``keccak256(...) returns (bytes32)``: - compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments + compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` ``sha256(...) returns (bytes32)``: - compute the SHA-256 hash of the (tightly packed) arguments + compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` ``sha3(...) returns (bytes32)``: alias to ``keccak256`` ``ripemd160(...) returns (bytes20)``: - compute RIPEMD-160 hash of the (tightly packed) arguments + compute RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover the address associated with the public key from elliptic curve signature or return zero on error (`example usage <https://ethereum.stackexchange.com/q/1777/222>`_) diff --git a/libdevcore/Exceptions.cpp b/libdevcore/Exceptions.cpp index f422d926..f204dbc2 100644 --- a/libdevcore/Exceptions.cpp +++ b/libdevcore/Exceptions.cpp @@ -24,10 +24,14 @@ using namespace dev; char const* Exception::what() const noexcept { + // Return the comment if available. if (string const* cmt = comment()) - return cmt->c_str(); - else - return nullptr; + return cmt->data(); + + // Fallback to base what(). + // Boost accepts nullptr, but the C++ standard doesn't + // and crashes on some platforms. + return std::exception::what(); } string Exception::lineInfo() const diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index cfe91be0..64963021 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -17,8 +17,6 @@ #include <libevmasm/AssemblyItem.h> -#include <libevmasm/SemanticInformation.h> - #include <libdevcore/CommonData.h> #include <libdevcore/FixedHash.h> @@ -112,7 +110,7 @@ bool AssemblyItem::canBeFunctional() const switch (m_type) { case Operation: - return !SemanticInformation::isDupInstruction(*this) && !SemanticInformation::isSwapInstruction(*this); + return !isDupInstruction(instruction()) && !isSwapInstruction(instruction()); case Push: case PushString: case PushTag: diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt index 5c945c7d..cfd59dff 100644 --- a/libevmasm/CMakeLists.txt +++ b/libevmasm/CMakeLists.txt @@ -2,4 +2,4 @@ file(GLOB sources "*.cpp") file(GLOB headers "*.h") add_library(evmasm ${sources} ${headers}) -target_link_libraries(evmasm PUBLIC devcore jsoncpp) +target_link_libraries(evmasm PUBLIC jsoncpp devcore) diff --git a/libevmasm/EVMSchedule.h b/libevmasm/EVMSchedule.h deleted file mode 100644 index 1695a59c..00000000 --- a/libevmasm/EVMSchedule.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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/>. -*/ -/** @file EVMSchedule.h - * @author Gav <i@gavwood.com> - * @author Christian <c@ethdev.com> - * @date 2015 - */ - -#pragma once - -namespace dev -{ -namespace solidity -{ - -struct EVMSchedule -{ - unsigned stackLimit = 1024; - unsigned expGas = 10; - unsigned expByteGas = 10; - unsigned keccak256Gas = 30; - unsigned keccak256WordGas = 6; - unsigned sloadGas = 200; - unsigned sstoreSetGas = 20000; - unsigned sstoreResetGas = 5000; - unsigned sstoreRefundGas = 15000; - unsigned jumpdestGas = 1; - unsigned logGas = 375; - unsigned logDataGas = 8; - unsigned logTopicGas = 375; - unsigned createGas = 32000; - unsigned callGas = 40; - unsigned callStipend = 2300; - unsigned callValueTransferGas = 9000; - unsigned callNewAccountGas = 25000; - unsigned selfdestructRefundGas = 24000; - unsigned memoryGas = 3; - unsigned quadCoeffDiv = 512; - unsigned createDataGas = 200; - unsigned txGas = 21000; - unsigned txCreateGas = 53000; - unsigned txDataZeroGas = 4; - unsigned txDataNonZeroGas = 68; - unsigned copyGas = 3; -}; - -} -} diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index afbef71d..d9c53900 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -197,6 +197,24 @@ enum class Instruction: uint8_t SELFDESTRUCT = 0xff ///< halt execution and register account for later deletion }; +/// @returns true if the instruction is a PUSH +inline bool isPushInstruction(Instruction _inst) +{ + return Instruction::PUSH1 <= _inst && _inst <= Instruction::PUSH32; +} + +/// @returns true if the instruction is a DUP +inline bool isDupInstruction(Instruction _inst) +{ + return Instruction::DUP1 <= _inst && _inst <= Instruction::DUP16; +} + +/// @returns true if the instruction is a SWAP +inline bool isSwapInstruction(Instruction _inst) +{ + return Instruction::SWAP1 <= _inst && _inst <= Instruction::SWAP16; +} + /// @returns the number of PUSH Instruction _inst inline unsigned getPushNumber(Instruction _inst) { diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index ceb3fbdd..83cfe2c6 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -90,14 +90,14 @@ bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) { if (_item.type() != Operation) return false; - return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16; + return solidity::isDupInstruction(_item.instruction()); } bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) { if (_item.type() != Operation) return false; - return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; + return solidity::isSwapInstruction(_item.instruction()); } bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item) diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 254f436f..d4ef3888 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -47,7 +47,8 @@ void CodeFragment::finalise(CompilerState const& _cs) } } -CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowASM) +CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM): + m_readFile(_readFile) { /* std::cout << "CodeFragment. Locals:"; @@ -103,7 +104,7 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowAS { bigint i = *_t.get<bigint*>(); if (i < 0 || i > bigint(u256(0) - 1)) - error<IntegerOutOfRange>(); + error<IntegerOutOfRange>(toString(i)); m_asm.append((u256)i); break; } @@ -113,6 +114,22 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowAS } } +namespace +{ +/// Returns true iff the instruction is valid as a function. +bool validFunctionalInstruction(string us) +{ + auto it = c_instructions.find(us); + return !( + it == c_instructions.end() || + solidity::isPushInstruction(it->second) || + solidity::isDupInstruction(it->second) || + solidity::isSwapInstruction(it->second) || + it->second == solidity::Instruction::JUMPDEST + ); +} +} + void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { if (_t.tag() == 0 && _t.empty()) @@ -157,7 +174,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { auto i = *++_t.begin(); if (i.tag()) - error<InvalidName>(); + error<InvalidName>(toString(i)); if (i.which() == sp::utree_type::string_type) { auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>(); @@ -198,7 +215,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) int c = 0; for (auto const& i: _t) if (c++) - m_asm.append(CodeFragment(i, _s, true).m_asm); + m_asm.append(CodeFragment(i, _s, m_readFile, true).m_asm); } else if (us == "INCLUDE") { @@ -207,10 +224,12 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) string fileName = firstAsString(); if (fileName.empty()) error<InvalidName>("Empty file name provided"); - string contents = contentsString(fileName); + if (!m_readFile) + error<InvalidName>("Import callback not present"); + string contents = m_readFile(fileName); if (contents.empty()) error<InvalidName>(std::string("File not found (or empty): ") + fileName); - m_asm.append(CodeFragment::compile(contents, _s).m_asm); + m_asm.append(CodeFragment::compile(contents, _s, m_readFile).m_asm); } else if (us == "SET") { @@ -219,7 +238,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) int c = 0; for (auto const& i: _t) if (c++ == 2) - m_asm.append(CodeFragment(i, _s, false).m_asm); + m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm); m_asm.append((u256)varAddress(firstAsString(), true)); m_asm.append(Instruction::MSTORE); } @@ -244,7 +263,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) if (ii == 1) { if (i.tag()) - error<InvalidName>(); + error<InvalidName>(toString(i)); if (i.which() == sp::utree_type::string_type) { auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>(); @@ -260,7 +279,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) if (_t.size() == 3) { /// NOTE: some compilers could do the assignment first if this is done in a single line - CodeFragment code = CodeFragment(i, _s); + CodeFragment code = CodeFragment(i, _s, m_readFile); _s.defs[n] = code; } else @@ -301,13 +320,13 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) } else if (ii == 1) { - pos = CodeFragment(i, _s); + pos = CodeFragment(i, _s, m_readFile); if (pos.m_asm.deposit() != 1) - error<InvalidDeposit>(us); + error<InvalidDeposit>(toString(i)); } else if (i.tag() != 0) { - error<InvalidLiteral>(); + error<InvalidLiteral>(toString(i)); } else if (i.which() == sp::utree_type::string_type) { @@ -318,7 +337,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { bigint bi = *i.get<bigint*>(); if (bi < 0) - error<IntegerOutOfRange>(); + error<IntegerOutOfRange>(toString(i)); else { bytes tmp = toCompactBigEndian(bi); @@ -327,7 +346,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) } else { - error<InvalidLiteral>(); + error<InvalidLiteral>(toString(i)); } ii++; @@ -380,9 +399,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) if (c++) { if (us == "LLL" && c == 1) - code.push_back(CodeFragment(i, ns)); + code.push_back(CodeFragment(i, ns, m_readFile)); else - code.push_back(CodeFragment(i, _s)); + code.push_back(CodeFragment(i, _s, m_readFile)); } auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); }; auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); }; @@ -403,13 +422,13 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) //requireDeposit(i, 1); cs.args[m.args[i]] = code[i]; } - m_asm.append(CodeFragment(m.code, cs).m_asm); + m_asm.append(CodeFragment(m.code, cs, m_readFile).m_asm); for (auto const& i: cs.defs) _s.defs[i.first] = i.second; for (auto const& i: cs.macros) _s.macros.insert(i); } - else if (c_instructions.count(us)) + else if (c_instructions.count(us) && validFunctionalInstruction(us)) { auto it = c_instructions.find(us); requireSize(instructionInfo(it->second).args); @@ -514,6 +533,44 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) m_asm.appendJump(begin); m_asm << end.tag(); } + else if (us == "SWITCH") + { + requireMinSize(1); + + bool hasDefault = (code.size() % 2 == 1); + int startDeposit = m_asm.deposit(); + int targetDeposit = hasDefault ? code[code.size() - 1].m_asm.deposit() : 0; + + // The conditions + AssemblyItems jumpTags; + for (unsigned i = 0; i < code.size() - 1; i += 2) + { + requireDeposit(i, 1); + m_asm.append(code[i].m_asm); + jumpTags.push_back(m_asm.appendJumpI()); + } + + // The default, if present + if (hasDefault) + m_asm.append(code[code.size() - 1].m_asm); + + // The targets - appending in reverse makes the top case the most efficient. + if (code.size() > 1) + { + auto end = m_asm.appendJump(); + for (int i = 2 * (code.size() / 2 - 1); i >= 0; i -= 2) + { + m_asm << jumpTags[i / 2].tag(); + requireDeposit(i + 1, targetDeposit); + m_asm.append(code[i + 1].m_asm); + if (i != 0) + m_asm.appendJump(end); + } + m_asm << end.tag(); + } + + m_asm.setDeposit(startDeposit + targetDeposit); + } else if (us == "ALLOC") { requireSize(1); @@ -622,13 +679,13 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) } } -CodeFragment CodeFragment::compile(string const& _src, CompilerState& _s) +CodeFragment CodeFragment::compile(string const& _src, CompilerState& _s, ReadCallback const& _readFile) { CodeFragment ret; sp::utree o; parseTreeLLL(_src, o); if (!o.empty()) - ret = CodeFragment(o, _s); + ret = CodeFragment(o, _s, _readFile); _s.treesToKill.push_back(o); return ret; } diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h index 95d21563..e5cac34e 100644 --- a/liblll/CodeFragment.h +++ b/liblll/CodeFragment.h @@ -39,10 +39,12 @@ struct CompilerState; class CodeFragment { public: + using ReadCallback = std::function<std::string(std::string const&)>; + CodeFragment() {} - CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowASM = false); + CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM = false); - static CodeFragment compile(std::string const& _src, CompilerState& _s); + static CodeFragment compile(std::string const& _src, CompilerState& _s, ReadCallback const& _readFile); /// Consolidates data and compiles code. Assembly& assembly(CompilerState const& _cs) { finalise(_cs); return m_asm; } @@ -60,6 +62,7 @@ private: bool m_finalised = false; Assembly m_asm; + ReadCallback m_readFile; }; static const CodeFragment NullCodeFragment; diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index b69675aa..1638f69e 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -28,13 +28,14 @@ using namespace std; using namespace dev; using namespace dev::eth; -bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _errors) + +bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _errors, ReadCallback const& _readFile) { try { CompilerState cs; cs.populateStandard(); - auto assembly = CodeFragment::compile(_src, cs).assembly(cs); + auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) assembly = assembly.optimise(true); bytes ret = assembly.assemble().bytecode; @@ -66,13 +67,13 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error return bytes(); } -std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector<std::string>* _errors) +std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile) { try { CompilerState cs; cs.populateStandard(); - auto assembly = CodeFragment::compile(_src, cs).assembly(cs); + auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) assembly = assembly.optimise(true); string ret = assembly.assemblyString(); diff --git a/liblll/Compiler.h b/liblll/Compiler.h index 04aa1e26..c3395b66 100644 --- a/liblll/Compiler.h +++ b/liblll/Compiler.h @@ -30,9 +30,11 @@ namespace dev namespace eth { +using ReadCallback = std::function<std::string(std::string const&)>; + std::string parseLLL(std::string const& _src); -std::string compileLLLToAsm(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr); -bytes compileLLL(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr); +std::string compileLLLToAsm(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); +bytes compileLLL(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); } } diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index d53dec7e..c0e344b2 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -82,5 +82,5 @@ void CompilerState::populateStandard() "(def 'shl (val shift) (mul val (exp 2 shift)))" "(def 'shr (val shift) (div val (exp 2 shift)))" "}"; - CodeFragment::compile(s, *this); + CodeFragment::compile(s, *this, CodeFragment::ReadCallback()); } diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 62dbd394..fd39d860 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -34,44 +34,29 @@ namespace solidity { GlobalContext::GlobalContext(): -m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)), - make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)), - make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)), - make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)), - make_shared<MagicVariableDeclaration>("suicide", - make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), - make_shared<MagicVariableDeclaration>("selfdestruct", - make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), - make_shared<MagicVariableDeclaration>("addmod", - make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("mulmod", - make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("sha3", - make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("keccak256", - make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("log0", - make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)), - make_shared<MagicVariableDeclaration>("log1", - make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)), - make_shared<MagicVariableDeclaration>("log2", - make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)), - make_shared<MagicVariableDeclaration>("log3", - make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log3)), - make_shared<MagicVariableDeclaration>("log4", - make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log4)), - make_shared<MagicVariableDeclaration>("sha256", - make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("ecrecover", - make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("ripemd160", - make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("assert", - make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("require", - make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("revert", - make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure))}) +m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ + make_shared<MagicVariableDeclaration>("addmod", make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("assert", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)), + make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)), + make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)), + make_shared<MagicVariableDeclaration>("log2", make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)), + make_shared<MagicVariableDeclaration>("log3", make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log3)), + make_shared<MagicVariableDeclaration>("log4", make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log4)), + make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)), + make_shared<MagicVariableDeclaration>("mulmod", make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)), + make_shared<MagicVariableDeclaration>("require", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), + make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), + make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)) +}) { } @@ -92,8 +77,7 @@ vector<Declaration const*> GlobalContext::declarations() const MagicVariableDeclaration const* GlobalContext::currentThis() const { if (!m_thisPointer[m_currentContract]) - m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>( - "this", make_shared<ContractType>(*m_currentContract)); + m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>("this", make_shared<ContractType>(*m_currentContract)); return m_thisPointer[m_currentContract].get(); } @@ -101,8 +85,7 @@ MagicVariableDeclaration const* GlobalContext::currentThis() const MagicVariableDeclaration const* GlobalContext::currentSuper() const { if (!m_superPointer[m_currentContract]) - m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>( - "super", make_shared<ContractType>(*m_currentContract, true)); + m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>("super", make_shared<ContractType>(*m_currentContract, true)); return m_superPointer[m_currentContract].get(); } diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 8f07d43a..20016112 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -149,8 +149,10 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) if (!length->annotation().type) ConstantEvaluator e(*length); auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get()); - if (!lengthType || lengthType->isFractional()) + if (!lengthType || !lengthType->mobileType()) fatalTypeError(length->location(), "Invalid array length, expected integer literal."); + else if (lengthType->isFractional()) + fatalTypeError(length->location(), "Array with fractional length specified."); else if (lengthType->isNegative()) fatalTypeError(length->location(), "Array with negative length specified."); else @@ -347,4 +349,3 @@ void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, m_errorOccurred = true; m_errorReporter.fatalDeclarationError(_location, _description); } - diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 187eb26f..0ca4b86c 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -182,8 +182,15 @@ bool SyntaxChecker::visit(Throw const& _throwStatement) bool SyntaxChecker::visit(UnaryOperation const& _operation) { + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_operation.getOperator() == Token::Add) - m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated."); + { + if (v050) + m_errorReporter.syntaxError(_operation.location(), "Use of unary + is deprecated."); + else + m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated."); + } return true; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 4b2ec8d6..b2a88059 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -75,6 +75,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract) ASTNode::listAccept(_contract.baseContracts(), *this); checkContractDuplicateFunctions(_contract); + checkContractDuplicateEvents(_contract); checkContractIllegalOverrides(_contract); checkContractAbstractFunctions(_contract); checkContractAbstractConstructors(_contract); @@ -183,9 +184,27 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con msg ); } - for (auto const& it: functions) + + findDuplicateDefinitions(functions, "Function with same name and arguments defined twice."); +} + +void TypeChecker::checkContractDuplicateEvents(ContractDefinition const& _contract) +{ + /// Checks that two events with the same name defined in this contract have different + /// argument types + map<string, vector<EventDefinition const*>> events; + for (EventDefinition const* event: _contract.events()) + events[event->name()].push_back(event); + + findDuplicateDefinitions(events, "Event with same name and arguments defined twice."); +} + +template <class T> +void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message) +{ + for (auto const& it: _definitions) { - vector<FunctionDefinition const*> const& overloads = it.second; + vector<T> const& overloads = it.second; set<size_t> reported; for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) { @@ -200,18 +219,17 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con if (ssl.infos.size() > 0) { - string msg = "Function with same name and arguments defined twice."; size_t occurrences = ssl.infos.size(); if (occurrences > 32) { ssl.infos.resize(32); - msg += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences."; + _message += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences."; } m_errorReporter.declarationError( overloads[i]->location(), ssl, - msg + _message ); } } @@ -627,14 +645,23 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!allowed) m_errorReporter.typeError(_variable.location(), "Constants of non-value type not yet implemented."); } + if (!_variable.value()) m_errorReporter.typeError(_variable.location(), "Uninitialized \"constant\" variable."); else if (!_variable.value()->annotation().isPure) - m_errorReporter.warning( - _variable.value()->location(), - "Initial value for constant variable has to be compile-time constant. " - "This will fail to compile with the next breaking version change." - ); + { + if (_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.typeError( + _variable.value()->location(), + "Initial value for constant variable has to be compile-time constant." + ); + else + m_errorReporter.warning( + _variable.value()->location(), + "Initial value for constant variable has to be compile-time constant. " + "This will fail to compile with the next breaking version change." + ); + } } if (!_variable.isStateVariable()) { @@ -1497,7 +1524,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) /* If no mobile type is available an error will be raised elsewhere. */ if (literal->mobileType()) m_errorReporter.warning( - _functionCall.location(), + arguments[i]->location(), "The type of \"" + argType->toString() + "\" was inferred as " + @@ -1954,7 +1981,7 @@ void TypeChecker::endVisit(Literal const& _literal) if (_literal.looksLikeAddress()) { if (_literal.passesAddressChecksum()) - _literal.annotation().type = make_shared<IntegerType>(0, IntegerType::Modifier::Address); + _literal.annotation().type = make_shared<IntegerType>(160, IntegerType::Modifier::Address); else m_errorReporter.warning( _literal.location(), diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 0c6f54d3..abe6dac1 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -61,6 +61,7 @@ private: /// Checks that two functions defined in this contract with the same name have different /// arguments and that there is at most one constructor. void checkContractDuplicateFunctions(ContractDefinition const& _contract); + void checkContractDuplicateEvents(ContractDefinition const& _contract); void checkContractIllegalOverrides(ContractDefinition const& _contract); /// Reports a type error with an appropiate message if overriden function signature differs. /// Also stores the direct super function in the AST annotations. @@ -108,6 +109,9 @@ private: virtual void endVisit(ElementaryTypeNameExpression const& _expr) override; virtual void endVisit(Literal const& _literal) override; + template <class T> + void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message); + bool contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set<ContractDefinition const*> const& _seenContracts = std::set<ContractDefinition const*>() diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 83a5b465..a3cbe50a 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -89,7 +89,7 @@ pair<u256, unsigned> const* StorageOffsets::offset(size_t _index) const MemberList& MemberList::operator=(MemberList&& _other) { - assert(&_other != this); + solAssert(&_other != this, ""); m_memberTypes = move(_other.m_memberTypes); m_storageOffsets = move(_other.m_storageOffsets); @@ -203,7 +203,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::Byte: return make_shared<FixedBytesType>(1); case Token::Address: - return make_shared<IntegerType>(0, IntegerType::Modifier::Address); + return make_shared<IntegerType>(160, IntegerType::Modifier::Address); case Token::Bool: return make_shared<BoolType>(); case Token::Bytes: @@ -327,11 +327,11 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) { if (isAddress()) - m_bits = 160; + solAssert(m_bits == 160, ""); solAssert( m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, "Invalid bit number for integer type: " + dev::toString(_bits) - ); + ); } string IntegerType::identifier() const @@ -1618,8 +1618,7 @@ string ContractType::canonicalName() const MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) const { - // All address members and all interface functions - MemberList::MemberMap members(IntegerType(120, IntegerType::Modifier::Address).nativeMembers(nullptr)); + MemberList::MemberMap members; if (m_super) { // add the most derived of all functions which are visible in derived contracts @@ -1661,9 +1660,45 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con &it.second->declaration() )); } + addNonConflictingAddressMembers(members); return members; } +void ContractType::addNonConflictingAddressMembers(MemberList::MemberMap& _members) +{ + MemberList::MemberMap addressMembers = IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr); + for (auto const& addressMember: addressMembers) + { + bool clash = false; + for (auto const& member: _members) + { + if ( + member.name == addressMember.name && + ( + // Members with different types are not allowed + member.type->category() != addressMember.type->category() || + // Members must overload functions without clash + ( + member.type->category() == Type::Category::Function && + dynamic_cast<FunctionType const&>(*member.type).hasEqualArgumentTypes(dynamic_cast<FunctionType const&>(*addressMember.type)) + ) + ) + ) + { + clash = true; + break; + } + } + + if (!clash) + _members.push_back(MemberList::Member( + addressMember.name, + addressMember.type, + addressMember.declaration + )); + } +} + shared_ptr<FunctionType const> const& ContractType::newExpressionType() const { if (!m_constructorType) @@ -2968,7 +3003,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { case Kind::Block: return MemberList::MemberMap({ - {"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, + {"coinbase", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, {"timestamp", make_shared<IntegerType>(256)}, {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"difficulty", make_shared<IntegerType>(256)}, @@ -2977,7 +3012,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Message: return MemberList::MemberMap({ - {"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, + {"sender", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, {"gas", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)}, {"data", make_shared<ArrayType>(DataLocation::CallData)}, @@ -2985,7 +3020,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Transaction: return MemberList::MemberMap({ - {"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, + {"origin", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, {"gasprice", make_shared<IntegerType>(256)} }); default: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 8ba55521..ce29975e 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -716,6 +716,8 @@ public: std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const; private: + static void addNonConflictingAddressMembers(MemberList::MemberMap& _members); + ContractDefinition const& m_contract; /// If true, it is the "super" type of the current contract, i.e. it contains only inherited /// members. diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 9f6c55ba..756148e7 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -162,7 +162,7 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) break; } case Type::Category::Contract: - templ("body", "cleaned := " + cleanupFunction(IntegerType(0, IntegerType::Modifier::Address)) + "(value)"); + templ("body", "cleaned := " + cleanupFunction(IntegerType(160, IntegerType::Modifier::Address)) + "(value)"); break; case Type::Category::Enum: { @@ -243,7 +243,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) toCategory == Type::Category::Integer || toCategory == Type::Category::Contract, ""); - IntegerType const addressType(0, IntegerType::Modifier::Address); + IntegerType const addressType(160, IntegerType::Modifier::Address); IntegerType const& to = toCategory == Type::Category::Integer ? dynamic_cast<IntegerType const&>(_to) : @@ -487,6 +487,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray( // TODO if this is not a byte array, we might just copy byte-by-byte anyway, // because the encoding is position-independent, but we have to check that. Whiskers templ(R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(start, length, pos) -> end { <storeLength> // might update pos <copyFun>(start, pos, length) @@ -495,6 +496,8 @@ string ABIFunctions::abiEncodingFunctionCalldataArray( )"); templ("storeLength", _to.isDynamicallySized() ? "mstore(pos, length) pos := add(pos, 0x20)" : ""); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("copyFun", copyToMemoryFunction(true)); templ("roundUpFun", roundUpFunction()); return templ.render(); @@ -527,6 +530,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( Whiskers templ( dynamicBase ? R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) <return> { let length := <lengthFun>(value) <storeLength> // might update pos @@ -545,6 +549,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( } )" : R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) <return> { let length := <lengthFun>(value) <storeLength> // might update pos @@ -560,6 +565,8 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( )" ); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("return", dynamic ? " -> end " : ""); templ("assignEnd", dynamic ? "end := pos" : ""); templ("lengthFun", arrayLengthFunction(_from)); @@ -639,6 +646,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( { solAssert(_to.isByteArray(), ""); Whiskers templ(R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) -> ret { let slotValue := sload(value) switch and(slotValue, 1) @@ -665,6 +673,8 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( } )"); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("arrayDataSlot", arrayDataAreaFunction(_from)); return templ.render(); } @@ -681,6 +691,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( // more than desired, i.e. it writes beyond the end of memory. Whiskers templ( R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) <return> { let length := <lengthFun>(value) <storeLength> // might update pos @@ -701,6 +712,8 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( )" ); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("return", dynamic ? " -> end " : ""); templ("assignEnd", dynamic ? "end := pos" : ""); templ("lengthFun", arrayLengthFunction(_from)); @@ -748,6 +761,7 @@ string ABIFunctions::abiEncodingFunctionStruct( bool fromStorage = _from.location() == DataLocation::Storage; bool dynamic = _to.isDynamicallyEncoded(); Whiskers templ(R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) <return> { let tail := add(pos, <headSize>) <init> @@ -761,6 +775,8 @@ string ABIFunctions::abiEncodingFunctionStruct( } )"); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("return", dynamic ? " -> end " : ""); templ("assignEnd", dynamic ? "end := tail" : ""); // to avoid multiple loads from the same slot for subsequent members @@ -995,9 +1011,11 @@ string ABIFunctions::shiftLeftFunction(size_t _numBits) return createFunction(functionName, [&]() { solAssert(_numBits < 256, ""); return - Whiskers(R"(function <functionName>(value) -> newValue { + Whiskers(R"( + function <functionName>(value) -> newValue { newValue := mul(value, <multiplier>) - })") + } + )") ("functionName", functionName) ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits)) .render(); @@ -1010,9 +1028,11 @@ string ABIFunctions::shiftRightFunction(size_t _numBits, bool _signed) return createFunction(functionName, [&]() { solAssert(_numBits < 256, ""); return - Whiskers(R"(function <functionName>(value) -> newValue { + Whiskers(R"( + function <functionName>(value) -> newValue { newValue := <div>(value, <multiplier>) - })") + } + )") ("functionName", functionName) ("div", _signed ? "sdiv" : "div") ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits)) @@ -1025,9 +1045,11 @@ string ABIFunctions::roundUpFunction() string functionName = "round_up_to_mul_of_32"; return createFunction(functionName, [&]() { return - Whiskers(R"(function <functionName>(value) -> result { + Whiskers(R"( + function <functionName>(value) -> result { result := and(add(value, 31), not(31)) - })") + } + )") ("functionName", functionName) .render(); }); diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 37aa1aea..f9b181ae 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -191,7 +191,7 @@ void CompilerUtils::encodeToMemory( { // Use the new JULIA-based encoding function auto stackHeightBefore = m_context.stackHeight(); - abiEncode(_givenTypes, targetTypes, _encodeAsLibraryTypes); + abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes); solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), ""); return; } @@ -302,7 +302,7 @@ void CompilerUtils::encodeToMemory( popStackSlots(argSize + dynPointers + 1); } -void CompilerUtils::abiEncode( +void CompilerUtils::abiEncodeV2( TypePointers const& _givenTypes, TypePointers const& _targetTypes, bool _encodeAsLibraryTypes @@ -541,7 +541,7 @@ void CompilerUtils::convertType( else { solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); - IntegerType addressType(0, IntegerType::Modifier::Address); + IntegerType addressType(160, IntegerType::Modifier::Address); IntegerType const& targetType = targetTypeCategory == Type::Category::Integer ? dynamic_cast<IntegerType const&>(_targetType) : addressType; if (stackTypeCategory == Type::Category::RationalNumber) @@ -596,7 +596,6 @@ void CompilerUtils::convertType( storeInMemoryDynamic(IntegerType(256)); // stack: mempos datapos storeStringData(data); - break; } else solAssert( @@ -810,9 +809,8 @@ void CompilerUtils::convertType( if (_cleanupNeeded) m_context << Instruction::ISZERO << Instruction::ISZERO; break; - case Type::Category::Function: - { - if (targetTypeCategory == Type::Category::Integer) + default: + if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Integer) { IntegerType const& targetType = dynamic_cast<IntegerType const&>(_targetType); solAssert(targetType.isAddress(), "Function type can only be converted to address."); @@ -821,17 +819,16 @@ void CompilerUtils::convertType( // stack: <address> <function_id> m_context << Instruction::POP; - break; } - } - // fall-through - default: - // All other types should not be convertible to non-equal types. - solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); - if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32) + else + { + // All other types should not be convertible to non-equal types. + solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32) m_context << ((u256(1) << (8 * _targetType.storageBytes())) - 1) << Instruction::AND; + } break; } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 5e45699b..ad3989ad 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -102,13 +102,26 @@ public: /// @note the locations of target reference types are ignored, because it will always be /// memory. void encodeToMemory( - TypePointers const& _givenTypes = {}, - TypePointers const& _targetTypes = {}, - bool _padToWords = true, - bool _copyDynamicDataInPlace = false, + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _padToWords, + bool _copyDynamicDataInPlace, bool _encodeAsLibraryTypes = false ); + /// Special case of @a encodeToMemory which assumes tight packing, e.g. no zero padding + /// and dynamic data is encoded in-place. + /// Stack pre: <value0> <value1> ... <valueN-1> <head_start> + /// Stack post: <mem_ptr> + void packedEncode( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _encodeAsLibraryTypes = false + ) + { + encodeToMemory(_givenTypes, _targetTypes, false, true, _encodeAsLibraryTypes); + } + /// Special case of @a encodeToMemory which assumes that everything is padded to words /// and dynamic data is not copied in place (i.e. a proper ABI encoding). /// Stack pre: <value0> <value1> ... <valueN-1> <head_start> @@ -117,6 +130,20 @@ public: TypePointers const& _givenTypes, TypePointers const& _targetTypes, bool _encodeAsLibraryTypes = false + ) + { + encodeToMemory(_givenTypes, _targetTypes, true, false, _encodeAsLibraryTypes); + } + + /// Special case of @a encodeToMemory which assumes that everything is padded to words + /// and dynamic data is not copied in place (i.e. a proper ABI encoding). + /// Uses a new, less tested encoder implementation. + /// Stack pre: <value0> <value1> ... <valueN-1> <head_start> + /// Stack post: <mem_ptr> + void abiEncodeV2( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _encodeAsLibraryTypes = false ); /// Zero-initialises (the data part of) an already allocated memory array. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 92782b8d..429db532 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -421,7 +421,7 @@ void ContractCompiler::appendReturnValuePacker(TypePointers const& _typeParamete utils.fetchFreeMemoryPointer(); //@todo optimization: if we return a single memory array, there should be enough space before // its data to add the needed parts and we avoid a memory copy. - utils.encodeToMemory(_typeParameters, _typeParameters, true, false, _isLibrary); + utils.abiEncode(_typeParameters, _typeParameters, _isLibrary); utils.toSizeAfterFreeMemoryPointer(); m_context << Instruction::RETURN; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c94baa10..c2bf0f5c 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -581,7 +581,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _context << Instruction::ADD; } ); - utils().encodeToMemory(argumentTypes, function.parameterTypes()); + utils().abiEncode(argumentTypes, function.parameterTypes()); // now on stack: memory_end_ptr // need: size, offset, endowment utils().toSizeAfterFreeMemoryPointer(); @@ -675,7 +675,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) argumentTypes.push_back(arg->annotation().type); } utils().fetchFreeMemoryPointer(); - utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true); + solAssert(!function.padArguments(), ""); + utils().packedEncode(argumentTypes, TypePointers()); utils().toSizeAfterFreeMemoryPointer(); m_context << Instruction::KECCAK256; break; @@ -694,11 +695,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } arguments.front()->accept(*this); utils().fetchFreeMemoryPointer(); - utils().encodeToMemory( + utils().packedEncode( {arguments.front()->annotation().type}, - {function.parameterTypes().front()}, - false, - true); + {function.parameterTypes().front()} + ); utils().toSizeAfterFreeMemoryPointer(); m_context << logInstruction(logNumber); break; @@ -717,11 +717,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) if (auto const& arrayType = dynamic_pointer_cast<ArrayType const>(function.parameterTypes()[arg - 1])) { utils().fetchFreeMemoryPointer(); - utils().encodeToMemory( + utils().packedEncode( {arguments[arg - 1]->annotation().type}, - {arrayType}, - false, - true + {arrayType} ); utils().toSizeAfterFreeMemoryPointer(); m_context << Instruction::KECCAK256; @@ -751,7 +749,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) nonIndexedParamTypes.push_back(function.parameterTypes()[arg]); } utils().fetchFreeMemoryPointer(); - utils().encodeToMemory(nonIndexedArgTypes, nonIndexedParamTypes); + utils().abiEncode(nonIndexedArgTypes, nonIndexedParamTypes); // need: topic1 ... topicn memsize memstart utils().toSizeAfterFreeMemoryPointer(); m_context << logInstruction(numIndexed); @@ -1014,59 +1012,65 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) switch (_memberAccess.expression().annotation().type->category()) { case Type::Category::Contract: + case Type::Category::Integer: { bool alsoSearchInteger = false; - ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); - if (type.isSuper()) + if (_memberAccess.expression().annotation().type->category() == Type::Category::Contract) { - solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); - utils().pushCombinedFunctionEntryLabel(m_context.superFunction( - dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration), - type.contractDefinition() - )); + ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); + if (type.isSuper()) + { + solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); + utils().pushCombinedFunctionEntryLabel(m_context.superFunction( + dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration), + type.contractDefinition() + )); + } + else + { + // ordinary contract type + if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) + { + u256 identifier; + if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration)) + identifier = FunctionType(*variable).externalIdentifier(); + else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration)) + identifier = FunctionType(*function).externalIdentifier(); + else + solAssert(false, "Contract member is neither variable nor function."); + utils().convertType(type, IntegerType(160, IntegerType::Modifier::Address), true); + m_context << identifier; + } + else + // not found in contract, search in members inherited from address + alsoSearchInteger = true; + } } else + alsoSearchInteger = true; + + if (alsoSearchInteger) { - // ordinary contract type - if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) + if (member == "balance") { - u256 identifier; - if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration)) - identifier = FunctionType(*variable).externalIdentifier(); - else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration)) - identifier = FunctionType(*function).externalIdentifier(); - else - solAssert(false, "Contract member is neither variable nor function."); - utils().convertType(type, IntegerType(0, IntegerType::Modifier::Address), true); - m_context << identifier; + utils().convertType( + *_memberAccess.expression().annotation().type, + IntegerType(160, IntegerType::Modifier::Address), + true + ); + m_context << Instruction::BALANCE; } + else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member)) + utils().convertType( + *_memberAccess.expression().annotation().type, + IntegerType(160, IntegerType::Modifier::Address), + true + ); else - // not found in contract, search in members inherited from address - alsoSearchInteger = true; + solAssert(false, "Invalid member access to integer"); } - if (!alsoSearchInteger) - break; - } - // fall-through - case Type::Category::Integer: - if (member == "balance") - { - utils().convertType( - *_memberAccess.expression().annotation().type, - IntegerType(0, IntegerType::Modifier::Address), - true - ); - m_context << Instruction::BALANCE; - } - else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member)) - utils().convertType( - *_memberAccess.expression().annotation().type, - IntegerType(0, IntegerType::Modifier::Address), - true - ); - else - solAssert(false, "Invalid member access to integer"); break; + } case Type::Category::Function: if (member == "selector") { @@ -1206,11 +1210,9 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) utils().fetchFreeMemoryPointer(); // stack: base index mem // note: the following operations must not allocate memory! - utils().encodeToMemory( + utils().packedEncode( TypePointers{_indexAccess.indexExpression()->annotation().type}, - TypePointers{keyType}, - false, - true + TypePointers{keyType} ); m_context << Instruction::SWAP1; utils().storeInMemoryDynamic(IntegerType(256)); diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 522928f0..0ceed3a7 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -72,28 +72,21 @@ void Z3Interface::addAssertion(Expression const& _expr) pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _expressionsToEvaluate) { -// cout << "---------------------------------" << endl; -// cout << m_solver << endl; CheckResult result; switch (m_solver.check()) { case z3::check_result::sat: result = CheckResult::SATISFIABLE; - cout << "sat" << endl; break; case z3::check_result::unsat: result = CheckResult::UNSATISFIABLE; - cout << "unsat" << endl; break; case z3::check_result::unknown: result = CheckResult::UNKNOWN; - cout << "unknown" << endl; break; default: solAssert(false, ""); } -// cout << "---------------------------------" << endl; - vector<string> values; if (result != CheckResult::UNSATISFIABLE) @@ -142,7 +135,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return m_context.int_val(n.c_str()); } - assert(arity.count(n) && arity.at(n) == arguments.size()); + solAssert(arity.count(n) && arity.at(n) == arguments.size(), ""); if (n == "ite") return z3::ite(arguments[0], arguments[1], arguments[2]); else if (n == "not") diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 3087ad86..1f4df75b 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -256,7 +256,7 @@ std::map<string, dev::solidity::Instruction> const& Parser::instructions() { if ( instruction.second == solidity::Instruction::JUMPDEST || - (solidity::Instruction::PUSH1 <= instruction.second && instruction.second <= solidity::Instruction::PUSH32) + solidity::isPushInstruction(instruction.second) ) continue; string name = instruction.first; @@ -443,9 +443,9 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) ret.location = ret.instruction.location; solidity::Instruction instr = ret.instruction.instruction; InstructionInfo instrInfo = instructionInfo(instr); - if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16) + if (solidity::isDupInstruction(instr)) fatalParserError("DUPi instructions not allowed for functional notation"); - if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16) + if (solidity::isSwapInstruction(instr)) fatalParserError("SWAPi instructions not allowed for functional notation"); expectToken(Token::LParen); unsigned args = unsigned(instrInfo.args); diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 852b392c..22cc0266 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -48,7 +48,7 @@ GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimatio ControlFlowGraph cfg(_items); for (BasicBlock const& block: cfg.optimisedBlocks()) { - assertThrow(!!block.startState, OptimizerException, ""); + solAssert(!!block.startState, ""); GasMeter meter(block.startState->copy()); auto const end = _items.begin() + block.end; for (auto iter = _items.begin() + block.begin; iter != end; ++iter) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 9a8bb358..821e81d2 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1133,6 +1133,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme options.allowVar = true; options.allowLocationSpecifier = true; variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType)); + nodeFactory.setEndPositionFromNode(variables.back()); } if (m_scanner->currentToken() == Token::Assign) { diff --git a/lllc/main.cpp b/lllc/main.cpp index 06a0fc81..912ce16a 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char** argv) } else if (mode == Binary || mode == Hex) { - auto bs = compileLLL(src, optimise ? true : false, &errors); + auto bs = compileLLL(src, optimise ? true : false, &errors, contentsString); if (mode == Hex) cout << toHex(bs) << endl; else if (mode == Binary) @@ -147,7 +147,7 @@ int main(int argc, char** argv) else if (mode == ParseTree) cout << parseLLL(src) << endl; else if (mode == Assembly) - cout << compileLLLToAsm(src, optimise ? true : false, &errors) << endl; + cout << compileLLLToAsm(src, optimise ? true : false, &errors, contentsString) << endl; for (auto const& i: errors) cerr << i << endl; if ( errors.size() ) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 3a1abe10..01dee81d 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -256,15 +256,6 @@ case $(uname -s) in #------------------------------------------------------------------------------ # Ubuntu # -# TODO - I wonder whether all of the Ubuntu-variants need some special -# treatment? -# -# TODO - We should also test this code on Ubuntu Server, Ubuntu Snappy Core -# and Ubuntu Phone. -# -# TODO - Our Ubuntu build is only working for amd64 and i386 processors. -# It would be good to add armel, armhf and arm64. -# See https://github.com/ethereum/webthree-umbrella/issues/228. #------------------------------------------------------------------------------ Ubuntu) @@ -320,6 +311,14 @@ case $(uname -s) in libboost-all-dev \ "$install_z3" if [ "$CI" = true ]; then + # install Z3 from PPA if the distribution does not provide it + if ! dpkg -l libz3-dev > /dev/null 2>&1 + then + sudo apt-add-repository -y ppa:hvr/z3 + sudo apt-get -y update + sudo apt-get -y install libz3-dev + fi + # Install 'eth', for use in the Solidity Tests-over-IPC. # We will not use this 'eth', but its dependencies sudo add-apt-repository -y ppa:ethereum/ethereum diff --git a/scripts/tests.sh b/scripts/tests.sh index 5d7eb0e1..bf3e3455 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -42,8 +42,8 @@ elif [ -z $CI ]; then ETH_PATH="eth" else mkdir -p /tmp/test - wget -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth - test "$(shasum /tmp/test/eth)" = "c132e8989229e4840831a4fb1a1d058b732a11d5 /tmp/test/eth" + wget -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth_byzantium + test "$(shasum /tmp/test/eth)" = "6e16ae5e0a0079d85fd63fb43547be3c52410e7e /tmp/test/eth" sync chmod +x /tmp/test/eth sync # Otherwise we might get a "text file busy" error diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index c4fbfefb..768c8c4b 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -217,11 +217,11 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts) { "sealEngine": "NoProof", "params": { - "accountStartNonce": "0x", + "accountStartNonce": "0x00", "maximumExtraDataSize": "0x1000000", "blockReward": "0x", - "allowFutureBlocks": "1", - "homsteadForkBlock": "0x00", + "allowFutureBlocks": true, + "homesteadForkBlock": "0x00", "EIP150ForkBlock": "0x00", "EIP158ForkBlock": "0x00" }, diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 094b59c6..c8747a06 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -45,6 +45,8 @@ Options::Options() showMessages = true; else if (string(suite.argv[i]) == "--no-ipc") disableIPC = true; + else if (string(suite.argv[i]) == "--no-smt") + disableSMT = true; if (!disableIPC && ipcPath.empty()) if (auto path = getenv("ETH_TEST_IPC")) diff --git a/test/TestHelper.h b/test/TestHelper.h index d50568ad..d25c5cd8 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -35,6 +35,7 @@ struct Options: boost::noncopyable bool showMessages = false; bool optimize = false; bool disableIPC = false; + bool disableSMT = false; static Options const& get(); diff --git a/test/boostTest.cpp b/test/boostTest.cpp index d8c5b678..7b452e06 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -39,6 +39,17 @@ using namespace boost::unit_test; +namespace +{ +void removeTestSuite(std::string const& _name) +{ + master_test_suite_t& master = framework::master_test_suite(); + auto id = master.get(_name); + assert(id != INV_TEST_UNIT_ID); + master.remove(id); +} +} + test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); @@ -57,12 +68,10 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) "SolidityEndToEndTest", "SolidityOptimizer" }) - { - auto id = master.get(suite); - assert(id != INV_TEST_UNIT_ID); - master.remove(id); - } + removeTestSuite(suite); } + if (dev::test::Options::get().disableSMT) + removeTestSuite("SMTChecker"); return 0; } diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp new file mode 100644 index 00000000..2d66bce1 --- /dev/null +++ b/test/liblll/Compiler.cpp @@ -0,0 +1,128 @@ +/* + 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/>. +*/ +/** + * @author Alex Beregszaszi + * @date 2017 + * Unit tests for the LLL compiler. + */ + +#include <string> +#include <memory> +#include <boost/test/unit_test.hpp> +#include <liblll/Compiler.h> + +using namespace std; + +namespace dev +{ +namespace lll +{ +namespace test +{ + +namespace +{ + +bool successCompile(std::string const& _sourceCode) +{ + std::vector<std::string> errors; + bytes bytecode = eth::compileLLL(_sourceCode, false, &errors); + if (!errors.empty()) + return false; + if (bytecode.empty()) + return false; + return true; +} + +} + +BOOST_AUTO_TEST_SUITE(LLLCompiler) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + char const* sourceCode = "1"; + BOOST_CHECK(successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_valid) +{ + char const* sourceCode = R"( + (switch (origin)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (panic) + 2 (panic)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (panic) + 2 (panic) + (panic)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + (origin)) + )"; + BOOST_CHECK(successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_arg_count) +{ + char const* sourceCode = R"( + (switch) + )"; + BOOST_CHECK(!successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_inconsistent_return_count) +{ + // cannot return stack items if the default case is not present + char const* sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + )"; + BOOST_CHECK(!successCompile(sourceCode)); + // return count mismatch + sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + (panic)) + )"; + BOOST_CHECK(!successCompile(sourceCode)); + // return count mismatch + sourceCode = R"( + (switch + 1 (panic) + 2 (panic) + (origin)) + )"; + BOOST_CHECK(!successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index 9292d963..1a5bb490 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -215,6 +215,92 @@ BOOST_AUTO_TEST_CASE(conditional_nested_then) BOOST_CHECK(callContractFunction("test()", 0xfc) == encodeArgs(u256(6))); } +BOOST_AUTO_TEST_CASE(conditional_switch) +{ + char const* sourceCode = R"( + (returnlll + (seq + (def 'input (calldataload 0x04)) + ;; Calculates width in bytes of utf-8 characters. + (return + (switch + (< input 0x80) 1 + (< input 0xE0) 2 + (< input 0xF0) 3 + (< input 0xF8) 4 + (< input 0xFC) 5 + 6)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0x00) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("test()", 0x80) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("test()", 0xe0) == encodeArgs(u256(3))); + BOOST_CHECK(callContractFunction("test()", 0xf0) == encodeArgs(u256(4))); + BOOST_CHECK(callContractFunction("test()", 0xf8) == encodeArgs(u256(5))); + BOOST_CHECK(callContractFunction("test()", 0xfc) == encodeArgs(u256(6))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_one_arg_with_deposit) +{ + char const* sourceCode = R"( + (returnlll + (return + (switch 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_one_arg_no_deposit) +{ + char const* sourceCode = R"( + (returnlll + (seq + (switch [0]:42) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_two_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (switch (= (calldataload 0x04) 1) [0]:42) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_three_args_with_deposit) +{ + char const* sourceCode = R"( + (returnlll + (return + (switch (= (calldataload 0x04) 1) 41 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(42))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(41))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_three_args_no_deposit) +{ + char const* sourceCode = R"( + (returnlll + (switch + (= (calldataload 0x04) 1) (return 41) + (return 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(42))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(41))); +} + BOOST_AUTO_TEST_CASE(exp_operator_const) { char const* sourceCode = R"( diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 5f5f6411..3bdc40a0 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -25,6 +25,8 @@ #include <libsolidity/ast/AST.h> +#include <libsolidity/parsing/Scanner.h> + #include <libdevcore/SHA3.h> #include <boost/test/unit_test.hpp> @@ -46,8 +48,7 @@ AnalysisFramework::parseAnalyseAndReturnError( m_compiler.addSource("", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source); if (!m_compiler.parse()) { - printErrors(); - BOOST_ERROR("Parsing contract failed in analysis test suite."); + BOOST_ERROR("Parsing contract failed in analysis test suite:" + formatErrors()); } m_compiler.analyze(); @@ -56,15 +57,24 @@ AnalysisFramework::parseAnalyseAndReturnError( for (auto const& currentError: m_compiler.errors()) { solAssert(currentError->comment(), ""); - if (currentError->comment()->find("This is a pre-release compiler version") == 0) - continue; + if (currentError->type() == Error::Type::Warning) + { + bool ignoreWarning = false; + for (auto const& filter: m_warningsToFilter) + if (currentError->comment()->find(filter) == 0) + { + ignoreWarning = true; + break; + } + if (ignoreWarning) + continue; + } if (_reportWarnings || (currentError->type() != Error::Type::Warning)) { if (firstError && !_allowMultipleErrors) { - printErrors(); - BOOST_FAIL("Multiple errors found."); + BOOST_FAIL("Multiple errors found: " + formatErrors()); } if (!firstError) firstError = currentError; @@ -78,7 +88,10 @@ SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source) { auto sourceAndError = parseAnalyseAndReturnError(_source); BOOST_REQUIRE(!!sourceAndError.first); - BOOST_REQUIRE(!sourceAndError.second); + string message; + if (sourceAndError.second) + message = "Unexpected error: " + formatError(*sourceAndError.second); + BOOST_REQUIRE_MESSAGE(!sourceAndError.second, message); return sourceAndError.first; } @@ -91,17 +104,23 @@ Error AnalysisFramework::expectError(std::string const& _source, bool _warning, { auto sourceAndError = parseAnalyseAndReturnError(_source, _warning, true, _allowMultiple); BOOST_REQUIRE(!!sourceAndError.second); - BOOST_REQUIRE(!!sourceAndError.first); + BOOST_REQUIRE_MESSAGE(!!sourceAndError.first, "Expected error, but no error happened."); return *sourceAndError.second; } -void AnalysisFramework::printErrors() +string AnalysisFramework::formatErrors() { + string message; for (auto const& error: m_compiler.errors()) - SourceReferenceFormatter::printExceptionInformation( - std::cerr, - *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", + message += formatError(*error); + return message; +} + +string AnalysisFramework::formatError(Error const& _error) +{ + return SourceReferenceFormatter::formatExceptionInformation( + _error, + (_error.type() == Error::Type::Warning) ? "Warning" : "Error", [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } ); } diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index 172ae01b..a566ba1d 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -45,7 +45,7 @@ class AnalysisFramework { protected: - std::pair<SourceUnit const*, std::shared_ptr<Error const>> + virtual std::pair<SourceUnit const*, std::shared_ptr<Error const>> parseAnalyseAndReturnError( std::string const& _source, bool _reportWarnings = false, @@ -57,7 +57,8 @@ protected: bool success(std::string const& _source); Error expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false); - void printErrors(); + std::string formatErrors(); + std::string formatError(Error const& _error); static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name); static FunctionTypePointer retrieveFunctionBySignature( @@ -65,6 +66,7 @@ protected: std::string const& _signature ); + std::vector<std::string> m_warningsToFilter = {"This is a pre-release compiler version"}; dev::solidity::CompilerStack m_compiler; }; @@ -104,7 +106,10 @@ CHECK_ERROR_OR_WARNING(text, Warning, substring, true, true) do \ { \ auto sourceAndError = parseAnalyseAndReturnError((text), true); \ - BOOST_CHECK(sourceAndError.second == nullptr); \ + std::string message; \ + if (sourceAndError.second) \ + message = formatError(*sourceAndError.second); \ + BOOST_CHECK_MESSAGE(!sourceAndError.second, message); \ } \ while(0) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index b759678f..c2886f5b 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -21,7 +21,6 @@ */ #include <test/libsolidity/SolidityExecutionFramework.h> -#include <libevmasm/EVMSchedule.h> #include <libevmasm/GasMeter.h> #include <libevmasm/KnownState.h> #include <libevmasm/PathGasMeter.h> @@ -63,15 +62,13 @@ public: void testCreationTimeGas(string const& _sourceCode) { - EVMSchedule schedule; - compileAndRun(_sourceCode); auto state = make_shared<KnownState>(); PathGasMeter meter(*m_compiler.assemblyItems()); GasMeter::GasConsumption gas = meter.estimateMax(0, state); u256 bytecodeSize(m_compiler.runtimeObject().bytecode.size()); // costs for deployment - gas += bytecodeSize * schedule.createDataGas; + gas += bytecodeSize * GasCosts::createDataGas; // costs for transaction gas += gasForTransaction(m_compiler.object().bytecode, true); @@ -103,10 +100,9 @@ public: static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) { - EVMSchedule schedule; - GasMeter::GasConsumption gas = _isCreation ? schedule.txCreateGas : schedule.txGas; + GasMeter::GasConsumption gas = _isCreation ? GasCosts::txCreateGas : GasCosts::txGas; for (auto i: _data) - gas += i != 0 ? schedule.txDataNonZeroGas : schedule.txDataZeroGas; + gas += i != 0 ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; return gas; } diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp new file mode 100644 index 00000000..d58f296f --- /dev/null +++ b/test/libsolidity/SMTChecker.cpp @@ -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/>. +*/ +/** + * Unit tests for the SMT checker. + */ + +#include <test/libsolidity/AnalysisFramework.h> + +#include <boost/test/unit_test.hpp> + +#include <string> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +class SMTCheckerFramework: public AnalysisFramework +{ +public: + SMTCheckerFramework() + { + m_warningsToFilter.push_back("Experimental features are turned on."); + } + +protected: + virtual std::pair<SourceUnit const*, std::shared_ptr<Error const>> + parseAnalyseAndReturnError( + std::string const& _source, + bool _reportWarnings = false, + bool _insertVersionPragma = true, + bool _allowMultipleErrors = false + ) + { + return AnalysisFramework::parseAnalyseAndReturnError( + "pragma experimental SMTChecker;\n" + _source, + _reportWarnings, + _insertVersionPragma, + _allowMultipleErrors + ); + } +}; + +BOOST_FIXTURE_TEST_SUITE(SMTChecker, SMTCheckerFramework) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + string text = R"( + contract C { } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(simple_overflow) +{ + string text = R"( + contract C { + function f(uint a, uint b) public pure returns (uint) { return a + b; } + } + )"; + CHECK_WARNING(text, "Overflow (resulting value larger than"); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 82dedf85..35916ec7 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1457,7 +1457,7 @@ BOOST_AUTO_TEST_CASE(msg_sig) } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo(uint256)"), encodeArgs(asString(FixedHash<4>(dev::keccak256("foo(uint256)")).asBytes()))); + ABI_CHECK(callContractFunction("foo(uint256)", 0), encodeArgs(asString(FixedHash<4>(dev::keccak256("foo(uint256)")).asBytes()))); } BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) @@ -1473,7 +1473,7 @@ BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo(uint256)"), encodeArgs(asString(FixedHash<4>(dev::keccak256("foo(uint256)")).asBytes()))); + ABI_CHECK(callContractFunction("foo(uint256)", 0), encodeArgs(asString(FixedHash<4>(dev::keccak256("foo(uint256)")).asBytes()))); } BOOST_AUTO_TEST_CASE(now) @@ -1494,7 +1494,7 @@ BOOST_AUTO_TEST_CASE(now) size_t endTime = blockTimestamp(endBlock); BOOST_CHECK(startBlock != endBlock); BOOST_CHECK(startTime != endTime); - BOOST_CHECK(ret == encodeArgs(true, endTime)); + ABI_CHECK(ret, encodeArgs(true, endTime)); } BOOST_AUTO_TEST_CASE(type_conversions_cleanup) @@ -3090,7 +3090,7 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics) char const* sourceCode = R"( contract ClientReceipt { event Deposit(address indexed _from, bytes32 indexed _id, uint indexed _value, uint indexed _value2, bytes32 data) anonymous; - function deposit(bytes32 _id, bool _manually) payable { + function deposit(bytes32 _id) payable { Deposit(msg.sender, _id, msg.value, 2, "abc"); } } @@ -3098,7 +3098,7 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics) compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); - callContractFunctionWithValue("deposit(bytes32,bool)", value, id); + callContractFunctionWithValue("deposit(bytes32)", value, id); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK(m_logs[0].data == encodeArgs("abc")); @@ -3757,11 +3757,11 @@ BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete) compileAndRun(sourceCode); string data = "123456789012345678901234567890123"; BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, u256(data.length()), 13, data), encodeArgs(true)); + ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); BOOST_CHECK(!storageEmpty(m_contractAddress)); ABI_CHECK(callContractFunction("copy()"), encodeArgs(true)); BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, u256(data.length()), 13, data), encodeArgs(true)); + ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); BOOST_CHECK(!storageEmpty(m_contractAddress)); ABI_CHECK(callContractFunction("del()"), encodeArgs(true)); BOOST_CHECK(storageEmpty(m_contractAddress)); @@ -5185,7 +5185,7 @@ BOOST_AUTO_TEST_CASE(packed_storage_signed) } )"; compileAndRun(sourceCode); - BOOST_CHECK( callContractFunction("test()") == encodeArgs(u256(-2), u256(4), u256(-112), u256(0))); + ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(-2), u256(4), u256(-112), u256(0))); } BOOST_AUTO_TEST_CASE(external_types_in_calls) @@ -5540,7 +5540,7 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) } } )"; - BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "A").empty()); + ABI_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "A"), encodeArgs()); } BOOST_AUTO_TEST_CASE(positive_integers_to_signed) @@ -6505,31 +6505,32 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key) "1" }; for (unsigned i = 0; i < strings.size(); i++) - BOOST_CHECK(callContractFunction( + ABI_CHECK(callContractFunction( "set(string,uint256)", u256(0x40), u256(7 + i), u256(strings[i].size()), strings[i] - ) == encodeArgs()); + ), encodeArgs()); for (unsigned i = 0; i < strings.size(); i++) - BOOST_CHECK(callContractFunction( + ABI_CHECK(callContractFunction( "get(string)", u256(0x20), u256(strings[i].size()), strings[i] - ) == encodeArgs(u256(7 + i))); + ), encodeArgs(u256(7 + i))); } BOOST_AUTO_TEST_CASE(accessor_for_state_variable) { char const* sourceCode = R"( - contract Lotto{ + contract Lotto { uint public ticketPrice = 500; - })"; + } + )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("ticketPrice()"), encodeArgs(u256(500))); + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("ticketPrice()"), encodeArgs(u256(500))); } BOOST_AUTO_TEST_CASE(accessor_for_const_state_variable) @@ -6537,10 +6538,11 @@ BOOST_AUTO_TEST_CASE(accessor_for_const_state_variable) char const* sourceCode = R"( contract Lotto{ uint constant public ticketPrice = 555; - })"; + } + )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("ticketPrice()"), encodeArgs(u256(555))); + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("ticketPrice()"), encodeArgs(u256(555))); } BOOST_AUTO_TEST_CASE(state_variable_under_contract_name) @@ -7402,7 +7404,7 @@ BOOST_AUTO_TEST_CASE(string_allocation_bug) } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("p(uint256)"), encodeArgs( + ABI_CHECK(callContractFunction("p(uint256)", 0), encodeArgs( u256(0xbbbb), u256(0xcccc), u256(0x80), @@ -7913,7 +7915,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_function_call_assignment) } )"; compileAndRun(sourceCode, 0, "C"); - BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1), u256(2), u256(7))); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(2), u256(7))); } BOOST_AUTO_TEST_CASE(inline_assembly_function_call2) @@ -8776,8 +8778,8 @@ BOOST_AUTO_TEST_CASE(return_external_function_type) )"; compileAndRun(sourceCode, 0, "C"); - BOOST_CHECK( - callContractFunction("f()") == + ABI_CHECK( + callContractFunction("f()"), m_contractAddress.asBytes() + FixedHash<4>(dev::keccak256("g()")).asBytes() + bytes(32 - 4 - 20, 0) ); } @@ -10120,9 +10122,9 @@ BOOST_AUTO_TEST_CASE(function_types_sig) } )"; compileAndRun(sourceCode, 0, "C"); - BOOST_CHECK(callContractFunction("f()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); - BOOST_CHECK(callContractFunction("g()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); - BOOST_CHECK(callContractFunction("h()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); + ABI_CHECK(callContractFunction("f()"), encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); + ABI_CHECK(callContractFunction("g()"), encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); + ABI_CHECK(callContractFunction("h()"), encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); } BOOST_AUTO_TEST_CASE(constant_string) @@ -10144,9 +10146,34 @@ BOOST_AUTO_TEST_CASE(constant_string) } )"; compileAndRun(sourceCode, 0, "C"); - BOOST_CHECK(callContractFunction("f()") == encodeDyn(string("\x03\x01\x02"))); - BOOST_CHECK(callContractFunction("g()") == encodeDyn(string("\x03\x01\x02"))); - BOOST_CHECK(callContractFunction("h()") == encodeDyn(string("hello"))); + ABI_CHECK(callContractFunction("f()"), encodeDyn(string("\x03\x01\x02"))); + ABI_CHECK(callContractFunction("g()"), encodeDyn(string("\x03\x01\x02"))); + ABI_CHECK(callContractFunction("h()"), encodeDyn(string("hello"))); +} + +BOOST_AUTO_TEST_CASE(address_overload_resolution) +{ + char const* sourceCode = R"( + contract C { + function balance() returns (uint) { + return 1; + } + function transfer(uint amount) returns (uint) { + return amount; + } + } + contract D { + function f() returns (uint) { + return (new C()).balance(); + } + function g() returns (uint) { + return (new C()).transfer(5); + } + } + )"; + compileAndRun(sourceCode, 0, "D"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(5))); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 39c47f9c..ed223678 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1395,6 +1395,61 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(events_with_same_name_unnamed_arguments) +{ + char const* text = R"( + contract test { + event A(uint); + event A(uint, uint); + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(events_with_same_name_different_types) +{ + char const* text = R"( + contract test { + event A(uint); + event A(bytes); + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(double_event_declaration) +{ + char const* text = R"( + contract test { + event A(uint i); + event A(uint i); + } + )"; + CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); +} + +BOOST_AUTO_TEST_CASE(double_event_declaration_ignores_anonymous) +{ + char const* text = R"( + contract test { + event A(uint i); + event A(uint i) anonymous; + } + )"; + CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); +} + +BOOST_AUTO_TEST_CASE(double_event_declaration_ignores_indexed) +{ + char const* text = R"( + contract test { + event A(uint i); + event A(uint indexed i); + } + )"; + CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); +} + BOOST_AUTO_TEST_CASE(event_call) { char const* text = R"( @@ -2306,17 +2361,28 @@ BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); } -BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable) +BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable_0_4_x) { char const* text = R"( contract C { address constant x = msg.sender; } )"; - // Change to TypeError for 0.5.0. CHECK_WARNING(text, "Initial value for constant variable has to be compile-time constant."); } +BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + + contract C { + address constant x = msg.sender; + } + )"; + CHECK_ERROR(text, TypeError, "Initial value for constant variable has to be compile-time constant."); +} + BOOST_AUTO_TEST_CASE(constant_string_literal_disallows_assignment) { char const* text = R"( @@ -2333,7 +2399,7 @@ BOOST_AUTO_TEST_CASE(constant_string_literal_disallows_assignment) CHECK_ERROR(text, TypeError, "Index access for string is not possible."); } -BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant) +BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant_0_4_x) { char const* text = R"( contract C { @@ -2341,10 +2407,22 @@ BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant) uint constant y = x(); } )"; - // Change to TypeError for 0.5.0. CHECK_WARNING(text, "Initial value for constant variable has to be compile-time constant."); } +BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + + contract C { + function () constant returns (uint) x; + uint constant y = x(); + } + )"; + CHECK_ERROR(text, TypeError, "Initial value for constant variable has to be compile-time constant."); +} + BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_conversion) { char const* text = R"( @@ -4135,6 +4213,8 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) } )"; CHECK_SUCCESS_NO_WARNINGS(text); + + // Test deprecation warning under < 0.5.0 text = R"( contract test { function f() pure public { @@ -4154,6 +4234,29 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) } )"; CHECK_WARNING(text,"Use of unary + is deprecated"); + + // Test syntax error under 0.5.0 + text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + ufixed16x2 a = +3.25; + fixed16x2 b = -3.25; + a; b; + } + } + )"; + CHECK_ERROR(text, SyntaxError, "Use of unary + is deprecated"); + text = R"( + pragma experimental "v0.5.0"; + contract test { + function f(uint x) pure public { + uint y = +x; + y; + } + } + )"; + CHECK_ERROR(text, SyntaxError, "Use of unary + is deprecated"); } BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) @@ -4251,7 +4354,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_rational) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); + CHECK_ERROR(text, TypeError, "Array with fractional length specified."); } BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) @@ -4263,7 +4366,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); } BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) @@ -4275,7 +4378,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); } BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) @@ -6953,6 +7056,39 @@ BOOST_AUTO_TEST_CASE(warn_about_suicide) CHECK_WARNING(text, "\"suicide\" has been deprecated in favour of \"selfdestruct\""); } +BOOST_AUTO_TEST_CASE(address_overload_resolution) +{ + char const* text = R"( + contract C { + function balance() returns (uint) { + this.balance; // to avoid pureness warning + return 1; + } + function transfer(uint amount) { + address(this).transfer(amount); // to avoid pureness warning + } + } + contract D { + function f() { + var x = (new C()).balance(); + x; + (new C()).transfer(5); + } + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(array_length_validation) +{ + char const* text = R"( + contract C { + uint[8**90] ids; + } + )"; + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); +} + BOOST_AUTO_TEST_SUITE_END() } |