diff --git a/.circleci/config.yml b/.circleci/config.yml index 47c6518ee..3e9cebb06 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -104,17 +104,6 @@ defaults: name: command line tests command: ./test/cmdlineTests.sh - - test_ubuntu1904: &test_ubuntu1904 - docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 - steps: - - checkout - - attach_workspace: - at: build - - run: *run_soltest - - store_test_results: *store_test_results - - store_artifacts: *artifacts_test_results - - test_ubuntu1904_clang: &test_ubuntu1904_clang docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang @@ -126,7 +115,7 @@ defaults: - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results - - test_ubuntu1904_all: &test_ubuntu1904 + - test_ubuntu1904: &test_ubuntu1904 docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904 steps: @@ -423,40 +412,61 @@ jobs: b_osx: macos: - xcode: "10.0.0" + xcode: "11.0.0" environment: TERM: xterm CMAKE_BUILD_TYPE: Debug - CMAKE_OPTIONS: -DLLL=ON steps: - checkout + - restore_cache: + keys: + - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} - run: name: Install build dependencies - command: | - brew unlink python - brew install z3 - brew install boost - brew install cmake - brew install wget - ./scripts/install_obsolete_jsoncpp_1_7_4.sh + command: ./.circleci/osx_install_dependencies.sh + - save_cache: + key: dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + paths: + - /usr/local/bin + - /usr/local/sbin + - /usr/local/lib + - /usr/local/include + - /usr/local/Cellar + - /usr/local/Homebrew - run: *run_build - store_artifacts: *artifacts_solc - - persist_to_workspace: *artifacts_executables + - persist_to_workspace: *artifacts_build_dir + + t_osx_soltest: + macos: + xcode: "11.0.0" + environment: + EVM: constantinople + OPTIMIZE: 0 + TERM: xterm + steps: + - checkout + - restore_cache: + keys: + - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + - attach_workspace: + at: build + - run: *run_soltest + - store_test_results: *store_test_results + - store_artifacts: *artifacts_test_results t_osx_cli: macos: - xcode: "10.0.0" + xcode: "11.0.0" environment: TERM: xterm steps: - checkout + - restore_cache: + keys: + - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} - attach_workspace: at: build - - run: - name: Install dependencies - command: | - brew unlink python - brew install z3 - run: *run_cmdline_tests - store_artifacts: *artifacts_test_results @@ -568,7 +578,7 @@ jobs: environment: EVM: constantinople OPTIMIZE: 0 - flags: --no-smt + SOLTEST_FLAGS: --no-smt ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 t_ems_solcjs: @@ -660,6 +670,7 @@ workflows: # OS/X build and tests - b_osx: *workflow_trigger_on_tags - t_osx_cli: *workflow_osx + - t_osx_soltest: *workflow_osx # Ubuntu build and tests - b_ubu: *workflow_trigger_on_tags diff --git a/.circleci/docker/Dockerfile.clang.ubuntu1904 b/.circleci/docker/Dockerfile.clang.ubuntu1904 index d21d26bcb..4fb9b1132 100644 --- a/.circleci/docker/Dockerfile.clang.ubuntu1904 +++ b/.circleci/docker/Dockerfile.clang.ubuntu1904 @@ -57,12 +57,12 @@ RUN git clone --recursive -b boost-1.69.0 https://github.com/boostorg/boost.git ./bootstrap.sh --with-toolset=clang --prefix=/usr; \ ./b2 toolset=clang headers; \ ./b2 toolset=clang variant=release \ - system regex filesystem unit_test_framework program_options \ + system filesystem unit_test_framework program_options \ install -j $(($(nproc)/2)); \ rm -rf /usr/src/boost # Z3 -RUN git clone --depth 1 -b Z3-4.8.5 https://github.com/Z3Prover/z3.git \ +RUN git clone --depth 1 -b z3-4.8.6 https://github.com/Z3Prover/z3.git \ /usr/src/z3; \ cd /usr/src/z3; \ python scripts/mk_make.py --prefix=/usr ; \ diff --git a/.circleci/docker/Dockerfile.ubuntu1804 b/.circleci/docker/Dockerfile.ubuntu1804 index 8d6e25289..0242661e7 100644 --- a/.circleci/docker/Dockerfile.ubuntu1804 +++ b/.circleci/docker/Dockerfile.ubuntu1804 @@ -34,7 +34,7 @@ RUN set -ex; \ build-essential \ software-properties-common \ cmake ninja-build clang++-8 \ - libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev \ + libboost-filesystem-dev libboost-test-dev libboost-system-dev \ libboost-program-options-dev \ libjsoncpp-dev \ llvm-8-dev libz3-static-dev \ diff --git a/.circleci/docker/Dockerfile.ubuntu1904 b/.circleci/docker/Dockerfile.ubuntu1904 index f356d0bde..504e13d1f 100644 --- a/.circleci/docker/Dockerfile.ubuntu1904 +++ b/.circleci/docker/Dockerfile.ubuntu1904 @@ -34,7 +34,7 @@ RUN set -ex; \ build-essential \ software-properties-common \ cmake ninja-build clang++-8 libc++-8-dev libc++abi-8-dev \ - libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev \ + libboost-filesystem-dev libboost-test-dev libboost-system-dev \ libboost-program-options-dev \ libjsoncpp-dev \ llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \ diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh new file mode 100755 index 000000000..22e14af7e --- /dev/null +++ b/.circleci/osx_install_dependencies.sh @@ -0,0 +1,59 @@ +#! /bin/bash +#------------------------------------------------------------------------------ +# Bash script to install osx dependencies +# +# The documentation for solidity is hosted at: +# +# https://solidity.readthedocs.org +# +# ------------------------------------------------------------------------------ +# 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 +# +# (c) 2016-2019 solidity contributors. +# ------------------------------------------------------------------------------ + +# note that the following directories may be cached by circleci: +# - /usr/local/bin +# - /usr/local/sbin +# - /usr/local/lib +# - /usr/local/include +# - /usr/local/Cellar +# - /usr/local/Homebrew + +if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies +then + brew unlink python + brew install boost + brew install cmake + brew install wget + brew install coreutils + ./scripts/install_obsolete_jsoncpp_1_7_4.sh + + # z3 + wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.6/z3-4.8.6-x64-osx-10.14.6.zip + unzip z3-4.8.6-x64-osx-10.14.6.zip + rm -f z3-4.8.6-x64-osx-10.14.6.zip + cp z3-4.8.6-x64-osx-10.14.6/bin/libz3.a /usr/local/lib + cp z3-4.8.6-x64-osx-10.14.6/bin/z3 /usr/local/bin + cp z3-4.8.6-x64-osx-10.14.6/include/* /usr/local/include + rm -rf z3-4.8.6-x64-osx-10.14.6 + + # evmone + wget https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-darwin-x86_64.tar.gz + tar xzpf evmone-0.1.0-darwin-x86_64.tar.gz -C /usr/local + rm -f evmone-0.1.0-darwin-x86_64.tar.gz +fi + diff --git a/.circleci/soltest.sh b/.circleci/soltest.sh index 41f08db77..948c79dee 100755 --- a/.circleci/soltest.sh +++ b/.circleci/soltest.sh @@ -12,6 +12,7 @@ # EVM=version_string Specifies EVM version to compile for (such as homestead, etc) # OPTIMIZE=1 Enables backend optimizer # ABI_ENCODER_V2=1 Enables ABI encoder version 2 +# SOLTEST_FLAGS= Appends to default SOLTEST_ARGS # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -54,7 +55,7 @@ get_logfile_basename() { } BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml" -SOLTEST_ARGS="--evm-version=$EVM --evmonepath /usr/lib/libevmone.so $flags" +SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS" test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize" test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul" diff --git a/CMakeLists.txt b/CMakeLists.txt index a1927bf33..07f26e291 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.5.12") +set(PROJECT_VERSION "0.5.13") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) include(TestBigEndian) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 3f44cd1f9..0d17fad18 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -119,7 +119,7 @@ Use `solAssert` and `solUnimplementedAssert` generously to check assumptions tha 4. Favour declarations close to use; don't habitually declare at top of scope ala C. 5. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move. 6. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments. -7. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer. +7. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``std::optional`` is better suited than a raw pointer. 8. Never use a macro where adequate non-preprocessor C++ can be written. 9. Only use ``auto`` if the type is very long and rather irrelevant. 10. Do not pass bools: prefer enumerations instead. @@ -135,12 +135,12 @@ enum class Accuracy }; struct MeanSigma { - float mean; - float standardDeviation; + float mean = 0.0f; + float standardDeviation = 1.0f; }; double const d = 0; -int i; -int j; +int i = 0; +int j = 0; char* s; MeanAndSigma ms meanAndSigma(std::vector const& _v, Accuracy _a); Derived* x = dynamic_cast(base); diff --git a/Changelog.md b/Changelog.md index f184c0568..3ffe69997 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,30 @@ +### 0.5.13 (2019-11-14) + +Language Features: + * Allow to obtain the address of a linked library with ``address(LibraryName)``. + + +Compiler Features: + * Code Generator: Use SELFBALANCE opcode for ``address(this).balance`` if using Istanbul EVM. + * EWasm: Experimental EWasm binary output via ``--ewasm`` and as documented in standard-json. + * SMTChecker: Add break/continue support to the CHC engine. + * SMTChecker: Support assignments to multi-dimensional arrays and mappings. + * SMTChecker: Support inheritance and function overriding. + * Standard JSON Interface: Output the storage layout of a contract when artifact ``storageLayout`` is requested. + * TypeChecker: List possible candidates when overload resolution fails. + * TypeChecker: Disallow variables of library types. + + +Bugfixes: + * Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer. + * SMTChecker: Fix internal error when accessing indices of fixed bytes. + * SMTChecker: Fix internal error when using function pointers as arguments. + * SMTChecker: Fix internal error when implicitly converting string literals to fixed bytes. + * Type Checker: Disallow constructor of the same class to be used as modifier. + * Type Checker: Treat magic variables as unknown identifiers in inline assembly. + + + ### 0.5.12 (2019-10-01) Language Features: @@ -10,6 +37,7 @@ Compiler Features: * SMTChecker: Add loop support to the CHC engine. * Yul Optimizer: Take side-effect-freeness of user-defined functions into account. * Yul Optimizer: Remove redundant mload/sload operations. + * Yul Optimizer: Use the fact that branch conditions have certain value inside the branch. Bugfixes: diff --git a/README.md b/README.md index 80a20a902..a8c04da2a 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ Please follow the [Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html) if you want to help. +You can find our current feature and bug priorities for forthcoming releases [in the projects section](https://github.com/ethereum/solidity/projects). + ## Maintainers * [@axic](https://github.com/axic) * [@chriseth](https://github.com/chriseth) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 7ffb45964..eb55feef1 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -23,6 +23,9 @@ endif() eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) +# Prevent the path of the source directory from ending up in the binary via __FILE__ macros. +eth_add_cxx_compiler_flag_if_supported("-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=/solidity") + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) # Enables all the warnings about constructions that some users consider questionable, # and that are easy to avoid. Also enable some extra warning flags that are not @@ -130,6 +133,8 @@ elseif (DEFINED MSVC) add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819) + add_compile_options(-DBOOST_REGEX_NO_LIB) # disable automatic boost::regex library selection + add_compile_options(-D_REGEX_MAX_STACK_COUNT=200000L) # increase std::regex recursion depth limit # disable empty object file warning set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 53319e6c8..d8bca8a31 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -26,7 +26,7 @@ set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts) set(Boost_USE_MULTITHREADED ON) option(Boost_USE_STATIC_LIBS "Link Boost statically" ON) -set(BOOST_COMPONENTS "regex;filesystem;unit_test_framework;program_options;system") +set(BOOST_COMPONENTS "filesystem;unit_test_framework;program_options;system") find_package(Boost 1.65.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) diff --git a/cmake/EthUtils.cmake b/cmake/EthUtils.cmake index a473abcb7..fca41107e 100644 --- a/cmake/EthUtils.cmake +++ b/cmake/EthUtils.cmake @@ -34,3 +34,17 @@ macro(eth_default_option O DEF) endif() endmacro() +function(detect_stray_source_files FILELIST DIRECTORY) + if(CMAKE_VERSION VERSION_LESS 3.12) + file(GLOB sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${DIRECTORY}/*.cpp" "${DIRECTORY}/*.h") + else() + file(GLOB sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CONFIGURE_DEPENDS "${DIRECTORY}/*.cpp" "${DIRECTORY}/*.h") + endif() + foreach(path IN LISTS FILELIST) + list(REMOVE_ITEM sources ${path}) + endforeach() + list(LENGTH sources leftover_sources) + if (leftover_sources) + message(SEND_ERROR "The following source files are present but are not compiled: ${sources}") + endif() +endfunction(detect_stray_source_files) diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 46ddc9abe..fd201d77c 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -292,27 +292,27 @@ Consider you have the following pre-0.5.0 contract already deployed: :: - // This will not compile with the current version of the compiler - pragma solidity ^0.4.25; - contract OldContract { - function someOldFunction(uint8 a) { - //... - } - function anotherOldFunction() constant returns (bool) { - //... - } - // ... - } + // This will not compile with the current version of the compiler + pragma solidity ^0.4.25; + contract OldContract { + function someOldFunction(uint8 a) { + //... + } + function anotherOldFunction() constant returns (bool) { + //... + } + // ... + } This will no longer compile with Solidity v0.5.0. However, you can define a compatible interface for it: :: - pragma solidity >=0.5.0 <0.7.0; - interface OldContract { - function someOldFunction(uint8 a) external; - function anotherOldFunction() external returns (bool); - } + pragma solidity >=0.5.0 <0.7.0; + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } Note that we did not declare ``anotherOldFunction`` to be ``view``, despite it being declared ``constant`` in the original contract. This is due to the fact that starting with Solidity v0.5.0 ``staticcall`` is used to call ``view`` functions. @@ -325,19 +325,19 @@ Given the interface defined above, you can now easily use the already deployed p :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; - interface OldContract { - function someOldFunction(uint8 a) external; - function anotherOldFunction() external returns (bool); - } + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } - contract NewContract { - function doSomething(OldContract a) public returns (bool) { - a.someOldFunction(0x42); - return a.anotherOldFunction(); - } - } + contract NewContract { + function doSomething(OldContract a) public returns (bool) { + a.someOldFunction(0x42); + return a.anotherOldFunction(); + } + } Similarly, pre-0.5.0 libraries can be used by defining the functions of the library without implementation and supplying the address of the pre-0.5.0 library during linking (see :ref:`commandline-compiler` for how to use the @@ -345,17 +345,17 @@ commandline compiler for linking): :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; - library OldLibrary { - function someFunction(uint8 a) public returns(bool); - } + library OldLibrary { + function someFunction(uint8 a) public returns(bool); + } - contract NewContract { - function f(uint8 a) public returns (bool) { - return OldLibrary.someFunction(a); - } - } + contract NewContract { + function f(uint8 a) public returns (bool) { + return OldLibrary.someFunction(a); + } + } Example @@ -368,144 +368,144 @@ Old version: :: - // This will not compile - pragma solidity ^0.4.25; + // This will not compile + pragma solidity ^0.4.25; - contract OtherContract { - uint x; - function f(uint y) external { - x = y; - } - function() payable external {} - } + contract OtherContract { + uint x; + function f(uint y) external { + x = y; + } + function() payable external {} + } - contract Old { - OtherContract other; - uint myNumber; + contract Old { + OtherContract other; + uint myNumber; - // Function mutability not provided, not an error. - function someInteger() internal returns (uint) { return 2; } + // Function mutability not provided, not an error. + function someInteger() internal returns (uint) { return 2; } - // Function visibility not provided, not an error. - // Function mutability not provided, not an error. - function f(uint x) returns (bytes) { - // Var is fine in this version. - var z = someInteger(); - x += z; - // Throw is fine in this version. - if (x > 100) - throw; - bytes b = new bytes(x); - y = -3 >> 1; - // y == -1 (wrong, should be -2) - do { - x += 1; - if (x > 10) continue; - // 'Continue' causes an infinite loop. - } while (x < 11); - // Call returns only a Bool. - bool success = address(other).call("f"); - if (!success) - revert(); - else { - // Local variables could be declared after their use. - int y; - } - return b; - } + // Function visibility not provided, not an error. + // Function mutability not provided, not an error. + function f(uint x) returns (bytes) { + // Var is fine in this version. + var z = someInteger(); + x += z; + // Throw is fine in this version. + if (x > 100) + throw; + bytes b = new bytes(x); + y = -3 >> 1; + // y == -1 (wrong, should be -2) + do { + x += 1; + if (x > 10) continue; + // 'Continue' causes an infinite loop. + } while (x < 11); + // Call returns only a Bool. + bool success = address(other).call("f"); + if (!success) + revert(); + else { + // Local variables could be declared after their use. + int y; + } + return b; + } - // No need for an explicit data location for 'arr' - function g(uint[] arr, bytes8 x, OtherContract otherContract) public { - otherContract.transfer(1 ether); + // No need for an explicit data location for 'arr' + function g(uint[] arr, bytes8 x, OtherContract otherContract) public { + otherContract.transfer(1 ether); - // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), - // the first 4 bytes of x will be lost. This might lead to - // unexpected behavior since bytesX are right padded. - uint32 y = uint32(x); - myNumber += y + msg.value; - } - } + // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), + // the first 4 bytes of x will be lost. This might lead to + // unexpected behavior since bytesX are right padded. + uint32 y = uint32(x); + myNumber += y + msg.value; + } + } New version: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; - contract OtherContract { - uint x; - function f(uint y) external { - x = y; - } - function() payable external {} - } + contract OtherContract { + uint x; + function f(uint y) external { + x = y; + } + function() payable external {} + } - contract New { - OtherContract other; - uint myNumber; + contract New { + OtherContract other; + uint myNumber; - // Function mutability must be specified. - function someInteger() internal pure returns (uint) { return 2; } + // Function mutability must be specified. + function someInteger() internal pure returns (uint) { return 2; } - // Function visibility must be specified. - // Function mutability must be specified. - function f(uint x) public returns (bytes memory) { - // The type must now be explicitly given. - uint z = someInteger(); - x += z; - // Throw is now disallowed. - require(x > 100); - int y = -3 >> 1; - // y == -2 (correct) - do { - x += 1; - if (x > 10) continue; - // 'Continue' jumps to the condition below. - } while (x < 11); + // Function visibility must be specified. + // Function mutability must be specified. + function f(uint x) public returns (bytes memory) { + // The type must now be explicitly given. + uint z = someInteger(); + x += z; + // Throw is now disallowed. + require(x > 100); + int y = -3 >> 1; + require(y == -2); + do { + x += 1; + if (x > 10) continue; + // 'Continue' jumps to the condition below. + } while (x < 11); - // Call returns (bool, bytes). - // Data location must be specified. - (bool success, bytes memory data) = address(other).call("f"); - if (!success) - revert(); - return data; - } + // Call returns (bool, bytes). + // Data location must be specified. + (bool success, bytes memory data) = address(other).call("f"); + if (!success) + revert(); + return data; + } - using address_make_payable for address; - // Data location for 'arr' must be specified - function g(uint[] memory arr, bytes8 x, OtherContract otherContract, address unknownContract) public payable { - // 'otherContract.transfer' is not provided. - // Since the code of 'OtherContract' is known and has the fallback - // function, address(otherContract) has type 'address payable'. - address(otherContract).transfer(1 ether); + using address_make_payable for address; + // Data location for 'arr' must be specified + function g(uint[] memory /* arr */, bytes8 x, OtherContract otherContract, address unknownContract) public payable { + // 'otherContract.transfer' is not provided. + // Since the code of 'OtherContract' is known and has the fallback + // function, address(otherContract) has type 'address payable'. + address(otherContract).transfer(1 ether); - // 'unknownContract.transfer' is not provided. - // 'address(unknownContract).transfer' is not provided - // since 'address(unknownContract)' is not 'address payable'. - // If the function takes an 'address' which you want to send - // funds to, you can convert it to 'address payable' via 'uint160'. - // Note: This is not recommended and the explicit type - // 'address payable' should be used whenever possible. - // To increase clarity, we suggest the use of a library for - // the conversion (provided after the contract in this example). - address payable addr = unknownContract.make_payable(); - require(addr.send(1 ether)); + // 'unknownContract.transfer' is not provided. + // 'address(unknownContract).transfer' is not provided + // since 'address(unknownContract)' is not 'address payable'. + // If the function takes an 'address' which you want to send + // funds to, you can convert it to 'address payable' via 'uint160'. + // Note: This is not recommended and the explicit type + // 'address payable' should be used whenever possible. + // To increase clarity, we suggest the use of a library for + // the conversion (provided after the contract in this example). + address payable addr = unknownContract.make_payable(); + require(addr.send(1 ether)); - // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), - // the conversion is not allowed. - // We need to convert to a common size first: - bytes4 x4 = bytes4(x); // Padding happens on the right - uint32 y = uint32(x4); // Conversion is consistent - // 'msg.value' cannot be used in a 'non-payable' function. - // We need to make the function payable - myNumber += y + msg.value; - } - } + // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), + // the conversion is not allowed. + // We need to convert to a common size first: + bytes4 x4 = bytes4(x); // Padding happens on the right + uint32 y = uint32(x4); // Conversion is consistent + // 'msg.value' cannot be used in a 'non-payable' function. + // We need to make the function payable + myNumber += y + msg.value; + } + } - // We can define a library for explicitly converting ``address`` - // to ``address payable`` as a workaround. - library address_make_payable { - function make_payable(address x) internal pure returns (address payable) { - return address(uint160(x)); - } - } + // We can define a library for explicitly converting ``address`` + // to ``address payable`` as a workaround. + library address_make_payable { + function make_payable(address x) internal pure returns (address payable) { + return address(uint160(x)); + } + } diff --git a/docs/assembly.rst b/docs/assembly.rst index 119242aea..d735b54cd 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -292,7 +292,7 @@ In the grammar, opcodes are represented as pre-defined identifiers. | create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address | | | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) | | | | | and send v wei and return the new address, where ``0xff`` is a | -| | | | 8 byte value, ``this`` is the current contract's address | +| | | | 1 byte value, ``this`` is the current contract's address | | | | | as a 20 byte value and ``s`` is a big-endian 256-bit value | +-------------------------+-----+---+-----------------------------------------------------------------+ | call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) | diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index d79aca112..402997af1 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -754,6 +754,10 @@ "bugs": [], "released": "2019-10-01" }, + "0.5.13": { + "bugs": [], + "released": "2019-11-14" + }, "0.5.2": { "bugs": [ "SignedArrayStorageCopy", diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 494f22dd4..ed87666d9 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -22,9 +22,8 @@ a contract where the goal is to send the most money to the contract in order to become the "richest", inspired by `King of the Ether `_. -In the following contract, if you are usurped as the richest, -you will receive the funds of the person who has gone on to -become the new richest. +In the following contract, if you are no longer the richest, +you receive the funds of the person who is now the richest. :: diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 7421e278c..bfeebdb77 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -178,6 +178,9 @@ custom types without the overhead of external function calls: } } +It is possible to obtain the address of a library by converting +the library type to the ``address`` type, i.e. using ``address(LibraryName)``. + As the compiler cannot know where the library will be deployed at, these addresses have to be filled into the final bytecode by a linker diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index 7bba31e7c..56d5b0e25 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -150,24 +150,24 @@ to write a function, for example: :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.0 <0.7.0; - contract arrayExample { - // public state variable - uint[] public myArray; + contract arrayExample { + // public state variable + uint[] public myArray; - // Getter function generated by the compiler - /* - function myArray(uint i) returns (uint) { - return myArray[i]; + // Getter function generated by the compiler + /* + function myArray(uint i) public view returns (uint) { + return myArray[i]; + } + */ + + // function that returns entire array + function getArray() public view returns (uint[] memory) { + return myArray; + } } - */ - - // function that returns entire array - function getArray() returns (uint[] memory) { - return myArray; - } - } Now you can use ``getArray()`` to retrieve the entire array, instead of ``myArray(i)``, which returns a single element per call. diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 3b680113e..7a811fa4b 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -37,7 +37,7 @@ breaking changes. These releases always have versions of the form The version pragma is used as follows:: - pragma solidity ^0.5.2; + pragma solidity ^0.5.2; A source file with the line above does not compile with a compiler earlier than version 0.5.2, and it also does not work on a compiler starting from version 0.6.0 (this diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 137513b83..74c2d0326 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -8,6 +8,8 @@ Miscellaneous Layout of State Variables in Storage ************************************ +.. _storage-inplace-encoding: + Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules: - The first item in a storage slot is stored lower-order aligned. @@ -49,6 +51,8 @@ The elements of structs and arrays are stored after each other, just as if they Mappings and Dynamic Arrays =========================== +.. _storage-hashed-encoding: + Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash computation to find the starting position of the value or the array data. These starting positions are always full stack slots. @@ -88,6 +92,267 @@ by checking if the lowest bit is set: short (not set) and long (set). .. note:: Handling invalidly encoded slots is currently not supported but may be added in the future. +JSON Output +=========== + +.. _storage-layout-top-level: + +The storage layout of a contract can be requested via the :ref:`standard JSON interface `. The output is a JSON object containing two keys, +``storage`` and ``types``. The ``storage`` object is an array where each +element has the following form: + + +.. code:: + + + { + "astId": 2, + "contract": "fileA:A", + "label": "x", + "offset": 0, + "slot": "0", + "type": "t_uint256" + } + +where the example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA`` +and + +- ``astId`` is the id of the AST node of the state variable's declaration +- ``contract`` is the name of the contract including its path as prefix +- ``label`` is the name of the state variable +- ``offset`` is the offset in bytes within the storage slot according to the encoding +- ``slot`` is the storage slot where the state variable resides or starts. This + number may be very large and therefore its JSON value is represented as a + string. +- ``type`` is an identifier used as key to the variable's type information (described in the following) + +The given ``type``, in this case ``t_uint256`` represents an element in +``types``, which has the form: + + +.. code:: + + { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32", + } + +where + +- ``encoding`` how the data is encoded in storage, where the possible values are: + + - ``inplace``: data is laid out contiguously in storage (see :ref:`above `). + - ``mapping``: Keccak-256 hash-based method (see :ref:`above `). + - ``dynamic_array``: Keccak-256 hash-based method (see :ref:`above `). + - ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above `). + +- ``label`` is the canonical type name. +- ``numberOfBytes`` is the number of used bytes (as a decimal string). Note that if ``numberOfBytes > 32`` this means that more than one slot is used. + +Some types have extra information besides the four above. Mappings contain +its ``key`` and ``value`` types (again referencing an entry in this mapping +of types), arrays have its ``base`` type, and structs list their ``members`` in +the same format as the top-level ``storage`` (see :ref:`above +`). + +.. note :: + The JSON output format of a contract's storage layout is still considered experimental + and is subject to change in non-breaking releases of Solidity. + +The following example shows a contract and its storage layout, containing +value and reference types, types that are encoded packed, and nested types. + + +.. code:: + + pragma solidity >=0.4.0 <0.7.0; + contract A { + struct S { + uint128 a; + uint128 b; + uint[2] staticArray; + uint[] dynArray; + } + + uint x; + uint y; + S s; + address addr; + mapping (uint => mapping (address => bool)) map; + uint[] array; + string s1; + bytes b1; + } + +.. code:: + + "storageLayout": { + "storage": [ + { + "astId": 14, + "contract": "fileA:A", + "label": "x", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 16, + "contract": "fileA:A", + "label": "y", + "offset": 0, + "slot": "1", + "type": "t_uint256" + }, + { + "astId": 18, + "contract": "fileA:A", + "label": "s", + "offset": 0, + "slot": "2", + "type": "t_struct(S)12_storage" + }, + { + "astId": 20, + "contract": "fileA:A", + "label": "addr", + "offset": 0, + "slot": "6", + "type": "t_address" + }, + { + "astId": 26, + "contract": "fileA:A", + "label": "map", + "offset": 0, + "slot": "7", + "type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))" + }, + { + "astId": 29, + "contract": "fileA:A", + "label": "array", + "offset": 0, + "slot": "8", + "type": "t_array(t_uint256)dyn_storage" + }, + { + "astId": 31, + "contract": "fileA:A", + "label": "s1", + "offset": 0, + "slot": "9", + "type": "t_string_storage" + }, + { + "astId": 33, + "contract": "fileA:A", + "label": "b1", + "offset": 0, + "slot": "10", + "type": "t_bytes_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)2_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[2]", + "numberOfBytes": "64" + }, + "t_array(t_uint256)dyn_storage": { + "base": "t_uint256", + "encoding": "dynamic_array", + "label": "uint256[]", + "numberOfBytes": "32" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes_storage": { + "encoding": "bytes", + "label": "bytes", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_uint256,t_mapping(t_address,t_bool))": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => mapping(address => bool))", + "numberOfBytes": "32", + "value": "t_mapping(t_address,t_bool)" + }, + "t_string_storage": { + "encoding": "bytes", + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(S)12_storage": { + "encoding": "inplace", + "label": "struct A.S", + "members": [ + { + "astId": 2, + "contract": "fileA:A", + "label": "a", + "offset": 0, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 4, + "contract": "fileA:A", + "label": "b", + "offset": 16, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 8, + "contract": "fileA:A", + "label": "staticArray", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)2_storage" + }, + { + "astId": 11, + "contract": "fileA:A", + "label": "dynArray", + "offset": 0, + "slot": "3", + "type": "t_array(t_uint256)dyn_storage" + } + ], + "numberOfBytes": "128" + }, + "t_uint128": { + "encoding": "inplace", + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + .. index: memory layout **************** diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index 5c9d98736..cfc94d81b 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -49,22 +49,22 @@ The following example shows a contract and a function using all available tags. .. code:: solidity - pragma solidity ^0.5.6; + pragma solidity ^0.5.6; - /// @title A simulator for trees - /// @author Larry A. Gardner - /// @notice You can use this contract for only the most basic simulation - /// @dev All function calls are currently implemented without side effects - contract Tree { - /// @author Mary A. Botanist - /// @notice Calculate tree age in years, rounded up, for live trees - /// @dev The Alexandr N. Tetearing algorithm could increase precision - /// @param rings The number of rings from dendrochronological sample - /// @return age in years, rounded up for partial years - function age(uint256 rings) external pure returns (uint256) { - return rings + 1; - } - } + /// @title A simulator for trees + /// @author Larry A. Gardner + /// @notice You can use this contract for only the most basic simulation + /// @dev All function calls are currently implemented without side effects + contract Tree { + /// @author Mary A. Botanist + /// @notice Calculate tree age in years, rounded up, for live trees + /// @dev The Alexandr N. Tetearing algorithm could increase precision + /// @param rings The number of rings from dendrochronological sample + /// @return age in years, rounded up for partial years + function age(uint256 rings) external pure returns (uint256) { + return rings + 1; + } + } .. _header-tags: diff --git a/docs/resources.rst b/docs/resources.rst index a3a89113a..4335c3deb 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -127,6 +127,9 @@ Solidity Tools * `Universal Mutator `_ A tool for mutation generation, with configurable rules and support for Solidity and Vyper. +* `PIET `_ + A tool to develop, audit and use solidity smart contracts through a simple graphical interface. + .. note:: Information like variable names, comments, and source code formatting is lost in the compilation process and it is not possible to completely recover the original source code. Decompiling smart contracts to view the original source code might not be possible, or the end result that useful. diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 3f18bf3b5..0b37d2fb5 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -499,26 +499,26 @@ not mean loss of proving power. :: - pragma solidity >=0.5.0; - pragma experimental SMTChecker; + pragma solidity >=0.5.0; + pragma experimental SMTChecker; - contract Recover - { - function f( - bytes32 hash, - uint8 _v1, uint8 _v2, - bytes32 _r1, bytes32 _r2, - bytes32 _s1, bytes32 _s2 - ) public pure returns (address) { - address a1 = ecrecover(hash, _v1, _r1, _s1); - require(_v1 == _v2); - require(_r1 == _r2); - require(_s1 == _s2); - address a2 = ecrecover(hash, _v2, _r2, _s2); - assert(a1 == a2); - return a1; - } - } + contract Recover + { + function f( + bytes32 hash, + uint8 _v1, uint8 _v2, + bytes32 _r1, bytes32 _r2, + bytes32 _s1, bytes32 _s2 + ) public pure returns (address) { + address a1 = ecrecover(hash, _v1, _r1, _s1); + require(_v1 == _v2); + require(_r1 == _r2); + require(_s1 == _s2); + address a2 = ecrecover(hash, _v2, _r2, _s2); + assert(a1 == a2); + return a1; + } + } In the example above, the SMTChecker is not expressive enough to actually compute ``ecrecover``, but by modelling the function calls as uninterpreted @@ -552,34 +552,34 @@ types. :: - pragma solidity >=0.5.0; - pragma experimental SMTChecker; - // This will not compile - contract Aliasing - { - uint[] array; - function f( - uint[] memory a, - uint[] memory b, - uint[][] memory c, - uint[] storage d - ) internal view { - require(array[0] == 42); - require(a[0] == 2); - require(c[0][0] == 2); - require(d[0] == 2); - b[0] = 1; - // Erasing knowledge about memory references should not - // erase knowledge about state variables. - assert(array[0] == 42); - // Fails because `a == b` is possible. - assert(a[0] == 2); - // Fails because `c[i] == b` is possible. - assert(c[0][0] == 2); - assert(d[0] == 2); - assert(b[0] == 1); - } - } + pragma solidity >=0.5.0; + pragma experimental SMTChecker; + // This will report a warning + contract Aliasing + { + uint[] array; + function f( + uint[] memory a, + uint[] memory b, + uint[][] memory c, + uint[] storage d + ) internal view { + require(array[0] == 42); + require(a[0] == 2); + require(c[0][0] == 2); + require(d[0] == 2); + b[0] = 1; + // Erasing knowledge about memory references should not + // erase knowledge about state variables. + assert(array[0] == 42); + // Fails because `a == b` is possible. + assert(a[0] == 2); + // Fails because `c[i] == b` is possible. + assert(c[0][0] == 2); + assert(d[0] == 2); + assert(b[0] == 1); + } + } After the assignment to ``b[0]``, we need to clear knowledge about ``a`` since it has the same type (``uint[]``) and data location (memory). We also need to diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index a7d607786..24eb1bc9d 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -4,7 +4,8 @@ Mapping Types ============= -You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``. +Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables +are declared as a mapping type using the syntax ``mapping (_KeyType => _ValueType) _VariableModifiers _VariableName``. The ``_KeyType`` can be any elementary type. This means it can be any of the built-in value types plus ``bytes`` and ``string``. User-defined or complex types like contract types, enums, mappings, structs and any array type @@ -27,11 +28,16 @@ They cannot be used as parameters or return parameters of contract functions that are publicly visible. You can mark state variables of mapping type as ``public`` and Solidity creates a -:ref:`getter ` for you. The ``_KeyType`` becomes a -parameter for the getter. If ``_ValueType`` is a value type or a struct, -the getter returns ``_ValueType``. +:ref:`getter ` for you. The ``_KeyType`` becomes a parameter for the getter. +If ``_ValueType`` is a value type or a struct, the getter returns ``_ValueType``. If ``_ValueType`` is an array or a mapping, the getter has one parameter for -each ``_KeyType``, recursively. For example with a mapping: +each ``_KeyType``, recursively. + +In the example below, the ``MappingExample`` contract defines a public ``balances`` +mapping, with the key type an ``address``, and a value type a ``uint``, mapping +an Ethereum address to an unsigned integer value. As ``uint`` is a value type, the getter +returns a value that matches the type, which you can see in the ``MappingUser`` +contract that returns the value at the specified address. :: @@ -53,7 +59,146 @@ each ``_KeyType``, recursively. For example with a mapping: } } +The example below is a simplified version of an `ERC20 token `_. +``_allowances`` is an example of a mapping type inside another mapping type. +The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account. -.. note:: - Mappings are not iterable, but it is possible to implement a data structure - on top of them. For an example, see `iterable mapping `_. +:: + + pragma solidity >=0.4.0 <0.7.0; + + contract MappingExample { + + mapping (address => uint256) private _balances; + mapping (address => mapping (address => uint256)) private _allowances; + + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + + function allowance(address owner, address spender) public view returns (uint256) { + return _allowances[owner][spender]; + } + + function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { + _transfer(sender, recipient, amount); + approve(sender, msg.sender, amount); + return true; + } + + function approve(address owner, address spender, uint256 amount) public returns (bool) { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + return true; + } + + function _transfer(address sender, address recipient, uint256 amount) internal { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _balances[sender] -= amount; + _balances[recipient] += amount; + emit Transfer(sender, recipient, amount); + } + } + + +.. index:: !iterable mappings +.. _iterable-mappings: + +Iterable Mappings +----------------- + +Mappings are not iterable, but it is possible to implement a data structure on +top of them and iterate over that. For example, the code below implements an +``IterableMapping`` library that the ``User`` contract then adds data too, and +the ``sum`` function iterates over to sum all the values. + +:: + + pragma solidity >=0.4.0 <0.7.0; + + library IterableMapping { + + struct itmap { + mapping(uint => IndexValue) data; + KeyFlag[] keys; + uint size; + } + + struct IndexValue { uint keyIndex; uint value; } + struct KeyFlag { uint key; bool deleted; } + + function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) { + uint keyIndex = self.data[key].keyIndex; + self.data[key].value = value; + if (keyIndex > 0) + return true; + else { + keyIndex = self.keys.length++; + self.data[key].keyIndex = keyIndex + 1; + self.keys[keyIndex].key = key; + self.size++; + return false; + } + } + + function remove(itmap storage self, uint key) internal returns (bool success) { + uint keyIndex = self.data[key].keyIndex; + if (keyIndex == 0) + return false; + delete self.data[key]; + self.keys[keyIndex - 1].deleted = true; + self.size --; + } + + function contains(itmap storage self, uint key) internal view returns (bool) { + return self.data[key].keyIndex > 0; + } + + function iterate_start(itmap storage self) internal view returns (uint keyIndex) { + return iterate_next(self, uint(-1)); + } + + function iterate_valid(itmap storage self, uint keyIndex) internal view returns (bool) { + return keyIndex < self.keys.length; + } + + function iterate_next(itmap storage self, uint keyIndex) internal view returns (uint r_keyIndex) { + keyIndex++; + while (keyIndex < self.keys.length && self.keys[keyIndex].deleted) + keyIndex++; + return keyIndex; + } + + function iterate_get(itmap storage self, uint keyIndex) internal view returns (uint key, uint value) { + key = self.keys[keyIndex].key; + value = self.data[key].value; + } + } + + // How to use it + contract User { + // Just a struct holding our data. + IterableMapping.itmap data; + + // Insert something + function insert(uint k, uint v) public returns (uint size) { + // Actually calls itmap_impl.insert, auto-supplying the first parameter for us. + IterableMapping.insert(data, k, v); + // We can still access members of the struct - but we should take care not to mess with them. + return data.size; + } + + // Computes the sum of all stored data. + function sum() public view returns (uint s) { + for (uint i = IterableMapping.iterate_start(data); + IterableMapping.iterate_valid(data, i); + i = IterableMapping.iterate_next(data, i)) { + (, uint value) = IterableMapping.iterate_get(data, i); + s += value; + } + } + } \ No newline at end of file diff --git a/docs/types/operators.rst b/docs/types/operators.rst index afbe2a742..f53d6ee57 100644 --- a/docs/types/operators.rst +++ b/docs/types/operators.rst @@ -17,7 +17,7 @@ equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a d array of length zero or a static array of the same length with all elements set to their initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves all other elements and the length of the array untouched. This especially means that it leaves -a gap in the array. If you plan to remove items, a mapping is probably a better choice. +a gap in the array. If you plan to remove items, a :ref:`mapping ` is probably a better choice. For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 12df74e84..db09b2a09 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -276,6 +276,7 @@ Input Description // metadata - Metadata // ir - Yul intermediate representation of the code before optimization // irOptimized - Intermediate representation after optimization + // storageLayout - Slots, offsets and types of the contract's state variables. // evm.assembly - New assembly format // evm.legacyAssembly - Old-style assembly format in JSON // evm.bytecode.object - Bytecode object @@ -376,6 +377,8 @@ Output Description "devdoc": {}, // Intermediate representation (string) "ir": "", + // See the Storage Layout documentation. + "storageLayout": {"storage": [...], "types": {...} }, // EVM-related outputs "evm": { // Assembly (string) diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index 8bc3bb525..7b4342f12 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -34,6 +34,10 @@ set(sources ) add_library(devcore ${sources}) -target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::regex Boost::system) +target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::system) target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") add_dependencies(devcore solidity_BuildInfo.h) + +if(SOLC_LINK_STATIC) + target_link_libraries(devcore PUBLIC Threads::Threads) +endif() diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index bc71c88c4..017a632dc 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -25,11 +25,10 @@ #include -#include - #include #include #include +#include #include #include #include @@ -277,7 +276,7 @@ void iterateReplacing(std::vector& _vector, F const& _f) std::vector modifiedVector; for (size_t i = 0; i < _vector.size(); ++i) { - if (boost::optional> r = _f(_vector[i])) + if (std::optional> r = _f(_vector[i])) { if (!useModified) { @@ -305,7 +304,7 @@ void iterateReplacingWindow(std::vector& _vector, F const& _f, std::index_seq size_t i = 0; for (; i + sizeof...(I) <= _vector.size(); ++i) { - if (boost::optional> r = _f(_vector[i + I]...)) + if (std::optional> r = _f(_vector[i + I]...)) { if (!useModified) { diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp index c48c8d6f1..09415960d 100644 --- a/libdevcore/Whiskers.cpp +++ b/libdevcore/Whiskers.cpp @@ -25,7 +25,7 @@ #include -#include +#include using namespace std; using namespace dev; @@ -72,9 +72,9 @@ string Whiskers::render() const void Whiskers::checkParameterValid(string const& _parameter) const { - static boost::regex validParam("^" + paramRegex() + "$"); + static regex validParam("^" + paramRegex() + "$"); assertThrow( - boost::regex_match(_parameter, validParam), + regex_match(_parameter, validParam), WhiskersError, "Parameter" + _parameter + " contains invalid characters." ); @@ -99,6 +99,32 @@ void Whiskers::checkParameterUnknown(string const& _parameter) const ); } +namespace +{ +template +string regex_replace( + string const& _source, + regex const& _pattern, + ReplaceCallback _replace, + regex_constants::match_flag_type _flags = regex_constants::match_default +) +{ + sregex_iterator curMatch(_source.begin(), _source.end(), _pattern, _flags); + sregex_iterator matchEnd; + string::const_iterator lastMatchedPos(_source.cbegin()); + string result; + while (curMatch != matchEnd) + { + result.append(curMatch->prefix().first, curMatch->prefix().second); + result.append(_replace(*curMatch)); + lastMatchedPos = (*curMatch)[0].second; + ++curMatch; + } + result.append(lastMatchedPos, _source.cend()); + return result; +} +} + string Whiskers::replace( string const& _template, StringMap const& _parameters, @@ -106,8 +132,7 @@ string Whiskers::replace( map> const& _listParameters ) { - using namespace boost; - static regex listOrTag("<(" + paramRegex() + ")>|<#(" + paramRegex() + ")>(.*?)|<\\?(" + paramRegex() + ")>(.*?)((.*?))?"); + static regex listOrTag("<(" + paramRegex() + ")>|<#(" + paramRegex() + ")>((?:.|\\r|\\n)*?)|<\\?(" + paramRegex() + ")>((?:.|\\r|\\n)*?)(((?:.|\\r|\\n)*?))?"); return regex_replace(_template, listOrTag, [&](match_results _match) -> string { string tagName(_match[1]); diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 183cba930..ae8d001d7 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -258,7 +258,7 @@ Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string string Assembly::toStringInHex(u256 _value) { std::stringstream hexStr; - hexStr << hex << _value; + hexStr << std::uppercase << hex << _value; return hexStr.str(); } diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index cc5561daf..8c0a45308 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -356,13 +356,13 @@ string dev::eth::disassemble(bytes const& _mem) stringstream ret; eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) { if (!isValidInstruction(_instr)) - ret << "0x" << hex << int(_instr) << " "; + ret << "0x" << std::uppercase << std::hex << int(_instr) << " "; else { InstructionInfo info = instructionInfo(_instr); ret << info.name << " "; if (info.additional) - ret << "0x" << hex << _data << " "; + ret << "0x" << std::uppercase << std::hex << _data << " "; } }); return ret.str(); diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 58e773f30..e67f614ae 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -49,7 +49,7 @@ template S modWorkaround(S const& _a, S const& _b) // This works around a bug fixed with Boost 1.64. // https://www.boost.org/doc/libs/1_68_0/libs/multiprecision/doc/html/boost_multiprecision/map/hist.html#boost_multiprecision.map.hist.multiprecision_2_3_1_boost_1_64 -inline u256 shlWorkaround(u256 const& _x, unsigned _amount) +template S shlWorkaround(S const& _x, unsigned _amount) { return u256((bigint(_x) << _amount) & u256(-1)); } @@ -66,44 +66,50 @@ std::vector> simplificationRuleListPart1( Pattern ) { + using Word = typename Pattern::Word; return std::vector> { // arithmetic on constants - {{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false}, - {{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false}, - {{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false}, - {{Instruction::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, - {{Instruction::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {{Instruction::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, - {{Instruction::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {{Instruction::EXP, {A, B}}, [=]{ return u256(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << 256)); }, false}, - {{Instruction::NOT, {A}}, [=]{ return ~A.d(); }, false}, - {{Instruction::LT, {A, B}}, [=]() -> u256 { return A.d() < B.d() ? 1 : 0; }, false}, - {{Instruction::GT, {A, B}}, [=]() -> u256 { return A.d() > B.d() ? 1 : 0; }, false}, - {{Instruction::SLT, {A, B}}, [=]() -> u256 { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, - {{Instruction::SGT, {A, B}}, [=]() -> u256 { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, - {{Instruction::EQ, {A, B}}, [=]() -> u256 { return A.d() == B.d() ? 1 : 0; }, false}, - {{Instruction::ISZERO, {A}}, [=]() -> u256 { return A.d() == 0 ? 1 : 0; }, false}, - {{Instruction::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false}, - {{Instruction::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false}, - {{Instruction::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false}, - {{Instruction::BYTE, {A, B}}, [=]{ return A.d() >= 32 ? 0 : (B.d() >> unsigned(8 * (31 - A.d()))) & 0xff; }, false}, - {{Instruction::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, - {{Instruction::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, - {{Instruction::SIGNEXTEND, {A, B}}, [=]() -> u256 { - if (A.d() >= 31) + {{Pattern::Builtins::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false}, + {{Pattern::Builtins::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false}, + {{Pattern::Builtins::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false}, + {{Pattern::Builtins::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, + {{Pattern::Builtins::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, + {{Pattern::Builtins::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, + {{Pattern::Builtins::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, + {{Pattern::Builtins::EXP, {A, B}}, [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, + {{Pattern::Builtins::NOT, {A}}, [=]{ return ~A.d(); }, false}, + {{Pattern::Builtins::LT, {A, B}}, [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, + {{Pattern::Builtins::GT, {A, B}}, [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, + {{Pattern::Builtins::SLT, {A, B}}, [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, + {{Pattern::Builtins::SGT, {A, B}}, [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, + {{Pattern::Builtins::EQ, {A, B}}, [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, + {{Pattern::Builtins::ISZERO, {A}}, [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, + {{Pattern::Builtins::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false}, + {{Pattern::Builtins::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false}, + {{Pattern::Builtins::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false}, + {{Pattern::Builtins::BYTE, {A, B}}, [=]{ + return + A.d() >= Pattern::WordSize / 8 ? + 0 : + (B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff; + }, false}, + {{Pattern::Builtins::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, + {{Pattern::Builtins::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, + {{Pattern::Builtins::SIGNEXTEND, {A, B}}, [=]() -> Word { + if (A.d() >= Pattern::WordSize / 8 - 1) return B.d(); unsigned testBit = unsigned(A.d()) * 8 + 7; - u256 mask = (u256(1) << testBit) - 1; + Word mask = (Word(1) << testBit) - 1; return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask; }, false}, - {{Instruction::SHL, {A, B}}, [=]{ - if (A.d() > 255) - return u256(0); + {{Pattern::Builtins::SHL, {A, B}}, [=]{ + if (A.d() >= Pattern::WordSize) + return Word(0); return shlWorkaround(B.d(), unsigned(A.d())); }, false}, - {{Instruction::SHR, {A, B}}, [=]{ - if (A.d() > 255) - return u256(0); + {{Pattern::Builtins::SHR, {A, B}}, [=]{ + if (A.d() >= Pattern::WordSize) + return Word(0); return B.d() >> unsigned(A.d()); }, false} }; @@ -118,50 +124,51 @@ std::vector> simplificationRuleListPart2( Pattern Y ) { + using Word = typename Pattern::Word; return std::vector> { // invariants involving known constants - {{Instruction::ADD, {X, 0}}, [=]{ return X; }, false}, - {{Instruction::ADD, {0, X}}, [=]{ return X; }, false}, - {{Instruction::SUB, {X, 0}}, [=]{ return X; }, false}, - {{Instruction::SUB, {~u256(0), X}}, [=]() -> Pattern { return {Instruction::NOT, {X}}; }, false}, - {{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }, true}, - {{Instruction::MUL, {0, X}}, [=]{ return u256(0); }, true}, - {{Instruction::MUL, {X, 1}}, [=]{ return X; }, false}, - {{Instruction::MUL, {1, X}}, [=]{ return X; }, false}, - {{Instruction::MUL, {X, u256(-1)}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false}, - {{Instruction::MUL, {u256(-1), X}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false}, - {{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }, true}, - {{Instruction::DIV, {0, X}}, [=]{ return u256(0); }, true}, - {{Instruction::DIV, {X, 1}}, [=]{ return X; }, false}, - {{Instruction::SDIV, {X, 0}}, [=]{ return u256(0); }, true}, - {{Instruction::SDIV, {0, X}}, [=]{ return u256(0); }, true}, - {{Instruction::SDIV, {X, 1}}, [=]{ return X; }, false}, - {{Instruction::AND, {X, ~u256(0)}}, [=]{ return X; }, false}, - {{Instruction::AND, {~u256(0), X}}, [=]{ return X; }, false}, - {{Instruction::AND, {X, 0}}, [=]{ return u256(0); }, true}, - {{Instruction::AND, {0, X}}, [=]{ return u256(0); }, true}, - {{Instruction::OR, {X, 0}}, [=]{ return X; }, false}, - {{Instruction::OR, {0, X}}, [=]{ return X; }, false}, - {{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }, true}, - {{Instruction::OR, {~u256(0), X}}, [=]{ return ~u256(0); }, true}, - {{Instruction::XOR, {X, 0}}, [=]{ return X; }, false}, - {{Instruction::XOR, {0, X}}, [=]{ return X; }, false}, - {{Instruction::MOD, {X, 0}}, [=]{ return u256(0); }, true}, - {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true}, - {{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, - {{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, - {{Instruction::SHL, {0, X}}, [=]{ return X; }, false}, - {{Instruction::SHR, {0, X}}, [=]{ return X; }, false}, - {{Instruction::SHL, {X, 0}}, [=]{ return u256(0); }, true}, - {{Instruction::SHR, {X, 0}}, [=]{ return u256(0); }, true}, - {{Instruction::GT, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false}, - {{Instruction::LT, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false}, - {{Instruction::GT, {X, ~u256(0)}}, [=]{ return u256(0); }, true}, - {{Instruction::LT, {~u256(0), X}}, [=]{ return u256(0); }, true}, - {{Instruction::GT, {0, X}}, [=]{ return u256(0); }, true}, - {{Instruction::LT, {X, 0}}, [=]{ return u256(0); }, true}, - {{Instruction::AND, {{Instruction::BYTE, {X, Y}}, {u256(0xff)}}}, [=]() -> Pattern { return {Instruction::BYTE, {X, Y}}; }, false}, - {{Instruction::BYTE, {31, X}}, [=]() -> Pattern { return {Instruction::AND, {X, u256(0xff)}}; }, false} + {{Pattern::Builtins::ADD, {X, 0}}, [=]{ return X; }, false}, + {{Pattern::Builtins::ADD, {0, X}}, [=]{ return X; }, false}, + {{Pattern::Builtins::SUB, {X, 0}}, [=]{ return X; }, false}, + {{Pattern::Builtins::SUB, {~Word(0), X}}, [=]() -> Pattern { return {Pattern::Builtins::NOT, {X}}; }, false}, + {{Pattern::Builtins::MUL, {X, 0}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::MUL, {0, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::MUL, {X, 1}}, [=]{ return X; }, false}, + {{Pattern::Builtins::MUL, {1, X}}, [=]{ return X; }, false}, + {{Pattern::Builtins::MUL, {X, Word(-1)}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false}, + {{Pattern::Builtins::MUL, {Word(-1), X}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false}, + {{Pattern::Builtins::DIV, {X, 0}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::DIV, {0, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::DIV, {X, 1}}, [=]{ return X; }, false}, + {{Pattern::Builtins::SDIV, {X, 0}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::SDIV, {0, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::SDIV, {X, 1}}, [=]{ return X; }, false}, + {{Pattern::Builtins::AND, {X, ~Word(0)}}, [=]{ return X; }, false}, + {{Pattern::Builtins::AND, {~Word(0), X}}, [=]{ return X; }, false}, + {{Pattern::Builtins::AND, {X, 0}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::AND, {0, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::OR, {X, 0}}, [=]{ return X; }, false}, + {{Pattern::Builtins::OR, {0, X}}, [=]{ return X; }, false}, + {{Pattern::Builtins::OR, {X, ~Word(0)}}, [=]{ return ~Word(0); }, true}, + {{Pattern::Builtins::OR, {~Word(0), X}}, [=]{ return ~Word(0); }, true}, + {{Pattern::Builtins::XOR, {X, 0}}, [=]{ return X; }, false}, + {{Pattern::Builtins::XOR, {0, X}}, [=]{ return X; }, false}, + {{Pattern::Builtins::MOD, {X, 0}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::MOD, {0, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::EQ, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }, + {{Pattern::Builtins::EQ, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }, + {{Pattern::Builtins::SHL, {0, X}}, [=]{ return X; }, false}, + {{Pattern::Builtins::SHR, {0, X}}, [=]{ return X; }, false}, + {{Pattern::Builtins::SHL, {X, 0}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::SHR, {X, 0}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::GT, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false}, + {{Pattern::Builtins::LT, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false}, + {{Pattern::Builtins::GT, {X, ~Word(0)}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::LT, {~Word(0), X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::GT, {0, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::LT, {X, 0}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::AND, {{Pattern::Builtins::BYTE, {X, Y}}, {Word(0xff)}}}, [=]() -> Pattern { return {Pattern::Builtins::BYTE, {X, Y}}; }, false}, + {{Pattern::Builtins::BYTE, {Pattern::WordSize / 8 - 1, X}}, [=]() -> Pattern { return {Pattern::Builtins::AND, {X, Word(0xff)}}; }, false} }; } @@ -174,18 +181,19 @@ std::vector> simplificationRuleListPart3( Pattern ) { + using Word = typename Pattern::Word; return std::vector> { // operations involving an expression and itself - {{Instruction::AND, {X, X}}, [=]{ return X; }, true}, - {{Instruction::OR, {X, X}}, [=]{ return X; }, true}, - {{Instruction::XOR, {X, X}}, [=]{ return u256(0); }, true}, - {{Instruction::SUB, {X, X}}, [=]{ return u256(0); }, true}, - {{Instruction::EQ, {X, X}}, [=]{ return u256(1); }, true}, - {{Instruction::LT, {X, X}}, [=]{ return u256(0); }, true}, - {{Instruction::SLT, {X, X}}, [=]{ return u256(0); }, true}, - {{Instruction::GT, {X, X}}, [=]{ return u256(0); }, true}, - {{Instruction::SGT, {X, X}}, [=]{ return u256(0); }, true}, - {{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true} + {{Pattern::Builtins::AND, {X, X}}, [=]{ return X; }, true}, + {{Pattern::Builtins::OR, {X, X}}, [=]{ return X; }, true}, + {{Pattern::Builtins::XOR, {X, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::SUB, {X, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::EQ, {X, X}}, [=]{ return Word(1); }, true}, + {{Pattern::Builtins::LT, {X, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::SLT, {X, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::GT, {X, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::SGT, {X, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::MOD, {X, X}}, [=]{ return Word(0); }, true} }; } @@ -198,25 +206,26 @@ std::vector> simplificationRuleListPart4( Pattern Y ) { + using Word = typename Pattern::Word; return std::vector> { // logical instruction combinations - {{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }, false}, - {{Instruction::XOR, {X, {Instruction::XOR, {X, Y}}}}, [=]{ return Y; }, true}, - {{Instruction::XOR, {X, {Instruction::XOR, {Y, X}}}}, [=]{ return Y; }, true}, - {{Instruction::XOR, {{Instruction::XOR, {X, Y}}, X}}, [=]{ return Y; }, true}, - {{Instruction::XOR, {{Instruction::XOR, {Y, X}}, X}}, [=]{ return Y; }, true}, - {{Instruction::OR, {X, {Instruction::AND, {X, Y}}}}, [=]{ return X; }, true}, - {{Instruction::OR, {X, {Instruction::AND, {Y, X}}}}, [=]{ return X; }, true}, - {{Instruction::OR, {{Instruction::AND, {X, Y}}, X}}, [=]{ return X; }, true}, - {{Instruction::OR, {{Instruction::AND, {Y, X}}, X}}, [=]{ return X; }, true}, - {{Instruction::AND, {X, {Instruction::OR, {X, Y}}}}, [=]{ return X; }, true}, - {{Instruction::AND, {X, {Instruction::OR, {Y, X}}}}, [=]{ return X; }, true}, - {{Instruction::AND, {{Instruction::OR, {X, Y}}, X}}, [=]{ return X; }, true}, - {{Instruction::AND, {{Instruction::OR, {Y, X}}, X}}, [=]{ return X; }, true}, - {{Instruction::AND, {X, {Instruction::NOT, {X}}}}, [=]{ return u256(0); }, true}, - {{Instruction::AND, {{Instruction::NOT, {X}}, X}}, [=]{ return u256(0); }, true}, - {{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true}, - {{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true}, + {{Pattern::Builtins::NOT, {{Pattern::Builtins::NOT, {X}}}}, [=]{ return X; }, false}, + {{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {X, Y}}}}, [=]{ return Y; }, true}, + {{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {Y, X}}}}, [=]{ return Y; }, true}, + {{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {X, Y}}, X}}, [=]{ return Y; }, true}, + {{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {Y, X}}, X}}, [=]{ return Y; }, true}, + {{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {X, Y}}}}, [=]{ return X; }, true}, + {{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {Y, X}}}}, [=]{ return X; }, true}, + {{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {X, Y}}, X}}, [=]{ return X; }, true}, + {{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {Y, X}}, X}}, [=]{ return X; }, true}, + {{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {X, Y}}}}, [=]{ return X; }, true}, + {{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {Y, X}}}}, [=]{ return X; }, true}, + {{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {X, Y}}, X}}, [=]{ return X; }, true}, + {{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {Y, X}}, X}}, [=]{ return X; }, true}, + {{Pattern::Builtins::AND, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::AND, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return Word(0); }, true}, + {{Pattern::Builtins::OR, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return ~Word(0); }, true}, + {{Pattern::Builtins::OR, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return ~Word(0); }, true}, }; } @@ -230,58 +239,61 @@ std::vector> simplificationRuleListPart5( Pattern ) { + using Word = typename Pattern::Word; + std::vector> rules; // Replace MOD X, with AND X, - 1 - for (size_t i = 0; i < 256; ++i) + for (size_t i = 0; i < Pattern::WordSize; ++i) { - u256 value = u256(1) << i; + Word value = Word(1) << i; rules.push_back({ - {Instruction::MOD, {X, value}}, - [=]() -> Pattern { return {Instruction::AND, {X, value - 1}}; }, + {Pattern::Builtins::MOD, {X, value}}, + [=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; }, false }); } // Replace SHL >=256, X with 0 rules.push_back({ - {Instruction::SHL, {A, X}}, - [=]() -> Pattern { return u256(0); }, + {Pattern::Builtins::SHL, {A, X}}, + [=]() -> Pattern { return Word(0); }, true, - [=]() { return A.d() >= 256; } + [=]() { return A.d() >= Pattern::WordSize; } }); // Replace SHR >=256, X with 0 rules.push_back({ - {Instruction::SHR, {A, X}}, - [=]() -> Pattern { return u256(0); }, + {Pattern::Builtins::SHR, {A, X}}, + [=]() -> Pattern { return Word(0); }, true, - [=]() { return A.d() >= 256; } + [=]() { return A.d() >= Pattern::WordSize; } }); // Replace BYTE(A, X), A >= 32 with 0 rules.push_back({ - {Instruction::BYTE, {A, X}}, - [=]() -> Pattern { return u256(0); }, + {Pattern::Builtins::BYTE, {A, X}}, + [=]() -> Pattern { return Word(0); }, true, - [=]() { return A.d() >= 32; } + [=]() { return A.d() >= Pattern::WordSize / 8; } }); for (auto const& op: std::vector{ - Instruction::ADDRESS, - Instruction::CALLER, - Instruction::ORIGIN, - Instruction::COINBASE + Pattern::Builtins::ADDRESS, + Pattern::Builtins::CALLER, + Pattern::Builtins::ORIGIN, + Pattern::Builtins::COINBASE }) { - u256 const mask = (u256(1) << 160) - 1; + assertThrow(Pattern::WordSize > 160, OptimizerException, ""); + Word const mask = (Word(1) << 160) - 1; rules.push_back({ - {Instruction::AND, {{op, mask}}}, + {Pattern::Builtins::AND, {{op, mask}}}, [=]() -> Pattern { return op; }, false }); rules.push_back({ - {Instruction::AND, {{mask, op}}}, + {Pattern::Builtins::AND, {{mask, op}}}, [=]() -> Pattern { return op; }, false }); @@ -302,27 +314,27 @@ std::vector> simplificationRuleListPart6( std::vector> rules; // Double negation of opcodes with boolean result for (auto const& op: std::vector{ - Instruction::EQ, - Instruction::LT, - Instruction::SLT, - Instruction::GT, - Instruction::SGT + Pattern::Builtins::EQ, + Pattern::Builtins::LT, + Pattern::Builtins::SLT, + Pattern::Builtins::GT, + Pattern::Builtins::SGT }) rules.push_back({ - {Instruction::ISZERO, {{Instruction::ISZERO, {{op, {X, Y}}}}}}, + {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}}, [=]() -> Pattern { return {op, {X, Y}}; }, false }); rules.push_back({ - {Instruction::ISZERO, {{Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}}}, - [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, + {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}}, + [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }); rules.push_back({ - {Instruction::ISZERO, {{Instruction::XOR, {X, Y}}}}, - [=]() -> Pattern { return { Instruction::EQ, {X, Y} }; }, + {Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}}, + [=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; }, false }); @@ -338,14 +350,15 @@ std::vector> simplificationRuleListPart7( Pattern Y ) { + using Word = typename Pattern::Word; std::vector> rules; // Associative operations - for (auto const& opFun: std::vector>>{ - {Instruction::ADD, std::plus()}, - {Instruction::MUL, std::multiplies()}, - {Instruction::AND, std::bit_and()}, - {Instruction::OR, std::bit_or()}, - {Instruction::XOR, std::bit_xor()} + for (auto const& opFun: std::vector>>{ + {Pattern::Builtins::ADD, std::plus()}, + {Pattern::Builtins::MUL, std::multiplies()}, + {Pattern::Builtins::AND, std::bit_and()}, + {Pattern::Builtins::OR, std::bit_or()}, + {Pattern::Builtins::XOR, std::bit_xor()} }) { auto op = opFun.first; @@ -382,13 +395,13 @@ std::vector> simplificationRuleListPart7( // Combine two SHL by constant rules.push_back({ // SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X) - {Instruction::SHL, {{B}, {Instruction::SHL, {{A}, {X}}}}}, + {Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}}, [=]() -> Pattern { bigint sum = bigint(A.d()) + B.d(); - if (sum >= 256) - return {Instruction::AND, {X, u256(0)}}; + if (sum >= Pattern::WordSize) + return {Pattern::Builtins::AND, {X, Word(0)}}; else - return {Instruction::SHL, {u256(sum), X}}; + return {Pattern::Builtins::SHL, {Word(sum), X}}; }, false }); @@ -396,13 +409,13 @@ std::vector> simplificationRuleListPart7( // Combine two SHR by constant rules.push_back({ // SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X) - {Instruction::SHR, {{B}, {Instruction::SHR, {{A}, {X}}}}}, + {Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}}, [=]() -> Pattern { bigint sum = bigint(A.d()) + B.d(); - if (sum >= 256) - return {Instruction::AND, {X, u256(0)}}; + if (sum >= Pattern::WordSize) + return {Pattern::Builtins::AND, {X, Word(0)}}; else - return {Instruction::SHR, {u256(sum), X}}; + return {Pattern::Builtins::SHR, {Word(sum), X}}; }, false }); @@ -410,112 +423,112 @@ std::vector> simplificationRuleListPart7( // Combine SHL-SHR by constant rules.push_back({ // SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask) - {Instruction::SHR, {{B}, {Instruction::SHL, {{A}, {X}}}}}, + {Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}}, [=]() -> Pattern { - u256 mask = shlWorkaround(u256(-1), unsigned(A.d())) >> unsigned(B.d()); + Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d()); if (A.d() > B.d()) - return {Instruction::AND, {{Instruction::SHL, {A.d() - B.d(), X}}, mask}}; + return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}}; else if (B.d() > A.d()) - return {Instruction::AND, {{Instruction::SHR, {B.d() - A.d(), X}}, mask}}; + return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}}; else - return {Instruction::AND, {X, mask}}; + return {Pattern::Builtins::AND, {X, mask}}; }, false, - [=] { return A.d() < 256 && B.d() < 256; } + [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } }); // Combine SHR-SHL by constant rules.push_back({ // SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask) - {Instruction::SHL, {{B}, {Instruction::SHR, {{A}, {X}}}}}, + {Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}}, [=]() -> Pattern { - u256 mask = shlWorkaround(u256(-1) >> unsigned(A.d()), unsigned(B.d())); + Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d())); if (A.d() > B.d()) - return {Instruction::AND, {{Instruction::SHR, {A.d() - B.d(), X}}, mask}}; + return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}}; else if (B.d() > A.d()) - return {Instruction::AND, {{Instruction::SHL, {B.d() - A.d(), X}}, mask}}; + return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}}; else - return {Instruction::AND, {X, mask}}; + return {Pattern::Builtins::AND, {X, mask}}; }, false, - [=] { return A.d() < 256 && B.d() < 256; } + [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } }); // Move AND with constant across SHL and SHR by constant - for (auto shiftOp: {Instruction::SHL, Instruction::SHR}) + for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR}) { auto replacement = [=]() -> Pattern { - u256 mask = - shiftOp == Instruction::SHL ? + Word mask = + shiftOp == Pattern::Builtins::SHL ? shlWorkaround(A.d(), unsigned(B.d())) : A.d() >> unsigned(B.d()); - return {Instruction::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}}; + return {Pattern::Builtins::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}}; }; rules.push_back({ // SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) - {shiftOp, {{B}, {Instruction::AND, {{X}, {A}}}}}, + {shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}}, replacement, false, - [=] { return B.d() < 256; } + [=] { return B.d() < Pattern::WordSize; } }); rules.push_back({ // SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) - {shiftOp, {{B}, {Instruction::AND, {{A}, {X}}}}}, + {shiftOp, {{B}, {Pattern::Builtins::AND, {{A}, {X}}}}}, replacement, false, - [=] { return B.d() < 256; } + [=] { return B.d() < Pattern::WordSize; } }); } rules.push_back({ // MUL(X, SHL(Y, 1)) -> SHL(Y, X) - {Instruction::MUL, {X, {Instruction::SHL, {Y, u256(1)}}}}, + {Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}}, [=]() -> Pattern { - return {Instruction::SHL, {Y, X}}; + return {Pattern::Builtins::SHL, {Y, X}}; }, // Actually only changes the order, does not remove. true }); rules.push_back({ // MUL(SHL(X, 1), Y) -> SHL(X, Y) - {Instruction::MUL, {{Instruction::SHL, {X, u256(1)}}, Y}}, + {Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}}, [=]() -> Pattern { - return {Instruction::SHL, {X, Y}}; + return {Pattern::Builtins::SHL, {X, Y}}; }, false }); rules.push_back({ // DIV(X, SHL(Y, 1)) -> SHR(Y, X) - {Instruction::DIV, {X, {Instruction::SHL, {Y, u256(1)}}}}, + {Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}}, [=]() -> Pattern { - return {Instruction::SHR, {Y, X}}; + return {Pattern::Builtins::SHR, {Y, X}}; }, // Actually only changes the order, does not remove. true }); std::function feasibilityFunction = [=]() { - if (B.d() > 256) + if (B.d() > Pattern::WordSize) return false; unsigned bAsUint = static_cast(B.d()); - return (A.d() & (u256(-1) >> bAsUint)) == (u256(-1) >> bAsUint); + return (A.d() & ((~Word(0)) >> bAsUint)) == ((~Word(0)) >> bAsUint); }; rules.push_back({ // AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B) - {Instruction::AND, {A, {Instruction::SHR, {B, X}}}}, - [=]() -> Pattern { return {Instruction::SHR, {B, X}}; }, + {Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}}, + [=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; }, false, feasibilityFunction }); rules.push_back({ // AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B) - {Instruction::AND, {{Instruction::SHR, {B, X}}, A}}, - [=]() -> Pattern { return {Instruction::SHR, {B, X}}; }, + {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}}, + [=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; }, false, feasibilityFunction }); @@ -538,28 +551,28 @@ std::vector> simplificationRuleListPart8( rules += std::vector>{ { // X - A -> X + (-A) - {Instruction::SUB, {X, A}}, - [=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; }, + {Pattern::Builtins::SUB, {X, A}}, + [=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; }, false }, { // (X + A) - Y -> (X - Y) + A - {Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}}, - [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }, + {Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}}, + [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; }, false }, { // (A + X) - Y -> (X - Y) + A - {Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}}, - [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }, + {Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}}, + [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; }, false }, { // X - (Y + A) -> (X - Y) + (-A) - {Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}}, - [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }, + {Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}}, + [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; }, false }, { // X - (A + Y) -> (X - Y) + (-A) - {Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}}, - [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }, + {Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}}, + [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; }, false } }; @@ -577,29 +590,31 @@ std::vector> simplificationRuleListPart9( Pattern Z ) { + using Word = typename Pattern::Word; std::vector> rules; - u256 const mask = (u256(1) << 160) - 1; + assertThrow(Pattern::WordSize > 160, OptimizerException, ""); + Word const mask = (Word(1) << 160) - 1; // CREATE rules.push_back({ - {Instruction::AND, {{Instruction::CREATE, {W, X, Y}}, mask}}, - [=]() -> Pattern { return {Instruction::CREATE, {W, X, Y}}; }, + {Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}}, + [=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; }, false }); rules.push_back({ - {Instruction::AND, {{mask, {Instruction::CREATE, {W, X, Y}}}}}, - [=]() -> Pattern { return {Instruction::CREATE, {W, X, Y}}; }, + {Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}}, + [=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; }, false }); // CREATE2 rules.push_back({ - {Instruction::AND, {{Instruction::CREATE2, {W, X, Y, Z}}, mask}}, - [=]() -> Pattern { return {Instruction::CREATE2, {W, X, Y, Z}}; }, + {Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}}, + [=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; }, false }); rules.push_back({ - {Instruction::AND, {{mask, {Instruction::CREATE2, {W, X, Y, Z}}}}}, - [=]() -> Pattern { return {Instruction::CREATE2, {W, X, Y, Z}}; }, + {Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}}, + [=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; }, false }); @@ -621,6 +636,14 @@ std::vector> simplificationRuleList( Pattern Z ) { + using Word = typename Pattern::Word; + // Some sanity checks + assertThrow(Pattern::WordSize % 8 == 0, OptimizerException, ""); + assertThrow(Pattern::WordSize >= 8, OptimizerException, ""); + assertThrow(Pattern::WordSize <= 256, OptimizerException, ""); + assertThrow(Word(-1) == ~Word(0), OptimizerException, ""); + assertThrow(Word(-1) + 1 == Word(0), OptimizerException, ""); + std::vector> rules; rules += simplificationRuleListPart1(A, B, C, W, X); rules += simplificationRuleListPart2(A, B, C, W, X); diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index 5c752545f..b35b89551 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -20,6 +20,7 @@ #pragma once +#include #include namespace dev @@ -54,5 +55,85 @@ struct SimplificationRule std::function feasible; }; +struct EVMBuiltins +{ + using InstrType = Instruction; + static auto constexpr STOP = Instruction::STOP; + static auto constexpr ADD = Instruction::ADD; + static auto constexpr SUB = Instruction::SUB; + static auto constexpr MUL = Instruction::MUL; + static auto constexpr DIV = Instruction::DIV; + static auto constexpr SDIV = Instruction::SDIV; + static auto constexpr MOD = Instruction::MOD; + static auto constexpr SMOD = Instruction::SMOD; + static auto constexpr EXP = Instruction::EXP; + static auto constexpr NOT = Instruction::NOT; + static auto constexpr LT = Instruction::LT; + static auto constexpr GT = Instruction::GT; + static auto constexpr SLT = Instruction::SLT; + static auto constexpr SGT = Instruction::SGT; + static auto constexpr EQ = Instruction::EQ; + static auto constexpr ISZERO = Instruction::ISZERO; + static auto constexpr AND = Instruction::AND; + static auto constexpr OR = Instruction::OR; + static auto constexpr XOR = Instruction::XOR; + static auto constexpr BYTE = Instruction::BYTE; + static auto constexpr SHL = Instruction::SHL; + static auto constexpr SHR = Instruction::SHR; + static auto constexpr SAR = Instruction::SAR; + static auto constexpr ADDMOD = Instruction::ADDMOD; + static auto constexpr MULMOD = Instruction::MULMOD; + static auto constexpr SIGNEXTEND = Instruction::SIGNEXTEND; + static auto constexpr KECCAK256 = Instruction::KECCAK256; + static auto constexpr ADDRESS = Instruction::ADDRESS; + static auto constexpr BALANCE = Instruction::BALANCE; + static auto constexpr ORIGIN = Instruction::ORIGIN; + static auto constexpr CALLER = Instruction::CALLER; + static auto constexpr CALLVALUE = Instruction::CALLVALUE; + static auto constexpr CALLDATALOAD = Instruction::CALLDATALOAD; + static auto constexpr CALLDATASIZE = Instruction::CALLDATASIZE; + static auto constexpr CALLDATACOPY = Instruction::CALLDATACOPY; + static auto constexpr CODESIZE = Instruction::CODESIZE; + static auto constexpr CODECOPY = Instruction::CODECOPY; + static auto constexpr GASPRICE = Instruction::GASPRICE; + static auto constexpr EXTCODESIZE = Instruction::EXTCODESIZE; + static auto constexpr EXTCODECOPY = Instruction::EXTCODECOPY; + static auto constexpr RETURNDATASIZE = Instruction::RETURNDATASIZE; + static auto constexpr RETURNDATACOPY = Instruction::RETURNDATACOPY; + static auto constexpr EXTCODEHASH = Instruction::EXTCODEHASH; + static auto constexpr BLOCKHASH = Instruction::BLOCKHASH; + static auto constexpr COINBASE = Instruction::COINBASE; + static auto constexpr TIMESTAMP = Instruction::TIMESTAMP; + static auto constexpr NUMBER = Instruction::NUMBER; + static auto constexpr DIFFICULTY = Instruction::DIFFICULTY; + static auto constexpr GASLIMIT = Instruction::GASLIMIT; + static auto constexpr CHAINID = Instruction::CHAINID; + static auto constexpr SELFBALANCE = Instruction::SELFBALANCE; + static auto constexpr POP = Instruction::POP; + static auto constexpr MLOAD = Instruction::MLOAD; + static auto constexpr MSTORE = Instruction::MSTORE; + static auto constexpr MSTORE8 = Instruction::MSTORE8; + static auto constexpr SLOAD = Instruction::SLOAD; + static auto constexpr SSTORE = Instruction::SSTORE; + static auto constexpr PC = Instruction::PC; + static auto constexpr MSIZE = Instruction::MSIZE; + static auto constexpr GAS = Instruction::GAS; + static auto constexpr LOG0 = Instruction::LOG0; + static auto constexpr LOG1 = Instruction::LOG1; + static auto constexpr LOG2 = Instruction::LOG2; + static auto constexpr LOG3 = Instruction::LOG3; + static auto constexpr LOG4 = Instruction::LOG4; + static auto constexpr CREATE = Instruction::CREATE; + static auto constexpr CALL = Instruction::CALL; + static auto constexpr CALLCODE = Instruction::CALLCODE; + static auto constexpr STATICCALL = Instruction::STATICCALL; + static auto constexpr RETURN = Instruction::RETURN; + static auto constexpr DELEGATECALL = Instruction::DELEGATECALL; + static auto constexpr CREATE2 = Instruction::CREATE2; + static auto constexpr REVERT = Instruction::REVERT; + static auto constexpr INVALID = Instruction::INVALID; + static auto constexpr SELFDESTRUCT = Instruction::SELFDESTRUCT; +}; + } } diff --git a/libevmasm/SimplificationRules.h b/libevmasm/SimplificationRules.h index fc45a46c3..a8c782b26 100644 --- a/libevmasm/SimplificationRules.h +++ b/libevmasm/SimplificationRules.h @@ -87,6 +87,10 @@ public: using Expression = ExpressionClasses::Expression; using Id = ExpressionClasses::Id; + using Builtins = dev::eth::EVMBuiltins; + static constexpr size_t WordSize = 256; + using Word = u256; + // Matches a specific constant value. Pattern(unsigned _value): Pattern(u256(_value)) {} // Matches a specific constant value. diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 3c67e304d..95a9eb089 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -22,9 +22,9 @@ #include +#include #include -#include #include @@ -51,12 +51,12 @@ public: static EVMVersion istanbul() { return {Version::Istanbul}; } static EVMVersion berlin() { return {Version::Berlin}; } - static boost::optional fromString(std::string const& _version) + static std::optional fromString(std::string const& _version) { for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg(), istanbul(), berlin()}) if (_version == v.name()) return v; - return {}; + return std::nullopt; } bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; } diff --git a/liblangutil/Exceptions.cpp b/liblangutil/Exceptions.cpp index 4e68dfa5b..4fd411b6e 100644 --- a/liblangutil/Exceptions.cpp +++ b/liblangutil/Exceptions.cpp @@ -29,7 +29,7 @@ using namespace langutil; Error::Error(Type _type, SourceLocation const& _location, string const& _description): m_type(_type) { - switch(m_type) + switch (m_type) { case Type::DeclarationError: m_typeName = "DeclarationError"; diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 131462289..0d65334fb 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -53,8 +53,9 @@ #include #include #include -#include + #include +#include #include #include @@ -187,7 +188,7 @@ bool Scanner::scanHexByte(char& o_scannedByte) return true; } -boost::optional Scanner::scanUnicode() +std::optional Scanner::scanUnicode() { unsigned x = 0; for (int i = 0; i < 4; i++) @@ -718,7 +719,7 @@ bool Scanner::scanEscape() break; case 'u': { - if (boost::optional codepoint = scanUnicode()) + if (auto const codepoint = scanUnicode(); codepoint.has_value()) addUnicodeAsUTF8(*codepoint); else return false; diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 3914b85ac..f5b535cce 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -57,6 +57,8 @@ #include #include #include + +#include #include namespace langutil @@ -207,7 +209,7 @@ private: inline Token selectToken(char _next, Token _then, Token _else); bool scanHexByte(char& o_scannedByte); - boost::optional scanUnicode(); + std::optional scanUnicode(); /// Scans a single Solidity token. void scanToken(); diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 81e86e621..ded239559 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -79,6 +79,8 @@ set(sources formal/BMC.h formal/CHC.cpp formal/CHC.h + formal/CHCSmtLib2Interface.cpp + formal/CHCSmtLib2Interface.h formal/CHCSolverInterface.h formal/EncodingContext.cpp formal/EncodingContext.h @@ -111,6 +113,8 @@ set(sources interface/ReadFile.h interface/StandardCompiler.cpp interface/StandardCompiler.h + interface/StorageLayout.cpp + interface/StorageLayout.h interface/Version.cpp interface/Version.h parsing/DocStringParser.cpp diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index fb766eea9..6bafc4caa 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -49,7 +49,7 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation) { solAssert(!!m_currentNode, ""); - switch(_operation.getOperator()) + switch (_operation.getOperator()) { case Token::Or: case Token::And: diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index e9792ddf2..cc28220ca 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -38,10 +38,12 @@ namespace solidity NameAndTypeResolver::NameAndTypeResolver( GlobalContext& _globalContext, + langutil::EVMVersion _evmVersion, map>& _scopes, ErrorReporter& _errorReporter -) : +): m_scopes(_scopes), + m_evmVersion(_evmVersion), m_errorReporter(_errorReporter), m_globalContext(_globalContext) { @@ -324,7 +326,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res { if (m_scopes.count(&_node)) setScope(&_node); - return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node); + return ReferencesResolver(m_errorReporter, *this, m_evmVersion, _resolveInsideCode).resolve(_node); } } diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index e5f5774ab..0c4452cde 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -28,6 +28,8 @@ #include #include +#include + #include #include @@ -55,6 +57,7 @@ public: /// are filled during the lifetime of this object. NameAndTypeResolver( GlobalContext& _globalContext, + langutil::EVMVersion _evmVersion, std::map>& _scopes, langutil::ErrorReporter& _errorReporter ); @@ -130,6 +133,7 @@ private: /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope. std::map>& m_scopes; + langutil::EVMVersion m_evmVersion; DeclarationContainer* m_currentScope = nullptr; langutil::ErrorReporter& m_errorReporter; GlobalContext& m_globalContext; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index a1df47abe..d8627d49d 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -122,11 +122,11 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName) if (!_typeName.annotation().type) { _typeName.annotation().type = TypeProvider::fromElementaryTypeName(_typeName.typeName()); - if (_typeName.stateMutability().is_initialized()) + if (_typeName.stateMutability().has_value()) { // for non-address types this was already caught by the parser solAssert(_typeName.annotation().type->category() == Type::Category::Address, ""); - switch(*_typeName.stateMutability()) + switch (*_typeName.stateMutability()) { case StateMutability::Payable: _typeName.annotation().type = TypeProvider::payableAddress(); @@ -323,12 +323,12 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // Will be re-generated later with correct information // We use the latest EVM version because we will re-run it anyway. yul::AsmAnalysisInfo analysisInfo; - boost::optional errorTypeForLoose = Error::Type::SyntaxError; + std::optional errorTypeForLoose = Error::Type::SyntaxError; yul::AsmAnalyzer( analysisInfo, errorsIgnored, errorTypeForLoose, - yul::EVMDialect::looseAssemblyForEVM(EVMVersion{}), + yul::EVMDialect::looseAssemblyForEVM(m_evmVersion), resolver ).analyze(_inlineAssembly.operations()); return false; diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 5631fb801..207eecdda 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -52,10 +53,12 @@ public: ReferencesResolver( langutil::ErrorReporter& _errorReporter, NameAndTypeResolver& _resolver, + langutil::EVMVersion _evmVersion, bool _resolveInsideCode = false ): m_errorReporter(_errorReporter), m_resolver(_resolver), + m_evmVersion(_evmVersion), m_resolveInsideCode(_resolveInsideCode) {} @@ -96,6 +99,7 @@ private: langutil::ErrorReporter& m_errorReporter; NameAndTypeResolver& m_resolver; + langutil::EVMVersion m_evmVersion; /// Stack of return parameters. std::vector m_returnParameters; bool const m_resolveInsideCode; diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 2a0aaddcb..0d2a978db 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -227,7 +227,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) if (m_constructor) { auto const* expr = &_memberAccess.expression(); - while(expr) + while (expr) { if (auto id = dynamic_cast(expr)) { diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b5abe59f4..262db9d1d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -390,11 +390,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function) set modifiers; for (ASTPointer const& modifier: _function.modifiers()) { + auto baseContracts = dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts; + // Delete first base which is just the main contract itself + baseContracts.erase(baseContracts.begin()); + visitManually( *modifier, - _function.isConstructor() ? - dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts : - vector() + _function.isConstructor() ? baseContracts : vector() ); Declaration const* decl = &dereference(*modifier->name()); if (modifiers.count(decl)) @@ -448,6 +450,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) TypePointer varType = _variable.annotation().type; solAssert(!!varType, "Variable type not provided."); + if (auto contractType = dynamic_cast(varType)) + if (contractType->contractDefinition().isLibrary()) + m_errorReporter.typeError(_variable.location(), "The type of a variable cannot be a library."); if (_variable.value()) expectType(*_variable.value(), *varType); if (_variable.isConstant()) @@ -522,7 +527,12 @@ void TypeChecker::visitManually( _modifier.arguments() ? *_modifier.arguments() : std::vector>(); for (ASTPointer const& argument: arguments) argument->accept(*this); - _modifier.name()->accept(*this); + + { + m_insideModifierInvocation = true; + ScopeGuard resetFlag{[&] () { m_insideModifierInvocation = false; }}; + _modifier.name()->accept(*this); + } auto const* declaration = &dereference(*_modifier.name()); vector> emptyParameterList; @@ -695,6 +705,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) } else if (_context == yul::IdentifierContext::LValue) { + if (dynamic_cast(declaration)) + return size_t(-1); + m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly."); return size_t(-1); } @@ -2342,12 +2355,30 @@ bool TypeChecker::visit(Identifier const& _identifier) if (functionType->canTakeArguments(*annotation.arguments)) candidates.push_back(declaration); } - if (candidates.empty()) - m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup."); - else if (candidates.size() == 1) + if (candidates.size() == 1) annotation.referencedDeclaration = candidates.front(); else - m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup."); + { + SecondarySourceLocation ssl; + + for (Declaration const* declaration: annotation.overloadedDeclarations) + if (declaration->location().isEmpty()) + { + // Try to re-construct function definition + string description; + for (auto const& param: declaration->functionType(true)->parameterTypes()) + description += (description.empty() ? "" : ", ") + param->toString(false); + description = "function " + _identifier.name() + "(" + description + ")"; + + ssl.append("Candidate: " + description, declaration->location()); + } + else + ssl.append("Candidate:", declaration->location()); + if (candidates.empty()) + m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No matching declaration found after argument-dependent lookup."); + else + m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No unique declaration found after argument-dependent lookup."); + } } } solAssert( @@ -2376,15 +2407,24 @@ bool TypeChecker::visit(Identifier const& _identifier) if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256) m_errorReporter.typeError( _identifier.location(), - "\"sha3\" has been deprecated in favour of \"keccak256\"" + "\"sha3\" has been deprecated in favour of \"keccak256\"." ); else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct) m_errorReporter.typeError( _identifier.location(), - "\"suicide\" has been deprecated in favour of \"selfdestruct\"" + "\"suicide\" has been deprecated in favour of \"selfdestruct\"." ); } + if (!m_insideModifierInvocation) + if (ModifierType const* type = dynamic_cast(_identifier.annotation().type)) + { + m_errorReporter.typeError( + _identifier.location(), + "Modifier can only be referenced in function headers." + ); + } + return false; } diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 8f532a89c..003915b2e 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -166,6 +166,9 @@ private: /// Flag indicating whether we are currently inside a StructDefinition. bool m_insideStruct = false; + /// Flag indicating whether we are currently inside the invocation of a modifier + bool m_insideModifierInvocation = false; + langutil::ErrorReporter& m_errorReporter; }; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index cf35a4889..58baec906 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -241,7 +241,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) void ViewPureChecker::reportMutability( StateMutability _mutability, SourceLocation const& _location, - boost::optional const& _nestedLocation + std::optional const& _nestedLocation ) { if (_mutability > m_bestMutabilityAndLocation.mutability) diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index fd2432a7d..edcabf2f0 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -23,6 +23,7 @@ #include #include +#include namespace langutil { @@ -67,7 +68,7 @@ private: void reportMutability( StateMutability _mutability, langutil::SourceLocation const& _location, - boost::optional const& _nestedLocation = {} + std::optional const& _nestedLocation = {} ); std::vector> const& m_ast; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4d03bf4ec..b86b28bc6 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -186,7 +187,7 @@ public: static std::string visibilityToString(Declaration::Visibility _visibility) { - switch(_visibility) + switch (_visibility) { case Declaration::Visibility::Public: return "public"; @@ -912,10 +913,10 @@ public: ElementaryTypeName( SourceLocation const& _location, ElementaryTypeNameToken const& _elem, - boost::optional _stateMutability = {} + std::optional _stateMutability = {} ): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability) { - solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, ""); + solAssert(!_stateMutability.has_value() || _elem.token() == Token::Address, ""); } void accept(ASTVisitor& _visitor) override; @@ -923,11 +924,11 @@ public: ElementaryTypeNameToken const& typeName() const { return m_type; } - boost::optional const& stateMutability() const { return m_stateMutability; } + std::optional const& stateMutability() const { return m_stateMutability; } private: ElementaryTypeNameToken m_type; - boost::optional m_stateMutability; ///< state mutability for address type + std::optional m_stateMutability; ///< state mutability for address type }; /** diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 539ba85bf..eb9002a03 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -26,10 +26,9 @@ #include #include -#include - #include #include +#include #include #include @@ -183,7 +182,7 @@ struct ExpressionAnnotation: ASTAnnotation /// Types and - if given - names of arguments if the expr. is a function /// that is called, used for overload resoultion - boost::optional arguments; + std::optional arguments; }; struct IdentifierAnnotation: ExpressionAnnotation diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h index a1a36efbd..11e539a8c 100644 --- a/libsolidity/ast/ASTEnums.h +++ b/libsolidity/ast/ASTEnums.h @@ -36,7 +36,7 @@ enum class StateMutability { Pure, View, NonPayable, Payable }; inline std::string stateMutabilityToString(StateMutability const& _stateMutability) { - switch(_stateMutability) + switch (_stateMutability) { case StateMutability::Pure: return "pure"; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 5424ee5bc..0162fc1c1 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -147,7 +148,7 @@ Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp, bool _short) return typeDescriptions; } -Json::Value ASTJsonConverter::typePointerToJson(boost::optional const& _tps) +Json::Value ASTJsonConverter::typePointerToJson(std::optional const& _tps) { if (_tps) { @@ -189,7 +190,7 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair #include + +#include +#include #include #include #include -#include namespace langutil { @@ -169,7 +171,7 @@ private: return json; } static Json::Value typePointerToJson(TypePointer _tp, bool _short = false); - static Json::Value typePointerToJson(boost::optional const& _tps); + static Json::Value typePointerToJson(std::optional const& _tps); void appendExpressionAttributes( std::vector> &_attributes, ExpressionAnnotation const& _annotation diff --git a/libsolidity/ast/TypeProvider.h b/libsolidity/ast/TypeProvider.h index 4b18f52e6..0aefc9edd 100644 --- a/libsolidity/ast/TypeProvider.h +++ b/libsolidity/ast/TypeProvider.h @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace dev diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 25d31a857..f5e315829 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -658,7 +658,7 @@ BoolResult FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) con TypeResult FixedPointType::unaryOperatorResult(Token _operator) const { - switch(_operator) + switch (_operator) { case Token::Delete: // "delete" is ok for all fixed types @@ -1812,10 +1812,10 @@ TypePointer ArrayType::decodingType() const TypeResult ArrayType::interfaceType(bool _inLibrary) const { - if (_inLibrary && m_interfaceType_library.is_initialized()) + if (_inLibrary && m_interfaceType_library.has_value()) return *m_interfaceType_library; - if (!_inLibrary && m_interfaceType.is_initialized()) + if (!_inLibrary && m_interfaceType.has_value()) return *m_interfaceType; TypeResult result{TypePointer{}}; @@ -1849,7 +1849,7 @@ u256 ArrayType::memoryDataSize() const solAssert(m_location == DataLocation::Memory, ""); solAssert(!isByteArray(), ""); bigint size = bigint(m_length) * m_baseType->memoryHeadSize(); - solAssert(size <= numeric_limits::max(), "Array size does not fit u256."); + solAssert(size <= numeric_limits::max(), "Array size does not fit u256."); return u256(size); } @@ -2106,10 +2106,10 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const TypeResult StructType::interfaceType(bool _inLibrary) const { - if (_inLibrary && m_interfaceType_library.is_initialized()) + if (_inLibrary && m_interfaceType_library.has_value()) return *m_interfaceType_library; - if (!_inLibrary && m_interfaceType.is_initialized()) + if (!_inLibrary && m_interfaceType.has_value()) return *m_interfaceType; TypeResult result{TypePointer{}}; @@ -2166,7 +2166,7 @@ TypeResult StructType::interfaceType(bool _inLibrary) const } }; - m_recursive = m_recursive.get() || (CycleDetector(visitor).run(structDefinition()) != nullptr); + m_recursive = m_recursive.value() || (CycleDetector(visitor).run(structDefinition()) != nullptr); std::string const recursiveErrMsg = "Recursive type not allowed for public or external contract functions."; @@ -2179,13 +2179,13 @@ TypeResult StructType::interfaceType(bool _inLibrary) const else m_interfaceType_library = TypeProvider::withLocation(this, DataLocation::Memory, true); - if (m_recursive.get()) + if (m_recursive.value()) m_interfaceType = TypeResult::err(recursiveErrMsg); return *m_interfaceType_library; } - if (m_recursive.get()) + if (m_recursive.value()) m_interfaceType = TypeResult::err(recursiveErrMsg); else if (!result.message().empty()) m_interfaceType = result; @@ -2846,7 +2846,7 @@ unsigned FunctionType::sizeOnStack() const unsigned size = 0; - switch(kind) + switch (kind) { case Kind::External: case Kind::DelegateCall: @@ -3373,6 +3373,15 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current return members; } +BoolResult TypeType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (auto const* address = dynamic_cast(&_convertTo)) + if (address->stateMutability() == StateMutability::NonPayable) + if (auto const* contractType = dynamic_cast(m_actualType)) + return contractType->contractDefinition().isLibrary(); + return isImplicitlyConvertibleTo(_convertTo); +} + ModifierType::ModifierType(ModifierDefinition const& _modifier) { TypePointers params; diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 992d95552..ac50c5862 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -31,11 +31,11 @@ #include #include -#include #include #include #include +#include #include #include @@ -769,8 +769,8 @@ private: Type const* m_baseType; bool m_hasDynamicLength = true; u256 m_length; - mutable boost::optional m_interfaceType; - mutable boost::optional m_interfaceType_library; + mutable std::optional m_interfaceType; + mutable std::optional m_interfaceType_library; }; /** @@ -865,12 +865,12 @@ public: bool recursive() const { - if (m_recursive.is_initialized()) - return m_recursive.get(); + if (m_recursive.has_value()) + return m_recursive.value(); interfaceType(false); - return m_recursive.get(); + return m_recursive.value(); } std::unique_ptr copyForLocation(DataLocation _location, bool _isPointer) const override; @@ -898,9 +898,9 @@ public: private: StructDefinition const& m_struct; // Caches for interfaceType(bool) - mutable boost::optional m_interfaceType; - mutable boost::optional m_interfaceType_library; - mutable boost::optional m_recursive; + mutable std::optional m_interfaceType; + mutable std::optional m_interfaceType_library; + mutable std::optional m_recursive; }; /** @@ -1287,6 +1287,7 @@ public: std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; private: TypePointer m_actualType; }; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index da947073f..d6045ee20 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -411,7 +411,7 @@ void CompilerContext::appendInlineAssembly( analyzerResult = yul::AsmAnalyzer( analysisInfo, errorReporter, - boost::none, + std::nullopt, dialect, identifierAccess.resolve ).analyze(*parserResult); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 2f8bf9455..62ae2dd63 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -475,7 +475,22 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.names().empty(), ""); - acceptAndConvert(*_functionCall.arguments().front(), *_functionCall.annotation().type); + auto const& expression = *_functionCall.arguments().front(); + auto const& targetType = *_functionCall.annotation().type; + if (auto const* typeType = dynamic_cast(expression.annotation().type)) + if (auto const* addressType = dynamic_cast(&targetType)) + { + auto const* contractType = dynamic_cast(typeType->actualType()); + solAssert( + contractType && + contractType->contractDefinition().isLibrary() && + addressType->stateMutability() == StateMutability::NonPayable, + "" + ); + m_context.appendLibraryAddress(contractType->contractDefinition().fullyQualifiedName()); + return false; + } + acceptAndConvert(expression, targetType); return false; } @@ -1229,6 +1244,28 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) utils().leftShiftNumberOnStack(224); return false; } + // Another special case for `address(this).balance`. Post-Istanbul, we can use the selfbalance + // opcode. + if ( + m_context.evmVersion().hasSelfBalance() && + member == "balance" && + _memberAccess.expression().annotation().type->category() == Type::Category::Address + ) + if (FunctionCall const* funCall = dynamic_cast(&_memberAccess.expression())) + if (auto const* addr = dynamic_cast(&funCall->expression())) + if ( + addr->typeName().token() == Token::Address && + funCall->arguments().size() == 1 + ) + if (auto arg = dynamic_cast( funCall->arguments().front().get())) + if ( + arg->name() == "this" && + dynamic_cast(arg->annotation().referencedDeclaration) + ) + { + m_context << Instruction::SELFBALANCE; + return false; + } _memberAccess.expression().accept(*this); switch (_memberAccess.expression().annotation().type->category()) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 9bb4a9984..8910ad7e7 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -962,11 +962,11 @@ string YulUtilFunctions::readFromCalldata(Type const& _type) return readFromMemoryOrCalldata(_type, true); } -string YulUtilFunctions::updateStorageValueFunction(Type const& _type, boost::optional const& _offset) +string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::optional const& _offset) { string const functionName = "update_storage_value_" + - (_offset.is_initialized() ? ("offset_" + to_string(*_offset)) : "") + + (_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") + _type.identifier(); return m_functionCollector->createFunction(functionName, [&] { @@ -983,11 +983,11 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, boost::op )") ("functionName", functionName) ("update", - _offset.is_initialized() ? + _offset.has_value() ? updateByteSliceFunction(_type.storageBytes(), *_offset) : updateByteSliceFunctionDynamic(_type.storageBytes()) ) - ("offset", _offset.is_initialized() ? "" : "offset, ") + ("offset", _offset.has_value() ? "" : "offset, ") ("prepare", prepareStoreFunction(_type)) .render(); } diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 3b9617c71..68d57a6fd 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -197,7 +197,7 @@ public: /// the specified slot and offset. If offset is not given, it is expected as /// runtime parameter. /// signature: (slot, [offset,] value) - std::string updateStorageValueFunction(Type const& _type, boost::optional const& _offset = boost::optional()); + std::string updateStorageValueFunction(Type const& _type, std::optional const& _offset = std::optional()); /// Returns the name of a function that will write the given value to /// the specified address. diff --git a/libsolidity/codegen/ir/IRLValue.cpp b/libsolidity/codegen/ir/IRLValue.cpp index fb1bcdcbe..6ded09c05 100644 --- a/libsolidity/codegen/ir/IRLValue.cpp +++ b/libsolidity/codegen/ir/IRLValue.cpp @@ -115,7 +115,7 @@ string IRStorageItem::storeValue(string const& _value, Type const& _sourceType) if (m_type->isValueType()) solAssert(_sourceType == *m_type, "Different type, but might not be an error."); - boost::optional offset; + std::optional offset; if (m_offset.type() == typeid(unsigned)) offset = get(m_offset); diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 0fcccdd75..dc1f611b6 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -108,14 +108,16 @@ bool BMC::shouldInlineFunctionCall(FunctionCall const& _funCall) bool BMC::visit(ContractDefinition const& _contract) { - SMTEncoder::visit(_contract); + initContract(_contract); /// Check targets created by state variable initialization. smt::Expression constraints = m_context.assertions(); checkVerificationTargets(constraints); m_verificationTargets.clear(); - return true; + SMTEncoder::visit(_contract); + + return false; } void BMC::endVisit(ContractDefinition const& _contract) @@ -426,15 +428,20 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) auto const& funType = dynamic_cast(calledExpr->annotation().type); solAssert(funType, ""); + auto const& functionParams = funDef->parameters(); + auto const& arguments = _funCall.arguments(); + unsigned firstParam = 0; if (funType->bound()) { auto const& boundFunction = dynamic_cast(calledExpr); solAssert(boundFunction, ""); - funArgs.push_back(expr(boundFunction->expression())); + funArgs.push_back(expr(boundFunction->expression(), functionParams.front()->type())); + firstParam = 1; } - for (auto arg: _funCall.arguments()) - funArgs.push_back(expr(*arg)); + solAssert((arguments.size() + firstParam) == functionParams.size(), ""); + for (unsigned i = 0; i < arguments.size(); ++i) + funArgs.push_back(expr(*arguments.at(i), functionParams.at(i + firstParam)->type())); initializeFunctionCallParameters(*funDef, funArgs); // The reason why we need to pushCallStack here instead of visit(FunctionDefinition) diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 867be77dd..a917faf82 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -17,6 +17,8 @@ #include +#include + #ifdef HAVE_Z3 #include #endif @@ -30,13 +32,20 @@ using namespace dev; using namespace langutil; using namespace dev::solidity; -CHC::CHC(smt::EncodingContext& _context, ErrorReporter& _errorReporter): +CHC::CHC( + smt::EncodingContext& _context, + ErrorReporter& _errorReporter, + map const& _smtlib2Responses +): SMTEncoder(_context), #ifdef HAVE_Z3 m_interface(make_shared()), +#else + m_interface(make_shared(_smtlib2Responses)), #endif m_outerErrorReporter(_errorReporter) { + (void)_smtlib2Responses; } void CHC::analyze(SourceUnit const& _source) @@ -47,12 +56,24 @@ void CHC::analyze(SourceUnit const& _source) auto z3Interface = dynamic_pointer_cast(m_interface); solAssert(z3Interface, ""); m_context.setSolver(z3Interface->z3Interface()); +#else + auto smtlib2Interface = dynamic_pointer_cast(m_interface); + solAssert(smtlib2Interface, ""); + m_context.setSolver(smtlib2Interface->smtlib2Interface()); +#endif m_context.clear(); m_context.setAssertionAccumulation(false); m_variableUsage.setFunctionInlining(false); _source.accept(*this); -#endif +} + +vector CHC::unhandledQueries() const +{ + if (auto smtlib2 = dynamic_pointer_cast(m_interface)) + return smtlib2->unhandledQueries(); + + return {}; } bool CHC::visit(ContractDefinition const& _contract) @@ -62,8 +83,7 @@ bool CHC::visit(ContractDefinition const& _contract) reset(); - if (!SMTEncoder::visit(_contract)) - return false; + initContract(_contract); m_stateVariables = _contract.stateVariablesIncludingInherited(); @@ -89,7 +109,6 @@ bool CHC::visit(ContractDefinition const& _contract) // If the contract has a constructor it is handled as a function. // Otherwise we zero-initialize all state vars. - // TODO take into account state vars init values. if (!_contract.constructor()) { string constructorName = "constructor_" + _contract.name() + "_" + to_string(_contract.id()); @@ -101,14 +120,15 @@ bool CHC::visit(ContractDefinition const& _contract) { auto const& symbVar = m_context.variable(*var); symbVar->increaseIndex(); - m_interface->declareVariable(symbVar->currentName(), *symbVar->sort()); + m_interface->declareVariable(symbVar->currentName(), symbVar->sort()); m_context.setZeroValue(*symbVar); } connectBlocks(constructorPred, interface()); } - return true; + SMTEncoder::visit(_contract); + return false; } void CHC::endVisit(ContractDefinition const& _contract) @@ -137,24 +157,22 @@ bool CHC::visit(FunctionDefinition const& _function) initFunction(_function); + auto functionEntryBlock = createBlock(m_currentFunction); + auto bodyBlock = createBlock(&m_currentFunction->body()); + + auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables()); + auto bodyPred = predicate(*bodyBlock); + // Store the constraints related to variable initialization. smt::Expression const& initAssertions = m_context.assertions(); m_context.pushSolver(); - solAssert(m_functionBlocks == 0, ""); - - createBlock(m_currentFunction); - createBlock(&m_currentFunction->body(), "block_"); - - auto functionPred = predicate(m_currentFunction); - auto bodyPred = predicate(&m_currentFunction->body()); - connectBlocks(interface(), functionPred); connectBlocks(functionPred, bodyPred); m_context.popSolver(); - pushBlock(&m_currentFunction->body()); + setCurrentBlock(*bodyBlock); // We need to re-add the constraints that were created for initialization of variables. m_context.addAssertion(initAssertions); @@ -169,25 +187,10 @@ void CHC::endVisit(FunctionDefinition const& _function) if (!shouldVisit(_function)) return; - solAssert(m_currentFunction == &_function, "Inlining internal function calls not yet implemented"); - - // Function Exit block. - createBlock(m_currentFunction); - connectBlocks(m_path.back(), predicate(&_function)); - - // Rule FunctionExit -> Interface, uses no constraints. - clearIndices(); - m_context.pushSolver(); - connectBlocks(predicate(&_function), interface()); - m_context.popSolver(); + connectBlocks(m_currentBlock, interface()); + solAssert(&_function == m_currentFunction, ""); m_currentFunction = nullptr; - solAssert(m_path.size() == m_functionBlocks, ""); - while (m_functionBlocks > 0) - popBlock(); - - solAssert(m_path.empty(), ""); - SMTEncoder::endVisit(_function); } @@ -198,7 +201,38 @@ bool CHC::visit(IfStatement const& _if) bool unknownFunctionCallWasSeen = m_unknownFunctionCallSeen; m_unknownFunctionCallSeen = false; - SMTEncoder::visit(_if); + solAssert(m_currentFunction, ""); + auto const& functionBody = m_currentFunction->body(); + + auto ifHeaderBlock = createBlock(&_if, "if_header_"); + auto trueBlock = createBlock(&_if.trueStatement(), "if_true_"); + auto falseBlock = _if.falseStatement() ? createBlock(_if.falseStatement(), "if_false_") : nullptr; + auto afterIfBlock = createBlock(&functionBody); + + connectBlocks(m_currentBlock, predicate(*ifHeaderBlock)); + + setCurrentBlock(*ifHeaderBlock); + _if.condition().accept(*this); + auto condition = expr(_if.condition()); + + connectBlocks(m_currentBlock, predicate(*trueBlock), condition); + if (_if.falseStatement()) + connectBlocks(m_currentBlock, predicate(*falseBlock), !condition); + else + connectBlocks(m_currentBlock, predicate(*afterIfBlock), !condition); + + setCurrentBlock(*trueBlock); + _if.trueStatement().accept(*this); + connectBlocks(m_currentBlock, predicate(*afterIfBlock)); + + if (_if.falseStatement()) + { + setCurrentBlock(*falseBlock); + _if.falseStatement()->accept(*this); + connectBlocks(m_currentBlock, predicate(*afterIfBlock)); + } + + setCurrentBlock(*afterIfBlock); if (m_unknownFunctionCallSeen) eraseKnowledge(); @@ -214,16 +248,41 @@ bool CHC::visit(WhileStatement const& _while) m_unknownFunctionCallSeen = false; solAssert(m_currentFunction, ""); + auto const& functionBody = m_currentFunction->body(); + + auto namePrefix = string(_while.isDoWhile() ? "do_" : "") + "while"; + auto loopHeaderBlock = createBlock(&_while, namePrefix + "_header_"); + auto loopBodyBlock = createBlock(&_while.body(), namePrefix + "_body_"); + auto afterLoopBlock = createBlock(&functionBody); + + auto outerBreakDest = m_breakDest; + auto outerContinueDest = m_continueDest; + m_breakDest = afterLoopBlock.get(); + m_continueDest = loopHeaderBlock.get(); if (_while.isDoWhile()) _while.body().accept(*this); - visitLoop( - _while, - &_while.condition(), - _while.body(), - nullptr - ); + connectBlocks(m_currentBlock, predicate(*loopHeaderBlock)); + + setCurrentBlock(*loopHeaderBlock); + + _while.condition().accept(*this); + auto condition = expr(_while.condition()); + + connectBlocks(m_currentBlock, predicate(*loopBodyBlock), condition); + connectBlocks(m_currentBlock, predicate(*afterLoopBlock), !condition); + + // Loop body visit. + setCurrentBlock(*loopBodyBlock); + _while.body().accept(*this); + + m_breakDest = outerBreakDest; + m_continueDest = outerContinueDest; + + // Back edge. + connectBlocks(m_currentBlock, predicate(*loopHeaderBlock)); + setCurrentBlock(*afterLoopBlock); if (m_unknownFunctionCallSeen) eraseKnowledge(); @@ -239,16 +298,52 @@ bool CHC::visit(ForStatement const& _for) m_unknownFunctionCallSeen = false; solAssert(m_currentFunction, ""); + auto const& functionBody = m_currentFunction->body(); + + auto loopHeaderBlock = createBlock(&_for, "for_header_"); + auto loopBodyBlock = createBlock(&_for.body(), "for_body_"); + auto afterLoopBlock = createBlock(&functionBody); + auto postLoop = _for.loopExpression(); + auto postLoopBlock = postLoop ? createBlock(postLoop, "for_post_") : nullptr; + + auto outerBreakDest = m_breakDest; + auto outerContinueDest = m_continueDest; + m_breakDest = afterLoopBlock.get(); + m_continueDest = postLoop ? postLoopBlock.get() : loopHeaderBlock.get(); if (auto init = _for.initializationExpression()) init->accept(*this); - visitLoop( - _for, - _for.condition(), - _for.body(), - _for.loopExpression() - ); + connectBlocks(m_currentBlock, predicate(*loopHeaderBlock)); + setCurrentBlock(*loopHeaderBlock); + + auto condition = smt::Expression(true); + if (auto forCondition = _for.condition()) + { + forCondition->accept(*this); + condition = expr(*forCondition); + } + + connectBlocks(m_currentBlock, predicate(*loopBodyBlock), condition); + connectBlocks(m_currentBlock, predicate(*afterLoopBlock), !condition); + + // Loop body visit. + setCurrentBlock(*loopBodyBlock); + _for.body().accept(*this); + + if (postLoop) + { + connectBlocks(m_currentBlock, predicate(*postLoopBlock)); + setCurrentBlock(*postLoopBlock); + postLoop->accept(*this); + } + + m_breakDest = outerBreakDest; + m_continueDest = outerContinueDest; + + // Back edge. + connectBlocks(m_currentBlock, predicate(*loopHeaderBlock)); + setCurrentBlock(*afterLoopBlock); if (m_unknownFunctionCallSeen) eraseKnowledge(); @@ -301,16 +396,20 @@ void CHC::endVisit(FunctionCall const& _funCall) createReturnedExpressions(_funCall); } -void CHC::endVisit(Break const&) +void CHC::endVisit(Break const& _break) { solAssert(m_breakDest, ""); - m_breakSeen = true; + connectBlocks(m_currentBlock, predicate(*m_breakDest)); + auto breakGhost = createBlock(&_break, "break_ghost_"); + m_currentBlock = predicate(*breakGhost); } -void CHC::endVisit(Continue const&) +void CHC::endVisit(Continue const& _continue) { solAssert(m_continueDest, ""); - m_continueSeen = true; + connectBlocks(m_currentBlock, predicate(*m_continueDest)); + auto continueGhost = createBlock(&_continue, "continue_ghost_"); + m_currentBlock = predicate(*continueGhost); } void CHC::visitAssert(FunctionCall const& _funCall) @@ -319,12 +418,10 @@ void CHC::visitAssert(FunctionCall const& _funCall) solAssert(args.size() == 1, ""); solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); - solAssert(!m_path.empty(), ""); - createErrorBlock(); smt::Expression assertNeg = !(m_context.expression(*args.front())->currentValue()); - connectBlocks(m_path.back(), error(), currentPathConditions() && assertNeg); + connectBlocks(m_currentBlock, error(), currentPathConditions() && assertNeg); m_verificationTargets.push_back(&_funCall); } @@ -341,85 +438,6 @@ void CHC::unknownFunctionCall(FunctionCall const&) m_unknownFunctionCallSeen = true; } -void CHC::visitLoop( - BreakableStatement const& _loop, - Expression const* _condition, - Statement const& _body, - ASTNode const* _postLoop -) -{ - bool breakWasSeen = m_breakSeen; - bool continueWasSeen = m_continueSeen; - m_breakSeen = false; - m_continueSeen = false; - - solAssert(m_currentFunction, ""); - auto const& functionBody = m_currentFunction->body(); - - createBlock(&_loop, "loop_header_"); - createBlock(&_body, "loop_body_"); - createBlock(&functionBody, "block_"); - - connectBlocks(m_path.back(), predicate(&_loop)); - - // We need to save the next block here because new blocks - // might be created inside the loop body. - // This will be m_path.back() in the end of this function. - pushBlock(&functionBody); - - smt::Expression loopHeader = predicate(&_loop); - pushBlock(&_loop); - - if (_condition) - _condition->accept(*this); - auto condition = _condition ? expr(*_condition) : smt::Expression(true); - - connectBlocks(loopHeader, predicate(&_body), condition); - connectBlocks(loopHeader, predicate(&functionBody), !condition); - - // Loop body visit. - pushBlock(&_body); - - m_breakDest = &functionBody; - m_continueDest = _postLoop ? _postLoop : &_loop; - - auto functionBlocks = m_functionBlocks; - _body.accept(*this); - if (_postLoop) - { - createBlock(_postLoop, "loop_post_"); - connectBlocks(m_path.back(), predicate(_postLoop)); - pushBlock(_postLoop); - _postLoop->accept(*this); - } - - // Back edge. - connectBlocks(m_path.back(), predicate(&_loop)); - - // Pop all function blocks created by nested inner loops - // to adjust the assertion context. - for (unsigned i = m_functionBlocks; i > functionBlocks; --i) - popBlock(); - m_functionBlocks = functionBlocks; - - // Loop body - popBlock(); - // Loop header - popBlock(); - - // New function block starts with indices = 0 - clearIndices(); - - if (m_breakSeen || m_continueSeen) - { - eraseKnowledge(); - m_context.resetVariables([](VariableDeclaration const&) { return true; }); - } - - m_breakSeen = breakWasSeen; - m_continueSeen = continueWasSeen; -} - void CHC::reset() { m_stateSorts.clear(); @@ -427,8 +445,9 @@ void CHC::reset() m_verificationTargets.clear(); m_safeAssertions.clear(); m_unknownFunctionCallSeen = false; - m_breakSeen = false; - m_continueSeen = false; + m_blockCounter = 0; + m_breakDest = nullptr; + m_continueDest = nullptr; } void CHC::eraseKnowledge() @@ -458,19 +477,12 @@ bool CHC::shouldVisit(FunctionDefinition const& _function) const return false; } -void CHC::pushBlock(ASTNode const* _node) -{ - clearIndices(); - m_context.pushSolver(); - m_path.push_back(predicate(_node)); - ++m_functionBlocks; -} - -void CHC::popBlock() +void CHC::setCurrentBlock(smt::SymbolicFunctionVariable const& _block) { m_context.popSolver(); - m_path.pop_back(); - --m_functionBlocks; + clearIndices(); + m_context.pushSolver(); + m_currentBlock = predicate(_block); } smt::SortPointer CHC::constructorSort() @@ -493,25 +505,24 @@ smt::SortPointer CHC::interfaceSort() smt::SortPointer CHC::sort(FunctionDefinition const& _function) { - if (m_nodeSorts.count(&_function)) - return m_nodeSorts.at(&_function); - auto boolSort = make_shared(smt::Kind::Bool); vector varSorts; for (auto const& var: _function.parameters() + _function.returnParameters()) - varSorts.push_back(smt::smtSort(*var->type())); - auto sort = make_shared( + { + // SMT solvers do not support function types as arguments. + if (var->type()->category() == Type::Category::Function) + varSorts.push_back(make_shared(smt::Kind::Int)); + else + varSorts.push_back(smt::smtSort(*var->type())); + } + return make_shared( m_stateSorts + varSorts, boolSort ); - return m_nodeSorts[&_function] = move(sort); } smt::SortPointer CHC::sort(ASTNode const* _node) { - if (m_nodeSorts.count(_node)) - return m_nodeSorts.at(_node); - if (auto funDef = dynamic_cast(_node)) return sort(*funDef); @@ -521,12 +532,17 @@ smt::SortPointer CHC::sort(ASTNode const* _node) auto boolSort = make_shared(smt::Kind::Bool); vector varSorts; for (auto const& var: m_currentFunction->localVariables()) - varSorts.push_back(smt::smtSort(*var->type())); - auto functionBodySort = make_shared( + { + // SMT solvers do not support function types as arguments. + if (var->type()->category() == Type::Category::Function) + varSorts.push_back(make_shared(smt::Kind::Int)); + else + varSorts.push_back(smt::smtSort(*var->type())); + } + return make_shared( fSort->domain + varSorts, boolSort ); - return m_nodeSorts[_node] = move(functionBodySort); } unique_ptr CHC::createSymbolicBlock(smt::SortPointer _sort, string const& _name) @@ -536,7 +552,7 @@ unique_ptr CHC::createSymbolicBlock(smt::SortPoin _name, m_context ); - m_interface->registerRelation(block->currentValue()); + m_interface->registerRelation(block->currentFunctionValue()); return block; } @@ -568,25 +584,24 @@ smt::Expression CHC::error() smt::Expression CHC::error(unsigned _idx) { - return m_errorPredicate->valueAtIndex(_idx)({}); + return m_errorPredicate->functionValueAtIndex(_idx)({}); } -void CHC::createBlock(ASTNode const* _node, string const& _prefix) +unique_ptr CHC::createBlock(ASTNode const* _node, string const& _prefix) { - if (m_predicates.count(_node)) - { - m_predicates.at(_node)->increaseIndex(); - m_interface->registerRelation(m_predicates.at(_node)->currentValue()); - } - else - m_predicates[_node] = createSymbolicBlock(sort(_node), _prefix + predicateName(_node)); + return createSymbolicBlock(sort(_node), + "block_" + + uniquePrefix() + + "_" + + _prefix + + predicateName(_node)); } void CHC::createErrorBlock() { solAssert(m_errorPredicate, ""); m_errorPredicate->increaseIndex(); - m_interface->registerRelation(m_errorPredicate->currentValue()); + m_interface->registerRelation(m_errorPredicate->currentFunctionValue()); } void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints) @@ -646,13 +661,20 @@ string CHC::predicateName(ASTNode const* _node) return prefix + to_string(_node->id()); } -smt::Expression CHC::predicate(ASTNode const* _node) +smt::Expression CHC::predicate(smt::SymbolicFunctionVariable const& _block) { - if (dynamic_cast(_node)) - return (*m_predicates.at(_node))(currentFunctionVariables()); - return (*m_predicates.at(_node))(currentBlockVariables()); + return _block(currentBlockVariables()); } +smt::Expression CHC::predicate( + smt::SymbolicFunctionVariable const& _block, + vector const& _arguments +) +{ + return _block(_arguments); +} + + void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) { m_interface->addRule(_rule, _ruleName); @@ -680,3 +702,8 @@ bool CHC::query(smt::Expression const& _query, langutil::SourceLocation const& _ } return false; } + +string CHC::uniquePrefix() +{ + return to_string(m_blockCounter++); +} diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 5b40b4c8e..a9fb9f2b7 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -44,12 +44,21 @@ namespace solidity class CHC: public SMTEncoder { public: - CHC(smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter); + CHC( + smt::EncodingContext& _context, + langutil::ErrorReporter& _errorReporter, + std::map const& _smtlib2Responses + ); void analyze(SourceUnit const& _sources); std::set const& safeAssertions() const { return m_safeAssertions; } + /// This is used if the Horn solver is not directly linked into this binary. + /// @returns a list of inputs to the Horn solver that were not part of the argument to + /// the constructor. + std::vector unhandledQueries() const; + private: /// Visitor functions. //@{ @@ -66,12 +75,6 @@ private: void visitAssert(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall); - void visitLoop( - BreakableStatement const& _loop, - Expression const* _condition, - Statement const& _body, - ASTNode const* _postLoop - ); //@} /// Helpers. @@ -80,8 +83,7 @@ private: void eraseKnowledge(); bool shouldVisit(ContractDefinition const& _contract) const; bool shouldVisit(FunctionDefinition const& _function) const; - void pushBlock(ASTNode const* _node); - void popBlock(); + void setCurrentBlock(smt::SymbolicFunctionVariable const& _block); //@} /// Sort helpers. @@ -105,9 +107,8 @@ private: smt::Expression error(); smt::Expression error(unsigned _idx); - /// Creates a block for the given _node or increases its SSA index - /// if the block already exists which in practice creates a new function. - void createBlock(ASTNode const* _node, std::string const& _prefix = ""); + /// Creates a block for the given _node. + std::unique_ptr createBlock(ASTNode const* _node, std::string const& _prefix = ""); /// Creates a new error block to be used by an assertion. /// Also registers the predicate. @@ -129,7 +130,9 @@ private: /// @returns the predicate name for a given node. std::string predicateName(ASTNode const* _node); /// @returns a predicate application over the current scoped variables. - smt::Expression predicate(ASTNode const* _node); + smt::Expression predicate(smt::SymbolicFunctionVariable const& _block); + /// @returns a predicate application over @param _arguments. + smt::Expression predicate(smt::SymbolicFunctionVariable const& _block, std::vector const& _arguments); //@} /// Solver related. @@ -140,22 +143,26 @@ private: bool query(smt::Expression const& _query, langutil::SourceLocation const& _location); //@} + /// Misc. + //@{ + /// Returns a prefix to be used in a new unique block name + /// and increases the block counter. + std::string uniquePrefix(); + //@} + /// Predicates. //@{ /// Constructor predicate. /// Default constructor sets state vars to 0. - std::unique_ptr m_constructorPredicate; + std::unique_ptr m_constructorPredicate; /// Artificial Interface predicate. /// Single entry block for all functions. - std::unique_ptr m_interfacePredicate; + std::unique_ptr m_interfacePredicate; /// Artificial Error predicate. /// Single error block for all assertions. - std::unique_ptr m_errorPredicate; - - /// Maps AST nodes to their predicates. - std::unordered_map> m_predicates; + std::unique_ptr m_errorPredicate; //@} /// Variables. @@ -166,9 +173,6 @@ private: /// State variables. /// Used to create all predicates. std::vector m_stateVariables; - - /// Input sorts for AST nodes. - std::map m_nodeSorts; //@} /// Verification targets. @@ -183,21 +187,19 @@ private: //@{ FunctionDefinition const* m_currentFunction = nullptr; - /// Number of basic blocks created for the body of the current function. - unsigned m_functionBlocks = 0; - /// The current control flow path. - std::vector m_path; + /// The current block. + smt::Expression m_currentBlock = smt::Expression(true); + + /// Counter to generate unique block names. + unsigned m_blockCounter = 0; + /// Whether a function call was seen in the current scope. bool m_unknownFunctionCallSeen = false; - /// Whether a break statement was seen in the current scope. - bool m_breakSeen = false; - /// Whether a continue statement was seen in the current scope. - bool m_continueSeen = false; /// Block where a loop break should go to. - ASTNode const* m_breakDest; + smt::SymbolicFunctionVariable const* m_breakDest = nullptr; /// Block where a loop continue should go to. - ASTNode const* m_continueDest; + smt::SymbolicFunctionVariable const* m_continueDest = nullptr; //@} /// CHC solver. diff --git a/libsolidity/formal/CHCSmtLib2Interface.cpp b/libsolidity/formal/CHCSmtLib2Interface.cpp new file mode 100644 index 000000000..fb424d782 --- /dev/null +++ b/libsolidity/formal/CHCSmtLib2Interface.cpp @@ -0,0 +1,160 @@ +/* + 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 . +*/ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::smt; + +CHCSmtLib2Interface::CHCSmtLib2Interface(map const& _queryResponses): + m_smtlib2(make_shared(_queryResponses)), + m_queryResponses(_queryResponses) +{ + reset(); +} + +void CHCSmtLib2Interface::reset() +{ + m_accumulatedOutput.clear(); + m_variables.clear(); +} + +void CHCSmtLib2Interface::registerRelation(smt::Expression const& _expr) +{ + solAssert(_expr.sort, ""); + solAssert(_expr.sort->kind == smt::Kind::Function, ""); + if (!m_variables.count(_expr.name)) + { + auto fSort = dynamic_pointer_cast(_expr.sort); + string domain = m_smtlib2->toSmtLibSort(fSort->domain); + // Relations are predicates which have implicit codomain Bool. + m_variables.insert(_expr.name); + write( + "(declare-rel |" + + _expr.name + + "| " + + domain + + ")" + ); + } +} + +void CHCSmtLib2Interface::addRule(smt::Expression const& _expr, std::string const& _name) +{ + write( + "(rule (! " + + m_smtlib2->toSExpr(_expr) + + " :named " + + _name + + "))" + ); +} + +pair> CHCSmtLib2Interface::query(smt::Expression const& _block) +{ + string accumulated{}; + swap(m_accumulatedOutput, accumulated); + for (auto const& var: m_smtlib2->variables()) + declareVariable(var.first, var.second); + m_accumulatedOutput += accumulated; + + string response = querySolver( + m_accumulatedOutput + + "\n(query " + _block.name + " :print-certificate true)" + ); + + CheckResult result; + // TODO proper parsing + if (boost::starts_with(response, "sat\n")) + result = CheckResult::SATISFIABLE; + else if (boost::starts_with(response, "unsat\n")) + result = CheckResult::UNSATISFIABLE; + else if (boost::starts_with(response, "unknown\n")) + result = CheckResult::UNKNOWN; + else + result = CheckResult::ERROR; + + // TODO collect invariants or counterexamples. + return make_pair(result, vector{}); +} + +void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort) +{ + solAssert(_sort, ""); + if (_sort->kind == Kind::Function) + declareFunction(_name, _sort); + else if (!m_variables.count(_name)) + { + m_variables.insert(_name); + write("(declare-var |" + _name + "| " + m_smtlib2->toSmtLibSort(*_sort) + ')'); + } +} + +void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort) +{ + solAssert(_sort, ""); + solAssert(_sort->kind == smt::Kind::Function, ""); + // TODO Use domain and codomain as key as well + if (!m_variables.count(_name)) + { + auto fSort = dynamic_pointer_cast(_sort); + solAssert(fSort->codomain, ""); + string domain = m_smtlib2->toSmtLibSort(fSort->domain); + string codomain = m_smtlib2->toSmtLibSort(*fSort->codomain); + m_variables.insert(_name); + write( + "(declare-fun |" + + _name + + "| " + + domain + + " " + + codomain + + ")" + ); + } +} + +void CHCSmtLib2Interface::write(string _data) +{ + m_accumulatedOutput += move(_data) + "\n"; +} + +string CHCSmtLib2Interface::querySolver(string const& _input) +{ + h256 inputHash = dev::keccak256(_input); + if (m_queryResponses.count(inputHash)) + return m_queryResponses.at(inputHash); + else + { + m_unhandledQueries.push_back(_input); + return "unknown\n"; + } +} diff --git a/libsolidity/formal/CHCSmtLib2Interface.h b/libsolidity/formal/CHCSmtLib2Interface.h new file mode 100644 index 000000000..22bbba859 --- /dev/null +++ b/libsolidity/formal/CHCSmtLib2Interface.h @@ -0,0 +1,75 @@ +/* + 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 . +*/ + +/** + * Interface for solving Horn systems via smtlib2. + */ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ +namespace smt +{ + +class CHCSmtLib2Interface: public CHCSolverInterface +{ +public: + explicit CHCSmtLib2Interface(std::map const& _queryResponses); + + void reset(); + + void registerRelation(Expression const& _expr) override; + + void addRule(Expression const& _expr, std::string const& _name) override; + + std::pair> query(Expression const& _expr) override; + + void declareVariable(std::string const& _name, SortPointer const& _sort) override; + + std::vector unhandledQueries() const { return m_unhandledQueries; } + + std::shared_ptr smtlib2Interface() { return m_smtlib2; } + +private: + void declareFunction(std::string const& _name, SortPointer const& _sort); + + void write(std::string _data); + + /// Communicates with the solver via the callback. Throws SMTSolverError on error. + std::string querySolver(std::string const& _input); + + /// Used to access toSmtLibSort, SExpr, and handle variables. + /// Needs to be a shared_ptr since it's also passed to EncodingContext. + std::shared_ptr m_smtlib2; + + std::string m_accumulatedOutput; + std::set m_variables; + + std::map const& m_queryResponses; + std::vector m_unhandledQueries; +}; + +} +} +} diff --git a/libsolidity/formal/CHCSolverInterface.h b/libsolidity/formal/CHCSolverInterface.h index 091aa3d7c..0862adaff 100644 --- a/libsolidity/formal/CHCSolverInterface.h +++ b/libsolidity/formal/CHCSolverInterface.h @@ -35,7 +35,7 @@ class CHCSolverInterface public: virtual ~CHCSolverInterface() = default; - virtual void declareVariable(std::string const& _name, Sort const& _sort) = 0; + virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0; /// Takes a function declaration as a relation. virtual void registerRelation(Expression const& _expr) = 0; diff --git a/libsolidity/formal/CVC4Interface.cpp b/libsolidity/formal/CVC4Interface.cpp index dfe0cb913..377df32d2 100644 --- a/libsolidity/formal/CVC4Interface.cpp +++ b/libsolidity/formal/CVC4Interface.cpp @@ -48,10 +48,10 @@ void CVC4Interface::pop() m_solver.pop(); } -void CVC4Interface::declareVariable(string const& _name, Sort const& _sort) +void CVC4Interface::declareVariable(string const& _name, SortPointer const& _sort) { - if (!m_variables.count(_name)) - m_variables.insert({_name, m_context.mkVar(_name.c_str(), cvc4Sort(_sort))}); + solAssert(_sort, ""); + m_variables[_name] = m_context.mkVar(_name.c_str(), cvc4Sort(*_sort)); } void CVC4Interface::addAssertion(Expression const& _expr) diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h index 897923647..28596bdcf 100644 --- a/libsolidity/formal/CVC4Interface.h +++ b/libsolidity/formal/CVC4Interface.h @@ -50,7 +50,7 @@ public: void push() override; void pop() override; - void declareVariable(std::string const&, Sort const&) override; + void declareVariable(std::string const&, SortPointer const&) override; void addAssertion(Expression const& _expr) override; std::pair> check(std::vector const& _expressionsToEvaluate) override; diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index 265d0f991..c061c8416 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -24,7 +24,7 @@ using namespace dev::solidity; ModelChecker::ModelChecker(ErrorReporter& _errorReporter, map const& _smtlib2Responses): m_bmc(m_context, _errorReporter, _smtlib2Responses), - m_chc(m_context, _errorReporter), + m_chc(m_context, _errorReporter, _smtlib2Responses), m_context() { } @@ -40,5 +40,5 @@ void ModelChecker::analyze(SourceUnit const& _source) vector ModelChecker::unhandledQueries() { - return m_bmc.unhandledQueries(); + return m_bmc.unhandledQueries() + m_chc.unhandledQueries(); } diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 8f4b369da..98d6519fb 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -36,12 +36,37 @@ SMTEncoder::SMTEncoder(smt::EncodingContext& _context): bool SMTEncoder::visit(ContractDefinition const& _contract) { - solAssert(m_currentContract == nullptr, ""); - m_currentContract = &_contract; + solAssert(m_currentContract, ""); - initializeStateVariables(_contract); + for (auto const& node: _contract.subNodes()) + if (!dynamic_pointer_cast(node)) + node->accept(*this); - return true; + vector resolvedFunctions = _contract.definedFunctions(); + for (auto const& base: _contract.annotation().linearizedBaseContracts) + for (auto const& baseFunction: base->definedFunctions()) + { + if (baseFunction->isConstructor()) + continue; + bool overridden = false; + for (auto const& function: resolvedFunctions) + if ( + function->name() == baseFunction->name() && + FunctionType(*function).asCallableFunction(false)-> + hasEqualParameterTypes(*FunctionType(*baseFunction).asCallableFunction(false)) + ) + { + overridden = true; + break; + } + if (!overridden) + resolvedFunctions.push_back(baseFunction); + } + + for (auto const& function: resolvedFunctions) + function->accept(*this); + + return false; } void SMTEncoder::endVisit(ContractDefinition const& _contract) @@ -110,9 +135,13 @@ void SMTEncoder::visitFunctionOrModifier() *modifierInvocation->name()->annotation().referencedDeclaration ); vector modifierArgsExpr; - if (modifierInvocation->arguments()) - for (auto arg: *modifierInvocation->arguments()) - modifierArgsExpr.push_back(expr(*arg)); + if (auto const* arguments = modifierInvocation->arguments()) + { + auto const& modifierParams = modifierDef.parameters(); + solAssert(modifierParams.size() == arguments->size(), ""); + for (unsigned i = 0; i < arguments->size(); ++i) + modifierArgsExpr.push_back(expr(*arguments->at(i), modifierParams.at(i)->type())); + } initializeFunctionCallParameters(modifierDef, modifierArgsExpr); pushCallStack({&modifierDef, modifierInvocation.get()}); modifierDef.body().accept(*this); @@ -512,6 +541,14 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) } } +void SMTEncoder::initContract(ContractDefinition const& _contract) +{ + solAssert(m_currentContract == nullptr, ""); + m_currentContract = &_contract; + + initializeStateVariables(_contract); +} + void SMTEncoder::initFunction(FunctionDefinition const& _function) { solAssert(m_callStack.empty(), ""); @@ -561,10 +598,10 @@ void SMTEncoder::endVisit(Identifier const& _identifier) { // Will be translated as part of the node that requested the lvalue. } - else if (_identifier.annotation().type->category() == Type::Category::Function) - visitFunctionIdentifier(_identifier); else if (auto decl = identifierToVariable(_identifier)) defineExpr(_identifier, currentValue(*decl)); + else if (_identifier.annotation().type->category() == Type::Category::Function) + visitFunctionIdentifier(_identifier); else if (_identifier.name() == "now") defineGlobalVariable(_identifier.name(), _identifier); else if (_identifier.name() == "this") @@ -665,7 +702,7 @@ void SMTEncoder::endVisit(Return const& _return) } } else if (returnParams.size() == 1) - m_context.addAssertion(expr(*_return.expression()) == m_context.newValue(*returnParams.front())); + m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front())); } } @@ -733,6 +770,15 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) auto varDecl = identifierToVariable(*id); solAssert(varDecl, ""); array = m_context.variable(*varDecl); + + if (varDecl->type()->category() == Type::Category::FixedBytes) + { + m_errorReporter.warning( + _indexAccess.location(), + "Assertion checker does not yet support index accessing fixed bytes." + ); + return; + } } else if (auto const& innerAccess = dynamic_cast(&_indexAccess.baseExpression())) { @@ -774,78 +820,79 @@ void SMTEncoder::arrayAssignment() void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression const& _rightHandSide) { - auto const& indexAccess = dynamic_cast(_expr); - if (auto const& id = dynamic_cast(&indexAccess.baseExpression())) + auto toStore = _rightHandSide; + auto indexAccess = dynamic_cast(&_expr); + solAssert(indexAccess, ""); + while (true) { - auto varDecl = identifierToVariable(*id); - solAssert(varDecl, ""); - - if (varDecl->hasReferenceOrMappingType()) - m_context.resetVariables([&](VariableDeclaration const& _var) { - if (_var == *varDecl) - return false; - - // If both are state variables no need to clear knowledge. - if (_var.isStateVariable() && varDecl->isStateVariable()) - return false; - - TypePointer prefix = _var.type(); - TypePointer originalType = typeWithoutPointer(varDecl->type()); - while ( - prefix->category() == Type::Category::Mapping || - prefix->category() == Type::Category::Array - ) - { - if (*originalType == *typeWithoutPointer(prefix)) - return true; - if (prefix->category() == Type::Category::Mapping) - { - auto mapPrefix = dynamic_cast(prefix); - solAssert(mapPrefix, ""); - prefix = mapPrefix->valueType(); - } - else - { - auto arrayPrefix = dynamic_cast(prefix); - solAssert(arrayPrefix, ""); - prefix = arrayPrefix->baseType(); - } - } - return false; - }); - - smt::Expression store = smt::Expression::store( - m_context.variable(*varDecl)->currentValue(), - expr(*indexAccess.indexExpression()), - _rightHandSide - ); - m_context.addAssertion(m_context.newValue(*varDecl) == store); - // Update the SMT select value after the assignment, - // necessary for sound models. - defineExpr(indexAccess, smt::Expression::select( - m_context.variable(*varDecl)->currentValue(), - expr(*indexAccess.indexExpression()) - )); - } - else if (dynamic_cast(&indexAccess.baseExpression())) - { - auto identifier = dynamic_cast(leftmostBase(indexAccess)); - if (identifier) + if (auto const& id = dynamic_cast(&indexAccess->baseExpression())) { - auto varDecl = identifierToVariable(*identifier); - m_context.newValue(*varDecl); - } + auto varDecl = identifierToVariable(*id); + solAssert(varDecl, ""); - m_errorReporter.warning( - indexAccess.location(), - "Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays." - ); + if (varDecl->hasReferenceOrMappingType()) + m_context.resetVariables([&](VariableDeclaration const& _var) { + if (_var == *varDecl) + return false; + + // If both are state variables no need to clear knowledge. + if (_var.isStateVariable() && varDecl->isStateVariable()) + return false; + + TypePointer prefix = _var.type(); + TypePointer originalType = typeWithoutPointer(varDecl->type()); + while ( + prefix->category() == Type::Category::Mapping || + prefix->category() == Type::Category::Array + ) + { + if (*originalType == *typeWithoutPointer(prefix)) + return true; + if (prefix->category() == Type::Category::Mapping) + { + auto mapPrefix = dynamic_cast(prefix); + solAssert(mapPrefix, ""); + prefix = mapPrefix->valueType(); + } + else + { + auto arrayPrefix = dynamic_cast(prefix); + solAssert(arrayPrefix, ""); + prefix = arrayPrefix->baseType(); + } + } + return false; + }); + + smt::Expression store = smt::Expression::store( + m_context.variable(*varDecl)->currentValue(), + expr(*indexAccess->indexExpression()), + toStore + ); + m_context.addAssertion(m_context.newValue(*varDecl) == store); + // Update the SMT select value after the assignment, + // necessary for sound models. + defineExpr(*indexAccess, smt::Expression::select( + m_context.variable(*varDecl)->currentValue(), + expr(*indexAccess->indexExpression()) + )); + + break; + } + else if (auto base = dynamic_cast(&indexAccess->baseExpression())) + { + toStore = smt::Expression::store(expr(*base), expr(*indexAccess->indexExpression()), toStore); + indexAccess = base; + } + else + { + m_errorReporter.warning( + _expr.location(), + "Assertion checker does not yet implement this expression." + ); + break; + } } - else - m_errorReporter.warning( - _expr.location(), - "Assertion checker does not yet implement this expression." - ); } void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _expr, bool _increaseIndex) @@ -1300,7 +1347,7 @@ void SMTEncoder::createExpr(Expression const& _e) void SMTEncoder::defineExpr(Expression const& _e, smt::Expression _value) { createExpr(_e); - solAssert(smt::smtKind(_e.annotation().type->category()) != smt::Kind::Function, "Equality operator applied to type that is not fully supported"); + solAssert(_value.sort->kind != smt::Kind::Function, "Equality operator applied to type that is not fully supported"); m_context.addAssertion(expr(_e) == _value); } diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 04984d090..37a7cea24 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -107,6 +107,7 @@ protected: void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); + void initContract(ContractDefinition const& _contract); void initFunction(FunctionDefinition const& _function); void visitAssert(FunctionCall const& _funCall); void visitRequire(FunctionCall const& _funCall); diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index c0d37431e..3c0f38601 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -60,27 +60,29 @@ void SMTLib2Interface::pop() m_accumulatedOutput.pop_back(); } -void SMTLib2Interface::declareVariable(string const& _name, Sort const& _sort) +void SMTLib2Interface::declareVariable(string const& _name, SortPointer const& _sort) { - if (_sort.kind == Kind::Function) + solAssert(_sort, ""); + if (_sort->kind == Kind::Function) declareFunction(_name, _sort); else if (!m_variables.count(_name)) { - m_variables.insert(_name); - write("(declare-fun |" + _name + "| () " + toSmtLibSort(_sort) + ')'); + m_variables.emplace(_name, _sort); + write("(declare-fun |" + _name + "| () " + toSmtLibSort(*_sort) + ')'); } } -void SMTLib2Interface::declareFunction(string const& _name, Sort const& _sort) +void SMTLib2Interface::declareFunction(string const& _name, SortPointer const& _sort) { - solAssert(_sort.kind == smt::Kind::Function, ""); + solAssert(_sort, ""); + solAssert(_sort->kind == smt::Kind::Function, ""); // TODO Use domain and codomain as key as well if (!m_variables.count(_name)) { - FunctionSort fSort = dynamic_cast(_sort); - string domain = toSmtLibSort(fSort.domain); - string codomain = toSmtLibSort(*fSort.codomain); - m_variables.insert(_name); + auto const& fSort = dynamic_pointer_cast(_sort); + string domain = toSmtLibSort(fSort->domain); + string codomain = toSmtLibSort(*fSort->codomain); + m_variables.emplace(_name, _sort); write( "(declare-fun |" + _name + @@ -159,6 +161,7 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort) case Kind::Array: { auto const& arraySort = dynamic_cast(_sort); + solAssert(arraySort.domain && arraySort.range, ""); return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')'; } default: diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index a39c861c6..21c11f80d 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -48,20 +48,23 @@ public: void push() override; void pop() override; - void declareVariable(std::string const&, Sort const&) override; + void declareVariable(std::string const&, SortPointer const&) override; void addAssertion(smt::Expression const& _expr) override; std::pair> check(std::vector const& _expressionsToEvaluate) override; std::vector unhandledQueries() override { return m_unhandledQueries; } -private: - void declareFunction(std::string const&, Sort const&); - + // Used by CHCSmtLib2Interface std::string toSExpr(smt::Expression const& _expr); std::string toSmtLibSort(Sort const& _sort); std::string toSmtLibSort(std::vector const& _sort); + std::map variables() { return m_variables; } + +private: + void declareFunction(std::string const& _name, SortPointer const& _sort); + void write(std::string _data); std::string checkSatAndGetValuesCommand(std::vector const& _expressionsToEvaluate); @@ -71,7 +74,7 @@ private: std::string querySolver(std::string const& _input); std::vector m_accumulatedOutput; - std::set m_variables; + std::map m_variables; std::map const& m_queryResponses; std::vector m_unhandledQueries; diff --git a/libsolidity/formal/SMTPortfolio.cpp b/libsolidity/formal/SMTPortfolio.cpp index 73c194a01..d47c01f31 100644 --- a/libsolidity/formal/SMTPortfolio.cpp +++ b/libsolidity/formal/SMTPortfolio.cpp @@ -59,8 +59,9 @@ void SMTPortfolio::pop() s->pop(); } -void SMTPortfolio::declareVariable(string const& _name, Sort const& _sort) +void SMTPortfolio::declareVariable(string const& _name, SortPointer const& _sort) { + solAssert(_sort, ""); for (auto const& s: m_solvers) s->declareVariable(_name, _sort); } diff --git a/libsolidity/formal/SMTPortfolio.h b/libsolidity/formal/SMTPortfolio.h index ded7985f9..d922affd2 100644 --- a/libsolidity/formal/SMTPortfolio.h +++ b/libsolidity/formal/SMTPortfolio.h @@ -49,7 +49,7 @@ public: void push() override; void pop() override; - void declareVariable(std::string const&, Sort const&) override; + void declareVariable(std::string const&, SortPointer const&) override; void addAssertion(smt::Expression const& _expr) override; diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 0004e1726..2889af25a 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -338,13 +338,13 @@ public: virtual void push() = 0; virtual void pop() = 0; - virtual void declareVariable(std::string const& _name, Sort const& _sort) = 0; - Expression newVariable(std::string _name, SortPointer _sort) + virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0; + Expression newVariable(std::string _name, SortPointer const& _sort) { // Subclasses should do something here solAssert(_sort, ""); - declareVariable(_name, *_sort); - return Expression(std::move(_name), {}, std::move(_sort)); + declareVariable(_name, _sort); + return Expression(std::move(_name), {}, _sort); } virtual void addAssertion(Expression const& _expr) = 0; diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index ab23b8712..591158a25 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -19,6 +19,7 @@ #include #include +#include #include using namespace std; @@ -139,7 +140,28 @@ pair> newSymbolicVariable( else if (isBool(_type.category())) var = make_shared(type, _uniqueName, _context); else if (isFunction(_type.category())) - var = make_shared(type, _uniqueName, _context); + { + auto const& fType = dynamic_cast(type); + auto const& paramsIn = fType->parameterTypes(); + auto const& paramsOut = fType->returnParameterTypes(); + auto findFunctionParam = [&](auto&& params) { + return find_if( + begin(params), + end(params), + [&](TypePointer _paramType) { return _paramType->category() == solidity::Type::Category::Function; } + ); + }; + if ( + findFunctionParam(paramsIn) != end(paramsIn) || + findFunctionParam(paramsOut) != end(paramsOut) + ) + { + abstract = true; + var = make_shared(TypeProvider::uint256(), type, _uniqueName, _context); + } + else + var = make_shared(type, _uniqueName, _context); + } else if (isInteger(_type.category())) var = make_shared(type, type, _uniqueName, _context); else if (isFixedBytes(_type.category())) diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 3190cc6c6..3a814ec7d 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -19,7 +19,6 @@ #include #include -#include using namespace std; using namespace dev; @@ -153,16 +152,38 @@ SymbolicFunctionVariable::SymbolicFunctionVariable( solAssert(m_sort->kind == Kind::Function, ""); } -void SymbolicFunctionVariable::resetDeclaration() +Expression SymbolicFunctionVariable::currentValue(solidity::TypePointer const& _targetType) const { - m_declaration = m_context.newVariable(currentName(), m_sort); + return m_abstract.currentValue(_targetType); +} + +Expression SymbolicFunctionVariable::currentFunctionValue() const +{ + return m_declaration; +} + +Expression SymbolicFunctionVariable::valueAtIndex(int _index) const +{ + return m_abstract.valueAtIndex(_index); +} + +Expression SymbolicFunctionVariable::functionValueAtIndex(int _index) const +{ + return SymbolicVariable::valueAtIndex(_index); +} + +Expression SymbolicFunctionVariable::resetIndex() +{ + SymbolicVariable::resetIndex(); + return m_abstract.resetIndex(); } Expression SymbolicFunctionVariable::increaseIndex() { ++(*m_ssa); resetDeclaration(); - return currentValue(); + m_abstract.increaseIndex(); + return m_abstract.currentValue(); } Expression SymbolicFunctionVariable::operator()(vector _arguments) const @@ -170,6 +191,11 @@ Expression SymbolicFunctionVariable::operator()(vector _arguments) c return m_declaration(_arguments); } +void SymbolicFunctionVariable::resetDeclaration() +{ + m_declaration = m_context.newVariable(currentName(), m_sort); +} + SymbolicMappingVariable::SymbolicMappingVariable( solidity::TypePointer _type, string _uniqueName, diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index c65a26326..9ae637490 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace dev @@ -138,7 +139,12 @@ public: }; /** - * Specialization of SymbolicVariable for FunctionType + * Specialization of SymbolicVariable for FunctionType. + * Besides containing a symbolic function declaration, + * it also has an integer used as abstraction. + * By default, the abstract representation is used when + * values are requested, and the function declaration is + * used when operator() is applied over arguments. */ class SymbolicFunctionVariable: public SymbolicVariable { @@ -154,8 +160,20 @@ public: EncodingContext& _context ); - Expression increaseIndex(); - Expression operator()(std::vector _arguments) const; + Expression currentValue(solidity::TypePointer const& _targetType = TypePointer{}) const override; + + // Explicit request the function declaration. + Expression currentFunctionValue() const; + + Expression valueAtIndex(int _index) const override; + + // Explicit request the function declaration. + Expression functionValueAtIndex(int _index) const; + + Expression resetIndex() override; + Expression increaseIndex() override; + + Expression operator()(std::vector _arguments) const override; private: /// Creates a new function declaration. @@ -163,6 +181,14 @@ private: /// Stores the current function declaration. Expression m_declaration; + + /// Abstract representation. + SymbolicIntVariable m_abstract{ + TypeProvider::uint256(), + TypeProvider::uint256(), + m_uniqueName + "_abstract", + m_context + }; }; /** diff --git a/libsolidity/formal/Z3CHCInterface.cpp b/libsolidity/formal/Z3CHCInterface.cpp index 100a1fb7d..87d0db0c4 100644 --- a/libsolidity/formal/Z3CHCInterface.cpp +++ b/libsolidity/formal/Z3CHCInterface.cpp @@ -47,8 +47,9 @@ Z3CHCInterface::Z3CHCInterface(): m_solver.set(p); } -void Z3CHCInterface::declareVariable(string const& _name, Sort const& _sort) +void Z3CHCInterface::declareVariable(string const& _name, SortPointer const& _sort) { + solAssert(_sort, ""); m_z3Interface->declareVariable(_name, _sort); } diff --git a/libsolidity/formal/Z3CHCInterface.h b/libsolidity/formal/Z3CHCInterface.h index a107ad424..3acab97c3 100644 --- a/libsolidity/formal/Z3CHCInterface.h +++ b/libsolidity/formal/Z3CHCInterface.h @@ -37,7 +37,7 @@ public: Z3CHCInterface(); /// Forwards variable declaration to Z3Interface. - void declareVariable(std::string const& _name, Sort const& _sort) override; + void declareVariable(std::string const& _name, SortPointer const& _sort) override; void registerRelation(Expression const& _expr) override; diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index e07bacdb7..aa20eb533 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -50,22 +50,25 @@ void Z3Interface::pop() m_solver.pop(); } -void Z3Interface::declareVariable(string const& _name, Sort const& _sort) +void Z3Interface::declareVariable(string const& _name, SortPointer const& _sort) { - if (_sort.kind == Kind::Function) - declareFunction(_name, _sort); - else if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.constant(_name.c_str(), z3Sort(_sort))}); + solAssert(_sort, ""); + if (_sort->kind == Kind::Function) + declareFunction(_name, *_sort); + else if (m_constants.count(_name)) + m_constants.at(_name) = m_context.constant(_name.c_str(), z3Sort(*_sort)); + else + m_constants.emplace(_name, m_context.constant(_name.c_str(), z3Sort(*_sort))); } void Z3Interface::declareFunction(string const& _name, Sort const& _sort) { solAssert(_sort.kind == smt::Kind::Function, ""); - if (!m_functions.count(_name)) - { - FunctionSort fSort = dynamic_cast(_sort); - m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain))}); - } + FunctionSort fSort = dynamic_cast(_sort); + if (m_functions.count(_name)) + m_functions.at(_name) = m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain)); + else + m_functions.emplace(_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain))); } void Z3Interface::addAssertion(Expression const& _expr) diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h index 171fdcaa5..38734dd00 100644 --- a/libsolidity/formal/Z3Interface.h +++ b/libsolidity/formal/Z3Interface.h @@ -38,7 +38,7 @@ public: void push() override; void pop() override; - void declareVariable(std::string const& _name, Sort const& _sort) override; + void declareVariable(std::string const& _name, SortPointer const& _sort) override; void addAssertion(Expression const& _expr) override; std::pair> check(std::vector const& _expressionsToEvaluate) override; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7df52ca23..57cae9374 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -95,7 +96,7 @@ CompilerStack::~CompilerStack() TypeProvider::reset(); } -boost::optional CompilerStack::parseRemapping(string const& _remapping) +std::optional CompilerStack::parseRemapping(string const& _remapping) { auto eq = find(_remapping.begin(), _remapping.end(), '='); if (eq == _remapping.end()) @@ -269,7 +270,7 @@ bool CompilerStack::analyze() noErrors = false; m_globalContext = make_shared(); - NameAndTypeResolver resolver(*m_globalContext, m_scopes, m_errorReporter); + NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_scopes, m_errorReporter); for (Source const* source: m_sourceOrder) if (!resolver.registerDeclarations(*source->ast)) return false; @@ -578,6 +579,14 @@ string const& CompilerStack::eWasm(string const& _contractName) const return contract(_contractName).eWasm; } +eth::LinkerObject const& CompilerStack::eWasmObject(string const& _contractName) const +{ + if (m_stackState != CompilationSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); + + return contract(_contractName).eWasmObject; +} + eth::LinkerObject const& CompilerStack::object(string const& _contractName) const { if (m_stackState != CompilationSuccessful) @@ -659,6 +668,28 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const return *_contract.abi; } +Json::Value const& CompilerStack::storageLayout(string const& _contractName) const +{ + if (m_stackState < AnalysisPerformed) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); + + return storageLayout(contract(_contractName)); +} + +Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const +{ + if (m_stackState < AnalysisPerformed) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); + + solAssert(_contract.contract, ""); + + // caches the result + if (!_contract.storageLayout) + _contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract))); + + return *_contract.storageLayout; +} + Json::Value const& CompilerStack::natspecUser(string const& _contractName) const { if (m_stackState < AnalysisPerformed) @@ -1047,7 +1078,9 @@ void CompilerStack::generateEWasm(ContractDefinition const& _contract) //cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl; // Turn into eWasm text representation. - compiledContract.eWasm = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm).assembly; + auto result = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm); + compiledContract.eWasm = std::move(result.assembly); + compiledContract.eWasmObject = std::move(*result.bytecode); } CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 72a81b437..005ef911f 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -121,7 +121,7 @@ public: void reset(bool _keepSettings = false); // Parses a remapping of the format "context:prefix=target". - static boost::optional parseRemapping(std::string const& _remapping); + static std::optional parseRemapping(std::string const& _remapping); /// Sets path remappings. /// Must be set before parsing. @@ -232,9 +232,12 @@ public: /// @returns the optimized IR representation of a contract. std::string const& yulIROptimized(std::string const& _contractName) const; - /// @returns the eWasm (text) representation of a contract. + /// @returns the eWasm text representation of a contract. std::string const& eWasm(std::string const& _contractName) const; + /// @returns the eWasm representation of a contract. + eth::LinkerObject const& eWasmObject(std::string const& _contractName) const; + /// @returns the assembled object for a contract. eth::LinkerObject const& object(std::string const& _contractName) const; @@ -269,6 +272,10 @@ public: /// Prerequisite: Successful call to parse or compile. Json::Value const& contractABI(std::string const& _contractName) const; + /// @returns a JSON representing the storage layout of the contract. + /// Prerequisite: Successful call to parse or compile. + Json::Value const& storageLayout(std::string const& _contractName) const; + /// @returns a JSON representing the contract's user documentation. /// Prerequisite: Successful call to parse or compile. Json::Value const& natspecUser(std::string const& _contractName) const; @@ -312,9 +319,11 @@ private: eth::LinkerObject runtimeObject; ///< Runtime object. std::string yulIR; ///< Experimental Yul IR code. std::string yulIROptimized; ///< Optimized experimental Yul IR code. - std::string eWasm; ///< Experimental eWasm code (text representation). + std::string eWasm; ///< Experimental eWasm text representation + eth::LinkerObject eWasmObject; ///< Experimental eWasm code mutable std::unique_ptr metadata; ///< The metadata json that will be hashed into the chain. mutable std::unique_ptr abi; + mutable std::unique_ptr storageLayout; mutable std::unique_ptr userDocumentation; mutable std::unique_ptr devDocumentation; mutable std::unique_ptr sourceMapping; @@ -346,7 +355,7 @@ private: /// The IR is stored but otherwise unused. void generateIR(ContractDefinition const& _contract); - /// Generate eWasm text representation for a single contract. + /// Generate eWasm representation for a single contract. void generateEWasm(ContractDefinition const& _contract); /// Links all the known library addresses in the available objects. Any unknown @@ -378,6 +387,10 @@ private: /// This will generate the JSON object and store it in the Contract object if it is not present yet. Json::Value const& contractABI(Contract const&) const; + /// @returns the storage layout of the contract as a JSON object. + /// This will generate the JSON object and store it in the Contract object if it is not present yet. + Json::Value const& storageLayout(Contract const&) const; + /// @returns the Natspec User documentation as a JSON object. /// This will generate the JSON object and store it in the Contract object if it is not present yet. Json::Value const& natspecUser(Contract const&) const; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index ca27deac2..921e90231 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -31,8 +31,9 @@ #include #include -#include + #include +#include using namespace std; using namespace dev; @@ -317,7 +318,7 @@ Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _so return output; } -boost::optional checkKeys(Json::Value const& _input, set const& _keys, string const& _name) +std::optional checkKeys(Json::Value const& _input, set const& _keys, string const& _name) { if (!!_input && !_input.isObject()) return formatFatalError("JSONError", "\"" + _name + "\" must be an object"); @@ -326,46 +327,46 @@ boost::optional checkKeys(Json::Value const& _input, set co if (!_keys.count(member)) return formatFatalError("JSONError", "Unknown key \"" + member + "\""); - return boost::none; + return std::nullopt; } -boost::optional checkRootKeys(Json::Value const& _input) +std::optional checkRootKeys(Json::Value const& _input) { static set keys{"auxiliaryInput", "language", "settings", "sources"}; return checkKeys(_input, keys, "root"); } -boost::optional checkSourceKeys(Json::Value const& _input, string const& _name) +std::optional checkSourceKeys(Json::Value const& _input, string const& _name) { static set keys{"content", "keccak256", "urls"}; return checkKeys(_input, keys, "sources." + _name); } -boost::optional checkAuxiliaryInputKeys(Json::Value const& _input) +std::optional checkAuxiliaryInputKeys(Json::Value const& _input) { static set keys{"smtlib2responses"}; return checkKeys(_input, keys, "auxiliaryInput"); } -boost::optional checkSettingsKeys(Json::Value const& _input) +std::optional checkSettingsKeys(Json::Value const& _input) { static set keys{"parserErrorRecovery", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"}; return checkKeys(_input, keys, "settings"); } -boost::optional checkOptimizerKeys(Json::Value const& _input) +std::optional checkOptimizerKeys(Json::Value const& _input) { static set keys{"details", "enabled", "runs"}; return checkKeys(_input, keys, "settings.optimizer"); } -boost::optional checkOptimizerDetailsKeys(Json::Value const& _input) +std::optional checkOptimizerDetailsKeys(Json::Value const& _input) { static set keys{"peephole", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails"}; return checkKeys(_input, keys, "settings.optimizer.details"); } -boost::optional checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting) +std::optional checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting) { if (_details.isMember(_name)) { @@ -376,7 +377,7 @@ boost::optional checkOptimizerDetail(Json::Value const& _details, s return {}; } -boost::optional checkMetadataKeys(Json::Value const& _input) +std::optional checkMetadataKeys(Json::Value const& _input) { if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool()) return formatFatalError("JSONError", "\"settings.metadata.useLiteralContent\" must be Boolean"); @@ -384,7 +385,7 @@ boost::optional checkMetadataKeys(Json::Value const& _input) return checkKeys(_input, keys, "settings.metadata"); } -boost::optional checkOutputSelection(Json::Value const& _outputSelection) +std::optional checkOutputSelection(Json::Value const& _outputSelection) { if (!!_outputSelection && !_outputSelection.isObject()) return formatFatalError("JSONError", "\"settings.outputSelection\" must be an object"); @@ -426,7 +427,7 @@ boost::optional checkOutputSelection(Json::Value const& _outputSele } } - return boost::none; + return std::nullopt; } /// Validates the optimizer settings and returns them in a parsed object. /// On error returns the json-formatted error message. @@ -635,7 +636,7 @@ boost::variant StandardCompile { if (!settings["evmVersion"].isString()) return formatFatalError("JSONError", "evmVersion must be a string."); - boost::optional version = langutil::EVMVersion::fromString(settings["evmVersion"].asString()); + std::optional version = langutil::EVMVersion::fromString(settings["evmVersion"].asString()); if (!version) return formatFatalError("JSONError", "Invalid EVM version requested."); ret.evmVersion = *version; @@ -886,10 +887,12 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting string file = contractName.substr(0, colon); string name = contractName.substr(colon + 1); - // ABI, documentation and metadata + // ABI, storage layout, documentation and metadata Json::Value contractData(Json::objectValue); if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental)) contractData["abi"] = compilerStack.contractABI(contractName); + if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false)) + contractData["storageLayout"] = compilerStack.storageLayout(contractName); if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental)) contractData["metadata"] = compilerStack.metadata(contractName); if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental)) @@ -906,6 +909,8 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting // eWasm if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental)) contractData["ewasm"]["wast"] = compilerStack.eWasm(contractName); + if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wasm", wildcardMatchesExperimental)) + contractData["ewasm"]["wasm"] = compilerStack.eWasmObject(contractName).toHex(); // EVM Json::Value evmData(Json::objectValue); diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 9d0320abb..63ceb4f6b 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -24,7 +24,7 @@ #include -#include +#include #include namespace dev diff --git a/libsolidity/interface/StorageLayout.cpp b/libsolidity/interface/StorageLayout.cpp new file mode 100644 index 000000000..e987d4de2 --- /dev/null +++ b/libsolidity/interface/StorageLayout.cpp @@ -0,0 +1,119 @@ +/* + 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 . +*/ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +Json::Value StorageLayout::generate(ContractDefinition const& _contractDef) +{ + solAssert(!m_contract, ""); + m_contract = &_contractDef; + m_types.clear(); + + auto typeType = dynamic_cast(_contractDef.type()); + solAssert(typeType, ""); + auto contractType = dynamic_cast(typeType->actualType()); + solAssert(contractType, ""); + + Json::Value variables(Json::arrayValue); + for (auto [var, slot, offset]: contractType->stateVariables()) + variables.append(generate(*var, slot, offset)); + + Json::Value layout; + layout["storage"] = move(variables); + layout["types"] = move(m_types); + return layout; +} + +Json::Value StorageLayout::generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset) +{ + Json::Value varEntry; + TypePointer varType = _var.type(); + + varEntry["label"] = _var.name(); + varEntry["astId"] = int(_var.id()); + varEntry["contract"] = m_contract->fullyQualifiedName(); + varEntry["slot"] = _slot.str(); + varEntry["offset"] = _offset; + varEntry["type"] = typeKeyName(varType); + + generate(varType); + + return varEntry; +} + +void StorageLayout::generate(TypePointer _type) +{ + if (m_types.isMember(typeKeyName(_type))) + return; + + // Register it now to cut recursive visits. + Json::Value& typeInfo = m_types[typeKeyName(_type)]; + typeInfo["label"] = _type->toString(true); + typeInfo["numberOfBytes"] = u256(_type->storageBytes() * _type->storageSize()).str(); + + if (auto structType = dynamic_cast(_type)) + { + Json::Value members(Json::arrayValue); + auto const& structDef = structType->structDefinition(); + for (auto const& member: structDef.members()) + { + auto const& offsets = structType->storageOffsetsOfMember(member->name()); + members.append(generate(*member, offsets.first, offsets.second)); + } + typeInfo["members"] = move(members); + typeInfo["encoding"] = "inplace"; + } + else if (auto mappingType = dynamic_cast(_type)) + { + typeInfo["key"] = typeKeyName(mappingType->keyType()); + typeInfo["value"] = typeKeyName(mappingType->valueType()); + generate(mappingType->keyType()); + generate(mappingType->valueType()); + typeInfo["encoding"] = "mapping"; + } + else if (auto arrayType = dynamic_cast(_type)) + { + if (arrayType->isByteArray()) + typeInfo["encoding"] = "bytes"; + else + { + typeInfo["base"] = typeKeyName(arrayType->baseType()); + generate(arrayType->baseType()); + typeInfo["encoding"] = arrayType->isDynamicallySized() ? "dynamic_array" : "inplace"; + } + } + else + { + solAssert(_type->isValueType(), ""); + typeInfo["encoding"] = "inplace"; + } + + solAssert(typeInfo.isMember("encoding"), ""); +} + +string StorageLayout::typeKeyName(TypePointer _type) +{ + if (auto refType = dynamic_cast(_type)) + return TypeProvider::withLocationIfReference(refType->location(), _type)->richIdentifier(); + return _type->richIdentifier(); +} diff --git a/libsolidity/interface/StorageLayout.h b/libsolidity/interface/StorageLayout.h new file mode 100644 index 000000000..1f7b3c1fd --- /dev/null +++ b/libsolidity/interface/StorageLayout.h @@ -0,0 +1,58 @@ +/* + 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 . +*/ +/** + * Generates the storage layout of a contract. + */ + +#pragma once + +#include +#include + +#include + +namespace dev +{ +namespace solidity +{ + +class StorageLayout +{ +public: + /// Generates the storage layout of the contract + /// @param _contractDef The contract definition + /// @return A JSON representation of the contract's storage layout. + Json::Value generate(ContractDefinition const& _contractDef); + +private: + /// Generates the JSON information for a variable and its storage location. + Json::Value generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset); + + /// Generates the JSON information for @param _type + void generate(TypePointer _type); + + /// The key for the JSON object describing a type. + std::string typeKeyName(TypePointer _type); + + Json::Value m_types; + + /// Current analyzed contract + ContractDefinition const* m_contract = nullptr; +}; + +} +} diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 56a74f47d..f1145ee83 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -241,7 +241,7 @@ ASTPointer Parser::parseImportDirective() ContractDefinition::ContractKind Parser::parseContractKind() { ContractDefinition::ContractKind kind; - switch(m_scanner->currentToken()) + switch (m_scanner->currentToken()) { case Token::Interface: kind = ContractDefinition::ContractKind::Interface; @@ -388,7 +388,7 @@ StateMutability Parser::parseStateMutability() { StateMutability stateMutability(StateMutability::NonPayable); Token token = m_scanner->currentToken(); - switch(token) + switch (token) { case Token::Payable: stateMutability = StateMutability::Payable; @@ -874,7 +874,9 @@ ASTPointer Parser::parseTypeName(bool _allowVar) ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); m_scanner->next(); - auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable); + auto stateMutability = elemTypeName.token() == Token::Address + ? optional{StateMutability::NonPayable} + : nullopt; if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) { if (elemTypeName.token() == Token::Address) diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index facae9187..14337d323 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -31,11 +31,11 @@ #include #include -#include #include #include #include +#include namespace langutil { @@ -59,7 +59,7 @@ public: explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, langutil::ErrorReporter& _errorReporter, - boost::optional _errorTypeForLoose, + std::optional _errorTypeForLoose, Dialect const& _dialect, ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver(), std::set const& _dataNames = {} @@ -127,7 +127,7 @@ private: langutil::ErrorReporter& m_errorReporter; langutil::EVMVersion m_evmVersion; Dialect const& m_dialect; - boost::optional m_errorTypeForLoose; + std::optional m_errorTypeForLoose; /// Names of data objects to be referenced by builtin functions with literal arguments. std::set m_dataNames; ForLoop const* m_currentForLoop = nullptr; diff --git a/libyul/AsmScope.h b/libyul/AsmScope.h index f6c7fe59a..2c7af4ef1 100644 --- a/libyul/AsmScope.h +++ b/libyul/AsmScope.h @@ -27,10 +27,10 @@ #include #include -#include #include #include +#include namespace yul { diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 15a1267a5..87df95f37 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -117,7 +117,7 @@ bool AssemblyStack::analyzeParsed(Object& _object) AsmAnalyzer analyzer( *_object.analysisInfo, m_errorReporter, - boost::none, + std::nullopt, languageToDialect(m_language, m_evmVersion), {}, _object.dataNames() @@ -200,7 +200,10 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const Dialect const& dialect = languageToDialect(m_language, EVMVersion{}); MachineAssemblyObject object; - object.assembly = EWasmObjectCompiler::compile(*m_parserResult, dialect); + auto result = EWasmObjectCompiler::compile(*m_parserResult, dialect); + object.assembly = std::move(result.first); + object.bytecode = make_shared(); + object.bytecode->bytecode = std::move(result.second); return object; } } diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 0bffc5389..f71171597 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -50,6 +50,8 @@ add_library(yul backends/wasm/EWasmObjectCompiler.h backends/wasm/EWasmToText.cpp backends/wasm/EWasmToText.h + backends/wasm/BinaryTransform.cpp + backends/wasm/BinaryTransform.h backends/wasm/WasmDialect.cpp backends/wasm/WasmDialect.h backends/wasm/WordSizeTransform.cpp @@ -66,6 +68,10 @@ add_library(yul optimiser/CallGraphGenerator.h optimiser/CommonSubexpressionEliminator.cpp optimiser/CommonSubexpressionEliminator.h + optimiser/ConditionalSimplifier.cpp + optimiser/ConditionalSimplifier.h + optimiser/ConditionalUnsimplifier.cpp + optimiser/ConditionalUnsimplifier.h optimiser/ControlFlowSimplifier.cpp optimiser/ControlFlowSimplifier.h optimiser/DataFlowAnalyzer.cpp diff --git a/libyul/Utilities.cpp b/libyul/Utilities.cpp index a5a2859c8..a9958716f 100644 --- a/libyul/Utilities.cpp +++ b/libyul/Utilities.cpp @@ -113,7 +113,7 @@ u256 yul::valueOfBoolLiteral(Literal const& _literal) u256 yul::valueOfLiteral(Literal const& _literal) { - switch(_literal.kind) + switch (_literal.kind) { case LiteralKind::Number: return valueOfNumberLiteral(_literal); diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 9ca079f07..d3493b573 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -723,9 +723,7 @@ void CodeTransform::visitExpression(Expression const& _expression) void CodeTransform::visitStatements(vector const& _statements) { - // Workaround boost bug: - // https://www.boost.org/doc/libs/1_63_0/libs/optional/doc/html/boost_optional/tutorial/gotchas/false_positive_with__wmaybe_uninitialized.html - boost::optional jumpTarget = boost::make_optional(false, AbstractAssembly::LabelID()); + std::optional jumpTarget = std::nullopt; for (auto const& statement: _statements) { @@ -740,7 +738,7 @@ void CodeTransform::visitStatements(vector const& _statements) else if (!functionDefinition && jumpTarget) { m_assembly.appendLabel(*jumpTarget); - jumpTarget = boost::none; + jumpTarget = std::nullopt; } boost::apply_visitor(*this, statement); diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index 866322d2d..e44953807 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -28,8 +28,8 @@ #include #include -#include +#include #include namespace langutil diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index 1c47c8005..15b955d78 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -47,7 +47,7 @@ struct BuiltinContext struct BuiltinFunctionForEVM: BuiltinFunction { - boost::optional instruction; + std::optional instruction; /// Function to generate code for the given function call and append it to the abstract /// assembly. The fourth parameter is called to visit (and generate code for) the arguments /// from right to left. diff --git a/libyul/backends/wasm/BinaryTransform.cpp b/libyul/backends/wasm/BinaryTransform.cpp new file mode 100644 index 000000000..18f02ec02 --- /dev/null +++ b/libyul/backends/wasm/BinaryTransform.cpp @@ -0,0 +1,582 @@ +/* + 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 . +*/ +/** + * EWasm to binary encoder. + */ + +#include + +#include +#include + +#include + +using namespace std; +using namespace yul; +using namespace dev; +using namespace yul::wasm; + +namespace +{ + +bytes toBytes(uint8_t _b) +{ + return bytes(1, _b); +} + +enum class Section: uint8_t +{ + CUSTOM = 0x00, + TYPE = 0x01, + IMPORT = 0x02, + FUNCTION = 0x03, + MEMORY = 0x05, + GLOBAL = 0x06, + EXPORT = 0x07, + CODE = 0x0a +}; + +bytes toBytes(Section _s) +{ + return toBytes(uint8_t(_s)); +} + +enum class ValueType: uint8_t +{ + Void = 0x40, + Function = 0x60, + I64 = 0x7e, + I32 = 0x7f +}; + +bytes toBytes(ValueType _vt) +{ + return toBytes(uint8_t(_vt)); +} + +enum class Export: uint8_t +{ + Function = 0x0, + Memory = 0x2 +}; + +bytes toBytes(Export _export) +{ + return toBytes(uint8_t(_export)); +} + +enum class Opcode: uint8_t +{ + Unreachable = 0x00, + Nop = 0x01, + Block = 0x02, + Loop = 0x03, + If = 0x04, + Else = 0x05, + Try = 0x06, + Catch = 0x07, + Throw = 0x08, + Rethrow = 0x09, + BrOnExn = 0x0a, + End = 0x0b, + Br = 0x0c, + BrIf = 0x0d, + BrTable = 0x0e, + Return = 0x0f, + Call = 0x10, + CallIndirect = 0x11, + ReturnCall = 0x12, + ReturnCallIndirect = 0x13, + Drop = 0x1a, + Select = 0x1b, + LocalGet = 0x20, + LocalSet = 0x21, + LocalTee = 0x22, + GlobalGet = 0x23, + GlobalSet = 0x24, + I32Const = 0x41, + I64Const = 0x42, +}; + +bytes toBytes(Opcode _o) +{ + return toBytes(uint8_t(_o)); +} + +static std::map const builtins = { + {"i32.load", 0x28}, + {"i64.load", 0x29}, + {"i32.load8_s", 0x2c}, + {"i32.load8_u", 0x2d}, + {"i32.load16_s", 0x2e}, + {"i32.load16_u", 0x2f}, + {"i64.load8_s", 0x30}, + {"i64.load8_u", 0x31}, + {"i64.load16_s", 0x32}, + {"i64.load16_u", 0x33}, + {"i64.load32_s", 0x34}, + {"i64.load32_u", 0x35}, + {"i32.store", 0x36}, + {"i64.store", 0x37}, + {"i32.store8", 0x3a}, + {"i32.store16", 0x3b}, + {"i64.store8", 0x3c}, + {"i64.store16", 0x3d}, + {"i64.store32", 0x3e}, + {"memory.size", 0x3f}, + {"memory.grow", 0x40}, + {"i32.eqz", 0x45}, + {"i32.eq", 0x46}, + {"i32.ne", 0x47}, + {"i32.lt_s", 0x48}, + {"i32.lt_u", 0x49}, + {"i32.gt_s", 0x4a}, + {"i32.gt_u", 0x4b}, + {"i32.le_s", 0x4c}, + {"i32.le_u", 0x4d}, + {"i32.ge_s", 0x4e}, + {"i32.ge_u", 0x4f}, + {"i64.eqz", 0x50}, + {"i64.eq", 0x51}, + {"i64.ne", 0x52}, + {"i64.lt_s", 0x53}, + {"i64.lt_u", 0x54}, + {"i64.gt_s", 0x55}, + {"i64.gt_u", 0x56}, + {"i64.le_s", 0x57}, + {"i64.le_u", 0x58}, + {"i64.ge_s", 0x59}, + {"i64.ge_u", 0x5a}, + {"i32.clz", 0x67}, + {"i32.ctz", 0x68}, + {"i32.popcnt", 0x69}, + {"i32.add", 0x6a}, + {"i32.sub", 0x6b}, + {"i32.mul", 0x6c}, + {"i32.div_s", 0x6d}, + {"i32.div_u", 0x6e}, + {"i32.rem_s", 0x6f}, + {"i32.rem_u", 0x70}, + {"i32.and", 0x71}, + {"i32.or", 0x72}, + {"i32.xor", 0x73}, + {"i32.shl", 0x74}, + {"i32.shr_s", 0x75}, + {"i32.shr_u", 0x76}, + {"i32.rotl", 0x77}, + {"i32.rotr", 0x78}, + {"i64.clz", 0x79}, + {"i64.ctz", 0x7a}, + {"i64.popcnt", 0x7b}, + {"i64.add", 0x7c}, + {"i64.sub", 0x7d}, + {"i64.mul", 0x7e}, + {"i64.div_s", 0x7f}, + {"i64.div_u", 0x80}, + {"i64.rem_s", 0x81}, + {"i64.rem_u", 0x82}, + {"i64.and", 0x83}, + {"i64.or", 0x84}, + {"i64.xor", 0x85}, + {"i64.shl", 0x86}, + {"i64.shr_s", 0x87}, + {"i64.shr_u", 0x88}, + {"i64.rotl", 0x89}, + {"i64.rotr", 0x8a}, + {"i32.wrap_i64", 0xa7}, + {"i64.extend_i32_s", 0xac}, + {"i64.extend_i32_u", 0xad}, +}; + +bytes lebEncode(uint64_t _n) +{ + bytes encoded; + while (_n > 0x7f) + { + encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f))); + _n >>= 7; + } + encoded.emplace_back(_n); + return encoded; +} + +bytes lebEncodeSigned(int64_t _n) +{ + if (_n >= 0 && _n < 0x40) + return toBytes(uint8_t(uint64_t(_n) & 0xff)); + else if (-_n > 0 && -_n < 0x40) + return toBytes(uint8_t(uint64_t(_n + 0x80) & 0xff)); + else + return toBytes(uint8_t(0x80 | uint8_t(_n & 0x7f))) + lebEncodeSigned(_n / 0x80); +} + +bytes prefixSize(bytes _data) +{ + size_t size = _data.size(); + return lebEncode(size) + std::move(_data); +} + +bytes makeSection(Section _section, bytes _data) +{ + return toBytes(_section) + prefixSize(std::move(_data)); +} + +} + +bytes BinaryTransform::run(Module const& _module) +{ + BinaryTransform bt; + + for (size_t i = 0; i < _module.globals.size(); ++i) + bt.m_globals[_module.globals[i].variableName] = i; + + size_t funID = 0; + for (FunctionImport const& fun: _module.imports) + bt.m_functions[fun.internalName] = funID++; + for (FunctionDefinition const& fun: _module.functions) + bt.m_functions[fun.name] = funID++; + + bytes ret{0, 'a', 's', 'm'}; + // version + ret += bytes{1, 0, 0, 0}; + ret += bt.typeSection(_module.imports, _module.functions); + ret += bt.importSection(_module.imports); + ret += bt.functionSection(_module.functions); + ret += bt.memorySection(); + ret += bt.globalSection(); + ret += bt.exportSection(); + for (auto const& sub: _module.subModules) + { + // TODO should we prefix and / or shorten the name? + bytes data = BinaryTransform::run(sub.second); + size_t length = data.size(); + ret += bt.customSection(sub.first, std::move(data)); + bt.m_subModulePosAndSize[sub.first] = {ret.size() - length, length}; + } + ret += bt.codeSection(_module.functions); + return ret; +} + +bytes BinaryTransform::operator()(Literal const& _literal) +{ + return toBytes(Opcode::I64Const) + lebEncodeSigned(_literal.value); +} + +bytes BinaryTransform::operator()(StringLiteral const&) +{ + // TODO is this used? + yulAssert(false, "String literals not yet implemented"); +} + +bytes BinaryTransform::operator()(LocalVariable const& _variable) +{ + return toBytes(Opcode::LocalGet) + lebEncode(m_locals.at(_variable.name)); +} + +bytes BinaryTransform::operator()(GlobalVariable const& _variable) +{ + return toBytes(Opcode::GlobalGet) + lebEncode(m_globals.at(_variable.name)); +} + +bytes BinaryTransform::operator()(BuiltinCall const& _call) +{ + // We need to avoid visiting the arguments of `dataoffset` and `datasize` because + // they are references to object names that should not end up in the code. + if (_call.functionName == "dataoffset") + { + string name = boost::get(_call.arguments.at(0)).value; + return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first); + } + else if (_call.functionName == "datasize") + { + string name = boost::get(_call.arguments.at(0)).value; + return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second); + } + + bytes args = visit(_call.arguments); + + if (_call.functionName == "unreachable") + return toBytes(Opcode::Unreachable); + else + { + yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found"); + bytes ret = std::move(args) + toBytes(builtins.at(_call.functionName)); + if ( + _call.functionName.find(".load") != string::npos || + _call.functionName.find(".store") != string::npos + ) + // alignment and offset + ret += bytes{{3, 0}}; + return ret; + } +} + +bytes BinaryTransform::operator()(FunctionCall const& _call) +{ + return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functions.at(_call.functionName)); +} + +bytes BinaryTransform::operator()(LocalAssignment const& _assignment) +{ + return + boost::apply_visitor(*this, *_assignment.value) + + toBytes(Opcode::LocalSet) + + lebEncode(m_locals.at(_assignment.variableName)); +} + +bytes BinaryTransform::operator()(GlobalAssignment const& _assignment) +{ + return + boost::apply_visitor(*this, *_assignment.value) + + toBytes(Opcode::GlobalSet) + + lebEncode(m_globals.at(_assignment.variableName)); +} + +bytes BinaryTransform::operator()(If const& _if) +{ + bytes result = + boost::apply_visitor(*this, *_if.condition) + + toBytes(Opcode::If) + + toBytes(ValueType::Void); + + m_labels.push({}); + + result += visit(_if.statements); + if (_if.elseStatements) + result += toBytes(Opcode::Else) + visit(*_if.elseStatements); + + m_labels.pop(); + + result += toBytes(Opcode::End); + return result; +} + +bytes BinaryTransform::operator()(Loop const& _loop) +{ + bytes result = toBytes(Opcode::Loop) + toBytes(ValueType::Void); + + m_labels.push(_loop.labelName); + result += visit(_loop.statements); + m_labels.pop(); + + result += toBytes(Opcode::End); + return result; +} + +bytes BinaryTransform::operator()(Break const&) +{ + yulAssert(false, "br not yet implemented."); + // TODO the index is just the nesting depth. + return {}; +} + +bytes BinaryTransform::operator()(BreakIf const&) +{ + yulAssert(false, "br_if not yet implemented."); + // TODO the index is just the nesting depth. + return {}; +} + +bytes BinaryTransform::operator()(Block const& _block) +{ + return + toBytes(Opcode::Block) + + toBytes(ValueType::Void) + + visit(_block.statements) + + toBytes(Opcode::End); +} + +bytes BinaryTransform::operator()(FunctionDefinition const& _function) +{ + bytes ret; + + // This is a kind of run-length-encoding of local types. Has to be adapted once + // we have locals of different types. + ret += lebEncode(1); // number of locals groups + ret += lebEncode(_function.locals.size()); + ret += toBytes(ValueType::I64); + + m_locals.clear(); + size_t varIdx = 0; + for (size_t i = 0; i < _function.parameterNames.size(); ++i) + m_locals[_function.parameterNames[i]] = varIdx++; + for (size_t i = 0; i < _function.locals.size(); ++i) + m_locals[_function.locals[i].variableName] = varIdx++; + + ret += visit(_function.body); + ret += toBytes(Opcode::End); + + return prefixSize(std::move(ret)); +} + +BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import) +{ + return { + encodeTypes(_import.paramTypes), + encodeTypes(_import.returnType ? vector(1, *_import.returnType) : vector()) + }; +} + +BinaryTransform::Type BinaryTransform::typeOf(FunctionDefinition const& _funDef) +{ + + return { + encodeTypes(vector(_funDef.parameterNames.size(), "i64")), + encodeTypes(vector(_funDef.returns ? 1 : 0, "i64")) + }; +} + +uint8_t BinaryTransform::encodeType(string const& _typeName) +{ + if (_typeName == "i32") + return uint8_t(ValueType::I32); + else if (_typeName == "i64") + return uint8_t(ValueType::I64); + else + yulAssert(false, ""); + return 0; +} + +vector BinaryTransform::encodeTypes(vector const& _typeNames) +{ + vector result; + for (auto const& t: _typeNames) + result.emplace_back(encodeType(t)); + return result; +} + +bytes BinaryTransform::typeSection( + vector const& _imports, + vector const& _functions +) +{ + map> types; + for (auto const& import: _imports) + types[typeOf(import)].emplace_back(import.internalName); + for (auto const& fun: _functions) + types[typeOf(fun)].emplace_back(fun.name); + + bytes result; + size_t index = 0; + for (auto const& [type, funNames]: types) + { + for (string const& name: funNames) + m_functionTypes[name] = index; + result += toBytes(ValueType::Function); + result += lebEncode(type.first.size()) + type.first; + result += lebEncode(type.second.size()) + type.second; + + index++; + } + + return makeSection(Section::TYPE, lebEncode(index) + std::move(result)); +} + +bytes BinaryTransform::importSection( + vector const& _imports +) +{ + bytes result = lebEncode(_imports.size()); + for (FunctionImport const& import: _imports) + { + uint8_t importKind = 0; // function + result += + encodeName(import.module) + + encodeName(import.externalName) + + toBytes(importKind) + + lebEncode(m_functionTypes[import.internalName]); + } + return makeSection(Section::IMPORT, std::move(result)); +} + +bytes BinaryTransform::functionSection(vector const& _functions) +{ + bytes result = lebEncode(_functions.size()); + for (auto const& fun: _functions) + result += lebEncode(m_functionTypes.at(fun.name)); + return makeSection(Section::FUNCTION, std::move(result)); +} + +bytes BinaryTransform::memorySection() +{ + bytes result = lebEncode(1); + result.push_back(0); // flags + result.push_back(1); // initial + return makeSection(Section::MEMORY, std::move(result)); +} + +bytes BinaryTransform::globalSection() +{ + bytes result = lebEncode(m_globals.size()); + for (size_t i = 0; i < m_globals.size(); ++i) + result += + // mutable i64 + bytes{uint8_t(ValueType::I64), 1} + + toBytes(Opcode::I64Const) + + lebEncodeSigned(0) + + toBytes(Opcode::End); + + return makeSection(Section::GLOBAL, std::move(result)); +} + +bytes BinaryTransform::exportSection() +{ + bytes result = lebEncode(2); + result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0); + result += encodeName("main") + toBytes(Export::Function) + lebEncode(m_functions.at("main")); + return makeSection(Section::EXPORT, std::move(result)); +} + +bytes BinaryTransform::customSection(string const& _name, bytes _data) +{ + bytes result = encodeName(_name) + std::move(_data); + return makeSection(Section::CUSTOM, std::move(result)); +} + +bytes BinaryTransform::codeSection(vector const& _functions) +{ + bytes result = lebEncode(_functions.size()); + for (FunctionDefinition const& fun: _functions) + result += (*this)(fun); + return makeSection(Section::CODE, std::move(result)); +} + +bytes BinaryTransform::visit(vector const& _expressions) +{ + bytes result; + for (auto const& expr: _expressions) + result += boost::apply_visitor(*this, expr); + return result; +} + +bytes BinaryTransform::visitReversed(vector const& _expressions) +{ + bytes result; + for (auto const& expr: _expressions | boost::adaptors::reversed) + result += boost::apply_visitor(*this, expr); + return result; +} + +bytes BinaryTransform::encodeName(std::string const& _name) +{ + // UTF-8 is allowed here by the Wasm spec, but since all names here should stem from + // Solidity or Yul identifiers or similar, non-ascii characters ending up here + // is a very bad sign. + for (char c: _name) + yulAssert(uint8_t(c) <= 0x7f, "Non-ascii character found."); + return lebEncode(_name.size()) + asBytes(_name); +} diff --git a/libyul/backends/wasm/BinaryTransform.h b/libyul/backends/wasm/BinaryTransform.h new file mode 100644 index 000000000..35c611b21 --- /dev/null +++ b/libyul/backends/wasm/BinaryTransform.h @@ -0,0 +1,94 @@ +/* + 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 . +*/ +/** + * EWasm to binary encoder. + */ + +#pragma once + +#include + +#include + +#include +#include + +namespace yul +{ +namespace wasm +{ + +/** + * Web assembly to binary transform. + */ +class BinaryTransform: public boost::static_visitor +{ +public: + static dev::bytes run(Module const& _module); + + dev::bytes operator()(wasm::Literal const& _literal); + dev::bytes operator()(wasm::StringLiteral const& _literal); + dev::bytes operator()(wasm::LocalVariable const& _identifier); + dev::bytes operator()(wasm::GlobalVariable const& _identifier); + dev::bytes operator()(wasm::BuiltinCall const& _builinCall); + dev::bytes operator()(wasm::FunctionCall const& _functionCall); + dev::bytes operator()(wasm::LocalAssignment const& _assignment); + dev::bytes operator()(wasm::GlobalAssignment const& _assignment); + dev::bytes operator()(wasm::If const& _if); + dev::bytes operator()(wasm::Loop const& _loop); + dev::bytes operator()(wasm::Break const& _break); + dev::bytes operator()(wasm::BreakIf const& _break); + dev::bytes operator()(wasm::Block const& _block); + dev::bytes operator()(wasm::FunctionDefinition const& _function); + +private: + using Type = std::pair, std::vector>; + static Type typeOf(wasm::FunctionImport const& _import); + static Type typeOf(wasm::FunctionDefinition const& _funDef); + + static uint8_t encodeType(std::string const& _typeName); + static std::vector encodeTypes(std::vector const& _typeNames); + dev::bytes typeSection( + std::vector const& _imports, + std::vector const& _functions + ); + + dev::bytes importSection(std::vector const& _imports); + dev::bytes functionSection(std::vector const& _functions); + dev::bytes memorySection(); + dev::bytes globalSection(); + dev::bytes exportSection(); + dev::bytes customSection(std::string const& _name, dev::bytes _data); + dev::bytes codeSection(std::vector const& _functions); + + dev::bytes visit(std::vector const& _expressions); + dev::bytes visitReversed(std::vector const& _expressions); + + static dev::bytes encodeName(std::string const& _name); + + std::map m_locals; + std::map m_globals; + std::map m_functions; + std::map m_functionTypes; + std::stack m_labels; + std::map> m_subModulePosAndSize; +}; + + +} +} + diff --git a/libyul/backends/wasm/EVMToEWasmTranslator.cpp b/libyul/backends/wasm/EVMToEWasmTranslator.cpp index 1be9a1a38..f4de4bc84 100644 --- a/libyul/backends/wasm/EVMToEWasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEWasmTranslator.cpp @@ -48,7 +48,7 @@ namespace { static string const polyfill{R"({ function or_bool(a, b, c, d) -> r { - r := i64.ne(0, i64.or(i64.or(a, b), i64.or(c, d))) + r := i64.or(i64.or(a, b), i64.or(c, d)) } // returns a + y + c plus carry value on 64 bit values. // c should be at most 1 @@ -205,9 +205,7 @@ function cmp(a, b) -> r { switch i64.lt_u(a, b) case 1 { r := 0xffffffffffffffff } default { - switch i64.gt_u(a, b) - case 1 { r := 1 } - default { r := 0 } + r := i64.ne(a, b) } } @@ -218,10 +216,7 @@ function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { case 0 { switch cmp(x3, y3) case 0 { - switch cmp(x4, y4) - case 0 { z4 := 0 } - case 1 { z4 := 0 } - default { z4 := 1 } + z4 := i64.lt_u(x4, y4) } case 1 { z4 := 0 } default { z4 := 1 } @@ -245,13 +240,78 @@ function sgt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { z1, z2, z3, z4 := slt(y1, y2, y3, y4, x1, x2, x3, x4) } -function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() +function shl_single(a, amount) -> x, y { + // amount < 64 + x := i64.shr_u(a, i64.sub(64, amount)) + y := i64.shl(a, amount) } + +function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + if i64.and(i64.eqz(x1), i64.eqz(x2)) { + if i64.eqz(x3) { + if i64.lt_u(x4, 256) { + if i64.ge_u(x4, 128) { + y1 := y3 + y2 := y4 + y3 := 0 + y4 := 0 + x4 := i64.sub(x4, 128) + } + if i64.ge_u(x4, 64) { + y1 := y2 + y2 := y3 + y3 := y4 + y4 := 0 + x4 := i64.sub(x4, 64) + } + let t, r + t, z4 := shl_single(y4, x4) + r, z3 := shl_single(y3, x4) + z3 := i64.or(z3, t) + t, z2 := shl_single(y2, x4) + z2 := i64.or(z2, r) + r, z1 := shl_single(y1, x4) + z1 := i64.or(z1, t) + } + } + } +} + +function shr_single(a, amount) -> x, y { + // amount < 64 + y := i64.shl(a, i64.sub(64, amount)) + x := i64.shr_u(a, amount) +} + function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() + if i64.and(i64.eqz(x1), i64.eqz(x2)) { + if i64.eqz(x3) { + if i64.lt_u(x4, 256) { + if i64.ge_u(x4, 128) { + y4 := y2 + y3 := y1 + y2 := 0 + y1 := 0 + x4 := i64.sub(x4, 128) + } + if i64.ge_u(x4, 64) { + y4 := y3 + y3 := y2 + y2 := y1 + y1 := 0 + x4 := i64.sub(x4, 64) + } + let t + z4, t := shr_single(y4, x4) + z3, t := shr_single(y3, x4) + z4 := i64.or(z4, t) + z2, t := shr_single(y2, x4) + z3 := i64.or(z3, t) + z1, t := shr_single(y1, x4) + z2 := i64.or(z2, t) + } + } + } } function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { // TODO implement @@ -291,45 +351,36 @@ function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { } function address() -> z1, z2, z3, z4 { - let t1, t2, t3, t4 := save_temp_mem_32() eth.getAddress(0) - z1, z2, z3, z4 := mload(0, 0, 0, 0) - restore_temp_mem_32(t1, t2, t3, t4) + z1, z2, z3, z4 := mload_internal(0) } function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 { // TODO implement unreachable() } function origin() -> z1, z2, z3, z4 { - let t1, t2, t3, t4 := save_temp_mem_32() eth.getTxOrigin(0) - z1, z2, z3, z4 := mload(0, 0, 0, 0) - restore_temp_mem_32(t1, t2, t3, t4) + z1, z2, z3, z4 := mload_internal(0) } function caller() -> z1, z2, z3, z4 { - let t1, t2, t3, t4 := save_temp_mem_32() eth.getCaller(0) - z1, z2, z3, z4 := mload(0, 0, 0, 0) - restore_temp_mem_32(t1, t2, t3, t4) + z1, z2, z3, z4 := mload_internal(0) } function callvalue() -> z1, z2, z3, z4 { - let t1, t2, t3, t4 := save_temp_mem_32() eth.getCallValue(0) - z1, z2, z3, z4 := mload(0, 0, 0, 0) - restore_temp_mem_32(t1, t2, t3, t4) + z1, z2, z3, z4 := mload_internal(0) } function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - let t1, t2, t3, t4 := save_temp_mem_32() eth.callDataCopy(0, u256_to_i32(x1, x2, x3, x4), 32) - z1, z2, z3, z4 := mload(0, 0, 0, 0) - restore_temp_mem_32(t1, t2, t3, t4) + z1, z2, z3, z4 := mload_internal(0) } function calldatasize() -> z1, z2, z3, z4 { z4 := eth.getCallDataSize() } function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { eth.callDataCopy( - u256_to_i32ptr(x1, x2, x3, x4), + // scratch - TODO: overflow check + i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64), u256_to_i32(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4) ) @@ -337,14 +388,13 @@ function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { // Needed? function codesize() -> z1, z2, z3, z4 { - let t1, t2, t3, t4 := save_temp_mem_32() eth.getCodeSize(0) - z1, z2, z3, z4 := mload(0, 0, 0, 0) - restore_temp_mem_32(t1, t2, t3, t4) + z1, z2, z3, z4 := mload_internal(0) } function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { eth.codeCopy( - u256_to_i32ptr(x1, x2, x3, x4), + // scratch - TODO: overflow check + i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64), u256_to_i32(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4) ) @@ -355,10 +405,8 @@ function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { } function gasprice() -> z1, z2, z3, z4 { - let t1, t2, t3, t4 := save_temp_mem_32() eth.getTxGasPrice(0) - z1, z2, z3, z4 := mload(0, 0, 0, 0) - restore_temp_mem_32(t1, t2, t3, t4) + z1, z2, z3, z4 := mload_internal(0) } function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 { // TODO implement @@ -378,7 +426,8 @@ function returndatasize() -> z1, z2, z3, z4 { } function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { eth.returnDataCopy( - u256_to_i32ptr(x1, x2, x3, x4), + // scratch - TODO: overflow check + i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64), u256_to_i32(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4) ) @@ -399,10 +448,8 @@ function number() -> z1, z2, z3, z4 { z4 := eth.getBlockNumber() } function difficulty() -> z1, z2, z3, z4 { - let t1, t2, t3, t4 := save_temp_mem_32() eth.getBlockDifficulty(0) - z1, z2, z3, z4 := mload(0, 0, 0, 0) - restore_temp_mem_32(t1, t2, t3, t4) + z1, z2, z3, z4 := mload_internal(0) } function gaslimit() -> z1, z2, z3, z4 { z4 := eth.getBlockGasLimit() @@ -463,6 +510,12 @@ function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) { } function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 { let pos := u256_to_i32ptr(x1, x2, x3, x4) + // Make room for the scratch space + // TODO do we need to check for overflow? + pos := i64.add(pos, 64) + z1, z2, z3, z4 := mload_internal(pos) +} +function mload_internal(pos) -> z1, z2, z3, z4 { z1 := endian_swap(i64.load(pos)) z2 := endian_swap(i64.load(i64.add(pos, 8))) z3 := endian_swap(i64.load(i64.add(pos, 16))) @@ -470,10 +523,16 @@ function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 { } function mstore(x1, x2, x3, x4, y1, y2, y3, y4) { let pos := u256_to_i32ptr(x1, x2, x3, x4) - i64.store(pos, endian_swap(x1)) - i64.store(i64.add(pos, 8), endian_swap(x2)) - i64.store(i64.add(pos, 16), endian_swap(x3)) - i64.store(i64.add(pos, 24), endian_swap(x4)) + // Make room for the scratch space + // TODO do we need to check for overflow? + pos := i64.add(pos, 64) + mstore_internal(pos, y1, y2, y3, y4) +} +function mstore_internal(pos, y1, y2, y3, y4) { + i64.store(pos, endian_swap(y1)) + i64.store(i64.add(pos, 8), endian_swap(y2)) + i64.store(i64.add(pos, 16), endian_swap(y3)) + i64.store(i64.add(pos, 24), endian_swap(y4)) } function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) { // TODO implement @@ -485,19 +544,15 @@ function msize() -> z1, z2, z3, z4 { unreachable() } function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - let t1, t2, t3, t4, t5, t6, t7, t8 := save_temp_mem_64() - mstore(0, 0, 0, 0, x1, x2, x3, x4) - eth.storageLoad(0, 16) - z1, z2, z3, z4 := mload(0, 0, 0, 16) - restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) + mstore_internal(0, x1, x2, x3, x4) + eth.storageLoad(0, 32) + z1, z2, z3, z4 := mload_internal(32) } function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { - let t1, t2, t3, t4, t5, t6, t7, t8 := save_temp_mem_64() - mstore(0, 0, 0, 0, x1, x2, x3, x4) - mstore(0, 0, 0, 32, y1, y2, y3, y4) + mstore_internal(0, x1, x2, x3, x4) + mstore_internal(32, y1, y2, y3, y4) eth.storageStore(0, 32) - restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) } // Needed? @@ -615,13 +670,15 @@ function selfdestruct(a1, a2, a3, a4) { function return(x1, x2, x3, x4, y1, y2, y3, y4) { eth.finish( - u256_to_i32ptr(x1, x2, x3, x4), + // scratch - TODO: overflow check + i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64), u256_to_i32(y1, y2, y3, y4) ) } function revert(x1, x2, x3, x4, y1, y2, y3, y4) { eth.revert( - u256_to_i32ptr(x1, x2, x3, x4), + // scratch - TODO: overflow check + i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64), u256_to_i32(y1, y2, y3, y4) ) } @@ -653,12 +710,13 @@ Object EVMToEWasmTranslator::run(Object const& _object) ast.statements.emplace_back(ASTCopier{}.translate(st)); Object ret; + ret.name = _object.name; ret.code = make_shared(move(ast)); ret.analysisInfo = make_shared(); ErrorList errors; ErrorReporter errorReporter(errors); - AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, boost::none, WasmDialect::instance(), {}, _object.dataNames()); + AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, std::nullopt, WasmDialect::instance(), {}, _object.dataNames()); if (!analyzer.analyze(*ret.code)) { // TODO the errors here are "wrong" because they have invalid source references! diff --git a/libyul/backends/wasm/EWasmAST.h b/libyul/backends/wasm/EWasmAST.h index e3ce6604f..677668ade 100644 --- a/libyul/backends/wasm/EWasmAST.h +++ b/libyul/backends/wasm/EWasmAST.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include namespace yul { @@ -33,7 +35,6 @@ struct Literal; struct StringLiteral; struct LocalVariable; struct GlobalVariable; -struct Label; struct FunctionCall; struct BuiltinCall; struct LocalAssignment; @@ -42,11 +43,11 @@ struct Block; struct If; struct Loop; struct Break; -struct Continue; +struct BreakIf; using Expression = boost::variant< - Literal, StringLiteral, LocalVariable, GlobalVariable, Label, + Literal, StringLiteral, LocalVariable, GlobalVariable, FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment, - Block, If, Loop, Break, Continue + Block, If, Loop, Break, BreakIf >; struct Literal { uint64_t value; }; @@ -66,7 +67,7 @@ struct If { }; struct Loop { std::string labelName; std::vector statements; }; struct Break { Label label; }; -struct Continue { Label label; }; +struct BreakIf { Label label; std::unique_ptr condition; }; struct VariableDeclaration { std::string variableName; }; struct GlobalVariableDeclaration { std::string variableName; }; @@ -87,6 +88,16 @@ struct FunctionDefinition std::vector body; }; +/** + * Abstract representation of a wasm module. + */ +struct Module +{ + std::vector globals; + std::vector imports; + std::vector functions; + std::map subModules; +}; } } diff --git a/libyul/backends/wasm/EWasmCodeTransform.cpp b/libyul/backends/wasm/EWasmCodeTransform.cpp index f9e28e94a..277df0df2 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.cpp +++ b/libyul/backends/wasm/EWasmCodeTransform.cpp @@ -20,7 +20,6 @@ #include -#include #include #include @@ -37,10 +36,11 @@ using namespace std; using namespace dev; using namespace yul; -string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) +wasm::Module EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) { + wasm::Module module; + EWasmCodeTransform transform(_dialect, _ast); - vector functions; for (auto const& statement: _ast.statements) { @@ -49,17 +49,14 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) "Expected only function definitions at the highest level." ); if (statement.type() == typeid(yul::FunctionDefinition)) - functions.emplace_back(transform.translateFunction(boost::get(statement))); + module.functions.emplace_back(transform.translateFunction(boost::get(statement))); } - std::vector imports; for (auto& imp: transform.m_functionsToImport) - imports.emplace_back(std::move(imp.second)); - return EWasmToText().run( - transform.m_globalVariables, - imports, - functions - ); + module.imports.emplace_back(std::move(imp.second)); + module.globals = transform.m_globalVariables; + + return module; } wasm::Expression EWasmCodeTransform::generateMultiAssignment( @@ -133,6 +130,8 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionalInstruction const& _f) wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call) { + bool typeConversionNeeded = false; + if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name)) { if (_call.functionName.name.str().substr(0, 4) == "eth.") @@ -152,6 +151,7 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call) imp.paramTypes.emplace_back(param.str()); m_functionsToImport[builtin->name] = std::move(imp); } + typeConversionNeeded = true; } else if (builtin->literalArguments) { @@ -161,14 +161,32 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call) return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)}; } else - return wasm::BuiltinCall{_call.functionName.name.str(), visit(_call.arguments)}; + { + wasm::BuiltinCall call{ + _call.functionName.name.str(), + injectTypeConversionIfNeeded(visit(_call.arguments), builtin->parameters) + }; + if (!builtin->returns.empty() && !builtin->returns.front().empty() && builtin->returns.front() != "i64"_yulstring) + { + yulAssert(builtin->returns.front() == "i32"_yulstring, "Invalid type " + builtin->returns.front().str()); + call = wasm::BuiltinCall{"i64.extend_i32_u", make_vector(std::move(call))}; + } + return {std::move(call)}; + } } // If this function returns multiple values, then the first one will // be returned in the expression itself and the others in global variables. // The values have to be used right away in an assignment or variable declaration, // so it is handled there. - return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)}; + + wasm::FunctionCall funCall{_call.functionName.name.str(), visit(_call.arguments)}; + if (typeConversionNeeded) + // Inject type conversion if needed on the fly. This is just a temporary measure + // and can be removed once we have proper types in Yul. + return injectTypeConversionIfNeeded(std::move(funCall)); + else + return {std::move(funCall)}; } wasm::Expression EWasmCodeTransform::operator()(Identifier const& _identifier) @@ -191,7 +209,16 @@ wasm::Expression EWasmCodeTransform::operator()(yul::Instruction const&) wasm::Expression EWasmCodeTransform::operator()(If const& _if) { - return wasm::If{visit(*_if.condition), visit(_if.body.statements), {}}; + // TODO converting i64 to i32 might not always be needed. + + vector args; + args.emplace_back(visitReturnByValue(*_if.condition)); + args.emplace_back(wasm::Literal{0}); + return wasm::If{ + make_unique(wasm::BuiltinCall{"i64.ne", std::move(args)}), + visit(_if.body.statements), + {} + }; } wasm::Expression EWasmCodeTransform::operator()(Switch const& _switch) @@ -248,8 +275,7 @@ wasm::Expression EWasmCodeTransform::operator()(ForLoop const& _for) wasm::Loop loop; loop.statements = visit(_for.pre.statements); - loop.statements.emplace_back(wasm::BuiltinCall{"br_if", make_vector( - wasm::Label{breakLabel}, + loop.statements.emplace_back(wasm::BreakIf{wasm::Label{breakLabel}, make_unique( wasm::BuiltinCall{"i64.eqz", make_vector( visitReturnByValue(*_for.condition) )} @@ -267,7 +293,7 @@ wasm::Expression EWasmCodeTransform::operator()(Break const&) wasm::Expression EWasmCodeTransform::operator()(Continue const&) { - return wasm::Continue{wasm::Label{m_breakContinueLabelNames.top().second}}; + return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().second}}; } wasm::Expression EWasmCodeTransform::operator()(Block const& _block) @@ -337,6 +363,40 @@ wasm::FunctionDefinition EWasmCodeTransform::translateFunction(yul::FunctionDefi return fun; } +wasm::Expression EWasmCodeTransform::injectTypeConversionIfNeeded(wasm::FunctionCall _call) const +{ + wasm::FunctionImport const& import = m_functionsToImport.at(YulString{_call.functionName}); + for (size_t i = 0; i < _call.arguments.size(); ++i) + if (import.paramTypes.at(i) == "i32") + _call.arguments[i] = wasm::BuiltinCall{"i32.wrap_i64", make_vector(std::move(_call.arguments[i]))}; + else + yulAssert(import.paramTypes.at(i) == "i64", "Unknown type " + import.paramTypes.at(i)); + + if (import.returnType && *import.returnType != "i64") + { + yulAssert(*import.returnType == "i32", "Invalid type " + *import.returnType); + return wasm::BuiltinCall{"i64.extend_i32_u", make_vector(std::move(_call))}; + } + return {std::move(_call)}; +} + +vector EWasmCodeTransform::injectTypeConversionIfNeeded( + vector _arguments, + vector const& _parameterTypes +) const +{ + for (size_t i = 0; i < _arguments.size(); ++i) + if (_parameterTypes.at(i) == "i32"_yulstring) + _arguments[i] = wasm::BuiltinCall{"i32.wrap_i64", make_vector(std::move(_arguments[i]))}; + else + yulAssert( + _parameterTypes.at(i).empty() || _parameterTypes.at(i) == "i64"_yulstring, + "Unknown type " + _parameterTypes.at(i).str() + ); + + return _arguments; +} + string EWasmCodeTransform::newLabel() { return m_nameDispenser.newName("label_"_yulstring).str(); diff --git a/libyul/backends/wasm/EWasmCodeTransform.h b/libyul/backends/wasm/EWasmCodeTransform.h index bbe0e1b69..cb9927d20 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.h +++ b/libyul/backends/wasm/EWasmCodeTransform.h @@ -35,7 +35,7 @@ struct AsmAnalysisInfo; class EWasmCodeTransform: public boost::static_visitor { public: - static std::string run(Dialect const& _dialect, yul::Block const& _ast); + static wasm::Module run(Dialect const& _dialect, yul::Block const& _ast); public: wasm::Expression operator()(yul::Instruction const& _instruction); @@ -82,6 +82,12 @@ private: wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef); + wasm::Expression injectTypeConversionIfNeeded(wasm::FunctionCall _call) const; + std::vector injectTypeConversionIfNeeded( + std::vector _arguments, + std::vector const& _parameterTypes + ) const; + std::string newLabel(); /// Makes sure that there are at least @a _amount global variables. void allocateGlobals(size_t _amount); diff --git a/libyul/backends/wasm/EWasmObjectCompiler.cpp b/libyul/backends/wasm/EWasmObjectCompiler.cpp index 2b0c4c02f..7ba72735a 100644 --- a/libyul/backends/wasm/EWasmObjectCompiler.cpp +++ b/libyul/backends/wasm/EWasmObjectCompiler.cpp @@ -21,32 +21,36 @@ #include #include +#include +#include #include #include +#include + using namespace yul; using namespace std; -string EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect) +pair EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect) { EWasmObjectCompiler compiler(_dialect); - return compiler.run(_object); + wasm::Module module = compiler.run(_object); + return {EWasmToText().run(module), wasm::BinaryTransform::run(module)}; } -string EWasmObjectCompiler::run(Object& _object) +wasm::Module EWasmObjectCompiler::run(Object& _object) { - string ret; + yulAssert(_object.analysisInfo, "No analysis info."); + yulAssert(_object.code, "No code."); + + wasm::Module module = EWasmCodeTransform::run(m_dialect, *_object.code); for (auto& subNode: _object.subObjects) if (Object* subObject = dynamic_cast(subNode.get())) - ret += compile(*subObject, m_dialect); + module.subModules[subObject->name.str()] = run(*subObject); else yulAssert(false, "Data is not yet supported for EWasm."); - yulAssert(_object.analysisInfo, "No analysis info."); - yulAssert(_object.code, "No code."); - ret += EWasmCodeTransform::run(m_dialect, *_object.code); - - return ret; + return module; } diff --git a/libyul/backends/wasm/EWasmObjectCompiler.h b/libyul/backends/wasm/EWasmObjectCompiler.h index 67d4f4098..c99d258e1 100644 --- a/libyul/backends/wasm/EWasmObjectCompiler.h +++ b/libyul/backends/wasm/EWasmObjectCompiler.h @@ -21,22 +21,33 @@ #pragma once #include +#include +#include +namespace dev +{ +using bytes = std::vector; +} namespace yul { struct Object; struct Dialect; +namespace wasm +{ +struct Module; +} class EWasmObjectCompiler { public: - static std::string compile(Object& _object, Dialect const& _dialect); + /// Compiles the given object and returns the WAST and the binary representation. + static std::pair compile(Object& _object, Dialect const& _dialect); private: EWasmObjectCompiler(Dialect const& _dialect): m_dialect(_dialect) {} - std::string run(Object& _object); + wasm::Module run(Object& _object); Dialect const& m_dialect; }; diff --git a/libyul/backends/wasm/EWasmToText.cpp b/libyul/backends/wasm/EWasmToText.cpp index 9d3bce0f5..3971be1f6 100644 --- a/libyul/backends/wasm/EWasmToText.cpp +++ b/libyul/backends/wasm/EWasmToText.cpp @@ -30,14 +30,15 @@ using namespace std; using namespace yul; using namespace dev; -string EWasmToText::run( - vector const& _globals, - vector const& _imports, - vector const& _functions -) +string EWasmToText::run(wasm::Module const& _module) { string ret = "(module\n"; - for (wasm::FunctionImport const& imp: _imports) + for (auto const& sub: _module.subModules) + ret += + " ;; sub-module \"" + + sub.first + + "\" will be encoded as custom section in binary here, but is skipped in text mode.\n"; + for (wasm::FunctionImport const& imp: _module.imports) { ret += " (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName; if (!imp.paramTypes.empty()) @@ -52,10 +53,10 @@ string EWasmToText::run( // export the main function ret += " (export \"main\" (func $main))\n"; - for (auto const& g: _globals) + for (auto const& g: _module.globals) ret += " (global $" + g.variableName + " (mut i64) (i64.const 0))\n"; ret += "\n"; - for (auto const& f: _functions) + for (auto const& f: _module.functions) ret += transform(f) + "\n"; return move(ret) + ")\n"; } @@ -74,17 +75,12 @@ string EWasmToText::operator()(wasm::StringLiteral const& _literal) string EWasmToText::operator()(wasm::LocalVariable const& _identifier) { - return "(get_local $" + _identifier.name + ")"; + return "(local.get $" + _identifier.name + ")"; } string EWasmToText::operator()(wasm::GlobalVariable const& _identifier) { - return "(get_global $" + _identifier.name + ")"; -} - -string EWasmToText::operator()(wasm::Label const& _label) -{ - return "$" + _label.name; + return "(global.get $" + _identifier.name + ")"; } string EWasmToText::operator()(wasm::BuiltinCall const& _builtinCall) @@ -101,12 +97,12 @@ string EWasmToText::operator()(wasm::FunctionCall const& _functionCall) string EWasmToText::operator()(wasm::LocalAssignment const& _assignment) { - return "(set_local $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n"; + return "(local.set $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n"; } string EWasmToText::operator()(wasm::GlobalAssignment const& _assignment) { - return "(set_global $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n"; + return "(global.set $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n"; } string EWasmToText::operator()(wasm::If const& _if) @@ -128,9 +124,9 @@ string EWasmToText::operator()(wasm::Break const& _break) return "(break $" + _break.label.name + ")\n"; } -string EWasmToText::operator()(wasm::Continue const& _continue) +string EWasmToText::operator()(wasm::BreakIf const& _break) { - return "(continue $" + _continue.label.name + ")\n"; + return "(br_if $" + _break.label.name + " " + visit(*_break.condition) + ")\n"; } string EWasmToText::operator()(wasm::Block const& _block) @@ -165,7 +161,7 @@ string EWasmToText::transform(wasm::FunctionDefinition const& _function) ret += " (result i64)\n"; for (auto const& local: _function.locals) ret += " (local $" + local.variableName + " i64)\n"; - ret += indented(joinTransformed(_function.body)); + ret += indented(joinTransformed(_function.body, '\n')); if (ret.back() != '\n') ret += '\n'; ret += ")\n"; diff --git a/libyul/backends/wasm/EWasmToText.h b/libyul/backends/wasm/EWasmToText.h index 7eed8c076..6163fd80b 100644 --- a/libyul/backends/wasm/EWasmToText.h +++ b/libyul/backends/wasm/EWasmToText.h @@ -31,18 +31,13 @@ struct AsmAnalysisInfo; class EWasmToText: public boost::static_visitor { public: - std::string run( - std::vector const& _globals, - std::vector const& _imports, - std::vector const& _functions - ); + std::string run(wasm::Module const& _module); public: std::string operator()(wasm::Literal const& _literal); std::string operator()(wasm::StringLiteral const& _literal); std::string operator()(wasm::LocalVariable const& _identifier); std::string operator()(wasm::GlobalVariable const& _identifier); - std::string operator()(wasm::Label const& _label); std::string operator()(wasm::BuiltinCall const& _builinCall); std::string operator()(wasm::FunctionCall const& _functionCall); std::string operator()(wasm::LocalAssignment const& _assignment); @@ -50,7 +45,7 @@ public: std::string operator()(wasm::If const& _if); std::string operator()(wasm::Loop const& _loop); std::string operator()(wasm::Break const& _break); - std::string operator()(wasm::Continue const& _continue); + std::string operator()(wasm::BreakIf const& _break); std::string operator()(wasm::Block const& _block); private: diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index f781292ae..69ba02413 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -46,12 +46,22 @@ WasmDialect::WasmDialect(): }) addFunction(name, 2, 1); + m_functions["i64.lt_u"_yulstring].returns.front() = "i32"_yulstring; + m_functions["i64.gt_u"_yulstring].returns.front() = "i32"_yulstring; + m_functions["i64.le_u"_yulstring].returns.front() = "i32"_yulstring; + m_functions["i64.ge_u"_yulstring].returns.front() = "i32"_yulstring; + m_functions["i64.eq"_yulstring].returns.front() = "i32"_yulstring; + m_functions["i64.ne"_yulstring].returns.front() = "i32"_yulstring; + addFunction("i64.eqz", 1, 1); + m_functions["i64.eqz"_yulstring].returns.front() = "i32"_yulstring; addFunction("i64.store", 2, 0, false); + m_functions["i64.store"_yulstring].parameters.front() = "i32"_yulstring; m_functions["i64.store"_yulstring].sideEffects.invalidatesStorage = false; addFunction("i64.load", 1, 1, false); + m_functions["i64.load"_yulstring].parameters.front() = "i32"_yulstring; m_functions["i64.load"_yulstring].sideEffects.invalidatesStorage = false; m_functions["i64.load"_yulstring].sideEffects.invalidatesMemory = false; m_functions["i64.load"_yulstring].sideEffects.sideEffectFree = true; @@ -63,8 +73,8 @@ WasmDialect::WasmDialect(): m_functions["unreachable"_yulstring].sideEffects.invalidatesStorage = false; m_functions["unreachable"_yulstring].sideEffects.invalidatesMemory = false; - addFunction("datasize", 1, 4, true, true); - addFunction("dataoffset", 1, 4, true, true); + addFunction("datasize", 1, 1, true, true); + addFunction("dataoffset", 1, 1, true, true); addEthereumExternals(); } diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 01f0c3784..81c80da99 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -82,11 +82,35 @@ void WordSizeTransform::operator()(Block& _block) { iterateReplacing( _block.statements, - [&](Statement& _s) -> boost::optional> + [&](Statement& _s) -> std::optional> { if (_s.type() == typeid(VariableDeclaration)) { VariableDeclaration& varDecl = boost::get(_s); + + // Special handling for datasize and dataoffset - they will only need one variable. + if (varDecl.value && varDecl.value->type() == typeid(FunctionCall)) + if (BuiltinFunction const* f = m_inputDialect.builtin(boost::get(*varDecl.value).functionName.name)) + if (f->literalArguments) + { + yulAssert(f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring, ""); + yulAssert(varDecl.variables.size() == 1, ""); + auto newLhs = generateU64IdentifierNames(varDecl.variables[0].name); + vector ret; + for (int i = 0; i < 3; i++) + ret.push_back(VariableDeclaration{ + varDecl.location, + {TypedName{varDecl.location, newLhs[i], "u64"_yulstring}}, + make_unique(Literal{locationOf(*varDecl.value), LiteralKind::Number, "0"_yulstring, "u64"_yulstring}) + }); + ret.push_back(VariableDeclaration{ + varDecl.location, + {TypedName{varDecl.location, newLhs[3], "u64"_yulstring}}, + std::move(varDecl.value) + }); + return {std::move(ret)}; + } + if ( !varDecl.value || varDecl.value->type() == typeid(FunctionalInstruction) || @@ -95,7 +119,7 @@ void WordSizeTransform::operator()(Block& _block) { if (varDecl.value) visit(*varDecl.value); rewriteVarDeclList(varDecl.variables); - return boost::none; + return std::nullopt; } else if ( varDecl.value->type() == typeid(Identifier) || @@ -114,7 +138,7 @@ void WordSizeTransform::operator()(Block& _block) std::move(newRhs[i]) } ); - return ret; + return {std::move(ret)}; } else yulAssert(false, ""); @@ -123,6 +147,30 @@ void WordSizeTransform::operator()(Block& _block) { Assignment& assignment = boost::get(_s); yulAssert(assignment.value, ""); + + // Special handling for datasize and dataoffset - they will only need one variable. + if (assignment.value->type() == typeid(FunctionCall)) + if (BuiltinFunction const* f = m_inputDialect.builtin(boost::get(*assignment.value).functionName.name)) + if (f->literalArguments) + { + yulAssert(f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring, ""); + yulAssert(assignment.variableNames.size() == 1, ""); + auto newLhs = generateU64IdentifierNames(assignment.variableNames[0].name); + vector ret; + for (int i = 0; i < 3; i++) + ret.push_back(Assignment{ + assignment.location, + {Identifier{assignment.location, newLhs[i]}}, + make_unique(Literal{locationOf(*assignment.value), LiteralKind::Number, "0"_yulstring, "u64"_yulstring}) + }); + ret.push_back(Assignment{ + assignment.location, + {Identifier{assignment.location, newLhs[3]}}, + std::move(assignment.value) + }); + return {std::move(ret)}; + } + if ( assignment.value->type() == typeid(FunctionalInstruction) || assignment.value->type() == typeid(FunctionCall) @@ -130,7 +178,7 @@ void WordSizeTransform::operator()(Block& _block) { if (assignment.value) visit(*assignment.value); rewriteIdentifierList(assignment.variableNames); - return boost::none; + return std::nullopt; } else if ( assignment.value->type() == typeid(Identifier) || @@ -149,7 +197,7 @@ void WordSizeTransform::operator()(Block& _block) std::move(newRhs[i]) } ); - return ret; + return {std::move(ret)}; } else yulAssert(false, ""); @@ -158,7 +206,7 @@ void WordSizeTransform::operator()(Block& _block) return handleSwitch(boost::get(_s)); else visit(_s); - return boost::none; + return std::nullopt; } ); } @@ -174,7 +222,7 @@ void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList) { iterateReplacing( _nameList, - [&](TypedName const& _n) -> boost::optional + [&](TypedName const& _n) -> std::optional { TypedNameList ret; for (auto newName: generateU64IdentifierNames(_n.name)) @@ -188,7 +236,7 @@ void WordSizeTransform::rewriteIdentifierList(vector& _ids) { iterateReplacing( _ids, - [&](Identifier const& _id) -> boost::optional> + [&](Identifier const& _id) -> std::optional> { vector ret; for (auto newId: m_variableMapping.at(_id.name)) @@ -202,7 +250,7 @@ void WordSizeTransform::rewriteFunctionCallArguments(vector& _args) { iterateReplacing( _args, - [&](Expression& _e) -> boost::optional> + [&](Expression& _e) -> std::optional> { return expandValueToVector(_e); } diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index b2e0a383a..dab3e296a 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -25,11 +25,11 @@ #include #include -#include -#include -#include #include +#include +#include +#include namespace yul { diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h index a1f244795..995f5fe21 100644 --- a/libyul/optimiser/ASTWalker.h +++ b/libyul/optimiser/ASTWalker.h @@ -26,11 +26,11 @@ #include #include -#include -#include -#include #include +#include +#include +#include namespace yul { diff --git a/libyul/optimiser/BlockFlattener.cpp b/libyul/optimiser/BlockFlattener.cpp index e6f085247..48816c695 100644 --- a/libyul/optimiser/BlockFlattener.cpp +++ b/libyul/optimiser/BlockFlattener.cpp @@ -30,7 +30,7 @@ void BlockFlattener::operator()(Block& _block) iterateReplacing( _block.statements, - [](Statement& _s) -> boost::optional> + [](Statement& _s) -> std::optional> { if (_s.type() == typeid(Block)) return std::move(boost::get(_s).statements); diff --git a/libyul/optimiser/CallGraphGenerator.h b/libyul/optimiser/CallGraphGenerator.h index 6946721bd..4dc420900 100644 --- a/libyul/optimiser/CallGraphGenerator.h +++ b/libyul/optimiser/CallGraphGenerator.h @@ -24,10 +24,9 @@ #include -#include - -#include #include +#include +#include namespace yul { diff --git a/libyul/optimiser/ConditionalSimplifier.cpp b/libyul/optimiser/ConditionalSimplifier.cpp new file mode 100644 index 000000000..d720ae88d --- /dev/null +++ b/libyul/optimiser/ConditionalSimplifier.cpp @@ -0,0 +1,92 @@ +/* + 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 . +*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; + +void ConditionalSimplifier::operator()(Switch& _switch) +{ + visit(*_switch.expression); + if (_switch.expression->type() != typeid(Identifier)) + { + ASTModifier::operator()(_switch); + return; + } + YulString expr = boost::get(*_switch.expression).name; + for (auto& _case: _switch.cases) + { + if (_case.value) + { + (*this)(*_case.value); + _case.body.statements.insert(_case.body.statements.begin(), + Assignment{ + _case.body.location, + {Identifier{_case.body.location, expr}}, + make_unique(*_case.value) + } + ); + } + (*this)(_case.body); + } +} + +void ConditionalSimplifier::operator()(Block& _block) +{ + iterateReplacing( + _block.statements, + [&](Statement& _s) -> std::optional> + { + visit(_s); + if (_s.type() == typeid(If)) + { + If& _if = boost::get(_s); + if ( + _if.condition->type() == typeid(Identifier) && + !_if.body.statements.empty() && + TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) != + TerminationFinder::ControlFlow::FlowOut + ) + { + YulString condition = boost::get(*_if.condition).name; + langutil::SourceLocation location = _if.location; + return make_vector( + std::move(_s), + Assignment{ + location, + {Identifier{location, condition}}, + make_unique(Literal{ + location, + LiteralKind::Number, + "0"_yulstring, + {} + }) + } + ); + } + } + return {}; + } + ); +} diff --git a/libyul/optimiser/ConditionalSimplifier.h b/libyul/optimiser/ConditionalSimplifier.h new file mode 100644 index 000000000..9b0f1cb26 --- /dev/null +++ b/libyul/optimiser/ConditionalSimplifier.h @@ -0,0 +1,71 @@ +/* + 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 . +*/ +#pragma once + +#include +#include +#include +#include + +namespace yul +{ + +/** + * Conditional simplifier. + * + * Inserts assignments to condition variables if the value can be determined + * from the control-flow. + * + * Destroys SSA form. + * + * Currently, this tool is very limited, mostly because we do not yet have support + * for boolean types. Since conditions only check for expressions being nonzero, + * we cannot assign a specific value. + * + * Current features: + * - switch cases: insert " := " + * - after if statement with terminating control-flow, insert " := 0" + * + * Future features: + * - allow replacements by "1" + * - take termination of user-defined functions into account + * + * Works best with SSA form and if dead code removal has run before. + * + * Prerequisite: Disambiguator. + */ +class ConditionalSimplifier: public ASTModifier +{ +public: + static constexpr char const* name{"ConditionalSimplifier"}; + static void run(OptimiserStepContext& _context, Block& _ast) + { + ConditionalSimplifier{_context.dialect}(_ast); + } + + using ASTModifier::operator(); + void operator()(Switch& _switch) override; + void operator()(Block& _block) override; + +private: + explicit ConditionalSimplifier(Dialect const& _dialect): + m_dialect(_dialect) + {} + Dialect const& m_dialect; +}; + +} diff --git a/libyul/optimiser/ConditionalUnsimplifier.cpp b/libyul/optimiser/ConditionalUnsimplifier.cpp new file mode 100644 index 000000000..224634017 --- /dev/null +++ b/libyul/optimiser/ConditionalUnsimplifier.cpp @@ -0,0 +1,98 @@ +/* + 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 . +*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; + +void ConditionalUnsimplifier::operator()(Switch& _switch) +{ + visit(*_switch.expression); + if (_switch.expression->type() != typeid(Identifier)) + { + ASTModifier::operator()(_switch); + return; + } + YulString expr = boost::get(*_switch.expression).name; + for (auto& _case: _switch.cases) + { + if (_case.value) + { + (*this)(*_case.value); + if ( + !_case.body.statements.empty() && + _case.body.statements.front().type() == typeid(Assignment) + ) + { + Assignment const& assignment = boost::get(_case.body.statements.front()); + if ( + assignment.variableNames.size() == 1 && + assignment.variableNames.front().name == expr && + assignment.value->type() == typeid(Literal) && + valueOfLiteral(boost::get(*assignment.value)) == valueOfLiteral(*_case.value) + ) + _case.body.statements.erase(_case.body.statements.begin()); + } + } + (*this)(_case.body); + } +} + +void ConditionalUnsimplifier::operator()(Block& _block) +{ + walkVector(_block.statements); + iterateReplacingWindow<2>( + _block.statements, + [&](Statement& _stmt1, Statement& _stmt2) -> std::optional> + { + if (_stmt1.type() == typeid(If)) + { + If& _if = boost::get(_stmt1); + if ( + _if.condition->type() == typeid(Identifier) && + !_if.body.statements.empty() + ) + { + YulString condition = boost::get(*_if.condition).name; + if ( + _stmt2.type() == typeid(Assignment) && + TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) != + TerminationFinder::ControlFlow::FlowOut + ) + { + Assignment const& assignment = boost::get(_stmt2); + if ( + assignment.variableNames.size() == 1 && + assignment.variableNames.front().name == condition && + assignment.value->type() == typeid(Literal) && + valueOfLiteral(boost::get(*assignment.value)) == 0 + ) + return {make_vector(std::move(_stmt1))}; + } + } + } + return {}; + } + ); +} diff --git a/libyul/optimiser/ConditionalUnsimplifier.h b/libyul/optimiser/ConditionalUnsimplifier.h new file mode 100644 index 000000000..16358c9f7 --- /dev/null +++ b/libyul/optimiser/ConditionalUnsimplifier.h @@ -0,0 +1,51 @@ +/* + 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 . +*/ +#pragma once + +#include +#include +#include +#include + +namespace yul +{ + +/** + * Reverse of conditional simplifier. + * + */ +class ConditionalUnsimplifier: public ASTModifier +{ +public: + static constexpr char const* name{"ConditionalUnsimplifier"}; + static void run(OptimiserStepContext& _context, Block& _ast) + { + ConditionalUnsimplifier{_context.dialect}(_ast); + } + + using ASTModifier::operator(); + void operator()(Switch& _switch) override; + void operator()(Block& _block) override; + +private: + explicit ConditionalUnsimplifier(Dialect const& _dialect): + m_dialect(_dialect) + {} + Dialect const& m_dialect; +}; + +} diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 96e399027..99fa4b137 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -30,7 +30,7 @@ using namespace std; using namespace dev; using namespace yul; -using OptionalStatements = boost::optional>; +using OptionalStatements = std::optional>; namespace { diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index c2042a19c..6cf65ff25 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -354,7 +354,7 @@ bool DataFlowAnalyzer::inScope(YulString _variableName) const return false; } -boost::optional> DataFlowAnalyzer::isSimpleStore( +std::optional> DataFlowAnalyzer::isSimpleStore( dev::eth::Instruction _store, ExpressionStatement const& _statement ) const diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index b8e455ea1..c4b3db5da 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -128,7 +128,7 @@ protected: /// Returns true iff the variable is in scope. bool inScope(YulString _variableName) const; - boost::optional> isSimpleStore( + std::optional> isSimpleStore( dev::eth::Instruction _store, ExpressionStatement const& _statement ) const; diff --git a/libyul/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h index ec6a0879d..cd3ff49a2 100644 --- a/libyul/optimiser/Disambiguator.h +++ b/libyul/optimiser/Disambiguator.h @@ -26,8 +26,8 @@ #include #include -#include +#include #include namespace yul diff --git a/libyul/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h index 16e48ceb5..9404f3e73 100644 --- a/libyul/optimiser/ExpressionInliner.h +++ b/libyul/optimiser/ExpressionInliner.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 4c857f0a3..86fb6206f 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -85,8 +85,8 @@ void ExpressionSplitter::operator()(Block& _block) vector saved; swap(saved, m_statementsToPrefix); - function>(Statement&)> f = - [&](Statement& _statement) -> boost::optional> { + function>(Statement&)> f = + [&](Statement& _statement) -> std::optional> { m_statementsToPrefix.clear(); visit(_statement); if (m_statementsToPrefix.empty()) diff --git a/libyul/optimiser/ForLoopConditionIntoBody.cpp b/libyul/optimiser/ForLoopConditionIntoBody.cpp index ca53b8d6c..a3c213a05 100644 --- a/libyul/optimiser/ForLoopConditionIntoBody.cpp +++ b/libyul/optimiser/ForLoopConditionIntoBody.cpp @@ -31,7 +31,11 @@ void ForLoopConditionIntoBody::run(OptimiserStepContext& _context, Block& _ast) void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) { - if (m_dialect.booleanNegationFunction() && _forLoop.condition->type() != typeid(Literal)) + if ( + m_dialect.booleanNegationFunction() && + _forLoop.condition->type() != typeid(Literal) && + _forLoop.condition->type() != typeid(Identifier) + ) { langutil::SourceLocation loc = locationOf(*_forLoop.condition); _forLoop.body.statements.insert( diff --git a/libyul/optimiser/ForLoopInitRewriter.cpp b/libyul/optimiser/ForLoopInitRewriter.cpp index 2de0a662f..a11215e50 100644 --- a/libyul/optimiser/ForLoopInitRewriter.cpp +++ b/libyul/optimiser/ForLoopInitRewriter.cpp @@ -27,7 +27,7 @@ void ForLoopInitRewriter::operator()(Block& _block) { iterateReplacing( _block.statements, - [&](Statement& _stmt) -> boost::optional> + [&](Statement& _stmt) -> std::optional> { if (_stmt.type() == typeid(ForLoop)) { diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 437758481..8b0af2691 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -147,14 +147,14 @@ bool FullInliner::recursive(FunctionDefinition const& _fun) const void InlineModifier::operator()(Block& _block) { - function>(Statement&)> f = [&](Statement& _statement) -> boost::optional> { + function>(Statement&)> f = [&](Statement& _statement) -> std::optional> { visit(_statement); return tryInlineStatement(_statement); }; iterateReplacing(_block.statements, f); } -boost::optional> InlineModifier::tryInlineStatement(Statement& _statement) +std::optional> InlineModifier::tryInlineStatement(Statement& _statement) { // Only inline for expression statements, assignments and variable declarations. Expression* e = boost::apply_visitor(GenericFallbackReturnsVisitor( diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index 4f1ea9d3c..746cc46db 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -30,8 +30,8 @@ #include #include -#include +#include #include namespace yul @@ -126,7 +126,7 @@ public: void operator()(Block& _block) override; private: - boost::optional> tryInlineStatement(Statement& _statement); + std::optional> tryInlineStatement(Statement& _statement); std::vector performInline(Statement& _statement, FunctionCall& _funCall); YulString m_currentFunction; diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp index d5f989d18..2a421f9d9 100644 --- a/libyul/optimiser/SSAReverser.cpp +++ b/libyul/optimiser/SSAReverser.cpp @@ -35,7 +35,7 @@ void SSAReverser::operator()(Block& _block) walkVector(_block.statements); iterateReplacingWindow<2>( _block.statements, - [&](Statement& _stmt1, Statement& _stmt2) -> boost::optional> + [&](Statement& _stmt1, Statement& _stmt2) -> std::optional> { auto* varDecl = boost::get(&_stmt1); diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index 9d9175754..1f5d47e20 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -58,7 +58,7 @@ void IntroduceSSA::operator()(Block& _block) { iterateReplacing( _block.statements, - [&](Statement& _s) -> boost::optional> + [&](Statement& _s) -> std::optional> { if (_s.type() == typeid(VariableDeclaration)) { @@ -213,7 +213,7 @@ void IntroduceControlFlowSSA::operator()(Block& _block) iterateReplacing( _block.statements, - [&](Statement& _s) -> boost::optional> + [&](Statement& _s) -> std::optional> { vector toPrepend; for (YulString toReassign: m_variablesToReassign) @@ -253,7 +253,7 @@ void IntroduceControlFlowSSA::operator()(Block& _block) else { toPrepend.emplace_back(std::move(_s)); - return toPrepend; + return {std::move(toPrepend)}; } } ); diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 3f5d8541b..96f225f8c 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -64,7 +64,7 @@ bool SimplificationRules::isInitialized() const return !m_rules[uint8_t(dev::eth::Instruction::ADD)].empty(); } -boost::optional const*>> +std::optional const*>> SimplificationRules::instructionAndArguments(Dialect const& _dialect, Expression const& _expr) { if (_expr.type() == typeid(FunctionalInstruction)) diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index a63071b9f..1caf323d8 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -26,9 +26,9 @@ #include #include -#include #include +#include #include namespace yul @@ -57,7 +57,7 @@ public: /// by the constructor, but we had some issues with static initialization. bool isInitialized() const; - static boost::optional const*>> + static std::optional const*>> instructionAndArguments(Dialect const& _dialect, Expression const& _expr); private: @@ -85,6 +85,10 @@ enum class PatternKind class Pattern { public: + using Builtins = dev::eth::EVMBuiltins; + static constexpr size_t WordSize = 256; + using Word = dev::u256; + /// Matches any expression. Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {} // Matches a specific constant value. diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index d7bd36fe9..706176456 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -28,7 +28,7 @@ using namespace std; using namespace dev; using namespace yul; -using OptionalStatements = boost::optional>; +using OptionalStatements = std::optional>; namespace { @@ -54,7 +54,7 @@ OptionalStatements replaceConstArgSwitch(Switch& _switchStmt, u256 const& _const if (matchingCaseBlock) return make_vector(std::move(*matchingCaseBlock)); else - return {{}}; + return optional>{vector{}}; } } @@ -80,8 +80,8 @@ void StructuralSimplifier::simplify(std::vector& _statements) return {}; }, [&](Switch& _switchStmt) -> OptionalStatements { - if (boost::optional const constExprVal = hasLiteralValue(*_switchStmt.expression)) - return replaceConstArgSwitch(_switchStmt, constExprVal.get()); + if (std::optional const constExprVal = hasLiteralValue(*_switchStmt.expression)) + return replaceConstArgSwitch(_switchStmt, constExprVal.value()); return {}; }, [&](ForLoop& _forLoop) -> OptionalStatements { @@ -107,7 +107,7 @@ void StructuralSimplifier::simplify(std::vector& _statements) bool StructuralSimplifier::expressionAlwaysTrue(Expression const& _expression) { - if (boost::optional value = hasLiteralValue(_expression)) + if (std::optional value = hasLiteralValue(_expression)) return *value != 0; else return false; @@ -115,16 +115,16 @@ bool StructuralSimplifier::expressionAlwaysTrue(Expression const& _expression) bool StructuralSimplifier::expressionAlwaysFalse(Expression const& _expression) { - if (boost::optional value = hasLiteralValue(_expression)) + if (std::optional value = hasLiteralValue(_expression)) return *value == 0; else return false; } -boost::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const +std::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const { if (_expression.type() == typeid(Literal)) return valueOfLiteral(boost::get(_expression)); else - return boost::optional(); + return std::optional(); } diff --git a/libyul/optimiser/StructuralSimplifier.h b/libyul/optimiser/StructuralSimplifier.h index 28917bff9..aa1f38a90 100644 --- a/libyul/optimiser/StructuralSimplifier.h +++ b/libyul/optimiser/StructuralSimplifier.h @@ -51,7 +51,7 @@ private: void simplify(std::vector& _statements); bool expressionAlwaysTrue(Expression const& _expression); bool expressionAlwaysFalse(Expression const& _expression); - boost::optional hasLiteralValue(Expression const& _expression) const; + std::optional hasLiteralValue(Expression const& _expression) const; }; } diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index a23f1f24e..50bfc6603 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +101,7 @@ void OptimiserSuite::run( BlockFlattener::name, ControlFlowSimplifier::name, LiteralRematerialiser::name, + ConditionalUnsimplifier::name, StructuralSimplifier::name, ControlFlowSimplifier::name, ForLoopConditionIntoBody::name, @@ -129,8 +134,13 @@ void OptimiserSuite::run( } { - // still in SSA, perform structural simplification + // perform structural simplification suite.runSequence({ + CommonSubexpressionEliminator::name, + ConditionalSimplifier::name, + LiteralRematerialiser::name, + ConditionalUnsimplifier::name, + StructuralSimplifier::name, LiteralRematerialiser::name, ForLoopConditionOutOfBody::name, ControlFlowSimplifier::name, @@ -199,6 +209,10 @@ void OptimiserSuite::run( { // SSA plus simplify suite.runSequence({ + ConditionalSimplifier::name, + LiteralRematerialiser::name, + ConditionalUnsimplifier::name, + CommonSubexpressionEliminator::name, SSATransform::name, RedundantAssignEliminator::name, RedundantAssignEliminator::name, @@ -314,6 +328,8 @@ map> const& OptimiserSuite::allSteps() instance = optimiserStepCollection< BlockFlattener, CommonSubexpressionEliminator, + ConditionalSimplifier, + ConditionalUnsimplifier, ControlFlowSimplifier, DeadCodeEliminator, EquivalentFunctionCombiner, @@ -343,10 +359,25 @@ map> const& OptimiserSuite::allSteps() void OptimiserSuite::runSequence(std::vector const& _steps, Block& _ast) { + unique_ptr copy; + if (m_debug == Debug::PrintChanges) + copy = make_unique(boost::get(ASTCopier{}(_ast))); for (string const& step: _steps) { if (m_debug == Debug::PrintStep) cout << "Running " << step << endl; allSteps().at(step)->run(m_context, _ast); + if (m_debug == Debug::PrintChanges) + { + // TODO should add switch to also compare variable names! + if (SyntacticallyEqual{}.statementEqual(_ast, *copy)) + cout << "== Running " << step << " did not cause changes." << endl; + else + { + cout << "== Running " << step << " changed the AST." << endl; + cout << AsmPrinter{}(_ast) << endl; + copy = make_unique(boost::get(ASTCopier{}(_ast))); + } + } } } diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index f47574799..8dce75f9d 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -48,7 +48,8 @@ public: enum class Debug { None, - PrintStep + PrintStep, + PrintChanges }; static void run( Dialect const& _dialect, diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index 5a18dfa32..45b53ee61 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -29,7 +29,7 @@ void VarDeclInitializer::operator()(Block& _block) { ASTModifier::operator()(_block); - using OptionalStatements = boost::optional>; + using OptionalStatements = std::optional>; GenericFallbackReturnsVisitor visitor{ [](VariableDeclaration& _varDecl) -> OptionalStatements { diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 0f9a8096e..340aba47e 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -17,7 +17,7 @@ fi FORMATERROR=$( ( - git grep -nIE "\<(if|for)\(" -- '*.h' '*.cpp' # no space after "if" or "for" + git grep -nIE "\<(if|for|while|switch)\(" -- '*.h' '*.cpp' # no space after "if", "for", "while" or "switch" git grep -nIE "\\s*\([^=]*\>\s:\s.*\)" -- '*.h' '*.cpp' # no space before range based for-loop git grep -nIE "\\s*\(.*\)\s*\{\s*$" -- '*.h' '*.cpp' # "{\n" on same line as "if" / "for" git grep -nIE "[,\(<]\s*const " -- '*.h' '*.cpp' # const on left side of type diff --git a/scripts/deps-ppa/static_z3.sh b/scripts/deps-ppa/static_z3.sh index b971fdd24..9f39a79c9 100755 --- a/scripts/deps-ppa/static_z3.sh +++ b/scripts/deps-ppa/static_z3.sh @@ -25,9 +25,9 @@ set -ev keyid=70D110489D66E2F6 email=builds@ethereum.org packagename=libz3-static-dev -version=4.8.5 +version=4.8.6 -DISTRIBUTIONS="bionic disco" +DISTRIBUTIONS="bionic disco eoan" for distribution in $DISTRIBUTIONS do @@ -40,7 +40,7 @@ pparepo=cpp-build-deps ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/${pparepo}/+files # Fetch source -git clone --depth 1 --branch Z3-${version} https://github.com/Z3Prover/z3.git +git clone --depth 1 --branch z3-${version} https://github.com/Z3Prover/z3.git cd z3 debversion="$version" diff --git a/scripts/install_evmone.sh b/scripts/install_evmone.sh deleted file mode 100755 index 0a460db90..000000000 --- a/scripts/install_evmone.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env sh - -# This is suitable for CIs, not end users. -# This script is invoked by ossfuzz build CI. -set -e - -TEMPDIR="src" -cd / -mkdir -p $TEMPDIR -cd $TEMPDIR -git clone --recurse-submodules https://github.com/chfast/evmone.git -( - cd evmone - mkdir build - cd build - cmake .. -DBUILD_SHARED_LIBS=OFF - make -j2 -) - -git clone https://github.com/chfast/intx.git -( - cd intx - mkdir build - cd build - cmake .. -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF - make -j2 -) - -git clone https://github.com/chfast/ethash.git -( - cd ethash - mkdir build - cd build - cmake .. -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF - make -j2 -) diff --git a/scripts/install_libfuzzer.sh b/scripts/install_libfuzzer.sh deleted file mode 100755 index f231403ef..000000000 --- a/scripts/install_libfuzzer.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env sh -set -e -TEMPDIR=$(mktemp -d) -( - cd $TEMPDIR - svn co https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer libfuzzer - mkdir -p build-libfuzzer - cd build-libfuzzer - CXXFLAGS="-O1 -stdlib=libstdc++" - $CXX $CXXFLAGS -std=c++11 -O2 -fPIC -c ../libfuzzer/*.cpp -I../libfuzzer - ar r /usr/lib/libFuzzingEngine.a *.o -) -rm -rf $TEMPDIR diff --git a/scripts/install_lpm.sh b/scripts/install_lpm.sh deleted file mode 100755 index 0bc4f83d2..000000000 --- a/scripts/install_lpm.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env sh -set -e - -TEMPDIR="src" -cd / -mkdir -p $TEMPDIR -( - cd $TEMPDIR - git clone --depth 1 https://github.com/google/libprotobuf-mutator.git - mkdir -p LPM - cd LPM - cmake ../libprotobuf-mutator -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON -DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release && ninja install -) diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 06feb0fc6..9492ee137 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -39,6 +39,7 @@ def extract_test_cases(path): # and abort a line not indented properly. def extract_docs_cases(path): inside = False + extractedLines = [] tests = [] # Collect all snippets of indented blocks @@ -46,15 +47,23 @@ def extract_docs_cases(path): if l != '': if not inside and l.startswith(' '): # start new test - tests += [''] + extractedLines += [''] inside = l.startswith(' ') if inside: - tests[-1] += l + '\n' - # Filter all tests that do not contain Solidity - return [ - test for test in tests - if re.search(r'^ [ ]*(pragma solidity|contract |library |interface )', test, re.MULTILINE) - ] + extractedLines[-1] += l + '\n' + + codeStart = "(pragma solidity|contract.*{|library.*{|interface.*{)" + + # Filter all tests that do not contain Solidity or are intended incorrectly. + for lines in extractedLines: + if re.search(r'^\s{0,3}' + codeStart, lines, re.MULTILINE): + print("Intendation error in " + path + ":") + print(lines) + exit(1) + if re.search(r'^\s{4}' + codeStart, lines, re.MULTILINE): + tests.append(lines) + + return tests def write_cases(f, tests): cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower() diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 78ea3118b..fcf3a8102 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -57,7 +57,7 @@ packagename=solc static_build_distribution=disco -DISTRIBUTIONS="bionic disco" +DISTRIBUTIONS="bionic disco eoan" if is_release then diff --git a/scripts/tests.sh b/scripts/tests.sh index 32fc4134c..01577af1c 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -36,11 +36,6 @@ source "${REPO_ROOT}/scripts/common.sh" WORKDIR=`mktemp -d` CMDLINE_PID= -if [[ "$OSTYPE" == "darwin"* ]] -then - SMT_FLAGS="--no-smt" -fi - cleanup() { # ensure failing commands don't cause termination during cleanup (especially within safe_kill) set +e diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index a35a4f49d..5c8a06e58 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -67,7 +67,7 @@ echo -en 'travis_fold:start:compiling_boost\\r' test -e "$WORKSPACE"/boost_1_70_0_install/include/boost/version.hpp || ( cd "$WORKSPACE"/boost_1_70_0 ./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \ - --with-system --with-regex --with-filesystem --with-test --with-program_options cxxflags="-Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \ + --with-system --with-filesystem --with-test --with-program_options cxxflags="-Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \ --prefix="$WORKSPACE"/boost_1_70_0_install install ) ln -sf "$WORKSPACE"/boost_1_70_0_install/lib/* /emsdk_portable/sdk/system/lib diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 79d85d82f..34b7c67c5 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -294,7 +294,7 @@ void CommandLineInterface::handleOpcode(string const& _contract) else { sout() << "Opcodes: " << endl; - sout() << dev::eth::disassemble(m_compiler->object(_contract).bytecode); + sout() << std::uppercase << dev::eth::disassemble(m_compiler->object(_contract).bytecode); sout() << endl; } } @@ -318,11 +318,18 @@ void CommandLineInterface::handleEWasm(string const& _contractName) if (m_args.count(g_argEWasm)) { if (m_args.count(g_argOutputDir)) + { createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->eWasm(_contractName)); + createFile( + m_compiler->filesystemFriendlyName(_contractName) + ".wasm", + asString(m_compiler->eWasmObject(_contractName).bytecode) + ); + } else { - sout() << "eWasm: " << endl; + sout() << "EWasm text: " << endl; sout() << m_compiler->eWasm(_contractName) << endl; + sout() << "EWasm binary (hex): " << m_compiler->eWasmObject(_contractName).toHex() << endl; } } } @@ -869,7 +876,7 @@ bool CommandLineInterface::processInput() if (m_args.count(g_strEVMVersion)) { string versionOptionStr = m_args[g_strEVMVersion].as(); - boost::optional versionOption = langutil::EVMVersion::fromString(versionOptionStr); + std::optional versionOption = langutil::EVMVersion::fromString(versionOptionStr); if (!versionOption) { serr() << "Invalid option for --evm-version: " << versionOptionStr << endl; diff --git a/test/.solhint.json b/test/.solhint.json index 0a8b2adfc..b15f97815 100644 --- a/test/.solhint.json +++ b/test/.solhint.json @@ -5,4 +5,4 @@ "compiler-fixed": "off", "no-inline-assembly": "off" } -} \ No newline at end of file +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eb72ae0be..387d6e515 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,34 +1,159 @@ -file(GLOB sources "*.cpp") -file(GLOB headers "*.h") +set(sources + boostTest.cpp + Common.cpp + Common.h + EVMHost.cpp + EVMHost.h + ExecutionFramework.cpp + ExecutionFramework.h + InteractiveTests.h + Metadata.cpp + Metadata.h + Options.cpp + Options.h + TestCase.cpp + TestCase.h +) +detect_stray_source_files("${sources}" ".") -file(GLOB contracts_sources "contracts/*.cpp") -file(GLOB contracts_headers "contracts/*.h") -file(GLOB libdevcore_sources "libdevcore/*.cpp") -file(GLOB libdevcore_headers "libdevcore/*.h") -file(GLOB liblangutil_sources "liblangutil/*.cpp") -file(GLOB liblangutil_headers "liblangutil/*.h") -file(GLOB libevmasm_sources "libevmasm/*.cpp") -file(GLOB libevmasm_headers "libevmasm/*.h") -file(GLOB libyul_sources "libyul/*.cpp") -file(GLOB libyul_headers "libyul/*.h") -if (LLL) - file(GLOB liblll_sources "liblll/*.cpp") - file(GLOB liblll_headers "liblll/*.h") -endif() -file(GLOB libsolidity_sources "libsolidity/*.cpp") -file(GLOB libsolidity_headers "libsolidity/*.h") -file(GLOB libsolidity_util_sources "libsolidity/util/*.cpp") -file(GLOB libsolidity_util_headers "libsolidity/util/*.h") +set(contracts_sources + contracts/AuctionRegistrar.cpp + contracts/ContractInterface.h + contracts/FixedFeeRegistrar.cpp + contracts/Wallet.cpp +) +detect_stray_source_files("${contracts_sources}" "contracts/") -add_executable(soltest ${sources} ${headers} - ${contracts_sources} ${contracts_headers} - ${libdevcore_sources} ${libdevcore_headers} - ${liblangutil_sources} ${liblangutil_headers} - ${libevmasm_sources} ${libevmasm_headers} - ${libyul_sources} ${libyul_headers} - ${liblll_sources} ${liblll_headers} - ${libsolidity_sources} ${libsolidity_headers} - ${libsolidity_util_sources} ${libsolidity_util_headers} +set(libdevcore_sources + libdevcore/Checksum.cpp + libdevcore/CommonData.cpp + libdevcore/IndentedWriter.cpp + libdevcore/IpfsHash.cpp + libdevcore/IterateReplacing.cpp + libdevcore/JSON.cpp + libdevcore/Keccak256.cpp + libdevcore/StringUtils.cpp + libdevcore/SwarmHash.cpp + libdevcore/UTF8.cpp + libdevcore/Whiskers.cpp +) +detect_stray_source_files("${libdevcore_sources}" "libdevcore/") + +set(libevmasm_sources + libevmasm/Assembler.cpp + libevmasm/Optimiser.cpp +) +detect_stray_source_files("${libevmasm_sources}" "libevmasm/") + +set(liblangutil_sources + liblangutil/CharStream.cpp + liblangutil/SourceLocation.cpp +) +detect_stray_source_files("${liblangutil_sources}" "liblangutil/") + +if(LLL) + set (liblll_sources + liblll/Compiler.cpp + liblll/EndToEndTest.cpp + liblll/ExecutionFramework.cpp + liblll/ExecutionFramework.h + liblll/LLL_ENS.cpp + liblll/LLL_ERC20.cpp + liblll/Parser.cpp + ) + detect_stray_source_files("${liblll_sources}" "liblll/") +endif(LLL) + +set(libsolidity_sources + libsolidity/ABIDecoderTests.cpp + libsolidity/ABIEncoderTests.cpp + libsolidity/ABIJsonTest.cpp + libsolidity/ABIJsonTest.h + libsolidity/ABITestsCommon.h + libsolidity/AnalysisFramework.cpp + libsolidity/AnalysisFramework.h + libsolidity/Assembly.cpp + libsolidity/ASTJSONTest.cpp + libsolidity/ASTJSONTest.h + libsolidity/ErrorCheck.cpp + libsolidity/ErrorCheck.h + libsolidity/GasCosts.cpp + libsolidity/GasMeter.cpp + libsolidity/GasTest.cpp + libsolidity/GasTest.h + libsolidity/Imports.cpp + libsolidity/InlineAssembly.cpp + libsolidity/LibSolc.cpp + libsolidity/Metadata.cpp + libsolidity/SemanticTest.cpp + libsolidity/SemanticTest.h + libsolidity/SemVerMatcher.cpp + libsolidity/SMTChecker.cpp + libsolidity/SMTCheckerJSONTest.cpp + libsolidity/SMTCheckerJSONTest.h + libsolidity/SolidityCompiler.cpp + libsolidity/SolidityEndToEndTest.cpp + libsolidity/SolidityExecutionFramework.cpp + libsolidity/SolidityExecutionFramework.h + libsolidity/SolidityExpressionCompiler.cpp + libsolidity/SolidityNameAndTypeResolution.cpp + libsolidity/SolidityNatspecJSON.cpp + libsolidity/SolidityOptimizer.cpp + libsolidity/SolidityParser.cpp + libsolidity/SolidityScanner.cpp + libsolidity/SolidityTypes.cpp + libsolidity/StandardCompiler.cpp + libsolidity/SyntaxTest.cpp + libsolidity/SyntaxTest.h + libsolidity/ViewPureChecker.cpp +) +detect_stray_source_files("${libsolidity_sources}" "libsolidity/") + +set(libsolidity_util_sources + libsolidity/util/BytesUtils.cpp + libsolidity/util/BytesUtils.h + libsolidity/util/ContractABIUtils.cpp + libsolidity/util/ContractABIUtils.h + libsolidity/util/SoltestErrors.h + libsolidity/util/SoltestTypes.h + libsolidity/util/TestFileParser.cpp + libsolidity/util/TestFileParser.h + libsolidity/util/TestFileParserTests.cpp + libsolidity/util/TestFunctionCall.cpp + libsolidity/util/TestFunctionCall.h + libsolidity/util/TestFunctionCallTests.cpp +) +detect_stray_source_files("${libsolidity_util_sources}" "libsolidity/util/") + +set(libyul_sources + libyul/Common.cpp + libyul/Common.h + libyul/CompilabilityChecker.cpp + libyul/FunctionSideEffects.cpp + libyul/FunctionSideEffects.h + libyul/Inliner.cpp + libyul/Metrics.cpp + libyul/ObjectCompilerTest.cpp + libyul/ObjectCompilerTest.h + libyul/ObjectParser.cpp + libyul/Parser.cpp + libyul/StackReuseCodegen.cpp + libyul/YulInterpreterTest.cpp + libyul/YulInterpreterTest.h + libyul/YulOptimizerTest.cpp + libyul/YulOptimizerTest.h +) +detect_stray_source_files("${libyul_sources}" "libyul/") + +add_executable(soltest ${sources} + ${contracts_sources} + ${libdevcore_sources} + ${liblangutil_sources} + ${libevmasm_sources} + ${libyul_sources} + ${liblll_sources} + ${libsolidity_sources} + ${libsolidity_util_sources} ) target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm devcore Boost::boost Boost::program_options Boost::unit_test_framework evmc) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 19d8f6f77..b7755f28f 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -21,7 +21,6 @@ #include -#include #include #include @@ -42,9 +41,17 @@ evmc::vm* EVMHost::getVM(string const& _path) if (!theVM && !_path.empty()) { evmc_loader_error_code errorCode = {}; - evmc_instance* vm = evmc_load_and_create(_path.c_str(), &errorCode); + evmc_instance* vm = evmc_load_and_configure(_path.c_str(), &errorCode); if (vm && errorCode == EVMC_LOADER_SUCCESS) - theVM = make_unique(vm); + { + if (evmc_vm_has_capability(vm, EVMC_CAPABILITY_EVM1)) + theVM = make_unique(vm); + else + { + evmc_destroy(vm); + cerr << "VM loaded does not support EVM1" << endl; + } + } else { cerr << "Error loading VM from " << _path; @@ -83,27 +90,27 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm): m_evmVersion = EVMC_PETERSBURG; } -evmc_storage_status EVMHost::set_storage(const evmc_address& _addr, const evmc_bytes32& _key, const evmc_bytes32& _value) noexcept +evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept { - evmc_bytes32 previousValue = m_state.accounts[_addr].storage[_key]; + evmc::bytes32 previousValue = m_state.accounts[_addr].storage[_key]; m_state.accounts[_addr].storage[_key] = _value; // TODO EVMC_STORAGE_MODIFIED_AGAIN should be also used if (previousValue == _value) return EVMC_STORAGE_UNCHANGED; - else if (previousValue == evmc_bytes32{}) + else if (previousValue == evmc::bytes32{}) return EVMC_STORAGE_ADDED; - else if (_value == evmc_bytes32{}) + else if (_value == evmc::bytes32{}) return EVMC_STORAGE_DELETED; else return EVMC_STORAGE_MODIFIED; } -void EVMHost::selfdestruct(const evmc_address& _addr, const evmc_address& _beneficiary) noexcept +void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _beneficiary) noexcept { // TODO actual selfdestruct is even more complicated. - evmc_uint256be balance = m_state.accounts[_addr].balance; + evmc::uint256be balance = m_state.accounts[_addr].balance; m_state.accounts.erase(_addr); m_state.accounts[_beneficiary].balance = balance; } @@ -182,7 +189,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept destination.balance = convertToEVMC(u256(convertFromEVMC(destination.balance)) + value); } - evmc_address currentAddress = m_currentAddress; + evmc::address currentAddress = m_currentAddress; m_currentAddress = message.destination; evmc::result result = m_vm->execute(*this, m_evmVersion, message, code.data(), code.size()); m_currentAddress = currentAddress; @@ -223,16 +230,16 @@ evmc_tx_context EVMHost::get_tx_context() noexcept return ctx; } -evmc_bytes32 EVMHost::get_block_hash(int64_t _number) noexcept +evmc::bytes32 EVMHost::get_block_hash(int64_t _number) noexcept { return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number); } void EVMHost::emit_log( - evmc_address const& _addr, + evmc::address const& _addr, uint8_t const* _data, size_t _dataSize, - evmc_bytes32 const _topics[], + evmc::bytes32 const _topics[], size_t _topicsCount ) noexcept { @@ -245,27 +252,27 @@ void EVMHost::emit_log( } -Address EVMHost::convertFromEVMC(evmc_address const& _addr) +Address EVMHost::convertFromEVMC(evmc::address const& _addr) { return Address(bytes(begin(_addr.bytes), end(_addr.bytes))); } -evmc_address EVMHost::convertToEVMC(Address const& _addr) +evmc::address EVMHost::convertToEVMC(Address const& _addr) { - evmc_address a; + evmc::address a; for (size_t i = 0; i < 20; ++i) a.bytes[i] = _addr[i]; return a; } -h256 EVMHost::convertFromEVMC(evmc_bytes32 const& _data) +h256 EVMHost::convertFromEVMC(evmc::bytes32 const& _data) { return h256(bytes(begin(_data.bytes), end(_data.bytes))); } -evmc_bytes32 EVMHost::convertToEVMC(h256 const& _data) +evmc::bytes32 EVMHost::convertToEVMC(h256 const& _data) { - evmc_bytes32 d; + evmc::bytes32 d; for (size_t i = 0; i < 32; ++i) d.bytes[i] = _data[i]; return d; diff --git a/test/EVMHost.h b/test/EVMHost.h index f66bdcc14..c01d27ad0 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -23,7 +23,6 @@ #include #include -#include #include @@ -47,11 +46,11 @@ public: struct Account { - evmc_uint256be balance = {}; + evmc::uint256be balance = {}; size_t nonce = 0; bytes code; - evmc_bytes32 codeHash = {}; - std::map storage; + evmc::bytes32 codeHash = {}; + std::map storage; }; struct LogEntry @@ -65,11 +64,11 @@ public: { size_t blockNumber; uint64_t timestamp; - std::map accounts; + std::map accounts; std::vector logs; }; - Account* account(evmc_address const& _address) + Account* account(evmc::address const& _address) { // Make all precompiled contracts exist. // Be future-proof and consider everything below 1024 as precompiled contract. @@ -87,12 +86,12 @@ public: m_state.logs.clear(); } - bool account_exists(evmc_address const& _addr) noexcept final + bool account_exists(evmc::address const& _addr) noexcept final { return account(_addr) != nullptr; } - evmc_bytes32 get_storage(evmc_address const& _addr, evmc_bytes32 const& _key) noexcept final + evmc::bytes32 get_storage(evmc::address const& _addr, evmc::bytes32 const& _key) noexcept final { if (Account* acc = account(_addr)) return acc->storage[_key]; @@ -100,26 +99,26 @@ public: } evmc_storage_status set_storage( - evmc_address const& _addr, - evmc_bytes32 const& _key, - evmc_bytes32 const& _value + evmc::address const& _addr, + evmc::bytes32 const& _key, + evmc::bytes32 const& _value ) noexcept; - evmc_uint256be get_balance(evmc_address const& _addr) noexcept final + evmc::uint256be get_balance(evmc::address const& _addr) noexcept final { if (Account const* acc = account(_addr)) return acc->balance; return {}; } - size_t get_code_size(evmc_address const& _addr) noexcept final + size_t get_code_size(evmc::address const& _addr) noexcept final { if (Account const* acc = account(_addr)) return acc->code.size(); return 0; } - evmc_bytes32 get_code_hash(evmc_address const& _addr) noexcept final + evmc::bytes32 get_code_hash(evmc::address const& _addr) noexcept final { if (Account const* acc = account(_addr)) return acc->codeHash; @@ -127,7 +126,7 @@ public: } size_t copy_code( - evmc_address const& _addr, + evmc::address const& _addr, size_t _codeOffset, uint8_t* _bufferData, size_t _bufferSize @@ -140,31 +139,31 @@ public: return i; } - void selfdestruct(evmc_address const& _addr, evmc_address const& _beneficiary) noexcept; + void selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept; evmc::result call(evmc_message const& _message) noexcept; evmc_tx_context get_tx_context() noexcept; - evmc_bytes32 get_block_hash(int64_t number) noexcept; + evmc::bytes32 get_block_hash(int64_t number) noexcept; void emit_log( - evmc_address const& _addr, + evmc::address const& _addr, uint8_t const* _data, size_t _dataSize, - evmc_bytes32 const _topics[], + evmc::bytes32 const _topics[], size_t _topicsCount ) noexcept; - static Address convertFromEVMC(evmc_address const& _addr); - static evmc_address convertToEVMC(Address const& _addr); - static h256 convertFromEVMC(evmc_bytes32 const& _data); - static evmc_bytes32 convertToEVMC(h256 const& _data); + static Address convertFromEVMC(evmc::address const& _addr); + static evmc::address convertToEVMC(Address const& _addr); + static h256 convertFromEVMC(evmc::bytes32 const& _data); + static evmc::bytes32 convertToEVMC(h256 const& _data); State m_state; - evmc_address m_currentAddress = {}; - evmc_address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878")); + evmc::address m_currentAddress = {}; + evmc::address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878")); private: evmc::result precompileECRecover(evmc_message const& _message) noexcept; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index a6b8188a1..9c08394ef 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -26,7 +26,6 @@ #include #include -#include #include @@ -235,7 +234,7 @@ bool ExecutionFramework::storageEmpty(Address const& _addr) if (EVMHost::Account const* acc = m_evmHost->account(EVMHost::convertToEVMC(_addr))) { for (auto const& entry: acc->storage) - if (!(entry.second == evmc_bytes32{})) + if (!(entry.second == evmc::bytes32{})) return false; } return true; diff --git a/test/Metadata.cpp b/test/Metadata.cpp index bdbf427e9..06f29ff6f 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -150,7 +150,7 @@ private: bytes const& m_metadata; }; -boost::optional> parseCBORMetadata(bytes const& _metadata) +std::optional> parseCBORMetadata(bytes const& _metadata) { try { diff --git a/test/Metadata.h b/test/Metadata.h index 4c7a10b31..a52ccf2e9 100644 --- a/test/Metadata.h +++ b/test/Metadata.h @@ -21,9 +21,8 @@ #include -#include - #include +#include #include namespace dev @@ -48,7 +47,7 @@ std::string bytecodeSansMetadata(std::string const& _bytecode); /// - bytes into hex strings /// - booleans into "true"/"false" strings /// - everything else is invalid -boost::optional> parseCBORMetadata(bytes const& _metadata); +std::optional> parseCBORMetadata(bytes const& _metadata); /// Expects a serialised metadata JSON and returns true if the /// content is valid metadata. diff --git a/test/TestCase.cpp b/test/TestCase.cpp index 88d7f7862..c264da100 100644 --- a/test/TestCase.cpp +++ b/test/TestCase.cpp @@ -184,7 +184,7 @@ bool EVMVersionRestrictedTestCase::validateSettings(langutil::EVMVersion _evmVer break; versionString = versionString.substr(versionBegin); - boost::optional version = langutil::EVMVersion::fromString(versionString); + std::optional version = langutil::EVMVersion::fromString(versionString); if (!version) throw runtime_error("Invalid EVM version: \"" + versionString + "\""); diff --git a/test/cmdlineTests/require_overload/err b/test/cmdlineTests/require_overload/err new file mode 100644 index 000000000..42243e7af --- /dev/null +++ b/test/cmdlineTests/require_overload/err @@ -0,0 +1,5 @@ +require_overload/input.sol:4:9: Error: No matching declaration found after argument-dependent lookup. + require(this); + ^-----^ +Candidate: function require(bool) +Candidate: function require(bool, string memory) diff --git a/test/cmdlineTests/require_overload/exit b/test/cmdlineTests/require_overload/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/require_overload/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/require_overload/input.sol b/test/cmdlineTests/require_overload/input.sol new file mode 100644 index 000000000..6d48ac24a --- /dev/null +++ b/test/cmdlineTests/require_overload/input.sol @@ -0,0 +1,6 @@ +pragma solidity >=0.0; +contract C { + function f() public pure { + require(this); + } +} diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index 17469c367..7f7787e96 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -1,103 +1,29 @@ {"contracts":{"A":{"C":{"ewasm":{"wast":"(module - (import \"ethereum\" \"revert\" (func $eth.revert (param i32 i32))) - (memory $memory (export \"memory\") 1) - (export \"main\" (func $main)) - -(func $main - (local $_1 i64) - (local $pos i64) - (local $_2 i64) - (local $hi i64) - (local $y i64) - (local $hi_1 i64) - (set_local $_1 (i64.const 0)) - (set_local $pos (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (i64.const 64))) - (set_local $_2 (i64.const 65280)) - (set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (get_local $_2)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (get_local $_1) (i64.const 16)))) (i64.const 32))) - (set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $_1) (i64.const 32))))) - (i64.store (get_local $pos) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 8)) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 16)) (get_local $y)) (set_local $hi_1 (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (i64.const 64) (i64.const 8)) (get_local $_2)) (i64.and (i64.shr_u (i64.const 64) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (i64.const 64) (i64.const 16)))) (i64.const 32))) - (i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 64) (i64.const 32))))) (call $eth.revert (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1))) -) - -(func $u256_to_i32 - (param $x1 i64) - (param $x2 i64) - (param $x3 i64) - (param $x4 i64) - (result i64) - (local $v i64) - (if (i64.ne (get_local $v) (i64.or (i64.or (get_local $x1) (get_local $x2)) (get_local $x3))) (then - (unreachable))) - (if (i64.ne (get_local $v) (i64.shr_u (get_local $x4) (i64.const 32))) (then - (unreachable))) - (set_local $v (get_local $x4)) - (get_local $v) -) - -(func $endian_swap_16 - (param $x i64) - (result i64) - (local $y i64) - (set_local $y (i64.or (i64.and (i64.shl (get_local $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $x) (i64.const 8)) (i64.const 255)))) - (get_local $y) -) - -(func $endian_swap_32 - (param $x i64) - (result i64) - (local $y i64) - (local $hi i64) - (set_local $hi (i64.shl (call $endian_swap_16 (get_local $x)) (i64.const 16))) - (set_local $y (i64.or (get_local $hi) (call $endian_swap_16 (i64.shr_u (get_local $x) (i64.const 16))))) - (get_local $y) -) - -) -(module + ;; sub-module \"C_2_deployed\" will be encoded as custom section in binary here, but is skipped in text mode. (import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32))) (import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32))) (memory $memory (export \"memory\") 1) (export \"main\" (func $main)) - (global $global_ (mut i64) (i64.const 0)) - (global $global__1 (mut i64) (i64.const 0)) - (global $global__2 (mut i64) (i64.const 0)) (func $main (local $_1 i64) - (local $pos i64) + (local $_2 i64) (local $hi i64) (local $y i64) (local $hi_1 i64) - (local $hi_2 i64) - (local $_2 i64) (local $_3 i64) - (local $_4 i64) - (local $_5 i64) - (local $_6 i64) - (local $_7 i64) - (local $_8 i64) - (local $_9 i64) - (set_local $_1 (i64.const 0)) - (set_local $pos (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (i64.const 64))) - (set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (get_local $_1) (i64.const 16)))) (i64.const 32))) - (set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $_1) (i64.const 32))))) - (i64.store (get_local $pos) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 8)) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 16)) (get_local $y)) (set_local $hi_1 (i64.shl (call $endian_swap_16 (i64.const 64)) (i64.const 16))) - (set_local $hi_2 (i64.shl (i64.or (get_local $hi_1) (call $endian_swap_16 (i64.shr_u (i64.const 64) (i64.const 16)))) (i64.const 32))) - (i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_2) (call $endian_swap_32 (i64.shr_u (i64.const 64) (i64.const 32))))) (block - (set_local $_2 (datasize \"C_2_deployed\")) - (set_local $_3 (get_global $global_)) - (set_local $_4 (get_global $global__1)) - (set_local $_5 (get_global $global__2)) - - ) - (block - (set_local $_6 (dataoffset \"C_2_deployed\")) - (set_local $_7 (get_global $global_)) - (set_local $_8 (get_global $global__1)) - (set_local $_9 (get_global $global__2)) - - ) - (call $eth.codeCopy (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_6) (get_local $_7) (get_local $_8) (get_local $_9)) (call $u256_to_i32 (get_local $_2) (get_local $_3) (get_local $_4) (get_local $_5))) (call $eth.finish (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_2) (get_local $_3) (get_local $_4) (get_local $_5))) + (local.set $_1 (i64.const 0)) + (local.set $_2 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64)) (i64.const 64))) + (local.set $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (local.get $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) + (i64.store (i32.wrap_i64 (local.get $_2)) (local.get $y)) + (i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 8))) (local.get $y)) + (i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 16))) (local.get $y)) + (local.set $hi_1 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) + (i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 24))) (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) + (local.set $_3 (datasize \"C_2_deployed\")) + (call $eth.codeCopy (i32.wrap_i64 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (i64.const 64))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\"))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)))) + (call $eth.finish (i32.wrap_i64 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (i64.const 64))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)))) ) (func $u256_to_i32 @@ -107,20 +33,20 @@ (param $x4 i64) (result i64) (local $v i64) - (if (i64.ne (get_local $v) (i64.or (i64.or (get_local $x1) (get_local $x2)) (get_local $x3))) (then + (if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then (unreachable))) - (if (i64.ne (get_local $v) (i64.shr_u (get_local $x4) (i64.const 32))) (then + (if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then (unreachable))) - (set_local $v (get_local $x4)) - (get_local $v) + (local.set $v (local.get $x4)) + (local.get $v) ) (func $endian_swap_16 (param $x i64) (result i64) (local $y i64) - (set_local $y (i64.or (i64.and (i64.shl (get_local $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $x) (i64.const 8)) (i64.const 255)))) - (get_local $y) + (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) + (local.get $y) ) (func $endian_swap_32 @@ -128,9 +54,9 @@ (result i64) (local $y i64) (local $hi i64) - (set_local $hi (i64.shl (call $endian_swap_16 (get_local $x)) (i64.const 16))) - (set_local $y (i64.or (get_local $hi) (call $endian_swap_16 (i64.shr_u (get_local $x) (i64.const 16))))) - (get_local $y) + (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + (local.get $y) ) ) diff --git a/test/cmdlineTests/storage_layout_bytes/input.json b/test/cmdlineTests/storage_layout_bytes/input.json new file mode 100644 index 000000000..fe1468c53 --- /dev/null +++ b/test/cmdlineTests/storage_layout_bytes/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { bytes s1 = \"test\"; bytes s2; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_bytes/output.json b/test/cmdlineTests/storage_layout_bytes/output.json new file mode 100644 index 000000000..d2829f360 --- /dev/null +++ b/test/cmdlineTests/storage_layout_bytes/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_bytes_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_bytes_storage"}],"types":{"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { bytes s1 = \"test\"; bytes s2; } +^-----------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":43,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_dyn_array/input.json b/test/cmdlineTests/storage_layout_dyn_array/input.json new file mode 100644 index 000000000..64ab3e20b --- /dev/null +++ b/test/cmdlineTests/storage_layout_dyn_array/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { uint[] array1; bool[] array2; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_dyn_array/output.json b/test/cmdlineTests/storage_layout_dyn_array/output.json new file mode 100644 index 000000000..51b3ac1a1 --- /dev/null +++ b/test/cmdlineTests/storage_layout_dyn_array/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"array1","offset":0,"slot":"0","type":"t_array(t_uint256)dyn_storage"},{"astId":6,"contract":"fileA:A","label":"array2","offset":0,"slot":"1","type":"t_array(t_bool)dyn_storage"}],"types":{"t_array(t_bool)dyn_storage":{"base":"t_bool","encoding":"dynamic_array","label":"bool[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { uint[] array1; bool[] array2; } +^------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":44,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_many/input.json b/test/cmdlineTests/storage_layout_many/input.json new file mode 100644 index 000000000..7ea41f211 --- /dev/null +++ b/test/cmdlineTests/storage_layout_many/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; mapping (uint => mapping (address => bool)) map; uint[] array; string s1; bytes b1; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_many/output.json b/test/cmdlineTests/storage_layout_many/output.json new file mode 100644 index 000000000..a2b3f8d2d --- /dev/null +++ b/test/cmdlineTests/storage_layout_many/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"},{"astId":26,"contract":"fileA:A","label":"map","offset":0,"slot":"7","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"},{"astId":29,"contract":"fileA:A","label":"array","offset":0,"slot":"8","type":"t_array(t_uint256)dyn_storage"},{"astId":31,"contract":"fileA:A","label":"s1","offset":0,"slot":"9","type":"t_string_storage"},{"astId":33,"contract":"fileA:A","label":"b1","offset":0,"slot":"10","type":"t_bytes_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { struct S { uint128 a; ... int[] array; string s1; bytes b1; } +^-------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":206,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_mapping/input.json b/test/cmdlineTests/storage_layout_mapping/input.json new file mode 100644 index 000000000..6292391d5 --- /dev/null +++ b/test/cmdlineTests/storage_layout_mapping/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_mapping/output.json b/test/cmdlineTests/storage_layout_mapping/output.json new file mode 100644 index 000000000..4696d46c8 --- /dev/null +++ b/test/cmdlineTests/storage_layout_mapping/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":10,"contract":"fileA:A","label":"map","offset":0,"slot":"2","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; } +^-----------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":79,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_smoke/input.json b/test/cmdlineTests/storage_layout_smoke/input.json new file mode 100644 index 000000000..47722e759 --- /dev/null +++ b/test/cmdlineTests/storage_layout_smoke/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_smoke/output.json b/test/cmdlineTests/storage_layout_smoke/output.json new file mode 100644 index 000000000..08191bc5d --- /dev/null +++ b/test/cmdlineTests/storage_layout_smoke/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { } +^------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json b/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json new file mode 100644 index 000000000..b31f3dd9d --- /dev/null +++ b/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json @@ -0,0 +1,19 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + }, + "fileB": { + "content": "contract A { uint x; uint y; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json b/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json new file mode 100644 index 000000000..761cf8334 --- /dev/null +++ b/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { } +^------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0},"fileB":{"id":1}}} diff --git a/test/cmdlineTests/storage_layout_string/input.json b/test/cmdlineTests/storage_layout_string/input.json new file mode 100644 index 000000000..834617b3a --- /dev/null +++ b/test/cmdlineTests/storage_layout_string/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { string s1 = \"test\"; string s2; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_string/output.json b/test/cmdlineTests/storage_layout_string/output.json new file mode 100644 index 000000000..19066171e --- /dev/null +++ b/test/cmdlineTests/storage_layout_string/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_string_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { string s1 = \"test\"; string s2; } +^-------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":45,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_struct/input.json b/test/cmdlineTests/storage_layout_struct/input.json new file mode 100644 index 000000000..31ab020cf --- /dev/null +++ b/test/cmdlineTests/storage_layout_struct/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_struct/output.json b/test/cmdlineTests/storage_layout_struct/output.json new file mode 100644 index 000000000..a925c99c0 --- /dev/null +++ b/test/cmdlineTests/storage_layout_struct/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"7","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"b","offset":0,"slot":"1","type":"t_uint256"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"2","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"4","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"160"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; } +^------------------------------------------------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":116,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_struct_packed/input.json b/test/cmdlineTests/storage_layout_struct_packed/input.json new file mode 100644 index 000000000..80c706c7b --- /dev/null +++ b/test/cmdlineTests/storage_layout_struct_packed/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_struct_packed/output.json b/test/cmdlineTests/storage_layout_struct_packed/output.json new file mode 100644 index 000000000..fe9bd0d50 --- /dev/null +++ b/test/cmdlineTests/storage_layout_struct_packed/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; } +^------------------------------------------------------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":122,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_value_types/input.json b/test/cmdlineTests/storage_layout_value_types/input.json new file mode 100644 index 000000000..1477dde08 --- /dev/null +++ b/test/cmdlineTests/storage_layout_value_types/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { uint x; uint y; address addr; uint[2] array; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_value_types/output.json b/test/cmdlineTests/storage_layout_value_types/output.json new file mode 100644 index 000000000..cb43276ed --- /dev/null +++ b/test/cmdlineTests/storage_layout_value_types/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":6,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":10,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { uint x; uint y; address addr; uint[2] array; } +^---------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":59,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_value_types_packed/input.json b/test/cmdlineTests/storage_layout_value_types_packed/input.json new file mode 100644 index 000000000..04a7bd3bf --- /dev/null +++ b/test/cmdlineTests/storage_layout_value_types_packed/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_value_types_packed/output.json b/test/cmdlineTests/storage_layout_value_types_packed/output.json new file mode 100644 index 000000000..cd7f866aa --- /dev/null +++ b/test/cmdlineTests/storage_layout_value_types_packed/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint64"},{"astId":4,"contract":"fileA:A","label":"y","offset":8,"slot":"0","type":"t_uint128"},{"astId":6,"contract":"fileA:A","label":"z","offset":0,"slot":"1","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":12,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"},"t_uint64":{"encoding":"inplace","label":"uint64","numberOfBytes":"8"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; } +^-------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":75,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index 81cba6cff..c826bfeae 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -303,7 +303,10 @@ enum evmc_status_code * For example, the Client tries running a code in the EVM 1.5. If the * code is not supported there, the execution falls back to the EVM 1.0. */ - EVMC_REJECTED = -2 + EVMC_REJECTED = -2, + + /** The VM failed to allocate the amount of memory needed for execution. */ + EVMC_OUT_OF_MEMORY = -3 }; /* Forward declaration. */ @@ -795,13 +798,15 @@ enum evmc_revision * * This function MAY be invoked multiple times for a single VM instance. * - * @param instance The VM instance. - * @param context The pointer to the Client execution context to be passed - * to the callback functions. See ::evmc_context. - * @param rev Requested EVM specification revision. - * @param msg Call parameters. See ::evmc_message. - * @param code Reference to the code to be executed. - * @param code_size The length of the code. + * @param instance The VM instance. This argument MUST NOT be NULL. + * @param context The pointer to the Host execution context to be passed + * to the Host interface methods (::evmc_host_interface). + * This argument MUST NOT be NULL unless + * the @p instance has the ::EVMC_CAPABILITY_PRECOMPILES capability. + * @param rev The requested EVM specification revision. + * @param msg The call parameters. See ::evmc_message. This argument MUST NOT be NULL. + * @param code The reference to the code to be executed. This argument MAY be NULL. + * @param code_size The length of the code. If @p code is NULL this argument MUST be 0. * @return The execution result. */ typedef struct evmc_result (*evmc_execute_fn)(struct evmc_instance* instance, @@ -857,7 +862,11 @@ typedef uint32_t evmc_capabilities_flagset; */ typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance); -/** The opaque type representing a Client-side tracer object. */ +/** + * The opaque type representing a Client-side tracer object. + * + * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). + */ struct evmc_tracer_context; /** @@ -869,6 +878,8 @@ struct evmc_tracer_context; * This piece of information can be acquired by inspecting messages being sent to the EVM in * ::evmc_execute_fn and the results of the messages execution. * + * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). + * * @param context The pointer to the Client-side tracing context. This allows to * implement the tracer in OOP manner. * @param code_offset The current instruction position in the code. @@ -916,6 +927,8 @@ typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context, * * This will overwrite the previous settings (the callback and the context). * + * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). + * * @param instance The EVM instance. * @param callback The tracer callback function. This argument MAY be NULL to disable previously * set tracer. @@ -989,6 +1002,10 @@ struct evmc_instance * Optional pointer to function setting the EVM instruction tracer. * * If the EVM does not support this feature the pointer can be NULL. + * + * @deprecated + * Since EVMC 6.3, the tracing API has been deprecated as there have been some + * design flaws discovered. New API is expected to be introduced in future. */ evmc_set_tracer_fn set_tracer; diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index 7d2344cdb..ff486a232 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -14,6 +15,250 @@ /// @ingroup cpp namespace evmc { +/// The big-endian 160-bit hash suitable for keeping an Ethereum address. +/// +/// This type wraps C ::evmc_address to make sure objects of this type are always initialized. +struct address : evmc_address +{ + /// Default and converting constructor. + /// + /// Initializes bytes to zeros if not other @p init value provided. + constexpr address(evmc_address init = {}) noexcept : evmc_address{init} {} + + /// Explicit operator converting to bool. + constexpr inline explicit operator bool() const noexcept; +}; + +/// The fixed size array of 32 bytes for storing 256-bit EVM values. +/// +/// This type wraps C ::evmc_bytes32 to make sure objects of this type are always initialized. +struct bytes32 : evmc_bytes32 +{ + /// Default and converting constructor. + /// + /// Initializes bytes to zeros if not other @p init value provided. + constexpr bytes32(evmc_bytes32 init = {}) noexcept : evmc_bytes32{init} {} + + /// Explicit operator converting to bool. + constexpr inline explicit operator bool() const noexcept; +}; + +/// The alias for evmc::bytes32 to represent a big-endian 256-bit integer. +using uint256be = bytes32; + + +/// Loads 64 bits / 8 bytes of data from the given @p bytes array in big-endian order. +constexpr inline uint64_t load64be(const uint8_t* bytes) noexcept +{ + // TODO: Report bug in clang incorrectly optimizing this with AVX2 enabled. + return (uint64_t{bytes[0]} << 56) | (uint64_t{bytes[1]} << 48) | (uint64_t{bytes[2]} << 40) | + (uint64_t{bytes[3]} << 32) | (uint64_t{bytes[4]} << 24) | (uint64_t{bytes[5]} << 16) | + (uint64_t{bytes[6]} << 8) | uint64_t{bytes[7]}; +} + +/// Loads 32 bits / 4 bytes of data from the given @p bytes array in big-endian order. +constexpr inline uint32_t load32be(const uint8_t* bytes) noexcept +{ + return (uint32_t{bytes[0]} << 24) | (uint32_t{bytes[1]} << 16) | (uint32_t{bytes[2]} << 8) | + uint32_t{bytes[3]}; +} + +namespace fnv +{ +constexpr auto prime = 0x100000001b3; ///< The 64-bit FNV prime number. +constexpr auto offset_basis = 0xcbf29ce484222325; ///< The 64-bit FNV offset basis. + +/// The hashing transformation for 64-bit inputs based on the FNV-1a formula. +constexpr inline uint64_t fnv1a_by64(uint64_t h, uint64_t x) noexcept +{ + return (h ^ x) * prime; +} +} // namespace fnv + + +/// The "equal" comparison operator for the evmc::address type. +constexpr bool operator==(const address& a, const address& b) noexcept +{ + // TODO: Report bug in clang keeping unnecessary bswap. + return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) && + load64be(&a.bytes[8]) == load64be(&b.bytes[8]) && + load32be(&a.bytes[16]) == load32be(&b.bytes[16]); +} + +/// The "not equal" comparison operator for the evmc::address type. +constexpr bool operator!=(const address& a, const address& b) noexcept +{ + return !(a == b); +} + +/// The "less" comparison operator for the evmc::address type. +constexpr bool operator<(const address& a, const address& b) noexcept +{ + return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) || + (load64be(&a.bytes[0]) == load64be(&b.bytes[0]) && + load64be(&a.bytes[8]) < load64be(&b.bytes[8])) || + (load64be(&a.bytes[8]) == load64be(&b.bytes[8]) && + load32be(&a.bytes[16]) < load32be(&b.bytes[16])); +} + +/// The "equal" comparison operator for the evmc::bytes32 type. +constexpr bool operator==(const bytes32& a, const bytes32& b) noexcept +{ + return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) && + load64be(&a.bytes[8]) == load64be(&b.bytes[8]) && + load64be(&a.bytes[16]) == load64be(&b.bytes[16]) && + load64be(&a.bytes[24]) == load64be(&b.bytes[24]); +} + +/// The "not equal" comparison operator for the evmc::bytes32 type. +constexpr bool operator!=(const bytes32& a, const bytes32& b) noexcept +{ + return !(a == b); +} + +/// The "less" comparison operator for the evmc::bytes32 type. +constexpr bool operator<(const bytes32& a, const bytes32& b) noexcept +{ + return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) || + (load64be(&a.bytes[0]) == load64be(&b.bytes[0]) && + load64be(&a.bytes[8]) < load64be(&b.bytes[8])) || + (load64be(&a.bytes[8]) == load64be(&b.bytes[8]) && + load64be(&a.bytes[16]) < load64be(&b.bytes[16])) || + (load64be(&a.bytes[16]) == load64be(&b.bytes[16]) && + load64be(&a.bytes[24]) < load64be(&b.bytes[24])); +} + +/// Checks if the given address is the zero address. +constexpr inline bool is_zero(const address& a) noexcept +{ + return a == address{}; +} + +constexpr address::operator bool() const noexcept +{ + return !is_zero(*this); +} + +/// Checks if the given bytes32 object has all zero bytes. +constexpr inline bool is_zero(const bytes32& a) noexcept +{ + return a == bytes32{}; +} + +constexpr bytes32::operator bool() const noexcept +{ + return !is_zero(*this); +} + +namespace literals +{ +namespace internal +{ +template +struct integer_sequence +{ +}; + +template +using byte_sequence = integer_sequence; + +template +using char_sequence = integer_sequence; + + +template +struct concatenate; + +template +struct concatenate, byte_sequence> +{ + using type = byte_sequence; +}; + +template +constexpr uint8_t parse_hex_digit() noexcept +{ + static_assert((D >= '0' && D <= '9') || (D >= 'a' && D <= 'f') || (D >= 'A' && D <= 'F'), + "literal must be hexadecimal integer"); + return static_cast( + (D >= '0' && D <= '9') ? D - '0' : (D >= 'a' && D <= 'f') ? D - 'a' + 10 : D - 'A' + 10); +} + + +template +struct parse_digits; + +template +struct parse_digits> +{ + using type = byte_sequence(parse_hex_digit() << 4) | + parse_hex_digit()>; +}; + +template +struct parse_digits> +{ + using type = typename concatenate>::type, + typename parse_digits>::type>::type; +}; + + +template +struct parse_literal; + +template +struct parse_literal> +{ + static_assert(Prefix1 == '0' && Prefix2 == 'x', "literal must be in hexadecimal notation"); + static_assert(sizeof...(Literal) == sizeof(T) * 2, "literal must match the result type size"); + + template + static constexpr T create_from(byte_sequence) noexcept + { + return T{{{Bytes...}}}; + } + + static constexpr T get() noexcept + { + return create_from(typename parse_digits>::type{}); + } +}; + +template +struct parse_literal> +{ + static_assert(Digit == '0', "only 0 is allowed as a single digit literal"); + static constexpr T get() noexcept { return {}; } +}; + +template +constexpr T parse() noexcept +{ + return parse_literal>::get(); +} +} // namespace internal + +/// Literal for evmc::address. +template +constexpr address operator"" _address() noexcept +{ + return internal::parse(); +} + +/// Literal for evmc::bytes32. +template +constexpr bytes32 operator"" _bytes32() noexcept +{ + return internal::parse(); +} +} // namespace literals + +using namespace literals; + + +/// Alias for evmc_make_result(). +constexpr auto make_result = evmc_make_result; + /// @copydoc evmc_result /// /// This is a RAII wrapper for evmc_result and objects of this type @@ -27,6 +272,22 @@ public: using evmc_result::output_size; using evmc_result::status_code; + /// Creates the result from the provided arguments. + /// + /// The provided output is copied to memory allocated with malloc() + /// and the evmc_result::release function is set to one invoking free(). + /// + /// @param _status_code The status code. + /// @param _gas_left The amount of gas left. + /// @param _output_data The pointer to the output. + /// @param _output_size The output size. + result(evmc_status_code _status_code, + int64_t _gas_left, + const uint8_t* _output_data, + size_t _output_size) noexcept + : evmc_result{make_result(_status_code, _gas_left, _output_data, _output_size)} + {} + /// Converting constructor from raw evmc_result. explicit result(evmc_result const& res) noexcept : evmc_result{res} {} @@ -80,11 +341,32 @@ public: class vm { public: + vm() noexcept = default; + /// Converting constructor from evmc_instance. explicit vm(evmc_instance* instance) noexcept : m_instance{instance} {} /// Destructor responsible for automatically destroying the VM instance. - ~vm() noexcept { m_instance->destroy(m_instance); } + ~vm() noexcept + { + if (m_instance) + m_instance->destroy(m_instance); + } + + vm(const vm&) = delete; + vm& operator=(const vm&) = delete; + + /// Move constructor. + vm(vm&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } + + /// Move assignment operator. + vm& operator=(vm&& other) noexcept + { + this->~vm(); + m_instance = other.m_instance; + other.m_instance = nullptr; + return *this; + } /// The constructor that captures a VM instance and configures the instance /// with provided list of options. @@ -96,6 +378,9 @@ public: set_option(option.first, option.second); } + /// Checks if contains a valid pointer to the VM instance. + explicit operator bool() const noexcept { return m_instance != nullptr; } + /// Checks whenever the VM instance is ABI compatible with the current EVMC API. bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; } @@ -128,7 +413,7 @@ public: } private: - evmc_instance* const m_instance = nullptr; + evmc_instance* m_instance = nullptr; }; /// The EVMC Host interface @@ -138,35 +423,33 @@ public: virtual ~HostInterface() noexcept = default; /// @copydoc evmc_host_interface::account_exists - virtual bool account_exists(const evmc_address& addr) noexcept = 0; + virtual bool account_exists(const address& addr) noexcept = 0; /// @copydoc evmc_host_interface::get_storage - virtual evmc_bytes32 get_storage(const evmc_address& addr, - const evmc_bytes32& key) noexcept = 0; + virtual bytes32 get_storage(const address& addr, const bytes32& key) noexcept = 0; /// @copydoc evmc_host_interface::set_storage - virtual evmc_storage_status set_storage(const evmc_address& addr, - const evmc_bytes32& key, - const evmc_bytes32& value) noexcept = 0; + virtual evmc_storage_status set_storage(const address& addr, + const bytes32& key, + const bytes32& value) noexcept = 0; /// @copydoc evmc_host_interface::get_balance - virtual evmc_uint256be get_balance(const evmc_address& addr) noexcept = 0; + virtual uint256be get_balance(const address& addr) noexcept = 0; /// @copydoc evmc_host_interface::get_code_size - virtual size_t get_code_size(const evmc_address& addr) noexcept = 0; + virtual size_t get_code_size(const address& addr) noexcept = 0; /// @copydoc evmc_host_interface::get_code_hash - virtual evmc_bytes32 get_code_hash(const evmc_address& addr) noexcept = 0; + virtual bytes32 get_code_hash(const address& addr) noexcept = 0; /// @copydoc evmc_host_interface::copy_code - virtual size_t copy_code(const evmc_address& addr, + virtual size_t copy_code(const address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) noexcept = 0; /// @copydoc evmc_host_interface::selfdestruct - virtual void selfdestruct(const evmc_address& addr, - const evmc_address& beneficiary) noexcept = 0; + virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0; /// @copydoc evmc_host_interface::call virtual result call(const evmc_message& msg) noexcept = 0; @@ -175,13 +458,13 @@ public: virtual evmc_tx_context get_tx_context() noexcept = 0; /// @copydoc evmc_host_interface::get_block_hash - virtual evmc_bytes32 get_block_hash(int64_t block_number) noexcept = 0; + virtual bytes32 get_block_hash(int64_t block_number) noexcept = 0; /// @copydoc evmc_host_interface::emit_log - virtual void emit_log(const evmc_address& addr, + virtual void emit_log(const address& addr, const uint8_t* data, size_t data_size, - const evmc_bytes32 topics[], + const bytes32 topics[], size_t num_topics) noexcept = 0; }; @@ -198,39 +481,39 @@ public: /// Implicit converting constructor from evmc_context. HostContext(evmc_context* ctx) noexcept : context{ctx} {} // NOLINT - bool account_exists(const evmc_address& address) noexcept final + bool account_exists(const address& address) noexcept final { return context->host->account_exists(context, &address); } - evmc_bytes32 get_storage(const evmc_address& address, const evmc_bytes32& key) noexcept final + bytes32 get_storage(const address& address, const bytes32& key) noexcept final { return context->host->get_storage(context, &address, &key); } - evmc_storage_status set_storage(const evmc_address& address, - const evmc_bytes32& key, - const evmc_bytes32& value) noexcept final + evmc_storage_status set_storage(const address& address, + const bytes32& key, + const bytes32& value) noexcept final { return context->host->set_storage(context, &address, &key, &value); } - evmc_uint256be get_balance(const evmc_address& address) noexcept final + uint256be get_balance(const address& address) noexcept final { return context->host->get_balance(context, &address); } - size_t get_code_size(const evmc_address& address) noexcept final + size_t get_code_size(const address& address) noexcept final { return context->host->get_code_size(context, &address); } - evmc_bytes32 get_code_hash(const evmc_address& address) noexcept final + bytes32 get_code_hash(const address& address) noexcept final { return context->host->get_code_hash(context, &address); } - size_t copy_code(const evmc_address& address, + size_t copy_code(const address& address, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) noexcept final @@ -238,9 +521,9 @@ public: return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size); } - void selfdestruct(const evmc_address& address, const evmc_address& beneficiary) noexcept final + void selfdestruct(const address& addr, const address& beneficiary) noexcept final { - context->host->selfdestruct(context, &address, &beneficiary); + context->host->selfdestruct(context, &addr, &beneficiary); } result call(const evmc_message& message) noexcept final @@ -261,18 +544,18 @@ public: return tx_context; } - evmc_bytes32 get_block_hash(int64_t number) noexcept final + bytes32 get_block_hash(int64_t number) noexcept final { return context->host->get_block_hash(context, number); } - void emit_log(const evmc_address& address, + void emit_log(const address& addr, const uint8_t* data, size_t data_size, - const evmc_bytes32 topics[], + const bytes32 topics[], size_t topics_count) noexcept final { - context->host->emit_log(context, &address, data, data_size, topics, topics_count); + context->host->emit_log(context, &addr, data, data_size, topics, topics_count); } }; @@ -351,7 +634,8 @@ inline void emit_log(evmc_context* h, const evmc_bytes32 topics[], size_t num_topics) noexcept { - static_cast(h)->emit_log(*addr, data, data_size, topics, num_topics); + static_cast(h)->emit_log(*addr, data, data_size, static_cast(topics), + num_topics); } constexpr evmc_host_interface interface{ @@ -360,6 +644,42 @@ constexpr evmc_host_interface interface{ }; } // namespace internal -inline Host::Host() noexcept : evmc_context{&internal::interface} {} +inline Host::Host() noexcept : evmc_context{&evmc::internal::interface} {} } // namespace evmc + + +namespace std +{ +/// Hash operator template specialization for evmc::address. Needed for unordered containers. +template <> +struct hash +{ + /// Hash operator using FNV1a-based folding. + constexpr size_t operator()(const evmc::address& s) const noexcept + { + using namespace evmc; + using namespace fnv; + return static_cast(fnv1a_by64( + fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])), load64be(&s.bytes[8])), + load32be(&s.bytes[16]))); + } +}; + +/// Hash operator template specialization for evmc::bytes32. Needed for unordered containers. +template <> +struct hash +{ + /// Hash operator using FNV1a-based folding. + constexpr size_t operator()(const evmc::bytes32& s) const noexcept + { + using namespace evmc; + using namespace fnv; + return static_cast( + fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])), + load64be(&s.bytes[8])), + load64be(&s.bytes[16])), + load64be(&s.bytes[24]))); + } +}; +} // namespace std diff --git a/test/evmc/helpers.h b/test/evmc/helpers.h index e43cfd76a..2c4dbceae 100644 --- a/test/evmc/helpers.h +++ b/test/evmc/helpers.h @@ -18,6 +18,8 @@ #pragma once #include +#include +#include /** * Returns true if the VM instance has a compatible ABI version. @@ -83,6 +85,7 @@ static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance* * * @see evmc_set_tracer_fn */ +EVMC_DEPRECATED static inline void evmc_set_tracer(struct evmc_instance* instance, evmc_trace_callback callback, struct evmc_tracer_context* context) @@ -106,6 +109,58 @@ static inline struct evmc_result evmc_execute(struct evmc_instance* instance, return instance->execute(instance, context, rev, msg, code, code_size); } +/// The evmc_result release function using free() for releasing the memory. +/// +/// This function is used in the evmc_make_result(), +/// but may be also used in other case if convenient. +/// +/// @param result The result object. +static void evmc_free_result_memory(const struct evmc_result* result) +{ + free((uint8_t*)result->output_data); +} + +/// Creates the result from the provided arguments. +/// +/// The provided output is copied to memory allocated with malloc() +/// and the evmc_result::release function is set to one invoking free(). +/// +/// In case of memory allocation failure, the result has all fields zeroed +/// and only evmc_result::status_code is set to ::EVMC_OUT_OF_MEMORY internal error. +/// +/// @param status_code The status code. +/// @param gas_left The amount of gas left. +/// @param output_data The pointer to the output. +/// @param output_size The output size. +static inline struct evmc_result evmc_make_result(enum evmc_status_code status_code, + int64_t gas_left, + const uint8_t* output_data, + size_t output_size) +{ + struct evmc_result result; + memset(&result, 0, sizeof(result)); + + if (output_size != 0) + { + uint8_t* buffer = (uint8_t*)malloc(output_size); + + if (!buffer) + { + result.status_code = EVMC_OUT_OF_MEMORY; + return result; + } + + memcpy(buffer, output_data, output_size); + result.output_data = buffer; + result.output_size = output_size; + result.release = evmc_free_result_memory; + } + + result.status_code = status_code; + result.gas_left = gas_left; + return result; +} + /** * Releases the resources allocated to the execution result. * diff --git a/test/evmc/helpers.hpp b/test/evmc/helpers.hpp index 1b420b2e5..f30eec87a 100644 --- a/test/evmc/helpers.hpp +++ b/test/evmc/helpers.hpp @@ -12,58 +12,78 @@ */ #pragma once -#include +#include #include #include +using evmc::is_zero; + /// The comparator for std::map. +EVMC_DEPRECATED inline bool operator<(const evmc_address& a, const evmc_address& b) { return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0; } /// The comparator for std::map. +EVMC_DEPRECATED inline bool operator<(const evmc_bytes32& a, const evmc_bytes32& b) { return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0; } /// The comparator for equality. +EVMC_DEPRECATED inline bool operator==(const evmc_address& a, const evmc_address& b) { return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0; } /// The comparator for equality. +EVMC_DEPRECATED inline bool operator==(const evmc_bytes32& a, const evmc_bytes32& b) { return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0; } -/// Check if the address is zero (all bytes are zeros). -inline bool is_zero(const evmc_address& address) noexcept +/// Parameters for the fnv1a hash function, specialized by the hash result size (size_t). +/// +/// The values for the matching size are taken from +/// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters. +/// +/// @tparam size The size of the hash result (size_t). +template +struct fnv1_params { - return address == evmc_address{}; -} +}; -/// Check if the hash is zero (all bytes are zeros). -inline bool is_zero(const evmc_bytes32& x) noexcept +/// Parameters for the fnv1a hash function, specialized for the hash result of 4 bytes. +template <> +struct fnv1_params<4> { - return x == evmc_bytes32{}; -} + static constexpr auto prime = 0x1000193; ///< The FNV prime. + static constexpr auto offset_basis = 0x811c9dc5; ///< The FNV offset basis. +}; -/// FNV1a hash function with 64-bit result. -inline uint64_t fnv1a_64(const uint8_t* ptr, size_t len) +/// Parameters for the fnv1a hash function, specialized for the hash result of 8 bytes. +template <> +struct fnv1_params<8> { - constexpr uint64_t prime = 1099511628211ULL; - constexpr uint64_t offset_basis = 14695981039346656037ULL; + static constexpr auto prime = 0x100000001b3; ///< The FNV prime. + static constexpr auto offset_basis = 0xcbf29ce484222325; ///< The FNV offset basis. +}; - uint64_t ret = offset_basis; +/// FNV1a hash function. +inline size_t fnv1a(const uint8_t* ptr, size_t len) noexcept +{ + using params = fnv1_params; + + auto ret = size_t{params::offset_basis}; for (size_t i = 0; i < len; i++) { ret ^= ptr[i]; - ret *= prime; + ret *= params::prime; } return ret; } @@ -72,25 +92,23 @@ namespace std { /// Hash operator template specialization for evmc_address needed for unordered containers. template <> -struct hash +struct EVMC_DEPRECATED hash { /// Hash operator using FNV1a. - std::enable_if::type operator()(const evmc_address& s) const - noexcept + size_t operator()(const evmc_address& s) const noexcept { - return fnv1a_64(s.bytes, sizeof(s.bytes)); + return fnv1a(s.bytes, sizeof(s.bytes)); } }; /// Hash operator template needed for std::unordered_set and others using hashes. template <> -struct hash +struct EVMC_DEPRECATED hash { /// Hash operator using FNV1a. - std::enable_if::type operator()(const evmc_bytes32& s) const - noexcept + size_t operator()(const evmc_bytes32& s) const noexcept { - return fnv1a_64(s.bytes, sizeof(s.bytes)); + return fnv1a(s.bytes, sizeof(s.bytes)); } }; } // namespace std diff --git a/test/evmc/loader.c b/test/evmc/loader.c index 51d7a85fb..30bb39023 100644 --- a/test/evmc/loader.c +++ b/test/evmc/loader.c @@ -42,20 +42,14 @@ #define ATTR_FORMAT(...) #endif -#if _WIN32 -#define strcpy_sx strcpy_s -#else /* * Limited variant of strcpy_s(). - * - * Provided for C standard libraries where strcpy_s() is not available. - * The availability check might need to adjusted for other C standard library implementations. */ #if !defined(EVMC_LOADER_MOCK) static #endif int - strcpy_sx(char* restrict dest, size_t destsz, const char* restrict src) + strcpy_sx(char* dest, size_t destsz, const char* src) { size_t len = strlen(src); if (len >= destsz) @@ -70,7 +64,6 @@ static dest[len] = 0; return 0; } -#endif #define PATH_MAX_LENGTH 4096 @@ -238,3 +231,93 @@ exit: return instance; } + +/// Gets the token delimited by @p delim character of the string pointed by the @p str_ptr. +/// If the delimiter is not found, the whole string is returned. +/// The @p str_ptr is also slided after the delimiter or to the string end +/// if the delimiter is not found (in this case the @p str_ptr points to an empty string). +static char* get_token(char** str_ptr, char delim) +{ + char* str = *str_ptr; + char* delim_pos = strchr(str, delim); + if (delim_pos) + { + // If the delimiter is found, null it to get null-terminated prefix + // and slide the str_ptr after the delimiter. + *delim_pos = '\0'; + *str_ptr = delim_pos + 1; + } + else + { + // Otherwise, slide the str_ptr to the end and return the whole string as the prefix. + *str_ptr += strlen(str); + } + return str; +} + +struct evmc_instance* evmc_load_and_configure(const char* config, + enum evmc_loader_error_code* error_code) +{ + enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; + struct evmc_instance* instance = NULL; + + char config_copy_buffer[PATH_MAX_LENGTH]; + if (strcpy_sx(config_copy_buffer, sizeof(config_copy_buffer), config) != 0) + { + ec = set_error(EVMC_LOADER_INVALID_ARGUMENT, + "invalid argument: configuration is too long (maximum allowed length is %d)", + (int)sizeof(config_copy_buffer)); + goto exit; + } + + char* options = config_copy_buffer; + const char* path = get_token(&options, ','); + + instance = evmc_load_and_create(path, error_code); + if (!instance) + return NULL; + + if (instance->set_option == NULL && strlen(options) != 0) + { + ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options", + instance->name, path); + goto exit; + } + + + while (strlen(options) != 0) + { + char* option = get_token(&options, ','); + + // Slit option into name and value by taking the name token. + // The option variable will have the value, can be empty. + const char* name = get_token(&option, '='); + + enum evmc_set_option_result r = instance->set_option(instance, name, option); + switch (r) + { + case EVMC_SET_OPTION_SUCCESS: + break; + case EVMC_SET_OPTION_INVALID_NAME: + ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s): unknown option '%s'", + instance->name, path, name); + goto exit; + case EVMC_SET_OPTION_INVALID_VALUE: + ec = set_error(EVMC_LOADER_INVALID_OPTION_VALUE, + "%s (%s): unsupported value '%s' for option '%s'", instance->name, path, + option, name); + goto exit; + } + } + +exit: + if (error_code) + *error_code = ec; + + if (ec == EVMC_LOADER_SUCCESS) + return instance; + + if (instance) + evmc_destroy(instance); + return NULL; +} diff --git a/test/evmc/loader.h b/test/evmc/loader.h index d8531af1d..0c50a81f4 100644 --- a/test/evmc/loader.h +++ b/test/evmc/loader.h @@ -40,7 +40,13 @@ enum evmc_loader_error_code EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4, /** The ABI version of the VM instance has mismatched. */ - EVMC_LOADER_ABI_VERSION_MISMATCH = 5 + EVMC_LOADER_ABI_VERSION_MISMATCH = 5, + + /** The VM option is invalid. */ + EVMC_LOADER_INVALID_OPTION_NAME = 6, + + /** The VM option value is invalid. */ + EVMC_LOADER_INVALID_OPTION_VALUE = 7 }; /** @@ -111,6 +117,43 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro struct evmc_instance* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code); +/** + * Dynamically loads the EVMC module, then creates and configures the VM instance. + * + * This function performs the following actions atomically: + * - loads the EVMC module (as evmc_load()), + * - creates the VM instance, + * - configures the VM instance with options provided in the @p config parameter. + * + * The configuration string (@p config) has the following syntax: + * + * ("," ["=" ])* + * + * In this syntax, an option without a value can be specified (`,option,`) + * as a shortcut for using empty value (`,option=,`). + * + * Options are passed to a VM in the order they are specified in the configuration string. + * It is up to the VM implementation how to handle duplicated options and other conflicts. + * + * Example configuration string: + * + * ./modules/vm.so,engine=compiler,trace,verbosity=2 + * + * The function signals the same errors as evmc_load_and_create() and additionally: + * - ::EVMC_LOADER_INVALID_OPTION_NAME + * when the provided options list contains an option unknown for the VM, + * - ::EVMC_LOADER_INVALID_OPTION_VALUE + * when there exists unsupported value for a given VM option. + + * + * @param config The path to the EVMC module with additional configuration options. + * @param error_code The pointer to the error code. If not NULL the value is set to + * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. + * @return The pointer to the created VM or NULL in case of error. + */ +struct evmc_instance* evmc_load_and_configure(const char* config, + enum evmc_loader_error_code* error_code); + /** * Returns the human-readable message describing the most recent error * that occurred in EVMC loading since the last call to this function. diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index d6574da4d..c018789ac 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -66,15 +66,14 @@ function setup setup_solcjs "$DIR" "$SOLJSON" download_project "$repo" "$branch" "$DIR" - - replace_version_pragmas } function replace_version_pragmas { - # Replace fixed-version pragmas in Gnosis (part of Consensys best practice) + # Replace fixed-version pragmas (part of Consensys best practice). + # Include all directories to also cover node dependencies. printLog "Replacing fixed-version pragmas..." - find contracts test -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity [\^0-9\.]*/pragma solidity >=0.0/' + find . test -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity [\^0-9\.]*/pragma solidity >=0.0/' } function replace_libsolc_call @@ -187,6 +186,7 @@ function run_test local compile_fn="$1" local test_fn="$2" + replace_version_pragmas force_solc "$CONFIG" "$DIR" "$SOLJSON" printLog "Checking optimizer level..." diff --git a/test/libdevcore/IterateReplacing.cpp b/test/libdevcore/IterateReplacing.cpp index 08cd1e22f..dd64d481b 100644 --- a/test/libdevcore/IterateReplacing.cpp +++ b/test/libdevcore/IterateReplacing.cpp @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_SUITE(IterateReplacing) BOOST_AUTO_TEST_CASE(no_replacement) { vector v{"abc", "def", "ghi"}; - function>(string&)> f = [](string&) -> boost::optional> { return {}; }; + function>(string&)> f = [](string&) -> std::optional> { return {}; }; iterateReplacing(v, f); vector expectation{"abc", "def", "ghi"}; BOOST_CHECK(v == expectation); @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_CASE(no_replacement) BOOST_AUTO_TEST_CASE(empty_input) { vector v; - function>(string&)> f = [](string&) -> boost::optional> { return {}; }; + function>(string&)> f = [](string&) -> std::optional> { return {}; }; iterateReplacing(v, f); vector expectation; BOOST_CHECK(v == expectation); @@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(empty_input) BOOST_AUTO_TEST_CASE(delete_some) { vector v{"abc", "def", "ghi"}; - function>(string&)> f = [](string& _s) -> boost::optional> { + function>(string&)> f = [](string& _s) -> std::optional> { if (_s == "def") return vector(); else @@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(delete_some) BOOST_AUTO_TEST_CASE(inject_some_start) { vector v{"abc", "def", "ghi"}; - function>(string&)> f = [](string& _s) -> boost::optional> { + function>(string&)> f = [](string& _s) -> std::optional> { if (_s == "abc") return vector{"x", "y"}; else @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(inject_some_start) BOOST_AUTO_TEST_CASE(inject_some_end) { vector v{"abc", "def", "ghi"}; - function>(string&)> f = [](string& _s) -> boost::optional> { + function>(string&)> f = [](string& _s) -> std::optional> { if (_s == "ghi") return vector{"x", "y"}; else diff --git a/test/libsolidity/ABIJson/library_function.sol b/test/libsolidity/ABIJson/library_function.sol index 2aad8e164..f61a1f8a9 100644 --- a/test/libsolidity/ABIJson/library_function.sol +++ b/test/libsolidity/ABIJson/library_function.sol @@ -1,9 +1,14 @@ +contract X {} library test { struct StructType { uint a; } - function f(StructType storage b, uint[] storage c, test d) public returns (uint[] memory e, StructType storage f) { f = f; } - function f1(uint[] memory c, test d) public pure returns (uint[] memory e) { } + function f(StructType storage b, uint[] storage c, X d) public returns (uint[] memory e, StructType storage f) { f = f; } + function f1(uint[] memory c, X d) public pure returns (uint[] memory e) { } } // ---- +// :X +// [] +// +// // :test // [ // { @@ -16,9 +21,9 @@ library test { // "type": "uint256[]" // }, // { -// "internalType": "library test", +// "internalType": "contract X", // "name": "d", -// "type": "test" +// "type": "X" // } // ], // "name": "f1", diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json index c8b71628c..075aacda9 100644 --- a/test/libsolidity/ASTJSON/address_payable.json +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -1,560 +1,560 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 37 - ] - }, - "id" : 38, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 37, - "linearizedBaseContracts" : - [ - 37 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "constant" : false, - "id" : 4, - "name" : "m", - "nodeType" : "VariableDeclaration", - "scope" : 37, - "src" : "17:44:1", - "stateVariable" : true, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_mapping$_t_address_$_t_address_payable_$", - "typeString" : "mapping(address => address payable)" - }, - "typeName" : - { - "id" : 3, - "keyType" : - { - "id" : 1, - "name" : "address", - "nodeType" : "ElementaryTypeName", - "src" : "25:7:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address", - "typeString" : "address" - } - }, - "nodeType" : "Mapping", - "src" : "17:35:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_mapping$_t_address_$_t_address_payable_$", - "typeString" : "mapping(address => address payable)" - }, - "valueType" : - { - "id" : 2, - "name" : "address", - "nodeType" : "ElementaryTypeName", - "src" : "36:15:1", - "stateMutability" : "payable", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - } - }, - "value" : null, - "visibility" : "public" - }, - { - "body" : - { - "id" : 35, - "nodeType" : "Block", - "src" : "134:122:1", - "statements" : - [ - { - "assignments" : - [ - 12 - ], - "declarations" : - [ - { - "constant" : false, - "id" : 12, - "name" : "a", - "nodeType" : "VariableDeclaration", - "scope" : 35, - "src" : "144:17:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - }, - "typeName" : - { - "id" : 11, - "name" : "address", - "nodeType" : "ElementaryTypeName", - "src" : "144:15:1", - "stateMutability" : "payable", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "id" : 16, - "initialValue" : - { - "argumentTypes" : null, - "baseExpression" : - { - "argumentTypes" : null, - "id" : 13, - "name" : "m", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 4, - "src" : "164:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_mapping$_t_address_$_t_address_payable_$", - "typeString" : "mapping(address => address payable)" - } - }, - "id" : 15, - "indexExpression" : - { - "argumentTypes" : null, - "id" : 14, - "name" : "arg", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 6, - "src" : "166:3:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "isConstant" : false, - "isLValue" : true, - "isPure" : false, - "lValueRequested" : false, - "nodeType" : "IndexAccess", - "src" : "164:6:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "nodeType" : "VariableDeclarationStatement", - "src" : "144:26:1" - }, - { - "expression" : - { - "argumentTypes" : null, - "id" : 19, - "isConstant" : false, - "isLValue" : false, - "isPure" : false, - "lValueRequested" : false, - "leftHandSide" : - { - "argumentTypes" : null, - "id" : 17, - "name" : "r", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 9, - "src" : "180:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "nodeType" : "Assignment", - "operator" : "=", - "rightHandSide" : - { - "argumentTypes" : null, - "id" : 18, - "name" : "arg", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 6, - "src" : "184:3:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "src" : "180:7:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "id" : 20, - "nodeType" : "ExpressionStatement", - "src" : "180:7:1" - }, - { - "assignments" : - [ - 22 - ], - "declarations" : - [ - { - "constant" : false, - "id" : 22, - "name" : "c", - "nodeType" : "VariableDeclaration", - "scope" : 35, - "src" : "197:9:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_address", - "typeString" : "address" - }, - "typeName" : - { - "id" : 21, - "name" : "address", - "nodeType" : "ElementaryTypeName", - "src" : "197:7:1", - "stateMutability" : "nonpayable", - "typeDescriptions" : - { - "typeIdentifier" : "t_address", - "typeString" : "address" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "id" : 26, - "initialValue" : - { - "argumentTypes" : null, - "arguments" : - [ - { - "argumentTypes" : null, - "id" : 24, - "name" : "this", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 66, - "src" : "217:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_contract$_C_$37", - "typeString" : "contract C" - } - } - ], - "expression" : - { - "argumentTypes" : - [ - { - "typeIdentifier" : "t_contract$_C_$37", - "typeString" : "contract C" - } - ], - "id" : 23, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "nodeType" : "ElementaryTypeNameExpression", - "src" : "209:7:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_type$_t_address_$", - "typeString" : "type(address)" - }, - "typeName" : "address" - }, - "id" : 25, - "isConstant" : false, - "isLValue" : false, - "isPure" : false, - "kind" : "typeConversion", - "lValueRequested" : false, - "names" : [], - "nodeType" : "FunctionCall", - "src" : "209:13:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address", - "typeString" : "address" - } - }, - "nodeType" : "VariableDeclarationStatement", - "src" : "197:25:1" - }, - { - "expression" : - { - "argumentTypes" : null, - "id" : 33, - "isConstant" : false, - "isLValue" : false, - "isPure" : false, - "lValueRequested" : false, - "leftHandSide" : - { - "argumentTypes" : null, - "baseExpression" : - { - "argumentTypes" : null, - "id" : 27, - "name" : "m", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 4, - "src" : "232:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_mapping$_t_address_$_t_address_payable_$", - "typeString" : "mapping(address => address payable)" - } - }, - "id" : 29, - "indexExpression" : - { - "argumentTypes" : null, - "id" : 28, - "name" : "c", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 22, - "src" : "234:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address", - "typeString" : "address" - } - }, - "isConstant" : false, - "isLValue" : true, - "isPure" : false, - "lValueRequested" : true, - "nodeType" : "IndexAccess", - "src" : "232:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "nodeType" : "Assignment", - "operator" : "=", - "rightHandSide" : - { - "argumentTypes" : null, - "arguments" : - [ - { - "argumentTypes" : null, - "hexValue" : "30", - "id" : 31, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "kind" : "number", - "lValueRequested" : false, - "nodeType" : "Literal", - "src" : "247:1:1", - "subdenomination" : null, - "typeDescriptions" : - { - "typeIdentifier" : "t_rational_0_by_1", - "typeString" : "int_const 0" - }, - "value" : "0" - } - ], - "expression" : - { - "argumentTypes" : - [ - { - "typeIdentifier" : "t_rational_0_by_1", - "typeString" : "int_const 0" - } - ], - "id" : 30, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "nodeType" : "ElementaryTypeNameExpression", - "src" : "239:7:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_type$_t_address_$", - "typeString" : "type(address)" - }, - "typeName" : "address" - }, - "id" : 32, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "kind" : "typeConversion", - "lValueRequested" : false, - "names" : [], - "nodeType" : "FunctionCall", - "src" : "239:10:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "src" : "232:17:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "id" : 34, - "nodeType" : "ExpressionStatement", - "src" : "232:17:1" - } - ] - }, - "documentation" : null, - "id" : 36, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "f", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 7, - "nodeType" : "ParameterList", - "parameters" : - [ - { - "constant" : false, - "id" : 6, - "name" : "arg", - "nodeType" : "VariableDeclaration", - "scope" : 36, - "src" : "78:19:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - }, - "typeName" : - { - "id" : 5, - "name" : "address", - "nodeType" : "ElementaryTypeName", - "src" : "78:15:1", - "stateMutability" : "payable", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "src" : "77:21:1" - }, - "returnParameters" : - { - "id" : 10, - "nodeType" : "ParameterList", - "parameters" : - [ - { - "constant" : false, - "id" : 9, - "name" : "r", - "nodeType" : "VariableDeclaration", - "scope" : 36, - "src" : "115:17:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - }, - "typeName" : - { - "id" : 8, - "name" : "address", - "nodeType" : "ElementaryTypeName", - "src" : "115:15:1", - "stateMutability" : "payable", - "typeDescriptions" : - { - "typeIdentifier" : "t_address_payable", - "typeString" : "address payable" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "src" : "114:19:1" - }, - "scope" : 37, - "src" : "67:189:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 38, - "src" : "0:258:1" - } - ], - "src" : "0:259:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 37 + ] + }, + "id": 38, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 37, + "linearizedBaseContracts": + [ + 37 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 4, + "name": "m", + "nodeType": "VariableDeclaration", + "scope": 37, + "src": "17:44:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_address_$_t_address_payable_$", + "typeString": "mapping(address => address payable)" + }, + "typeName": + { + "id": 3, + "keyType": + { + "id": 1, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "25:7:1", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "Mapping", + "src": "17:35:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_address_$_t_address_payable_$", + "typeString": "mapping(address => address payable)" + }, + "valueType": + { + "id": 2, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "36:15:1", + "stateMutability": "payable", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + } + }, + "value": null, + "visibility": "public" + }, + { + "body": + { + "id": 35, + "nodeType": "Block", + "src": "134:122:1", + "statements": + [ + { + "assignments": + [ + 12 + ], + "declarations": + [ + { + "constant": false, + "id": 12, + "name": "a", + "nodeType": "VariableDeclaration", + "scope": 35, + "src": "144:17:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + }, + "typeName": + { + "id": 11, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "144:15:1", + "stateMutability": "payable", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "value": null, + "visibility": "internal" + } + ], + "id": 16, + "initialValue": + { + "argumentTypes": null, + "baseExpression": + { + "argumentTypes": null, + "id": 13, + "name": "m", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4, + "src": "164:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_address_$_t_address_payable_$", + "typeString": "mapping(address => address payable)" + } + }, + "id": 15, + "indexExpression": + { + "argumentTypes": null, + "id": 14, + "name": "arg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 6, + "src": "166:3:1", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "164:6:1", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "144:26:1" + }, + { + "expression": + { + "argumentTypes": null, + "id": 19, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": + { + "argumentTypes": null, + "id": 17, + "name": "r", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 9, + "src": "180:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": + { + "argumentTypes": null, + "id": 18, + "name": "arg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 6, + "src": "184:3:1", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "src": "180:7:1", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "id": 20, + "nodeType": "ExpressionStatement", + "src": "180:7:1" + }, + { + "assignments": + [ + 22 + ], + "declarations": + [ + { + "constant": false, + "id": 22, + "name": "c", + "nodeType": "VariableDeclaration", + "scope": 35, + "src": "197:9:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": + { + "id": 21, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "197:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "value": null, + "visibility": "internal" + } + ], + "id": 26, + "initialValue": + { + "argumentTypes": null, + "arguments": + [ + { + "argumentTypes": null, + "id": 24, + "name": "this", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 66, + "src": "217:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_C_$37", + "typeString": "contract C" + } + } + ], + "expression": + { + "argumentTypes": + [ + { + "typeIdentifier": "t_contract$_C_$37", + "typeString": "contract C" + } + ], + "id": 23, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "209:7:1", + "typeDescriptions": + { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": "address" + }, + "id": 25, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "typeConversion", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "209:13:1", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "197:25:1" + }, + { + "expression": + { + "argumentTypes": null, + "id": 33, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": + { + "argumentTypes": null, + "baseExpression": + { + "argumentTypes": null, + "id": 27, + "name": "m", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4, + "src": "232:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_address_$_t_address_payable_$", + "typeString": "mapping(address => address payable)" + } + }, + "id": 29, + "indexExpression": + { + "argumentTypes": null, + "id": 28, + "name": "c", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 22, + "src": "234:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "nodeType": "IndexAccess", + "src": "232:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": + { + "argumentTypes": null, + "arguments": + [ + { + "argumentTypes": null, + "hexValue": "30", + "id": 31, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "247:1:1", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + } + ], + "expression": + { + "argumentTypes": + [ + { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + } + ], + "id": 30, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "239:7:1", + "typeDescriptions": + { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": "address" + }, + "id": 32, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "239:10:1", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "src": "232:17:1", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "id": 34, + "nodeType": "ExpressionStatement", + "src": "232:17:1" + } + ] + }, + "documentation": null, + "id": 36, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 6, + "name": "arg", + "nodeType": "VariableDeclaration", + "scope": 36, + "src": "78:19:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + }, + "typeName": + { + "id": 5, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "78:15:1", + "stateMutability": "payable", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "77:21:1" + }, + "returnParameters": + { + "id": 10, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 9, + "name": "r", + "nodeType": "VariableDeclaration", + "scope": 36, + "src": "115:17:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + }, + "typeName": + { + "id": 8, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "115:15:1", + "stateMutability": "payable", + "typeDescriptions": + { + "typeIdentifier": "t_address_payable", + "typeString": "address payable" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "114:19:1" + }, + "scope": 37, + "src": "67:189:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 38, + "src": "0:258:1" + } + ], + "src": "0:259:1" } diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json index 8abebce08..3e8c66616 100644 --- a/test/libsolidity/ASTJSON/address_payable_legacy.json +++ b/test/libsolidity/ASTJSON/address_payable_legacy.json @@ -1,601 +1,601 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 37 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 37 - ], - "name" : "C", - "scope" : 38 - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "m", - "scope" : 37, - "stateVariable" : true, - "storageLocation" : "default", - "type" : "mapping(address => address payable)", - "value" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "type" : "mapping(address => address payable)" - }, - "children" : - [ - { - "attributes" : - { - "name" : "address", - "type" : "address" - }, - "id" : 1, - "name" : "ElementaryTypeName", - "src" : "25:7:1" - }, - { - "attributes" : - { - "name" : "address", - "stateMutability" : "payable", - "type" : "address payable" - }, - "id" : 2, - "name" : "ElementaryTypeName", - "src" : "36:15:1" - } - ], - "id" : 3, - "name" : "Mapping", - "src" : "17:35:1" - } - ], - "id" : 4, - "name" : "VariableDeclaration", - "src" : "17:44:1" - }, - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "f", - "scope" : 37, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "arg", - "scope" : 36, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "address payable", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "address", - "stateMutability" : "payable", - "type" : "address payable" - }, - "id" : 5, - "name" : "ElementaryTypeName", - "src" : "78:15:1" - } - ], - "id" : 6, - "name" : "VariableDeclaration", - "src" : "78:19:1" - } - ], - "id" : 7, - "name" : "ParameterList", - "src" : "77:21:1" - }, - { - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "r", - "scope" : 36, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "address payable", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "address", - "stateMutability" : "payable", - "type" : "address payable" - }, - "id" : 8, - "name" : "ElementaryTypeName", - "src" : "115:15:1" - } - ], - "id" : 9, - "name" : "VariableDeclaration", - "src" : "115:17:1" - } - ], - "id" : 10, - "name" : "ParameterList", - "src" : "114:19:1" - }, - { - "children" : - [ - { - "attributes" : - { - "assignments" : - [ - 12 - ] - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "a", - "scope" : 35, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "address payable", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "address", - "stateMutability" : "payable", - "type" : "address payable" - }, - "id" : 11, - "name" : "ElementaryTypeName", - "src" : "144:15:1" - } - ], - "id" : 12, - "name" : "VariableDeclaration", - "src" : "144:17:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "isConstant" : false, - "isLValue" : true, - "isPure" : false, - "lValueRequested" : false, - "type" : "address payable" - }, - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 4, - "type" : "mapping(address => address payable)", - "value" : "m" - }, - "id" : 13, - "name" : "Identifier", - "src" : "164:1:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 6, - "type" : "address payable", - "value" : "arg" - }, - "id" : 14, - "name" : "Identifier", - "src" : "166:3:1" - } - ], - "id" : 15, - "name" : "IndexAccess", - "src" : "164:6:1" - } - ], - "id" : 16, - "name" : "VariableDeclarationStatement", - "src" : "144:26:1" - }, - { - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "isConstant" : false, - "isLValue" : false, - "isPure" : false, - "lValueRequested" : false, - "operator" : "=", - "type" : "address payable" - }, - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 9, - "type" : "address payable", - "value" : "r" - }, - "id" : 17, - "name" : "Identifier", - "src" : "180:1:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 6, - "type" : "address payable", - "value" : "arg" - }, - "id" : 18, - "name" : "Identifier", - "src" : "184:3:1" - } - ], - "id" : 19, - "name" : "Assignment", - "src" : "180:7:1" - } - ], - "id" : 20, - "name" : "ExpressionStatement", - "src" : "180:7:1" - }, - { - "attributes" : - { - "assignments" : - [ - 22 - ] - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "c", - "scope" : 35, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "address", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "address", - "stateMutability" : "nonpayable", - "type" : "address" - }, - "id" : 21, - "name" : "ElementaryTypeName", - "src" : "197:7:1" - } - ], - "id" : 22, - "name" : "VariableDeclaration", - "src" : "197:9:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "isConstant" : false, - "isLValue" : false, - "isPure" : false, - "isStructConstructorCall" : false, - "lValueRequested" : false, - "names" : - [ - null - ], - "type" : "address", - "type_conversion" : true - }, - "children" : - [ - { - "attributes" : - { - "argumentTypes" : - [ - { - "typeIdentifier" : "t_contract$_C_$37", - "typeString" : "contract C" - } - ], - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "type" : "type(address)", - "value" : "address" - }, - "id" : 23, - "name" : "ElementaryTypeNameExpression", - "src" : "209:7:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 66, - "type" : "contract C", - "value" : "this" - }, - "id" : 24, - "name" : "Identifier", - "src" : "217:4:1" - } - ], - "id" : 25, - "name" : "FunctionCall", - "src" : "209:13:1" - } - ], - "id" : 26, - "name" : "VariableDeclarationStatement", - "src" : "197:25:1" - }, - { - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "isConstant" : false, - "isLValue" : false, - "isPure" : false, - "lValueRequested" : false, - "operator" : "=", - "type" : "address payable" - }, - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "isConstant" : false, - "isLValue" : true, - "isPure" : false, - "lValueRequested" : true, - "type" : "address payable" - }, - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 4, - "type" : "mapping(address => address payable)", - "value" : "m" - }, - "id" : 27, - "name" : "Identifier", - "src" : "232:1:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 22, - "type" : "address", - "value" : "c" - }, - "id" : 28, - "name" : "Identifier", - "src" : "234:1:1" - } - ], - "id" : 29, - "name" : "IndexAccess", - "src" : "232:4:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "isStructConstructorCall" : false, - "lValueRequested" : false, - "names" : - [ - null - ], - "type" : "address payable", - "type_conversion" : true - }, - "children" : - [ - { - "attributes" : - { - "argumentTypes" : - [ - { - "typeIdentifier" : "t_rational_0_by_1", - "typeString" : "int_const 0" - } - ], - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "type" : "type(address)", - "value" : "address" - }, - "id" : 30, - "name" : "ElementaryTypeNameExpression", - "src" : "239:7:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "hexvalue" : "30", - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "subdenomination" : null, - "token" : "number", - "type" : "int_const 0", - "value" : "0" - }, - "id" : 31, - "name" : "Literal", - "src" : "247:1:1" - } - ], - "id" : 32, - "name" : "FunctionCall", - "src" : "239:10:1" - } - ], - "id" : 33, - "name" : "Assignment", - "src" : "232:17:1" - } - ], - "id" : 34, - "name" : "ExpressionStatement", - "src" : "232:17:1" - } - ], - "id" : 35, - "name" : "Block", - "src" : "134:122:1" - } - ], - "id" : 36, - "name" : "FunctionDefinition", - "src" : "67:189:1" - } - ], - "id" : 37, - "name" : "ContractDefinition", - "src" : "0:258:1" - } - ], - "id" : 38, - "name" : "SourceUnit", - "src" : "0:259:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 37 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 37 + ], + "name": "C", + "scope": 38 + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "m", + "scope": 37, + "stateVariable": true, + "storageLocation": "default", + "type": "mapping(address => address payable)", + "value": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "type": "mapping(address => address payable)" + }, + "children": + [ + { + "attributes": + { + "name": "address", + "type": "address" + }, + "id": 1, + "name": "ElementaryTypeName", + "src": "25:7:1" + }, + { + "attributes": + { + "name": "address", + "stateMutability": "payable", + "type": "address payable" + }, + "id": 2, + "name": "ElementaryTypeName", + "src": "36:15:1" + } + ], + "id": 3, + "name": "Mapping", + "src": "17:35:1" + } + ], + "id": 4, + "name": "VariableDeclaration", + "src": "17:44:1" + }, + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "scope": 37, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "arg", + "scope": 36, + "stateVariable": false, + "storageLocation": "default", + "type": "address payable", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "address", + "stateMutability": "payable", + "type": "address payable" + }, + "id": 5, + "name": "ElementaryTypeName", + "src": "78:15:1" + } + ], + "id": 6, + "name": "VariableDeclaration", + "src": "78:19:1" + } + ], + "id": 7, + "name": "ParameterList", + "src": "77:21:1" + }, + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "r", + "scope": 36, + "stateVariable": false, + "storageLocation": "default", + "type": "address payable", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "address", + "stateMutability": "payable", + "type": "address payable" + }, + "id": 8, + "name": "ElementaryTypeName", + "src": "115:15:1" + } + ], + "id": 9, + "name": "VariableDeclaration", + "src": "115:17:1" + } + ], + "id": 10, + "name": "ParameterList", + "src": "114:19:1" + }, + { + "children": + [ + { + "attributes": + { + "assignments": + [ + 12 + ] + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "a", + "scope": 35, + "stateVariable": false, + "storageLocation": "default", + "type": "address payable", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "address", + "stateMutability": "payable", + "type": "address payable" + }, + "id": 11, + "name": "ElementaryTypeName", + "src": "144:15:1" + } + ], + "id": 12, + "name": "VariableDeclaration", + "src": "144:17:1" + }, + { + "attributes": + { + "argumentTypes": null, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "type": "address payable" + }, + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 4, + "type": "mapping(address => address payable)", + "value": "m" + }, + "id": 13, + "name": "Identifier", + "src": "164:1:1" + }, + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 6, + "type": "address payable", + "value": "arg" + }, + "id": 14, + "name": "Identifier", + "src": "166:3:1" + } + ], + "id": 15, + "name": "IndexAccess", + "src": "164:6:1" + } + ], + "id": 16, + "name": "VariableDeclarationStatement", + "src": "144:26:1" + }, + { + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "operator": "=", + "type": "address payable" + }, + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 9, + "type": "address payable", + "value": "r" + }, + "id": 17, + "name": "Identifier", + "src": "180:1:1" + }, + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 6, + "type": "address payable", + "value": "arg" + }, + "id": 18, + "name": "Identifier", + "src": "184:3:1" + } + ], + "id": 19, + "name": "Assignment", + "src": "180:7:1" + } + ], + "id": 20, + "name": "ExpressionStatement", + "src": "180:7:1" + }, + { + "attributes": + { + "assignments": + [ + 22 + ] + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "c", + "scope": 35, + "stateVariable": false, + "storageLocation": "default", + "type": "address", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "address", + "stateMutability": "nonpayable", + "type": "address" + }, + "id": 21, + "name": "ElementaryTypeName", + "src": "197:7:1" + } + ], + "id": 22, + "name": "VariableDeclaration", + "src": "197:9:1" + }, + { + "attributes": + { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "isStructConstructorCall": false, + "lValueRequested": false, + "names": + [ + null + ], + "type": "address", + "type_conversion": true + }, + "children": + [ + { + "attributes": + { + "argumentTypes": + [ + { + "typeIdentifier": "t_contract$_C_$37", + "typeString": "contract C" + } + ], + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "type": "type(address)", + "value": "address" + }, + "id": 23, + "name": "ElementaryTypeNameExpression", + "src": "209:7:1" + }, + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 66, + "type": "contract C", + "value": "this" + }, + "id": 24, + "name": "Identifier", + "src": "217:4:1" + } + ], + "id": 25, + "name": "FunctionCall", + "src": "209:13:1" + } + ], + "id": 26, + "name": "VariableDeclarationStatement", + "src": "197:25:1" + }, + { + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "operator": "=", + "type": "address payable" + }, + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "type": "address payable" + }, + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 4, + "type": "mapping(address => address payable)", + "value": "m" + }, + "id": 27, + "name": "Identifier", + "src": "232:1:1" + }, + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 22, + "type": "address", + "value": "c" + }, + "id": 28, + "name": "Identifier", + "src": "234:1:1" + } + ], + "id": 29, + "name": "IndexAccess", + "src": "232:4:1" + }, + { + "attributes": + { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": true, + "isStructConstructorCall": false, + "lValueRequested": false, + "names": + [ + null + ], + "type": "address payable", + "type_conversion": true + }, + "children": + [ + { + "attributes": + { + "argumentTypes": + [ + { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + } + ], + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "type": "type(address)", + "value": "address" + }, + "id": 30, + "name": "ElementaryTypeNameExpression", + "src": "239:7:1" + }, + { + "attributes": + { + "argumentTypes": null, + "hexvalue": "30", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "number", + "type": "int_const 0", + "value": "0" + }, + "id": 31, + "name": "Literal", + "src": "247:1:1" + } + ], + "id": 32, + "name": "FunctionCall", + "src": "239:10:1" + } + ], + "id": 33, + "name": "Assignment", + "src": "232:17:1" + } + ], + "id": 34, + "name": "ExpressionStatement", + "src": "232:17:1" + } + ], + "id": 35, + "name": "Block", + "src": "134:122:1" + } + ], + "id": 36, + "name": "FunctionDefinition", + "src": "67:189:1" + } + ], + "id": 37, + "name": "ContractDefinition", + "src": "0:258:1" + } + ], + "id": 38, + "name": "SourceUnit", + "src": "0:259:1" } diff --git a/test/libsolidity/ASTJSON/array_type_name.json b/test/libsolidity/ASTJSON/array_type_name.json index e3a3bea94..37802d1d4 100644 --- a/test/libsolidity/ASTJSON/array_type_name.json +++ b/test/libsolidity/ASTJSON/array_type_name.json @@ -1,76 +1,76 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 4 - ] - }, - "id" : 5, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 4, - "linearizedBaseContracts" : - [ - 4 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "constant" : false, - "id" : 3, - "name" : "i", - "nodeType" : "VariableDeclaration", - "scope" : 4, - "src" : "13:8:1", - "stateVariable" : true, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage", - "typeString" : "uint256[]" - }, - "typeName" : - { - "baseType" : - { - "id" : 1, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "13:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "id" : 2, - "length" : null, - "nodeType" : "ArrayTypeName", - "src" : "13:6:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", - "typeString" : "uint256[]" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "scope" : 5, - "src" : "0:24:1" - } - ], - "src" : "0:25:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 4 + ] + }, + "id": 5, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 4, + "linearizedBaseContracts": + [ + 4 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 3, + "name": "i", + "nodeType": "VariableDeclaration", + "scope": 4, + "src": "13:8:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage", + "typeString": "uint256[]" + }, + "typeName": + { + "baseType": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "13:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2, + "length": null, + "nodeType": "ArrayTypeName", + "src": "13:6:1", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage_ptr", + "typeString": "uint256[]" + } + }, + "value": null, + "visibility": "internal" + } + ], + "scope": 5, + "src": "0:24:1" + } + ], + "src": "0:25:1" } diff --git a/test/libsolidity/ASTJSON/array_type_name.sol b/test/libsolidity/ASTJSON/array_type_name.sol index 202ecf02b..97ed03510 100644 --- a/test/libsolidity/ASTJSON/array_type_name.sol +++ b/test/libsolidity/ASTJSON/array_type_name.sol @@ -1 +1,3 @@ contract C { uint[] i; } + +// ---- diff --git a/test/libsolidity/ASTJSON/array_type_name_legacy.json b/test/libsolidity/ASTJSON/array_type_name_legacy.json index 80feb344b..eed4ecbdc 100644 --- a/test/libsolidity/ASTJSON/array_type_name_legacy.json +++ b/test/libsolidity/ASTJSON/array_type_name_legacy.json @@ -1,89 +1,89 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 4 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 4 - ], - "name" : "C", - "scope" : 5 - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "i", - "scope" : 4, - "stateVariable" : true, - "storageLocation" : "default", - "type" : "uint256[]", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "length" : null, - "type" : "uint256[]" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 1, - "name" : "ElementaryTypeName", - "src" : "13:4:1" - } - ], - "id" : 2, - "name" : "ArrayTypeName", - "src" : "13:6:1" - } - ], - "id" : 3, - "name" : "VariableDeclaration", - "src" : "13:8:1" - } - ], - "id" : 4, - "name" : "ContractDefinition", - "src" : "0:24:1" - } - ], - "id" : 5, - "name" : "SourceUnit", - "src" : "0:25:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 4 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 4 + ], + "name": "C", + "scope": 5 + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "i", + "scope": 4, + "stateVariable": true, + "storageLocation": "default", + "type": "uint256[]", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "length": null, + "type": "uint256[]" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 1, + "name": "ElementaryTypeName", + "src": "13:4:1" + } + ], + "id": 2, + "name": "ArrayTypeName", + "src": "13:6:1" + } + ], + "id": 3, + "name": "VariableDeclaration", + "src": "13:8:1" + } + ], + "id": 4, + "name": "ContractDefinition", + "src": "0:24:1" + } + ], + "id": 5, + "name": "SourceUnit", + "src": "0:25:1" } diff --git a/test/libsolidity/ASTJSON/constructor.json b/test/libsolidity/ASTJSON/constructor.json index b0bc42019..89ffa5fd3 100644 --- a/test/libsolidity/ASTJSON/constructor.json +++ b/test/libsolidity/ASTJSON/constructor.json @@ -1,70 +1,70 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ] - }, - "id" : 6, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 5, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 3, - "nodeType" : "Block", - "src" : "35:4:1", - "statements" : [] - }, - "documentation" : null, - "id" : 4, - "implemented" : true, - "kind" : "constructor", - "modifiers" : [], - "name" : "", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "25:2:1" - }, - "returnParameters" : - { - "id" : 2, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "35:0:1" - }, - "scope" : 5, - "src" : "14:25:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 6, - "src" : "0:41:1" - } - ], - "src" : "0:42:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ] + }, + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "35:4:1", + "statements": [] + }, + "documentation": null, + "id": 4, + "implemented": true, + "kind": "constructor", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "25:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "35:0:1" + }, + "scope": 5, + "src": "14:25:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 6, + "src": "0:41:1" + } + ], + "src": "0:42:1" } diff --git a/test/libsolidity/ASTJSON/constructor.sol b/test/libsolidity/ASTJSON/constructor.sol index 79d04eb5a..f89ac45b4 100644 --- a/test/libsolidity/ASTJSON/constructor.sol +++ b/test/libsolidity/ASTJSON/constructor.sol @@ -2,3 +2,5 @@ contract C { constructor() public { } } + +// ---- diff --git a/test/libsolidity/ASTJSON/constructor_legacy.json b/test/libsolidity/ASTJSON/constructor_legacy.json index 0617073e3..fe89775a1 100644 --- a/test/libsolidity/ASTJSON/constructor_legacy.json +++ b/test/libsolidity/ASTJSON/constructor_legacy.json @@ -1,110 +1,110 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "scope" : 6 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : true, - "kind" : "constructor", - "modifiers" : - [ - null - ], - "name" : "", - "scope" : 5, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "25:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 2, - "name" : "ParameterList", - "src" : "35:0:1" - }, - { - "attributes" : - { - "statements" : - [ - null - ] - }, - "children" : [], - "id" : 3, - "name" : "Block", - "src" : "35:4:1" - } - ], - "id" : 4, - "name" : "FunctionDefinition", - "src" : "14:25:1" - } - ], - "id" : 5, - "name" : "ContractDefinition", - "src" : "0:41:1" - } - ], - "id" : 6, - "name" : "SourceUnit", - "src" : "0:42:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "scope": 6 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": true, + "kind": "constructor", + "modifiers": + [ + null + ], + "name": "", + "scope": 5, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "25:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "35:0:1" + }, + { + "attributes": + { + "statements": + [ + null + ] + }, + "children": [], + "id": 3, + "name": "Block", + "src": "35:4:1" + } + ], + "id": 4, + "name": "FunctionDefinition", + "src": "14:25:1" + } + ], + "id": 5, + "name": "ContractDefinition", + "src": "0:41:1" + } + ], + "id": 6, + "name": "SourceUnit", + "src": "0:42:1" } diff --git a/test/libsolidity/ASTJSON/contract_dep_order.json b/test/libsolidity/ASTJSON/contract_dep_order.json index de42ca600..521ad93f1 100644 --- a/test/libsolidity/ASTJSON/contract_dep_order.json +++ b/test/libsolidity/ASTJSON/contract_dep_order.json @@ -1,233 +1,233 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "A" : - [ - 1 - ], - "B" : - [ - 4 - ], - "C" : - [ - 7 - ], - "D" : - [ - 10 - ], - "E" : - [ - 13 - ] - }, - "id" : 14, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 1, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "A", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 14, - "src" : "0:14:1" - }, - { - "baseContracts" : - [ - { - "arguments" : null, - "baseName" : - { - "contractScope" : null, - "id" : 2, - "name" : "A", - "nodeType" : "UserDefinedTypeName", - "referencedDeclaration" : 1, - "src" : "29:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_contract$_A_$1", - "typeString" : "contract A" - } - }, - "id" : 3, - "nodeType" : "InheritanceSpecifier", - "src" : "29:1:1" - } - ], - "contractDependencies" : - [ - 1 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 4, - "linearizedBaseContracts" : - [ - 4, - 1 - ], - "name" : "B", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 14, - "src" : "15:19:1" - }, - { - "baseContracts" : - [ - { - "arguments" : null, - "baseName" : - { - "contractScope" : null, - "id" : 5, - "name" : "B", - "nodeType" : "UserDefinedTypeName", - "referencedDeclaration" : 4, - "src" : "49:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_contract$_B_$4", - "typeString" : "contract B" - } - }, - "id" : 6, - "nodeType" : "InheritanceSpecifier", - "src" : "49:1:1" - } - ], - "contractDependencies" : - [ - 1, - 4 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 7, - "linearizedBaseContracts" : - [ - 7, - 4, - 1 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 14, - "src" : "35:19:1" - }, - { - "baseContracts" : - [ - { - "arguments" : null, - "baseName" : - { - "contractScope" : null, - "id" : 8, - "name" : "C", - "nodeType" : "UserDefinedTypeName", - "referencedDeclaration" : 7, - "src" : "69:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_contract$_C_$7", - "typeString" : "contract C" - } - }, - "id" : 9, - "nodeType" : "InheritanceSpecifier", - "src" : "69:1:1" - } - ], - "contractDependencies" : - [ - 1, - 4, - 7 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 10, - "linearizedBaseContracts" : - [ - 10, - 7, - 4, - 1 - ], - "name" : "D", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 14, - "src" : "55:19:1" - }, - { - "baseContracts" : - [ - { - "arguments" : null, - "baseName" : - { - "contractScope" : null, - "id" : 11, - "name" : "D", - "nodeType" : "UserDefinedTypeName", - "referencedDeclaration" : 10, - "src" : "89:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_contract$_D_$10", - "typeString" : "contract D" - } - }, - "id" : 12, - "nodeType" : "InheritanceSpecifier", - "src" : "89:1:1" - } - ], - "contractDependencies" : - [ - 1, - 4, - 7, - 10 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 13, - "linearizedBaseContracts" : - [ - 13, - 10, - 7, - 4, - 1 - ], - "name" : "E", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 14, - "src" : "75:19:1" - } - ], - "src" : "0:95:1" + "absolutePath": "a", + "exportedSymbols": + { + "A": + [ + 1 + ], + "B": + [ + 4 + ], + "C": + [ + 7 + ], + "D": + [ + 10 + ], + "E": + [ + 13 + ] + }, + "id": 14, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 1, + "linearizedBaseContracts": + [ + 1 + ], + "name": "A", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 14, + "src": "0:14:1" + }, + { + "baseContracts": + [ + { + "arguments": null, + "baseName": + { + "contractScope": null, + "id": 2, + "name": "A", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 1, + "src": "29:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_A_$1", + "typeString": "contract A" + } + }, + "id": 3, + "nodeType": "InheritanceSpecifier", + "src": "29:1:1" + } + ], + "contractDependencies": + [ + 1 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 4, + "linearizedBaseContracts": + [ + 4, + 1 + ], + "name": "B", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 14, + "src": "15:19:1" + }, + { + "baseContracts": + [ + { + "arguments": null, + "baseName": + { + "contractScope": null, + "id": 5, + "name": "B", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 4, + "src": "49:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_B_$4", + "typeString": "contract B" + } + }, + "id": 6, + "nodeType": "InheritanceSpecifier", + "src": "49:1:1" + } + ], + "contractDependencies": + [ + 1, + 4 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 7, + "linearizedBaseContracts": + [ + 7, + 4, + 1 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 14, + "src": "35:19:1" + }, + { + "baseContracts": + [ + { + "arguments": null, + "baseName": + { + "contractScope": null, + "id": 8, + "name": "C", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 7, + "src": "69:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_C_$7", + "typeString": "contract C" + } + }, + "id": 9, + "nodeType": "InheritanceSpecifier", + "src": "69:1:1" + } + ], + "contractDependencies": + [ + 1, + 4, + 7 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 10, + "linearizedBaseContracts": + [ + 10, + 7, + 4, + 1 + ], + "name": "D", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 14, + "src": "55:19:1" + }, + { + "baseContracts": + [ + { + "arguments": null, + "baseName": + { + "contractScope": null, + "id": 11, + "name": "D", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 10, + "src": "89:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_D_$10", + "typeString": "contract D" + } + }, + "id": 12, + "nodeType": "InheritanceSpecifier", + "src": "89:1:1" + } + ], + "contractDependencies": + [ + 1, + 4, + 7, + 10 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 13, + "linearizedBaseContracts": + [ + 13, + 10, + 7, + 4, + 1 + ], + "name": "E", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 14, + "src": "75:19:1" + } + ], + "src": "0:95:1" } diff --git a/test/libsolidity/ASTJSON/contract_dep_order_legacy.json b/test/libsolidity/ASTJSON/contract_dep_order_legacy.json index 5990b52ca..ff80f7fb6 100644 --- a/test/libsolidity/ASTJSON/contract_dep_order_legacy.json +++ b/test/libsolidity/ASTJSON/contract_dep_order_legacy.json @@ -1,288 +1,288 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "A" : - [ - 1 - ], - "B" : - [ - 4 - ], - "C" : - [ - 7 - ], - "D" : - [ - 10 - ], - "E" : - [ - 13 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "A", - "nodes" : - [ - null - ], - "scope" : 14 - }, - "id" : 1, - "name" : "ContractDefinition", - "src" : "0:14:1" - }, - { - "attributes" : - { - "contractDependencies" : - [ - 1 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 4, - 1 - ], - "name" : "B", - "nodes" : - [ - null - ], - "scope" : 14 - }, - "children" : - [ - { - "attributes" : - { - "arguments" : null - }, - "children" : - [ - { - "attributes" : - { - "contractScope" : null, - "name" : "A", - "referencedDeclaration" : 1, - "type" : "contract A" - }, - "id" : 2, - "name" : "UserDefinedTypeName", - "src" : "29:1:1" - } - ], - "id" : 3, - "name" : "InheritanceSpecifier", - "src" : "29:1:1" - } - ], - "id" : 4, - "name" : "ContractDefinition", - "src" : "15:19:1" - }, - { - "attributes" : - { - "contractDependencies" : - [ - 1, - 4 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 7, - 4, - 1 - ], - "name" : "C", - "nodes" : - [ - null - ], - "scope" : 14 - }, - "children" : - [ - { - "attributes" : - { - "arguments" : null - }, - "children" : - [ - { - "attributes" : - { - "contractScope" : null, - "name" : "B", - "referencedDeclaration" : 4, - "type" : "contract B" - }, - "id" : 5, - "name" : "UserDefinedTypeName", - "src" : "49:1:1" - } - ], - "id" : 6, - "name" : "InheritanceSpecifier", - "src" : "49:1:1" - } - ], - "id" : 7, - "name" : "ContractDefinition", - "src" : "35:19:1" - }, - { - "attributes" : - { - "contractDependencies" : - [ - 1, - 4, - 7 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 10, - 7, - 4, - 1 - ], - "name" : "D", - "nodes" : - [ - null - ], - "scope" : 14 - }, - "children" : - [ - { - "attributes" : - { - "arguments" : null - }, - "children" : - [ - { - "attributes" : - { - "contractScope" : null, - "name" : "C", - "referencedDeclaration" : 7, - "type" : "contract C" - }, - "id" : 8, - "name" : "UserDefinedTypeName", - "src" : "69:1:1" - } - ], - "id" : 9, - "name" : "InheritanceSpecifier", - "src" : "69:1:1" - } - ], - "id" : 10, - "name" : "ContractDefinition", - "src" : "55:19:1" - }, - { - "attributes" : - { - "contractDependencies" : - [ - 1, - 4, - 7, - 10 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 13, - 10, - 7, - 4, - 1 - ], - "name" : "E", - "nodes" : - [ - null - ], - "scope" : 14 - }, - "children" : - [ - { - "attributes" : - { - "arguments" : null - }, - "children" : - [ - { - "attributes" : - { - "contractScope" : null, - "name" : "D", - "referencedDeclaration" : 10, - "type" : "contract D" - }, - "id" : 11, - "name" : "UserDefinedTypeName", - "src" : "89:1:1" - } - ], - "id" : 12, - "name" : "InheritanceSpecifier", - "src" : "89:1:1" - } - ], - "id" : 13, - "name" : "ContractDefinition", - "src" : "75:19:1" - } - ], - "id" : 14, - "name" : "SourceUnit", - "src" : "0:95:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "A": + [ + 1 + ], + "B": + [ + 4 + ], + "C": + [ + 7 + ], + "D": + [ + 10 + ], + "E": + [ + 13 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 1 + ], + "name": "A", + "nodes": + [ + null + ], + "scope": 14 + }, + "id": 1, + "name": "ContractDefinition", + "src": "0:14:1" + }, + { + "attributes": + { + "contractDependencies": + [ + 1 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 4, + 1 + ], + "name": "B", + "nodes": + [ + null + ], + "scope": 14 + }, + "children": + [ + { + "attributes": + { + "arguments": null + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "A", + "referencedDeclaration": 1, + "type": "contract A" + }, + "id": 2, + "name": "UserDefinedTypeName", + "src": "29:1:1" + } + ], + "id": 3, + "name": "InheritanceSpecifier", + "src": "29:1:1" + } + ], + "id": 4, + "name": "ContractDefinition", + "src": "15:19:1" + }, + { + "attributes": + { + "contractDependencies": + [ + 1, + 4 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 7, + 4, + 1 + ], + "name": "C", + "nodes": + [ + null + ], + "scope": 14 + }, + "children": + [ + { + "attributes": + { + "arguments": null + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "B", + "referencedDeclaration": 4, + "type": "contract B" + }, + "id": 5, + "name": "UserDefinedTypeName", + "src": "49:1:1" + } + ], + "id": 6, + "name": "InheritanceSpecifier", + "src": "49:1:1" + } + ], + "id": 7, + "name": "ContractDefinition", + "src": "35:19:1" + }, + { + "attributes": + { + "contractDependencies": + [ + 1, + 4, + 7 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 10, + 7, + 4, + 1 + ], + "name": "D", + "nodes": + [ + null + ], + "scope": 14 + }, + "children": + [ + { + "attributes": + { + "arguments": null + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "C", + "referencedDeclaration": 7, + "type": "contract C" + }, + "id": 8, + "name": "UserDefinedTypeName", + "src": "69:1:1" + } + ], + "id": 9, + "name": "InheritanceSpecifier", + "src": "69:1:1" + } + ], + "id": 10, + "name": "ContractDefinition", + "src": "55:19:1" + }, + { + "attributes": + { + "contractDependencies": + [ + 1, + 4, + 7, + 10 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 13, + 10, + 7, + 4, + 1 + ], + "name": "E", + "nodes": + [ + null + ], + "scope": 14 + }, + "children": + [ + { + "attributes": + { + "arguments": null + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "D", + "referencedDeclaration": 10, + "type": "contract D" + }, + "id": 11, + "name": "UserDefinedTypeName", + "src": "89:1:1" + } + ], + "id": 12, + "name": "InheritanceSpecifier", + "src": "89:1:1" + } + ], + "id": 13, + "name": "ContractDefinition", + "src": "75:19:1" + } + ], + "id": 14, + "name": "SourceUnit", + "src": "0:95:1" } diff --git a/test/libsolidity/ASTJSON/documentation.json b/test/libsolidity/ASTJSON/documentation.json index ce1e0b57a..ba66e73a9 100644 --- a/test/libsolidity/ASTJSON/documentation.json +++ b/test/libsolidity/ASTJSON/documentation.json @@ -1,180 +1,180 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 1 - ] - }, - "id" : 2, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : "This contract is empty", - "fullyImplemented" : true, - "id" : 1, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 2, - "src" : "28:13:1" - } - ], - "src" : "28:14:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 1 + ] + }, + "id": 2, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": "This contract is empty", + "fullyImplemented": true, + "id": 1, + "linearizedBaseContracts": + [ + 1 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 2, + "src": "28:13:1" + } + ], + "src": "28:14:1" }, { - "absolutePath" : "b", - "exportedSymbols" : - { - "C" : - [ - 3 - ] - }, - "id" : 4, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : "This contract is empty\nand has a line-breaking comment.", - "fullyImplemented" : true, - "id" : 3, - "linearizedBaseContracts" : - [ - 3 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 4, - "src" : "62:13:2" - } - ], - "src" : "62:14:2" + "absolutePath": "b", + "exportedSymbols": + { + "C": + [ + 3 + ] + }, + "id": 4, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": "This contract is empty\nand has a line-breaking comment.", + "fullyImplemented": true, + "id": 3, + "linearizedBaseContracts": + [ + 3 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 4, + "src": "62:13:2" + } + ], + "src": "62:14:2" }, { - "absolutePath" : "c", - "exportedSymbols" : - { - "C" : - [ - 15 - ] - }, - "id" : 16, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 15, - "linearizedBaseContracts" : - [ - 15 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "anonymous" : false, - "documentation" : "Some comment on Evt.", - "id" : 6, - "name" : "Evt", - "nodeType" : "EventDefinition", - "parameters" : - { - "id" : 5, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "51:2:3" - }, - "src" : "42:12:3" - }, - { - "body" : - { - "id" : 9, - "nodeType" : "Block", - "src" : "99:6:3", - "statements" : - [ - { - "id" : 8, - "nodeType" : "PlaceholderStatement", - "src" : "101:1:3" - } - ] - }, - "documentation" : "Some comment on mod.", - "id" : 10, - "name" : "mod", - "nodeType" : "ModifierDefinition", - "parameters" : - { - "id" : 7, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "96:2:3" - }, - "src" : "84:21:3", - "visibility" : "internal" - }, - { - "body" : - { - "id" : 13, - "nodeType" : "Block", - "src" : "155:2:3", - "statements" : [] - }, - "documentation" : "Some comment on fn.", - "id" : 14, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "fn", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 11, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "145:2:3" - }, - "returnParameters" : - { - "id" : 12, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "155:0:3" - }, - "scope" : 15, - "src" : "134:23:3", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 16, - "src" : "0:159:3" - } - ], - "src" : "0:160:3" + "absolutePath": "c", + "exportedSymbols": + { + "C": + [ + 15 + ] + }, + "id": 16, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 15, + "linearizedBaseContracts": + [ + 15 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "documentation": "Some comment on Evt.", + "id": 6, + "name": "Evt", + "nodeType": "EventDefinition", + "parameters": + { + "id": 5, + "nodeType": "ParameterList", + "parameters": [], + "src": "51:2:3" + }, + "src": "42:12:3" + }, + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "99:6:3", + "statements": + [ + { + "id": 8, + "nodeType": "PlaceholderStatement", + "src": "101:1:3" + } + ] + }, + "documentation": "Some comment on mod.", + "id": 10, + "name": "mod", + "nodeType": "ModifierDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": [], + "src": "96:2:3" + }, + "src": "84:21:3", + "visibility": "internal" + }, + { + "body": + { + "id": 13, + "nodeType": "Block", + "src": "155:2:3", + "statements": [] + }, + "documentation": "Some comment on fn.", + "id": 14, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "fn", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "145:2:3" + }, + "returnParameters": + { + "id": 12, + "nodeType": "ParameterList", + "parameters": [], + "src": "155:0:3" + }, + "scope": 15, + "src": "134:23:3", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 16, + "src": "0:159:3" + } + ], + "src": "0:160:3" } diff --git a/test/libsolidity/ASTJSON/documentation.sol b/test/libsolidity/ASTJSON/documentation.sol index e65af9b44..5c5fe7d2b 100644 --- a/test/libsolidity/ASTJSON/documentation.sol +++ b/test/libsolidity/ASTJSON/documentation.sol @@ -15,3 +15,5 @@ contract C { /** Some comment on mod.*/ modifier mod() { _; } /** Some comment on fn.*/ function fn() public {} } + +// ---- diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json index 0277902f8..caf5e43d9 100644 --- a/test/libsolidity/ASTJSON/documentation_legacy.json +++ b/test/libsolidity/ASTJSON/documentation_legacy.json @@ -1,177 +1,177 @@ { - "attributes" : - { - "absolutePath" : "c", - "exportedSymbols" : - { - "C" : - [ - 15 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 15 - ], - "name" : "C", - "scope" : 16 - }, - "children" : - [ - { - "attributes" : - { - "anonymous" : false, - "documentation" : "Some comment on Evt.", - "name" : "Evt" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 5, - "name" : "ParameterList", - "src" : "51:2:3" - } - ], - "id" : 6, - "name" : "EventDefinition", - "src" : "42:12:3" - }, - { - "attributes" : - { - "documentation" : "Some comment on mod.", - "name" : "mod", - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 7, - "name" : "ParameterList", - "src" : "96:2:3" - }, - { - "children" : - [ - { - "id" : 8, - "name" : "PlaceholderStatement", - "src" : "101:1:3" - } - ], - "id" : 9, - "name" : "Block", - "src" : "99:6:3" - } - ], - "id" : 10, - "name" : "ModifierDefinition", - "src" : "84:21:3" - }, - { - "attributes" : - { - "documentation" : "Some comment on fn.", - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "fn", - "scope" : 15, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 11, - "name" : "ParameterList", - "src" : "145:2:3" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 12, - "name" : "ParameterList", - "src" : "155:0:3" - }, - { - "attributes" : - { - "statements" : - [ - null - ] - }, - "children" : [], - "id" : 13, - "name" : "Block", - "src" : "155:2:3" - } - ], - "id" : 14, - "name" : "FunctionDefinition", - "src" : "134:23:3" - } - ], - "id" : 15, - "name" : "ContractDefinition", - "src" : "0:159:3" - } - ], - "id" : 16, - "name" : "SourceUnit", - "src" : "0:160:3" + "attributes": + { + "absolutePath": "c", + "exportedSymbols": + { + "C": + [ + 15 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 15 + ], + "name": "C", + "scope": 16 + }, + "children": + [ + { + "attributes": + { + "anonymous": false, + "documentation": "Some comment on Evt.", + "name": "Evt" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 5, + "name": "ParameterList", + "src": "51:2:3" + } + ], + "id": 6, + "name": "EventDefinition", + "src": "42:12:3" + }, + { + "attributes": + { + "documentation": "Some comment on mod.", + "name": "mod", + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 7, + "name": "ParameterList", + "src": "96:2:3" + }, + { + "children": + [ + { + "id": 8, + "name": "PlaceholderStatement", + "src": "101:1:3" + } + ], + "id": 9, + "name": "Block", + "src": "99:6:3" + } + ], + "id": 10, + "name": "ModifierDefinition", + "src": "84:21:3" + }, + { + "attributes": + { + "documentation": "Some comment on fn.", + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "fn", + "scope": 15, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 11, + "name": "ParameterList", + "src": "145:2:3" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 12, + "name": "ParameterList", + "src": "155:0:3" + }, + { + "attributes": + { + "statements": + [ + null + ] + }, + "children": [], + "id": 13, + "name": "Block", + "src": "155:2:3" + } + ], + "id": 14, + "name": "FunctionDefinition", + "src": "134:23:3" + } + ], + "id": 15, + "name": "ContractDefinition", + "src": "0:159:3" + } + ], + "id": 16, + "name": "SourceUnit", + "src": "0:160:3" } diff --git a/test/libsolidity/ASTJSON/enum_value.json b/test/libsolidity/ASTJSON/enum_value.json index 21afd9a78..a8562c4d4 100644 --- a/test/libsolidity/ASTJSON/enum_value.json +++ b/test/libsolidity/ASTJSON/enum_value.json @@ -1,57 +1,57 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 4 - ] - }, - "id" : 5, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 4, - "linearizedBaseContracts" : - [ - 4 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "canonicalName" : "C.E", - "id" : 3, - "members" : - [ - { - "id" : 1, - "name" : "A", - "nodeType" : "EnumValue", - "src" : "22:1:1" - }, - { - "id" : 2, - "name" : "B", - "nodeType" : "EnumValue", - "src" : "25:1:1" - } - ], - "name" : "E", - "nodeType" : "EnumDefinition", - "src" : "13:15:1" - } - ], - "scope" : 5, - "src" : "0:30:1" - } - ], - "src" : "0:31:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 4 + ] + }, + "id": 5, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 4, + "linearizedBaseContracts": + [ + 4 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "canonicalName": "C.E", + "id": 3, + "members": + [ + { + "id": 1, + "name": "A", + "nodeType": "EnumValue", + "src": "22:1:1" + }, + { + "id": 2, + "name": "B", + "nodeType": "EnumValue", + "src": "25:1:1" + } + ], + "name": "E", + "nodeType": "EnumDefinition", + "src": "13:15:1" + } + ], + "scope": 5, + "src": "0:30:1" + } + ], + "src": "0:31:1" } diff --git a/test/libsolidity/ASTJSON/enum_value.sol b/test/libsolidity/ASTJSON/enum_value.sol index ef0875fbe..37e450f22 100644 --- a/test/libsolidity/ASTJSON/enum_value.sol +++ b/test/libsolidity/ASTJSON/enum_value.sol @@ -1 +1,3 @@ contract C { enum E { A, B } } + +// ---- diff --git a/test/libsolidity/ASTJSON/enum_value_legacy.json b/test/libsolidity/ASTJSON/enum_value_legacy.json index d7782969b..9bc5319bf 100644 --- a/test/libsolidity/ASTJSON/enum_value_legacy.json +++ b/test/libsolidity/ASTJSON/enum_value_legacy.json @@ -1,78 +1,78 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 4 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 4 - ], - "name" : "C", - "scope" : 5 - }, - "children" : - [ - { - "attributes" : - { - "canonicalName" : "C.E", - "name" : "E" - }, - "children" : - [ - { - "attributes" : - { - "name" : "A" - }, - "id" : 1, - "name" : "EnumValue", - "src" : "22:1:1" - }, - { - "attributes" : - { - "name" : "B" - }, - "id" : 2, - "name" : "EnumValue", - "src" : "25:1:1" - } - ], - "id" : 3, - "name" : "EnumDefinition", - "src" : "13:15:1" - } - ], - "id" : 4, - "name" : "ContractDefinition", - "src" : "0:30:1" - } - ], - "id" : 5, - "name" : "SourceUnit", - "src" : "0:31:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 4 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 4 + ], + "name": "C", + "scope": 5 + }, + "children": + [ + { + "attributes": + { + "canonicalName": "C.E", + "name": "E" + }, + "children": + [ + { + "attributes": + { + "name": "A" + }, + "id": 1, + "name": "EnumValue", + "src": "22:1:1" + }, + { + "attributes": + { + "name": "B" + }, + "id": 2, + "name": "EnumValue", + "src": "25:1:1" + } + ], + "id": 3, + "name": "EnumDefinition", + "src": "13:15:1" + } + ], + "id": 4, + "name": "ContractDefinition", + "src": "0:30:1" + } + ], + "id": 5, + "name": "SourceUnit", + "src": "0:31:1" } diff --git a/test/libsolidity/ASTJSON/event_definition.json b/test/libsolidity/ASTJSON/event_definition.json index 029062c32..45f16ba85 100644 --- a/test/libsolidity/ASTJSON/event_definition.json +++ b/test/libsolidity/ASTJSON/event_definition.json @@ -1,50 +1,50 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 3 - ] - }, - "id" : 4, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 3, - "linearizedBaseContracts" : - [ - 3 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "anonymous" : false, - "documentation" : null, - "id" : 2, - "name" : "E", - "nodeType" : "EventDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "20:2:1" - }, - "src" : "13:10:1" - } - ], - "scope" : 4, - "src" : "0:25:1" - } - ], - "src" : "0:26:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 3 + ] + }, + "id": 4, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 3, + "linearizedBaseContracts": + [ + 3 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "documentation": null, + "id": 2, + "name": "E", + "nodeType": "EventDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "20:2:1" + }, + "src": "13:10:1" + } + ], + "scope": 4, + "src": "0:25:1" + } + ], + "src": "0:26:1" } diff --git a/test/libsolidity/ASTJSON/event_definition.sol b/test/libsolidity/ASTJSON/event_definition.sol index 81b43c672..9fa7eaeb5 100644 --- a/test/libsolidity/ASTJSON/event_definition.sol +++ b/test/libsolidity/ASTJSON/event_definition.sol @@ -1 +1,3 @@ contract C { event E(); } + +// ---- diff --git a/test/libsolidity/ASTJSON/event_definition_legacy.json b/test/libsolidity/ASTJSON/event_definition_legacy.json index f5967bf4e..5f0859fea 100644 --- a/test/libsolidity/ASTJSON/event_definition_legacy.json +++ b/test/libsolidity/ASTJSON/event_definition_legacy.json @@ -1,74 +1,74 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 3 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 3 - ], - "name" : "C", - "scope" : 4 - }, - "children" : - [ - { - "attributes" : - { - "anonymous" : false, - "documentation" : null, - "name" : "E" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "20:2:1" - } - ], - "id" : 2, - "name" : "EventDefinition", - "src" : "13:10:1" - } - ], - "id" : 3, - "name" : "ContractDefinition", - "src" : "0:25:1" - } - ], - "id" : 4, - "name" : "SourceUnit", - "src" : "0:26:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 3 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 3 + ], + "name": "C", + "scope": 4 + }, + "children": + [ + { + "attributes": + { + "anonymous": false, + "documentation": null, + "name": "E" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "20:2:1" + } + ], + "id": 2, + "name": "EventDefinition", + "src": "13:10:1" + } + ], + "id": 3, + "name": "ContractDefinition", + "src": "0:25:1" + } + ], + "id": 4, + "name": "SourceUnit", + "src": "0:26:1" } diff --git a/test/libsolidity/ASTJSON/fallback.json b/test/libsolidity/ASTJSON/fallback.json index a9c85b2fb..2e089b2b9 100644 --- a/test/libsolidity/ASTJSON/fallback.json +++ b/test/libsolidity/ASTJSON/fallback.json @@ -1,70 +1,70 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ] - }, - "id" : 6, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 5, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 3, - "nodeType" : "Block", - "src" : "43:5:1", - "statements" : [] - }, - "documentation" : null, - "id" : 4, - "implemented" : true, - "kind" : "fallback", - "modifiers" : [], - "name" : "", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "23:2:1" - }, - "returnParameters" : - { - "id" : 2, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "43:0:1" - }, - "scope" : 5, - "src" : "15:33:1", - "stateMutability" : "payable", - "superFunction" : null, - "visibility" : "external" - } - ], - "scope" : 6, - "src" : "0:50:1" - } - ], - "src" : "0:51:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ] + }, + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "43:5:1", + "statements": [] + }, + "documentation": null, + "id": 4, + "implemented": true, + "kind": "fallback", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "43:0:1" + }, + "scope": 5, + "src": "15:33:1", + "stateMutability": "payable", + "superFunction": null, + "visibility": "external" + } + ], + "scope": 6, + "src": "0:50:1" + } + ], + "src": "0:51:1" } diff --git a/test/libsolidity/ASTJSON/fallback.sol b/test/libsolidity/ASTJSON/fallback.sol index 4e318892e..7d346042d 100644 --- a/test/libsolidity/ASTJSON/fallback.sol +++ b/test/libsolidity/ASTJSON/fallback.sol @@ -2,3 +2,5 @@ contract C { function() external payable { } } + +// ---- diff --git a/test/libsolidity/ASTJSON/fallback_legacy.json b/test/libsolidity/ASTJSON/fallback_legacy.json index 0aca3128e..1b4c90926 100644 --- a/test/libsolidity/ASTJSON/fallback_legacy.json +++ b/test/libsolidity/ASTJSON/fallback_legacy.json @@ -1,110 +1,110 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "scope" : 6 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "fallback", - "modifiers" : - [ - null - ], - "name" : "", - "scope" : 5, - "stateMutability" : "payable", - "superFunction" : null, - "visibility" : "external" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "23:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 2, - "name" : "ParameterList", - "src" : "43:0:1" - }, - { - "attributes" : - { - "statements" : - [ - null - ] - }, - "children" : [], - "id" : 3, - "name" : "Block", - "src" : "43:5:1" - } - ], - "id" : 4, - "name" : "FunctionDefinition", - "src" : "15:33:1" - } - ], - "id" : 5, - "name" : "ContractDefinition", - "src" : "0:50:1" - } - ], - "id" : 6, - "name" : "SourceUnit", - "src" : "0:51:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "scope": 6 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "fallback", + "modifiers": + [ + null + ], + "name": "", + "scope": 5, + "stateMutability": "payable", + "superFunction": null, + "visibility": "external" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "23:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "43:0:1" + }, + { + "attributes": + { + "statements": + [ + null + ] + }, + "children": [], + "id": 3, + "name": "Block", + "src": "43:5:1" + } + ], + "id": 4, + "name": "FunctionDefinition", + "src": "15:33:1" + } + ], + "id": 5, + "name": "ContractDefinition", + "src": "0:50:1" + } + ], + "id": 6, + "name": "SourceUnit", + "src": "0:51:1" } diff --git a/test/libsolidity/ASTJSON/fallback_payable.json b/test/libsolidity/ASTJSON/fallback_payable.json index 9d56f74bd..da8fc641d 100644 --- a/test/libsolidity/ASTJSON/fallback_payable.json +++ b/test/libsolidity/ASTJSON/fallback_payable.json @@ -1,70 +1,70 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ] - }, - "id" : 6, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 5, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 3, - "nodeType" : "Block", - "src" : "34:2:1", - "statements" : [] - }, - "documentation" : null, - "id" : 4, - "implemented" : true, - "kind" : "fallback", - "modifiers" : [], - "name" : "", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "22:2:1" - }, - "returnParameters" : - { - "id" : 2, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "34:0:1" - }, - "scope" : 5, - "src" : "14:22:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "external" - } - ], - "scope" : 6, - "src" : "0:38:1" - } - ], - "src" : "0:39:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ] + }, + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "34:2:1", + "statements": [] + }, + "documentation": null, + "id": 4, + "implemented": true, + "kind": "fallback", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "22:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "34:0:1" + }, + "scope": 5, + "src": "14:22:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "external" + } + ], + "scope": 6, + "src": "0:38:1" + } + ], + "src": "0:39:1" } diff --git a/test/libsolidity/ASTJSON/fallback_payable.sol b/test/libsolidity/ASTJSON/fallback_payable.sol index 21db99ecd..4a3cee6b5 100644 --- a/test/libsolidity/ASTJSON/fallback_payable.sol +++ b/test/libsolidity/ASTJSON/fallback_payable.sol @@ -1,3 +1,5 @@ contract C { function() external {} } + +// ---- diff --git a/test/libsolidity/ASTJSON/fallback_payable_legacy.json b/test/libsolidity/ASTJSON/fallback_payable_legacy.json index 7320f574a..8776b5efd 100644 --- a/test/libsolidity/ASTJSON/fallback_payable_legacy.json +++ b/test/libsolidity/ASTJSON/fallback_payable_legacy.json @@ -1,110 +1,110 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "scope" : 6 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "fallback", - "modifiers" : - [ - null - ], - "name" : "", - "scope" : 5, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "external" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "22:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 2, - "name" : "ParameterList", - "src" : "34:0:1" - }, - { - "attributes" : - { - "statements" : - [ - null - ] - }, - "children" : [], - "id" : 3, - "name" : "Block", - "src" : "34:2:1" - } - ], - "id" : 4, - "name" : "FunctionDefinition", - "src" : "14:22:1" - } - ], - "id" : 5, - "name" : "ContractDefinition", - "src" : "0:38:1" - } - ], - "id" : 6, - "name" : "SourceUnit", - "src" : "0:39:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "scope": 6 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "fallback", + "modifiers": + [ + null + ], + "name": "", + "scope": 5, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "external" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "22:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "34:0:1" + }, + { + "attributes": + { + "statements": + [ + null + ] + }, + "children": [], + "id": 3, + "name": "Block", + "src": "34:2:1" + } + ], + "id": 4, + "name": "FunctionDefinition", + "src": "14:22:1" + } + ], + "id": 5, + "name": "ContractDefinition", + "src": "0:38:1" + } + ], + "id": 6, + "name": "SourceUnit", + "src": "0:39:1" } diff --git a/test/libsolidity/ASTJSON/function_type.json b/test/libsolidity/ASTJSON/function_type.json index b78d84463..1a0456efb 100644 --- a/test/libsolidity/ASTJSON/function_type.json +++ b/test/libsolidity/ASTJSON/function_type.json @@ -1,224 +1,224 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 17 - ] - }, - "id" : 18, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 17, - "linearizedBaseContracts" : - [ - 17 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 15, - "nodeType" : "Block", - "src" : "120:2:1", - "statements" : [] - }, - "documentation" : null, - "id" : 16, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "f", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 7, - "nodeType" : "ParameterList", - "parameters" : - [ - { - "constant" : false, - "id" : 6, - "name" : "x", - "nodeType" : "VariableDeclaration", - "scope" : 16, - "src" : "24:44:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_function_external_payable$__$returns$_t_uint256_$", - "typeString" : "function () payable external returns (uint256)" - }, - "typeName" : - { - "id" : 5, - "nodeType" : "FunctionTypeName", - "parameterTypes" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "32:2:1" - }, - "returnParameterTypes" : - { - "id" : 4, - "nodeType" : "ParameterList", - "parameters" : - [ - { - "constant" : false, - "id" : 3, - "name" : "", - "nodeType" : "VariableDeclaration", - "scope" : 5, - "src" : "61:4:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - }, - "typeName" : - { - "id" : 2, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "61:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "src" : "60:6:1" - }, - "src" : "24:44:1", - "stateMutability" : "payable", - "typeDescriptions" : - { - "typeIdentifier" : "t_function_external_payable$__$returns$_t_uint256_$", - "typeString" : "function () payable external returns (uint256)" - }, - "visibility" : "external" - }, - "value" : null, - "visibility" : "internal" - } - ], - "src" : "23:46:1" - }, - "returnParameters" : - { - "id" : 14, - "nodeType" : "ParameterList", - "parameters" : - [ - { - "constant" : false, - "id" : 13, - "name" : "", - "nodeType" : "VariableDeclaration", - "scope" : 16, - "src" : "79:40:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_function_external_view$__$returns$_t_uint256_$", - "typeString" : "function () view external returns (uint256)" - }, - "typeName" : - { - "id" : 12, - "nodeType" : "FunctionTypeName", - "parameterTypes" : - { - "id" : 8, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "87:2:1" - }, - "returnParameterTypes" : - { - "id" : 11, - "nodeType" : "ParameterList", - "parameters" : - [ - { - "constant" : false, - "id" : 10, - "name" : "", - "nodeType" : "VariableDeclaration", - "scope" : 12, - "src" : "113:4:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - }, - "typeName" : - { - "id" : 9, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "113:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "src" : "112:6:1" - }, - "src" : "79:40:1", - "stateMutability" : "view", - "typeDescriptions" : - { - "typeIdentifier" : "t_function_external_view$__$returns$_t_uint256_$", - "typeString" : "function () view external returns (uint256)" - }, - "visibility" : "external" - }, - "value" : null, - "visibility" : "internal" - } - ], - "src" : "78:41:1" - }, - "scope" : 17, - "src" : "13:109:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 18, - "src" : "0:124:1" - } - ], - "src" : "0:125:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 17 + ] + }, + "id": 18, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 17, + "linearizedBaseContracts": + [ + 17 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 15, + "nodeType": "Block", + "src": "120:2:1", + "statements": [] + }, + "documentation": null, + "id": 16, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 6, + "name": "x", + "nodeType": "VariableDeclaration", + "scope": 16, + "src": "24:44:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_function_external_payable$__$returns$_t_uint256_$", + "typeString": "function () payable external returns (uint256)" + }, + "typeName": + { + "id": 5, + "nodeType": "FunctionTypeName", + "parameterTypes": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "32:2:1" + }, + "returnParameterTypes": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 3, + "name": "", + "nodeType": "VariableDeclaration", + "scope": 5, + "src": "61:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "61:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "60:6:1" + }, + "src": "24:44:1", + "stateMutability": "payable", + "typeDescriptions": + { + "typeIdentifier": "t_function_external_payable$__$returns$_t_uint256_$", + "typeString": "function () payable external returns (uint256)" + }, + "visibility": "external" + }, + "value": null, + "visibility": "internal" + } + ], + "src": "23:46:1" + }, + "returnParameters": + { + "id": 14, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 13, + "name": "", + "nodeType": "VariableDeclaration", + "scope": 16, + "src": "79:40:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_function_external_view$__$returns$_t_uint256_$", + "typeString": "function () view external returns (uint256)" + }, + "typeName": + { + "id": 12, + "nodeType": "FunctionTypeName", + "parameterTypes": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": [], + "src": "87:2:1" + }, + "returnParameterTypes": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 10, + "name": "", + "nodeType": "VariableDeclaration", + "scope": 12, + "src": "113:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 9, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "113:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "112:6:1" + }, + "src": "79:40:1", + "stateMutability": "view", + "typeDescriptions": + { + "typeIdentifier": "t_function_external_view$__$returns$_t_uint256_$", + "typeString": "function () view external returns (uint256)" + }, + "visibility": "external" + }, + "value": null, + "visibility": "internal" + } + ], + "src": "78:41:1" + }, + "scope": 17, + "src": "13:109:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 18, + "src": "0:124:1" + } + ], + "src": "0:125:1" } diff --git a/test/libsolidity/ASTJSON/function_type_legacy.json b/test/libsolidity/ASTJSON/function_type_legacy.json index 72ceec81e..80c8f51cf 100644 --- a/test/libsolidity/ASTJSON/function_type_legacy.json +++ b/test/libsolidity/ASTJSON/function_type_legacy.json @@ -1,266 +1,266 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 17 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 17 - ], - "name" : "C", - "scope" : 18 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "f", - "scope" : 17, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "x", - "scope" : 16, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "function () payable external returns (uint256)", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "stateMutability" : "payable", - "type" : "function () payable external returns (uint256)", - "visibility" : "external" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "32:2:1" - }, - { - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "", - "scope" : 5, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "uint256", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 2, - "name" : "ElementaryTypeName", - "src" : "61:4:1" - } - ], - "id" : 3, - "name" : "VariableDeclaration", - "src" : "61:4:1" - } - ], - "id" : 4, - "name" : "ParameterList", - "src" : "60:6:1" - } - ], - "id" : 5, - "name" : "FunctionTypeName", - "src" : "24:44:1" - } - ], - "id" : 6, - "name" : "VariableDeclaration", - "src" : "24:44:1" - } - ], - "id" : 7, - "name" : "ParameterList", - "src" : "23:46:1" - }, - { - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "", - "scope" : 16, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "function () view external returns (uint256)", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "stateMutability" : "view", - "type" : "function () view external returns (uint256)", - "visibility" : "external" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 8, - "name" : "ParameterList", - "src" : "87:2:1" - }, - { - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "", - "scope" : 12, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "uint256", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 9, - "name" : "ElementaryTypeName", - "src" : "113:4:1" - } - ], - "id" : 10, - "name" : "VariableDeclaration", - "src" : "113:4:1" - } - ], - "id" : 11, - "name" : "ParameterList", - "src" : "112:6:1" - } - ], - "id" : 12, - "name" : "FunctionTypeName", - "src" : "79:40:1" - } - ], - "id" : 13, - "name" : "VariableDeclaration", - "src" : "79:40:1" - } - ], - "id" : 14, - "name" : "ParameterList", - "src" : "78:41:1" - }, - { - "attributes" : - { - "statements" : - [ - null - ] - }, - "children" : [], - "id" : 15, - "name" : "Block", - "src" : "120:2:1" - } - ], - "id" : 16, - "name" : "FunctionDefinition", - "src" : "13:109:1" - } - ], - "id" : 17, - "name" : "ContractDefinition", - "src" : "0:124:1" - } - ], - "id" : 18, - "name" : "SourceUnit", - "src" : "0:125:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 17 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 17 + ], + "name": "C", + "scope": 18 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "scope": 17, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "x", + "scope": 16, + "stateVariable": false, + "storageLocation": "default", + "type": "function () payable external returns (uint256)", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "stateMutability": "payable", + "type": "function () payable external returns (uint256)", + "visibility": "external" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "32:2:1" + }, + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "", + "scope": 5, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 2, + "name": "ElementaryTypeName", + "src": "61:4:1" + } + ], + "id": 3, + "name": "VariableDeclaration", + "src": "61:4:1" + } + ], + "id": 4, + "name": "ParameterList", + "src": "60:6:1" + } + ], + "id": 5, + "name": "FunctionTypeName", + "src": "24:44:1" + } + ], + "id": 6, + "name": "VariableDeclaration", + "src": "24:44:1" + } + ], + "id": 7, + "name": "ParameterList", + "src": "23:46:1" + }, + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "", + "scope": 16, + "stateVariable": false, + "storageLocation": "default", + "type": "function () view external returns (uint256)", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "stateMutability": "view", + "type": "function () view external returns (uint256)", + "visibility": "external" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 8, + "name": "ParameterList", + "src": "87:2:1" + }, + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "", + "scope": 12, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 9, + "name": "ElementaryTypeName", + "src": "113:4:1" + } + ], + "id": 10, + "name": "VariableDeclaration", + "src": "113:4:1" + } + ], + "id": 11, + "name": "ParameterList", + "src": "112:6:1" + } + ], + "id": 12, + "name": "FunctionTypeName", + "src": "79:40:1" + } + ], + "id": 13, + "name": "VariableDeclaration", + "src": "79:40:1" + } + ], + "id": 14, + "name": "ParameterList", + "src": "78:41:1" + }, + { + "attributes": + { + "statements": + [ + null + ] + }, + "children": [], + "id": 15, + "name": "Block", + "src": "120:2:1" + } + ], + "id": 16, + "name": "FunctionDefinition", + "src": "13:109:1" + } + ], + "id": 17, + "name": "ContractDefinition", + "src": "0:124:1" + } + ], + "id": 18, + "name": "SourceUnit", + "src": "0:125:1" } diff --git a/test/libsolidity/ASTJSON/inheritance_specifier.json b/test/libsolidity/ASTJSON/inheritance_specifier.json index edef86779..9de5f68b8 100644 --- a/test/libsolidity/ASTJSON/inheritance_specifier.json +++ b/test/libsolidity/ASTJSON/inheritance_specifier.json @@ -1,80 +1,80 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C1" : - [ - 1 - ], - "C2" : - [ - 4 - ] - }, - "id" : 5, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 1, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "C1", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 5, - "src" : "0:14:1" - }, - { - "baseContracts" : - [ - { - "arguments" : null, - "baseName" : - { - "contractScope" : null, - "id" : 2, - "name" : "C1", - "nodeType" : "UserDefinedTypeName", - "referencedDeclaration" : 1, - "src" : "30:2:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_contract$_C1_$1", - "typeString" : "contract C1" - } - }, - "id" : 3, - "nodeType" : "InheritanceSpecifier", - "src" : "30:2:1" - } - ], - "contractDependencies" : - [ - 1 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 4, - "linearizedBaseContracts" : - [ - 4, - 1 - ], - "name" : "C2", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 5, - "src" : "15:20:1" - } - ], - "src" : "0:36:1" + "absolutePath": "a", + "exportedSymbols": + { + "C1": + [ + 1 + ], + "C2": + [ + 4 + ] + }, + "id": 5, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 1, + "linearizedBaseContracts": + [ + 1 + ], + "name": "C1", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 5, + "src": "0:14:1" + }, + { + "baseContracts": + [ + { + "arguments": null, + "baseName": + { + "contractScope": null, + "id": 2, + "name": "C1", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 1, + "src": "30:2:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_C1_$1", + "typeString": "contract C1" + } + }, + "id": 3, + "nodeType": "InheritanceSpecifier", + "src": "30:2:1" + } + ], + "contractDependencies": + [ + 1 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 4, + "linearizedBaseContracts": + [ + 4, + 1 + ], + "name": "C2", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 5, + "src": "15:20:1" + } + ], + "src": "0:36:1" } diff --git a/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json b/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json index 0fcf29394..8f688a4d9 100644 --- a/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json +++ b/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json @@ -1,105 +1,105 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C1" : - [ - 1 - ], - "C2" : - [ - 4 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "C1", - "nodes" : - [ - null - ], - "scope" : 5 - }, - "id" : 1, - "name" : "ContractDefinition", - "src" : "0:14:1" - }, - { - "attributes" : - { - "contractDependencies" : - [ - 1 - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 4, - 1 - ], - "name" : "C2", - "nodes" : - [ - null - ], - "scope" : 5 - }, - "children" : - [ - { - "attributes" : - { - "arguments" : null - }, - "children" : - [ - { - "attributes" : - { - "contractScope" : null, - "name" : "C1", - "referencedDeclaration" : 1, - "type" : "contract C1" - }, - "id" : 2, - "name" : "UserDefinedTypeName", - "src" : "30:2:1" - } - ], - "id" : 3, - "name" : "InheritanceSpecifier", - "src" : "30:2:1" - } - ], - "id" : 4, - "name" : "ContractDefinition", - "src" : "15:20:1" - } - ], - "id" : 5, - "name" : "SourceUnit", - "src" : "0:36:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C1": + [ + 1 + ], + "C2": + [ + 4 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 1 + ], + "name": "C1", + "nodes": + [ + null + ], + "scope": 5 + }, + "id": 1, + "name": "ContractDefinition", + "src": "0:14:1" + }, + { + "attributes": + { + "contractDependencies": + [ + 1 + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 4, + 1 + ], + "name": "C2", + "nodes": + [ + null + ], + "scope": 5 + }, + "children": + [ + { + "attributes": + { + "arguments": null + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "C1", + "referencedDeclaration": 1, + "type": "contract C1" + }, + "id": 2, + "name": "UserDefinedTypeName", + "src": "30:2:1" + } + ], + "id": 3, + "name": "InheritanceSpecifier", + "src": "30:2:1" + } + ], + "id": 4, + "name": "ContractDefinition", + "src": "15:20:1" + } + ], + "id": 5, + "name": "SourceUnit", + "src": "0:36:1" } diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json index c6d40af24..218280f63 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json @@ -1,175 +1,175 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "c" : - [ - 11 - ] - }, - "id" : 12, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 11, - "linearizedBaseContracts" : - [ - 11 - ], - "name" : "c", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 9, - "nodeType" : "Block", - "src" : "33:19:1", - "statements" : - [ - { - "assignments" : - [ - 4 - ], - "declarations" : - [ - { - "constant" : false, - "id" : 4, - "name" : "a", - "nodeType" : "VariableDeclaration", - "scope" : 9, - "src" : "35:6:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - }, - "typeName" : - { - "id" : 3, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "35:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "id" : 8, - "initialValue" : - { - "argumentTypes" : null, - "commonType" : - { - "typeIdentifier" : "t_rational_5_by_1", - "typeString" : "int_const 5" - }, - "id" : 7, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "leftExpression" : - { - "argumentTypes" : null, - "hexValue" : "32", - "id" : 5, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "kind" : "number", - "lValueRequested" : false, - "nodeType" : "Literal", - "src" : "44:1:1", - "subdenomination" : null, - "typeDescriptions" : - { - "typeIdentifier" : "t_rational_2_by_1", - "typeString" : "int_const 2" - }, - "value" : "2" - }, - "nodeType" : "BinaryOperation", - "operator" : "+", - "rightExpression" : - { - "argumentTypes" : null, - "hexValue" : "33", - "id" : 6, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "kind" : "number", - "lValueRequested" : false, - "nodeType" : "Literal", - "src" : "48:1:1", - "subdenomination" : null, - "typeDescriptions" : - { - "typeIdentifier" : "t_rational_3_by_1", - "typeString" : "int_const 3" - }, - "value" : "3" - }, - "src" : "44:5:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_rational_5_by_1", - "typeString" : "int_const 5" - } - }, - "nodeType" : "VariableDeclarationStatement", - "src" : "35:14:1" - } - ] - }, - "documentation" : null, - "id" : 10, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "f", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "23:2:1" - }, - "returnParameters" : - { - "id" : 2, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "33:0:1" - }, - "scope" : 11, - "src" : "13:39:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 12, - "src" : "0:54:1" - } - ], - "src" : "0:55:1" + "absolutePath": "a", + "exportedSymbols": + { + "c": + [ + 11 + ] + }, + "id": 12, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 11, + "linearizedBaseContracts": + [ + 11 + ], + "name": "c", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "33:19:1", + "statements": + [ + { + "assignments": + [ + 4 + ], + "declarations": + [ + { + "constant": false, + "id": 4, + "name": "a", + "nodeType": "VariableDeclaration", + "scope": 9, + "src": "35:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "35:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": null, + "visibility": "internal" + } + ], + "id": 8, + "initialValue": + { + "argumentTypes": null, + "commonType": + { + "typeIdentifier": "t_rational_5_by_1", + "typeString": "int_const 5" + }, + "id": 7, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "leftExpression": + { + "argumentTypes": null, + "hexValue": "32", + "id": 5, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "44:1:1", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + }, + "nodeType": "BinaryOperation", + "operator": "+", + "rightExpression": + { + "argumentTypes": null, + "hexValue": "33", + "id": 6, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "48:1:1", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_rational_3_by_1", + "typeString": "int_const 3" + }, + "value": "3" + }, + "src": "44:5:1", + "typeDescriptions": + { + "typeIdentifier": "t_rational_5_by_1", + "typeString": "int_const 5" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "35:14:1" + } + ] + }, + "documentation": null, + "id": 10, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "scope": 11, + "src": "13:39:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 12, + "src": "0:54:1" + } + ], + "src": "0:55:1" } diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation.sol b/test/libsolidity/ASTJSON/long_type_name_binary_operation.sol index f07029d7a..745b14ae8 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation.sol +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation.sol @@ -1 +1,3 @@ contract c { function f() public { uint a = 2 + 3; } } + +// ---- diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json index b53332860..32b419908 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json @@ -1,208 +1,208 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "c" : - [ - 11 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 11 - ], - "name" : "c", - "scope" : 12 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "f", - "scope" : 11, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "23:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 2, - "name" : "ParameterList", - "src" : "33:0:1" - }, - { - "children" : - [ - { - "attributes" : - { - "assignments" : - [ - 4 - ] - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "a", - "scope" : 9, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "uint256", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 3, - "name" : "ElementaryTypeName", - "src" : "35:4:1" - } - ], - "id" : 4, - "name" : "VariableDeclaration", - "src" : "35:6:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "commonType" : - { - "typeIdentifier" : "t_rational_5_by_1", - "typeString" : "int_const 5" - }, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "operator" : "+", - "type" : "int_const 5" - }, - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "hexvalue" : "32", - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "subdenomination" : null, - "token" : "number", - "type" : "int_const 2", - "value" : "2" - }, - "id" : 5, - "name" : "Literal", - "src" : "44:1:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "hexvalue" : "33", - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "subdenomination" : null, - "token" : "number", - "type" : "int_const 3", - "value" : "3" - }, - "id" : 6, - "name" : "Literal", - "src" : "48:1:1" - } - ], - "id" : 7, - "name" : "BinaryOperation", - "src" : "44:5:1" - } - ], - "id" : 8, - "name" : "VariableDeclarationStatement", - "src" : "35:14:1" - } - ], - "id" : 9, - "name" : "Block", - "src" : "33:19:1" - } - ], - "id" : 10, - "name" : "FunctionDefinition", - "src" : "13:39:1" - } - ], - "id" : 11, - "name" : "ContractDefinition", - "src" : "0:54:1" - } - ], - "id" : 12, - "name" : "SourceUnit", - "src" : "0:55:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "c": + [ + 11 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 11 + ], + "name": "c", + "scope": 12 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "scope": 11, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "23:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "33:0:1" + }, + { + "children": + [ + { + "attributes": + { + "assignments": + [ + 4 + ] + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "a", + "scope": 9, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 3, + "name": "ElementaryTypeName", + "src": "35:4:1" + } + ], + "id": 4, + "name": "VariableDeclaration", + "src": "35:6:1" + }, + { + "attributes": + { + "argumentTypes": null, + "commonType": + { + "typeIdentifier": "t_rational_5_by_1", + "typeString": "int_const 5" + }, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "operator": "+", + "type": "int_const 5" + }, + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "hexvalue": "32", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "number", + "type": "int_const 2", + "value": "2" + }, + "id": 5, + "name": "Literal", + "src": "44:1:1" + }, + { + "attributes": + { + "argumentTypes": null, + "hexvalue": "33", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "number", + "type": "int_const 3", + "value": "3" + }, + "id": 6, + "name": "Literal", + "src": "48:1:1" + } + ], + "id": 7, + "name": "BinaryOperation", + "src": "44:5:1" + } + ], + "id": 8, + "name": "VariableDeclarationStatement", + "src": "35:14:1" + } + ], + "id": 9, + "name": "Block", + "src": "33:19:1" + } + ], + "id": 10, + "name": "FunctionDefinition", + "src": "13:39:1" + } + ], + "id": 11, + "name": "ContractDefinition", + "src": "0:54:1" + } + ], + "id": 12, + "name": "SourceUnit", + "src": "0:55:1" } diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier.json b/test/libsolidity/ASTJSON/long_type_name_identifier.json index 505d260c4..8ac6ecaab 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier.json @@ -1,181 +1,181 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "c" : - [ - 14 - ] - }, - "id" : 15, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 14, - "linearizedBaseContracts" : - [ - 14 - ], - "name" : "c", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "constant" : false, - "id" : 3, - "name" : "a", - "nodeType" : "VariableDeclaration", - "scope" : 14, - "src" : "13:8:1", - "stateVariable" : true, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage", - "typeString" : "uint256[]" - }, - "typeName" : - { - "baseType" : - { - "id" : 1, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "13:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "id" : 2, - "length" : null, - "nodeType" : "ArrayTypeName", - "src" : "13:6:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", - "typeString" : "uint256[]" - } - }, - "value" : null, - "visibility" : "internal" - }, - { - "body" : - { - "id" : 12, - "nodeType" : "Block", - "src" : "43:25:1", - "statements" : - [ - { - "assignments" : - [ - 9 - ], - "declarations" : - [ - { - "constant" : false, - "id" : 9, - "name" : "b", - "nodeType" : "VariableDeclaration", - "scope" : 12, - "src" : "45:16:1", - "stateVariable" : false, - "storageLocation" : "storage", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", - "typeString" : "uint256[]" - }, - "typeName" : - { - "baseType" : - { - "id" : 7, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "45:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "id" : 8, - "length" : null, - "nodeType" : "ArrayTypeName", - "src" : "45:6:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", - "typeString" : "uint256[]" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "id" : 11, - "initialValue" : - { - "argumentTypes" : null, - "id" : 10, - "name" : "a", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 3, - "src" : "64:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage", - "typeString" : "uint256[] storage ref" - } - }, - "nodeType" : "VariableDeclarationStatement", - "src" : "45:20:1" - } - ] - }, - "documentation" : null, - "id" : 13, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "f", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 4, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "33:2:1" - }, - "returnParameters" : - { - "id" : 5, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "43:0:1" - }, - "scope" : 14, - "src" : "23:45:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 15, - "src" : "0:70:1" - } - ], - "src" : "0:71:1" + "absolutePath": "a", + "exportedSymbols": + { + "c": + [ + 14 + ] + }, + "id": 15, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 14, + "linearizedBaseContracts": + [ + 14 + ], + "name": "c", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 3, + "name": "a", + "nodeType": "VariableDeclaration", + "scope": 14, + "src": "13:8:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage", + "typeString": "uint256[]" + }, + "typeName": + { + "baseType": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "13:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2, + "length": null, + "nodeType": "ArrayTypeName", + "src": "13:6:1", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage_ptr", + "typeString": "uint256[]" + } + }, + "value": null, + "visibility": "internal" + }, + { + "body": + { + "id": 12, + "nodeType": "Block", + "src": "43:25:1", + "statements": + [ + { + "assignments": + [ + 9 + ], + "declarations": + [ + { + "constant": false, + "id": 9, + "name": "b", + "nodeType": "VariableDeclaration", + "scope": 12, + "src": "45:16:1", + "stateVariable": false, + "storageLocation": "storage", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage_ptr", + "typeString": "uint256[]" + }, + "typeName": + { + "baseType": + { + "id": 7, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "45:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 8, + "length": null, + "nodeType": "ArrayTypeName", + "src": "45:6:1", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage_ptr", + "typeString": "uint256[]" + } + }, + "value": null, + "visibility": "internal" + } + ], + "id": 11, + "initialValue": + { + "argumentTypes": null, + "id": 10, + "name": "a", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 3, + "src": "64:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage", + "typeString": "uint256[] storage ref" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "45:20:1" + } + ] + }, + "documentation": null, + "id": 13, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:2:1" + }, + "returnParameters": + { + "id": 5, + "nodeType": "ParameterList", + "parameters": [], + "src": "43:0:1" + }, + "scope": 14, + "src": "23:45:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 15, + "src": "0:70:1" + } + ], + "src": "0:71:1" } diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier.sol b/test/libsolidity/ASTJSON/long_type_name_identifier.sol index f03f7a84d..60193cebb 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier.sol +++ b/test/libsolidity/ASTJSON/long_type_name_identifier.sol @@ -1 +1,3 @@ contract c { uint[] a; function f() public { uint[] storage b = a; } } + +// ---- diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json index d3bcda561..bfdb72f8d 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json @@ -1,220 +1,220 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "c" : - [ - 14 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 14 - ], - "name" : "c", - "scope" : 15 - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "a", - "scope" : 14, - "stateVariable" : true, - "storageLocation" : "default", - "type" : "uint256[]", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "length" : null, - "type" : "uint256[]" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 1, - "name" : "ElementaryTypeName", - "src" : "13:4:1" - } - ], - "id" : 2, - "name" : "ArrayTypeName", - "src" : "13:6:1" - } - ], - "id" : 3, - "name" : "VariableDeclaration", - "src" : "13:8:1" - }, - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "f", - "scope" : 14, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 4, - "name" : "ParameterList", - "src" : "33:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 5, - "name" : "ParameterList", - "src" : "43:0:1" - }, - { - "children" : - [ - { - "attributes" : - { - "assignments" : - [ - 9 - ] - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "b", - "scope" : 12, - "stateVariable" : false, - "storageLocation" : "storage", - "type" : "uint256[]", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "length" : null, - "type" : "uint256[]" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 7, - "name" : "ElementaryTypeName", - "src" : "45:4:1" - } - ], - "id" : 8, - "name" : "ArrayTypeName", - "src" : "45:6:1" - } - ], - "id" : 9, - "name" : "VariableDeclaration", - "src" : "45:16:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 3, - "type" : "uint256[] storage ref", - "value" : "a" - }, - "id" : 10, - "name" : "Identifier", - "src" : "64:1:1" - } - ], - "id" : 11, - "name" : "VariableDeclarationStatement", - "src" : "45:20:1" - } - ], - "id" : 12, - "name" : "Block", - "src" : "43:25:1" - } - ], - "id" : 13, - "name" : "FunctionDefinition", - "src" : "23:45:1" - } - ], - "id" : 14, - "name" : "ContractDefinition", - "src" : "0:70:1" - } - ], - "id" : 15, - "name" : "SourceUnit", - "src" : "0:71:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "c": + [ + 14 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 14 + ], + "name": "c", + "scope": 15 + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "a", + "scope": 14, + "stateVariable": true, + "storageLocation": "default", + "type": "uint256[]", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "length": null, + "type": "uint256[]" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 1, + "name": "ElementaryTypeName", + "src": "13:4:1" + } + ], + "id": 2, + "name": "ArrayTypeName", + "src": "13:6:1" + } + ], + "id": 3, + "name": "VariableDeclaration", + "src": "13:8:1" + }, + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "scope": 14, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 4, + "name": "ParameterList", + "src": "33:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 5, + "name": "ParameterList", + "src": "43:0:1" + }, + { + "children": + [ + { + "attributes": + { + "assignments": + [ + 9 + ] + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "b", + "scope": 12, + "stateVariable": false, + "storageLocation": "storage", + "type": "uint256[]", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "length": null, + "type": "uint256[]" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 7, + "name": "ElementaryTypeName", + "src": "45:4:1" + } + ], + "id": 8, + "name": "ArrayTypeName", + "src": "45:6:1" + } + ], + "id": 9, + "name": "VariableDeclaration", + "src": "45:16:1" + }, + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 3, + "type": "uint256[] storage ref", + "value": "a" + }, + "id": 10, + "name": "Identifier", + "src": "64:1:1" + } + ], + "id": 11, + "name": "VariableDeclarationStatement", + "src": "45:20:1" + } + ], + "id": 12, + "name": "Block", + "src": "43:25:1" + } + ], + "id": 13, + "name": "FunctionDefinition", + "src": "23:45:1" + } + ], + "id": 14, + "name": "ContractDefinition", + "src": "0:70:1" + } + ], + "id": 15, + "name": "SourceUnit", + "src": "0:71:1" } diff --git a/test/libsolidity/ASTJSON/modifier_definition.json b/test/libsolidity/ASTJSON/modifier_definition.json index 66359453c..b7eb257dd 100644 --- a/test/libsolidity/ASTJSON/modifier_definition.json +++ b/test/libsolidity/ASTJSON/modifier_definition.json @@ -1,174 +1,174 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 14 - ] - }, - "id" : 15, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 14, - "linearizedBaseContracts" : - [ - 14 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 5, - "nodeType" : "Block", - "src" : "32:6:1", - "statements" : - [ - { - "id" : 4, - "nodeType" : "PlaceholderStatement", - "src" : "34:1:1" - } - ] - }, - "documentation" : null, - "id" : 6, - "name" : "M", - "nodeType" : "ModifierDefinition", - "parameters" : - { - "id" : 3, - "nodeType" : "ParameterList", - "parameters" : - [ - { - "constant" : false, - "id" : 2, - "name" : "i", - "nodeType" : "VariableDeclaration", - "scope" : 6, - "src" : "24:6:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - }, - "typeName" : - { - "id" : 1, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "24:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "src" : "23:8:1" - }, - "src" : "13:25:1", - "visibility" : "internal" - }, - { - "body" : - { - "id" : 12, - "nodeType" : "Block", - "src" : "64:2:1", - "statements" : [] - }, - "documentation" : null, - "id" : 13, - "implemented" : true, - "kind" : "function", - "modifiers" : - [ - { - "arguments" : - [ - { - "argumentTypes" : null, - "hexValue" : "31", - "id" : 9, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "kind" : "number", - "lValueRequested" : false, - "nodeType" : "Literal", - "src" : "54:1:1", - "subdenomination" : null, - "typeDescriptions" : - { - "typeIdentifier" : "t_rational_1_by_1", - "typeString" : "int_const 1" - }, - "value" : "1" - } - ], - "id" : 10, - "modifierName" : - { - "argumentTypes" : null, - "id" : 8, - "name" : "M", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 6, - "src" : "52:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_modifier$_t_uint256_$", - "typeString" : "modifier (uint256)" - } - }, - "nodeType" : "ModifierInvocation", - "src" : "52:4:1" - } - ], - "name" : "F", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 7, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "49:2:1" - }, - "returnParameters" : - { - "id" : 11, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "64:0:1" - }, - "scope" : 14, - "src" : "39:27:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 15, - "src" : "0:68:1" - } - ], - "src" : "0:69:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 14 + ] + }, + "id": 15, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 14, + "linearizedBaseContracts": + [ + 14 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 5, + "nodeType": "Block", + "src": "32:6:1", + "statements": + [ + { + "id": 4, + "nodeType": "PlaceholderStatement", + "src": "34:1:1" + } + ] + }, + "documentation": null, + "id": 6, + "name": "M", + "nodeType": "ModifierDefinition", + "parameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 2, + "name": "i", + "nodeType": "VariableDeclaration", + "scope": 6, + "src": "24:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "24:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "23:8:1" + }, + "src": "13:25:1", + "visibility": "internal" + }, + { + "body": + { + "id": 12, + "nodeType": "Block", + "src": "64:2:1", + "statements": [] + }, + "documentation": null, + "id": 13, + "implemented": true, + "kind": "function", + "modifiers": + [ + { + "arguments": + [ + { + "argumentTypes": null, + "hexValue": "31", + "id": 9, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "54:1:1", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + }, + "value": "1" + } + ], + "id": 10, + "modifierName": + { + "argumentTypes": null, + "id": 8, + "name": "M", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 6, + "src": "52:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_modifier$_t_uint256_$", + "typeString": "modifier (uint256)" + } + }, + "nodeType": "ModifierInvocation", + "src": "52:4:1" + } + ], + "name": "F", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": [], + "src": "49:2:1" + }, + "returnParameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "64:0:1" + }, + "scope": 14, + "src": "39:27:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 15, + "src": "0:68:1" + } + ], + "src": "0:69:1" } diff --git a/test/libsolidity/ASTJSON/modifier_definition.sol b/test/libsolidity/ASTJSON/modifier_definition.sol index 96474e0f3..1dcdb5946 100644 --- a/test/libsolidity/ASTJSON/modifier_definition.sol +++ b/test/libsolidity/ASTJSON/modifier_definition.sol @@ -1 +1,3 @@ contract C { modifier M(uint i) { _; } function F() M(1) public {} } + +// ---- diff --git a/test/libsolidity/ASTJSON/modifier_definition_legacy.json b/test/libsolidity/ASTJSON/modifier_definition_legacy.json index 5186912c8..3aa67cd14 100644 --- a/test/libsolidity/ASTJSON/modifier_definition_legacy.json +++ b/test/libsolidity/ASTJSON/modifier_definition_legacy.json @@ -1,212 +1,212 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 14 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 14 - ], - "name" : "C", - "scope" : 15 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "name" : "M", - "visibility" : "internal" - }, - "children" : - [ - { - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "i", - "scope" : 6, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "uint256", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 1, - "name" : "ElementaryTypeName", - "src" : "24:4:1" - } - ], - "id" : 2, - "name" : "VariableDeclaration", - "src" : "24:6:1" - } - ], - "id" : 3, - "name" : "ParameterList", - "src" : "23:8:1" - }, - { - "children" : - [ - { - "id" : 4, - "name" : "PlaceholderStatement", - "src" : "34:1:1" - } - ], - "id" : 5, - "name" : "Block", - "src" : "32:6:1" - } - ], - "id" : 6, - "name" : "ModifierDefinition", - "src" : "13:25:1" - }, - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "name" : "F", - "scope" : 14, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 7, - "name" : "ParameterList", - "src" : "49:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 11, - "name" : "ParameterList", - "src" : "64:0:1" - }, - { - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 6, - "type" : "modifier (uint256)", - "value" : "M" - }, - "id" : 8, - "name" : "Identifier", - "src" : "52:1:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "hexvalue" : "31", - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "subdenomination" : null, - "token" : "number", - "type" : "int_const 1", - "value" : "1" - }, - "id" : 9, - "name" : "Literal", - "src" : "54:1:1" - } - ], - "id" : 10, - "name" : "ModifierInvocation", - "src" : "52:4:1" - }, - { - "attributes" : - { - "statements" : - [ - null - ] - }, - "children" : [], - "id" : 12, - "name" : "Block", - "src" : "64:2:1" - } - ], - "id" : 13, - "name" : "FunctionDefinition", - "src" : "39:27:1" - } - ], - "id" : 14, - "name" : "ContractDefinition", - "src" : "0:68:1" - } - ], - "id" : 15, - "name" : "SourceUnit", - "src" : "0:69:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 14 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 14 + ], + "name": "C", + "scope": 15 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "name": "M", + "visibility": "internal" + }, + "children": + [ + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "i", + "scope": 6, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 1, + "name": "ElementaryTypeName", + "src": "24:4:1" + } + ], + "id": 2, + "name": "VariableDeclaration", + "src": "24:6:1" + } + ], + "id": 3, + "name": "ParameterList", + "src": "23:8:1" + }, + { + "children": + [ + { + "id": 4, + "name": "PlaceholderStatement", + "src": "34:1:1" + } + ], + "id": 5, + "name": "Block", + "src": "32:6:1" + } + ], + "id": 6, + "name": "ModifierDefinition", + "src": "13:25:1" + }, + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "name": "F", + "scope": 14, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 7, + "name": "ParameterList", + "src": "49:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 11, + "name": "ParameterList", + "src": "64:0:1" + }, + { + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 6, + "type": "modifier (uint256)", + "value": "M" + }, + "id": 8, + "name": "Identifier", + "src": "52:1:1" + }, + { + "attributes": + { + "argumentTypes": null, + "hexvalue": "31", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "number", + "type": "int_const 1", + "value": "1" + }, + "id": 9, + "name": "Literal", + "src": "54:1:1" + } + ], + "id": 10, + "name": "ModifierInvocation", + "src": "52:4:1" + }, + { + "attributes": + { + "statements": + [ + null + ] + }, + "children": [], + "id": 12, + "name": "Block", + "src": "64:2:1" + } + ], + "id": 13, + "name": "FunctionDefinition", + "src": "39:27:1" + } + ], + "id": 14, + "name": "ContractDefinition", + "src": "0:68:1" + } + ], + "id": 15, + "name": "SourceUnit", + "src": "0:69:1" } diff --git a/test/libsolidity/ASTJSON/modifier_invocation.json b/test/libsolidity/ASTJSON/modifier_invocation.json index 66359453c..b7eb257dd 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation.json +++ b/test/libsolidity/ASTJSON/modifier_invocation.json @@ -1,174 +1,174 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 14 - ] - }, - "id" : 15, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 14, - "linearizedBaseContracts" : - [ - 14 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 5, - "nodeType" : "Block", - "src" : "32:6:1", - "statements" : - [ - { - "id" : 4, - "nodeType" : "PlaceholderStatement", - "src" : "34:1:1" - } - ] - }, - "documentation" : null, - "id" : 6, - "name" : "M", - "nodeType" : "ModifierDefinition", - "parameters" : - { - "id" : 3, - "nodeType" : "ParameterList", - "parameters" : - [ - { - "constant" : false, - "id" : 2, - "name" : "i", - "nodeType" : "VariableDeclaration", - "scope" : 6, - "src" : "24:6:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - }, - "typeName" : - { - "id" : 1, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "24:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "src" : "23:8:1" - }, - "src" : "13:25:1", - "visibility" : "internal" - }, - { - "body" : - { - "id" : 12, - "nodeType" : "Block", - "src" : "64:2:1", - "statements" : [] - }, - "documentation" : null, - "id" : 13, - "implemented" : true, - "kind" : "function", - "modifiers" : - [ - { - "arguments" : - [ - { - "argumentTypes" : null, - "hexValue" : "31", - "id" : 9, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "kind" : "number", - "lValueRequested" : false, - "nodeType" : "Literal", - "src" : "54:1:1", - "subdenomination" : null, - "typeDescriptions" : - { - "typeIdentifier" : "t_rational_1_by_1", - "typeString" : "int_const 1" - }, - "value" : "1" - } - ], - "id" : 10, - "modifierName" : - { - "argumentTypes" : null, - "id" : 8, - "name" : "M", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 6, - "src" : "52:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_modifier$_t_uint256_$", - "typeString" : "modifier (uint256)" - } - }, - "nodeType" : "ModifierInvocation", - "src" : "52:4:1" - } - ], - "name" : "F", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 7, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "49:2:1" - }, - "returnParameters" : - { - "id" : 11, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "64:0:1" - }, - "scope" : 14, - "src" : "39:27:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 15, - "src" : "0:68:1" - } - ], - "src" : "0:69:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 14 + ] + }, + "id": 15, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 14, + "linearizedBaseContracts": + [ + 14 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 5, + "nodeType": "Block", + "src": "32:6:1", + "statements": + [ + { + "id": 4, + "nodeType": "PlaceholderStatement", + "src": "34:1:1" + } + ] + }, + "documentation": null, + "id": 6, + "name": "M", + "nodeType": "ModifierDefinition", + "parameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 2, + "name": "i", + "nodeType": "VariableDeclaration", + "scope": 6, + "src": "24:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "24:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "23:8:1" + }, + "src": "13:25:1", + "visibility": "internal" + }, + { + "body": + { + "id": 12, + "nodeType": "Block", + "src": "64:2:1", + "statements": [] + }, + "documentation": null, + "id": 13, + "implemented": true, + "kind": "function", + "modifiers": + [ + { + "arguments": + [ + { + "argumentTypes": null, + "hexValue": "31", + "id": 9, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "54:1:1", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + }, + "value": "1" + } + ], + "id": 10, + "modifierName": + { + "argumentTypes": null, + "id": 8, + "name": "M", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 6, + "src": "52:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_modifier$_t_uint256_$", + "typeString": "modifier (uint256)" + } + }, + "nodeType": "ModifierInvocation", + "src": "52:4:1" + } + ], + "name": "F", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": [], + "src": "49:2:1" + }, + "returnParameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "64:0:1" + }, + "scope": 14, + "src": "39:27:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 15, + "src": "0:68:1" + } + ], + "src": "0:69:1" } diff --git a/test/libsolidity/ASTJSON/modifier_invocation.sol b/test/libsolidity/ASTJSON/modifier_invocation.sol index 96474e0f3..1dcdb5946 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation.sol +++ b/test/libsolidity/ASTJSON/modifier_invocation.sol @@ -1 +1,3 @@ contract C { modifier M(uint i) { _; } function F() M(1) public {} } + +// ---- diff --git a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json index 5186912c8..3aa67cd14 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json +++ b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json @@ -1,212 +1,212 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 14 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 14 - ], - "name" : "C", - "scope" : 15 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "name" : "M", - "visibility" : "internal" - }, - "children" : - [ - { - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "i", - "scope" : 6, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "uint256", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 1, - "name" : "ElementaryTypeName", - "src" : "24:4:1" - } - ], - "id" : 2, - "name" : "VariableDeclaration", - "src" : "24:6:1" - } - ], - "id" : 3, - "name" : "ParameterList", - "src" : "23:8:1" - }, - { - "children" : - [ - { - "id" : 4, - "name" : "PlaceholderStatement", - "src" : "34:1:1" - } - ], - "id" : 5, - "name" : "Block", - "src" : "32:6:1" - } - ], - "id" : 6, - "name" : "ModifierDefinition", - "src" : "13:25:1" - }, - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "name" : "F", - "scope" : 14, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 7, - "name" : "ParameterList", - "src" : "49:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 11, - "name" : "ParameterList", - "src" : "64:0:1" - }, - { - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 6, - "type" : "modifier (uint256)", - "value" : "M" - }, - "id" : 8, - "name" : "Identifier", - "src" : "52:1:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "hexvalue" : "31", - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "subdenomination" : null, - "token" : "number", - "type" : "int_const 1", - "value" : "1" - }, - "id" : 9, - "name" : "Literal", - "src" : "54:1:1" - } - ], - "id" : 10, - "name" : "ModifierInvocation", - "src" : "52:4:1" - }, - { - "attributes" : - { - "statements" : - [ - null - ] - }, - "children" : [], - "id" : 12, - "name" : "Block", - "src" : "64:2:1" - } - ], - "id" : 13, - "name" : "FunctionDefinition", - "src" : "39:27:1" - } - ], - "id" : 14, - "name" : "ContractDefinition", - "src" : "0:68:1" - } - ], - "id" : 15, - "name" : "SourceUnit", - "src" : "0:69:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 14 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 14 + ], + "name": "C", + "scope": 15 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "name": "M", + "visibility": "internal" + }, + "children": + [ + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "i", + "scope": 6, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 1, + "name": "ElementaryTypeName", + "src": "24:4:1" + } + ], + "id": 2, + "name": "VariableDeclaration", + "src": "24:6:1" + } + ], + "id": 3, + "name": "ParameterList", + "src": "23:8:1" + }, + { + "children": + [ + { + "id": 4, + "name": "PlaceholderStatement", + "src": "34:1:1" + } + ], + "id": 5, + "name": "Block", + "src": "32:6:1" + } + ], + "id": 6, + "name": "ModifierDefinition", + "src": "13:25:1" + }, + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "name": "F", + "scope": 14, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 7, + "name": "ParameterList", + "src": "49:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 11, + "name": "ParameterList", + "src": "64:0:1" + }, + { + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 6, + "type": "modifier (uint256)", + "value": "M" + }, + "id": 8, + "name": "Identifier", + "src": "52:1:1" + }, + { + "attributes": + { + "argumentTypes": null, + "hexvalue": "31", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "number", + "type": "int_const 1", + "value": "1" + }, + "id": 9, + "name": "Literal", + "src": "54:1:1" + } + ], + "id": 10, + "name": "ModifierInvocation", + "src": "52:4:1" + }, + { + "attributes": + { + "statements": + [ + null + ] + }, + "children": [], + "id": 12, + "name": "Block", + "src": "64:2:1" + } + ], + "id": 13, + "name": "FunctionDefinition", + "src": "39:27:1" + } + ], + "id": 14, + "name": "ContractDefinition", + "src": "0:68:1" + } + ], + "id": 15, + "name": "SourceUnit", + "src": "0:69:1" } diff --git a/test/libsolidity/ASTJSON/non_utf8.json b/test/libsolidity/ASTJSON/non_utf8.json index 1852bd38f..1544c7715 100644 --- a/test/libsolidity/ASTJSON/non_utf8.json +++ b/test/libsolidity/ASTJSON/non_utf8.json @@ -1,122 +1,122 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 8 - ] - }, - "id" : 9, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 8, - "linearizedBaseContracts" : - [ - 8 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 6, - "nodeType" : "Block", - "src" : "33:20:1", - "statements" : - [ - { - "assignments" : - [ - 3 - ], - "declarations" : - [ - { - "constant" : false, - "id" : 3, - "name" : "x", - "nodeType" : "VariableDeclaration", - "scope" : 6, - "src" : "35:5:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_string_memory_ptr", - "typeString" : "string" - }, - "typeName" : null, - "value" : null, - "visibility" : "internal" - } - ], - "id" : 5, - "initialValue" : - { - "argumentTypes" : null, - "hexValue" : "ff", - "id" : 4, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "kind" : "string", - "lValueRequested" : false, - "nodeType" : "Literal", - "src" : "43:7:1", - "subdenomination" : null, - "typeDescriptions" : - { - "typeIdentifier" : "t_stringliteral_8b1a944cf13a9a1c08facb2c9e98623ef3254d2ddb48113885c3e8e97fec8db9", - "typeString" : "literal_string (contains invalid UTF-8 sequence at position 0)" - }, - "value" : null - }, - "nodeType" : "VariableDeclarationStatement", - "src" : "35:15:1" - } - ] - }, - "documentation" : null, - "id" : 7, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "f", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "23:2:1" - }, - "returnParameters" : - { - "id" : 2, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "33:0:1" - }, - "scope" : 8, - "src" : "13:40:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 9, - "src" : "0:55:1" - } - ], - "src" : "0:56:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 8 + ] + }, + "id": 9, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 8, + "linearizedBaseContracts": + [ + 8 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 6, + "nodeType": "Block", + "src": "33:20:1", + "statements": + [ + { + "assignments": + [ + 3 + ], + "declarations": + [ + { + "constant": false, + "id": 3, + "name": "x", + "nodeType": "VariableDeclaration", + "scope": 6, + "src": "35:5:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string" + }, + "typeName": null, + "value": null, + "visibility": "internal" + } + ], + "id": 5, + "initialValue": + { + "argumentTypes": null, + "hexValue": "ff", + "id": 4, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "43:7:1", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_stringliteral_8b1a944cf13a9a1c08facb2c9e98623ef3254d2ddb48113885c3e8e97fec8db9", + "typeString": "literal_string (contains invalid UTF-8 sequence at position 0)" + }, + "value": null + }, + "nodeType": "VariableDeclarationStatement", + "src": "35:15:1" + } + ] + }, + "documentation": null, + "id": 7, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "scope": 8, + "src": "13:40:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 9, + "src": "0:55:1" + } + ], + "src": "0:56:1" } diff --git a/test/libsolidity/ASTJSON/non_utf8.sol b/test/libsolidity/ASTJSON/non_utf8.sol index b83f3d706..f9a79d2fa 100644 --- a/test/libsolidity/ASTJSON/non_utf8.sol +++ b/test/libsolidity/ASTJSON/non_utf8.sol @@ -1 +1,3 @@ contract C { function f() public { var x = hex"ff"; } } + +// ---- diff --git a/test/libsolidity/ASTJSON/non_utf8_legacy.json b/test/libsolidity/ASTJSON/non_utf8_legacy.json index df1050966..438d4c3da 100644 --- a/test/libsolidity/ASTJSON/non_utf8_legacy.json +++ b/test/libsolidity/ASTJSON/non_utf8_legacy.json @@ -1,156 +1,156 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 8 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 8 - ], - "name" : "C", - "scope" : 9 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "f", - "scope" : 8, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "23:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 2, - "name" : "ParameterList", - "src" : "33:0:1" - }, - { - "children" : - [ - { - "attributes" : - { - "assignments" : - [ - 3 - ] - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "x", - "scope" : 6, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "string", - "typeName" : null, - "value" : null, - "visibility" : "internal" - }, - "children" : [], - "id" : 3, - "name" : "VariableDeclaration", - "src" : "35:5:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "hexvalue" : "ff", - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "subdenomination" : null, - "token" : "string", - "type" : "literal_string (contains invalid UTF-8 sequence at position 0)", - "value" : null - }, - "id" : 4, - "name" : "Literal", - "src" : "43:7:1" - } - ], - "id" : 5, - "name" : "VariableDeclarationStatement", - "src" : "35:15:1" - } - ], - "id" : 6, - "name" : "Block", - "src" : "33:20:1" - } - ], - "id" : 7, - "name" : "FunctionDefinition", - "src" : "13:40:1" - } - ], - "id" : 8, - "name" : "ContractDefinition", - "src" : "0:55:1" - } - ], - "id" : 9, - "name" : "SourceUnit", - "src" : "0:56:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 8 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 8 + ], + "name": "C", + "scope": 9 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "scope": 8, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "23:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "33:0:1" + }, + { + "children": + [ + { + "attributes": + { + "assignments": + [ + 3 + ] + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "x", + "scope": 6, + "stateVariable": false, + "storageLocation": "default", + "type": "string", + "typeName": null, + "value": null, + "visibility": "internal" + }, + "children": [], + "id": 3, + "name": "VariableDeclaration", + "src": "35:5:1" + }, + { + "attributes": + { + "argumentTypes": null, + "hexvalue": "ff", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "string", + "type": "literal_string (contains invalid UTF-8 sequence at position 0)", + "value": null + }, + "id": 4, + "name": "Literal", + "src": "43:7:1" + } + ], + "id": 5, + "name": "VariableDeclarationStatement", + "src": "35:15:1" + } + ], + "id": 6, + "name": "Block", + "src": "33:20:1" + } + ], + "id": 7, + "name": "FunctionDefinition", + "src": "13:40:1" + } + ], + "id": 8, + "name": "ContractDefinition", + "src": "0:55:1" + } + ], + "id": 9, + "name": "SourceUnit", + "src": "0:56:1" } diff --git a/test/libsolidity/ASTJSON/placeholder_statement.json b/test/libsolidity/ASTJSON/placeholder_statement.json index 496e15001..1a7b005dc 100644 --- a/test/libsolidity/ASTJSON/placeholder_statement.json +++ b/test/libsolidity/ASTJSON/placeholder_statement.json @@ -1,64 +1,64 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ] - }, - "id" : 6, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 5, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 3, - "nodeType" : "Block", - "src" : "24:6:1", - "statements" : - [ - { - "id" : 2, - "nodeType" : "PlaceholderStatement", - "src" : "26:1:1" - } - ] - }, - "documentation" : null, - "id" : 4, - "name" : "M", - "nodeType" : "ModifierDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "24:0:1" - }, - "src" : "13:17:1", - "visibility" : "internal" - } - ], - "scope" : 6, - "src" : "0:32:1" - } - ], - "src" : "0:33:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ] + }, + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "24:6:1", + "statements": + [ + { + "id": 2, + "nodeType": "PlaceholderStatement", + "src": "26:1:1" + } + ] + }, + "documentation": null, + "id": 4, + "name": "M", + "nodeType": "ModifierDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "24:0:1" + }, + "src": "13:17:1", + "visibility": "internal" + } + ], + "scope": 6, + "src": "0:32:1" + } + ], + "src": "0:33:1" } diff --git a/test/libsolidity/ASTJSON/placeholder_statement.sol b/test/libsolidity/ASTJSON/placeholder_statement.sol index cb2c0990d..22a719fa4 100644 --- a/test/libsolidity/ASTJSON/placeholder_statement.sol +++ b/test/libsolidity/ASTJSON/placeholder_statement.sol @@ -1 +1,3 @@ contract C { modifier M { _; } } + +// ---- diff --git a/test/libsolidity/ASTJSON/placeholder_statement_legacy.json b/test/libsolidity/ASTJSON/placeholder_statement_legacy.json index a55829762..8b79618f2 100644 --- a/test/libsolidity/ASTJSON/placeholder_statement_legacy.json +++ b/test/libsolidity/ASTJSON/placeholder_statement_legacy.json @@ -1,87 +1,87 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "scope" : 6 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "name" : "M", - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "24:0:1" - }, - { - "children" : - [ - { - "id" : 2, - "name" : "PlaceholderStatement", - "src" : "26:1:1" - } - ], - "id" : 3, - "name" : "Block", - "src" : "24:6:1" - } - ], - "id" : 4, - "name" : "ModifierDefinition", - "src" : "13:17:1" - } - ], - "id" : 5, - "name" : "ContractDefinition", - "src" : "0:32:1" - } - ], - "id" : 6, - "name" : "SourceUnit", - "src" : "0:33:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "scope": 6 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "name": "M", + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "24:0:1" + }, + { + "children": + [ + { + "id": 2, + "name": "PlaceholderStatement", + "src": "26:1:1" + } + ], + "id": 3, + "name": "Block", + "src": "24:6:1" + } + ], + "id": 4, + "name": "ModifierDefinition", + "src": "13:17:1" + } + ], + "id": 5, + "name": "ContractDefinition", + "src": "0:32:1" + } + ], + "id": 6, + "name": "SourceUnit", + "src": "0:33:1" } diff --git a/test/libsolidity/ASTJSON/short_type_name.json b/test/libsolidity/ASTJSON/short_type_name.json index acb461571..41921c0af 100644 --- a/test/libsolidity/ASTJSON/short_type_name.json +++ b/test/libsolidity/ASTJSON/short_type_name.json @@ -1,126 +1,126 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "c" : - [ - 10 - ] - }, - "id" : 11, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 10, - "linearizedBaseContracts" : - [ - 10 - ], - "name" : "c", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 8, - "nodeType" : "Block", - "src" : "33:20:1", - "statements" : - [ - { - "assignments" : - [ - 6 - ], - "declarations" : - [ - { - "constant" : false, - "id" : 6, - "name" : "x", - "nodeType" : "VariableDeclaration", - "scope" : 8, - "src" : "35:15:1", - "stateVariable" : false, - "storageLocation" : "memory", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_memory_ptr", - "typeString" : "uint256[]" - }, - "typeName" : - { - "baseType" : - { - "id" : 4, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "35:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "id" : 5, - "length" : null, - "nodeType" : "ArrayTypeName", - "src" : "35:6:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", - "typeString" : "uint256[]" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "id" : 7, - "initialValue" : null, - "nodeType" : "VariableDeclarationStatement", - "src" : "35:15:1" - } - ] - }, - "documentation" : null, - "id" : 9, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "f", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "23:2:1" - }, - "returnParameters" : - { - "id" : 2, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "33:0:1" - }, - "scope" : 10, - "src" : "13:40:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 11, - "src" : "0:55:1" - } - ], - "src" : "0:56:1" + "absolutePath": "a", + "exportedSymbols": + { + "c": + [ + 10 + ] + }, + "id": 11, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 10, + "linearizedBaseContracts": + [ + 10 + ], + "name": "c", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 8, + "nodeType": "Block", + "src": "33:20:1", + "statements": + [ + { + "assignments": + [ + 6 + ], + "declarations": + [ + { + "constant": false, + "id": 6, + "name": "x", + "nodeType": "VariableDeclaration", + "scope": 8, + "src": "35:15:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_memory_ptr", + "typeString": "uint256[]" + }, + "typeName": + { + "baseType": + { + "id": 4, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "35:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 5, + "length": null, + "nodeType": "ArrayTypeName", + "src": "35:6:1", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage_ptr", + "typeString": "uint256[]" + } + }, + "value": null, + "visibility": "internal" + } + ], + "id": 7, + "initialValue": null, + "nodeType": "VariableDeclarationStatement", + "src": "35:15:1" + } + ] + }, + "documentation": null, + "id": 9, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "scope": 10, + "src": "13:40:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 11, + "src": "0:55:1" + } + ], + "src": "0:56:1" } diff --git a/test/libsolidity/ASTJSON/short_type_name.sol b/test/libsolidity/ASTJSON/short_type_name.sol index 533874ae4..077e715b9 100644 --- a/test/libsolidity/ASTJSON/short_type_name.sol +++ b/test/libsolidity/ASTJSON/short_type_name.sol @@ -1 +1,3 @@ contract c { function f() public { uint[] memory x; } } + +// ---- diff --git a/test/libsolidity/ASTJSON/short_type_name_legacy.json b/test/libsolidity/ASTJSON/short_type_name_legacy.json index 1f9b19687..20219d809 100644 --- a/test/libsolidity/ASTJSON/short_type_name_legacy.json +++ b/test/libsolidity/ASTJSON/short_type_name_legacy.json @@ -1,163 +1,163 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "c" : - [ - 10 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 10 - ], - "name" : "c", - "scope" : 11 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "f", - "scope" : 10, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "23:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 2, - "name" : "ParameterList", - "src" : "33:0:1" - }, - { - "children" : - [ - { - "attributes" : - { - "assignments" : - [ - 6 - ], - "initialValue" : null - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "x", - "scope" : 8, - "stateVariable" : false, - "storageLocation" : "memory", - "type" : "uint256[]", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "length" : null, - "type" : "uint256[]" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 4, - "name" : "ElementaryTypeName", - "src" : "35:4:1" - } - ], - "id" : 5, - "name" : "ArrayTypeName", - "src" : "35:6:1" - } - ], - "id" : 6, - "name" : "VariableDeclaration", - "src" : "35:15:1" - } - ], - "id" : 7, - "name" : "VariableDeclarationStatement", - "src" : "35:15:1" - } - ], - "id" : 8, - "name" : "Block", - "src" : "33:20:1" - } - ], - "id" : 9, - "name" : "FunctionDefinition", - "src" : "13:40:1" - } - ], - "id" : 10, - "name" : "ContractDefinition", - "src" : "0:55:1" - } - ], - "id" : 11, - "name" : "SourceUnit", - "src" : "0:56:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "c": + [ + 10 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 10 + ], + "name": "c", + "scope": 11 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "scope": 10, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "23:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "33:0:1" + }, + { + "children": + [ + { + "attributes": + { + "assignments": + [ + 6 + ], + "initialValue": null + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "x", + "scope": 8, + "stateVariable": false, + "storageLocation": "memory", + "type": "uint256[]", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "length": null, + "type": "uint256[]" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 4, + "name": "ElementaryTypeName", + "src": "35:4:1" + } + ], + "id": 5, + "name": "ArrayTypeName", + "src": "35:6:1" + } + ], + "id": 6, + "name": "VariableDeclaration", + "src": "35:15:1" + } + ], + "id": 7, + "name": "VariableDeclarationStatement", + "src": "35:15:1" + } + ], + "id": 8, + "name": "Block", + "src": "33:20:1" + } + ], + "id": 9, + "name": "FunctionDefinition", + "src": "13:40:1" + } + ], + "id": 10, + "name": "ContractDefinition", + "src": "0:55:1" + } + ], + "id": 11, + "name": "SourceUnit", + "src": "0:56:1" } diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.json b/test/libsolidity/ASTJSON/short_type_name_ref.json index b6b7bca5d..22df04355 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref.json @@ -1,138 +1,138 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "c" : - [ - 11 - ] - }, - "id" : 12, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 11, - "linearizedBaseContracts" : - [ - 11 - ], - "name" : "c", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 9, - "nodeType" : "Block", - "src" : "33:25:1", - "statements" : - [ - { - "assignments" : - [ - 7 - ], - "declarations" : - [ - { - "constant" : false, - "id" : 7, - "name" : "rows", - "nodeType" : "VariableDeclaration", - "scope" : 9, - "src" : "35:20:1", - "stateVariable" : false, - "storageLocation" : "memory", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_array$_t_uint256_$dyn_memory_$dyn_memory_ptr", - "typeString" : "uint256[][]" - }, - "typeName" : - { - "baseType" : - { - "baseType" : - { - "id" : 4, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "35:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - }, - "id" : 5, - "length" : null, - "nodeType" : "ArrayTypeName", - "src" : "35:6:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", - "typeString" : "uint256[]" - } - }, - "id" : 6, - "length" : null, - "nodeType" : "ArrayTypeName", - "src" : "35:8:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_array$_t_array$_t_uint256_$dyn_storage_$dyn_storage_ptr", - "typeString" : "uint256[][]" - } - }, - "value" : null, - "visibility" : "internal" - } - ], - "id" : 8, - "initialValue" : null, - "nodeType" : "VariableDeclarationStatement", - "src" : "35:20:1" - } - ] - }, - "documentation" : null, - "id" : 10, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "f", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "23:2:1" - }, - "returnParameters" : - { - "id" : 2, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "33:0:1" - }, - "scope" : 11, - "src" : "13:45:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 12, - "src" : "0:60:1" - } - ], - "src" : "0:61:1" + "absolutePath": "a", + "exportedSymbols": + { + "c": + [ + 11 + ] + }, + "id": 12, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 11, + "linearizedBaseContracts": + [ + 11 + ], + "name": "c", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "33:25:1", + "statements": + [ + { + "assignments": + [ + 7 + ], + "declarations": + [ + { + "constant": false, + "id": 7, + "name": "rows", + "nodeType": "VariableDeclaration", + "scope": 9, + "src": "35:20:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_$dyn_memory_ptr", + "typeString": "uint256[][]" + }, + "typeName": + { + "baseType": + { + "baseType": + { + "id": 4, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "35:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 5, + "length": null, + "nodeType": "ArrayTypeName", + "src": "35:6:1", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_uint256_$dyn_storage_ptr", + "typeString": "uint256[]" + } + }, + "id": 6, + "length": null, + "nodeType": "ArrayTypeName", + "src": "35:8:1", + "typeDescriptions": + { + "typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_storage_$dyn_storage_ptr", + "typeString": "uint256[][]" + } + }, + "value": null, + "visibility": "internal" + } + ], + "id": 8, + "initialValue": null, + "nodeType": "VariableDeclarationStatement", + "src": "35:20:1" + } + ] + }, + "documentation": null, + "id": 10, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "scope": 11, + "src": "13:45:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 12, + "src": "0:60:1" + } + ], + "src": "0:61:1" } diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.sol b/test/libsolidity/ASTJSON/short_type_name_ref.sol index a808b982b..9128d69d6 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref.sol +++ b/test/libsolidity/ASTJSON/short_type_name_ref.sol @@ -1 +1,3 @@ contract c { function f() public { uint[][] memory rows; } } + +// ---- diff --git a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json index 420b0f609..ce9178a79 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json @@ -1,176 +1,176 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "c" : - [ - 11 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 11 - ], - "name" : "c", - "scope" : 12 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "f", - "scope" : 11, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "23:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 2, - "name" : "ParameterList", - "src" : "33:0:1" - }, - { - "children" : - [ - { - "attributes" : - { - "assignments" : - [ - 7 - ], - "initialValue" : null - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "rows", - "scope" : 9, - "stateVariable" : false, - "storageLocation" : "memory", - "type" : "uint256[][]", - "value" : null, - "visibility" : "internal" - }, - "children" : - [ - { - "attributes" : - { - "length" : null, - "type" : "uint256[][]" - }, - "children" : - [ - { - "attributes" : - { - "length" : null, - "type" : "uint256[]" - }, - "children" : - [ - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 4, - "name" : "ElementaryTypeName", - "src" : "35:4:1" - } - ], - "id" : 5, - "name" : "ArrayTypeName", - "src" : "35:6:1" - } - ], - "id" : 6, - "name" : "ArrayTypeName", - "src" : "35:8:1" - } - ], - "id" : 7, - "name" : "VariableDeclaration", - "src" : "35:20:1" - } - ], - "id" : 8, - "name" : "VariableDeclarationStatement", - "src" : "35:20:1" - } - ], - "id" : 9, - "name" : "Block", - "src" : "33:25:1" - } - ], - "id" : 10, - "name" : "FunctionDefinition", - "src" : "13:45:1" - } - ], - "id" : 11, - "name" : "ContractDefinition", - "src" : "0:60:1" - } - ], - "id" : 12, - "name" : "SourceUnit", - "src" : "0:61:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "c": + [ + 11 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 11 + ], + "name": "c", + "scope": 12 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "scope": 11, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "23:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "33:0:1" + }, + { + "children": + [ + { + "attributes": + { + "assignments": + [ + 7 + ], + "initialValue": null + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "rows", + "scope": 9, + "stateVariable": false, + "storageLocation": "memory", + "type": "uint256[][]", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "length": null, + "type": "uint256[][]" + }, + "children": + [ + { + "attributes": + { + "length": null, + "type": "uint256[]" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 4, + "name": "ElementaryTypeName", + "src": "35:4:1" + } + ], + "id": 5, + "name": "ArrayTypeName", + "src": "35:6:1" + } + ], + "id": 6, + "name": "ArrayTypeName", + "src": "35:8:1" + } + ], + "id": 7, + "name": "VariableDeclaration", + "src": "35:20:1" + } + ], + "id": 8, + "name": "VariableDeclarationStatement", + "src": "35:20:1" + } + ], + "id": 9, + "name": "Block", + "src": "33:25:1" + } + ], + "id": 10, + "name": "FunctionDefinition", + "src": "13:45:1" + } + ], + "id": 11, + "name": "ContractDefinition", + "src": "0:60:1" + } + ], + "id": 12, + "name": "SourceUnit", + "src": "0:61:1" } diff --git a/test/libsolidity/ASTJSON/smoke.json b/test/libsolidity/ASTJSON/smoke.json index f5369bfc8..039ad3750 100644 --- a/test/libsolidity/ASTJSON/smoke.json +++ b/test/libsolidity/ASTJSON/smoke.json @@ -1,33 +1,33 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 1 - ] - }, - "id" : 2, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 1, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 2, - "src" : "0:13:1" - } - ], - "src" : "0:14:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 1 + ] + }, + "id": 2, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 1, + "linearizedBaseContracts": + [ + 1 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 2, + "src": "0:13:1" + } + ], + "src": "0:14:1" } diff --git a/test/libsolidity/ASTJSON/smoke.sol b/test/libsolidity/ASTJSON/smoke.sol index 2dde0d209..3796bf807 100644 --- a/test/libsolidity/ASTJSON/smoke.sol +++ b/test/libsolidity/ASTJSON/smoke.sol @@ -1 +1,3 @@ contract C {} + +// ---- diff --git a/test/libsolidity/ASTJSON/smoke_legacy.json b/test/libsolidity/ASTJSON/smoke_legacy.json index e01a3c9b2..0cb99ef43 100644 --- a/test/libsolidity/ASTJSON/smoke_legacy.json +++ b/test/libsolidity/ASTJSON/smoke_legacy.json @@ -1,48 +1,48 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 1 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "C", - "nodes" : - [ - null - ], - "scope" : 2 - }, - "id" : 1, - "name" : "ContractDefinition", - "src" : "0:13:1" - } - ], - "id" : 2, - "name" : "SourceUnit", - "src" : "0:14:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 1 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 1 + ], + "name": "C", + "nodes": + [ + null + ], + "scope": 2 + }, + "id": 1, + "name": "ContractDefinition", + "src": "0:13:1" + } + ], + "id": 2, + "name": "SourceUnit", + "src": "0:14:1" } diff --git a/test/libsolidity/ASTJSON/source_location.json b/test/libsolidity/ASTJSON/source_location.json index f0ed216db..c64cc115b 100644 --- a/test/libsolidity/ASTJSON/source_location.json +++ b/test/libsolidity/ASTJSON/source_location.json @@ -1,160 +1,160 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 11 - ] - }, - "id" : 12, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 11, - "linearizedBaseContracts" : - [ - 11 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "body" : - { - "id" : 9, - "nodeType" : "Block", - "src" : "26:19:1", - "statements" : - [ - { - "assignments" : - [ - 3 - ], - "declarations" : - [ - { - "constant" : false, - "id" : 3, - "name" : "x", - "nodeType" : "VariableDeclaration", - "scope" : 9, - "src" : "28:5:1", - "stateVariable" : false, - "storageLocation" : "default", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint8", - "typeString" : "uint8" - }, - "typeName" : null, - "value" : null, - "visibility" : "internal" - } - ], - "id" : 5, - "initialValue" : - { - "argumentTypes" : null, - "hexValue" : "32", - "id" : 4, - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "kind" : "number", - "lValueRequested" : false, - "nodeType" : "Literal", - "src" : "36:1:1", - "subdenomination" : null, - "typeDescriptions" : - { - "typeIdentifier" : "t_rational_2_by_1", - "typeString" : "int_const 2" - }, - "value" : "2" - }, - "nodeType" : "VariableDeclarationStatement", - "src" : "28:9:1" - }, - { - "expression" : - { - "argumentTypes" : null, - "id" : 7, - "isConstant" : false, - "isLValue" : false, - "isPure" : false, - "lValueRequested" : false, - "nodeType" : "UnaryOperation", - "operator" : "++", - "prefix" : false, - "src" : "39:3:1", - "subExpression" : - { - "argumentTypes" : null, - "id" : 6, - "name" : "x", - "nodeType" : "Identifier", - "overloadedDeclarations" : [], - "referencedDeclaration" : 3, - "src" : "39:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint8", - "typeString" : "uint8" - } - }, - "typeDescriptions" : - { - "typeIdentifier" : "t_uint8", - "typeString" : "uint8" - } - }, - "id" : 8, - "nodeType" : "ExpressionStatement", - "src" : "39:3:1" - } - ] - }, - "documentation" : null, - "id" : 10, - "implemented" : true, - "kind" : "function", - "modifiers" : [], - "name" : "f", - "nodeType" : "FunctionDefinition", - "parameters" : - { - "id" : 1, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "23:2:1" - }, - "returnParameters" : - { - "id" : 2, - "nodeType" : "ParameterList", - "parameters" : [], - "src" : "26:0:1" - }, - "scope" : 11, - "src" : "13:32:1", - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - } - ], - "scope" : 12, - "src" : "0:47:1" - } - ], - "src" : "0:48:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 11 + ] + }, + "id": 12, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 11, + "linearizedBaseContracts": + [ + 11 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "26:19:1", + "statements": + [ + { + "assignments": + [ + 3 + ], + "declarations": + [ + { + "constant": false, + "id": 3, + "name": "x", + "nodeType": "VariableDeclaration", + "scope": 9, + "src": "28:5:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint8", + "typeString": "uint8" + }, + "typeName": null, + "value": null, + "visibility": "internal" + } + ], + "id": 5, + "initialValue": + { + "argumentTypes": null, + "hexValue": "32", + "id": 4, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "36:1:1", + "subdenomination": null, + "typeDescriptions": + { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + }, + "nodeType": "VariableDeclarationStatement", + "src": "28:9:1" + }, + { + "expression": + { + "argumentTypes": null, + "id": 7, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "++", + "prefix": false, + "src": "39:3:1", + "subExpression": + { + "argumentTypes": null, + "id": 6, + "name": "x", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 3, + "src": "39:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint8", + "typeString": "uint8" + } + }, + "typeDescriptions": + { + "typeIdentifier": "t_uint8", + "typeString": "uint8" + } + }, + "id": 8, + "nodeType": "ExpressionStatement", + "src": "39:3:1" + } + ] + }, + "documentation": null, + "id": 10, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "26:0:1" + }, + "scope": 11, + "src": "13:32:1", + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + } + ], + "scope": 12, + "src": "0:47:1" + } + ], + "src": "0:48:1" } diff --git a/test/libsolidity/ASTJSON/source_location.sol b/test/libsolidity/ASTJSON/source_location.sol index 1fcec2e66..1b763646d 100644 --- a/test/libsolidity/ASTJSON/source_location.sol +++ b/test/libsolidity/ASTJSON/source_location.sol @@ -1 +1,3 @@ contract C { function f() { var x = 2; x++; } } + +// ---- diff --git a/test/libsolidity/ASTJSON/source_location_legacy.json b/test/libsolidity/ASTJSON/source_location_legacy.json index a65979d66..4b48233da 100644 --- a/test/libsolidity/ASTJSON/source_location_legacy.json +++ b/test/libsolidity/ASTJSON/source_location_legacy.json @@ -1,199 +1,199 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 11 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 11 - ], - "name" : "C", - "scope" : 12 - }, - "children" : - [ - { - "attributes" : - { - "documentation" : null, - "implemented" : true, - "isConstructor" : false, - "kind" : "function", - "modifiers" : - [ - null - ], - "name" : "f", - "scope" : 11, - "stateMutability" : "nonpayable", - "superFunction" : null, - "visibility" : "public" - }, - "children" : - [ - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 1, - "name" : "ParameterList", - "src" : "23:2:1" - }, - { - "attributes" : - { - "parameters" : - [ - null - ] - }, - "children" : [], - "id" : 2, - "name" : "ParameterList", - "src" : "26:0:1" - }, - { - "children" : - [ - { - "attributes" : - { - "assignments" : - [ - 3 - ] - }, - "children" : - [ - { - "attributes" : - { - "constant" : false, - "name" : "x", - "scope" : 9, - "stateVariable" : false, - "storageLocation" : "default", - "type" : "uint8", - "typeName" : null, - "value" : null, - "visibility" : "internal" - }, - "children" : [], - "id" : 3, - "name" : "VariableDeclaration", - "src" : "28:5:1" - }, - { - "attributes" : - { - "argumentTypes" : null, - "hexvalue" : "32", - "isConstant" : false, - "isLValue" : false, - "isPure" : true, - "lValueRequested" : false, - "subdenomination" : null, - "token" : "number", - "type" : "int_const 2", - "value" : "2" - }, - "id" : 4, - "name" : "Literal", - "src" : "36:1:1" - } - ], - "id" : 5, - "name" : "VariableDeclarationStatement", - "src" : "28:9:1" - }, - { - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "isConstant" : false, - "isLValue" : false, - "isPure" : false, - "lValueRequested" : false, - "operator" : "++", - "prefix" : false, - "type" : "uint8" - }, - "children" : - [ - { - "attributes" : - { - "argumentTypes" : null, - "overloadedDeclarations" : - [ - null - ], - "referencedDeclaration" : 3, - "type" : "uint8", - "value" : "x" - }, - "id" : 6, - "name" : "Identifier", - "src" : "39:1:1" - } - ], - "id" : 7, - "name" : "UnaryOperation", - "src" : "39:3:1" - } - ], - "id" : 8, - "name" : "ExpressionStatement", - "src" : "39:3:1" - } - ], - "id" : 9, - "name" : "Block", - "src" : "26:19:1" - } - ], - "id" : 10, - "name" : "FunctionDefinition", - "src" : "13:32:1" - } - ], - "id" : 11, - "name" : "ContractDefinition", - "src" : "0:47:1" - } - ], - "id" : 12, - "name" : "SourceUnit", - "src" : "0:48:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 11 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 11 + ], + "name": "C", + "scope": 12 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "scope": 11, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "23:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "26:0:1" + }, + { + "children": + [ + { + "attributes": + { + "assignments": + [ + 3 + ] + }, + "children": + [ + { + "attributes": + { + "constant": false, + "name": "x", + "scope": 9, + "stateVariable": false, + "storageLocation": "default", + "type": "uint8", + "typeName": null, + "value": null, + "visibility": "internal" + }, + "children": [], + "id": 3, + "name": "VariableDeclaration", + "src": "28:5:1" + }, + { + "attributes": + { + "argumentTypes": null, + "hexvalue": "32", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "number", + "type": "int_const 2", + "value": "2" + }, + "id": 4, + "name": "Literal", + "src": "36:1:1" + } + ], + "id": 5, + "name": "VariableDeclarationStatement", + "src": "28:9:1" + }, + { + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "operator": "++", + "prefix": false, + "type": "uint8" + }, + "children": + [ + { + "attributes": + { + "argumentTypes": null, + "overloadedDeclarations": + [ + null + ], + "referencedDeclaration": 3, + "type": "uint8", + "value": "x" + }, + "id": 6, + "name": "Identifier", + "src": "39:1:1" + } + ], + "id": 7, + "name": "UnaryOperation", + "src": "39:3:1" + } + ], + "id": 8, + "name": "ExpressionStatement", + "src": "39:3:1" + } + ], + "id": 9, + "name": "Block", + "src": "26:19:1" + } + ], + "id": 10, + "name": "FunctionDefinition", + "src": "13:32:1" + } + ], + "id": 11, + "name": "ContractDefinition", + "src": "0:47:1" + } + ], + "id": 12, + "name": "SourceUnit", + "src": "0:48:1" } diff --git a/test/libsolidity/ASTJSON/using_for_directive.json b/test/libsolidity/ASTJSON/using_for_directive.json index 33caabb47..016e8c744 100644 --- a/test/libsolidity/ASTJSON/using_for_directive.json +++ b/test/libsolidity/ASTJSON/using_for_directive.json @@ -1,87 +1,87 @@ { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ], - "L" : - [ - 1 - ] - }, - "id" : 6, - "nodeType" : "SourceUnit", - "nodes" : - [ - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "library", - "documentation" : null, - "fullyImplemented" : true, - "id" : 1, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "L", - "nodeType" : "ContractDefinition", - "nodes" : [], - "scope" : 6, - "src" : "0:12:1" - }, - { - "baseContracts" : [], - "contractDependencies" : [], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "id" : 5, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "nodeType" : "ContractDefinition", - "nodes" : - [ - { - "id" : 4, - "libraryName" : - { - "contractScope" : null, - "id" : 2, - "name" : "L", - "nodeType" : "UserDefinedTypeName", - "referencedDeclaration" : 1, - "src" : "32:1:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_contract$_L_$1", - "typeString" : "library L" - } - }, - "nodeType" : "UsingForDirective", - "src" : "26:17:1", - "typeName" : - { - "id" : 3, - "name" : "uint", - "nodeType" : "ElementaryTypeName", - "src" : "38:4:1", - "typeDescriptions" : - { - "typeIdentifier" : "t_uint256", - "typeString" : "uint256" - } - } - } - ], - "scope" : 6, - "src" : "13:32:1" - } - ], - "src" : "0:46:1" + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ], + "L": + [ + 1 + ] + }, + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "library", + "documentation": null, + "fullyImplemented": true, + "id": 1, + "linearizedBaseContracts": + [ + 1 + ], + "name": "L", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 6, + "src": "0:12:1" + }, + { + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "id": 4, + "libraryName": + { + "contractScope": null, + "id": 2, + "name": "L", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 1, + "src": "32:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_L_$1", + "typeString": "library L" + } + }, + "nodeType": "UsingForDirective", + "src": "26:17:1", + "typeName": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "38:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + } + ], + "scope": 6, + "src": "13:32:1" + } + ], + "src": "0:46:1" } diff --git a/test/libsolidity/ASTJSON/using_for_directive.sol b/test/libsolidity/ASTJSON/using_for_directive.sol index a882ad887..be6e5288f 100644 --- a/test/libsolidity/ASTJSON/using_for_directive.sol +++ b/test/libsolidity/ASTJSON/using_for_directive.sol @@ -1 +1,3 @@ library L {} contract C { using L for uint; } + +// ---- diff --git a/test/libsolidity/ASTJSON/using_for_directive_legacy.json b/test/libsolidity/ASTJSON/using_for_directive_legacy.json index 0827ef90f..889fb56c0 100644 --- a/test/libsolidity/ASTJSON/using_for_directive_legacy.json +++ b/test/libsolidity/ASTJSON/using_for_directive_legacy.json @@ -1,110 +1,110 @@ { - "attributes" : - { - "absolutePath" : "a", - "exportedSymbols" : - { - "C" : - [ - 5 - ], - "L" : - [ - 1 - ] - } - }, - "children" : - [ - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "library", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 1 - ], - "name" : "L", - "nodes" : - [ - null - ], - "scope" : 6 - }, - "id" : 1, - "name" : "ContractDefinition", - "src" : "0:12:1" - }, - { - "attributes" : - { - "baseContracts" : - [ - null - ], - "contractDependencies" : - [ - null - ], - "contractKind" : "contract", - "documentation" : null, - "fullyImplemented" : true, - "linearizedBaseContracts" : - [ - 5 - ], - "name" : "C", - "scope" : 6 - }, - "children" : - [ - { - "children" : - [ - { - "attributes" : - { - "contractScope" : null, - "name" : "L", - "referencedDeclaration" : 1, - "type" : "library L" - }, - "id" : 2, - "name" : "UserDefinedTypeName", - "src" : "32:1:1" - }, - { - "attributes" : - { - "name" : "uint", - "type" : "uint256" - }, - "id" : 3, - "name" : "ElementaryTypeName", - "src" : "38:4:1" - } - ], - "id" : 4, - "name" : "UsingForDirective", - "src" : "26:17:1" - } - ], - "id" : 5, - "name" : "ContractDefinition", - "src" : "13:32:1" - } - ], - "id" : 6, - "name" : "SourceUnit", - "src" : "0:46:1" + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 5 + ], + "L": + [ + 1 + ] + } + }, + "children": + [ + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "library", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 1 + ], + "name": "L", + "nodes": + [ + null + ], + "scope": 6 + }, + "id": 1, + "name": "ContractDefinition", + "src": "0:12:1" + }, + { + "attributes": + { + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "scope": 6 + }, + "children": + [ + { + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "L", + "referencedDeclaration": 1, + "type": "library L" + }, + "id": 2, + "name": "UserDefinedTypeName", + "src": "32:1:1" + }, + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 3, + "name": "ElementaryTypeName", + "src": "38:4:1" + } + ], + "id": 4, + "name": "UsingForDirective", + "src": "26:17:1" + } + ], + "id": 5, + "name": "ContractDefinition", + "src": "13:32:1" + } + ], + "id": 6, + "name": "SourceUnit", + "src": "0:46:1" } diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index db3b395a5..3ae071929 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -64,7 +64,7 @@ eth::AssemblyItems compileContract(std::shared_ptr _sourceCode) map> scopes; GlobalContext globalContext; - NameAndTypeResolver resolver(globalContext, scopes, errorReporter); + NameAndTypeResolver resolver(globalContext, dev::test::Options::get().evmVersion(), scopes, errorReporter); solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 76985f54d..2dffbf3bf 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -34,11 +34,11 @@ #include -#include #include -#include #include +#include +#include using namespace std; using namespace langutil; @@ -54,7 +54,7 @@ namespace test namespace { -boost::optional parseAndReturnFirstError( +std::optional parseAndReturnFirstError( string const& _source, bool _assemble = false, bool _allowWarnings = true, diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 89093e035..2526dd8b6 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -41,7 +41,7 @@ map requireParsedCBORMetadata(bytes const& _bytecode) { bytes cborMetadata = dev::test::onlyMetadata(_bytecode); BOOST_REQUIRE(!cborMetadata.empty()); - boost::optional> tmp = dev::test::parseCBORMetadata(cborMetadata); + std::optional> tmp = dev::test::parseCBORMetadata(cborMetadata); BOOST_REQUIRE(tmp); return *tmp; } diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index cdb4e3625..ceb1db2ba 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -78,15 +78,34 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref for (auto& test: m_tests) test.reset(); + map libraries; + + bool constructed = false; + for (auto& test: m_tests) { - if (&test == &m_tests.front()) - if (test.call().isConstructor) - deploy("", test.call().value, test.call().arguments.rawBytes()); - else - soltestAssert(deploy("", 0, bytes()), "Failed to deploy contract."); + if (constructed) + { + soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); + soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments."); + } + else if (test.call().isLibrary) + { + soltestAssert( + deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, + "Failed to deploy library " + test.call().signature + ); + libraries[test.call().signature] = m_contractAddress; + continue; + } else - soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call."); + { + if (test.call().isConstructor) + deploy("", test.call().value, test.call().arguments.rawBytes(), libraries); + else + soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); + constructed = true; + } if (test.call().isConstructor) { @@ -171,8 +190,8 @@ void SemanticTest::parseExpectations(istream& _stream) std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests)); } -bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments) +bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map const& _libraries) { - auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments); + auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments, _libraries); return !output.empty() && m_transactionSuccessful; } diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index f7891633d..71e03518b 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -60,7 +60,7 @@ public: /// Compiles and deploys currently held source. /// Returns true if deployment was successful, false otherwise. - bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments); + bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map const& _libraries = {}); private: std::string m_source; diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 607706162..feebcbe37 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -118,7 +118,7 @@ bytes compileFirstExpression( ErrorReporter errorReporter(errors); GlobalContext globalContext; map> scopes; - NameAndTypeResolver resolver(globalContext, scopes, errorReporter); + NameAndTypeResolver resolver(globalContext, dev::test::Options::get().evmVersion(), scopes, errorReporter); resolver.registerDeclarations(*sourceUnit); vector inheritanceHierarchy; @@ -617,6 +617,25 @@ BOOST_AUTO_TEST_CASE(gas_left) BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } +BOOST_AUTO_TEST_CASE(selfbalance) +{ + char const* sourceCode = R"( + contract test { + function f() returns (uint) { + return address(this).balance; + } + } + )"; + + bytes code = compileFirstExpression(sourceCode, {}, {}); + + if (dev::test::Options::get().evmVersion() == EVMVersion::istanbul()) + { + bytes expectation({uint8_t(Instruction::SELFBALANCE)}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); + } +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 9f8a2e144..e3e96bb21 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -104,7 +104,7 @@ public: /// @returns the number of instructions in the given bytecode, not taking the metadata hash /// into account. - size_t numInstructions(bytes const& _bytecode, boost::optional _which = boost::optional{}) + size_t numInstructions(bytes const& _bytecode, std::optional _which = std::optional{}) { bytes realCode = bytecodeSansMetadata(_bytecode); BOOST_REQUIRE_MESSAGE(!realCode.empty(), "Invalid or missing metadata in bytecode."); diff --git a/test/libsolidity/semanticTests/libraries/library_address.sol b/test/libsolidity/semanticTests/libraries/library_address.sol new file mode 100644 index 000000000..3854a39bf --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_address.sol @@ -0,0 +1,56 @@ +library L { + function f(uint256 v) external pure returns (uint) { + return v * v; + } + function g(uint256 v) external returns (uint) { + return v * v; + } +} +contract C { + function addr() public view returns (bool) { + return address(L) == address(0); + } + function g(uint256 v) public view returns (uint256) { + return L.f(v); + } + function h(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(L).delegatecall(abi.encodeWithSignature("f(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function i(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("f(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function j(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(L).delegatecall(abi.encodeWithSignature("g(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function k(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("g(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// library: L +// addr() -> false +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 +// h(uint256): 1 -> 1 +// h(uint256): 2 -> 4 +// h(uint256): 4 -> 16 +// i(uint256): 1 -> 1 +// i(uint256): 2 -> 4 +// i(uint256): 4 -> 16 +// j(uint256): 1 -> 1 +// j(uint256): 2 -> 4 +// j(uint256): 4 -> 16 +// k(uint256): 1 -> FAILURE +// k(uint256): 2 -> FAILURE +// k(uint256): 4 -> FAILURE diff --git a/test/libsolidity/semanticTests/libraries/library_address_homestead.sol b/test/libsolidity/semanticTests/libraries/library_address_homestead.sol new file mode 100644 index 000000000..ce7344f4a --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_address_homestead.sol @@ -0,0 +1,24 @@ +library L { + function f(uint256 a, uint256 b) external { + assert(a * a == b); + } +} +contract C { + function addr() public view returns (bool) { + return address(L) == address(0); + } + function g(uint256 a, uint256 b) public returns (bool) { + (bool success,) = address(L).delegatecall(abi.encodeWithSignature("f(uint256,uint256)", a, b)); + return success; + } +} +// ---- +// library: L +// g(uint256,uint256): 1, 1 -> true +// g(uint256,uint256): 1, 2 -> false +// g(uint256,uint256): 2, 3 -> false +// g(uint256,uint256): 2, 4 -> true +// g(uint256,uint256): 2, 5 -> false +// g(uint256,uint256): 4, 15 -> false +// g(uint256,uint256): 4, 16 -> true +// g(uint256,uint256): 4, 17 -> false diff --git a/test/libsolidity/semanticTests/libraries/stub.sol b/test/libsolidity/semanticTests/libraries/stub.sol new file mode 100644 index 000000000..8bae3df27 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/stub.sol @@ -0,0 +1,13 @@ +library L { + function f(uint256 v) external returns (uint256) { return v*v; } +} +contract C { + function g(uint256 v) external returns (uint256) { + return L.f(v); + } +} +// ---- +// library: L +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/libraries/stub_internal.sol b/test/libsolidity/semanticTests/libraries/stub_internal.sol new file mode 100644 index 000000000..075b8dbb2 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/stub_internal.sol @@ -0,0 +1,12 @@ +library L { + function f(uint256 v) internal returns (uint256) { return v*v; } +} +contract C { + function g(uint256 v) external returns (uint256) { + return L.f(v); + } +} +// ---- +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/viaYul/require.sol b/test/libsolidity/semanticTests/viaYul/require.sol index f816ace05..09e96c0f4 100644 --- a/test/libsolidity/semanticTests/viaYul/require.sol +++ b/test/libsolidity/semanticTests/viaYul/require.sol @@ -12,17 +12,33 @@ contract C { x = true; require(true); } - /* Not properly supported by test system yet function f2(bool a) public pure returns (bool x) { x = a; string memory message; + message = "fancy message!"; require(a, message); - }*/ + } + function f3(bool a) public pure returns (bool x) { + x = a; + require(a, "msg"); + } + function f4(bool a) public pure returns (bool x) { + x = a; + string memory message; + require(a, message); + } } // ==== -// compileViaYul: true +// compileViaYul: also +// EVMVersion: >=byzantium // ---- // f(bool): true -> true // f(bool): false -> FAILURE // fail() -> FAILURE // succeed() -> true +// f2(bool): true -> true +// f2(bool): false -> FAILURE, hex"08c379a0", 0x20, 14, "fancy message!" +// f3(bool): true -> true +// f3(bool): false -> FAILURE, hex"08c379a0", 0x20, 3, "msg" +// f4(bool): true -> true +// f4(bool): false -> FAILURE, hex"08c379a0", 0x20, 0 diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol new file mode 100644 index 000000000..d43e90955 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +// 2 warnings, A.f and A.g +contract A { + uint x; + + function f() public view { + assert(x == 1); + } + function g() public view { + assert(x == 1); + } +} + +// 2 warnings, B.f and A.g +contract B is A { + function f() public view { + assert(x == 0); + } +} +// ---- +// Warning: (113-127): Assertion violation happens here +// Warning: (162-176): Assertion violation happens here +// Warning: (259-273): Assertion violation happens here +// Warning: (162-176): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol new file mode 100644 index 000000000..c23eb0037 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +// 2 warnings, A.f and A.g +contract A { + uint x; + + function f() public view { + assert(x == 1); + } + function g() public view { + assert(x == 1); + } +} + +// 2 warnings, B.f and A.g +contract B is A { + uint y; + + function f() public view { + assert(x == 0); + } +} +// ---- +// Warning: (113-127): Assertion violation happens here +// Warning: (162-176): Assertion violation happens here +// Warning: (269-283): Assertion violation happens here +// Warning: (162-176): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol new file mode 100644 index 000000000..d8fbdabfc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol @@ -0,0 +1,47 @@ +pragma experimental SMTChecker; + +// 2 warnings, A.f and A.g +contract A { + uint x; + + function f() public view { + assert(x == 1); + } + function g() public view { + assert(x == 1); + } +} + +// 3 warnings, B.f, B.h, A.g +contract B is A { + uint y; + + function f() public view { + assert(x == 0); + } + function h() public view { + assert(x == 2); + } +} + +// 4 warnings, C.f, C.i, B.h, A.g +contract C is B { + uint z; + + function f() public view { + assert(x == 0); + } + function i() public view { + assert(x == 0); + } +} +// ---- +// Warning: (113-127): Assertion violation happens here +// Warning: (162-176): Assertion violation happens here +// Warning: (271-285): Assertion violation happens here +// Warning: (320-334): Assertion violation happens here +// Warning: (162-176): Assertion violation happens here +// Warning: (434-448): Assertion violation happens here +// Warning: (483-497): Assertion violation happens here +// Warning: (320-334): Assertion violation happens here +// Warning: (162-176): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_array_for.sol b/test/libsolidity/smtCheckerTests/invariants/loop_array_for.sol deleted file mode 100644 index 1bd8c7517..000000000 --- a/test/libsolidity/smtCheckerTests/invariants/loop_array_for.sol +++ /dev/null @@ -1,16 +0,0 @@ -pragma experimental SMTChecker; - -contract Simple { - uint[] a; - function f(uint n) public { - uint i; - for (i = 0; i < n; ++i) - a[i] = i; - require(n > 1); - // Assertion is safe but current solver version cannot solve it. - // Keep test for next solver release. - assert(a[n-1] > a[n-2]); - } -} -// ---- -// Warning: (267-290): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_break.sol b/test/libsolidity/smtCheckerTests/loops/do_while_break.sol new file mode 100644 index 000000000..7bdcc251e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/do_while_break.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint x; + do { + break; + x = 1; + } while (x == 0); + assert(x == 0); + } +} +// ---- +// Warning: (104-109): Unreachable code. +// Warning: (122-128): Unreachable code. diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_break_2.sol b/test/libsolidity/smtCheckerTests/loops/do_while_break_2.sol new file mode 100644 index 000000000..c3635a976 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/do_while_break_2.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint a = 0; + while (true) { + do { + break; + a = 2; + } while (true); + a = 1; + break; + } + assert(a == 1); + } +} +// ---- +// Warning: (128-133): Unreachable code. +// Warning: (147-151): Unreachable code. diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_break_2_fail.sol b/test/libsolidity/smtCheckerTests/loops/do_while_break_2_fail.sol new file mode 100644 index 000000000..4b1824e23 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/do_while_break_2_fail.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint a = 0; + while (true) { + do { + break; + a = 2; + } while (true); + a = 1; + break; + } + assert(a == 2); + } +} +// ---- +// Warning: (128-133): Unreachable code. +// Warning: (147-151): Unreachable code. +// Warning: (180-194): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/do_while_break_fail.sol new file mode 100644 index 000000000..d8cd8f0c7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/do_while_break_fail.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint x; + do { + break; + x = 1; + } while (x == 0); + assert(x == 1); + } +} +// ---- +// Warning: (104-109): Unreachable code. +// Warning: (122-128): Unreachable code. +// Warning: (133-147): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_continue.sol b/test/libsolidity/smtCheckerTests/loops/do_while_continue.sol new file mode 100644 index 000000000..9527b1e46 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/do_while_continue.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint x; + do { + continue; + x = 1; + } while (x == 0); + assert(x == 0); + } +} +// ---- +// Warning: (107-112): Unreachable code. diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_break.sol b/test/libsolidity/smtCheckerTests/loops/for_1_break.sol new file mode 100644 index 000000000..32dc51dd8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_1_break.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x, bool b) public pure { + require(x < 10); + for (; x < 10; ) { + if (b) + ++x; + else { + x = 20; + break; + } + } + assert(x >= 10); + } +} diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_break_fail.sol new file mode 100644 index 000000000..b25d5fdad --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_1_break_fail.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x, bool b) public pure { + for (; x < 10; ) { + if (b) + ++x; + else { + break; + } + } + // Fails because the loop might break. + assert(x >= 10); + } +} +// ---- +// Warning: (201-216): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_continue.sol b/test/libsolidity/smtCheckerTests/loops/for_1_continue.sol new file mode 100644 index 000000000..7ba4c611a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_1_continue.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x, bool b) public pure { + require(x < 10); + for (; x < 10; ++x) { + if (b) { + x = 20; + continue; + } + } + assert(x > 0); + } +} diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol new file mode 100644 index 000000000..fb0fcb6c7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x, bool b) public pure { + require(x < 10); + for (; x < 10; ) { + if (b) { + x = 20; + continue; + } + ++x; + } + assert(x > 15); + } +} +// ---- +// Warning: (185-199): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_break_direct.sol b/test/libsolidity/smtCheckerTests/loops/for_break_direct.sol new file mode 100644 index 000000000..2d932b438 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_break_direct.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x) public pure { + for (x = 0; x < 10; ++x) + break; + assert(x == 0); + } +} +// ---- +// Warning: (102-105): Unreachable code. diff --git a/test/libsolidity/smtCheckerTests/loops/while_1_break.sol b/test/libsolidity/smtCheckerTests/loops/while_1_break.sol index b406a336d..6deb0461b 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_1_break.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_1_break.sol @@ -12,10 +12,6 @@ contract C break; } } - // Assertion is safe but break is unsupported for now - // so knowledge is erased. assert(x >= 10); } } -// ---- -// Warning: (274-289): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_1_continue.sol b/test/libsolidity/smtCheckerTests/loops/while_1_continue.sol index 72e2dfd78..ff22d139e 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_1_continue.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_1_continue.sol @@ -3,20 +3,14 @@ pragma experimental SMTChecker; contract C { function f(uint x, bool b) public pure { - require(x < 100); + require(x < 10); while (x < 10) { if (b) { - x = 15; + x = 20; continue; } - else - x = 20; - + ++x; } - // Should be safe, but fails due to continue being unsupported - // and erasing all knowledge. - assert(x >= 15); + assert(x >= 10); } } -// ---- -// Warning: (294-309): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_1_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_1_continue_fail.sol index 389942906..c8e14258e 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_1_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_1_continue_fail.sol @@ -8,9 +8,9 @@ contract C if (b) { x = 15; continue; + x = 200; } - else - x = 20; + x = 20; } // Fails due to the if. @@ -18,4 +18,5 @@ contract C } } // ---- -// Warning: (223-238): Assertion violation happens here +// Warning: (169-176): Unreachable code. +// Warning: (227-242): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_2_break.sol b/test/libsolidity/smtCheckerTests/loops/while_2_break.sol index 8fde4b06d..12fe93b55 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_2_break.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_2_break.sol @@ -9,11 +9,8 @@ contract C break; ++x; } - // Assertion is safe but break is unsupported for now - // so knowledge is erased. assert(x == 1); } } // ---- // Warning: (128-131): Unreachable code. -// Warning: (224-238): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_break_direct.sol b/test/libsolidity/smtCheckerTests/loops/while_break_direct.sol new file mode 100644 index 000000000..8961f97d8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_break_direct.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x) public pure { + x = 0; + while (x < 10) + break; + assert(x == 0); + } +} +// ---- +// Warning: (98-104): Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_memory.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_memory.sol deleted file mode 100644 index 6cb6cd6d9..000000000 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_memory.sol +++ /dev/null @@ -1,23 +0,0 @@ -pragma experimental SMTChecker; - -contract LoopFor2 { - uint[] b; - uint[] c; - - function testUnboundedForLoop(uint n) public { - b[0] = 900; - uint[] memory a = b; - require(n > 0 && n < 100); - uint i; - while (i < n) { - b[i] = i + 1; - c[i] = b[i]; - ++i; - } - assert(b[0] == c[0]); - assert(a[0] == 900); - assert(b[0] == 900); - } -} -// ---- -// Warning: (312-331): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol deleted file mode 100644 index 7670ddb6d..000000000 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol +++ /dev/null @@ -1,24 +0,0 @@ -pragma experimental SMTChecker; - -contract LoopFor2 { - uint[] b; - uint[] c; - - function testUnboundedForLoop(uint n) public { - b[0] = 900; - uint[] storage a = b; - require(n > 0 && n < 100); - uint i; - while (i < n) { - b[i] = i + 1; - c[i] = b[i]; - ++i; - } - assert(b[0] == c[0]); - assert(a[0] == 900); - assert(b[0] == 900); - } -} -// ---- -// Warning: (290-309): Assertion violation happens here -// Warning: (313-332): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_break.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_break.sol new file mode 100644 index 000000000..b79e714ce --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_break.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x, uint y, bool b, bool c) public pure { + require(x < 10); + while (x < 10) { + if (b) { + ++x; + if (x == 10) + x = 15; + } + else { + require(y < 10); + while (y < 10) { + if (c) + ++y; + else { + y = 20; + break; + } + } + assert(y >= 10); + x = 15; + break; + } + } + assert(x >= 15); + } +} diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol new file mode 100644 index 000000000..986e7205b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol @@ -0,0 +1,33 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x, uint y, bool b, bool c) public pure { + require(x < 10); + while (x < 10) { + if (b) { + ++x; + if (x == 10) + x = 15; + } + else { + require(y < 10); + while (y < 10) { + if (c) + ++y; + else { + y = 20; + break; + } + } + assert(y >= 15); + x = 15; + break; + } + } + assert(x >= 20); + } +} +// ---- +// Warning: (329-344): Assertion violation happens here +// Warning: (380-395): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_continue.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_continue.sol new file mode 100644 index 000000000..775d0b92f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_continue.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x, uint y, bool b, bool c) public pure { + require(x < 10); + while (x < 10) { + if (b) { + x = 20; + continue; + } + else { + require(y < 10); + while (y < 10) { + if (c) { + y = 20; + continue; + } + y = 15; + break; + } + assert(y >= 15); + x = y; + } + } + assert(x >= 15); + } +} diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol new file mode 100644 index 000000000..71238c3be --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol @@ -0,0 +1,31 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x, uint y, bool b, bool c) public pure { + require(x < 10); + while (x < 10) { + if (b) { + x = 15; + continue; + } + else { + require(y < 10); + while (y < 10) { + if (c) { + y = 20; + continue; + } + y = 15; + break; + } + assert(y >= 20); + x = y; + } + } + assert(x >= 20); + } +} +// ---- +// Warning: (323-338): Assertion violation happens here +// Warning: (362-377): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol b/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol new file mode 100644 index 000000000..0169efaec --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract C { + bytes20 x; + function f(bytes16 b) public view { + b[uint8(x[2])]; + } +} +// ---- +// Warning: (116-120): Assertion checker does not yet support index accessing fixed bytes. +// Warning: (108-122): Assertion checker does not yet support index accessing fixed bytes. diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_address.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_address.sol new file mode 100644 index 000000000..c0b7d109d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_address.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract C { + function f(address a, function(uint) external g) internal pure { + address b = address(g); + assert(a == b); + } +} +// ---- +// Warning: (128-138): Type conversion is not yet fully supported and might yield false positives. +// Warning: (142-156): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol new file mode 100644 index 000000000..799852592 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f(function(uint) external returns (uint) g, function(uint) external returns (uint) h) public { + assert(g(2) == h(2)); + } +} +// ---- +// Warning: (155-175): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol new file mode 100644 index 000000000..1657a95c6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal { + assert(g(2) == h(2)); + assert(g == h); + } +} +// ---- +// Warning: (146-150): Assertion checker does not yet implement this type of function call. +// Warning: (154-158): Assertion checker does not yet implement this type of function call. +// Warning: (170-176): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning: (139-159): Assertion violation happens here +// Warning: (163-177): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol new file mode 100644 index 000000000..b2701fdf9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract B { + function f() pure public { + g("0123456"); + } + function g(bytes7 a) pure public { + assert(a == "0123456"); + assert(a == "1234567"); + } +} +// ---- +// Warning: (162-184): Assertion violation happens here +// Warning: (136-158): Assertion violation happens here +// Warning: (162-184): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol new file mode 100644 index 000000000..41459169f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract B { + function f() mod2("0123456") pure public { } + modifier mod2(bytes7 a) { + assert(a == "0123456"); + assert(a == "1234567"); + _; + } +} +// ---- +// Warning: (152-174): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol new file mode 100644 index 000000000..09fd68ef6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function g() public pure returns (bytes32 val) { return "abc"; } + function f1() public pure returns (bytes32 val) { return g(); } + + function a() public pure { + assert(f1() == "abc"); + assert(f1() == "cde"); + } +} +// ---- +// Warning: (238-259): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol new file mode 100644 index 000000000..f2ba3ce23 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C { + function h() public pure returns (bytes32 val, bytes3 val2) { return ("abc", "def"); } + function g() public pure returns (bytes32 val) { return "abc"; } + function f1() public pure returns (bytes32 val) { return g(); } + function f2() public pure returns (bytes32 val, bytes3 val2) { return h(); } + + function a() public pure { + (bytes32 v1, bytes3 v2) = f2(); + assert(v1 == "abc"); + assert(v2 == "cde"); + } +} +// ---- +// Warning: (442-461): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_branch_2d.sol b/test/libsolidity/smtCheckerTests/types/array_branch_2d.sol index e08d10060..a68ea8b60 100644 --- a/test/libsolidity/smtCheckerTests/types/array_branch_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_branch_2d.sol @@ -11,6 +11,4 @@ contract C } } // ---- -// Warning: (90-97): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (115-122): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. // Warning: (130-149): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_branch_3d.sol b/test/libsolidity/smtCheckerTests/types/array_branch_3d.sol index 4a1c81c42..87eda064c 100644 --- a/test/libsolidity/smtCheckerTests/types/array_branch_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_branch_3d.sol @@ -11,6 +11,4 @@ contract C } } // ---- -// Warning: (92-102): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (120-130): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. // Warning: (138-160): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_branches_2d.sol b/test/libsolidity/smtCheckerTests/types/array_branches_2d.sol index 3f5b9d644..79f23ad5e 100644 --- a/test/libsolidity/smtCheckerTests/types/array_branches_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_branches_2d.sol @@ -13,7 +13,3 @@ contract C } } // ---- -// Warning: (90-97): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (115-122): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (138-145): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (153-172): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_branches_3d.sol b/test/libsolidity/smtCheckerTests/types/array_branches_3d.sol index 0d502df3d..6ab84ec6a 100644 --- a/test/libsolidity/smtCheckerTests/types/array_branches_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_branches_3d.sol @@ -13,7 +13,3 @@ contract C } } // ---- -// Warning: (92-102): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (120-130): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (146-156): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (164-186): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_struct_array_branches_2d.sol b/test/libsolidity/smtCheckerTests/types/array_struct_array_branches_2d.sol index 725cecc86..ee92be251 100644 --- a/test/libsolidity/smtCheckerTests/types/array_struct_array_branches_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_struct_array_branches_2d.sol @@ -17,15 +17,15 @@ contract C // Warning: (124-130): Assertion checker does not yet support this expression. // Warning: (124-128): Assertion checker does not yet implement type struct C.S memory // Warning: (124-133): Assertion checker does not yet implement this expression. -// Warning: (124-136): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (124-136): Assertion checker does not yet implement this expression. // Warning: (154-160): Assertion checker does not yet support this expression. // Warning: (154-158): Assertion checker does not yet implement type struct C.S memory // Warning: (154-163): Assertion checker does not yet implement this expression. -// Warning: (154-166): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (154-166): Assertion checker does not yet implement this expression. // Warning: (182-188): Assertion checker does not yet support this expression. // Warning: (182-186): Assertion checker does not yet implement type struct C.S memory // Warning: (182-191): Assertion checker does not yet implement this expression. -// Warning: (182-194): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (182-194): Assertion checker does not yet implement this expression. // Warning: (209-215): Assertion checker does not yet support this expression. // Warning: (209-213): Assertion checker does not yet implement type struct C.S memory // Warning: (209-218): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_as_argument.sol b/test/libsolidity/smtCheckerTests/types/function_type_as_argument.sol new file mode 100644 index 000000000..0ba33e408 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_as_argument.sol @@ -0,0 +1,5 @@ +pragma experimental SMTChecker; +contract C { + function f(function(uint) external g) public { + } +} diff --git a/test/libsolidity/smtCheckerTests/types/function_type_call.sol b/test/libsolidity/smtCheckerTests/types/function_type_call.sol new file mode 100644 index 000000000..90a80dd9e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_call.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + function(uint) m_g; + function f(function(uint) internal g) internal { + g(2); + } + function h() public { + f(m_g); + } +} +// ---- +// Warning: (121-125): Assertion checker does not yet implement this type of function call. +// Warning: (121-125): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_members.sol b/test/libsolidity/smtCheckerTests/types/function_type_members.sol new file mode 100644 index 000000000..2296fa798 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_members.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(function(uint) external payable g) internal { + g.selector; + g.gas(2).value(3)(4); + } +} +// ---- +// Warning: (108-118): Assertion checker does not yet support this expression. +// Warning: (122-130): Assertion checker does not yet implement this type of function call. +// Warning: (122-139): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_nested.sol b/test/libsolidity/smtCheckerTests/types/function_type_nested.sol new file mode 100644 index 000000000..5eb637315 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_nested.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; +contract C { + function(uint) m_g; + function f1(function(uint) internal g1) internal { + g1(2); + } + function f2(function(function(uint) internal) internal g2) internal { + g2(m_g); + } + function h() public { + f2(f1); + } +} +// ---- +// Warning: (123-128): Assertion checker does not yet implement this type of function call. +// Warning: (152-197): Assertion checker does not yet support the type of this variable. +// Warning: (212-214): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (212-219): Assertion checker does not yet implement this type of function call. +// Warning: (255-257): Internal error: Expression undefined for SMT solver. +// Warning: (255-257): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (212-214): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (212-219): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol b/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol new file mode 100644 index 000000000..2e974fef8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; +contract C { + function(uint) m_g; + function r() internal view returns (function(uint)) { + return m_g; + } + function f1(function(uint) internal g1) internal { + g1(2); + } + function f2(function(function(uint) internal) internal g2) internal { + g2(r()); + } + function h() public { + f2(f1); + } +} +// ---- +// Warning: (195-200): Assertion checker does not yet implement this type of function call. +// Warning: (224-269): Assertion checker does not yet support the type of this variable. +// Warning: (284-286): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (287-288): Assertion checker does not yet support this global variable. +// Warning: (284-291): Assertion checker does not yet implement this type of function call. +// Warning: (327-329): Internal error: Expression undefined for SMT solver. +// Warning: (327-329): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (284-286): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (287-288): Assertion checker does not yet support this global variable. +// Warning: (284-291): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/mapping_2d_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_2d_1.sol index b6474903a..3afab9550 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_2d_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_2d_1.sol @@ -10,5 +10,3 @@ contract C } } // ---- -// Warning: (134-145): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (154-178): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol b/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol index dd4d568ef..6072d3f2e 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol @@ -10,5 +10,4 @@ contract C } } // ---- -// Warning: (134-145): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. // Warning: (154-178): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_3d_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_3d_1.sol index 6c5f439a7..b2a90c913 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_3d_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_3d_1.sol @@ -10,5 +10,3 @@ contract C } } // ---- -// Warning: (152-167): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. -// Warning: (176-204): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol b/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol index dfd4ddafc..aed10ca1f 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol @@ -10,5 +10,4 @@ contract C } } // ---- -// Warning: (152-167): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. // Warning: (176-204): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/struct_array_branches_2d.sol b/test/libsolidity/smtCheckerTests/types/struct_array_branches_2d.sol index e8fbd3d15..fe5fca87f 100644 --- a/test/libsolidity/smtCheckerTests/types/struct_array_branches_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/struct_array_branches_2d.sol @@ -19,15 +19,15 @@ contract C // Warning: (117-120): Assertion checker does not yet support this expression. // Warning: (117-118): Assertion checker does not yet implement type struct C.S memory // Warning: (117-123): Assertion checker does not yet implement this expression. -// Warning: (117-126): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (117-126): Assertion checker does not yet implement this expression. // Warning: (144-147): Assertion checker does not yet support this expression. // Warning: (144-145): Assertion checker does not yet implement type struct C.S memory // Warning: (144-150): Assertion checker does not yet implement this expression. -// Warning: (144-153): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (144-153): Assertion checker does not yet implement this expression. // Warning: (169-172): Assertion checker does not yet support this expression. // Warning: (169-170): Assertion checker does not yet implement type struct C.S memory // Warning: (169-175): Assertion checker does not yet implement this expression. -// Warning: (169-178): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (169-178): Assertion checker does not yet implement this expression. // Warning: (193-196): Assertion checker does not yet support this expression. // Warning: (193-194): Assertion checker does not yet implement type struct C.S memory // Warning: (193-199): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/struct_array_branches_3d.sol b/test/libsolidity/smtCheckerTests/types/struct_array_branches_3d.sol index 2ba32209e..c91e163ba 100644 --- a/test/libsolidity/smtCheckerTests/types/struct_array_branches_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/struct_array_branches_3d.sol @@ -18,15 +18,15 @@ contract C // Warning: (124-127): Assertion checker does not yet support this expression. // Warning: (124-125): Assertion checker does not yet implement type struct C.S memory // Warning: (124-130): Assertion checker does not yet implement this expression. -// Warning: (124-136): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (124-136): Assertion checker does not yet implement this expression. // Warning: (154-157): Assertion checker does not yet support this expression. // Warning: (154-155): Assertion checker does not yet implement type struct C.S memory // Warning: (154-160): Assertion checker does not yet implement this expression. -// Warning: (154-166): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (154-166): Assertion checker does not yet implement this expression. // Warning: (182-185): Assertion checker does not yet support this expression. // Warning: (182-183): Assertion checker does not yet implement type struct C.S memory // Warning: (182-188): Assertion checker does not yet implement this expression. -// Warning: (182-194): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays. +// Warning: (182-194): Assertion checker does not yet implement this expression. // Warning: (209-212): Assertion checker does not yet support this expression. // Warning: (209-210): Assertion checker does not yet implement type struct C.S memory // Warning: (209-215): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/syntaxTests/array/length/not_too_large.sol b/test/libsolidity/syntaxTests/array/length/not_too_large.sol new file mode 100644 index 000000000..897c28582 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/not_too_large.sol @@ -0,0 +1,10 @@ +// Used to cause ICE because of a too strict assert +pragma experimental ABIEncoderV2; +contract C { + struct S { uint a; T[222222222222222222222222222] sub; } + struct T { uint[] x; } + function f() public returns (uint, S memory) { + } +} +// ---- +// Warning: (52-85): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/deprecated_functions.sol b/test/libsolidity/syntaxTests/deprecated_functions.sol index c5764e96c..4774de47f 100644 --- a/test/libsolidity/syntaxTests/deprecated_functions.sol +++ b/test/libsolidity/syntaxTests/deprecated_functions.sol @@ -8,5 +8,5 @@ contract test { } } // ---- -// TypeError: (58-62): "sha3" has been deprecated in favour of "keccak256" -// TypeError: (101-108): "suicide" has been deprecated in favour of "selfdestruct" +// TypeError: (58-62): "sha3" has been deprecated in favour of "keccak256". +// TypeError: (101-108): "suicide" has been deprecated in favour of "selfdestruct". diff --git a/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol index 37b60e5e4..4e3b5fd05 100644 --- a/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol +++ b/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol @@ -5,4 +5,4 @@ contract C } } // ---- -// TypeError: (60-64): "sha3" has been deprecated in favour of "keccak256" +// TypeError: (60-64): "sha3" has been deprecated in favour of "keccak256". diff --git a/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol index bf3f5ebc8..f243e2f26 100644 --- a/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol +++ b/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol @@ -5,4 +5,4 @@ contract C } } // ---- -// TypeError: (60-67): "suicide" has been deprecated in favour of "selfdestruct" +// TypeError: (60-67): "suicide" has been deprecated in favour of "selfdestruct". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_special.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_special.sol index db28e5074..976bfcfbc 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_special.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_special.sol @@ -2,12 +2,18 @@ contract C { function f() public { assembly { super := 1 + this := 1 + msg := 1 + block := 1 f := 1 C := 1 } } } // ---- -// TypeError: (58-63): Only local variables can be assigned to in inline assembly. -// TypeError: (75-76): Only local variables can be assigned to in inline assembly. -// TypeError: (88-89): Only local variables can be assigned to in inline assembly. +// DeclarationError: (58-63): Variable not found or variable not lvalue. +// DeclarationError: (75-79): Variable not found or variable not lvalue. +// DeclarationError: (91-94): Variable not found or variable not lvalue. +// DeclarationError: (106-111): Variable not found or variable not lvalue. +// TypeError: (123-124): Only local variables can be assigned to in inline assembly. +// TypeError: (136-137): Only local variables can be assigned to in inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/istanbul.sol b/test/libsolidity/syntaxTests/inlineAssembly/istanbul.sol index 3e1a1cd2a..5e6334749 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/istanbul.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/istanbul.sol @@ -1,12 +1,12 @@ contract C { - function f() pure external { + function f() pure external returns (uint id) { assembly { - pop(chainid()) + id := chainid() } } - function g() view external { + function g() view external returns (uint sb) { assembly { - pop(selfbalance()) + sb := selfbalance() } } } diff --git a/test/libsolidity/syntaxTests/inlineAssembly/istanbul_on_petersburg.sol b/test/libsolidity/syntaxTests/inlineAssembly/istanbul_on_petersburg.sol new file mode 100644 index 000000000..aa7eb1a8b --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/istanbul_on_petersburg.sol @@ -0,0 +1,17 @@ +contract C { + function f() pure external returns (uint id) { + assembly { + id := chainid() + } + } + function g() view external returns (uint sb) { + assembly { + sb := selfbalance() + } + } +} +// ==== +// EVMVersion: =petersburg +// ---- +// TypeError: (101-110): The "chainid" instruction is only available for Istanbul-compatible VMs (you are currently compiling for "petersburg"). +// TypeError: (215-228): The "selfbalance" instruction is only available for Istanbul-compatible VMs (you are currently compiling for "petersburg"). diff --git a/test/libsolidity/syntaxTests/modifiers/constructor_as_modifier.sol b/test/libsolidity/syntaxTests/modifiers/constructor_as_modifier.sol new file mode 100644 index 000000000..a65e0e84c --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/constructor_as_modifier.sol @@ -0,0 +1,5 @@ +contract C { + constructor() C() public {} +} +// ---- +// TypeError: (31-34): Referenced declaration is neither modifier nor base class. diff --git a/test/libsolidity/syntaxTests/modifiers/use_in_invalid_context.sol b/test/libsolidity/syntaxTests/modifiers/use_in_invalid_context.sol new file mode 100644 index 000000000..1dd797ee6 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/use_in_invalid_context.sol @@ -0,0 +1,9 @@ +contract test { + modifier mod() { _; } + + function f() public { + mod ; + } +} +// ---- +// TypeError: (77-80): Modifier can only be referenced in function headers. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol index dcf11a6e3..3eebc7926 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol @@ -6,4 +6,5 @@ contract test { } } // ---- +// TypeError: (87-90): The type of a variable cannot be a library. // TypeError: (100-103): Member "l" not found or not visible after argument-dependent lookup in library L. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol new file mode 100644 index 000000000..c28bec419 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol @@ -0,0 +1,14 @@ +library X { } + +contract Y { + X abc; + function foo(X param) private view + { + X ofg; + ofg = abc; + } +} +// ---- +// TypeError: (29-34): The type of a variable cannot be a library. +// TypeError: (50-57): The type of a variable cannot be a library. +// TypeError: (77-82): The type of a variable cannot be a library. diff --git a/test/libsolidity/syntaxTests/types/library_to_address.sol b/test/libsolidity/syntaxTests/types/library_to_address.sol new file mode 100644 index 000000000..50ef844da --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_to_address.sol @@ -0,0 +1,8 @@ +library L { +} +contract C { + function f() public pure returns (address) { + return address(L); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/library_to_address_payable.sol b/test/libsolidity/syntaxTests/types/library_to_address_payable.sol new file mode 100644 index 000000000..36a7a8968 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_to_address_payable.sol @@ -0,0 +1,9 @@ +library L { +} +contract C { + function f() public pure returns (address payable) { + return address(L); + } +} +// ---- +// TypeError: (99-109): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable. diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index af104e86e..312b5fb2e 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -263,7 +263,7 @@ string BytesUtils::formatBytes( os << formatHexString(_bytes); break; case ABIType::String: - os << formatString(_bytes); + os << formatString(_bytes, _bytes.size() - countRightPaddedZeros(_bytes)); break; case ABIType::Failure: break; @@ -311,3 +311,12 @@ string BytesUtils::formatBytesRange( return os.str(); } +size_t BytesUtils::countRightPaddedZeros(bytes const& _bytes) +{ + return find_if( + _bytes.rbegin(), + _bytes.rend(), + [](uint8_t b) { return b != '\0'; } + ) - _bytes.rbegin(); +} + diff --git a/test/libsolidity/util/BytesUtils.h b/test/libsolidity/util/BytesUtils.h index f83c9c3c2..0038baffb 100644 --- a/test/libsolidity/util/BytesUtils.h +++ b/test/libsolidity/util/BytesUtils.h @@ -122,6 +122,10 @@ public: ParameterList const& _parameters, bool _highlight ); + + /// Count the number of zeros between the last non-zero byte and the end of + /// \param _bytes. + static size_t countRightPaddedZeros(bytes const& _bytes); }; } diff --git a/test/libsolidity/util/ContractABIUtils.cpp b/test/libsolidity/util/ContractABIUtils.cpp index 3d1a7c79a..bd94ec99a 100644 --- a/test/libsolidity/util/ContractABIUtils.cpp +++ b/test/libsolidity/util/ContractABIUtils.cpp @@ -141,14 +141,14 @@ string functionSignatureFromABI(Json::Value const& _functionABI) } -boost::optional ContractABIUtils::parametersFromJsonOutputs( +std::optional ContractABIUtils::parametersFromJsonOutputs( ErrorReporter& _errorReporter, Json::Value const& _contractABI, string const& _functionSignature ) { if (!_contractABI) - return boost::none; + return std::nullopt; for (auto const& function: _contractABI) if (_functionSignature == functionSignatureFromABI(function)) @@ -177,17 +177,17 @@ boost::optional ContractABIUtils::parameters "Could not convert \"" + type + "\" to internal ABI type representation. Falling back to default encoding." ); - return boost::none; + return std::nullopt; } finalParams += inplaceTypeParams; inplaceTypeParams.clear(); } - return boost::optional(finalParams + dynamicTypeParams); + return std::optional(finalParams + dynamicTypeParams); } - return boost::none; + return std::nullopt; } bool ContractABIUtils::appendTypesFromName( diff --git a/test/libsolidity/util/ContractABIUtils.h b/test/libsolidity/util/ContractABIUtils.h index 3b1b0dc56..cc4a541ee 100644 --- a/test/libsolidity/util/ContractABIUtils.h +++ b/test/libsolidity/util/ContractABIUtils.h @@ -42,7 +42,7 @@ public: /// a list of internal type representations of isoltest. /// Creates parameters from Contract ABI and is used to generate values for /// auto-correction during interactive update routine. - static boost::optional parametersFromJsonOutputs( + static std::optional parametersFromJsonOutputs( ErrorReporter& _errorReporter, Json::Value const& _contractABI, std::string const& _functionSignature diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index 275b1e657..d945ee022 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -58,6 +58,7 @@ namespace test K(Boolean, "boolean", 0) \ /* special keywords */ \ K(Left, "left", 0) \ + K(Library, "library", 0) \ K(Right, "right", 0) \ K(Failure, "FAILURE", 0) \ @@ -268,6 +269,8 @@ struct FunctionCall /// Marks this function call as "short-handed", meaning /// no `->` declared. bool omitsArrow = true; + /// Marks a library deployment call. + bool isLibrary = false; }; } diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index f33853dfe..77330f51c 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -24,11 +24,11 @@ #include #include -#include #include #include #include +#include #include using namespace dev; @@ -75,44 +75,56 @@ vector TestFileParser::parseFunctionCalls(siz try { - tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); - if (accept(Token::Comma, true)) - call.value = parseFunctionCallValue(); - if (accept(Token::Colon, true)) - call.arguments = parseFunctionCallArguments(); - - if (accept(Token::Newline, true)) + if (accept(Token::Library, true)) { - call.displayMode = FunctionCall::DisplayMode::MultiLine; - m_lineNumber++; - } - - call.arguments.comment = parseComment(); - - if (accept(Token::Newline, true)) - { - call.displayMode = FunctionCall::DisplayMode::MultiLine; - m_lineNumber++; - } - - if (accept(Token::Arrow, true)) - { - call.omitsArrow = false; - call.expectations = parseFunctionCallExpectations(); - if (accept(Token::Newline, true)) - m_lineNumber++; + expect(Token::Colon); + call.signature = m_scanner.currentLiteral(); + expect(Token::Identifier); + call.isLibrary = true; + call.expectations.failure = false; } else { - call.expectations.failure = false; - call.displayMode = FunctionCall::DisplayMode::SingleLine; + tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); + if (accept(Token::Comma, true)) + call.value = parseFunctionCallValue(); + if (accept(Token::Colon, true)) + call.arguments = parseFunctionCallArguments(); + + if (accept(Token::Newline, true)) + { + call.displayMode = FunctionCall::DisplayMode::MultiLine; + m_lineNumber++; + } + + call.arguments.comment = parseComment(); + + if (accept(Token::Newline, true)) + { + call.displayMode = FunctionCall::DisplayMode::MultiLine; + m_lineNumber++; + } + + if (accept(Token::Arrow, true)) + { + call.omitsArrow = false; + call.expectations = parseFunctionCallExpectations(); + if (accept(Token::Newline, true)) + m_lineNumber++; + } + else + { + call.expectations.failure = false; + call.displayMode = FunctionCall::DisplayMode::SingleLine; + } + + call.expectations.comment = parseComment(); + + if (call.signature == "constructor()") + call.isConstructor = true; + } - call.expectations.comment = parseComment(); - - if (call.signature == "constructor()") - call.isConstructor = true; - calls.emplace_back(std::move(call)); } catch (Error const& _e) @@ -456,6 +468,7 @@ void TestFileParser::Scanner::scanNextToken() if (_literal == "false") return TokenDesc{Token::Boolean, _literal}; if (_literal == "ether") return TokenDesc{Token::Ether, _literal}; if (_literal == "left") return TokenDesc{Token::Left, _literal}; + if (_literal == "library") return TokenDesc{Token::Library, _literal}; if (_literal == "right") return TokenDesc{Token::Right, _literal}; if (_literal == "hex") return TokenDesc{Token::Hex, _literal}; if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal}; diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 3ef9b6b81..2323609b5 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -57,7 +57,9 @@ void testFunctionCall( u256 _value = 0, string _argumentComment = "", string _expectationComment = "", - vector _rawArguments = vector{} + vector _rawArguments = vector{}, + bool _isConstructor = false, + bool _isLibrary = false ) { BOOST_REQUIRE_EQUAL(_call.expectations.failure, _failure); @@ -79,6 +81,9 @@ void testFunctionCall( ++index; } } + + BOOST_REQUIRE_EQUAL(_call.isConstructor, _isConstructor); + BOOST_REQUIRE_EQUAL(_call.isLibrary, _isLibrary); } BOOST_AUTO_TEST_SUITE(TestFileParserTest) @@ -883,6 +888,51 @@ BOOST_AUTO_TEST_CASE(call_unexpected_character) BOOST_REQUIRE_THROW(parse(source), langutil::Error); } +BOOST_AUTO_TEST_CASE(constructor) +{ + char const* source = R"( + // constructor() + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + testFunctionCall( + calls.at(0), + Mode::SingleLine, + "constructor()", + false, + {}, + {}, + 0, + "", + "", + {}, + true + ); +} + +BOOST_AUTO_TEST_CASE(library) +{ + char const* source = R"( + // library: L + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + testFunctionCall( + calls.at(0), + Mode::SingleLine, + "L", + false, + {}, + {}, + 0, + "", + "", + {}, + false, + true + ); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 25b60a184..5fcfef08b 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -20,8 +20,8 @@ #include #include -#include +#include #include #include @@ -55,6 +55,12 @@ string TestFunctionCall::format( string newline = formatToken(Token::Newline); string failure = formatToken(Token::Failure); + if (m_call.isLibrary) + { + stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature; + return; + } + /// Formats the function signature. This is the same independent from the display-mode. stream << _linePrefix << newline << ws << m_call.signature; if (m_call.value > u256(0)) @@ -124,12 +130,12 @@ string TestFunctionCall::format( if (!matchesExpectation()) { - boost::optional abiParams; + std::optional abiParams; if (isFailure) { if (!output.empty()) - abiParams = boost::make_optional(ContractABIUtils::failureParameters(output)); + abiParams = ContractABIUtils::failureParameters(output); } else abiParams = ContractABIUtils::parametersFromJsonOutputs( @@ -139,7 +145,7 @@ string TestFunctionCall::format( ); string bytesOutput = abiParams ? - BytesUtils::formatRawBytes(output, abiParams.get(), _linePrefix) : + BytesUtils::formatRawBytes(output, abiParams.value(), _linePrefix) : BytesUtils::formatRawBytes( output, ContractABIUtils::defaultParameters(ceil(output.size() / 32)), @@ -208,7 +214,7 @@ string TestFunctionCall::formatBytesParameters( } else { - boost::optional abiParams = ContractABIUtils::parametersFromJsonOutputs( + std::optional abiParams = ContractABIUtils::parametersFromJsonOutputs( _errorReporter, m_contractABI, _signature @@ -216,17 +222,17 @@ string TestFunctionCall::formatBytesParameters( if (abiParams) { - boost::optional preferredParams = ContractABIUtils::preferredParameters( + std::optional preferredParams = ContractABIUtils::preferredParameters( _errorReporter, _parameters, - abiParams.get(), + abiParams.value(), _bytes ); if (preferredParams) { - ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.get(), abiParams.get()); - os << BytesUtils::formatBytesRange(_bytes, preferredParams.get(), _highlight); + ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.value(), abiParams.value()); + os << BytesUtils::formatBytesRange(_bytes, preferredParams.value(), _highlight); } } else diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h index 8edb2afc6..6e5c16dab 100644 --- a/test/libsolidity/util/TestFunctionCall.h +++ b/test/libsolidity/util/TestFunctionCall.h @@ -50,7 +50,7 @@ public: TestFunctionCall(FunctionCall _call): m_call(std::move(_call)) {} /// Formats this function call test and applies the format that was detected during parsing. - /// If _renderResult is false, the expected result of the call will is used, if it's false + /// If _renderResult is false, the expected result of the call will be used, if it's true /// the actual result is used. /// If _highlight is false, it's formatted without colorized highlighting. If it's true, AnsiColorized is /// used to apply a colorized highlighting. diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 34be474b5..35cf21f24 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -27,11 +27,11 @@ #include -#include #include -#include #include +#include +#include using namespace std; using namespace langutil; @@ -63,7 +63,7 @@ std::pair parse(string const& _source) return {false, {}}; } -boost::optional parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) +std::optional parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) { bool success; ErrorList errors; diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 899f40f72..c08e0fe18 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -31,11 +31,11 @@ #include #include -#include #include -#include #include +#include +#include using namespace std; using namespace dev; @@ -61,7 +61,7 @@ bool parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorR return (yul::AsmAnalyzer( analysisInfo, errorReporter, - boost::none, + std::nullopt, _dialect )).analyze(*parserResult); } @@ -73,7 +73,7 @@ bool parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorR return false; } -boost::optional parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarnings = true) +std::optional parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarnings = true) { ErrorList errors; ErrorReporter errorReporter(errors); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index c8fb703f2..fb120ea03 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -157,6 +159,16 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line disambiguate(); CommonSubexpressionEliminator::run(*m_context, *m_ast); } + else if (m_optimizerStep == "conditionalUnsimplifier") + { + disambiguate(); + ConditionalUnsimplifier::run(*m_context, *m_ast); + } + else if (m_optimizerStep == "conditionalSimplifier") + { + disambiguate(); + ConditionalSimplifier::run(*m_context, *m_ast); + } else if (m_optimizerStep == "expressionSplitter") ExpressionSplitter::run(*m_context, *m_ast); else if (m_optimizerStep == "expressionJoiner") diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul new file mode 100644 index 000000000..00c46fcdf --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul @@ -0,0 +1,17 @@ +{ + let y := mload(0x20) + for {} and(y, 8) { pop(y) } { + if y { break } + } +} +// ==== +// step: conditionalSimplifier +// ---- +// { +// let y := mload(0x20) +// for { } and(y, 8) { pop(y) } +// { +// if y { break } +// y := 0 +// } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul new file mode 100644 index 000000000..1e6b0828a --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul @@ -0,0 +1,17 @@ +{ + let y := mload(0x20) + for {} and(y, 8) { pop(y) } { + if y { continue } + } +} +// ==== +// step: conditionalSimplifier +// ---- +// { +// let y := mload(0x20) +// for { } and(y, 8) { pop(y) } +// { +// if y { continue } +// y := 0 +// } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul new file mode 100644 index 000000000..e936287db --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul @@ -0,0 +1,22 @@ +{ + let x := mload(0) + let y := mload(0) + if x { revert(0, 0) } + if y { revert(0, 0) } + for {} and(x, y) {} { + x := 2 + } +} +// ==== +// step: conditionalSimplifier +// ---- +// { +// let x := mload(0) +// let y := mload(0) +// if x { revert(0, 0) } +// x := 0 +// if y { revert(0, 0) } +// y := 0 +// for { } and(x, y) { } +// { x := 2 } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul new file mode 100644 index 000000000..c541590bd --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul @@ -0,0 +1,23 @@ +{ + let x + for {} x { sstore(1, x) } { + if x { continue } + // x is 0 here, but should not be 0 + // anymore in the for loop post block + sstore(0, x) + } + sstore(0, x) +} +// ==== +// step: conditionalSimplifier +// ---- +// { +// let x +// for { } x { sstore(1, x) } +// { +// if x { continue } +// x := 0 +// sstore(0, x) +// } +// sstore(0, x) +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul new file mode 100644 index 000000000..101407783 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul @@ -0,0 +1,32 @@ +{ + let x := mload(0) + for {} 1 {} { + if x { sstore(7, 8) break sstore(8, 9) } + sstore(1, x) + if x { sstore(7, 8) break } + sstore(10, x) + } +} +// ==== +// step: conditionalSimplifier +// ---- +// { +// let x := mload(0) +// for { } 1 { } +// { +// if x +// { +// sstore(7, 8) +// break +// sstore(8, 9) +// } +// sstore(1, x) +// if x +// { +// sstore(7, 8) +// break +// } +// x := 0 +// sstore(10, x) +// } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul new file mode 100644 index 000000000..86983b316 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul @@ -0,0 +1,13 @@ +{ + let x := mload(0) + if x { sstore(0, x) } + sstore(1, x) +} +// ==== +// step: conditionalSimplifier +// ---- +// { +// let x := mload(0) +// if x { sstore(0, x) } +// sstore(1, x) +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul new file mode 100644 index 000000000..ca8bbba87 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul @@ -0,0 +1,18 @@ +{ + let x := mload(0) + if x { sstore(0, x) revert(0, 0) } + sstore(1, x) +} +// ==== +// step: conditionalSimplifier +// ---- +// { +// let x := mload(0) +// if x +// { +// sstore(0, x) +// revert(0, 0) +// } +// x := 0 +// sstore(1, x) +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul new file mode 100644 index 000000000..1f4da9eea --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul @@ -0,0 +1,20 @@ +{ + let x := calldataload(0) + switch x + case 0 { } + case 1 { } + default { } + + pop(x) +} +// ==== +// step: conditionalSimplifier +// ---- +// { +// let x := calldataload(0) +// switch x +// case 0 { x := 0 } +// case 1 { x := 1 } +// default { } +// pop(x) +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul new file mode 100644 index 000000000..f37bf3d01 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ==== +// step: conditionalSimplifier +// ---- +// { } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul new file mode 100644 index 000000000..01b301610 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul @@ -0,0 +1,15 @@ +{ + let y := mload(0x20) + for {} and(y, 8) { pop(y) } { + if y { break } + y := 0 + } +} +// ==== +// step: conditionalUnsimplifier +// ---- +// { +// let y := mload(0x20) +// for { } and(y, 8) { pop(y) } +// { if y { break } } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul new file mode 100644 index 000000000..d4014c2b0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul @@ -0,0 +1,15 @@ +{ + let y := mload(0x20) + for {} and(y, 8) { pop(y) } { + if y { continue } + y := 0 + } +} +// ==== +// step: conditionalUnsimplifier +// ---- +// { +// let y := mload(0x20) +// for { } and(y, 8) { pop(y) } +// { if y { continue } } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul new file mode 100644 index 000000000..de41539f0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul @@ -0,0 +1,22 @@ +{ + let x := mload(0) + let y := mload(0) + if x { revert(0, 0) } + x := 0 + if y { revert(0, 0) } + y := 0 + for {} and(x, y) {} { + x := 2 + } +} +// ==== +// step: conditionalUnsimplifier +// ---- +// { +// let x := mload(0) +// let y := mload(0) +// if x { revert(0, 0) } +// if y { revert(0, 0) } +// for { } and(x, y) { } +// { x := 2 } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul new file mode 100644 index 000000000..d84611352 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul @@ -0,0 +1,21 @@ +{ + let x + for {} x { sstore(1, x) } { + if x { continue } + x := 0 + sstore(0, x) + } + sstore(0, x) +} +// ==== +// step: conditionalUnsimplifier +// ---- +// { +// let x +// for { } x { sstore(1, x) } +// { +// if x { continue } +// sstore(0, x) +// } +// sstore(0, x) +// } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul new file mode 100644 index 000000000..a697c6d25 --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul @@ -0,0 +1,34 @@ +{ + let x := mload(0) + for {} 1 {} { + if x { sstore(7, 8) break sstore(8, 9) } + x := 0 + sstore(1, x) + if x { sstore(7, 8) break } + x := 0 + sstore(10, x) + } +} +// ==== +// step: conditionalUnsimplifier +// ---- +// { +// let x := mload(0) +// for { } 1 { } +// { +// if x +// { +// sstore(7, 8) +// break +// sstore(8, 9) +// } +// x := 0 +// sstore(1, x) +// if x +// { +// sstore(7, 8) +// break +// } +// sstore(10, x) +// } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul new file mode 100644 index 000000000..830ae064c --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul @@ -0,0 +1,15 @@ +{ + let x := mload(0) + if x { sstore(0, x) } + x := 0 + sstore(1, x) +} +// ==== +// step: conditionalUnsimplifier +// ---- +// { +// let x := mload(0) +// if x { sstore(0, x) } +// x := 0 +// sstore(1, x) +// } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul new file mode 100644 index 000000000..13ffc0bac --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul @@ -0,0 +1,18 @@ +{ + let x := mload(0) + if x { sstore(0, x) revert(0, 0) } + x := 0 + sstore(1, x) +} +// ==== +// step: conditionalUnsimplifier +// ---- +// { +// let x := mload(0) +// if x +// { +// sstore(0, x) +// revert(0, 0) +// } +// sstore(1, x) +// } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul new file mode 100644 index 000000000..107b2923b --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul @@ -0,0 +1,22 @@ +{ + let x := calldataload(0) + switch x + case 0 { x := 0 } + case 1 { x := 1 } + case 2 { x := 8 /* wrong literal */ } + default { } + + pop(x) +} +// ==== +// step: conditionalUnsimplifier +// ---- +// { +// let x := calldataload(0) +// switch x +// case 0 { } +// case 1 { } +// case 2 { x := 8 } +// default { } +// pop(x) +// } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul new file mode 100644 index 000000000..cb88da72b --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ==== +// step: conditionalUnsimplifier +// ---- +// { } diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul index 27742ae05..9b401e638 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul @@ -17,8 +17,8 @@ // { } // for { } 0 { } // { } -// for { } 1 { } -// { if iszero(a) { break } } +// for { } a { } +// { } // for { } 1 { } // { // if iszero(add(a, a)) { break } diff --git a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul new file mode 100644 index 000000000..19816b445 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul @@ -0,0 +1,22 @@ +{ + let y := mload(0x20) + for {} and(y, 8) { if y { revert(0, 0) } } { + if y { continue } + sstore(1, y) + } + if y { revert(0, 0) } +} +// ==== +// step: fullSuite +// ---- +// { +// { +// let y := mload(0x20) +// for { } and(y, 8) { if y { revert(0, 0) } } +// { +// if y { continue } +// sstore(1, 0) +// } +// if y { revert(0, 0) } +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul b/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul new file mode 100644 index 000000000..ff898b926 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul @@ -0,0 +1,31 @@ +{ + sstore(0, array_sum(calldataload(0))) + + function array_sum(x) -> sum { + let length := calldataload(x) + for { let i := 0 } lt(i, length) { i := add(i, 1) } { + sum := add(sum, array_load(x, i)) + } + } + function array_load(x, i) -> v { + let len := calldataload(x) + if iszero(lt(i, len)) { revert(0, 0) } + let data := add(x, 0x20) + v := calldataload(add(data, mul(i, 0x20))) + } +} +// ==== +// step: fullSuite +// ---- +// { +// { +// let _1 := calldataload(0) +// let sum := 0 +// let i := sum +// for { } lt(i, calldataload(_1)) { i := add(i, 1) } +// { +// sum := add(sum, calldataload(add(add(_1, mul(i, 0x20)), 0x20))) +// } +// sstore(0, sum) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul index a27acf809..c5ed25e2f 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -21,10 +21,10 @@ // ---- // { // { -// let _1 := mload(0x40) -// mstore(0x40, add(_1, 0x20)) -// mstore(0x40, add(_1, 96)) -// mstore(add(_1, 128), 2) +// let p := mload(0x40) +// mstore(0x40, add(p, 0x20)) +// mstore(0x40, add(p, 96)) +// mstore(add(p, 128), 2) // mstore(0x40, 0x20) // } // } diff --git a/test/solcjsTests.sh b/test/solcjsTests.sh index 93eac32e5..df2554c21 100755 --- a/test/solcjsTests.sh +++ b/test/solcjsTests.sh @@ -53,11 +53,6 @@ DIR=$(mktemp -d) rm -f soljson.js cp "$SOLJSON" soljson.js - # ensure to use always 0.5.0 sources - # FIXME: should be removed once the version bump in this repo is done - rm -rf test/DAO040 - cp -R test/DAO test/DAO040 - # Update version (needed for some tests) echo "Updating package.json to version $VERSION" npm version --allow-same-version --no-git-tag-version $VERSION diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 08b11f40b..052e6b570 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -350,7 +350,7 @@ void setupTerminal() #endif } -boost::optional runTestSuite( +std::optional runTestSuite( TestCreator _testCaseCreator, TestOptions const& _options, fs::path const& _basePath, @@ -364,7 +364,7 @@ boost::optional runTestSuite( if (!fs::exists(testPath) || !fs::is_directory(testPath)) { cerr << _name << " tests not found. Use the --testpath argument." << endl; - return {}; + return std::nullopt; } TestStats stats = TestTool::processPath( diff --git a/test/tools/ossfuzz/abiV2Proto.proto b/test/tools/ossfuzz/abiV2Proto.proto index bf5f782f9..33f887587 100644 --- a/test/tools/ossfuzz/abiV2Proto.proto +++ b/test/tools/ossfuzz/abiV2Proto.proto @@ -17,30 +17,6 @@ syntax = "proto2"; -// Flattened specification of array dimension -// If is_static is false, and this array dimension is contained -// inside another dimension e.g., x[][2] ([2] being the outer dimension) -// then `length` for this dimension is the length of the first dynamically -// sized array. The other (n-1) lengths are unspecified -message ArrayDimensionInfo { - required uint32 length = 1; - required bool is_static = 2; -} - -// TODO: Add more base types -// See https://github.com/ethereum/solidity/issues/6749 -message ArrayType { - oneof base_type_oneof { - IntegerType inty = 1; - FixedByteType byty = 2; - AddressType adty = 3; - StructType stty = 4; - BoolType boolty = 5; - DynamicByteArrayType dynbytesty = 6; - } - repeated ArrayDimensionInfo info = 7; -} - // bool message BoolType {} @@ -78,6 +54,16 @@ message DynamicByteArrayType { required DType type = 1; } +message ArrayType { + required Type t = 1; + required uint32 length = 2; + required bool is_static = 3; +} + +message StructType { + repeated Type t = 1; +} + message NonValueType { oneof nonvalue_type_oneof { DynamicByteArrayType dynbytearray = 1; @@ -86,6 +72,8 @@ message NonValueType { } } +// TODO: Add more types +// See https://github.com/ethereum/solidity/issues/6749 message Type { oneof type_oneof { ValueType vtype = 1; @@ -93,9 +81,6 @@ message Type { } } -// TODO: This must not reference itself either directly or indirectly -message StructType {} - message VarDecl { required Type type = 1; } @@ -107,8 +92,13 @@ message TestFunction { } message Contract { + enum Test { + CALLDATA_CODER = 1; + RETURNDATA_CODER = 2; + } required VarDecl state_vars = 1; required TestFunction testfunction = 2; + required Test test = 3; } package dev.test.abiv2fuzzer; \ No newline at end of file diff --git a/test/tools/ossfuzz/protoToAbiV2.cpp b/test/tools/ossfuzz/protoToAbiV2.cpp index b20fca810..b980a6f93 100644 --- a/test/tools/ossfuzz/protoToAbiV2.cpp +++ b/test/tools/ossfuzz/protoToAbiV2.cpp @@ -1,732 +1,239 @@ -#include -#include -#include #include -#include -#include -#include using namespace std; using namespace dev; using namespace dev::test::abiv2fuzzer; -// Create a new variable declaration and append said variable to function parameter lists -// of coder functions. -// Declared name is x_; parameterized name is c_ -// where is a monotonically increasing integer. -void ProtoConverter::createDeclAndParamList( - std::string const& _type, - DataType _dataType, - std::string& _varName, - std::string& _paramName -) -{ - auto varNames = newVarNames(getNextVarCounter()); - _varName = varNames.first; - _paramName = varNames.second; - - // Declare array - appendVarDeclToOutput(_type, _varName, getQualifier(_dataType)); - - // Add typed params for calling public and external functions with said type - appendTypedParams( - CalleeType::PUBLIC, - isValueType(_dataType), - _type, - _paramName, - ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) - ); - appendTypedParams( - CalleeType::EXTERNAL, - isValueType(_dataType), - _type, - _paramName, - ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) - ); -} - -void ProtoConverter::visitArrayType(std::string const& _baseType, ArrayType const& _x) -{ - std::string type = arrayTypeAsString(_baseType, _x); - std::string varName, paramName; - createDeclAndParamList(type, DataType::ARRAY, varName, paramName); - // Resize-initialize array and add checks - resizeInitArray(_x, _baseType, varName, paramName); -} - -void ProtoConverter::visitType( - DataType _dataType, - std::string const& _type, - std::string const& _value -) -{ - std::string varName, paramName; - createDeclAndParamList(_type, _dataType, varName, paramName); - addCheckedVarDef(_dataType, varName, paramName, _value); -} - -void ProtoConverter::appendVarDeclToOutput( - std::string const& _type, - std::string const& _varName, - std::string const& _qualifier +string ProtoConverter::getVarDecl( + string const& _type, + string const& _varName, + string const& _qualifier ) { // One level of indentation for state variable declarations // Two levels of indentation for local variable declarations - m_output << Whiskers(R"( + return Whiskers(R"( ;)" - ) + ) ("isLocalVar", !m_isStateVar) ("type", _type) ("qual", !_qualifier.empty()) ("qualifier", _qualifier) ("varName", _varName) - .render(); + .render() + + "\n"; } -void ProtoConverter::appendChecks( - DataType _type, - std::string const& _varName, - std::string const& _rhs +pair ProtoConverter::visit(Type const& _type) +{ + switch (_type.type_oneof_case()) + { + case Type::kVtype: + return visit(_type.vtype()); + case Type::kNvtype: + return visit(_type.nvtype()); + case Type::TYPE_ONEOF_NOT_SET: + return make_pair("", ""); + } +} + +pair ProtoConverter::visit(ValueType const& _type) +{ + switch (_type.value_type_oneof_case()) + { + case ValueType::kBoolty: + return visit(_type.boolty()); + case ValueType::kInty: + return visit(_type.inty()); + case ValueType::kByty: + return visit(_type.byty()); + case ValueType::kAdty: + return visit(_type.adty()); + case ValueType::VALUE_TYPE_ONEOF_NOT_SET: + return make_pair("", ""); + } +} + +pair ProtoConverter::visit(NonValueType const& _type) +{ + switch (_type.nonvalue_type_oneof_case()) + { + case NonValueType::kDynbytearray: + return visit(_type.dynbytearray()); + case NonValueType::kArrtype: + if (ValidityVisitor().visit(_type.arrtype())) + return visit(_type.arrtype()); + else + return make_pair("", ""); + case NonValueType::kStype: + if (ValidityVisitor().visit(_type.stype())) + return visit(_type.stype()); + else + return make_pair("", ""); + case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET: + return make_pair("", ""); + } +} + +pair ProtoConverter::visit(BoolType const& _type) +{ + return processType(_type, true); +} + +pair ProtoConverter::visit(IntegerType const& _type) +{ + return processType(_type, true); +} + +pair ProtoConverter::visit(FixedByteType const& _type) +{ + return processType(_type, true); +} + +pair ProtoConverter::visit(AddressType const& _type) +{ + return processType(_type, true); +} + +pair ProtoConverter::visit(DynamicByteArrayType const& _type) +{ + return processType(_type, false); +} + +pair ProtoConverter::visit(ArrayType const& _type) +{ + return processType(_type, false); +} + +pair ProtoConverter::visit(StructType const& _type) +{ + return processType(_type, false); +} + +template +pair ProtoConverter::processType(T const& _type, bool _isValueType) +{ + ostringstream local, global; + auto [varName, paramName] = newVarNames(getNextVarCounter(), m_isStateVar); + + // Add variable name to the argument list of coder function call + if (m_argsCoder.str().empty()) + m_argsCoder << varName; + else + m_argsCoder << ", " << varName; + + string location{}; + if (!m_isStateVar && !_isValueType) + location = "memory"; + + auto varDeclBuffers = varDecl( + varName, + paramName, + _type, + _isValueType, + location + ); + global << varDeclBuffers.first; + local << varDeclBuffers.second; + auto assignCheckBuffers = assignChecker(varName, paramName, _type); + global << assignCheckBuffers.first; + local << assignCheckBuffers.second; + + m_structCounter += m_numStructsAdded; + return make_pair(global.str(), local.str()); +} + +template +pair ProtoConverter::varDecl( + string const& _varName, + string const& _paramName, + T _type, + bool _isValueType, + string const& _location ) { - std::string check = {}; - switch (_type) - { - case DataType::STRING: - check = Whiskers(R"(!bytesCompare(bytes(), ))") - ("varName", _varName) - ("value", _rhs) - .render(); - break; - case DataType::BYTES: - check = Whiskers(R"(!bytesCompare(, ))") - ("varName", _varName) - ("value", _rhs) - .render(); - break; - case DataType::VALUE: - check = Whiskers(R"( != )") - ("varName", _varName) - ("value", _rhs) - .render(); - break; - case DataType::ARRAY: - solUnimplemented("Proto ABIv2 fuzzer: Invalid data type."); - } + ostringstream local, global; - // Each (failing) check returns a unique value to simplify debugging. - m_checks << Whiskers(R"( - if () return ;)" - ) - ("check", check) - ("returnVal", std::to_string(m_returnValue++)) - .render(); + TypeVisitor tVisitor(m_structCounter); + string typeStr = tVisitor.visit(_type); + if (typeStr.empty()) + return make_pair("", ""); + + // Append struct defs + global << tVisitor.structDef(); + m_numStructsAdded = tVisitor.numStructs(); + + // variable declaration + if (m_isStateVar) + global << getVarDecl(typeStr, _varName, _location); + else + local << getVarDecl(typeStr, _varName, _location); + + // Add typed params for calling public and external functions with said type + appendTypedParams( + CalleeType::PUBLIC, + _isValueType, + typeStr, + _paramName, + ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) + ); + appendTypedParams( + CalleeType::EXTERNAL, + _isValueType, + typeStr, + _paramName, + ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) + ); + appendTypes( + _isValueType, + typeStr, + ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) + ); + appendTypedReturn( + _isValueType, + typeStr, + ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) + ); + + // Update dyn param only if necessary + if (tVisitor.isLastDynParamRightPadded()) + m_isLastDynParamRightPadded = true; + + return make_pair(global.str(), local.str()); } -void ProtoConverter::addVarDef(std::string const& _varName, std::string const& _rhs) +template +pair ProtoConverter::assignChecker( + string const& _varName, + string const& _paramName, + T _type +) { - std::string varDefString = Whiskers(R"( - = ;)" - ) - ("varName", _varName) - ("rhs", _rhs) - .render(); + ostringstream local; + AssignCheckVisitor acVisitor( + _varName, + _paramName, + m_returnValue, + m_isStateVar, + m_counter, + m_structCounter + ); + pair assignCheckStrPair = acVisitor.visit(_type); + m_returnValue += acVisitor.errorStmts(); + m_counter += acVisitor.counted(); + + m_checks << assignCheckStrPair.second; // State variables cannot be assigned in contract-scope // Therefore, we buffer their assignments and // render them in function scope later. - if (m_isStateVar) - m_statebuffer << varDefString; - else - m_output << varDefString; + local << assignCheckStrPair.first; + return make_pair("", local.str()); } -void ProtoConverter::addCheckedVarDef( - DataType _type, - std::string const& _varName, - std::string const& _paramName, - std::string const& _rhs) +pair ProtoConverter::visit(VarDecl const& _x) { - addVarDef(_varName, _rhs); - appendChecks(_type, _paramName, _rhs); -} - -// Runtime check for array length. -void ProtoConverter::checkResizeOp(std::string const& _paramName, unsigned _len) -{ - appendChecks(DataType::VALUE, _paramName + ".length", std::to_string(_len)); -} - -std::string ProtoConverter::boolValueAsString(unsigned _counter) -{ - return ((_counter % 2) ? "true" : "false"); -} - -/* Input(s) - * - Unsigned integer to be hashed - * - Width of desired uint value - * Processing - * - Take hash of first parameter and mask it with the max unsigned value for given bit width - * Output - * - string representation of uint value - */ -std::string ProtoConverter::uintValueAsString(unsigned _width, unsigned _counter) -{ - solAssert( - (_width % 8 == 0), - "Proto ABIv2 Fuzzer: Unsigned integer width is not a multiple of 8" - ); - return maskUnsignedIntToHex(_counter, _width/4); -} - -/* Input(s) - * - counter to be hashed to derive a value for Integer type - * - Width of desired int value - * Processing - * - Take hash of first parameter and mask it with the max signed value for given bit width - * Output - * - string representation of int value - */ -std::string ProtoConverter::intValueAsString(unsigned _width, unsigned _counter) -{ - solAssert( - (_width % 8 == 0), - "Proto ABIv2 Fuzzer: Signed integer width is not a multiple of 8" - ); - return maskUnsignedIntToHex(_counter, ((_width/4) - 1)); -} - -std::string ProtoConverter::addressValueAsString(unsigned _counter) -{ - return Whiskers(R"(address())") - ("value", uintValueAsString(160, _counter)) - .render(); -} - -std::string ProtoConverter::croppedString( - unsigned _numBytes, - unsigned _counter, - bool _isHexLiteral -) -{ - // _numBytes can not be zero or exceed 32 bytes - solAssert( - _numBytes > 0 && _numBytes <= 32, - "Proto ABIv2 fuzzer: Too short or too long a cropped string" - ); - - // Number of masked nibbles is twice the number of bytes for a - // hex literal of _numBytes bytes. For a string literal, each nibble - // is treated as a character. - unsigned numMaskNibbles = _isHexLiteral ? _numBytes * 2 : _numBytes; - - // Start position of substring equals totalHexStringLength - numMaskNibbles - // totalHexStringLength = 64 + 2 = 66 - // e.g., 0x12345678901234567890123456789012 is a total of 66 characters - // |---------------------^-----------| - // <--- start position---><--numMask-> - // <-----------total length ---------> - // Note: This assumes that maskUnsignedIntToHex() invokes toHex(..., HexPrefix::Add) - unsigned startPos = 66 - numMaskNibbles; - // Extracts the least significant numMaskNibbles from the result - // of maskUnsignedIntToHex(). - return maskUnsignedIntToHex( - _counter, - numMaskNibbles - ).substr(startPos, numMaskNibbles); -} - -std::string ProtoConverter::hexValueAsString( - unsigned _numBytes, - unsigned _counter, - bool _isHexLiteral, - bool _decorate -) -{ - solAssert(_numBytes > 0 && _numBytes <= 32, - "Proto ABIv2 fuzzer: Invalid hex length" - ); - - // If _decorate is set, then we return a hex"" or a "" string. - if (_numBytes == 0) - return Whiskers(R"(hex"")") - ("decorate", _decorate) - ("isHex", _isHexLiteral) - .render(); - - // This is needed because solidity interprets a 20-byte 0x prefixed hex literal as an address - // payable type. - return Whiskers(R"(hex"")") - ("decorate", _decorate) - ("isHex", _isHexLiteral) - ("value", croppedString(_numBytes, _counter, _isHexLiteral)) - .render(); -} - -std::string ProtoConverter::variableLengthValueAsString( - unsigned _numBytes, - unsigned _counter, - bool _isHexLiteral -) -{ - solAssert(_numBytes >= 0 && _numBytes <= s_maxDynArrayLength, - "Proto ABIv2 fuzzer: Invalid hex length" - ); - if (_numBytes == 0) - return Whiskers(R"(hex"")") - ("isHex", _isHexLiteral) - .render(); - - unsigned numBytesRemaining = _numBytes; - // Stores the literal - string output{}; - // If requested value is shorter than or exactly 32 bytes, - // the literal is the return value of hexValueAsString. - if (numBytesRemaining <= 32) - output = hexValueAsString( - numBytesRemaining, - _counter, - _isHexLiteral, - /*decorate=*/false - ); - // If requested value is longer than 32 bytes, the literal - // is obtained by duplicating the return value of hexValueAsString - // until we reach a value of the requested size. - else - { - // Create a 32-byte value to be duplicated and - // update number of bytes to be appended. - // Stores the cached literal that saves us - // (expensive) calls to keccak256. - string cachedString = hexValueAsString( - /*numBytes=*/32, - _counter, - _isHexLiteral, - /*decorate=*/false - ); - output = cachedString; - numBytesRemaining -= 32; - - // Append bytes from cachedString until - // we create a value of desired length. - unsigned numAppendedBytes; - while (numBytesRemaining > 0) - { - // We append at most 32 bytes at a time - numAppendedBytes = numBytesRemaining >= 32 ? 32 : numBytesRemaining; - output += cachedString.substr( - 0, - // Double the substring length for hex literals since each - // character is actually half a byte (or a nibble). - _isHexLiteral ? numAppendedBytes * 2 : numAppendedBytes - ); - numBytesRemaining -= numAppendedBytes; - } - solAssert( - numBytesRemaining == 0, - "Proto ABIv2 fuzzer: Logic flaw in variable literal creation" - ); - } - - if (_isHexLiteral) - solAssert( - output.size() == 2 * _numBytes, - "Proto ABIv2 fuzzer: Generated hex literal is of incorrect length" - ); - else - solAssert( - output.size() == _numBytes, - "Proto ABIv2 fuzzer: Generated string literal is of incorrect length" - ); - - // Decorate output - return Whiskers(R"(hex"")") - ("isHexLiteral", _isHexLiteral) - ("value", output) - .render(); -} - -std::string ProtoConverter::fixedByteValueAsString(unsigned _width, unsigned _counter) -{ - solAssert( - (_width >= 1 && _width <= 32), - "Proto ABIv2 Fuzzer: Fixed byte width is not between 1--32" - ); - return hexValueAsString(_width, _counter, /*isHexLiteral=*/true); -} - -std::string ProtoConverter::integerValueAsString(bool _sign, unsigned _width, unsigned _counter) -{ - if (_sign) - return intValueAsString(_width, _counter); - else - return uintValueAsString(_width, _counter); -} - -std::string ProtoConverter::bytesArrayTypeAsString(DynamicByteArrayType const& _x) -{ - switch (_x.type()) - { - case DynamicByteArrayType::BYTES: - return "bytes"; - case DynamicByteArrayType::STRING: - return "string"; - } -} - -std::string ProtoConverter::structTypeAsString(StructType const&) -{ - // TODO: Implement this - return {}; -} - -void ProtoConverter::visit(BoolType const&) -{ - visitType( - DataType::VALUE, - getBoolTypeAsString(), - boolValueAsString(getNextCounter()) - ); -} - -void ProtoConverter::visit(IntegerType const& _x) -{ - visitType( - DataType::VALUE, - getIntTypeAsString(_x), - integerValueAsString(isIntSigned(_x), getIntWidth(_x), getNextCounter()) - ); -} - -void ProtoConverter::visit(AddressType const& _x) -{ - visitType( - DataType::VALUE, - getAddressTypeAsString(_x), - addressValueAsString(getNextCounter()) - ); -} - -void ProtoConverter::visit(FixedByteType const& _x) -{ - visitType( - DataType::VALUE, - getFixedByteTypeAsString(_x), - fixedByteValueAsString(getFixedByteWidth(_x), getNextCounter()) - ); -} - -void ProtoConverter::visit(ValueType const& _x) -{ - switch (_x.value_type_oneof_case()) - { - case ValueType::kInty: - visit(_x.inty()); - break; - case ValueType::kByty: - visit(_x.byty()); - break; - case ValueType::kAdty: - visit(_x.adty()); - break; - case ValueType::kBoolty: - visit(_x.boolty()); - break; - case ValueType::VALUE_TYPE_ONEOF_NOT_SET: - break; - } -} - -void ProtoConverter::visit(DynamicByteArrayType const& _x) -{ - bool isBytes = _x.type() == DynamicByteArrayType::BYTES; - visitType( - isBytes ? DataType::BYTES : DataType::STRING, - bytesArrayTypeAsString(_x), - bytesArrayValueAsString( - getNextCounter(), - isBytes - ) - ); - // Update right padding of type - m_isLastDynParamRightPadded = true; -} - -// TODO: Implement struct visitor -void ProtoConverter::visit(StructType const&) -{ -} - -std::string ProtoConverter::arrayDimInfoAsString(ArrayDimensionInfo const& _x) -{ - return Whiskers(R"([])") - ("isStatic", _x.is_static()) - ("length", std::to_string(getStaticArrayLengthFromFuzz(_x.length()))) - .render(); -} - -void ProtoConverter::arrayDimensionsAsStringVector( - ArrayType const& _x, - std::vector& _vecOfStr) -{ - solAssert(_x.info_size() > 0, "Proto ABIv2 Fuzzer: Array dimensions empty."); - for (auto const& dim: _x.info()) - _vecOfStr.push_back(arrayDimInfoAsString(dim)); -} - -ProtoConverter::VecOfBoolUnsigned ProtoConverter::arrayDimensionsAsPairVector( - ArrayType const& _x -) -{ - VecOfBoolUnsigned arrayDimsPairVector = {}; - for (auto const& dim: _x.info()) - arrayDimsPairVector.push_back(arrayDimInfoAsPair(dim)); - solAssert(!arrayDimsPairVector.empty(), "Proto ABIv2 Fuzzer: Array dimensions empty."); - return arrayDimsPairVector; -} - -std::string ProtoConverter::getValueByBaseType(ArrayType const& _x) -{ - switch (_x.base_type_oneof_case()) - { - case ArrayType::kInty: - return integerValueAsString(isIntSigned(_x.inty()), getIntWidth(_x.inty()), getNextCounter()); - case ArrayType::kByty: - return fixedByteValueAsString(getFixedByteWidth(_x.byty()), getNextCounter()); - case ArrayType::kAdty: - return addressValueAsString(getNextCounter()); - case ArrayType::kBoolty: - return boolValueAsString(getNextCounter()); - case ArrayType::kDynbytesty: - return bytesArrayValueAsString( - getNextCounter(), - _x.dynbytesty().type() == DynamicByteArrayType::BYTES - ); - // TODO: Implement structs. - case ArrayType::kStty: - case ArrayType::BASE_TYPE_ONEOF_NOT_SET: - solAssert(false, "Proto ABIv2 fuzzer: Invalid array base type"); - } -} - -ProtoConverter::DataType ProtoConverter::getDataTypeByBaseType(ArrayType const& _x) -{ - switch (_x.base_type_oneof_case()) - { - case ArrayType::kInty: - case ArrayType::kByty: - case ArrayType::kAdty: - case ArrayType::kBoolty: - return DataType::VALUE; - case ArrayType::kDynbytesty: - return getDataTypeOfDynBytesType(_x.dynbytesty()); - case ArrayType::kStty: - case ArrayType::BASE_TYPE_ONEOF_NOT_SET: - solUnimplemented("Proto ABIv2 fuzzer: Invalid array base type"); - } -} - -// Adds a resize operation for a given dimension of type `_type` and expression referenced -// by `_var`. `_isStatic` is true for statically sized dimensions, false otherwise. -// `_arrayLen` is equal to length of statically sized array dimension. For dynamically -// sized dimension, we use `getDynArrayLengthFromFuzz()` and a monotonically increasing -// counter to obtain actual length. Function returns dimension length. -unsigned ProtoConverter::resizeDimension( - bool _isStatic, - unsigned _arrayLen, - std::string const& _var, - std::string const& _param, - std::string const& _type -) -{ - unsigned length; - if (_isStatic) - length = _arrayLen; - else - { - length = getDynArrayLengthFromFuzz(_arrayLen, getNextCounter()); - - // If local var, new T(l); - // Else, l; - std::string lhs, rhs; - if (m_isStateVar) - { - lhs = _var + ".length"; - rhs = Whiskers(R"()") - ("length", std::to_string(length)) - .render(); - } - else - { - lhs = _var; - rhs = Whiskers(R"(new ())") - ("type", _type) - ("length", std::to_string(length)) - .render(); - } - // If local var, x = new T(l); - // Else, x.length = l; - addVarDef(lhs, rhs); - } - - // if (c.length != l) - checkResizeOp(_param, length); - return length; -} - -void ProtoConverter::resizeHelper( - ArrayType const& _x, - std::vector _arrStrVec, - VecOfBoolUnsigned _arrInfoVec, - std::string const& _varName, - std::string const& _paramName -) -{ - // Initialize value expressions if we have arrived at leaf node, - // (depth-first) recurse otherwise. - if (_arrInfoVec.empty()) - { - // expression name is _var - // value is a value of base type - std::string value = getValueByBaseType(_x); - // add assignment and check - DataType dataType = getDataTypeByBaseType(_x); - addCheckedVarDef(dataType, _varName, _paramName, value); - } - else - { - auto& dim = _arrInfoVec.back(); - - std::string type = std::accumulate( - _arrStrVec.begin(), - _arrStrVec.end(), - std::string("") - ); - unsigned length = resizeDimension(dim.first, dim.second, _varName, _paramName, type); - // Recurse one level dimension down. - _arrStrVec.pop_back(); - _arrInfoVec.pop_back(); - for (unsigned i = 0; i < length; i++) - resizeHelper( - _x, - _arrStrVec, - _arrInfoVec, - _varName + "[" + std::to_string(i) + "]", - _paramName + "[" + std::to_string(i) + "]" - ); - } -} - -// This function takes care of properly resizing and initializing ArrayType. -// In parallel, it adds runtime checks on array bound and values. -void ProtoConverter::resizeInitArray( - ArrayType const& _x, - std::string const& _baseType, - std::string const& _varName, - std::string const& _paramName -) -{ - VecOfBoolUnsigned arrInfoVec = arrayDimensionsAsPairVector(_x); - std::vector arrStrVec = {_baseType}; - arrayDimensionsAsStringVector(_x, arrStrVec); - resizeHelper(_x, arrStrVec, arrInfoVec, _varName, _paramName); -} - -// Returns array type from it's base type (e.g., int8) and array dimensions info contained in -// ArrayType. -std::string ProtoConverter::arrayTypeAsString(std::string const& _baseType, ArrayType const& _x) -{ - std::vector typeStringVec = {_baseType}; - arrayDimensionsAsStringVector(_x, typeStringVec); - - return std::accumulate( - typeStringVec.begin(), - typeStringVec.end(), - std::string("") - ); -} - -void ProtoConverter::visit(ArrayType const& _x) -{ - // Bail out if input contains too few or too many dimensions. - if (_x.info_size() == 0 || _x.info_size() > (int)s_maxArrayDimensions) - return; - - // Array type is dynamically encoded if one of the following is true - // - array base type is "bytes" or "string" - // - at least one array dimension is dynamically sized. - if (_x.base_type_oneof_case() == ArrayType::kDynbytesty) - m_isLastDynParamRightPadded = true; - else - for (auto const& dim: _x.info()) - if (!dim.is_static()) - { - m_isLastDynParamRightPadded = true; - break; - } - - string baseType = {}; - switch (_x.base_type_oneof_case()) - { - case ArrayType::kInty: - baseType = getIntTypeAsString(_x.inty()); - break; - case ArrayType::kByty: - baseType = getFixedByteTypeAsString(_x.byty()); - break; - case ArrayType::kAdty: - baseType = getAddressTypeAsString(_x.adty()); - break; - case ArrayType::kBoolty: - baseType = getBoolTypeAsString(); - break; - case ArrayType::kDynbytesty: - baseType = bytesArrayTypeAsString(_x.dynbytesty()); - break; - case ArrayType::kStty: - case ArrayType::BASE_TYPE_ONEOF_NOT_SET: - return; - } - visitArrayType(baseType, _x); -} - -void ProtoConverter::visit(NonValueType const& _x) -{ - switch (_x.nonvalue_type_oneof_case()) - { - case NonValueType::kDynbytearray: - visit(_x.dynbytearray()); - break; - case NonValueType::kArrtype: - visit(_x.arrtype()); - break; - case NonValueType::kStype: - visit(_x.stype()); - break; - case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET: - break; - } -} - -void ProtoConverter::visit(Type const& _x) -{ - switch (_x.type_oneof_case()) - { - case Type::kVtype: - visit(_x.vtype()); - break; - case Type::kNvtype: - visit(_x.nvtype()); - break; - case Type::TYPE_ONEOF_NOT_SET: - break; - } -} - -void ProtoConverter::visit(VarDecl const& _x) -{ - visit(_x.type()); + return visit(_x.type()); } std::string ProtoConverter::equalityChecksAsString() @@ -769,6 +276,41 @@ void ProtoConverter::appendTypedParams( } } +void ProtoConverter::appendTypes( + bool _isValueType, + string const& _typeString, + Delimiter _delimiter +) +{ + string qualifiedTypeString = ( + _isValueType ? + _typeString : + _typeString + " memory" + ); + m_types << Whiskers(R"()") + ("delimiter", delimiterToString(_delimiter)) + ("type", qualifiedTypeString) + .render(); +} + +void ProtoConverter::appendTypedReturn( + bool _isValueType, + string const& _typeString, + Delimiter _delimiter +) +{ + string qualifiedTypeString = ( + _isValueType ? + _typeString : + _typeString + " memory" + ); + m_typedReturn << Whiskers(R"( )") + ("delimiter", delimiterToString(_delimiter)) + ("type", qualifiedTypeString) + ("varName", "lv_" + to_string(m_varCounter - 1)) + .render(); +} + // Adds the qualifier "calldata" to non-value parameter of an external function. void ProtoConverter::appendTypedParamsExternal( bool _isValueType, @@ -820,34 +362,85 @@ std::string ProtoConverter::typedParametersAsString(CalleeType _calleeType) } } -/// Test function to be called externally. -void ProtoConverter::visit(TestFunction const& _x) +string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDefs) { - m_output << R"( - - function test() public returns (uint) { - )"; - - // Define state variables in function scope - m_output << m_statebuffer.str(); - // TODO: Support more than one but less than N local variables - visit(_x.local_vars()); + auto localVarBuffers = visit(_x.local_vars()); - m_output << Whiskers(R"( - uint returnVal = this.coder_public(); + string structTypeDecl = localVarBuffers.first; + string localVarDefs = localVarBuffers.second; + + ostringstream testBuffer; + + string testFunction = Whiskers(R"( + function test() public returns (uint) { + return test_calldata_coding(); + return test_returndata_coding(); + })") + ("calldata", m_test == Contract_Test::Contract_Test_CALLDATA_CODER) + ("returndata", m_test == Contract_Test::Contract_Test_RETURNDATA_CODER) + .render(); + + string functionDeclCalldata = "function test_calldata_coding() internal returns (uint)"; + string functionDeclReturndata = "function test_returndata_coding() internal returns (uint)"; + + testBuffer << Whiskers(R"( + + + { + + + + } + + + + { + + } + + + function coder_returndata_external() external returns () { + + + return (); + } + +)") + ("testFunction", testFunction) + ("calldata", m_test == Contract_Test::Contract_Test_CALLDATA_CODER) + ("returndata", m_test == Contract_Test::Contract_Test_RETURNDATA_CODER) + ("calldataHelperFuncs", calldataHelperFunctions()) + ("varsPresent", !m_types.str().empty()) + ("structTypeDecl", structTypeDecl) + ("functionDeclCalldata", functionDeclCalldata) + ("functionDeclReturndata", functionDeclReturndata) + ("storageVarDefs", _storageVarDefs) + ("localVarDefs", localVarDefs) + ("calldataTestCode", testCallDataFunction(_x.invalid_encoding_length())) + ("returndataTestCode", testReturnDataFunction()) + ("return_types", m_types.str()) + ("return_values", m_argsCoder.str()) + .render(); + return testBuffer.str(); +} + +string ProtoConverter::testCallDataFunction(unsigned _invalidLength) +{ + return Whiskers(R"( + uint returnVal = this.coder_calldata_public(); if (returnVal != 0) return returnVal; - returnVal = this.coder_external(); + returnVal = this.coder_calldata_external(); if (returnVal != 0) return uint(200000) + returnVal; - bytes memory argumentEncoding = abi.encode(); + bytes memory argumentEncoding = abi.encode(); returnVal = checkEncodedCall( - this.coder_public.selector, + this.coder_calldata_public.selector, argumentEncoding, , @@ -856,7 +449,7 @@ void ProtoConverter::visit(TestFunction const& _x) return returnVal; returnVal = checkEncodedCall( - this.coder_external.selector, + this.coder_calldata_external.selector, argumentEncoding, , @@ -865,27 +458,33 @@ void ProtoConverter::visit(TestFunction const& _x) return uint(200000) + returnVal; return 0; - } - )") - ("parameterNames", dev::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter)) - ("invalidLengthFuzz", std::to_string(_x.invalid_encoding_length())) - ("isRightPadded", isLastDynParamRightPadded() ? "true" : "false") - ("atLeastOneVar", m_varCounter > 0) - .render(); + )") + ("argumentNames", m_argsCoder.str()) + ("invalidLengthFuzz", std::to_string(_invalidLength)) + ("isRightPadded", isLastDynParamRightPadded() ? "true" : "false") + ("atLeastOneVar", m_varCounter > 0) + .render(); } -void ProtoConverter::writeHelperFunctions() +string ProtoConverter::testReturnDataFunction() { - m_output << R"( - function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) { - if(a.length != b.length) - return false; - for (uint i = 0; i < a.length; i++) - if (a[i] != b[i]) - return false; - return true; - } + return Whiskers(R"( + + () = this.coder_returndata_external(); + + + return 0; + )") + ("varsPresent", !m_typedReturn.str().empty()) + ("varDecl", m_typedReturn.str()) + ("equality_checks", m_checks.str()) + .render(); +} +string ProtoConverter::calldataHelperFunctions() +{ + stringstream calldataHelperFuncs; + calldataHelperFuncs << R"( /// Accepts function selector, correct argument encoding, and length of /// invalid encoding and returns the correct and incorrect abi encoding /// for calling the function specified by the function selector. @@ -948,20 +547,19 @@ void ProtoConverter::writeHelperFunctions() if (success == true) return 400001; return 0; - } - )"; + })"; // These are callee functions that encode from storage, decode to // memory/calldata and check if decoded value matches storage value // return true on successful match, false otherwise - m_output << Whiskers(R"( - function coder_public() public pure returns (uint) { - + calldataHelperFuncs << Whiskers(R"( + function coder_calldata_public() public pure returns (uint) { + return 0; } - function coder_external() external pure returns (uint) { - + function coder_calldata_external() external pure returns (uint) { + return 0; } )") @@ -969,26 +567,638 @@ void ProtoConverter::writeHelperFunctions() ("equality_checks", equalityChecksAsString()) ("parameters_calldata", typedParametersAsString(CalleeType::EXTERNAL)) .render(); + + return calldataHelperFuncs.str(); +} + +string ProtoConverter::commonHelperFunctions() +{ + stringstream helperFuncs; + helperFuncs << R"( + /// Compares bytes, returning true if they are equal and false otherwise. + function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) { + if(a.length != b.length) + return false; + for (uint i = 0; i < a.length; i++) + if (a[i] != b[i]) + return false; + return true; + } + )"; + + return helperFuncs.str(); } void ProtoConverter::visit(Contract const& _x) { - m_output << R"(pragma solidity >=0.0; -pragma experimental ABIEncoderV2; + string pragmas = R"(pragma solidity >=0.0; +pragma experimental ABIEncoderV2;)"; + + // Record test spec + m_test = _x.test(); -contract C { -)"; // TODO: Support more than one but less than N state variables - visit(_x.state_vars()); + auto storageBuffers = visit(_x.state_vars()); + string storageVarDecls = storageBuffers.first; + string storageVarDefs = storageBuffers.second; m_isStateVar = false; - // Test function - visit(_x.testfunction()); - writeHelperFunctions(); - m_output << "\n}"; + string testFunction = visit(_x.testfunction(), storageVarDefs); + /* Structure of contract body + * - Storage variable declarations + * - Struct definitions + * - Test function + * - Storage variable assignments + * - Local variable definitions and assignments + * - Test code proper (calls public and external functions) + * - Helper functions + */ + ostringstream contractBody; + contractBody << storageVarDecls + << testFunction + << commonHelperFunctions(); + m_output << Whiskers(R"( + + +)") + ("pragmas", pragmas) + ("contractStart", "contract C {") + ("contractBody", contractBody.str()) + ("contractEnd", "}") + .render(); } string ProtoConverter::contractToString(Contract const& _input) { visit(_input); return m_output.str(); -} \ No newline at end of file +} + +/// Type visitor +string TypeVisitor::visit(BoolType const&) +{ + m_baseType = "bool"; + return m_baseType; +} + +string TypeVisitor::visit(IntegerType const& _type) +{ + m_baseType = getIntTypeAsString(_type); + return m_baseType; +} + +string TypeVisitor::visit(FixedByteType const& _type) +{ + m_baseType = getFixedByteTypeAsString(_type); + return m_baseType; +} + +string TypeVisitor::visit(AddressType const& _type) +{ + m_baseType = getAddressTypeAsString(_type); + return m_baseType; +} + +string TypeVisitor::visit(ArrayType const& _type) +{ + if (!ValidityVisitor().visit(_type)) + return ""; + + string baseType = visit(_type.t()); + solAssert(!baseType.empty(), ""); + string arrayBraces = _type.is_static() ? + string("[") + + to_string(getStaticArrayLengthFromFuzz(_type.length())) + + string("]") : + string("[]"); + m_baseType += arrayBraces; + + // If we don't know yet if the array will be dynamically encoded, + // check again. If we already know that it will be, there's no + // need to do anything. + if (!m_isLastDynParamRightPadded) + m_isLastDynParamRightPadded = DynParamVisitor().visit(_type); + + return baseType + arrayBraces; +} + +string TypeVisitor::visit(DynamicByteArrayType const& _type) +{ + m_isLastDynParamRightPadded = true; + m_baseType = bytesArrayTypeAsString(_type); + return m_baseType; +} + +void TypeVisitor::structDefinition(StructType const& _type) +{ + // Return an empty string if struct is empty + solAssert(ValidityVisitor().visit(_type), ""); + + // Reset field counter and indentation + unsigned wasFieldCounter = m_structFieldCounter; + unsigned wasIndentation = m_indentation; + + m_indentation = 1; + m_structFieldCounter = 0; + + // Commence struct declaration + string structDef = lineString( + "struct " + + string(s_structNamePrefix) + + to_string(m_structCounter) + + " {" + ); + + // Increase indentation for struct fields + m_indentation++; + for (auto const& t: _type.t()) + { + string type{}; + + if (!ValidityVisitor().visit(t)) + continue; + + TypeVisitor tVisitor(m_structCounter + 1); + type = tVisitor.visit(t); + m_structCounter += tVisitor.numStructs(); + m_structDef << tVisitor.structDef(); + + solAssert(!type.empty(), ""); + + structDef += lineString( + Whiskers(R"( ;)") + ("type", type) + ("member", "m" + to_string(m_structFieldCounter++)) + .render() + ); + } + m_indentation--; + structDef += lineString("}"); + m_structCounter++; + m_structDef << structDef; + m_indentation = wasIndentation; + m_structFieldCounter = wasFieldCounter; +} + +string TypeVisitor::visit(StructType const& _type) +{ + if (ValidityVisitor().visit(_type)) + { + // Add struct definition + structDefinition(_type); + // Set last dyn param if struct contains a dyn param e.g., bytes, array etc. + m_isLastDynParamRightPadded = DynParamVisitor().visit(_type); + // If top-level struct is a non-emtpy struct, assign the name S + m_baseType = s_structTypeName + to_string(m_structStartCounter); + } + else + m_baseType = {}; + + return m_baseType; +} + +/// AssignCheckVisitor implementation +pair AssignCheckVisitor::visit(BoolType const& _type) +{ + string value = ValueGetterVisitor(counter()).visit(_type); + return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE); +} + +pair AssignCheckVisitor::visit(IntegerType const& _type) +{ + string value = ValueGetterVisitor(counter()).visit(_type); + return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE); +} + +pair AssignCheckVisitor::visit(FixedByteType const& _type) +{ + string value = ValueGetterVisitor(counter()).visit(_type); + return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE); +} + +pair AssignCheckVisitor::visit(AddressType const& _type) +{ + string value = ValueGetterVisitor(counter()).visit(_type); + return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE); +} + +pair AssignCheckVisitor::visit(DynamicByteArrayType const& _type) +{ + string value = ValueGetterVisitor(counter()).visit(_type); + DataType dataType = _type.type() == DynamicByteArrayType::BYTES ? DataType::BYTES : DataType::STRING; + return assignAndCheckStringPair(m_varName, m_paramName, value, value, dataType); +} + +pair AssignCheckVisitor::visit(ArrayType const& _type) +{ + if (!ValidityVisitor().visit(_type)) + return make_pair("", ""); + + // Obtain type of array to be resized and initialized + string typeStr{}; + + unsigned wasStructCounter = m_structCounter; + TypeVisitor tVisitor(m_structCounter); + typeStr = tVisitor.visit(_type); + + pair resizeBuffer; + string lengthStr; + unsigned length; + + // Resize dynamic arrays + if (!_type.is_static()) + { + length = getDynArrayLengthFromFuzz(_type.length(), counter()); + lengthStr = to_string(length); + if (m_stateVar) + resizeBuffer = assignAndCheckStringPair( + m_varName + ".length", + m_paramName + ".length", + lengthStr, + lengthStr, + DataType::VALUE + ); + else + { + // Resizing memory arrays via the new operator + string resizeOp = Whiskers(R"(new ())") + ("fullTypeStr", typeStr) + ("length", lengthStr) + .render(); + resizeBuffer = assignAndCheckStringPair( + m_varName, + m_paramName + ".length", + resizeOp, + lengthStr, + DataType::VALUE + ); + } + } + else + { + length = getStaticArrayLengthFromFuzz(_type.length()); + lengthStr = to_string(length); + // Add check on length + resizeBuffer.second = checkString(m_paramName + ".length", lengthStr, DataType::VALUE); + } + + // Add assignCheckBuffer and check statements + pair assignCheckBuffer; + string wasVarName = m_varName; + string wasParamName = m_paramName; + for (unsigned i = 0; i < length; i++) + { + m_varName = wasVarName + "[" + to_string(i) + "]"; + m_paramName = wasParamName + "[" + to_string(i) + "]"; + pair assign = visit(_type.t()); + assignCheckBuffer.first += assign.first; + assignCheckBuffer.second += assign.second; + if (i < length - 1) + m_structCounter = wasStructCounter; + } + + // Since struct visitor won't be called for zero-length + // arrays, struct counter will not get incremented. Therefore, + // we need to manually force a recursive struct visit. + if (length == 0 && TypeVisitor().arrayOfStruct(_type)) + visit(_type.t()); + + m_varName = wasVarName; + m_paramName = wasParamName; + + // Compose resize and initialization assignment and check + return make_pair( + resizeBuffer.first + assignCheckBuffer.first, + resizeBuffer.second + assignCheckBuffer.second + ); +} + +pair AssignCheckVisitor::visit(StructType const& _type) +{ + if (!ValidityVisitor().visit(_type)) + return make_pair("", ""); + + pair assignCheckBuffer; + unsigned i = 0; + + // Increment struct counter + m_structCounter++; + + string wasVarName = m_varName; + string wasParamName = m_paramName; + + for (auto const& t: _type.t()) + { + m_varName = wasVarName + ".m" + to_string(i); + m_paramName = wasParamName + ".m" + to_string(i); + pair assign = visit(t); + // If type is not well formed continue without + // updating state. + if (assign.first.empty() && assign.second.empty()) + continue; + assignCheckBuffer.first += assign.first; + assignCheckBuffer.second += assign.second; + i++; + } + m_varName = wasVarName; + m_paramName = wasParamName; + return assignCheckBuffer; +} + +pair AssignCheckVisitor::assignAndCheckStringPair( + string const& _varRef, + string const& _checkRef, + string const& _assignValue, + string const& _checkValue, + DataType _type +) +{ + return make_pair(assignString(_varRef, _assignValue), checkString(_checkRef, _checkValue, _type)); +} + +string AssignCheckVisitor::assignString(string const& _ref, string const& _value) +{ + string assignStmt = Whiskers(R"( = ;)") + ("ref", _ref) + ("value", _value) + .render(); + return indentation() + assignStmt + "\n"; +} + +string AssignCheckVisitor::checkString(string const& _ref, string const& _value, DataType _type) +{ + string checkPred; + switch (_type) + { + case DataType::STRING: + checkPred = Whiskers(R"(!bytesCompare(bytes(), ))") + ("varName", _ref) + ("value", _value) + .render(); + break; + case DataType::BYTES: + checkPred = Whiskers(R"(!bytesCompare(, ))") + ("varName", _ref) + ("value", _value) + .render(); + break; + case DataType::VALUE: + checkPred = Whiskers(R"( != )") + ("varName", _ref) + ("value", _value) + .render(); + break; + case DataType::ARRAY: + solUnimplemented("Proto ABIv2 fuzzer: Invalid data type."); + } + string checkStmt = Whiskers(R"(if () return ;)") + ("checkPred", checkPred) + ("errCode", to_string(m_errorCode++)) + .render(); + return indentation() + checkStmt + "\n"; +} + +/// ValueGetterVisitor +string ValueGetterVisitor::visit(BoolType const&) +{ + return counter() % 2 ? "true" : "false"; +} + +string ValueGetterVisitor::visit(IntegerType const& _type) +{ + return integerValueAsString( + _type.is_signed(), + getIntWidth(_type), + counter() + ); +} + +string ValueGetterVisitor::visit(FixedByteType const& _type) +{ + return fixedByteValueAsString( + getFixedByteWidth(_type), + counter() + ); +} + +string ValueGetterVisitor::visit(AddressType const&) +{ + return addressValueAsString(counter()); +} + +string ValueGetterVisitor::visit(DynamicByteArrayType const& _type) +{ + return bytesArrayValueAsString( + counter(), + getDataTypeOfDynBytesType(_type) == DataType::BYTES + ); +} + +std::string ValueGetterVisitor::integerValueAsString(bool _sign, unsigned _width, unsigned _counter) +{ + if (_sign) + return intValueAsString(_width, _counter); + else + return uintValueAsString(_width, _counter); +} + +/* Input(s) + * - Unsigned integer to be hashed + * - Width of desired uint value + * Processing + * - Take hash of first parameter and mask it with the max unsigned value for given bit width + * Output + * - string representation of uint value + */ +std::string ValueGetterVisitor::uintValueAsString(unsigned _width, unsigned _counter) +{ + solAssert( + (_width % 8 == 0), + "Proto ABIv2 Fuzzer: Unsigned integer width is not a multiple of 8" + ); + return maskUnsignedIntToHex(_counter, _width/4); +} + +/* Input(s) + * - counter to be hashed to derive a value for Integer type + * - Width of desired int value + * Processing + * - Take hash of first parameter and mask it with the max signed value for given bit width + * Output + * - string representation of int value + */ +std::string ValueGetterVisitor::intValueAsString(unsigned _width, unsigned _counter) +{ + solAssert( + (_width % 8 == 0), + "Proto ABIv2 Fuzzer: Signed integer width is not a multiple of 8" + ); + return maskUnsignedIntToHex(_counter, ((_width/4) - 1)); +} + +std::string ValueGetterVisitor::croppedString( + unsigned _numBytes, + unsigned _counter, + bool _isHexLiteral +) +{ + solAssert( + _numBytes > 0 && _numBytes <= 32, + "Proto ABIv2 fuzzer: Too short or too long a cropped string" + ); + + // Number of masked nibbles is twice the number of bytes for a + // hex literal of _numBytes bytes. For a string literal, each nibble + // is treated as a character. + unsigned numMaskNibbles = _isHexLiteral ? _numBytes * 2 : _numBytes; + + // Start position of substring equals totalHexStringLength - numMaskNibbles + // totalHexStringLength = 64 + 2 = 66 + // e.g., 0x12345678901234567890123456789012 is a total of 66 characters + // |---------------------^-----------| + // <--- start position---><--numMask-> + // <-----------total length ---------> + // Note: This assumes that maskUnsignedIntToHex() invokes toHex(..., HexPrefix::Add) + unsigned startPos = 66 - numMaskNibbles; + // Extracts the least significant numMaskNibbles from the result + // of maskUnsignedIntToHex(). + return maskUnsignedIntToHex( + _counter, + numMaskNibbles + ).substr(startPos, numMaskNibbles); +} + +std::string ValueGetterVisitor::hexValueAsString( + unsigned _numBytes, + unsigned _counter, + bool _isHexLiteral, + bool _decorate +) +{ + solAssert(_numBytes > 0 && _numBytes <= 32, + "Proto ABIv2 fuzzer: Invalid hex length" + ); + + // If _decorate is set, then we return a hex"" or a "" string. + if (_numBytes == 0) + return Whiskers(R"(hex"")") + ("decorate", _decorate) + ("isHex", _isHexLiteral) + .render(); + + // This is needed because solidity interprets a 20-byte 0x prefixed hex literal as an address + // payable type. + return Whiskers(R"(hex"")") + ("decorate", _decorate) + ("isHex", _isHexLiteral) + ("value", croppedString(_numBytes, _counter, _isHexLiteral)) + .render(); +} + +std::string ValueGetterVisitor::fixedByteValueAsString(unsigned _width, unsigned _counter) +{ + solAssert( + (_width >= 1 && _width <= 32), + "Proto ABIv2 Fuzzer: Fixed byte width is not between 1--32" + ); + return hexValueAsString(_width, _counter, /*isHexLiteral=*/true); +} + +std::string ValueGetterVisitor::addressValueAsString(unsigned _counter) +{ + return Whiskers(R"(address())") + ("value", uintValueAsString(160, _counter)) + .render(); +} + +std::string ValueGetterVisitor::variableLengthValueAsString( + unsigned _numBytes, + unsigned _counter, + bool _isHexLiteral +) +{ + // TODO: Move this to caller +// solAssert(_numBytes >= 0 && _numBytes <= s_maxDynArrayLength, +// "Proto ABIv2 fuzzer: Invalid hex length" +// ); + if (_numBytes == 0) + return Whiskers(R"(hex"")") + ("isHex", _isHexLiteral) + .render(); + + unsigned numBytesRemaining = _numBytes; + // Stores the literal + string output{}; + // If requested value is shorter than or exactly 32 bytes, + // the literal is the return value of hexValueAsString. + if (numBytesRemaining <= 32) + output = hexValueAsString( + numBytesRemaining, + _counter, + _isHexLiteral, + /*decorate=*/false + ); + // If requested value is longer than 32 bytes, the literal + // is obtained by duplicating the return value of hexValueAsString + // until we reach a value of the requested size. + else + { + // Create a 32-byte value to be duplicated and + // update number of bytes to be appended. + // Stores the cached literal that saves us + // (expensive) calls to keccak256. + string cachedString = hexValueAsString( + /*numBytes=*/32, + _counter, + _isHexLiteral, + /*decorate=*/false + ); + output = cachedString; + numBytesRemaining -= 32; + + // Append bytes from cachedString until + // we create a value of desired length. + unsigned numAppendedBytes; + while (numBytesRemaining > 0) + { + // We append at most 32 bytes at a time + numAppendedBytes = numBytesRemaining >= 32 ? 32 : numBytesRemaining; + output += cachedString.substr( + 0, + // Double the substring length for hex literals since each + // character is actually half a byte (or a nibble). + _isHexLiteral ? numAppendedBytes * 2 : numAppendedBytes + ); + numBytesRemaining -= numAppendedBytes; + } + solAssert( + numBytesRemaining == 0, + "Proto ABIv2 fuzzer: Logic flaw in variable literal creation" + ); + } + + if (_isHexLiteral) + solAssert( + output.size() == 2 * _numBytes, + "Proto ABIv2 fuzzer: Generated hex literal is of incorrect length" + ); + else + solAssert( + output.size() == _numBytes, + "Proto ABIv2 fuzzer: Generated string literal is of incorrect length" + ); + + // Decorate output + return Whiskers(R"(hex"")") + ("isHexLiteral", _isHexLiteral) + ("value", output) + .render(); +} + +string ValueGetterVisitor::bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral) +{ + return variableLengthValueAsString( + getVarLength(_counter), + _counter, + _isHexLiteral + ); +} diff --git a/test/tools/ossfuzz/protoToAbiV2.h b/test/tools/ossfuzz/protoToAbiV2.h index 401867364..236554bbc 100644 --- a/test/tools/ossfuzz/protoToAbiV2.h +++ b/test/tools/ossfuzz/protoToAbiV2.h @@ -1,100 +1,144 @@ #pragma once +#include + #include #include +#include #include -#include + +#include + +#include #include +#include + #include #include /** * Template of the solidity test program generated by this converter is as follows: * - * pragma solidity >=0.0; - * pragma experimental ABIEncoderV2; + * pragma solidity >=0.0; + * pragma experimental ABIEncoderV2; * - * contract C { - * // State variable - * string x_0; - * // Test function that is called by the VM. - * function test() public returns (uint) { - * // Local variable - * bytes x_1 = "1"; - * x_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"; - * uint returnVal = this.coder_public(x_0, x_1); - * if (returnVal != 0) - * return returnVal; - * // Since the return codes in the public and external coder functions are identical - * // we offset error code by a fixed amount (200000) for differentiation. - * returnVal = this.coder_external(x_0, x_1); - * if (returnVal != 0) - * return 200000 + returnVal; - * // Encode parameters - * bytes memory argumentEncoding = abi.encode(); - * returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, ); - * // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data. - * if (returnVal != 0) - * return returnVal; + * contract C { + * // State variable + * string sv_0; + * // Test function that is called by the VM. + * // There are 2 variations of this function: one returns + * // the output of test_calldata_coding() and the other + * // returns the output of test_returndata_coding(). The + * // proto field called Test decides which one of the two + * // are chosen for creating a test case. + * function test() public returns (uint) { + * // The protobuf field "Contract.test" decides which of + * // the two internal functions "test_calldata_coding()" + * // and "test_returndata_coding()" are called. Here, + * // we assume that the protobuf field equals "CALLDATA_CODER" + * return this.test_calldata_coding() + * } * - * returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, ); - * // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data. - * // Offset return value to distinguish between failures originating from coder_public and coder_external. - * if (returnVal != 0) - * return uint(200000) + returnVal; - * // Return zero if all checks pass. - * return 0; - * } + * // The following function is generated if the protobuf field + * // "Contract.test" is equal to "RETURNDATA_CODER". + * function test_returndata_coding() internal returns (uint) { + * string memory lv_0, bytes memory lv_1 = test_returndata_external(); + * if (lv_0 != 044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d) + * return 1; + * if (lv_1 != "1") + * return 2; + * return 0; + * } * - * /// Accepts function selector, correct argument encoding, and an invalid encoding length as input. - * /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding - * /// succeeds. Returns zero if both calls meet expectation. - * function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz) - * public returns (uint) { - * ... - * } + * // The following function is generated if the protobuf field + * // "Contract.test" is equal to "RETURNDATA_CODER". + * function test_returndata_external() external returns (string memory, bytes memory) + * { + * sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"; + * bytes memory lv_0 = "1"; + * return (sv_0, lv_0); + * } * - * /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns - * /// the correct and incorrect abi encoding for calling the function specified by the function selector. - * function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz) - * internal pure returns (bytes memory, bytes memory) { - * ... - * } + * // The following function is generated if the protobuf field + * // "Contract.test" is equal to "CALLDATA_CODER". + * function test_calldata_coding() internal returns (uint) { + * // Local variable + * bytes lv_1 = "1"; + * sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"; + * uint returnVal = this.coder_public(sv_0, lv_1); + * if (returnVal != 0) + * return returnVal; + * // Since the return codes in the public and external coder functions are identical + * // we offset error code by a fixed amount (200000) for differentiation. + * returnVal = this.coder_external(sv_0, lv_1); + * if (returnVal != 0) + * return 200000 + returnVal; + * // Encode parameters + * bytes memory argumentEncoding = abi.encode(); + * returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, ); + * // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data. + * if (returnVal != 0) + * return returnVal; * - * /// Compares two dynamically sized bytes arrays for equality. - * function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) { - * ... - * } + * returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, ); + * // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data. + * // Offset return value to distinguish between failures originating from coder_public and coder_external. + * if (returnVal != 0) + * return uint(200000) + returnVal; + * // Return zero if all checks pass. + * return 0; + * } * - * // Public function that is called by test() function. Accepts one or more arguments and returns - * // a uint value (zero if abi en/decoding was successful, non-zero otherwise) - * function coder_public(string memory c_0, bytes memory c_1) public pure returns (uint) { - * if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d")) - * return 1; - * if (!bytesCompare(c_1, "1")) - * return 2; - * return 0; - * } + * /// Accepts function selector, correct argument encoding, and an invalid encoding length as input. + * /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding + * /// succeeds. Returns zero if both calls meet expectation. + * function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz) + * public returns (uint) { + * ... + * } * - * // External function that is called by test() function. Accepts one or more arguments and returns - * // a uint value (zero if abi en/decoding was successful, non-zero otherwise) - * function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) { - * if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d")) - * return 1; - * if (!bytesCompare(c_1, "1")) - * return 2; - * return 0; - * } - * } + * /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns + * /// the correct and incorrect abi encoding for calling the function specified by the function selector. + * function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz) + * internal pure returns (bytes memory, bytes memory) { + * ... + * } + * + * /// Compares two dynamically sized bytes arrays for equality. + * function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) { + * ... + * } + * + * // Public function that is called by test() function. Accepts one or more arguments and returns + * // a uint value (zero if abi en/decoding was successful, non-zero otherwise) + * function coder_public(string memory c_0, bytes memory c_1) public pure returns (uint) { + * if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d")) + * return 1; + * if (!bytesCompare(c_1, "1")) + * return 2; + * return 0; + * } + * + * // External function that is called by test() function. Accepts one or more arguments and returns + * // a uint value (zero if abi en/decoding was successful, non-zero otherwise) + * function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) { + * if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d")) + * return 1; + * if (!bytesCompare(c_1, "1")) + * return 2; + * return 0; + * } + * } */ - namespace dev { namespace test { namespace abiv2fuzzer { +/// Converts a protobuf input into a Solidity program that tests +/// abi coding. class ProtoConverter { public: @@ -103,101 +147,109 @@ public: m_counter(0), m_varCounter(0), m_returnValue(1), - m_isLastDynParamRightPadded(false) + m_isLastDynParamRightPadded(false), + m_structCounter(0), + m_numStructsAdded(0) {} ProtoConverter(ProtoConverter const&) = delete; - ProtoConverter(ProtoConverter&&) = delete; - std::string contractToString(Contract const& _input); - private: - using VecOfBoolUnsigned = std::vector>; - enum class Delimiter { ADD, SKIP }; + /// Enum of possible function types that decode abi + /// encoded parameters. enum class CalleeType { PUBLIC, EXTERNAL }; - enum class DataType - { - BYTES, - STRING, - VALUE, - ARRAY - }; - - void visit(BoolType const&); - - void visit(IntegerType const&); - - void visit(FixedByteType const&); - - void visit(AddressType const&); - - void visit(ArrayType const&); - - void visit(DynamicByteArrayType const&); - - void visit(StructType const&); - - void visit(ValueType const&); - - void visit(NonValueType const&); - - void visit(Type const&); - - void visit(VarDecl const&); - - void visit(TestFunction const&); + /// Visitors for various Protobuf types + /// Visit top-level contract specification void visit(Contract const&); - std::string getValueByBaseType(ArrayType const&); + /// Convert test function specification into Solidity test + /// function + /// @param _testSpec: Protobuf test function specification + /// @param _storageDefs: String containing Solidity assignment + /// statements to be placed inside the scope of the test function. + std::string visit(TestFunction const& _testSpec, std::string const& _storageDefs); - DataType getDataTypeByBaseType(ArrayType const& _x); + /// Visitors for the remaining protobuf types. They convert + /// the input protobuf specification type into Solidity code. + /// @return A pair of strings, first of which contains Solidity + /// code to be placed inside contract scope, second of which contains + /// Solidity code to be placed inside test function scope. + std::pair visit(VarDecl const&); + std::pair visit(Type const&); + std::pair visit(ValueType const&); + std::pair visit(NonValueType const&); + std::pair visit(BoolType const&); + std::pair visit(IntegerType const&); + std::pair visit(FixedByteType const&); + std::pair visit(AddressType const&); + std::pair visit(DynamicByteArrayType const&); + std::pair visit(ArrayType const&); + std::pair visit(StructType const&); - void resizeInitArray( - ArrayType const& _x, - std::string const& _baseType, - std::string const& _varName, - std::string const& _paramName - ); + /// Convert a protobuf type @a _T into Solidity code to be placed + /// inside contract and test function scopes. + /// @param: _type (of parameterized type protobuf type T) is the type + /// of protobuf input to be converted. + /// @param: _isValueType is true if _type is a Solidity value type e.g., uint + /// and false otherwise e.g., string + /// @return: A pair of strings, first of which contains Solidity + /// code to be placed inside contract scope, second of which contains + /// Solidity code to be placed inside test function scope. + template + std::pair processType(T const& _type, bool _isValueType); - unsigned resizeDimension( - bool _isStatic, - unsigned _arrayLen, - std::string const& _var, - std::string const& _param, - std::string const& _type - ); - - void resizeHelper( - ArrayType const& _x, - std::vector _arrStrVec, - VecOfBoolUnsigned _arrInfoVec, - std::string const& _varName, - std::string const& _paramName - ); - - // Utility functions - void appendChecks(DataType _type, std::string const& _varName, std::string const& _rhs); - - void addVarDef(std::string const& _varName, std::string const& _rhs); - - void addCheckedVarDef( - DataType _type, + /// Convert a protobuf type @a _T into Solidity variable assignment and check + /// statements to be placed inside contract and test function scopes. + /// @param: _varName is the name of the Solidity variable + /// @param: _paramName is the name of the Solidity parameter that is passed + /// to the test function + /// @param: _type (of parameterized type protobuf type T) is the type + /// of protobuf input to be converted. + /// @return: A pair of strings, first of which contains Solidity + /// statements to be placed inside contract scope, second of which contains + /// Solidity statements to be placed inside test function scope. + template + std::pair assignChecker( std::string const& _varName, std::string const& _paramName, - std::string const& _rhs + T _type ); + /// Convert a protobuf type @a _T into Solidity variable declaration statement. + /// @param: _varName is the name of the Solidity variable + /// @param: _paramName is the name of the Solidity parameter that is passed + /// to the test function + /// @param: _type (of parameterized type protobuf type T) is the type + /// of protobuf input to be converted. + /// @param: _isValueType is a boolean that is true if _type is a + /// Solidity value type e.g., uint and false otherwise e.g., string + /// @param: _location is the Solidity location qualifier string to + /// be used inside variable declaration statements + /// @return: A pair of strings, first of which contains Solidity + /// variable declaration statement to be placed inside contract scope, + /// second of which contains Solidity variable declaration statement + /// to be placed inside test function scope. + template + std::pair varDecl( + std::string const& _varName, + std::string const& _paramName, + T _type, + bool _isValueType, + std::string const& _location + ); + + /// Appends a function parameter to the function parameter stream. void appendTypedParams( CalleeType _calleeType, bool _isValueType, @@ -206,6 +258,8 @@ private: Delimiter _delimiter ); + /// Appends a function parameter to the public test function's + /// parameter stream. void appendTypedParamsPublic( bool _isValueType, std::string const& _typeString, @@ -213,6 +267,8 @@ private: Delimiter _delimiter = Delimiter::ADD ); + /// Appends a function parameter to the external test function's + /// parameter stream. void appendTypedParamsExternal( bool _isValueType, std::string const& _typeString, @@ -220,111 +276,167 @@ private: Delimiter _delimiter = Delimiter::ADD ); - void appendVarDeclToOutput( + /// Append types to typed stream used by returndata coders. + void appendTypes( + bool _isValueType, + std::string const& _typeString, + Delimiter _delimiter + ); + + /// Append typed return value. + void appendTypedReturn( + bool _isValueType, + std::string const& _typeString, + Delimiter _delimiter + ); + + /// Returns a Solidity variable declaration statement + /// @param _type: string containing Solidity type of the + /// variable to be declared. + /// @param _varName: string containing Solidity variable + /// name + /// @param _qualifier: string containing location where + /// the variable will be placed + std::string getVarDecl( std::string const& _type, std::string const& _varName, std::string const& _qualifier ); - void checkResizeOp(std::string const& _varName, unsigned _len); - - void visitType(DataType _dataType, std::string const& _type, std::string const& _value); - - void visitArrayType(std::string const&, ArrayType const&); - - void createDeclAndParamList( - std::string const& _type, - DataType _dataType, - std::string& _varName, - std::string& _paramName - ); - + /// Return checks that are encoded as Solidity if statements + /// as string std::string equalityChecksAsString(); + /// Return comma separated typed function parameters as string std::string typedParametersAsString(CalleeType _calleeType); - void writeHelperFunctions(); + /// Return commonly used Solidity helper functions as string + std::string commonHelperFunctions(); - // Function definitions - // m_counter is used to derive values for typed variables - unsigned getNextCounter() - { - return m_counter++; - } + /// Return helper functions used to test calldata coding + std::string calldataHelperFunctions(); - // m_varCounter is used to derive declared and parameterized variable names + /// Return top-level calldata coder test function as string + std::string testCallDataFunction(unsigned _invalidLength); + + /// Return top-level returndata coder test function as string + std::string testReturnDataFunction(); + + /// Return the next variable count that is used for + /// variable naming. unsigned getNextVarCounter() { return m_varCounter++; } - // Accepts an unsigned counter and returns a pair of strings - // First string is declared name (s_varNamePrefix) - // Second string is parameterized name (s_paramPrefix) - auto newVarNames(unsigned _varCounter) + /// Return a pair of names for Solidity variable and the same variable when + /// passed either as a function parameter or used to store the tuple + /// returned from a function. + /// @param _varCounter: name suffix + /// @param _stateVar: predicate that is true for state variables, false otherwise + std::pair newVarNames(unsigned _varCounter, bool _stateVar) { + std::string varName = _stateVar ? s_stateVarNamePrefix : s_localVarNamePrefix; return std::make_pair( - s_varNamePrefix + std::to_string(_varCounter), - s_paramNamePrefix + std::to_string(_varCounter) + varName + std::to_string(_varCounter), + paramName() + std::to_string(_varCounter) ); } - std::string getQualifier(DataType _dataType) + std::string paramName() { - return ((isValueType(_dataType) || m_isStateVar) ? "" : "memory"); + switch (m_test) + { + case Contract_Test::Contract_Test_CALLDATA_CODER: + return s_paramNamePrefix; + case Contract_Test::Contract_Test_RETURNDATA_CODER: + return s_localVarNamePrefix; + } } + /// Checks if the last dynamically encoded Solidity type is right + /// padded, returning true if it is and false otherwise. bool isLastDynParamRightPadded() { return m_isLastDynParamRightPadded; } - // Static declarations - static std::string structTypeAsString(StructType const& _x); - static std::string boolValueAsString(unsigned _counter); - static std::string intValueAsString(unsigned _width, unsigned _counter); - static std::string uintValueAsString(unsigned _width, unsigned _counter); - static std::string integerValueAsString(bool _sign, unsigned _width, unsigned _counter); - static std::string addressValueAsString(unsigned _counter); - static std::string fixedByteValueAsString(unsigned _width, unsigned _counter); - - /// Returns a hex literal if _isHexLiteral is true, a string literal otherwise. - /// The size of the returned literal is _numBytes bytes. - /// @param _decorate If true, the returned string is enclosed within double quotes - /// if _isHexLiteral is false. - /// @param _isHexLiteral If true, the returned string is enclosed within - /// double quotes prefixed by the string "hex" if _decorate is true. If - /// _decorate is false, the returned string is returned as-is. - /// @return hex value as string - static std::string hexValueAsString( - unsigned _numBytes, - unsigned _counter, - bool _isHexLiteral, - bool _decorate = true - ); - - /// Concatenates the hash value obtained from monotonically increasing counter - /// until the desired number of bytes determined by _numBytes. - /// @param _width Desired number of bytes for hex value - /// @param _counter A counter value used for creating a keccak256 hash - /// @param _isHexLiteral Since this routine may be used to construct - /// string or hex literals, this flag is used to construct a valid output. - /// @return Valid hex or string literal of size _width bytes - static std::string variableLengthValueAsString( - unsigned _width, - unsigned _counter, - bool _isHexLiteral - ); - static std::vector> arrayDimensionsAsPairVector(ArrayType const& _x); - static std::string arrayDimInfoAsString(ArrayDimensionInfo const& _x); - static void arrayDimensionsAsStringVector( - ArrayType const& _x, - std::vector& - ); - static std::string bytesArrayTypeAsString(DynamicByteArrayType const& _x); - static std::string arrayTypeAsString(std::string const&, ArrayType const&); + /// Convert delimter to a comma or null string. static std::string delimiterToString(Delimiter _delimiter); - static std::string croppedString(unsigned _numBytes, unsigned _counter, bool _isHexLiteral); + + /// Contains the test program + std::ostringstream m_output; + /// Contains a subset of the test program. This subset contains + /// checks to be encoded in the test program + std::ostringstream m_checks; + /// Contains typed parameter list to be passed to callee functions + std::ostringstream m_typedParamsExternal; + std::ostringstream m_typedParamsPublic; + /// Contains type stream to be used in returndata coder function + /// signature + std::ostringstream m_types; + std::ostringstream m_typedReturn; + /// Argument names to be passed to coder functions + std::ostringstream m_argsCoder; + /// Predicate that is true if we are in contract scope + bool m_isStateVar; + unsigned m_counter; + unsigned m_varCounter; + /// Monotonically increasing return value for error reporting + unsigned m_returnValue; + /// Flag that indicates if last dynamically encoded parameter + /// passed to a function call is of a type that is going to be + /// right padded by the ABI encoder. + bool m_isLastDynParamRightPadded; + /// Struct counter + unsigned m_structCounter; + unsigned m_numStructsAdded; + /// Enum stating abiv2 coder to be tested + Contract_Test m_test; + /// Prefixes for declared and parameterized variable names + static auto constexpr s_localVarNamePrefix = "lv_"; + static auto constexpr s_stateVarNamePrefix = "sv_"; + static auto constexpr s_paramNamePrefix = "p_"; +}; + +/// Visitor interface for Solidity protobuf types. +template +class AbiV2ProtoVisitor +{ +public: + static unsigned constexpr s_maxArrayDimensions = 3; + virtual ~AbiV2ProtoVisitor() = default; + + virtual T visit(BoolType const& _node) = 0; + virtual T visit(IntegerType const& _node) = 0; + virtual T visit(FixedByteType const& _node) = 0; + virtual T visit(AddressType const& _node) = 0; + virtual T visit(DynamicByteArrayType const& _node) = 0; + virtual T visit(ArrayType const& _node) = 0; + virtual T visit(StructType const& _node) = 0; + virtual T visit(ValueType const& _node) + { + return visitValueType(_node); + } + virtual T visit(NonValueType const& _node) + { + return visitNonValueType(_node); + } + virtual T visit(Type const& _node) + { + return visitType(_node); + } + + enum class DataType + { + BYTES, + STRING, + VALUE, + ARRAY + }; + + /// Prefixes for declared and parameterized variable names + static auto constexpr s_structNamePrefix = "S"; // Static function definitions static bool isValueType(DataType _dataType) @@ -342,11 +454,6 @@ private: return _x.is_signed(); } - static std::string getBoolTypeAsString() - { - return "bool"; - } - static std::string getIntTypeAsString(IntegerType const& _x) { return ((isIntSigned(_x) ? "int" : "uint") + std::to_string(getIntWidth(_x))); @@ -364,7 +471,7 @@ private: static std::string getAddressTypeAsString(AddressType const& _x) { - return (_x.payable() ? "address payable": "address"); + return (_x.payable() ? "address payable" : "address"); } static DataType getDataTypeOfDynBytesType(DynamicByteArrayType const& _x) @@ -375,12 +482,6 @@ private: return DataType::BYTES; } - /// Returns true if input is either a string or bytes, false otherwise. - static bool isDataTypeBytesOrString(DataType _type) - { - return _type == DataType::STRING || _type == DataType::BYTES; - } - // Convert _counter to string and return its keccak256 hash static u256 hashUnsignedInt(unsigned _counter) { @@ -389,7 +490,7 @@ private: static u256 maskUnsignedInt(unsigned _counter, unsigned _numMaskNibbles) { - return hashUnsignedInt(_counter) & u256("0x" + std::string(_numMaskNibbles, 'f')); + return hashUnsignedInt(_counter) & u256("0x" + std::string(_numMaskNibbles, 'f')); } // Requires caller to pass number of nibbles (twice the number of bytes) as second argument. @@ -414,15 +515,6 @@ private: return _fuzz % s_maxArrayLength + 1; } - static std::pair arrayDimInfoAsPair(ArrayDimensionInfo const& _x) - { - return ( - _x.is_static() ? - std::make_pair(true, getStaticArrayLengthFromFuzz(_x.length())) : - std::make_pair(false, getDynArrayLengthFromFuzz(_x.length(), 0)) - ); - } - /// Returns a pseudo-random value for the size of a string/hex /// literal. Used for creating variable length hex/string literals. /// @param _counter Monotonically increasing counter value @@ -435,47 +527,411 @@ private: return (_counter + 879) * 32 % (s_maxDynArrayLength + 1); } + static std::string bytesArrayTypeAsString(DynamicByteArrayType const& _x) + { + switch (_x.type()) + { + case DynamicByteArrayType::BYTES: + return "bytes"; + case DynamicByteArrayType::STRING: + return "string"; + } + } +protected: +T visitValueType(ValueType const& _type) +{ + switch (_type.value_type_oneof_case()) + { + case ValueType::kInty: + return visit(_type.inty()); + case ValueType::kByty: + return visit(_type.byty()); + case ValueType::kAdty: + return visit(_type.adty()); + case ValueType::kBoolty: + return visit(_type.boolty()); + case ValueType::VALUE_TYPE_ONEOF_NOT_SET: + return T(); + } +} + +T visitNonValueType(NonValueType const& _type) +{ + switch (_type.nonvalue_type_oneof_case()) + { + case NonValueType::kDynbytearray: + return visit(_type.dynbytearray()); + case NonValueType::kArrtype: + return visit(_type.arrtype()); + case NonValueType::kStype: + return visit(_type.stype()); + case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET: + return T(); + } +} + +T visitType(Type const& _type) +{ + switch (_type.type_oneof_case()) + { + case Type::kVtype: + return visit(_type.vtype()); + case Type::kNvtype: + return visit(_type.nvtype()); + case Type::TYPE_ONEOF_NOT_SET: + return T(); + } +} +private: + static unsigned constexpr s_maxArrayLength = 4; + static unsigned constexpr s_maxDynArrayLength = 256; +}; + +/// Converts a protobuf type into a Solidity type string. +class TypeVisitor: public AbiV2ProtoVisitor +{ +public: + TypeVisitor(unsigned _structSuffix = 0): + m_indentation(1), + m_structCounter(_structSuffix), + m_structStartCounter(_structSuffix), + m_structFieldCounter(0), + m_isLastDynParamRightPadded(false) + {} + + std::string visit(BoolType const&) override; + std::string visit(IntegerType const&) override; + std::string visit(FixedByteType const&) override; + std::string visit(AddressType const&) override; + std::string visit(ArrayType const&) override; + std::string visit(DynamicByteArrayType const&) override; + std::string visit(StructType const&) override; + using AbiV2ProtoVisitor::visit; + std::string baseType() + { + return m_baseType; + } + bool isLastDynParamRightPadded() + { + return m_isLastDynParamRightPadded; + } + std::string structDef() + { + return m_structDef.str(); + } + unsigned numStructs() + { + return m_structCounter - m_structStartCounter; + } + static bool arrayOfStruct(ArrayType const& _type) + { + Type const& baseType = _type.t(); + if (baseType.has_nvtype() && baseType.nvtype().has_stype()) + return true; + else if (baseType.has_nvtype() && baseType.nvtype().has_arrtype()) + return arrayOfStruct(baseType.nvtype().arrtype()); + else + return false; + } +private: + void structDefinition(StructType const&); + + std::string indentation() + { + return std::string(m_indentation * 1, '\t'); + } + std::string lineString(std::string const& _line) + { + return indentation() + _line + "\n"; + } + + std::string m_baseType; + std::ostringstream m_structDef; + unsigned m_indentation; + unsigned m_structCounter; + unsigned m_structStartCounter; + unsigned m_structFieldCounter; + bool m_isLastDynParamRightPadded; + + static auto constexpr s_structTypeName = "S"; +}; + +/// Returns a pair of strings, first of which contains assignment statements +/// to initialize a given type, and second of which contains checks to be +/// placed inside the coder function to test abi en/decoding. +class AssignCheckVisitor: public AbiV2ProtoVisitor> +{ +public: + AssignCheckVisitor( + std::string _varName, + std::string _paramName, + unsigned _errorStart, + bool _stateVar, + unsigned _counter, + unsigned _structCounter + ) + { + m_counter = m_counterStart = _counter; + m_varName = _varName; + m_paramName = _paramName; + m_errorCode = m_errorStart = _errorStart; + m_indentation = 2; + m_stateVar = _stateVar; + m_structCounter = m_structStart = _structCounter; + } + std::pair visit(BoolType const&) override; + std::pair visit(IntegerType const&) override; + std::pair visit(FixedByteType const&) override; + std::pair visit(AddressType const&) override; + std::pair visit(ArrayType const&) override; + std::pair visit(DynamicByteArrayType const&) override; + std::pair visit(StructType const&) override; + using AbiV2ProtoVisitor>::visit; + + unsigned errorStmts() + { + return m_errorCode - m_errorStart; + } + + unsigned counted() + { + return m_counter - m_counterStart; + } + + unsigned structs() + { + return m_structCounter - m_structStart; + } +private: + std::string indentation() + { + return std::string(m_indentation * 1, '\t'); + } + unsigned counter() + { + return m_counter++; + } + + std::pair assignAndCheckStringPair( + std::string const& _varRef, + std::string const& _checkRef, + std::string const& _assignValue, + std::string const& _checkValue, + DataType _type + ); + std::string assignString(std::string const&, std::string const&); + std::string checkString(std::string const&, std::string const&, DataType); + unsigned m_counter; + unsigned m_counterStart; + std::string m_varName; + std::string m_paramName; + unsigned m_errorCode; + unsigned m_errorStart; + unsigned m_indentation; + bool m_stateVar; + unsigned m_structCounter; + unsigned m_structStart; +}; + +/// Returns a valid value (as a string) for a given type. +class ValueGetterVisitor: AbiV2ProtoVisitor +{ +public: + ValueGetterVisitor(unsigned _counter = 0): m_counter(_counter) {} + + std::string visit(BoolType const&) override; + std::string visit(IntegerType const&) override; + std::string visit(FixedByteType const&) override; + std::string visit(AddressType const&) override; + std::string visit(DynamicByteArrayType const&) override; + std::string visit(ArrayType const&) override + { + solAssert(false, "ABIv2 proto fuzzer: Cannot call valuegettervisitor on complex type"); + } + std::string visit(StructType const&) override + { + solAssert(false, "ABIv2 proto fuzzer: Cannot call valuegettervisitor on complex type"); + } + using AbiV2ProtoVisitor::visit; +private: + unsigned counter() + { + return m_counter++; + } + + static std::string intValueAsString(unsigned _width, unsigned _counter); + static std::string uintValueAsString(unsigned _width, unsigned _counter); + static std::string integerValueAsString(bool _sign, unsigned _width, unsigned _counter); + static std::string addressValueAsString(unsigned _counter); + static std::string fixedByteValueAsString(unsigned _width, unsigned _counter); + + /// Returns a hex literal if _isHexLiteral is true, a string literal otherwise. + /// The size of the returned literal is _numBytes bytes. + /// @param _decorate If true, the returned string is enclosed within double quotes + /// if _isHexLiteral is false. + /// @param _isHexLiteral If true, the returned string is enclosed within + /// double quotes prefixed by the string "hex" if _decorate is true. If + /// _decorate is false, the returned string is returned as-is. + /// @return hex value as string + static std::string hexValueAsString( + unsigned _numBytes, + unsigned _counter, + bool _isHexLiteral, + bool _decorate = true + ); + /// Returns a hex/string literal of variable length whose value and /// size are pseudo-randomly determined from the counter value. /// @param _counter A monotonically increasing counter value /// @param _isHexLiteral Flag that indicates whether hex (if true) or /// string literal (false) is desired /// @return A variable length hex/string value - static std::string bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral) + static std::string bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral); + + /// Concatenates the hash value obtained from monotonically increasing counter + /// until the desired number of bytes determined by _numBytes. + /// @param _width Desired number of bytes for hex value + /// @param _counter A counter value used for creating a keccak256 hash + /// @param _isHexLiteral Since this routine may be used to construct + /// string or hex literals, this flag is used to construct a valid output. + /// @return Valid hex or string literal of size _width bytes + static std::string variableLengthValueAsString( + unsigned _width, + unsigned _counter, + bool _isHexLiteral + ); + + /// Returns a value that is @a _numBytes bytes long. + /// @param _numBytes: Number of bytes of desired value + /// @param _counter: A counter value + /// @param _isHexLiteral: True if desired value is a hex literal, false otherwise + static std::string croppedString(unsigned _numBytes, unsigned _counter, bool _isHexLiteral); + + unsigned m_counter; +}; + +/// Returns true if protobuf array specification is well-formed, false otherwise +class ValidityVisitor: AbiV2ProtoVisitor +{ +public: + ValidityVisitor(): m_arrayDimensions(0) {} + + bool visit(BoolType const&) override { - return variableLengthValueAsString( - getVarLength(_counter), - _counter, - _isHexLiteral - ); + return true; } - /// Contains the test program - std::ostringstream m_output; - /// Temporary storage for state variable definitions - std::ostringstream m_statebuffer; - /// Contains a subset of the test program. This subset contains - /// checks to be encoded in the test program - std::ostringstream m_checks; - /// Contains typed parameter list to be passed to callee functions - std::ostringstream m_typedParamsExternal; - std::ostringstream m_typedParamsPublic; - /// Predicate that is true if we are in contract scope - bool m_isStateVar; - unsigned m_counter; - unsigned m_varCounter; - /// Monotonically increasing return value for error reporting - unsigned m_returnValue; - /// Flag that indicates if last dynamically encoded parameter - /// passed to a function call is of a type that is going to be - /// right padded by the ABI encoder. - bool m_isLastDynParamRightPadded; - static unsigned constexpr s_maxArrayLength = 4; - static unsigned constexpr s_maxArrayDimensions = 4; - static unsigned constexpr s_maxDynArrayLength = 256; - /// Prefixes for declared and parameterized variable names - static auto constexpr s_varNamePrefix = "x_"; - static auto constexpr s_paramNamePrefix = "c_"; + bool visit(IntegerType const&) override + { + return true; + } + + bool visit(FixedByteType const&) override + { + return true; + } + + bool visit(AddressType const&) override + { + return true; + } + + bool visit(DynamicByteArrayType const&) override + { + return true; + } + + bool visit(ArrayType const& _type) override + { + // Mark array type as invalid in one of the following is true + // - contains more than s_maxArrayDimensions dimensions + // - contains an invalid base type, which happens in the + // following cases + // - array base type is invalid + // - array base type is empty + m_arrayDimensions++; + if (m_arrayDimensions > s_maxArrayDimensions) + return false; + return visit(_type.t()); + } + + bool visit(StructType const& _type) override + { + // A struct is marked invalid only if all of its fields + // are invalid. This is done to prevent an empty struct + // being defined (which is a Solidity error). + for (auto const& t: _type.t()) + if (visit(t)) + return true; + return false; + } + + unsigned m_arrayDimensions; + using AbiV2ProtoVisitor::visit; +}; + +/// Returns true if visited type is dynamically encoded by the abi coder, +/// false otherwise. +class DynParamVisitor: AbiV2ProtoVisitor +{ +public: + DynParamVisitor() = default; + + bool visit(BoolType const&) override + { + return false; + } + + bool visit(IntegerType const&) override + { + return false; + } + + bool visit(FixedByteType const&) override + { + return false; + } + + bool visit(AddressType const&) override + { + return false; + } + + bool visit(DynamicByteArrayType const&) override + { + return true; + } + + bool visit(ArrayType const& _type) override + { + // Return early if array spec is not well-formed + if (!ValidityVisitor().visit(_type)) + return false; + + // Array is dynamically encoded if it at least one of the following is true + // - at least one dimension is dynamically sized + // - base type is dynamically encoded + if (!_type.is_static()) + return true; + else + return visit(_type.t()); + } + + bool visit(StructType const& _type) override + { + // Return early if empty struct + if (!ValidityVisitor().visit(_type)) + return false; + + // Struct is dynamically encoded if at least one of its fields + // is dynamically encoded. + for (auto const& t: _type.t()) + if (visit(t)) + return true; + return false; + } + + using AbiV2ProtoVisitor::visit; }; } } diff --git a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp index c61bf9817..477189798 100644 --- a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp @@ -78,35 +78,21 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) ostringstream os1; ostringstream os2; - try - { - yulFuzzerUtil::interpret( - os1, - stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) - ); - } - catch (yul::test::StepLimitReached const&) - { + yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( + os1, + stack.parserResult()->code, + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) + ); + if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached) return 0; - } - catch (yul::test::InterpreterTerminatedGeneric const&) - { - } stack.optimize(); - try - { - yulFuzzerUtil::interpret( - os2, - stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), - (yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 1.5) - ); - } - catch (yul::test::InterpreterTerminatedGeneric const&) - { - } + termReason = yulFuzzerUtil::interpret( + os2, + stack.parserResult()->code, + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), + (yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4) + ); bool isTraceEq = (os1.str() == os2.str()); yulAssert(isTraceEq, "Interpreted traces for optimized and unoptimized code differ."); diff --git a/test/tools/ossfuzz/yulFuzzerCommon.cpp b/test/tools/ossfuzz/yulFuzzerCommon.cpp index f6c0cff23..2069582b0 100644 --- a/test/tools/ossfuzz/yulFuzzerCommon.cpp +++ b/test/tools/ossfuzz/yulFuzzerCommon.cpp @@ -20,7 +20,7 @@ using namespace std; using namespace yul; using namespace yul::test::yul_fuzzer; -void yulFuzzerUtil::interpret( +yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret( ostream& _os, shared_ptr _ast, Dialect const& _dialect, @@ -31,7 +31,38 @@ void yulFuzzerUtil::interpret( InterpreterState state; state.maxTraceSize = _maxTraceSize; state.maxSteps = _maxSteps; + // Add 64 bytes of pseudo-randomly generated calldata so that + // calldata opcodes perform non trivial work. + state.calldata = { + 0xe9, 0x96, 0x40, 0x7d, 0xa5, 0xda, 0xb0, 0x2d, + 0x97, 0xf5, 0xc3, 0x44, 0xd7, 0x65, 0x0a, 0xd8, + 0x2c, 0x14, 0x3a, 0xf3, 0xe7, 0x40, 0x0f, 0x1e, + 0x67, 0xce, 0x90, 0x44, 0x2e, 0x92, 0xdb, 0x88, + 0xb8, 0x43, 0x9c, 0x41, 0x42, 0x08, 0xf1, 0xd7, + 0x65, 0xe9, 0x7f, 0xeb, 0x7b, 0xb9, 0x56, 0x9f, + 0xc7, 0x60, 0x5f, 0x7c, 0xcd, 0xfb, 0x92, 0xcd, + 0x8e, 0xf3, 0x9b, 0xe4, 0x4f, 0x6c, 0x14, 0xde + }; Interpreter interpreter(state, _dialect); - interpreter(*_ast); + + TerminationReason reason = TerminationReason::None; + try + { + interpreter(*_ast); + } + catch (StepLimitReached const&) + { + reason = TerminationReason::StepLimitReached; + } + catch (TraceLimitReached const&) + { + reason = TerminationReason::TraceLimitReached; + } + catch (ExplicitlyTerminated const&) + { + reason = TerminationReason::ExplicitlyTerminated; + } + state.dumpTraceAndState(_os); + return reason; } diff --git a/test/tools/ossfuzz/yulFuzzerCommon.h b/test/tools/ossfuzz/yulFuzzerCommon.h index fd48bb796..7cc0e156f 100644 --- a/test/tools/ossfuzz/yulFuzzerCommon.h +++ b/test/tools/ossfuzz/yulFuzzerCommon.h @@ -25,7 +25,15 @@ namespace yul_fuzzer { struct yulFuzzerUtil { - static void interpret( + enum class TerminationReason + { + ExplicitlyTerminated, + StepLimitReached, + TraceLimitReached, + None + }; + + static TerminationReason interpret( std::ostream& _os, std::shared_ptr _ast, Dialect const& _dialect, diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 41cd6edab..557073766 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -66,9 +66,6 @@ DEFINE_PROTO_FUZZER(Program const& _input) of.write(yul_source.data(), yul_source.size()); } - if (yul_source.size() > 1200) - return; - YulStringRepository::reset(); // AssemblyStack entry point @@ -95,35 +92,22 @@ DEFINE_PROTO_FUZZER(Program const& _input) ostringstream os1; ostringstream os2; - try - { - yulFuzzerUtil::interpret( - os1, - stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) - ); - } - catch (yul::test::StepLimitReached const&) - { + yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( + os1, + stack.parserResult()->code, + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) + ); + + if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached) return; - } - catch (yul::test::InterpreterTerminatedGeneric const&) - { - } stack.optimize(); - try - { - yulFuzzerUtil::interpret( - os2, - stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), - (yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 1.5) - ); - } - catch (yul::test::InterpreterTerminatedGeneric const&) - { - } + termReason = yulFuzzerUtil::interpret( + os2, + stack.parserResult()->code, + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), + (yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4) + ); bool isTraceEq = (os1.str() == os2.str()); yulAssert(isTraceEq, "Interpreted traces for optimized and unoptimized code differ."); diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp index 8e0eebcaa..390c5bf05 100644 --- a/test/tools/yulInterpreter/Interpreter.cpp +++ b/test/tools/yulInterpreter/Interpreter.cpp @@ -52,7 +52,7 @@ void InterpreterState::dumpTraceAndState(ostream& _out) const words[(offset / 0x20) * 0x20] |= u256(uint32_t(value)) << (256 - 8 - 8 * size_t(offset % 0x20)); for (auto const& [offset, value]: words) if (value != 0) - _out << " " << std::hex << std::setw(4) << offset << ": " << h256(value).hex() << endl; + _out << " " << std::uppercase << std::hex << std::setw(4) << offset << ": " << h256(value).hex() << endl; _out << "Storage dump:" << endl; for (auto const& slot: storage) if (slot.second != h256(0)) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 7d0b1d952..aaaeab511 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -139,7 +140,8 @@ public: cout << " (e)xpr inline/(i)nline/(s)implify/varname c(l)eaner/(u)nusedprune/ss(a) transform/" << endl; cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/for-loop-condition-(I)nto-body/" << endl; cout << " for-loop-condition-(O)ut-of-body/s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/" << endl; - cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/? " << endl; + cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/ " << endl; + cout << " (C)onditional simplifier?" << endl; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -164,6 +166,9 @@ public: case 'c': CommonSubexpressionEliminator::run(context, *m_ast); break; + case 'C': + ConditionalSimplifier::run(context, *m_ast); + break; case 'd': VarDeclInitializer::run(context, *m_ast); break;