diff options
425 files changed, 7166 insertions, 4602 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index cf9c9212..6c5f3e85 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,6 +14,7 @@ defaults: command: | mkdir -p build cd build + [ -n "$COVERAGE" -a "$CIRCLE_BRANCH" != release -a -z "$CIRCLE_TAG" ] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON" cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKE_OPTIONS make -j4 - run_tests: &run_tests @@ -53,7 +54,7 @@ jobs: name: Save Boost build key: *boost-cache-key paths: - - boost_1_57_0 + - boost_1_67_0 - store_artifacts: path: build/libsolc/soljson.js destination: soljson.js @@ -122,7 +123,7 @@ jobs: - image: buildpack-deps:artful environment: TERM: xterm - CMAKE_OPTIONS: -DCOVERAGE=OFF + COVERAGE: "ON" steps: - checkout - run: @@ -139,6 +140,27 @@ jobs: paths: - "*" + build_x86_archlinux: + docker: + - image: archlinux/base + environment: + TERM: xterm + steps: + - run: + name: Install build dependencies + command: | + pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake z3 cvc4 git openssh tar + - checkout + - run: *setup_prerelease_commit_hash + - run: *run_build + - store_artifacts: *solc_artifact + - persist_to_workspace: + root: build + paths: + - solc/solc + - test/soltest + - test/tools/solfuzzer + build_x86_clang7: docker: - image: buildpack-deps:cosmic @@ -146,6 +168,7 @@ jobs: TERM: xterm CC: /usr/bin/clang-7 CXX: /usr/bin/clang++-7 + CMAKE_OPTIONS: -DLLL=ON steps: - checkout - run: @@ -167,6 +190,7 @@ jobs: xcode: "10.0.0" environment: TERM: xterm + CMAKE_OPTIONS: -DLLL=ON steps: - checkout - run: @@ -260,6 +284,27 @@ jobs: path: test_results/ destination: test_results/ + test_x86_archlinux: + docker: + - image: archlinux/base + environment: + TERM: xterm + steps: + - run: + name: Install dependencies + command: | + pacman --noconfirm -Syu --noprogressbar --needed boost z3 cvc4 git openssh tar + - checkout + - attach_workspace: + at: build + - run: mkdir -p test_results + - run: build/test/soltest --logger=JUNIT,test_suite,test_results/result.xml -- --no-ipc --testpath test + - store_test_results: + path: test_results/ + - store_artifacts: + path: test_results/ + destination: test_results/ + test_x86_mac: macos: xcode: "10.0.0" @@ -330,3 +375,8 @@ workflows: requires: - build_x86_mac - docs: *build_on_tags + - build_x86_archlinux: *build_on_tags + - test_x86_archlinux: + <<: *build_on_tags + requires: + - build_x86_archlinux diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c3caf86e..dd4d0b41 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -21,6 +21,7 @@ about: Bug reports about the Solidity Compiler. ## Environment - Compiler version: +- Target EVM version (as per compiler settings): - Framework/IDE (e.g. Truffle or Remix): - EVM execution environment / backend / blockchain client: - Operating system: diff --git a/.travis.yml b/.travis.yml index ef4f8b39..d68c79ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -90,7 +90,7 @@ matrix: before_install: - nvm install 8 - nvm use 8 - - docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit + - docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit env: - SOLC_EMSCRIPTEN=On - SOLC_INSTALL_DEPS_TRAVIS=Off @@ -153,7 +153,7 @@ git: cache: ccache: true directories: - - boost_1_57_0 + - boost_1_67_0 - $HOME/.local install: diff --git a/CMakeLists.txt b/CMakeLists.txt index f2c84d20..b22ade23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,12 +8,13 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.5.0") +set(PROJECT_VERSION "0.5.1") project(solidity VERSION ${PROJECT_VERSION}) +option(LLL "Build LLL" OFF) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) option(LLLC_LINK_STATIC "Link lllc executable statically on supported platforms" OFF) -option(INSTALL_LLLC "Include lllc executable in installation" OFF) +option(INSTALL_LLLC "Include lllc executable in installation" ${LLL}) # Setup cccache. include(EthCcache) @@ -44,14 +45,18 @@ include(EthOptions) configure_project(TESTS) add_subdirectory(libdevcore) +add_subdirectory(liblangutil) add_subdirectory(libevmasm) +add_subdirectory(libyul) add_subdirectory(libsolidity) add_subdirectory(libsolc) if (NOT EMSCRIPTEN) add_subdirectory(solc) - add_subdirectory(liblll) - add_subdirectory(lllc) + if (LLL) + add_subdirectory(liblll) + add_subdirectory(lllc) + endif() endif() if (TESTS AND NOT EMSCRIPTEN) diff --git a/Changelog.md b/Changelog.md index 026e2bac..a53cca7e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,44 @@ +### 0.5.1 (2018-12-03) + +Language Features: + * Allow mapping type for parameters and return variables of public and external library functions. + * Allow public functions to override external functions. + +Compiler Features: + * Code generator: Do not perform redundant double cleanup on unsigned integers when loading from calldata. + * Commandline interface: Experimental ``--optimize`` option for assembly mode (``--strict-assembly`` and ``--yul``). + * SMTChecker: SMTLib2 queries and responses passed via standard JSON compiler interface. + * SMTChecker: Support ``msg``, ``tx`` and ``block`` member variables. + * SMTChecker: Support ``gasleft()`` and ``blockhash()`` functions. + * SMTChecker: Support internal bound function calls. + * Yul: Support Yul objects in ``--assemble``, ``--strict-assembly`` and ``--yul`` commandline options. + +Bugfixes: + * Assembly output: Do not mix in/out jump annotations with arguments. + * Commandline interface: Fix crash when using ``--ast`` on empty runtime code. + * Code Generator: Annotate jump from calldata decoder to function as "jump in". + * Code Generator: Fix internal error related to state variables of function type access via base contract name. + * Optimizer: Fix nondeterminism bug related to the boost version and constants representation. The bug only resulted in less optimal but still correct code because the generated routine is always verified to be correct. + * Type Checker: Properly detect different return types when overriding an external interface function with a public contract function. + * Type Checker: Disallow struct return types for getters of public state variables unless the new ABI encoder is active. + * Type Checker: Fix internal compiler error when a field of a struct used as a parameter in a function type has a non-existent type. + * Type Checker: Disallow functions ``sha3`` and ``suicide`` also without a function call. + * Type Checker: Fix internal compiler error with ``super`` when base contract function is not implemented. + * Type Checker: Fixed internal error when trying to create abstract contract in some cases. + * Type Checker: Fixed internal error related to double declaration of events. + * Type Checker: Disallow inline arrays of mapping type. + * Type Checker: Consider abstract function to be implemented by public state variable. + +Build System: + * CMake: LLL is not built anymore by default. Must configure it with CMake as `-DLLL=ON`. + * Docker: Includes both Scratch and Alpine images. + * Emscripten: Upgrade to Emscripten SDK 1.37.21 and boost 1.67. + +Solc-Js: + * Fix handling of standard-json in the commandline executable. + * Remove support of nodejs 4. + + ### 0.5.0 (2018-11-13) How to update your code: @@ -62,5 +62,6 @@ if you want to help. [@chriseth](https://github.com/chriseth) ## License -Solidity is licensed under [GNU General Public License v3.0](https://github.com/ethereum/solidity/blob/develop/LICENSE.txt) +Solidity is licensed under [GNU General Public License v3.0](LICENSE.txt) +Some third-party code has its [own licensing terms](cmake/templates/license.h.in). diff --git a/cmake/templates/license.h.in b/cmake/templates/license.h.in index 4f22d8f4..31e2e17d 100644 --- a/cmake/templates/license.h.in +++ b/cmake/templates/license.h.in @@ -67,7 +67,7 @@ jsoncpp: license you like. scanner/token: - The libsolidity/parsing/{scanner,token}.{h,cpp} files are dervied from + The liblangutil/{CharStream,Scanner,Token}.{h,cpp} files are dervied from code originating from the V8 project licensed under the following terms: Copyright 2006-2012, the V8 project authors. All rights reserved. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 2631b286..658aab9c 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -612,5 +612,9 @@ "0.5.0": { "bugs": [], "released": "2018-11-13" + }, + "0.5.1": { + "bugs": [], + "released": "2018-12-03" } }
\ No newline at end of file diff --git a/docs/contracts.rst b/docs/contracts.rst index c1c51e56..98419430 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -485,6 +485,110 @@ value types and strings. Functions ********* +.. _function-parameters-return-variables: + +Function Parameters and Return Variables +======================================== + +As in JavaScript, functions may take parameters as input. Unlike in JavaScript +and C, functions may also return an arbitrary number of values as output. + +Function Parameters +------------------- + +Function parameters are declared the same way as variables, and the name of +unused parameters can be omitted. + +For example, if you want your contract to accept one kind of external call +with two integers, you would use something like:: + + pragma solidity >=0.4.16 <0.6.0; + + contract Simple { + uint sum; + function taker(uint _a, uint _b) public { + sum = _a + _b; + } + } + +Function parameters can be used as any other local variable and they can also be assigned to. + +.. note:: + + An :ref:`external function<external-function-calls>` cannot accept a + multi-dimensional array as an input + parameter. This functionality is possible if you enable the new + experimental ``ABIEncoderV2`` feature by adding ``pragma experimental ABIEncoderV2;`` to your source file. + + An :ref:`internal function<external-function-calls>` can accept a + multi-dimensional array without enabling the feature. + +.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct + +Return Variables +---------------- + +Function return variables are declared with the same syntax after the +``returns`` keyword. + +For example, suppose you want to return two results: the sum and the product of +two integers passed as function parameters, then you use something like:: + + pragma solidity >=0.4.16 <0.6.0; + + contract Simple { + function arithmetic(uint _a, uint _b) + public + pure + returns (uint o_sum, uint o_product) + { + o_sum = _a + _b; + o_product = _a * _b; + } + } + +The names of return variables can be omitted. +Return variables can be used as any other local variable and they +are initialized with their :ref:`default value <default-value>` and have that value unless explicitly set. + +You can either explicitly assign to return variables and +then leave the function using ``return;``, +or you can provide return values +(either a single or :ref:`multiple ones<multi-return>`) directly with the ``return`` +statement:: + + pragma solidity >=0.4.16 <0.6.0; + + contract Simple { + function arithmetic(uint _a, uint _b) + public + pure + returns (uint o_sum, uint o_product) + { + return (_a + _b, _a * _b); + } + } + +This form is equivalent to first assigning values to the +return variables and then using ``return;`` to leave the function. + +.. note:: + You cannot return some types from non-internal functions, notably + multi-dimensional dynamic arrays and structs. If you enable the + new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental + ABIEncoderV2;`` to your source file then more types are available, but + ``mapping`` types are still limited to inside a single contract and you + cannot transfer them. + +.. _multi-return: + +Returning Multiple Values +------------------------- + +When a function has multiple return types, the statement ``return (v0, v1, ..., vn) can be used to return multiple values. +vn)`` can return multiple values. The number of components must be +the same as the number of return types. + .. index:: ! view function, function;view .. _view-functions: @@ -569,6 +673,20 @@ In addition to the list of state modifying statements explained above, the follo } } +Pure functions are able to use the `revert()` and `require()` functions to revert +potential state changes when an :ref:`error occurs <assert-and-require>`. + +Reverting a state change is not considered a "state modification", as only changes to the +state made previously in code that did not have the ``view`` or ``pure`` restriction +are reverted and that code has the option to catch the ``revert`` and not pass it on. + +This behaviour is also in line with the ``STATICCALL`` opcode. + +.. warning:: + It is not possible to prevent functions from reading the state at the level + of the EVM, it is only possible to prevent them from writing to the state + (i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not). + .. note:: Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode for ``pure`` functions. @@ -577,13 +695,8 @@ In addition to the list of state modifying statements explained above, the follo By using ``STATICCALL`` for ``pure`` functions, modifications to the state are prevented on the level of the EVM. -.. warning:: - It is not possible to prevent functions from reading the state at the level - of the EVM, it is only possible to prevent them from writing to the state - (i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not). - -.. warning:: - Before version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state. +.. note:: + Prior to version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state. It is a compile-time type check, which can be circumvented doing invalid explicit conversions between contract types, because the compiler can verify that the type of the contract does not do state-changing operations, but it cannot check that the contract that will be called @@ -932,14 +1045,15 @@ Additional Resources for Understanding Events Inheritance *********** -Solidity supports multiple inheritance by copying code including polymorphism. +Solidity supports multiple inheritance including polymorphism. All function calls are virtual, which means that the most derived function -is called, except when the contract name is explicitly given. +is called, except when the contract name is explicitly given or the +``super`` keyword is used. When a contract inherits from other contracts, only a single contract is created on the blockchain, and the code from all the base contracts -is copied into the created contract. +is compiled into the created contract. The general inheritance system is very similar to `Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_, diff --git a/docs/contributing.rst b/docs/contributing.rst index 11f95206..43b2fd38 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -70,16 +70,15 @@ Thank you for your help! Running the compiler tests ========================== -There is a script at ``scripts/tests.sh`` which executes most of the tests and +The ``./scripts/tests.sh`` script executes most Solidity tests and runs ``aleth`` automatically if it is in the path, but does not download it, -so it most likely will not work right away. Please read on for the details. +so you need to install it first. Please read on for the details. -Solidity includes different types of tests. Most of them are bundled in the application -called ``soltest``. Some of them require the ``aleth`` client in testing mode, -some others require ``libz3`` to be installed. +Solidity includes different types of tests, most of them bundled into the ``soltest`` +application. Some of them require the ``aleth`` client in testing mode, others require ``libz3``. -To run a basic set of tests that neither require ``aleth`` nor ``libz3``, run -``./scripts/soltest.sh --no-ipc --no-smt``. This script will run ``build/test/soltest`` +To run a basic set of tests that require neither ``aleth`` nor ``libz3``, run +``./scripts/soltest.sh --no-ipc --no-smt``. This script runs ``./build/test/soltest`` internally. .. note :: @@ -90,23 +89,41 @@ internally. The option ``--no-smt`` disables the tests that require ``libz3`` and ``--no-ipc`` disables those that require ``aleth``. -If you want to run the ipc tests (those test the semantics of the generated code), +If you want to run the ipc tests (that test the semantics of the generated code), you need to install `aleth <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/aleth_2018-06-20_artful>`_ and run it in testing mode: ``aleth --test -d /tmp/testeth`` (make sure to rename it). -Then you run the actual tests: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``. +To run the actual tests, use: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``. -To run a subset of tests, filters can be used: +To run a subset of tests, you can use filters: ``./scripts/soltest.sh -t TestSuite/TestName --ipcpath /tmp/testeth/geth.ipc``, where ``TestName`` can be a wildcard ``*``. -The script ``scripts/tests.sh`` also runs commandline tests and compilation tests +For example, here's an example test you might run; +``./scripts/soltest.sh -t "yulOptimizerTests/disambiguator/*" --no-ipc --no-smt``. +This will test all the tests for the disambiguator. + +To get a list of all tests, use +``./build/test/soltest --list_content=HRF -- --ipcpath /tmp/irrelevant``. + +If you want to debug using GDB, make sure you build differently than the "usual". +For example, you could run the following command in your ``build`` folder: +:: + + cmake -DCMAKE_BUILD_TYPE=Debug .. + make + +This will create symbols such that when you debug a test using the ``--debug`` flag, you will have acecess to functions and varialbes in which you can break or print with. + + +The script ``./scripts/tests.sh`` also runs commandline tests and compilation tests in addition to those found in ``soltest``. -The CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. +The CI runs additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. .. note :: - Some versions of ``aleth`` cannot be used for testing. We suggest using the same version that is used by the Solidity continuous integration tests. + You can not use some versions of ``aleth`` for testing. We suggest using + the same version that the Solidity continuous integration tests use. Currently the CI uses ``d661ac4fec0aeffbedcdc195f67f5ded0c798278`` of ``aleth``. Writing and running syntax tests @@ -114,11 +131,11 @@ Writing and running syntax tests Syntax tests check that the compiler generates the correct error messages for invalid code and properly accepts valid code. -They are stored in individual files inside ``tests/libsolidity/syntaxTests``. +They are stored in individual files inside the ``tests/libsolidity/syntaxTests`` folder. These files must contain annotations, stating the expected result(s) of the respective test. -The test suite will compile and check them against the given expectations. +The test suite compiles and checks them against the given expectations. -Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` +For example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` :: @@ -129,14 +146,14 @@ Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` // ---- // DeclarationError: (36-52): Identifier already declared. -A syntax test must contain at least the contract under test itself, followed by the separator ``// ----``. The following comments are used to describe the +A syntax test must contain at least the contract under test itself, followed by the separator ``// ----``. The comments that follow the separator are used to describe the expected compiler errors or warnings. The number range denotes the location in the source where the error occurred. -In case the contract should compile without any errors or warning, the section after the separator has to be empty -and the separator can be left out completely. +If you want the contract to compile without any errors or warning you can leave +out the separator and the comments that follow it. -In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifier was already declared. +In the above example, the state variable ``variable`` was declared twice, which is not allowed. This results in a ``DeclarationError`` stating that the identifier was already declared. -The tool that is being used for those tests is called ``isoltest`` and can be found under ``./test/tools/``. It is an interactive tool which allows +The ``isoltest`` tool is used for these tests and you can find it under ``./build/test/tools/``. It is an interactive tool which allows editing of failing contracts using your preferred text editor. Let's try to break this test by removing the second declaration of ``variable``: :: @@ -147,7 +164,7 @@ editing of failing contracts using your preferred text editor. Let's try to brea // ---- // DeclarationError: (36-52): Identifier already declared. -Running ``./test/isoltest`` again will result in a test failure: +Running ``./build/test/isoltest`` again results in a test failure: :: @@ -163,15 +180,19 @@ Running ``./test/isoltest`` again will result in a test failure: Success -``isoltest`` prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit. +``isoltest`` prints the expected result next to the obtained result, and also +provides a way to edit, update or skip the current contract file, or quit the application. + It offers several options for failing tests: -- edit: ``isoltest`` tries to open the contract in an editor so you can adjust it. It either uses the editor given on the command line (as ``isoltest --editor /path/to/editor``), in the environment variable ``EDITOR`` or just ``/usr/bin/editor`` (in this order). -- update: Updates the contract under test. This either removes the annotation which contains the exception not met or adds missing expectations. The test will then be run again. -- skip: Skips the execution of this particular test. -- quit: Quits ``isoltest``. +- ``edit``: ``isoltest`` tries to open the contract in an editor so you can adjust it. It either uses the editor given on the command line (as ``isoltest --editor /path/to/editor``), in the environment variable ``EDITOR`` or just ``/usr/bin/editor`` (in that order). +- ``update``: Updates the expectations for contract under test. This updates the annotations by removing unmet expectations and adding missing expectations. The test is then run again. +- ``skip``: Skips the execution of this particular test. +- ``quit``: Quits ``isoltest``. + +All of these options apply to the current contract, expect ``quit`` which stops the entire testing process. -Automatically updating the test above will change it to +Automatically updating the test above changes it to :: @@ -180,7 +201,7 @@ Automatically updating the test above will change it to } // ---- -and re-run the test. It will now pass again: +and re-run the test. It now passes again: :: @@ -190,7 +211,7 @@ and re-run the test. It will now pass again: .. note:: - Please choose a name for the contract file that explains what it tests, e.g. ``double_variable_declaration.sol``. + Choose a name for the contract file that explains what it tests, e.g. ``double_variable_declaration.sol``. Do not put more than one contract into a single file, unless you are testing inheritance or cross-contract calls. Each file should test one aspect of your new feature. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 5e3b722b..f8016806 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -2,74 +2,8 @@ Expressions and Control Structures ################################## -.. index:: ! parameter, parameter;input, parameter;output, parameter;multiple +.. index:: ! parameter, parameter;input, parameter;output, function parameter, parameter;function, return variable, variable;return, return -Input Parameters and Output Parameters -====================================== - -As in Javascript, functions may take parameters as input; -unlike in Javascript and C, they may also return arbitrary number of -parameters as output. - -Input Parameters ----------------- - -The input parameters are declared the same way as variables are. -The name of unused parameters can be omitted. -For example, suppose we want our contract to -accept one kind of external calls with two integers, we would write -something like:: - - pragma solidity >=0.4.16 <0.6.0; - - contract Simple { - uint sum; - function taker(uint _a, uint _b) public { - sum = _a + _b; - } - } - -Input parameters can be used just as any other local variable -can be used, they can also be assigned to. - -.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct - -Output Parameters ------------------ - -The output parameters can be declared with the same syntax after the -``returns`` keyword. For example, suppose we wished to return two results: -the sum and the product of the two given integers, then we would -write:: - - pragma solidity >=0.4.16 <0.6.0; - - contract Simple { - function arithmetic(uint _a, uint _b) - public - pure - returns (uint o_sum, uint o_product) - { - o_sum = _a + _b; - o_product = _a * _b; - } - } - -The names of output parameters can be omitted. -The return values can be specified using ``return`` statements, -which are also capable of :ref:`returning multiple values<multi-return>`. -Return parameters can be used as any other local variable and they -are zero-initialized; if they are not explicitly -set, they stay zero. - - -.. note:: - You cannot return some types from non-internal functions, notably - multi-dimensional dynamic arrays and structs. If you enable the - new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental - ABIEncoderV2;`` to your source file then more types are available, but - ``mapping`` types are still limited to inside a single contract and you - cannot transfer them. .. index:: if, else, while, do/while, for, break, continue, return, switch, goto @@ -88,15 +22,6 @@ Note that there is no type conversion from non-boolean to boolean types as there is in C and JavaScript, so ``if (1) { ... }`` is *not* valid Solidity. -.. _multi-return: - -Returning Multiple Values -------------------------- - -When a function has multiple output parameters, ``return (v0, v1, ..., -vn)`` can return multiple values. The number of components must be -the same as the number of output parameters. - .. index:: ! function;call, function;internal, function;external .. _function-calls: @@ -104,6 +29,8 @@ the same as the number of output parameters. Function Calls ============== +.. _internal-function-calls: + Internal Function Calls ----------------------- @@ -125,6 +52,8 @@ contract can be called internally. You should still avoid excessive recursion, as every internal function call uses up at least one stack slot and there are at most 1024 slots available. +.. _external-function-calls: + External Function Calls ----------------------- diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 8b655b0d..f3c5b1f7 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -38,24 +38,6 @@ has it (which includes `Remix <https://remix.ethereum.org/>`_), then ``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my examples. -Is it possible to in-line initialize an array like so: ``string[] myarray = ["a", "b"];`` -========================================================================================= - -Yes. However it should be noted that this currently only works with statically sized memory arrays. You can even create an inline memory -array in the return statement. - -Example:: - - pragma solidity >=0.4.16 <0.6.0; - - contract C { - function f() public pure returns (uint8[5] memory) { - string[4] memory adaArr = ["This", "is", "an", "array"]; - adaArr[0] = "That"; - return [1, 2, 3, 4, 5]; - } - } - If I return an ``enum``, I only get integer values in web3.js. How to get the named values? =========================================================================================== @@ -143,44 +125,6 @@ arguments for you. See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_ and `pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_. -When returning a value of say ``uint`` type, is it possible to return an ``undefined`` or "null"-like value? -============================================================================================================ - -This is not possible, because all types use up the full value range. - -You have the option to ``throw`` on error, which will also revert the whole -transaction, which might be a good idea if you ran into an unexpected -situation. - -If you do not want to throw, you can return a pair:: - - pragma solidity >0.4.23 <0.6.0; - - contract C { - uint[] counters; - - function getCounter(uint index) - public - view - returns (uint counter, bool error) { - if (index >= counters.length) - return (0, true); - else - return (counters[index], false); - } - - function checkCounter(uint index) public view { - (uint counter, bool error) = getCounter(index); - if (error) { - // Handle the error - } else { - // Do something with counter. - require(counter > 7, "Invalid counter value"); - } - } - } - - Are comments included with deployed contracts and do they increase deployment gas? ================================================================================== @@ -232,24 +176,6 @@ Note2: Optimizing storage access can pull the gas costs down considerably, becau currently do not work across loops and also have a problem with bounds checking. You might get much better results in the future, though. -What happens to a ``struct``'s mapping when copying over a ``struct``? -====================================================================== - -This is a very interesting question. Suppose that we have a contract field set up like such:: - - struct User { - mapping(string => string) comments; - } - - function somefunction public { - User user1; - user1.comments["Hello"] = "World"; - User user2 = user1; - } - -In this case, the mapping of the struct being copied over into ``user2`` is ignored as there is no "list of mapped keys". -Therefore it is not possible to find out which values should be copied over. - How do I initialize a contract with only a specific amount of wei? ================================================================== @@ -273,28 +199,6 @@ In this example:: } } -What is the relationship between ``bytes32`` and ``string``? Why is it that ``bytes32 somevar = "stringliteral";`` works and what does the saved 32-byte hex value mean? -======================================================================================================================================================================== - -The type ``bytes32`` can hold 32 (raw) bytes. In the assignment ``bytes32 samevar = "stringliteral";``, -the string literal is interpreted in its raw byte form and if you inspect ``somevar`` and -see a 32-byte hex value, this is just ``"stringliteral"`` in hex. - -The type ``bytes`` is similar, only that it can change its length. - -Finally, ``string`` is basically identical to ``bytes`` only that it is assumed -to hold the UTF-8 encoding of a real string. Since ``string`` stores the -data in UTF-8 encoding it is quite expensive to compute the number of -characters in the string (the encoding of some characters takes more -than a single byte). Because of that, ``string s; s.length`` is not yet -supported and not even index access ``s[2]``. But if you want to access -the low-level byte encoding of the string, you can use -``bytes(s).length`` and ``bytes(s)[2]`` which will result in the number -of bytes in the UTF-8 encoding of the string (not the number of -characters) and the second byte (not character) of the UTF-8 encoded -string, respectively. - - Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) to another contract? ===================================================================================================== @@ -325,55 +229,6 @@ to create an independent copy of the storage value in memory. On the other hand, ``h(x)`` successfully modifies ``x`` because only a reference and not a copy is passed. -Sometimes, when I try to change the length of an array with ex: ``arrayname.length = 7;`` I get a compiler error ``Value must be an lvalue``. Why? -================================================================================================================================================== - -You can resize a dynamic array in storage (i.e. an array declared at the -contract level) with ``arrayname.length = <some new length>;``. If you get the -"lvalue" error, you are probably doing one of two things wrong. - -1. You might be trying to resize an array in "memory", or - -2. You might be trying to resize a non-dynamic array. - -:: - - pragma solidity >=0.4.18 <0.6.0; - - // This will not compile - contract C { - int8[] dynamicStorageArray; - int8[5] fixedStorageArray; - - function f() public { - int8[] memory memArr; // Case 1 - memArr.length++; // illegal - - int8[5] storage storageArr = fixedStorageArray; // Case 2 - storageArr.length++; // illegal - - int8[] storage storageArr2 = dynamicStorageArray; - storageArr2.length++; // legal - - - } - } - -**Important note:** In Solidity, array dimensions are declared backwards from the way you -might be used to declaring them in C or Java, but they are access as in -C or Java. - -For example, ``int8[][5] somearray;`` are 5 dynamic ``int8`` arrays. - -The reason for this is that ``T[5]`` is always an array of 5 ``T``'s, -no matter whether ``T`` itself is an array or not (this is not the -case in C or Java). - -Is it possible to return an array of strings (``string[]``) from a Solidity function? -===================================================================================== - -Only when ``pragma experimental "ABIEncoderV2";`` is used. - What does the following strange check do in the Custom Token contract? ====================================================================== diff --git a/docs/grammar.txt b/docs/grammar.txt index b9c8ddb9..3d2c9bc3 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -72,7 +72,7 @@ WhileStatement = 'while' '(' Expression ')' Statement PlaceholderStatement = '_' SimpleStatement = VariableDefinition | ExpressionStatement ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement -InlineAssemblyStatement = 'assembly' StringLiteral? InlineAssemblyBlock +InlineAssemblyStatement = 'assembly' StringLiteral? AssemblyBlock DoWhileStatement = 'do' Statement 'while' '(' Expression ')' Continue = 'continue' Break = 'break' @@ -152,11 +152,30 @@ Fixed = 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) Ufixed = 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) -InlineAssemblyBlock = '{' AssemblyItem* '}' -AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyVariableDeclaration | AssemblyAssignment | AssemblyLabel | NumberLiteral | StringLiteral | HexLiteral -AssemblyExpression = Identifier | FunctionalAssemblyExpression | NumberLiteral | StringLiteral | HexLiteral -AssemblyVariableDeclaration = 'let' Identifier ':=' AssemblyExpression -AssemblyAssignment = ( Identifier ':=' AssemblyExpression ) | ( '=:' Identifier ) -AssemblyLabel = Identifier ':' -FunctionalAssemblyExpression = Identifier '(' AssemblyItem? ( ',' AssemblyItem )* ')' +AssemblyBlock = '{' AssemblyStatement* '}' + +AssemblyStatement = AssemblyBlock + | AssemblyFunctionDefinition + | AssemblyVariableDeclaration + | AssemblyAssignment + | AssemblyIf + | AssemblyExpression + | AssemblySwitch + | AssemblyForLoop + | AssemblyBreakContinue +AssemblyFunctionDefinition = + 'function' Identifier '(' AssemblyIdentifierList? ')' + ( '->' AssemblyIdentifierList )? AssemblyBlock +AssemblyVariableDeclaration = 'let' AssemblyIdentifierList ( ':=' AssemblyExpression )? +AssemblyAssignment = AssemblyIdentifierList ':=' AssemblyExpression +AssemblyExpression = AssemblyFunctionCall | Identifier | Literal +AssemblyIf = 'if' AssemblyExpression AssemblyBlock +AssemblySwitch = 'switch' AssemblyExpression ( Case+ AssemblyDefault? | AssemblyDefault ) +AssemblyCase = 'case' Literal AssemblyBlock +AssemblyDefault = 'default' AssemblyBlock +AssemblyForLoop = 'for' AssemblyBlock AssemblyExpression AssemblyBlock AssemblyBlock +AssemblyBreakContinue = 'break' | 'continue' +AssemblyFunctionCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')' + +AssemblyIdentifierList = Identifier ( ',' Identifier )* diff --git a/docs/index.rst b/docs/index.rst index 17abf4b2..ed931163 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,26 +19,43 @@ user-defined types among other features. With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions, and multi-signature wallets. -.. note:: - The best way to try out Solidity right now is using - `Remix <https://remix.ethereum.org/>`_ - (it can take a while to load, please be patient). Remix is a web browser - based IDE that allows you to write Solidity smart contracts, then deploy - and run the smart contracts. +Language Documentation +---------------------- + +If you are new to the concept of smart contracts we recommend you start with +:ref:`an example smart contract <simple-smart-contract>` written +in Solidity. When you are ready for more detail, we recommend you read the +:doc:`"Solidity by Example" <solidity-by-example>` and :doc:`"Solidity in Depth" <solidity-in-depth>` sections to learn the core concepts of the language. + +For further reading, try :ref:`the basics of blockchains <blockchain-basics>` +and details of the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`. + +.. hint:: + You can always try out code examples in your browser with the + `Remix IDE <https://remix.ethereum.org>`_. Remix is a web browser based IDE + that allows you to write Solidity smart contracts, then deploy and run the + smart contracts. It can take a while to load, so please be patient. .. warning:: - Since software is written by humans, it can have bugs. Thus, also - smart contracts should be created following well-known best-practices in - software development. This includes code review, testing, audits and correctness proofs. - Also note that users are sometimes more confident in code than its authors. - Finally, blockchains have their own things to watch out for, so please take - a look at the section :ref:`security_considerations`. + As humans write software, it can have bugs. You should follow established + software development best-practices when writing your smart contracts, this + includes code review, testing, audits, and correctness proofs. Smart contract + users are sometimes more confident with code than their authors, and + blockchains and smart contracts have their own unique issues to + watch out for, so before working on production code, make sure you read the + :ref:`security_considerations` section. + +If you have any questions, you can try searching for answers or asking on the +`Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_, or our `gitter channel <https://gitter.im/ethereum/solidity/>`_. + +Ideas for improving Solidity or this documentation are always welcome, read our :doc:`contributors guide <contributing>` for more details. Translations ------------ -This documentation is translated into several languages by community volunteers -with varying degrees of completeness and up-to-dateness. The English version stands as a reference. +Community volunteers help translate this documentation into several languages. +They have varying degrees of completeness and up-to-dateness. The English +version stands as a reference. * `Simplified Chinese <http://solidity-cn.readthedocs.io>`_ (in progress) * `Spanish <https://solidity-es.readthedocs.io>`_ @@ -46,25 +63,6 @@ with varying degrees of completeness and up-to-dateness. The English version sta * `Korean <http://solidity-kr.readthedocs.io>`_ (in progress) * `French <http://solidity-fr.readthedocs.io>`_ (in progress) -Language Documentation ----------------------- - -On the next pages, we will first see a :ref:`simple smart contract <simple-smart-contract>` written -in Solidity followed by the basics about :ref:`blockchains <blockchain-basics>` -and the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`. - -The next section will explain several *features* of Solidity by giving -useful :ref:`example contracts <voting>`. -Remember that you can always try out the contracts -`in your browser <https://remix.ethereum.org>`_! - -The fourth and most extensive section will cover all aspects of Solidity in depth. - -If you still have questions, you can try searching or asking on the -`Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_ -site, or come to our `gitter channel <https://gitter.im/ethereum/solidity/>`_. -Ideas for improving Solidity or this documentation are always welcome! - Contents ======== diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index f8de0e8d..2797d8b0 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -115,7 +115,7 @@ Arch Linux also has packages, albeit limited to the latest development version: pacman -S solidity -We distribute the Solidity compiler through Homebrow +We distribute the Solidity compiler through Homebrew as a build-from-source version. Pre-built bottles are currently not supported. diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 9245300b..34ef012e 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -400,7 +400,7 @@ within a word). At the time of expansion, the cost in gas must be paid. Memory i costly the larger it grows (it scales quadratically). The EVM is not a register machine but a stack machine, so all -computations are performed on an data area called the **stack**. It has a maximum size of +computations are performed on a data area called the **stack**. It has a maximum size of 1024 elements and contains words of 256 bits. Access to the stack is limited to the top end in the following way: It is possible to copy one of diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 9a8bdc3e..ad84b200 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -276,7 +276,7 @@ functions, annotate conditions for formal verification, and provide a function. In the following example we document the title of the contract, the explanation -for the two input parameters and two returned values. +for the two function parameters and two return variables. :: diff --git a/docs/lll.rst b/docs/lll.rst index d9409bf8..16be829e 100644 --- a/docs/lll.rst +++ b/docs/lll.rst @@ -9,6 +9,13 @@ LLL is a low-level language for the EVM with an s-expressions syntax. The Solidity repository contains an LLL compiler, which shares the assembler subsystem with Solidity. However, apart from maintaining that it still compiles, no other improvements are made to it. +It is not built unless specifically requested: + +.. code-block:: bash + + $ cmake -DLLL=ON .. + $ cmake --build . + .. warning:: The LLL codebase is deprecated and will be removed from the Solidity repository in the future. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 21978ded..017d5b81 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -8,13 +8,17 @@ Miscellaneous Layout of State Variables in Storage ************************************ -Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules: +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. - Elementary types use only that many bytes that are necessary to store them. - If an elementary type does not fit the remaining part of a storage slot, it is moved to the next storage slot. - Structs and array data always start a new slot and occupy whole slots (but items inside a struct or array are packed tightly according to these rules). +For contracts that use inheritance, the ordering of state variables is determined by the +C3-linearized order of contracts starting with the most base-ward contract. If allowed +by the above rules, state variables from different contracts do share the same storage slot. + .. warning:: When using elements that are smaller than 32 bytes, your contract's gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller diff --git a/docs/resources.rst b/docs/resources.rst index e9e65692..4dd617fa 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -21,9 +21,8 @@ Solidity Integrations * Generic: - * `EthFiddle <https://ethfiddle.com/>`_ - Solidity IDE in the Browser. Write and share your solidity code. Uses server-side components. + Solidity IDE in the Browser. Write and share your Solidity code. Uses server-side components. * `Remix <https://remix.ethereum.org/>`_ Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components. @@ -34,6 +33,9 @@ Solidity Integrations * `Solhint <https://github.com/protofire/solhint>`_ Solidity linter that provides security, style guide and best practice rules for smart contract validation. + * `Superblocks Lab <https://lab.superblocks.com/>`_ + Browser-based IDE. Built-in browser-based VM and Metamask integration (one click deployment to Testnet/Mainnet). + * Atom: * `Etheratom <https://github.com/0mkara/etheratom>`_ diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 752e8641..d07452c5 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -647,10 +647,10 @@ Safe Remote Purchase Micropayment Channel ******************** -In this section we will learn how to build a simple implementation -of a payment channel. It use cryptographics signatures to make +In this section we will learn how to build an example implementation +of a payment channel. It uses cryptographic signatures to make repeated transfers of Ether between the same parties secure, instantaneous, and -without transaction fees. To do it we need to understand how to +without transaction fees. For the example, we need to understand how to sign and verify signatures, and setup the payment channel. Creating and verifying signatures @@ -658,88 +658,67 @@ Creating and verifying signatures Imagine Alice wants to send a quantity of Ether to Bob, i.e. Alice is the sender and the Bob is the recipient. + Alice only needs to send cryptographically signed messages off-chain -(e.g. via email) to Bob and it will be very similar to writing checks. - -Signatures are used to authorize transactions, -and they are a general tool that is available to -smart contracts. Alice will build a simple -smart contract that lets her transmit Ether, but -in a unusual way, instead of calling a function herself -to initiate a payment, she will let Bob -do that, and therefore pay the transaction fee. +(e.g. via email) to Bob and it is similar to writing checks. + +Alice and Bob use signatures to authorise transactions, which is possible with smart contracts on Ethereum. +Alice will build a simple smart contract that lets her transmit Ether, but instead of calling a function herself +to initiate a payment, she will let Bob do that, and therefore pay the transaction fee. + The contract will work as follows: 1. Alice deploys the ``ReceiverPays`` contract, attaching enough Ether to cover the payments that will be made. - 2. Alice authorizes a payment by signing a message with their private key. + 2. Alice authorises a payment by signing a message with their private key. 3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret - (you will understand it later), and the mechanism for sending it does not matter. + (explained later), and the mechanism for sending it does not matter. 4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the authenticity of the message and then releases the funds. Creating the signature ---------------------- -Alice does not need to interact with Ethereum network to -sign the transaction, the process is completely offline. -In this tutorial, we will sign messages in the browser -using ``web3.js`` and ``MetaMask``. -In particular, we will use the standard way described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_, +Alice does not need to interact with the Ethereum network to sign the transaction, the process is completely offline. +In this tutorial, we will sign messages in the browser using `web3.js <https://github.com/ethereum/web3.js>`_ and `MetaMask <https://metamask.io>`_, using the method described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_, as it provides a number of other security benefits. :: + /// Hashing first makes things easier + var hash = web3.utils.sha3("message to sign"); + web3.eth.personal.sign(hash, web3.eth.defaultAccount, function () { console.log("Signed"); }); - /// Hashing first makes a few things easier - var hash = web3.sha3("message to sign"); - web3.personal.sign(hash, web3.eth.defaultAccount, function () {...}); - - -Note that the ``web3.personal.sign`` prepends the length of the message to the signed data. -Since we hash first, the message will always be exactly 32 bytes long, -and thus this length prefix is always the same, making everything easier. +.. note:: + The ``web3.eth.personal.sign`` prepends the length of the message to the signed data. Since we hash first, the message will always be exactly 32 bytes long, and thus this length prefix is always the same. What to Sign ------------ -For a contract that fulfills payments, the signed message must include: +For a contract that fulfils payments, the signed message must include: - 1. The recipient's address - 2. The amount to be transferred - 3. Protection against replay attacks + 1. The recipient's address. + 2. The amount to be transferred. + 3. Protection against replay attacks. A replay attack is when a signed message is reused to claim authorization for a second action. -To avoid replay attacks we will use the same as in Ethereum transactions +To avoid replay attacks we use the same as in Ethereum transactions themselves, a so-called nonce, which is the number of transactions sent by an account. -The smart contract will check if a nonce is used multiple times. +The smart contract checks if a nonce is used multiple times. -There is another type of replay attacks, it occurs when the -owner deploys a ``ReceiverPays`` smart contract, performs some payments, -and then destroy the contract. Later, she decides to deploy the -``RecipientPays`` smart contract again, but the new contract does not -know the nonces used in the previous deployment, so the attacker -can use the old messages again. +Another type of replay attack can occur when the owner deploys a ``ReceiverPays`` smart contract, makes some payments, and then destroys the contract. Later, they decide to deploy the ``RecipientPays`` smart contract again, but the new contract does not know the nonces used in the previous deployment, so the attacker can use the old messages again. -Alice can protect against it including -the contract's address in the message, and only -messages containing contract's address itself will be accepted. -This functionality can be found in the first two lines of the ``claimPayment()`` function in the full contract -at the end of this chapter. +Alice can protect against this attack by including the contract's address in the message, and only messages containing the contract's address itself will be accepted. You can find an example of this in the first two lines of the ``claimPayment()`` function of the full contract at the end of this section. Packing arguments ----------------- -Now that we have identified what information to include in the -signed message, we are ready to put the message together, hash it, -and sign it. For simplicity, we just concatenate the data. -The -`ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_ library provides -a function called ``soliditySHA3`` that mimics the behavior -of Solidity's ``keccak256`` function applied to arguments encoded -using ``abi.encodePacked``. -Putting it all together, here is a JavaScript function that -creates the proper signature for the ``ReceiverPays`` example: +Now that we have identified what information to include in the signed message, +we are ready to put the message together, hash it, and sign it. For simplicity, +we concatenate the data. The `ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_ +library provides a function called ``soliditySHA3`` that mimics the behaviour of +Solidity's ``keccak256`` function applied to arguments encoded using ``abi.encodePacked``. +Here is a JavaScript function that creates the proper signature for the ``ReceiverPays`` example: :: @@ -748,46 +727,30 @@ creates the proper signature for the ``ReceiverPays`` example: // nonce can be any unique number to prevent replay attacks // contractAddress is used to prevent cross-contract replay attacks function signPayment(recipient, amount, nonce, contractAddress, callback) { - var hash = "0x" + ethereumjs.ABI.soliditySHA3( + var hash = "0x" + abi.soliditySHA3( ["address", "uint256", "uint256", "address"], [recipient, amount, nonce, contractAddress] ).toString("hex"); - web3.personal.sign(hash, web3.eth.defaultAccount, callback); + web3.eth.personal.sign(hash, web3.eth.defaultAccount, callback); } Recovering the Message Signer in Solidity ----------------------------------------- -In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. -Signatures in Ethereum include a third parameter called ``v``, that can be used -to recover which account's private key was used to sign in the message, -the transaction's sender. Solidity provides a built-in function -`ecrecover <mathematical-and-cryptographic-functions>`_ -that accepts a message along with the ``r``, ``s`` and ``v`` parameters and -returns the address that was used to sign the message. +In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. Signatures in Ethereum include a third parameter called ``v``, that you can use to verify which account's private key was used to sign the message, and the transaction's sender. Solidity provides a built-in function `ecrecover <mathematical-and-cryptographic-functions>`_ that accepts a message along with the ``r``, ``s`` and ``v`` parameters and returns the address that was used to sign the message. Extracting the Signature Parameters ----------------------------------- -Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, -so the first step is splitting those parameters back out. It can be done on the client, -but doing it inside the smart contract means only one signature parameter -needs to be sent rather than three. -Splitting apart a byte array into component parts is a little messy. -We will use `inline assembly <assembly>`_ to do the job -in the ``splitSignature`` function (the third function in the full contract -at the end of this chapter). +Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a messy, so we use `inline assembly <assembly>`_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section). Computing the Message Hash -------------------------- -The smart contract needs to know exactly what parameters were signed, -and so it must recreate the message from the parameters and use that -for signature verification. The functions ``prefixed`` and -``recoverSigner`` do this and their use can be found in the -``claimPayment`` function. - +The smart contract needs to know exactly what parameters were signed, and so it +must recreate the message from the parameters and use that for signature verification. +The functions ``prefixed`` and ``recoverSigner`` do this in the ``claimPayment`` function. The full contract ----------------- @@ -861,41 +824,26 @@ The full contract Writing a Simple Payment Channel ================================ -Alice will now build a simple but complete implementation of a payment channel. -Payment channels use cryptographic signatures to make repeated transfers -of Ether securely, instantaneously, and without transaction fees. +Alice now builds a simple but complete implementation of a payment channel. Payment channels use cryptographic signatures to make repeated transfers of Ether securely, instantaneously, and without transaction fees. What is a Payment Channel? -------------------------- -Payment channels allow participants to make repeated transfers of Ether without -using transactions. This means that the delays and fees associated with transactions -can be avoided. We are going to explore a simple unidirectional payment channel between -two parties (Alice and Bob). Using it involves three steps: +Payment channels allow participants to make repeated transfers of Ether without using transactions. This means that you can avoid the delays and fees associated with transactions. We are going to explore a simple unidirectional payment channel between two parties (Alice and Bob). It involves three steps: 1. Alice funds a smart contract with Ether. This "opens" the payment channel. 2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment. 3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender. -Note that only steps 1 and 3 require Ethereum transactions, step 2 means that -the sender transmits a cryptographically signed message to the recipient via off chain ways (e.g. email). -This means only two transactions are required to support any number of transfers. +.. note:: + Only steps 1 and 3 require Ethereum transactions, step 2 means that the sender transmits a cryptographically signed message to the recipient via off chain methods (e.g. email). This means only two transactions are required to support any number of transfers. -Bob is guaranteed to receive their funds because the smart contract escrows -the Ether and honors a valid signed message. The smart contract also enforces a timeout, -so Alice is guaranteed to eventually recover their funds even if the recipient refuses -to close the channel. -It is up to the participants in a payment channel to decide how long to keep it open. -For a short-lived transaction, such as paying an internet cafe for each minute of network access, -or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years. +Bob is guaranteed to receive their funds because the smart contract escrows the Ether and honours a valid signed message. The smart contract also enforces a timeout, so Alice is guaranteed to eventually recover their funds even if the recipient refuses to close the channel. It is up to the participants in a payment channel to decide how long to keep it open. For a short-lived transaction, such as paying an internet café for each minute of network access, or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years. Opening the Payment Channel --------------------------- -To open the payment channel, Alice deploys the smart contract, -attaching the Ether to be escrowed and specifying the intendend recipient -and a maximum duration for the channel to exist. It is the function -``SimplePaymentChannel`` in the contract, that is at the end of this chapter. +To open the payment channel, Alice deploys the smart contract, attaching the Ether to be escrowed and specifying the intended recipient and a maximum duration for the channel to exist. This is the function ``SimplePaymentChannel`` in the contract, at the end of this section. Making Payments --------------- @@ -910,27 +858,26 @@ Each message includes the following information: * The total amount of Ether that is owed the recipient so far. A payment channel is closed just once, at the end of a series of transfers. -Because of this, only one of the messages sent will be redeemed. This is why +Because of this, only one of the messages sent is redeemed. This is why each message specifies a cumulative total amount of Ether owed, rather than the amount of the individual micropayment. The recipient will naturally choose to redeem the most recent message because that is the one with the highest total. -The nonce per-message is not needed anymore, because the smart contract will -only honor a single message. The address of the smart contract is still used +The nonce per-message is not needed anymore, because the smart contract only honors a single message. The address of the smart contract is still used to prevent a message intended for one payment channel from being used for a different channel. -Here is the modified javascript code to cryptographically sign a message from the previous chapter: +Here is the modified JavaScript code to cryptographically sign a message from the previous section: :: function constructPaymentMessage(contractAddress, amount) { - return ethereumjs.ABI.soliditySHA3( + return abi.soliditySHA3( ["address", "uint256"], [contractAddress, amount] ); } function signMessage(message, callback) { - web3.personal.sign( + web3.eth.personal.sign( "0x" + message.toString("hex"), web3.eth.defaultAccount, callback @@ -951,18 +898,15 @@ Closing the Payment Channel When Bob is ready to receive their funds, it is time to close the payment channel by calling a ``close`` function on the smart contract. -Closing the channel pays the recipient the Ether they are owed and destroys the contract, -sending any remaining Ether back to Alice. -To close the channel, Bob needs to provide a message signed by Alice. +Closing the channel pays the recipient the Ether they are owed and destroys the contract, sending any remaining Ether back to Alice. To close the channel, Bob needs to provide a message signed by Alice. The smart contract must verify that the message contains a valid signature from the sender. The process for doing this verification is the same as the process the recipient uses. The Solidity functions ``isValidSignature`` and ``recoverSigner`` work just like their -JavaScript counterparts in the previous section. The latter is borrowed from the -``ReceiverPays`` contract in the previous chapter. +JavaScript counterparts in the previous section, with the latter function borrowed from the ``ReceiverPays`` contract. -The ``close`` function can only be called by the payment channel recipient, -who will naturally pass the most recent payment message because that message +Only the payment channel recipient can call the ``close`` function, +who naturally passes the most recent payment message because that message carries the highest total owed. If the sender were allowed to call this function, they could provide a message with a lower amount and cheat the recipient out of what they are owed. @@ -977,13 +921,11 @@ Channel Expiration Bob can close the payment channel at any time, but if they fail to do so, Alice needs a way to recover their escrowed funds. An *expiration* time was set at the time of contract deployment. Once that time is reached, Alice can call -``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the -full contract. +``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the full contract. After this function is called, Bob can no longer receive any Ether, so it is important that Bob closes the channel before the expiration is reached. - The full contract ----------------- @@ -1081,16 +1023,15 @@ The full contract } -Note: The function ``splitSignature`` is very simple and does not use all security checks. -A real implementation should use a more rigorously tested library, such as -openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code. - - +.. note:: + The function ``splitSignature`` does not use all security + checks. A real implementation should use a more rigorously tested library, + such as openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code. Verifying Payments ------------------ -Unlike in our previous chapter, messages in a payment channel aren't +Unlike in the previous section, messages in a payment channel aren't redeemed right away. The recipient keeps track of the latest message and redeems it when it's time to close the payment channel. This means it's critical that the recipient perform their own verification of each message. @@ -1105,10 +1046,8 @@ The recipient should verify each message using the following process: 4. Verify that the signature is valid and comes from the payment channel sender. We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_ -library to write this verifications. The final step can be done a number of ways, -but if it's being done in **JavaScript**. -The following code borrows the `constructMessage` function from the signing **JavaScript code** -above: +library to write this verification. The final step can be done a number of ways, +and we use JavaScript. The following code borrows the `constructMessage` function from the signing **JavaScript code** above: :: diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 582e5338..569639e0 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -56,7 +56,8 @@ Functions are the executable units of code within a contract. :ref:`function-calls` can happen internally or externally and have different levels of :ref:`visibility<visibility-and-getters>` -towards other contracts. +towards other contracts. :ref:`Functions<functions>` accept :ref:`parameters and return variables<function-parameters-return-variables>` to pass parameters +and values between them. .. _structure-function-modifiers: diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 7b48ccad..68fee871 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -809,7 +809,23 @@ possible permutations for function declarations. Mappings ======== -TODO +In variable declarations, do not separate the keyword ``mapping`` from its +type by a space. Do not separate any nested ``mapping`` keyword from its type by +whitespace. + +Yes:: + + mapping(uint => uint) map; + mapping(address => bool) registeredAddresses; + mapping(uint => mapping(bool => Data[])) public data; + mapping(uint => mapping(uint => s)) data; + +No:: + + mapping (uint => uint) map; + mapping( address => bool ) registeredAddresses; + mapping (uint => mapping (bool => Data[])) public data; + mapping(uint => mapping (uint => s)) data; Variable Declarations ===================== diff --git a/docs/types.rst b/docs/types.rst index 34b94b88..f67a6d1a 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -13,6 +13,11 @@ Solidity provides several elementary types which can be combined to form complex In addition, types can interact with each other in expressions containing operators. For a quick reference of the various operators, see :ref:`order`. +The concept of "undefined" or "null" values does not exist in Solidity, but newly +declared variables always have a :ref:`default value<default-value>` dependent +on its type. To handle any unexpected values, you should use the :ref:`revert function<assert-and-require>` to revert the whole transaction, or return a +tuple with a second `bool` value denoting success. + .. index:: ! value type, ! type;value Value Types @@ -253,7 +258,7 @@ Send is the low-level counterpart of ``transfer``. If the execution fails, the c In order to interface with contracts that do not adhere to the ABI, or to get more direct control over the encoding, the functions ``call``, ``delegatecall`` and ``staticcall`` are provided. -They all take a single ``bytes memory`` argument as input and +They all take a single ``bytes memory`` parameter and return the success condition (as a ``bool``) and the returned data (``bytes memory``). The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector`` @@ -273,22 +278,22 @@ Example:: when the call returns. The regular way to interact with other contracts is to call a function on a contract object (``x.f()``). -:: note:: +.. note:: Previous versions of Solidity allowed these functions to receive arbitrary arguments and would also handle a first argument of type ``bytes4`` differently. These edge cases were removed in version 0.5.0. It is possible to adjust the supplied gas with the ``.gas()`` modifier:: - namReg.call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName")); + address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName")); Similarly, the supplied Ether value can be controlled too:: - nameReg.call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); + address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); Lastly, these modifiers can be combined. Their order does not matter:: - nameReg.call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); + address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. @@ -460,11 +465,13 @@ a non-rational number). .. index:: literal, literal;string, string .. _string_literals: -String Literals ---------------- +String Literals and Types +------------------------- String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. +For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. + String literals support the following escape characters: - ``\<newline>`` (escapes an actual newline) @@ -857,13 +864,20 @@ or create a new memory array and copy every element. } } -.. index:: ! array;literals, !inline;arrays +.. index:: ! array;literals, ! inline;arrays + +Array Literals +^^^^^^^^^^^^^^ + +An array literal is a comma-separated list of one or more expressions, enclosed +in square brackets (``[...]``). For example ``[1, a, f(3)]``. There must be a +common type all elements can be implicitly converted to. This is the elementary +type of the array. -Array Literals / Inline Arrays -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Array literals are always statically-sized memory arrays. -Array literals are arrays that are written as an expression and are not -assigned to a variable right away. +In the example below, the type of ``[1, 2, 3]`` is +``uint8[3] memory``. Because the type of each of these constants is ``uint8``, if you want the result to be a ``uint[3] memory`` type, you need to convert the first element to ``uint``. :: @@ -878,13 +892,7 @@ assigned to a variable right away. } } -The type of an array literal is a memory array of fixed size whose base -type is the common type of the given elements. The type of ``[1, 2, 3]`` is -``uint8[3] memory``, because the type of each of these constants is ``uint8``. -Because of that, it is necessary to convert the first element in the example -above to ``uint``. Note that currently, fixed size memory arrays cannot -be assigned to dynamically-sized memory arrays, i.e. the following is not -possible: +Fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible: :: @@ -899,8 +907,8 @@ possible: } } -It is planned to remove this restriction in the future but currently creates -some complications because of how arrays are passed in the ABI. +It is planned to remove this restriction in the future, but it creates some +complications because of how arrays are passed in the ABI. .. index:: ! array;length, length, push, pop, !array;push, !array;pop @@ -913,7 +921,9 @@ Members For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array. Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion. Increasing the length adds new zero-initialised elements to the array. - Reducing the length performs an implicit :ref:``delete`` on each of the removed elements. + Reducing the length performs an implicit :ref:``delete`` on each of the + removed elements. If you try to resize a non-dynamic array that isn't in + storage, you receive a ``Value must be an lvalue`` error. **push**: Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length. **pop**: diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 7f62e71e..336aaf77 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -43,8 +43,8 @@ library has to be updated by an external oracle. .. note:: The suffix ``years`` has been removed in version 0.5.0 due to the reasons above. -These suffixes cannot be applied to variables. If you want to -interpret some input variable in e.g. days, you can do it in the following way:: +These suffixes cannot be applied to variables. For example, if you want to +interpret a function parameter in days, you can in the following way:: function f(uint start, uint daysAfter) public { if (now >= start + daysAfter * 1 days) { @@ -125,7 +125,7 @@ ABI Encoding and Decoding Functions These encoding functions can be used to craft data for external function calls without actually calling an external function. Furthermore, ``keccak256(abi.encodePacked(a, b))`` is a way to compute the hash of structured data (although be aware that it is possible to - craft a "hash collision" using different inputs types). + craft a "hash collision" using different function parameter types). See the documentation about the :ref:`ABI <ABI>` and the :ref:`tightly packed encoding <abi_packed_mode>` for details about the encoding. @@ -240,4 +240,3 @@ Furthermore, all functions of the current contract are callable directly includi .. note:: Prior to version 0.5.0, there was a function called ``suicide`` with the same semantics as ``selfdestruct``. - diff --git a/docs/yul.rst b/docs/yul.rst index 9e9fac8e..9e50f126 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -369,10 +369,10 @@ The following functions must be available: +---------------------------------------------+-----------------------------------------------------------------+ | gtu256(x:u256, y:u256) -> z:bool | true if x > y, false otherwise | +---------------------------------------------+-----------------------------------------------------------------+ -| sltu256(x:s256, y:s256) -> z:bool | true if x < y, false otherwise | +| lts256(x:s256, y:s256) -> z:bool | true if x < y, false otherwise | | | (for signed numbers in two's complement) | +---------------------------------------------+-----------------------------------------------------------------+ -| sgtu256(x:s256, y:s256) -> z:bool | true if x > y, false otherwise | +| gts256(x:s256, y:s256) -> z:bool | true if x > y, false otherwise | | | (for signed numbers in two's complement) | +---------------------------------------------+-----------------------------------------------------------------+ | equ256(x:u256, y:u256) -> z:bool | true if x == y, false otherwise | @@ -391,7 +391,7 @@ The following functions must be available: +---------------------------------------------+-----------------------------------------------------------------+ | shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y | +---------------------------------------------+-----------------------------------------------------------------+ -| saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y | +| sars256(x:s256, y:u256) -> z:u256 | arithmetic right shift of x by y | +---------------------------------------------+-----------------------------------------------------------------+ | byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte | | | Cannot this be just replaced by and256(shr256(n, x), 0xff) and | @@ -515,6 +515,16 @@ The following functions must be available: +---------------------------------------------+-----------------------------------------------------------------+ | keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) | +---------------------------------------------+-----------------------------------------------------------------+ +| *Object access* | | ++---------------------------------------------+-----------------------------------------------------------------+ +| datasize(name:string) -> size:u256 | size of the data object in bytes, name has to be string literal | ++---------------------------------------------+-----------------------------------------------------------------+ +| dataoffset(name:string) -> offset:u256 | offset of the data object inside the data area in bytes, | +| | name has to be string literal | ++---------------------------------------------+-----------------------------------------------------------------+ +| datacopy(dst:u256, src:u256, len:u256) | copy len bytes from the data area starting at offset src bytes | +| | to memory at position dst | ++---------------------------------------------+-----------------------------------------------------------------+ Backends -------- @@ -540,12 +550,18 @@ TBD Specification of Yul Object =========================== +Yul objects are used to group named code and data sections. +The functions ``datasize``, ``dataoffset`` and ``datacopy`` +can be used to access these sections from within code. +Hex strings can be used to specify data in hex encoding, +regular strings in native encoding. For code, +``datacopy`` will access its assembled binary representation. + Grammar:: - TopLevelObject = 'object' '{' Code? ( Object | Data )* '}' - Object = 'object' StringLiteral '{' Code? ( Object | Data )* '}' + Object = 'object' StringLiteral '{' Code ( Object | Data )* '}' Code = 'code' Block - Data = 'data' StringLiteral HexLiteral + Data = 'data' StringLiteral ( HexLiteral | StringLiteral ) HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' @@ -558,14 +574,28 @@ An example Yul Object is shown below: // Code consists of a single object. A single "code" node is the code of the object. // Every (other) named object or data section is serialized and // made accessible to the special built-in functions datacopy / dataoffset / datasize - object { + // Access to nested objects can be performed by joining the names using ``.``. + // The current object and sub-objects and data items inside the current object + // are in scope without nested access. + object "Contract1" { code { - let size = datasize("runtime") + // first create "runtime.Contract2" + let size = datasize("runtime.Contract2") let offset = allocate(size) // This will turn into a memory->memory copy for eWASM and // a codecopy for EVM - datacopy(dataoffset("runtime"), offset, size) - // this is a constructor and the runtime code is returned + datacopy(offset, dataoffset("runtime.Contract2"), size) + // constructor parameter is a single number 0x1234 + mstore(add(offset, size), 0x1234) + create(offset, add(size, 32)) + + // now return the runtime object (this is + // constructor code) + size := datasize("runtime") + offset := allocate(size) + // This will turn into a memory->memory copy for eWASM and + // a codecopy for EVM + datacopy(offset, dataoffset("runtime"), size) return(offset, size) } @@ -579,7 +609,7 @@ An example Yul Object is shown below: let offset = allocate(size) // This will turn into a memory->memory copy for eWASM and // a codecopy for EVM - datacopy(dataoffset("Contract2"), offset, size) + datacopy(offset, dataoffset("Contract2"), size) // constructor parameter is a single number 0x1234 mstore(add(offset, size), 0x1234) create(offset, add(size, 32)) diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index fa7e3f48..01a8bcc6 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -1,7 +1,17 @@ -file(GLOB sources "*.cpp") -file(GLOB headers "*.h") +set(sources + CommonData.cpp + CommonIO.cpp + Exceptions.cpp + IndentedWriter.cpp + JSON.cpp + Keccak256.cpp + StringUtils.cpp + SwarmHash.cpp + UTF8.cpp + Whiskers.cpp +) -add_library(devcore ${sources} ${headers}) +add_library(devcore ${sources}) target_link_libraries(devcore PRIVATE jsoncpp ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) diff --git a/libdevcore/JSON.cpp b/libdevcore/JSON.cpp index 6317cc89..086fae50 100644 --- a/libdevcore/JSON.cpp +++ b/libdevcore/JSON.cpp @@ -21,6 +21,8 @@ #include "JSON.h" +#include "CommonIO.h" + #include <sstream> #include <map> #include <memory> @@ -111,4 +113,10 @@ bool jsonParse(string const& _input, Json::Value& _json, string *_errs /* = null return parse(readerBuilder, _input, _json, _errs); } +bool jsonParseFile(string const& _fileName, Json::Value& _json, string *_errs /* = nullptr */) +{ + return jsonParse(readFileAsString(_fileName), _json, _errs); +} + + } // namespace dev diff --git a/libdevcore/JSON.h b/libdevcore/JSON.h index 1ce822cd..ecb93467 100644 --- a/libdevcore/JSON.h +++ b/libdevcore/JSON.h @@ -48,4 +48,11 @@ bool jsonParseStrict(std::string const& _input, Json::Value& _json, std::string* /// \return \c true if the document was successfully parsed, \c false if an error occurred. bool jsonParse(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); +/// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json) +/// \param _input file containing JSON input +/// \param _json [out] resulting JSON object +/// \param _errs [out] Formatted error messages +/// \return \c true if the document was successfully parsed, \c false if an error occurred. +bool jsonParseFile(std::string const& _fileName, Json::Value& _json, std::string* _errs = nullptr); + } diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index e63194a0..29d9846d 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -35,6 +35,7 @@ using namespace std; using namespace dev; using namespace dev::eth; +using namespace langutil; void Assembly::append(Assembly const& _a) { @@ -113,10 +114,10 @@ namespace string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) { - if (_location.isEmpty() || _sourceCodes.empty() || _location.start >= _location.end || _location.start < 0) + if (_location.isEmpty() || !_location.source.get() || _sourceCodes.empty() || _location.start >= _location.end || _location.start < 0) return ""; - auto it = _sourceCodes.find(*_location.sourceName); + auto it = _sourceCodes.find(_location.source->name()); if (it == _sourceCodes.end()) return ""; @@ -185,11 +186,11 @@ public: void printLocation() { - if (!m_location.sourceName && m_location.isEmpty()) + if (!m_location.source && m_location.isEmpty()) return; m_out << m_prefix << " /*"; - if (m_location.sourceName) - m_out << " \"" + *m_location.sourceName + "\""; + if (m_location.source) + m_out << " \"" + m_location.source->name() + "\""; if (!m_location.isEmpty()) m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end); m_out << " " << locationFromSources(m_sourceCodes, m_location); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 8ef36923..d846b475 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -18,12 +18,12 @@ #pragma once #include <libevmasm/Instruction.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include <libevmasm/AssemblyItem.h> #include <libevmasm/LinkerObject.h> #include <libevmasm/Exceptions.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libdevcore/Common.h> #include <libdevcore/Assertions.h> @@ -93,7 +93,7 @@ public: void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } /// Changes the source location used for each appended item. - void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; } + void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; } /// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached. LinkerObject const& assemble() const; @@ -178,7 +178,7 @@ protected: int m_deposit = 0; - SourceLocation m_currentSourceLocation; + langutil::SourceLocation m_currentSourceLocation; }; inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a) diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index a3a4d6b6..52f246d1 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -117,6 +117,8 @@ int AssemblyItem::returnValues() const bool AssemblyItem::canBeFunctional() const { + if (m_jumpType != JumpType::Ordinary) + return false; switch (m_type) { case Operation: diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 6187e18f..a7875171 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -26,7 +26,7 @@ #include <libdevcore/Common.h> #include <libdevcore/Assertions.h> #include <libevmasm/Instruction.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include "Exceptions.h" using namespace dev::solidity; @@ -57,14 +57,14 @@ class AssemblyItem public: enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; - AssemblyItem(u256 _push, SourceLocation const& _location = SourceLocation()): + AssemblyItem(u256 _push, langutil::SourceLocation const& _location = langutil::SourceLocation()): AssemblyItem(Push, _push, _location) { } - AssemblyItem(solidity::Instruction _i, SourceLocation const& _location = SourceLocation()): + AssemblyItem(solidity::Instruction _i, langutil::SourceLocation const& _location = langutil::SourceLocation()): m_type(Operation), m_instruction(_i), m_location(_location) {} - AssemblyItem(AssemblyItemType _type, u256 _data = 0, SourceLocation const& _location = SourceLocation()): + AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation const& _location = langutil::SourceLocation()): m_type(_type), m_location(_location) { @@ -124,8 +124,8 @@ public: /// @returns true if the assembly item can be used in a functional context. bool canBeFunctional() const; - void setLocation(SourceLocation const& _location) { m_location = _location; } - SourceLocation const& location() const { return m_location; } + void setLocation(langutil::SourceLocation const& _location) { m_location = _location; } + langutil::SourceLocation const& location() const { return m_location; } void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; } JumpType getJumpType() const { return m_jumpType; } @@ -140,7 +140,7 @@ private: AssemblyItemType m_type; Instruction m_instruction; ///< Only valid if m_type == Operation std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation - SourceLocation m_location; + langutil::SourceLocation m_location; JumpType m_jumpType = JumpType::Ordinary; /// Pushed value for operations with data to be determined during assembly stage, /// e.g. PushSubSize, PushTag, PushSub, etc. diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt index 86192c1b..e0e3389a 100644 --- a/libevmasm/CMakeLists.txt +++ b/libevmasm/CMakeLists.txt @@ -1,5 +1,21 @@ -file(GLOB sources "*.cpp") -file(GLOB headers "*.h") +set(sources + Assembly.cpp + AssemblyItem.cpp + BlockDeduplicator.cpp + CommonSubexpressionEliminator.cpp + ConstantOptimiser.cpp + ControlFlowGraph.cpp + ExpressionClasses.cpp + GasMeter.cpp + Instruction.cpp + JumpdestRemover.cpp + KnownState.cpp + LinkerObject.cpp + PathGasMeter.cpp + PeepholeOptimiser.cpp + SemanticInformation.cpp + SimplificationRules.cpp +) -add_library(evmasm ${sources} ${headers}) +add_library(evmasm ${sources}) target_link_libraries(evmasm PUBLIC devcore) diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 04926986..949d2c75 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -30,6 +30,7 @@ using namespace std; using namespace dev; using namespace dev::eth; +using namespace langutil; vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() { diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index b20de246..eba25db0 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -34,6 +34,11 @@ #include <libevmasm/SemanticInformation.h> #include <libevmasm/KnownState.h> +namespace langutil +{ +struct SourceLocation; +} + namespace dev { namespace eth @@ -137,10 +142,10 @@ private: bool removeStackTopIfPossible(); /// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position. - void appendDup(int _fromPosition, SourceLocation const& _location); + void appendDup(int _fromPosition, langutil::SourceLocation const& _location); /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. /// @note this might also remove the last item if it exactly the same swap instruction. - void appendOrRemoveSwap(int _fromPosition, SourceLocation const& _location); + void appendOrRemoveSwap(int _fromPosition, langutil::SourceLocation const& _location); /// Appends the given assembly item. void appendItem(AssemblyItem const& _item); diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 9844ba3a..ae1a5643 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -193,7 +193,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value) bigint bestGas = gasNeeded(routine); for (unsigned bits = 255; bits > 8 && m_maxSteps > 0; --bits) { - unsigned gapDetector = unsigned(_value >> (bits - 8)) & 0x1ff; + unsigned gapDetector = unsigned((_value >> (bits - 8)) & 0x1ff); if (gapDetector != 0xff && gapDetector != 0x100) continue; diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index 2c753fa8..04c43c5d 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -23,7 +23,7 @@ #include <libevmasm/Exceptions.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libdevcore/Assertions.h> #include <libdevcore/CommonData.h> @@ -109,8 +109,8 @@ class LiteralMethod: public ConstantOptimisationMethod public: explicit LiteralMethod(Params const& _params, u256 const& _value): ConstantOptimisationMethod(_params, _value) {} - virtual bigint gasNeeded() const override; - virtual AssemblyItems execute(Assembly&) const override { return AssemblyItems{}; } + bigint gasNeeded() const override; + AssemblyItems execute(Assembly&) const override { return AssemblyItems{}; } }; /** @@ -120,8 +120,8 @@ class CodeCopyMethod: public ConstantOptimisationMethod { public: explicit CodeCopyMethod(Params const& _params, u256 const& _value); - virtual bigint gasNeeded() const override; - virtual AssemblyItems execute(Assembly& _assembly) const override; + bigint gasNeeded() const override; + AssemblyItems execute(Assembly& _assembly) const override; protected: static AssemblyItems const& copyRoutine(); @@ -144,8 +144,8 @@ public: ); } - virtual bigint gasNeeded() const override { return gasNeeded(m_routine); } - virtual AssemblyItems execute(Assembly&) const override + bigint gasNeeded() const override { return gasNeeded(m_routine); } + AssemblyItems execute(Assembly&) const override { return m_routine; } diff --git a/libevmasm/ControlFlowGraph.cpp b/libevmasm/ControlFlowGraph.cpp index 86f16d48..d62f5436 100644 --- a/libevmasm/ControlFlowGraph.cpp +++ b/libevmasm/ControlFlowGraph.cpp @@ -275,7 +275,7 @@ void ControlFlowGraph::gatherKnowledge() //@todo in the case of JUMPI, add knowledge about the condition to the state // (for both values of the condition) set<u256> tags = state->tagsInExpression( - state->stackElement(state->stackHeight(), SourceLocation()) + state->stackElement(state->stackHeight(), langutil::SourceLocation{}) ); state->feedItem(m_items.at(pc++)); diff --git a/libevmasm/ExpressionClasses.cpp b/libevmasm/ExpressionClasses.cpp index 42a1819a..abbbbc2c 100644 --- a/libevmasm/ExpressionClasses.cpp +++ b/libevmasm/ExpressionClasses.cpp @@ -34,7 +34,7 @@ using namespace std; using namespace dev; using namespace dev::eth; - +using namespace langutil; bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const { diff --git a/libevmasm/ExpressionClasses.h b/libevmasm/ExpressionClasses.h index df8082f9..a34844c5 100644 --- a/libevmasm/ExpressionClasses.h +++ b/libevmasm/ExpressionClasses.h @@ -31,6 +31,11 @@ #include <memory> #include <set> +namespace langutil +{ +struct SourceLocation; +} + namespace dev { namespace eth @@ -82,7 +87,7 @@ public: void forceEqual(Id _id, AssemblyItem const& _item, Ids const& _arguments, bool _copyItem = true); /// @returns the id of a new class which is different to all other classes. - Id newClass(SourceLocation const& _location); + Id newClass(langutil::SourceLocation const& _location); /// @returns true if the values of the given classes are known to be different (on every input). /// @note that this function might still return false for some different inputs. diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index da90b028..e3909e86 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -24,7 +24,7 @@ #include <libevmasm/ExpressionClasses.h> #include <libevmasm/AssemblyItem.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <ostream> #include <tuple> diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index b6c1bcc9..a5546e61 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -29,6 +29,7 @@ using namespace std; using namespace dev; using namespace dev::eth; +using namespace langutil; ostream& KnownState::stream(ostream& _out) const { diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h index cd50550e..3ab1c4b1 100644 --- a/libevmasm/KnownState.h +++ b/libevmasm/KnownState.h @@ -46,6 +46,11 @@ #include <libevmasm/ExpressionClasses.h> #include <libevmasm/SemanticInformation.h> +namespace langutil +{ +struct SourceLocation; +} + namespace dev { namespace eth @@ -121,9 +126,9 @@ public: /// Retrieves the current equivalence class fo the given stack element (or generates a new /// one if it does not exist yet). - Id stackElement(int _stackHeight, SourceLocation const& _location); + Id stackElement(int _stackHeight, langutil::SourceLocation const& _location); /// @returns the stackElement relative to the current stack height. - Id relativeStackElement(int _stackOffset, SourceLocation const& _location = SourceLocation()); + Id relativeStackElement(int _stackOffset, langutil::SourceLocation const& _location = {}); /// @returns its set of tags if the given expression class is a known tag union; returns a set /// containing the tag if it is a PushTag expression and the empty set otherwise. @@ -142,22 +147,22 @@ private: /// Assigns a new equivalence class to the next sequence number of the given stack element. void setStackElement(int _stackHeight, Id _class); /// Swaps the given stack elements in their next sequence number. - void swapStackElements(int _stackHeightA, int _stackHeightB, SourceLocation const& _location); + void swapStackElements(int _stackHeightA, int _stackHeightB, langutil::SourceLocation const& _location); /// Increments the sequence number, deletes all storage information that might be overwritten /// and stores the new value at the given slot. /// @returns the store operation, which might be invalid if storage was not modified - StoreOperation storeInStorage(Id _slot, Id _value, SourceLocation const& _location); + StoreOperation storeInStorage(Id _slot, Id _value, langutil::SourceLocation const& _location); /// Retrieves the current value at the given slot in storage or creates a new special sload class. - Id loadFromStorage(Id _slot, SourceLocation const& _location); + Id loadFromStorage(Id _slot, langutil::SourceLocation const& _location); /// Increments the sequence number, deletes all memory information that might be overwritten /// and stores the new value at the given slot. /// @returns the store operation, which might be invalid if memory was not modified - StoreOperation storeInMemory(Id _slot, Id _value, SourceLocation const& _location); + StoreOperation storeInMemory(Id _slot, Id _value, langutil::SourceLocation const& _location); /// Retrieves the current value at the given slot in memory or creates a new special mload class. - Id loadFromMemory(Id _slot, SourceLocation const& _location); + Id loadFromMemory(Id _slot, langutil::SourceLocation const& _location); /// Finds or creates a new expression that applies the Keccak-256 hash function to the contents in memory. - Id applyKeccak256(Id _start, Id _length, SourceLocation const& _location); + Id applyKeccak256(Id _start, Id _length, langutil::SourceLocation const& _location); /// @returns a new or already used Id representing the given set of tags. Id tagUnion(std::set<u256> _tags); diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h index fb821684..772df484 100644 --- a/libevmasm/PathGasMeter.h +++ b/libevmasm/PathGasMeter.h @@ -23,7 +23,7 @@ #include <libevmasm/GasMeter.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <set> #include <vector> diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index 120d1787..1dce5f1e 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -38,7 +38,7 @@ using namespace std; using namespace dev; using namespace dev::eth; - +using namespace langutil; SimplificationRule<Pattern> const* Rules::findFirstMatch( Expression const& _expr, diff --git a/libevmasm/SimplificationRules.h b/libevmasm/SimplificationRules.h index fbe5a2b0..fc45a46c 100644 --- a/libevmasm/SimplificationRules.h +++ b/libevmasm/SimplificationRules.h @@ -31,6 +31,11 @@ #include <functional> #include <vector> +namespace langutil +{ +struct SourceLocation; +} + namespace dev { namespace eth @@ -97,7 +102,7 @@ public: unsigned matchGroup() const { return m_matchGroup; } bool matches(Expression const& _expr, ExpressionClasses const& _classes) const; - AssemblyItem toAssemblyItem(SourceLocation const& _location) const; + AssemblyItem toAssemblyItem(langutil::SourceLocation const& _location) const; std::vector<Pattern> arguments() const { return m_arguments; } /// @returns the id of the matched expression if this pattern is part of a match group. @@ -135,7 +140,7 @@ struct ExpressionTemplate { using Expression = ExpressionClasses::Expression; using Id = ExpressionClasses::Id; - explicit ExpressionTemplate(Pattern const& _pattern, SourceLocation const& _location); + explicit ExpressionTemplate(Pattern const& _pattern, langutil::SourceLocation const& _location); std::string toString() const; bool hasId = false; /// Id of the matched expression, if available. diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt new file mode 100644 index 00000000..dfcccfce --- /dev/null +++ b/liblangutil/CMakeLists.txt @@ -0,0 +1,13 @@ +# Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul) +set(sources + CharStream.cpp + ErrorReporter.cpp + Exceptions.cpp + ParserBase.cpp + Scanner.cpp + SourceReferenceFormatter.cpp + Token.cpp +) + +add_library(langutil ${sources}) +target_link_libraries(langutil PUBLIC devcore) diff --git a/liblangutil/CharStream.cpp b/liblangutil/CharStream.cpp new file mode 100644 index 00000000..aee7cb3e --- /dev/null +++ b/liblangutil/CharStream.cpp @@ -0,0 +1,108 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. + + This file is derived from the file "scanner.cc", which was part of the + V8 project. The original copyright header follows: + + Copyright 2006-2012, the V8 project authors. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity scanner. + */ + +#include <liblangutil/CharStream.h> +#include <liblangutil/Exceptions.h> + +using namespace std; +using namespace langutil; + +char CharStream::advanceAndGet(size_t _chars) +{ + if (isPastEndOfInput()) + return 0; + m_position += _chars; + if (isPastEndOfInput()) + return 0; + return m_source[m_position]; +} + +char CharStream::rollback(size_t _amount) +{ + solAssert(m_position >= _amount, ""); + m_position -= _amount; + return get(); +} + +string CharStream::lineAtPosition(int _position) const +{ + // if _position points to \n, it returns the line before the \n + using size_type = string::size_type; + size_type searchStart = min<size_type>(m_source.size(), _position); + if (searchStart > 0) + searchStart--; + size_type lineStart = m_source.rfind('\n', searchStart); + if (lineStart == string::npos) + lineStart = 0; + else + lineStart++; + return m_source.substr(lineStart, min(m_source.find('\n', lineStart), + m_source.size()) - lineStart); +} + +tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const +{ + using size_type = string::size_type; + size_type searchPosition = min<size_type>(m_source.size(), _position); + int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n'); + size_type lineStart; + if (searchPosition == 0) + lineStart = 0; + else + { + lineStart = m_source.rfind('\n', searchPosition - 1); + lineStart = lineStart == string::npos ? 0 : lineStart + 1; + } + return tuple<int, int>(lineNumber, searchPosition - lineStart); +} + + diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h new file mode 100644 index 00000000..f92beb30 --- /dev/null +++ b/liblangutil/CharStream.h @@ -0,0 +1,100 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. + + This file is derived from the file "scanner.h", which was part of the + V8 project. The original copyright header follows: + + Copyright 2006-2012, the V8 project authors. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity scanner. + */ + +#pragma once + +#include <cstdint> +#include <string> +#include <tuple> + +namespace langutil +{ + +/** + * Bidirectional stream of characters. + * + * This CharStream is used by lexical analyzers as the source. + */ +class CharStream +{ +public: + CharStream(): m_position(0) {} + explicit CharStream(std::string const& _source, std::string const& name): + m_source(_source), m_name(name), m_position(0) {} + + int position() const { return m_position; } + bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); } + + char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; } + char advanceAndGet(size_t _chars = 1); + char rollback(size_t _amount); + + void reset() { m_position = 0; } + + std::string const& source() const noexcept { return m_source; } + std::string const& name() const noexcept { return m_name; } + + ///@{ + ///@name Error printing helper functions + /// Functions that help pretty-printing parse errors + /// Do only use in error cases, they are quite expensive. + std::string lineAtPosition(int _position) const; + std::tuple<int, int> translatePositionToLineColumn(int _position) const; + ///@} + +private: + std::string m_source; + std::string m_name; + size_t m_position; +}; + +} diff --git a/libsolidity/interface/EVMVersion.h b/liblangutil/EVMVersion.h index 657727ac..657727ac 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/liblangutil/EVMVersion.h diff --git a/libsolidity/interface/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index 368e25e0..5b6e0072 100644 --- a/libsolidity/interface/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -20,13 +20,13 @@ * Error helper class. */ -#include <libsolidity/interface/ErrorReporter.h> -#include <libsolidity/ast/AST.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/SourceLocation.h> #include <memory> using namespace std; using namespace dev; -using namespace dev::solidity; +using namespace langutil; ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter) { diff --git a/libsolidity/interface/ErrorReporter.h b/liblangutil/ErrorReporter.h index fd53587a..d90e652e 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/liblangutil/ErrorReporter.h @@ -22,15 +22,11 @@ #pragma once -#include <libsolidity/interface/Exceptions.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/Exceptions.h> +#include <liblangutil/SourceLocation.h> -namespace dev +namespace langutil { -namespace solidity -{ - -class ASTNode; class ErrorReporter { @@ -120,7 +116,5 @@ private: const unsigned c_maxErrorsAllowed = 256; }; - -} } diff --git a/libsolidity/interface/Exceptions.cpp b/liblangutil/Exceptions.cpp index ecadd0b7..346313d5 100644 --- a/libsolidity/interface/Exceptions.cpp +++ b/liblangutil/Exceptions.cpp @@ -20,11 +20,11 @@ * Solidity exception hierarchy. */ -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> using namespace std; using namespace dev; -using namespace dev::solidity; +using namespace langutil; Error::Error(Type _type, SourceLocation const& _location, string const& _description): m_type(_type) diff --git a/libsolidity/interface/Exceptions.h b/liblangutil/Exceptions.h index 629b8f3f..5ad31ab2 100644 --- a/libsolidity/interface/Exceptions.h +++ b/liblangutil/Exceptions.h @@ -24,33 +24,33 @@ #include <string> #include <utility> +#include <vector> +#include <memory> #include <libdevcore/Exceptions.h> #include <libdevcore/Assertions.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> -namespace dev -{ -namespace solidity +namespace langutil { class Error; using ErrorList = std::vector<std::shared_ptr<Error const>>; -struct CompilerError: virtual Exception {}; -struct InternalCompilerError: virtual Exception {}; -struct FatalError: virtual Exception {}; -struct UnimplementedFeatureError: virtual Exception{}; +struct CompilerError: virtual dev::Exception {}; +struct InternalCompilerError: virtual dev::Exception {}; +struct FatalError: virtual dev::Exception {}; +struct UnimplementedFeatureError: virtual dev::Exception {}; /// Assertion that throws an InternalCompilerError containing the given description if it is not met. #define solAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION) + assertThrow(CONDITION, ::langutil::InternalCompilerError, DESCRIPTION) #define solUnimplementedAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION) + assertThrow(CONDITION, ::langutil::UnimplementedFeatureError, DESCRIPTION) #define solUnimplemented(DESCRIPTION) \ solUnimplementedAssert(false, DESCRIPTION) -class Error: virtual public Exception +class Error: virtual public dev::Exception { public: enum class Type @@ -98,7 +98,6 @@ private: std::string m_typeName; }; - using errorSourceLocationInfo = std::pair<std::string, SourceLocation>; class SecondarySourceLocation @@ -109,6 +108,7 @@ public: infos.push_back(std::make_pair(_errMsg, _sourceLocation)); return *this; } + /// Limits the number of secondary source locations to 32 and appends a notice to the /// error message. void limitSize(std::string& _message) @@ -124,9 +124,8 @@ public: std::vector<errorSourceLocationInfo> infos; }; - using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, SourceLocation>; using errinfo_secondarySourceLocation = boost::error_info<struct tag_secondarySourceLocation, SecondarySourceLocation>; -} + } diff --git a/libsolidity/parsing/ParserBase.cpp b/liblangutil/ParserBase.cpp index 1d4cb1e2..391af291 100644 --- a/libsolidity/parsing/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -20,18 +20,12 @@ * Solidity parser shared functionality. */ -#include <libsolidity/parsing/ParserBase.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ParserBase.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/ErrorReporter.h> using namespace std; -using namespace dev; -using namespace dev::solidity; - -std::shared_ptr<string const> const& ParserBase::sourceName() const -{ - return m_scanner->sourceName(); -} +using namespace langutil; int ParserBase::position() const { @@ -106,10 +100,10 @@ void ParserBase::decreaseRecursionDepth() void ParserBase::parserError(string const& _description) { - m_errorReporter.parserError(SourceLocation(position(), endPosition(), sourceName()), _description); + m_errorReporter.parserError(SourceLocation(position(), endPosition(), source()), _description); } void ParserBase::fatalParserError(string const& _description) { - m_errorReporter.fatalParserError(SourceLocation(position(), endPosition(), sourceName()), _description); + m_errorReporter.fatalParserError(SourceLocation(position(), endPosition(), source()), _description); } diff --git a/libsolidity/parsing/ParserBase.h b/liblangutil/ParserBase.h index e01f37d8..855201e2 100644 --- a/libsolidity/parsing/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -22,12 +22,12 @@ #pragma once +#include <liblangutil/Token.h> +#include <liblangutil/Scanner.h> #include <memory> -#include <libsolidity/parsing/Token.h> +#include <string> -namespace dev -{ -namespace solidity +namespace langutil { class ErrorReporter; @@ -38,7 +38,7 @@ class ParserBase public: explicit ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {} - std::shared_ptr<std::string const> const& sourceName() const; + std::shared_ptr<CharStream> source() const { return m_scanner->charStream(); } protected: /// Utility class that creates an error and throws an exception if the @@ -90,4 +90,3 @@ protected: }; } -} diff --git a/libsolidity/parsing/Scanner.cpp b/liblangutil/Scanner.cpp index e9dad2ad..ac298bd5 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -50,16 +50,15 @@ * Solidity scanner. */ +#include <liblangutil/Exceptions.h> +#include <liblangutil/Scanner.h> #include <algorithm> +#include <ostream> #include <tuple> -#include <libsolidity/interface/Exceptions.h> -#include <libsolidity/parsing/Scanner.h> using namespace std; -namespace dev -{ -namespace solidity +namespace langutil { namespace @@ -102,7 +101,32 @@ int hexValue(char c) } } // end anonymous namespace +std::string to_string(ScannerError _errorCode) +{ + switch (_errorCode) + { + case ScannerError::NoError: return "No error."; + case ScannerError::IllegalToken: return "Invalid token."; + case ScannerError::IllegalHexString: return "Expected even number of hex-nibbles within double-quotes."; + case ScannerError::IllegalHexDigit: return "Hexadecimal digit missing or invalid."; + case ScannerError::IllegalCommentTerminator: return "Expected multi-line comment-terminator."; + case ScannerError::IllegalEscapeSequence: return "Invalid escape sequence."; + case ScannerError::IllegalStringEndQuote: return "Expected string end-quote."; + case ScannerError::IllegalNumberSeparator: return "Invalid use of number separator '_'."; + case ScannerError::IllegalExponent: return "Invalid exponent."; + case ScannerError::IllegalNumberEnd: return "Identifier-start is not allowed at end of a number."; + case ScannerError::OctalNotAllowed: return "Octal numbers not allowed."; + default: + solAssert(false, "Unhandled case in to_string(ScannerError)"); + return ""; + } +} +std::ostream& operator<<(std::ostream& os, ScannerError _errorCode) +{ + os << to_string(_errorCode); + return os; +} /// Scoped helper for literal recording. Automatically drops the literal /// if aborting the scanning before it's complete. @@ -143,17 +167,23 @@ private: }; // end of LiteralScope class -void Scanner::reset(CharStream const& _source, string const& _sourceName) +void Scanner::reset(CharStream _source) { + m_source = make_shared<CharStream>(std::move(_source)); + reset(); +} + +void Scanner::reset(std::shared_ptr<CharStream> _source) +{ + solAssert(_source.get() != nullptr, "You MUST provide a CharStream when resetting."); m_source = _source; - m_sourceName = make_shared<string const>(_sourceName); reset(); } void Scanner::reset() { - m_source.reset(); - m_char = m_source.get(); + m_source->reset(); + m_char = m_source->get(); skipWhitespace(); scanToken(); next(); @@ -272,13 +302,13 @@ Token Scanner::scanSingleLineDocComment() { // check if next line is also a documentation comment skipWhitespace(); - if (!m_source.isPastEndOfInput(3) && - m_source.get(0) == '/' && - m_source.get(1) == '/' && - m_source.get(2) == '/') + if (!m_source->isPastEndOfInput(3) && + m_source->get(0) == '/' && + m_source->get(1) == '/' && + m_source->get(2) == '/') { addCommentLiteralChar('\n'); - m_char = m_source.advanceAndGet(3); + m_char = m_source->advanceAndGet(3); } else break; // next line is not a documentation comment, we are done @@ -313,7 +343,7 @@ Token Scanner::skipMultiLineComment() } } // Unterminated multi-line comment. - return Token::Illegal; + return setError(ScannerError::IllegalCommentTerminator); } Token Scanner::scanMultiLineDocComment() @@ -331,20 +361,20 @@ Token Scanner::scanMultiLineDocComment() if (isLineTerminator(m_char)) { skipWhitespace(); - if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '*') + if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '*') { // it is unknown if this leads to the end of the comment addCommentLiteralChar('*'); advance(); } - else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/') + else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) != '/') { // skip first '*' in subsequent lines if (charsAdded) addCommentLiteralChar('\n'); - m_char = m_source.advanceAndGet(2); + m_char = m_source->advanceAndGet(2); } - else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') + else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/') { // if after newline the comment ends, don't insert the newline - m_char = m_source.advanceAndGet(2); + m_char = m_source->advanceAndGet(2); endFound = true; break; } @@ -352,9 +382,9 @@ Token Scanner::scanMultiLineDocComment() addCommentLiteralChar('\n'); } - if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') + if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/') { - m_char = m_source.advanceAndGet(2); + m_char = m_source->advanceAndGet(2); endFound = true; break; } @@ -364,7 +394,7 @@ Token Scanner::scanMultiLineDocComment() } literal.complete(); if (!endFound) - return Token::Illegal; + return setError(ScannerError::IllegalCommentTerminator); else return Token::CommentLiteral; } @@ -394,7 +424,7 @@ Token Scanner::scanSlash() { // doxygen style /** natspec comment if (!advance()) /* slash star comment before EOS */ - return Token::Illegal; + return setError(ScannerError::IllegalCommentTerminator); else if (m_char == '*') { advance(); //consume the last '*' at /** @@ -412,7 +442,7 @@ Token Scanner::scanSlash() m_nextSkippedComment.location.end = sourcePos(); m_nextSkippedComment.token = comment; if (comment == Token::Illegal) - return Token::Illegal; + return Token::Illegal; // error already set else return Token::Whitespace; } @@ -427,6 +457,7 @@ Token Scanner::scanSlash() void Scanner::scanToken() { + m_nextToken.error = ScannerError::NoError; m_nextToken.literal.clear(); m_nextToken.extendedTokenInfo = make_tuple(0, 0); m_nextSkippedComment.literal.clear(); @@ -612,7 +643,7 @@ void Scanner::scanToken() if (m_char == '"' || m_char == '\'') token = scanHexString(); else - token = Token::IllegalHex; + token = setError(ScannerError::IllegalToken); } } else if (isDecimalDigit(m_char)) @@ -622,7 +653,7 @@ void Scanner::scanToken() else if (isSourcePastEndOfInput()) token = Token::EOS; else - token = selectToken(Token::Illegal); + token = selectErrorToken(ScannerError::IllegalToken); break; } // Continue scanning for tokens as long as we're just skipping @@ -690,11 +721,11 @@ bool Scanner::isUnicodeLinebreak() if (0x0a <= m_char && m_char <= 0x0d) // line feed, vertical tab, form feed, carriage return return true; - else if (!m_source.isPastEndOfInput(1) && uint8_t(m_source.get(0)) == 0xc2 && uint8_t(m_source.get(1)) == 0x85) + else if (!m_source->isPastEndOfInput(1) && uint8_t(m_source->get(0)) == 0xc2 && uint8_t(m_source->get(1)) == 0x85) // NEL - U+0085, C2 85 in utf8 return true; - else if (!m_source.isPastEndOfInput(2) && uint8_t(m_source.get(0)) == 0xe2 && uint8_t(m_source.get(1)) == 0x80 && ( - uint8_t(m_source.get(2)) == 0xa8 || uint8_t(m_source.get(2)) == 0xa9 + else if (!m_source->isPastEndOfInput(2) && uint8_t(m_source->get(0)) == 0xe2 && uint8_t(m_source->get(1)) == 0x80 && ( + uint8_t(m_source->get(2)) == 0xa8 || uint8_t(m_source->get(2)) == 0xa9 )) // LS - U+2028, E2 80 A8 in utf8 // PS - U+2029, E2 80 A9 in utf8 @@ -715,13 +746,13 @@ Token Scanner::scanString() if (c == '\\') { if (isSourcePastEndOfInput() || !scanEscape()) - return Token::Illegal; + return setError(ScannerError::IllegalEscapeSequence); } else addLiteralChar(c); } if (m_char != quote) - return Token::Illegal; + return setError(ScannerError::IllegalStringEndQuote); literal.complete(); advance(); // consume quote return Token::StringLiteral; @@ -736,11 +767,14 @@ Token Scanner::scanHexString() { char c = m_char; if (!scanHexByte(c)) - return Token::IllegalHex; + // can only return false if hex-byte is incomplete (only one hex digit instead of two) + return setError(ScannerError::IllegalHexString); addLiteralChar(c); } + if (m_char != quote) - return Token::IllegalHex; + return setError(ScannerError::IllegalStringEndQuote); + literal.complete(); advance(); // consume quote return Token::StringLiteral; @@ -755,7 +789,7 @@ void Scanner::scanDecimalDigits() // May continue with decimal digit or underscore for grouping. do addLiteralCharAndAdvance(); - while (!m_source.isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_')); + while (!m_source->isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_')); // Defer further validation of underscore to SyntaxChecker. } @@ -769,7 +803,7 @@ Token Scanner::scanNumber(char _charSeen) // we have already seen a decimal point of the float addLiteralChar('.'); if (m_char == '_') - return Token::Illegal; + return setError(ScannerError::IllegalToken); scanDecimalDigits(); // we know we have at least one digit } else @@ -786,14 +820,14 @@ Token Scanner::scanNumber(char _charSeen) kind = HEX; addLiteralCharAndAdvance(); if (!isHexDigit(m_char)) - return Token::Illegal; // we must have at least one hex digit after 'x' + return setError(ScannerError::IllegalHexDigit); // we must have at least one hex digit after 'x' while (isHexDigit(m_char) || m_char == '_') // We keep the underscores for later validation addLiteralCharAndAdvance(); } else if (isDecimalDigit(m_char)) // We do not allow octal numbers - return Token::Illegal; + return setError(ScannerError::OctalNotAllowed); } // Parse decimal digits and allow trailing fractional part. if (kind == DECIMAL) @@ -801,7 +835,7 @@ Token Scanner::scanNumber(char _charSeen) scanDecimalDigits(); // optional if (m_char == '.') { - if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') + if (!m_source->isPastEndOfInput(1) && m_source->get(1) == '_') { // Assume the input may be a floating point number with leading '_' in fraction part. // Recover by consuming it all but returning `Illegal` right away. @@ -809,7 +843,7 @@ Token Scanner::scanNumber(char _charSeen) addLiteralCharAndAdvance(); // '_' scanDecimalDigits(); } - if (m_source.isPastEndOfInput() || !isDecimalDigit(m_source.get(1))) + if (m_source->isPastEndOfInput() || !isDecimalDigit(m_source->get(1))) { // A '.' has to be followed by a number. literal.complete(); @@ -825,8 +859,8 @@ Token Scanner::scanNumber(char _charSeen) { solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); if (kind != DECIMAL) - return Token::Illegal; - else if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') + return setError(ScannerError::IllegalExponent); + else if (!m_source->isPastEndOfInput(1) && m_source->get(1) == '_') { // Recover from wrongly placed underscore as delimiter in literal with scientific // notation by consuming until the end. @@ -840,8 +874,8 @@ Token Scanner::scanNumber(char _charSeen) addLiteralCharAndAdvance(); // 'e' | 'E' if (m_char == '+' || m_char == '-') addLiteralCharAndAdvance(); - if (!isDecimalDigit(m_char)) - return Token::Illegal; // we must have at least one decimal digit after 'e'/'E' + if (!isDecimalDigit(m_char)) // we must have at least one decimal digit after 'e'/'E' + return setError(ScannerError::IllegalExponent); scanDecimalDigits(); } // The source character immediately following a numeric literal must @@ -849,7 +883,7 @@ Token Scanner::scanNumber(char _charSeen) // section 7.8.3, page 17 (note that we read only one decimal digit // if the value is 0). if (isDecimalDigit(m_char) || isIdentifierStart(m_char)) - return Token::Illegal; + return setError(ScannerError::IllegalNumberEnd); literal.complete(); return Token::Number; } @@ -866,55 +900,5 @@ tuple<Token, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() return TokenTraits::fromIdentifierOrKeyword(m_nextToken.literal); } -char CharStream::advanceAndGet(size_t _chars) -{ - if (isPastEndOfInput()) - return 0; - m_position += _chars; - if (isPastEndOfInput()) - return 0; - return m_source[m_position]; -} -char CharStream::rollback(size_t _amount) -{ - solAssert(m_position >= _amount, ""); - m_position -= _amount; - return get(); -} - -string CharStream::lineAtPosition(int _position) const -{ - // if _position points to \n, it returns the line before the \n - using size_type = string::size_type; - size_type searchStart = min<size_type>(m_source.size(), _position); - if (searchStart > 0) - searchStart--; - size_type lineStart = m_source.rfind('\n', searchStart); - if (lineStart == string::npos) - lineStart = 0; - else - lineStart++; - return m_source.substr(lineStart, min(m_source.find('\n', lineStart), - m_source.size()) - lineStart); -} - -tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const -{ - using size_type = string::size_type; - size_type searchPosition = min<size_type>(m_source.size(), _position); - int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n'); - size_type lineStart; - if (searchPosition == 0) - lineStart = 0; - else - { - lineStart = m_source.rfind('\n', searchPosition - 1); - lineStart = lineStart == string::npos ? 0 : lineStart + 1; - } - return tuple<int, int>(lineNumber, searchPosition - lineStart); -} - - -} } diff --git a/libsolidity/parsing/Scanner.h b/liblangutil/Scanner.h index 14eeb66e..72d0072f 100644 --- a/libsolidity/parsing/Scanner.h +++ b/liblangutil/Scanner.h @@ -52,62 +52,54 @@ #pragma once +#include <liblangutil/Token.h> +#include <liblangutil/CharStream.h> +#include <liblangutil/SourceLocation.h> #include <libdevcore/Common.h> #include <libdevcore/CommonData.h> -#include <libevmasm/SourceLocation.h> -#include <libsolidity/parsing/Token.h> +#include <iosfwd> -namespace dev +namespace langutil { -namespace solidity -{ - class AstRawString; class AstValueFactory; class ParserRecorder; -class CharStream +enum class ScannerError { -public: - CharStream(): m_position(0) {} - explicit CharStream(std::string const& _source): m_source(_source), m_position(0) {} - int position() const { return m_position; } - bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); } - char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; } - char advanceAndGet(size_t _chars = 1); - char rollback(size_t _amount); - - void reset() { m_position = 0; } - - std::string const& source() const { return m_source; } - - ///@{ - ///@name Error printing helper functions - /// Functions that help pretty-printing parse errors - /// Do only use in error cases, they are quite expensive. - std::string lineAtPosition(int _position) const; - std::tuple<int, int> translatePositionToLineColumn(int _position) const; - ///@} - -private: - std::string m_source; - size_t m_position; + NoError, + + IllegalToken, + IllegalHexString, + IllegalHexDigit, + IllegalCommentTerminator, + IllegalEscapeSequence, + IllegalStringEndQuote, + IllegalNumberSeparator, + IllegalExponent, + IllegalNumberEnd, + + OctalNotAllowed, }; - +std::string to_string(ScannerError _errorCode); +std::ostream& operator<<(std::ostream& os, ScannerError _errorCode); class Scanner { friend class LiteralScope; public: + explicit Scanner(std::shared_ptr<CharStream> _source) { reset(std::move(_source)); } + explicit Scanner(CharStream _source = CharStream()) { reset(std::move(_source)); } - explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); } + std::string const& source() const noexcept { return m_source->source(); } - std::string source() const { return m_source.source(); } + std::shared_ptr<CharStream> charStream() noexcept { return m_source; } - /// Resets the scanner as if newly constructed with _source and _sourceName as input. - void reset(CharStream const& _source, std::string const& _sourceName); + /// Resets the scanner as if newly constructed with _source as input. + void reset(CharStream _source); + void reset(std::shared_ptr<CharStream> _source); /// Resets scanner to the start of input. void reset(); @@ -133,6 +125,10 @@ public: SourceLocation currentLocation() const { return m_currentToken.location; } std::string const& currentLiteral() const { return m_currentToken.literal; } std::tuple<unsigned, unsigned> const& currentTokenInfo() const { return m_currentToken.extendedTokenInfo; } + + /// Retrieves the last error that occurred during lexical analysis. + /// @note If no error occurred, the value is undefined. + ScannerError currentError() const noexcept { return m_currentToken.error; } ///@} ///@{ @@ -154,30 +150,34 @@ public: std::string const& peekLiteral() const { return m_nextToken.literal; } ///@} - std::shared_ptr<std::string const> const& sourceName() const { return m_sourceName; } - ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors /// Do only use in error cases, they are quite expensive. - std::string lineAtPosition(int _position) const { return m_source.lineAtPosition(_position); } - std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); } + std::string lineAtPosition(int _position) const { return m_source->lineAtPosition(_position); } + std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source->translatePositionToLineColumn(_position); } std::string sourceAt(SourceLocation const& _location) const { solAssert(!_location.isEmpty(), ""); - solAssert(m_sourceName && _location.sourceName, ""); - solAssert(*m_sourceName == *_location.sourceName, ""); - return m_source.source().substr(_location.start, _location.end - _location.start); + solAssert(m_source.get() == _location.source.get(), "CharStream memory locations must match."); + return m_source->source().substr(_location.start, _location.end - _location.start); } ///@} private: + inline Token setError(ScannerError _error) noexcept + { + m_nextToken.error = _error; + return Token::Illegal; + } + /// Used for the current and look-ahead token and comments struct TokenDesc { Token token; SourceLocation location; std::string literal; + ScannerError error = ScannerError::NoError; std::tuple<unsigned, unsigned> extendedTokenInfo; }; @@ -189,9 +189,10 @@ private: void addUnicodeAsUTF8(unsigned codepoint); ///@} - bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } - void rollback(int _amount) { m_char = m_source.rollback(_amount); } + bool advance() { m_char = m_source->advanceAndGet(); return !m_source->isPastEndOfInput(); } + void rollback(int _amount) { m_char = m_source->rollback(_amount); } + inline Token selectErrorToken(ScannerError _err) { advance(); return setError(_err); } inline Token selectToken(Token _tok) { advance(); return _tok; } /// If the next character is _next, advance and return _then, otherwise return _else. inline Token selectToken(char _next, Token _then, Token _else); @@ -229,8 +230,8 @@ private: bool isUnicodeLinebreak(); /// Return the current source position. - int sourcePos() const { return m_source.position(); } - bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } + int sourcePos() const { return m_source->position(); } + bool isSourcePastEndOfInput() const { return m_source->isPastEndOfInput(); } TokenDesc m_skippedComment; // desc for current skipped comment TokenDesc m_nextSkippedComment; // desc for next skipped comment @@ -238,12 +239,10 @@ private: TokenDesc m_currentToken; // desc for current token (as returned by Next()) TokenDesc m_nextToken; // desc for next token (one token look-ahead) - CharStream m_source; - std::shared_ptr<std::string const> m_sourceName; + std::shared_ptr<CharStream> m_source; /// one character look-ahead, equals 0 at end of input char m_char; }; } -} diff --git a/libevmasm/SourceLocation.h b/liblangutil/SourceLocation.h index b42c3aa9..2d18a7d1 100644 --- a/libevmasm/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -22,13 +22,14 @@ #pragma once +#include <libdevcore/Common.h> // defines noexcept macro for MSVC +#include <liblangutil/CharStream.h> #include <memory> #include <string> #include <ostream> #include <tuple> -#include <libdevcore/Common.h> // defines noexcept macro for MSVC -namespace dev +namespace langutil { /** @@ -37,28 +38,13 @@ namespace dev */ struct SourceLocation { - SourceLocation(): start(-1), end(-1) { } - SourceLocation(int _start, int _end, std::shared_ptr<std::string const> _sourceName): - start(_start), end(_end), sourceName(_sourceName) { } - SourceLocation(SourceLocation&& _other) noexcept: - start(_other.start), - end(_other.end), - sourceName(std::move(_other.sourceName)) - {} - SourceLocation(SourceLocation const&) = default; - SourceLocation& operator=(SourceLocation const&) = default; - SourceLocation& operator=(SourceLocation&& _other) noexcept - { - start = _other.start; - end = _other.end; - sourceName = std::move(_other.sourceName); - return *this; - } + SourceLocation(): start(-1), end(-1), source{nullptr} { } + SourceLocation(int _start, int _end, std::shared_ptr<CharStream> _source): + start(_start), end(_end), source{std::move(_source)} { } bool operator==(SourceLocation const& _other) const { - return start == _other.start && end == _other.end && - ((!sourceName && !_other.sourceName) || (sourceName && _other.sourceName && *sourceName == *_other.sourceName)); + return source.get() == _other.source.get() && start == _other.start && end == _other.end; } bool operator!=(SourceLocation const& _other) const { return !operator==(_other); } inline bool operator<(SourceLocation const& _other) const; @@ -69,7 +55,7 @@ struct SourceLocation int start; int end; - std::shared_ptr<std::string const> sourceName; + std::shared_ptr<CharStream> source; }; /// Stream output for Location (used e.g. in boost exceptions). @@ -77,27 +63,33 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat { if (_location.isEmpty()) return _out << "NO_LOCATION_SPECIFIED"; - return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")"; + + if (_location.source) + _out << _location.source->name(); + + _out << "[" << _location.start << "," << _location.end << ")"; + + return _out; } bool SourceLocation::operator<(SourceLocation const& _other) const { - if (!sourceName || !_other.sourceName) - return std::make_tuple(int(!!sourceName), start, end) < std::make_tuple(int(!!_other.sourceName), _other.start, _other.end); + if (!source|| !_other.source) + return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end); else - return std::make_tuple(*sourceName, start, end) < std::make_tuple(*_other.sourceName, _other.start, _other.end); + return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end); } bool SourceLocation::contains(SourceLocation const& _other) const { - if (isEmpty() || _other.isEmpty() || ((!sourceName || !_other.sourceName || *sourceName != *_other.sourceName) && (sourceName || _other.sourceName))) + if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get()) return false; return start <= _other.start && _other.end <= end; } bool SourceLocation::intersects(SourceLocation const& _other) const { - if (isEmpty() || _other.isEmpty() || ((!sourceName || !_other.sourceName || *sourceName != *_other.sourceName) && (sourceName || _other.sourceName))) + if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get()) return false; return _other.start < end && start < _other.end; } diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/liblangutil/SourceReferenceFormatter.cpp index 865907e2..8ac05b4e 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/liblangutil/SourceReferenceFormatter.cpp @@ -20,22 +20,19 @@ * Formatting functions for errors referencing positions and locations in the source. */ -#include <libsolidity/interface/SourceReferenceFormatter.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/SourceReferenceFormatter.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/Exceptions.h> using namespace std; - -namespace dev -{ -namespace solidity -{ +using namespace dev; +using namespace langutil; void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location) { - if (!_location || !_location->sourceName) + if (!_location || !_location->source) return; // Nothing we can print here - auto const& scanner = m_scannerFromSourceName(*_location->sourceName); + auto const& scanner = m_scannerFromSourceName(_location->source->name()); int startLine; int startColumn; tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start); @@ -92,17 +89,17 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati void SourceReferenceFormatter::printSourceName(SourceLocation const* _location) { - if (!_location || !_location->sourceName) + if (!_location || !_location->source) return; // Nothing we can print here - auto const& scanner = m_scannerFromSourceName(*_location->sourceName); + auto const& scanner = m_scannerFromSourceName(_location->source->name()); int startLine; int startColumn; tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start); - m_stream << *_location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; + m_stream << _location->source->name() << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; } void SourceReferenceFormatter::printExceptionInformation( - Exception const& _exception, + dev::Exception const& _exception, string const& _name ) { @@ -130,6 +127,3 @@ void SourceReferenceFormatter::printExceptionInformation( m_stream << endl; } } - -} -} diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/liblangutil/SourceReferenceFormatter.h index a32babdc..0ef3ca00 100644 --- a/libsolidity/interface/SourceReferenceFormatter.h +++ b/liblangutil/SourceReferenceFormatter.h @@ -25,23 +25,21 @@ #include <ostream> #include <sstream> #include <functional> -#include <libevmasm/SourceLocation.h> namespace dev { - struct Exception; // forward +} -namespace solidity +namespace langutil { - -class Scanner; // forward -class CompilerStack; // forward +struct SourceLocation; +class Scanner; class SourceReferenceFormatter { public: - using ScannerFromSourceNameFun = std::function<Scanner const&(std::string const&)>; + using ScannerFromSourceNameFun = std::function<langutil::Scanner const&(std::string const&)>; explicit SourceReferenceFormatter( std::ostream& _stream, @@ -52,11 +50,11 @@ public: {} /// Prints source location if it is given. - void printSourceLocation(SourceLocation const* _location); - void printExceptionInformation(Exception const& _exception, std::string const& _name); + void printSourceLocation(langutil::SourceLocation const* _location); + void printExceptionInformation(dev::Exception const& _exception, std::string const& _name); static std::string formatExceptionInformation( - Exception const& _exception, + dev::Exception const& _exception, std::string const& _name, ScannerFromSourceNameFun const& _scannerFromSourceName ) @@ -69,11 +67,10 @@ public: } private: /// Prints source name if location is given. - void printSourceName(SourceLocation const* _location); + void printSourceName(langutil::SourceLocation const* _location); std::ostream& m_stream; ScannerFromSourceNameFun m_scannerFromSourceName; }; } -} diff --git a/libsolidity/parsing/Token.cpp b/liblangutil/Token.cpp index dccd9037..cbfd4a8c 100644 --- a/libsolidity/parsing/Token.cpp +++ b/liblangutil/Token.cpp @@ -40,15 +40,13 @@ // You should have received a copy of the GNU General Public License // along with solidity. If not, see <http://www.gnu.org/licenses/>. -#include <map> -#include <libsolidity/parsing/Token.h> +#include <liblangutil/Token.h> #include <boost/range/iterator_range.hpp> +#include <map> using namespace std; -namespace dev -{ -namespace solidity +namespace langutil { void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second) @@ -204,4 +202,3 @@ tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(string const& _ } } -} diff --git a/liblangutil/Token.h b/liblangutil/Token.h new file mode 100644 index 00000000..f832fdf7 --- /dev/null +++ b/liblangutil/Token.h @@ -0,0 +1,376 @@ +// Copyright 2006-2012, the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Modifications as part of solidity under the following license: +// +// solidity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// solidity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with solidity. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include <libdevcore/Common.h> +#include <liblangutil/Exceptions.h> +#include <liblangutil/UndefMacros.h> + +#include <iosfwd> +#include <string> +#include <tuple> + +namespace langutil +{ + +// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the +// same signature M(name, string, precedence), where name is the +// symbolic token name, string is the corresponding syntactic symbol +// (or nullptr, for literals), and precedence is the precedence (or 0). +// The parameters are invoked for token categories as follows: +// +// T: Non-keyword tokens +// K: Keyword tokens + +// IGNORE_TOKEN is a convenience macro that can be supplied as +// an argument (at any position) for a TOKEN_LIST call. It does +// nothing with tokens belonging to the respective category. + +#define IGNORE_TOKEN(name, string, precedence) + +#define TOKEN_LIST(T, K) \ + /* End of source indicator. */ \ + T(EOS, "EOS", 0) \ + \ + /* Punctuators (ECMA-262, section 7.7, page 15). */ \ + T(LParen, "(", 0) \ + T(RParen, ")", 0) \ + T(LBrack, "[", 0) \ + T(RBrack, "]", 0) \ + T(LBrace, "{", 0) \ + T(RBrace, "}", 0) \ + T(Colon, ":", 0) \ + T(Semicolon, ";", 0) \ + T(Period, ".", 0) \ + T(Conditional, "?", 3) \ + T(Arrow, "=>", 0) \ + \ + /* Assignment operators. */ \ + /* IsAssignmentOp() relies on this block of enum values being */ \ + /* contiguous and sorted in the same order!*/ \ + T(Assign, "=", 2) \ + /* The following have to be in exactly the same order as the simple binary operators*/ \ + T(AssignBitOr, "|=", 2) \ + T(AssignBitXor, "^=", 2) \ + T(AssignBitAnd, "&=", 2) \ + T(AssignShl, "<<=", 2) \ + T(AssignSar, ">>=", 2) \ + T(AssignShr, ">>>=", 2) \ + T(AssignAdd, "+=", 2) \ + T(AssignSub, "-=", 2) \ + T(AssignMul, "*=", 2) \ + T(AssignDiv, "/=", 2) \ + T(AssignMod, "%=", 2) \ + \ + /* Binary operators sorted by precedence. */ \ + /* IsBinaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(Comma, ",", 1) \ + T(Or, "||", 4) \ + T(And, "&&", 5) \ + T(BitOr, "|", 8) \ + T(BitXor, "^", 9) \ + T(BitAnd, "&", 10) \ + T(SHL, "<<", 11) \ + T(SAR, ">>", 11) \ + T(SHR, ">>>", 11) \ + T(Add, "+", 12) \ + T(Sub, "-", 12) \ + T(Mul, "*", 13) \ + T(Div, "/", 13) \ + T(Mod, "%", 13) \ + T(Exp, "**", 14) \ + \ + /* Compare operators sorted by precedence. */ \ + /* IsCompareOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(Equal, "==", 6) \ + T(NotEqual, "!=", 6) \ + T(LessThan, "<", 7) \ + T(GreaterThan, ">", 7) \ + T(LessThanOrEqual, "<=", 7) \ + T(GreaterThanOrEqual, ">=", 7) \ + \ + /* Unary operators. */ \ + /* IsUnaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(Not, "!", 0) \ + T(BitNot, "~", 0) \ + T(Inc, "++", 0) \ + T(Dec, "--", 0) \ + K(Delete, "delete", 0) \ + \ + /* Keywords */ \ + K(Anonymous, "anonymous", 0) \ + K(As, "as", 0) \ + K(Assembly, "assembly", 0) \ + K(Break, "break", 0) \ + K(Constant, "constant", 0) \ + K(Constructor, "constructor", 0) \ + K(Continue, "continue", 0) \ + K(Contract, "contract", 0) \ + K(Do, "do", 0) \ + K(Else, "else", 0) \ + K(Enum, "enum", 0) \ + K(Emit, "emit", 0) \ + K(Event, "event", 0) \ + K(External, "external", 0) \ + K(For, "for", 0) \ + K(Function, "function", 0) \ + K(Hex, "hex", 0) \ + K(If, "if", 0) \ + K(Indexed, "indexed", 0) \ + K(Interface, "interface", 0) \ + K(Internal, "internal", 0) \ + K(Import, "import", 0) \ + K(Is, "is", 0) \ + K(Library, "library", 0) \ + K(Mapping, "mapping", 0) \ + K(Memory, "memory", 0) \ + K(Modifier, "modifier", 0) \ + K(New, "new", 0) \ + K(Payable, "payable", 0) \ + K(Public, "public", 0) \ + K(Pragma, "pragma", 0) \ + K(Private, "private", 0) \ + K(Pure, "pure", 0) \ + K(Return, "return", 0) \ + K(Returns, "returns", 0) \ + K(Storage, "storage", 0) \ + K(CallData, "calldata", 0) \ + K(Struct, "struct", 0) \ + K(Throw, "throw", 0) \ + K(Using, "using", 0) \ + K(Var, "var", 0) \ + K(View, "view", 0) \ + K(While, "while", 0) \ + \ + /* Ether subdenominations */ \ + K(SubWei, "wei", 0) \ + K(SubSzabo, "szabo", 0) \ + K(SubFinney, "finney", 0) \ + K(SubEther, "ether", 0) \ + K(SubSecond, "seconds", 0) \ + K(SubMinute, "minutes", 0) \ + K(SubHour, "hours", 0) \ + K(SubDay, "days", 0) \ + K(SubWeek, "weeks", 0) \ + K(SubYear, "years", 0) \ + /* type keywords*/ \ + K(Int, "int", 0) \ + K(UInt, "uint", 0) \ + K(Bytes, "bytes", 0) \ + K(Byte, "byte", 0) \ + K(String, "string", 0) \ + K(Address, "address", 0) \ + K(Bool, "bool", 0) \ + K(Fixed, "fixed", 0) \ + K(UFixed, "ufixed", 0) \ + T(IntM, "intM", 0) \ + T(UIntM, "uintM", 0) \ + T(BytesM, "bytesM", 0) \ + T(FixedMxN, "fixedMxN", 0) \ + T(UFixedMxN, "ufixedMxN", 0) \ + T(TypesEnd, nullptr, 0) /* used as type enum end marker */ \ + \ + /* Literals */ \ + K(TrueLiteral, "true", 0) \ + K(FalseLiteral, "false", 0) \ + T(Number, nullptr, 0) \ + T(StringLiteral, nullptr, 0) \ + T(CommentLiteral, nullptr, 0) \ + \ + /* Identifiers (not keywords or future reserved words). */ \ + T(Identifier, nullptr, 0) \ + \ + /* Keywords reserved for future use. */ \ + K(Abstract, "abstract", 0) \ + K(After, "after", 0) \ + K(Alias, "alias", 0) \ + K(Apply, "apply", 0) \ + K(Auto, "auto", 0) \ + K(Case, "case", 0) \ + K(Catch, "catch", 0) \ + K(CopyOf, "copyof", 0) \ + K(Default, "default", 0) \ + K(Define, "define", 0) \ + K(Final, "final", 0) \ + K(Immutable, "immutable", 0) \ + K(Implements, "implements", 0) \ + K(In, "in", 0) \ + K(Inline, "inline", 0) \ + K(Let, "let", 0) \ + K(Macro, "macro", 0) \ + K(Match, "match", 0) \ + K(Mutable, "mutable", 0) \ + K(NullLiteral, "null", 0) \ + K(Of, "of", 0) \ + K(Override, "override", 0) \ + K(Partial, "partial", 0) \ + K(Promise, "promise", 0) \ + K(Reference, "reference", 0) \ + K(Relocatable, "relocatable", 0) \ + K(Sealed, "sealed", 0) \ + K(Sizeof, "sizeof", 0) \ + K(Static, "static", 0) \ + K(Supports, "supports", 0) \ + K(Switch, "switch", 0) \ + K(Try, "try", 0) \ + K(Type, "type", 0) \ + K(Typedef, "typedef", 0) \ + K(TypeOf, "typeof", 0) \ + K(Unchecked, "unchecked", 0) \ + \ + /* Illegal token - not able to scan. */ \ + T(Illegal, "ILLEGAL", 0) \ + \ + /* Scanner-internal use only. */ \ + T(Whitespace, nullptr, 0) + +// All token values. +// attention! msvc issue: +// http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 +// @todo: avoid TOKEN_LIST macro +enum class Token : unsigned int { +#define T(name, string, precedence) name, + TOKEN_LIST(T, T) + NUM_TOKENS +#undef T +}; + +namespace TokenTraits +{ + constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); } + + // Predicates + constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; } + constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; } + constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } + constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd || + op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; } + constexpr bool isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; } + constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; } + + constexpr bool isBitOp(Token op) { return (Token::BitOr <= op && op <= Token::BitAnd) || op == Token::BitNot; } + constexpr bool isBooleanOp(Token op) { return (Token::Or <= op && op <= Token::And) || op == Token::Not; } + constexpr bool isUnaryOp(Token op) { return (Token::Not <= op && op <= Token::Delete) || op == Token::Add || op == Token::Sub; } + constexpr bool isCountOp(Token op) { return op == Token::Inc || op == Token::Dec; } + constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); } + constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; } + constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; } + constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; } + + constexpr bool isStateMutabilitySpecifier(Token op, bool _allowConstant = true) + { + return (op == Token::Constant && _allowConstant) + || op == Token::Pure || op == Token::View || op == Token::Payable; + } + + constexpr bool isEtherSubdenomination(Token op) { return op == Token::SubWei || op == Token::SubSzabo || op == Token::SubFinney || op == Token::SubEther; } + constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; } + constexpr bool isReservedKeyword(Token op) { return (Token::Abstract <= op && op <= Token::Unchecked); } + + inline Token AssignmentToBinaryOp(Token op) + { + solAssert(isAssignmentOp(op) && op != Token::Assign, ""); + return static_cast<Token>(static_cast<int>(op) + (static_cast<int>(Token::BitOr) - static_cast<int>(Token::AssignBitOr))); + } + + // @returns the precedence > 0 for binary and compare + // operators; returns 0 otherwise. + int precedence(Token tok); + + std::tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal); + + // @returns a string corresponding to the C++ token name + // (e.g. "LT" for the token LT). + char const* name(Token tok); + + // @returns a string corresponding to the JS token string + // (.e., "<" for the token LT) or nullptr if the token doesn't + // have a (unique) string (e.g. an IDENTIFIER). + char const* toString(Token tok); + + std::string friendlyName(Token tok); +} + +inline std::ostream& operator<<(std::ostream& os, Token token) +{ + os << TokenTraits::friendlyName(token); + return os; +} + +class ElementaryTypeNameToken +{ +public: + ElementaryTypeNameToken(Token _token, unsigned const& _firstNumber, unsigned const& _secondNumber) + { + assertDetails(_token, _firstNumber, _secondNumber); + } + + unsigned int firstNumber() const { return m_firstNumber; } + unsigned int secondNumber() const { return m_secondNumber; } + Token token() const { return m_token; } + + ///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type + std::string toString(bool const& tokenValue = false) const + { + std::string name = TokenTraits::toString(m_token); + if (tokenValue || (firstNumber() == 0 && secondNumber() == 0)) + return name; + solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here."); + if (m_token == Token::FixedMxN || m_token == Token::UFixedMxN) + return name.substr(0, name.size() - 3) + std::to_string(m_firstNumber) + "x" + std::to_string(m_secondNumber); + else + return name.substr(0, name.size() - 1) + std::to_string(m_firstNumber); + } + +private: + Token m_token; + unsigned int m_firstNumber; + unsigned int m_secondNumber; + /// throws if type is not properly sized + void assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second); +}; + +} diff --git a/libsolidity/parsing/UndefMacros.h b/liblangutil/UndefMacros.h index d96e242e..d96e242e 100644 --- a/libsolidity/parsing/UndefMacros.h +++ b/liblangutil/UndefMacros.h diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt index 4cdc073a..9566c62f 100644 --- a/liblll/CMakeLists.txt +++ b/liblll/CMakeLists.txt @@ -1,5 +1,9 @@ -file(GLOB sources "*.cpp") -file(GLOB headers "*.h") +set(sources + CodeFragment.cpp + Compiler.cpp + CompilerState.cpp + Parser.cpp +) -add_library(lll ${sources} ${headers}) +add_library(lll ${sources}) target_link_libraries(lll PUBLIC evmasm devcore) diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index f37cb8b9..b32f14e9 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -259,6 +259,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) } else if (us == "SET") { + // TODO: move this to be a stack variable (and not a memory variable) if (_t.size() != 3) error<IncorrectParameterCount>(us); int c = 0; @@ -268,6 +269,15 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) m_asm.append((u256)varAddress(firstAsString(), true)); m_asm.append(Instruction::MSTORE); } + else if (us == "UNSET") + { + // TODO: this doesn't actually free up anything, since it is a memory variable (see "SET") + if (_t.size() != 2) + error<IncorrectParameterCount>(); + auto it = _s.vars.find(firstAsString()); + if (it != _s.vars.end()) + _s.vars.erase(it); + } else if (us == "GET") { if (_t.size() != 2) @@ -275,6 +285,35 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) m_asm.append((u256)varAddress(firstAsString())); m_asm.append(Instruction::MLOAD); } + else if (us == "WITH") + { + if (_t.size() != 4) + error<IncorrectParameterCount>(); + string key = firstAsString(); + if (_s.vars.find(key) != _s.vars.end()) + error<InvalidName>(string("Symbol already used: ") + key); + + // Create variable + // TODO: move this to be a stack variable (and not a memory variable) + size_t c = 0; + for (auto const& i: _t) + if (c++ == 2) + m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm); + m_asm.append((u256)varAddress(key, true)); + m_asm.append(Instruction::MSTORE); + + // Insert sub with variable access, but new state + CompilerState ns = _s; + c = 0; + for (auto const& i: _t) + if (c++ == 3) + m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm); + + // Remove variable + auto it = _s.vars.find(key); + if (it != _s.vars.end()) + _s.vars.erase(it); + } else if (us == "REF") m_asm.append((u256)varAddress(firstAsString())); else if (us == "DEF") diff --git a/liblll/Compiler.h b/liblll/Compiler.h index 1ff7d5f8..93235cdd 100644 --- a/liblll/Compiler.h +++ b/liblll/Compiler.h @@ -23,7 +23,7 @@ #include <libdevcore/Common.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <string> #include <vector> diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 136d39b1..dc4c6d15 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -1,14 +1,60 @@ # Until we have a clear separation, libyul has to be included here -file(GLOB_RECURSE sources "*.cpp" "../libyul/*.cpp") -file(GLOB_RECURSE headers "*.h" "../libyul/*.h") +set(sources + analysis/ConstantEvaluator.cpp + analysis/ContractLevelChecker.cpp + analysis/ControlFlowAnalyzer.cpp + analysis/ControlFlowBuilder.cpp + analysis/ControlFlowGraph.cpp + analysis/DeclarationContainer.cpp + analysis/DocStringAnalyser.cpp + analysis/GlobalContext.cpp + analysis/NameAndTypeResolver.cpp + analysis/PostTypeChecker.cpp + analysis/ReferencesResolver.cpp + analysis/SemVerHandler.cpp + analysis/StaticAnalyzer.cpp + analysis/SyntaxChecker.cpp + analysis/TypeChecker.cpp + analysis/ViewPureChecker.cpp + ast/AST.cpp + ast/ASTAnnotations.cpp + ast/ASTJsonConverter.cpp + ast/ASTPrinter.cpp + ast/Types.cpp + codegen/ABIFunctions.cpp + codegen/ArrayUtils.cpp + codegen/Compiler.cpp + codegen/CompilerContext.cpp + codegen/CompilerUtils.cpp + codegen/ContractCompiler.cpp + codegen/ExpressionCompiler.cpp + codegen/LValue.cpp + formal/SMTChecker.cpp + formal/SMTLib2Interface.cpp + formal/SMTPortfolio.cpp + formal/SSAVariable.cpp + formal/SymbolicTypes.cpp + formal/SymbolicVariables.cpp + formal/VariableUsage.cpp + interface/ABI.cpp + interface/AssemblyStack.cpp + interface/CompilerStack.cpp + interface/GasEstimator.cpp + interface/Natspec.cpp + interface/StandardCompiler.cpp + interface/Version.cpp + parsing/DocStringParser.cpp + parsing/Parser.cpp +) find_package(Z3 QUIET) if (${Z3_FOUND}) include_directories(${Z3_INCLUDE_DIR}) add_definitions(-DHAVE_Z3) message("Z3 SMT solver found. This enables optional SMT checking with Z3.") + set(z3_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp") else() - list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp") + set(z3_SRCS) endif() find_package(CVC4 QUIET) @@ -16,8 +62,9 @@ if (${CVC4_FOUND}) include_directories(${CVC4_INCLUDE_DIR}) add_definitions(-DHAVE_CVC4) message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.") + set(cvc4_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp") else() - list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp") + set(cvc4_SRCS) endif() if (NOT (${Z3_FOUND} OR ${CVC4_FOUND})) @@ -25,8 +72,8 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND})) \nPlease install Z3 or CVC4 or remove the option disabling them (USE_Z3, USE_CVC4).") endif() -add_library(solidity ${sources} ${headers}) -target_link_libraries(solidity PUBLIC evmasm devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}) +add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS}) +target_link_libraries(solidity PUBLIC yul evmasm langutil devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}) if (${Z3_FOUND}) target_link_libraries(solidity PUBLIC ${Z3_LIBRARY}) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index f9b00927..9d041ce5 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -22,7 +22,7 @@ #include <libsolidity/analysis/ConstantEvaluator.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> using namespace std; using namespace dev; diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index ac3a24a1..23ca3628 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -24,12 +24,16 @@ #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; class TypeChecker; /** @@ -39,7 +43,7 @@ class ConstantEvaluator: private ASTConstVisitor { public: ConstantEvaluator( - ErrorReporter& _errorReporter, + langutil::ErrorReporter& _errorReporter, size_t _newDepth = 0, std::shared_ptr<std::map<ASTNode const*, TypePointer>> _types = std::make_shared<std::map<ASTNode const*, TypePointer>>() ): @@ -61,7 +65,7 @@ private: void setType(ASTNode const& _node, TypePointer const& _type); TypePointer type(ASTNode const& _node); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; /// Current recursion depth. size_t m_depth = 0; std::shared_ptr<std::map<ASTNode const*, TypePointer>> m_types; diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp new file mode 100644 index 00000000..6dc564de --- /dev/null +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -0,0 +1,463 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Component that verifies overloads, abstract contracts, function clashes and others + * checks at contract or function level. + */ + +#include <libsolidity/analysis/ContractLevelChecker.h> +#include <libsolidity/ast/AST.h> + +#include <liblangutil/ErrorReporter.h> + +#include <boost/range/adaptor/reversed.hpp> + + +using namespace std; +using namespace dev; +using namespace langutil; +using namespace dev::solidity; + + +bool ContractLevelChecker::check(ContractDefinition const& _contract) +{ + checkDuplicateFunctions(_contract); + checkDuplicateEvents(_contract); + checkIllegalOverrides(_contract); + checkAbstractFunctions(_contract); + checkBaseConstructorArguments(_contract); + checkConstructor(_contract); + checkFallbackFunction(_contract); + checkExternalTypeClashes(_contract); + checkHashCollisions(_contract); + checkLibraryRequirements(_contract); + + return Error::containsOnlyWarnings(m_errorReporter.errors()); +} + +void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _contract) +{ + /// Checks that two functions with the same name defined in this contract have different + /// argument types and that there is at most one constructor. + map<string, vector<FunctionDefinition const*>> functions; + FunctionDefinition const* constructor = nullptr; + FunctionDefinition const* fallback = nullptr; + for (FunctionDefinition const* function: _contract.definedFunctions()) + if (function->isConstructor()) + { + if (constructor) + m_errorReporter.declarationError( + function->location(), + SecondarySourceLocation().append("Another declaration is here:", constructor->location()), + "More than one constructor defined." + ); + constructor = function; + } + else if (function->isFallback()) + { + if (fallback) + m_errorReporter.declarationError( + function->location(), + SecondarySourceLocation().append("Another declaration is here:", fallback->location()), + "Only one fallback function is allowed." + ); + fallback = function; + } + else + { + solAssert(!function->name().empty(), ""); + functions[function->name()].push_back(function); + } + + findDuplicateDefinitions(functions, "Function with same name and arguments defined twice."); +} + +void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract) +{ + /// Checks that two events with the same name defined in this contract have different + /// argument types + map<string, vector<EventDefinition const*>> events; + for (EventDefinition const* event: _contract.events()) + events[event->name()].push_back(event); + + findDuplicateDefinitions(events, "Event with same name and arguments defined twice."); +} + +template <class T> +void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message) +{ + for (auto const& it: _definitions) + { + vector<T> const& overloads = it.second; + set<size_t> reported; + for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) + { + SecondarySourceLocation ssl; + + for (size_t j = i + 1; j < overloads.size(); ++j) + if (FunctionType(*overloads[i]).asCallableFunction(false)->hasEqualParameterTypes( + *FunctionType(*overloads[j]).asCallableFunction(false)) + ) + { + ssl.append("Other declaration is here:", overloads[j]->location()); + reported.insert(j); + } + + if (ssl.infos.size() > 0) + { + ssl.limitSize(_message); + + m_errorReporter.declarationError( + overloads[i]->location(), + ssl, + _message + ); + } + } + } +} + +void ContractLevelChecker::checkIllegalOverrides(ContractDefinition const& _contract) +{ + // TODO unify this at a later point. for this we need to put the constness and the access specifier + // into the types + map<string, vector<FunctionDefinition const*>> functions; + map<string, ModifierDefinition const*> modifiers; + + // We search from derived to base, so the stored item causes the error. + for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) + { + for (FunctionDefinition const* function: contract->definedFunctions()) + { + if (function->isConstructor()) + continue; // constructors can neither be overridden nor override anything + string const& name = function->name(); + if (modifiers.count(name)) + m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier."); + + for (FunctionDefinition const* overriding: functions[name]) + checkFunctionOverride(*overriding, *function); + + functions[name].push_back(function); + } + for (ModifierDefinition const* modifier: contract->functionModifiers()) + { + string const& name = modifier->name(); + ModifierDefinition const*& override = modifiers[name]; + if (!override) + override = modifier; + else if (ModifierType(*override) != ModifierType(*modifier)) + m_errorReporter.typeError(override->location(), "Override changes modifier signature."); + if (!functions[name].empty()) + m_errorReporter.typeError(override->location(), "Override changes modifier to function."); + } + } +} + +void ContractLevelChecker::checkFunctionOverride(FunctionDefinition const& _function, FunctionDefinition const& _super) +{ + FunctionTypePointer functionType = FunctionType(_function).asCallableFunction(false); + FunctionTypePointer superType = FunctionType(_super).asCallableFunction(false); + + if (!functionType->hasEqualParameterTypes(*superType)) + return; + if (!functionType->hasEqualReturnTypes(*superType)) + overrideError(_function, _super, "Overriding function return types differ."); + + if (!_function.annotation().superFunction) + _function.annotation().superFunction = &_super; + + if (_function.visibility() != _super.visibility()) + { + // Visibility change from external to public is fine. + // Any other change is disallowed. + if (!( + _super.visibility() == FunctionDefinition::Visibility::External && + _function.visibility() == FunctionDefinition::Visibility::Public + )) + overrideError(_function, _super, "Overriding function visibility differs."); + } + if (_function.stateMutability() != _super.stateMutability()) + overrideError( + _function, + _super, + "Overriding function changes state mutability from \"" + + stateMutabilityToString(_super.stateMutability()) + + "\" to \"" + + stateMutabilityToString(_function.stateMutability()) + + "\"." + ); +} + +void ContractLevelChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message) +{ + m_errorReporter.typeError( + function.location(), + SecondarySourceLocation().append("Overridden function is here:", super.location()), + message + ); +} + +void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _contract) +{ + // Mapping from name to function definition (exactly one per argument type equality class) and + // flag to indicate whether it is fully implemented. + using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>; + map<string, vector<FunTypeAndFlag>> functions; + + auto registerFunction = [&](Declaration const& _declaration, FunctionTypePointer const& _type, bool _implemented) + { + auto& overloads = functions[_declaration.name()]; + auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) + { + return _type->hasEqualParameterTypes(*_funAndFlag.first); + }); + if (it == overloads.end()) + overloads.push_back(make_pair(_type, _implemented)); + else if (it->second) + { + if (!_implemented) + m_errorReporter.typeError(_declaration.location(), "Redeclaring an already implemented function as abstract"); + } + else if (_implemented) + it->second = true; + }; + + // Search from base to derived, collect all functions and update + // the 'implemented' flag. + for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) + { + for (VariableDeclaration const* v: contract->stateVariables()) + if (v->isPartOfExternalInterface()) + registerFunction(*v, make_shared<FunctionType>(*v), true); + + for (FunctionDefinition const* function: contract->definedFunctions()) + if (!function->isConstructor()) + registerFunction( + *function, + make_shared<FunctionType>(*function)->asCallableFunction(false), + function->isImplemented() + ); + } + + // Set to not fully implemented if at least one flag is false. + for (auto const& it: functions) + for (auto const& funAndFlag: it.second) + if (!funAndFlag.second) + { + FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration()); + solAssert(function, ""); + _contract.annotation().unimplementedFunctions.push_back(function); + break; + } +} + + +void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition const& _contract) +{ + vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; + + // Determine the arguments that are used for the base constructors. + for (ContractDefinition const* contract: bases) + { + if (FunctionDefinition const* constructor = contract->constructor()) + for (auto const& modifier: constructor->modifiers()) + if (auto baseContract = dynamic_cast<ContractDefinition const*>( + modifier->name()->annotation().referencedDeclaration + )) + { + if (modifier->arguments()) + { + if (baseContract->constructor()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); + } + else + m_errorReporter.declarationError( + modifier->location(), + "Modifier-style base constructor call without arguments." + ); + } + + for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) + { + ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( + base->name().annotation().referencedDeclaration + ); + solAssert(baseContract, ""); + + if (baseContract->constructor() && base->arguments() && !base->arguments()->empty()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get()); + } + } + + // check that we get arguments for all base constructors that need it. + // If not mark the contract as abstract (not fully implemented) + for (ContractDefinition const* contract: bases) + if (FunctionDefinition const* constructor = contract->constructor()) + if (contract != &_contract && !constructor->parameters().empty()) + if (!_contract.annotation().baseConstructorArguments.count(constructor)) + _contract.annotation().unimplementedFunctions.push_back(constructor); +} + +void ContractLevelChecker::annotateBaseConstructorArguments( + ContractDefinition const& _currentContract, + FunctionDefinition const* _baseConstructor, + ASTNode const* _argumentNode +) +{ + solAssert(_baseConstructor, ""); + solAssert(_argumentNode, ""); + + auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert( + std::make_pair(_baseConstructor, _argumentNode) + ); + if (!insertionResult.second) + { + ASTNode const* previousNode = insertionResult.first->second; + + SourceLocation const* mainLocation = nullptr; + SecondarySourceLocation ssl; + + if ( + _currentContract.location().contains(previousNode->location()) || + _currentContract.location().contains(_argumentNode->location()) + ) + { + mainLocation = &previousNode->location(); + ssl.append("Second constructor call is here:", _argumentNode->location()); + } + else + { + mainLocation = &_currentContract.location(); + ssl.append("First constructor call is here: ", _argumentNode->location()); + ssl.append("Second constructor call is here: ", previousNode->location()); + } + + m_errorReporter.declarationError( + *mainLocation, + ssl, + "Base constructor arguments given twice." + ); + } + +} + +void ContractLevelChecker::checkConstructor(ContractDefinition const& _contract) +{ + FunctionDefinition const* constructor = _contract.constructor(); + if (!constructor) + return; + + if (!constructor->returnParameters().empty()) + m_errorReporter.typeError(constructor->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); + if (constructor->stateMutability() != StateMutability::NonPayable && constructor->stateMutability() != StateMutability::Payable) + m_errorReporter.typeError( + constructor->location(), + "Constructor must be payable or non-payable, but is \"" + + stateMutabilityToString(constructor->stateMutability()) + + "\"." + ); + if (constructor->visibility() != FunctionDefinition::Visibility::Public && constructor->visibility() != FunctionDefinition::Visibility::Internal) + m_errorReporter.typeError(constructor->location(), "Constructor must be public or internal."); +} + +void ContractLevelChecker::checkFallbackFunction(ContractDefinition const& _contract) +{ + FunctionDefinition const* fallback = _contract.fallbackFunction(); + if (!fallback) + return; + + if (_contract.isLibrary()) + m_errorReporter.typeError(fallback->location(), "Libraries cannot have fallback functions."); + if (fallback->stateMutability() != StateMutability::NonPayable && fallback->stateMutability() != StateMutability::Payable) + m_errorReporter.typeError( + fallback->location(), + "Fallback function must be payable or non-payable, but is \"" + + stateMutabilityToString(fallback->stateMutability()) + + "\"." + ); + if (!fallback->parameters().empty()) + m_errorReporter.typeError(fallback->parameterList().location(), "Fallback function cannot take parameters."); + if (!fallback->returnParameters().empty()) + m_errorReporter.typeError(fallback->returnParameterList()->location(), "Fallback function cannot return values."); + if (fallback->visibility() != FunctionDefinition::Visibility::External) + m_errorReporter.typeError(fallback->location(), "Fallback function must be defined as \"external\"."); +} + +void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _contract) +{ + map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations; + for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) + { + for (FunctionDefinition const* f: contract->definedFunctions()) + if (f->isPartOfExternalInterface()) + { + auto functionType = make_shared<FunctionType>(*f); + // under non error circumstances this should be true + if (functionType->interfaceFunctionType()) + externalDeclarations[functionType->externalSignature()].push_back( + make_pair(f, functionType->asCallableFunction(false)) + ); + } + for (VariableDeclaration const* v: contract->stateVariables()) + if (v->isPartOfExternalInterface()) + { + auto functionType = make_shared<FunctionType>(*v); + // under non error circumstances this should be true + if (functionType->interfaceFunctionType()) + externalDeclarations[functionType->externalSignature()].push_back( + make_pair(v, functionType->asCallableFunction(false)) + ); + } + } + for (auto const& it: externalDeclarations) + for (size_t i = 0; i < it.second.size(); ++i) + for (size_t j = i + 1; j < it.second.size(); ++j) + if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second)) + m_errorReporter.typeError( + it.second[j].first->location(), + "Function overload clash during conversion to external types for arguments." + ); +} + +void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contract) +{ + set<FixedHash<4>> hashes; + for (auto const& it: _contract.interfaceFunctionList()) + { + FixedHash<4> const& hash = it.first; + if (hashes.count(hash)) + m_errorReporter.typeError( + _contract.location(), + string("Function signature hash collision for ") + it.second->externalSignature() + ); + hashes.insert(hash); + } +} + +void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract) +{ + if (!_contract.isLibrary()) + return; + + if (!_contract.baseContracts().empty()) + m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit."); + + for (auto const& var: _contract.stateVariables()) + if (!var->isConstant()) + m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables"); +} diff --git a/libsolidity/analysis/ContractLevelChecker.h b/libsolidity/analysis/ContractLevelChecker.h new file mode 100644 index 00000000..15cbf45d --- /dev/null +++ b/libsolidity/analysis/ContractLevelChecker.h @@ -0,0 +1,87 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Component that verifies overloads, abstract contracts, function clashes and others + * checks at contract or function level. + */ + +#pragma once + +#include <libsolidity/ast/ASTForward.h> + +#include <map> + +namespace langutil +{ +class ErrorReporter; +} + +namespace dev +{ +namespace solidity +{ + +/** + * Component that verifies overloads, abstract contracts, function clashes and others + * checks at contract or function level. + */ +class ContractLevelChecker +{ +public: + /// @param _errorReporter provides the error logging functionality. + explicit ContractLevelChecker(langutil::ErrorReporter& _errorReporter): + m_errorReporter(_errorReporter) + {} + + /// Performs checks on the given contract. + /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings + bool check(ContractDefinition const& _contract); + +private: + /// Checks that two functions defined in this contract with the same name have different + /// arguments and that there is at most one constructor. + void checkDuplicateFunctions(ContractDefinition const& _contract); + void checkDuplicateEvents(ContractDefinition const& _contract); + template <class T> + void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message); + void checkIllegalOverrides(ContractDefinition const& _contract); + /// Reports a type error with an appropriate message if overridden function signature differs. + /// Also stores the direct super function in the AST annotations. + void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); + void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); + void checkAbstractFunctions(ContractDefinition const& _contract); + void checkBaseConstructorArguments(ContractDefinition const& _contract); + void annotateBaseConstructorArguments( + ContractDefinition const& _currentContract, + FunctionDefinition const* _baseConstructor, + ASTNode const* _argumentNode + ); + void checkConstructor(ContractDefinition const& _contract); + void checkFallbackFunction(ContractDefinition const& _contract); + /// Checks that different functions with external visibility end up having different + /// external argument types (i.e. different signature). + void checkExternalTypeClashes(ContractDefinition const& _contract); + /// Checks for hash collisions in external function signatures. + void checkHashCollisions(ContractDefinition const& _contract); + /// Checks that all requirements for a library are fulfilled if this is a library. + void checkLibraryRequirements(ContractDefinition const& _contract); + + langutil::ErrorReporter& m_errorReporter; +}; + +} +} diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 8a608552..fe58f0aa 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -16,8 +16,10 @@ */ #include <libsolidity/analysis/ControlFlowAnalyzer.h> +#include <liblangutil/SourceLocation.h> using namespace std; +using namespace langutil; using namespace dev::solidity; bool ControlFlowAnalyzer::analyze(ASTNode const& _astRoot) diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h index 43e13fb6..411d57ff 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.h +++ b/libsolidity/analysis/ControlFlowAnalyzer.h @@ -29,12 +29,12 @@ namespace solidity class ControlFlowAnalyzer: private ASTConstVisitor { public: - explicit ControlFlowAnalyzer(CFG const& _cfg, ErrorReporter& _errorReporter): + explicit ControlFlowAnalyzer(CFG const& _cfg, langutil::ErrorReporter& _errorReporter): m_cfg(_cfg), m_errorReporter(_errorReporter) {} bool analyze(ASTNode const& _astRoot); - virtual bool visit(FunctionDefinition const& _function) override; + bool visit(FunctionDefinition const& _function) override; private: static std::set<VariableDeclaration const*> variablesAssignedInNode(CFGNode const *node); @@ -45,7 +45,7 @@ private: ) const; CFG const& m_cfg; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index e9d96e5f..40605e00 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -46,19 +46,19 @@ public: private: explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow); - virtual bool visit(BinaryOperation const& _operation) override; - virtual bool visit(Conditional const& _conditional) override; - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual bool visit(Break const&) override; - virtual bool visit(Continue const&) override; - virtual bool visit(Throw const&) override; - virtual bool visit(Block const&) override; - virtual void endVisit(Block const&) override; - virtual bool visit(Return const& _return) override; - virtual bool visit(PlaceholderStatement const&) override; - virtual bool visit(FunctionCall const& _functionCall) override; + bool visit(BinaryOperation const& _operation) override; + bool visit(Conditional const& _conditional) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(ForStatement const& _forStatement) override; + bool visit(WhileStatement const& _whileStatement) override; + bool visit(Break const&) override; + bool visit(Continue const&) override; + bool visit(Throw const&) override; + bool visit(Block const&) override; + void endVisit(Block const&) override; + bool visit(Return const& _return) override; + bool visit(PlaceholderStatement const&) override; + bool visit(FunctionCall const& _functionCall) override; /// Appends the control flow of @a _node to the current control flow. @@ -74,7 +74,7 @@ private: protected: - virtual bool visitNode(ASTNode const& node) override; + bool visitNode(ASTNode const& node) override; private: diff --git a/libsolidity/analysis/ControlFlowGraph.cpp b/libsolidity/analysis/ControlFlowGraph.cpp index 9b3da0eb..b8860158 100644 --- a/libsolidity/analysis/ControlFlowGraph.cpp +++ b/libsolidity/analysis/ControlFlowGraph.cpp @@ -23,6 +23,7 @@ #include <algorithm> using namespace std; +using namespace langutil; using namespace dev::solidity; bool CFG::constructFlow(ASTNode const& _astRoot) @@ -133,4 +134,4 @@ void CFG::applyModifierFlowToFunctionFlow( _functionFlow->entry = copySrcToCopyDst[_modifierFlow.entry]; _functionFlow->exit = copySrcToCopyDst[_modifierFlow.exit]; -}
\ No newline at end of file +} diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h index c646e4f1..8fe9fe8e 100644 --- a/libsolidity/analysis/ControlFlowGraph.h +++ b/libsolidity/analysis/ControlFlowGraph.h @@ -19,7 +19,7 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/ast/ASTVisitor.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <map> #include <memory> @@ -101,12 +101,12 @@ struct ModifierFlow: FunctionFlow class CFG: private ASTConstVisitor { public: - explicit CFG(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + explicit CFG(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool constructFlow(ASTNode const& _astRoot); - virtual bool visit(ModifierDefinition const& _modifier) override; - virtual bool visit(FunctionDefinition const& _function) override; + bool visit(ModifierDefinition const& _modifier) override; + bool visit(FunctionDefinition const& _function) override; FunctionFlow const& functionFlow(FunctionDefinition const& _function) const; @@ -133,7 +133,7 @@ private: FunctionFlow* _functionFlow ); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; /// Node container. /// All nodes allocated during the construction of the control flow graph diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index c1b97def..69a7a43c 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -23,11 +23,12 @@ #include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <libsolidity/parsing/DocStringParser.h> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit) diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index 5d339428..f6b236db 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -25,13 +25,16 @@ #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; - /** * Parses and analyses the doc strings. * Stores the parsing results in the AST annotations and reports errors. @@ -39,14 +42,14 @@ class ErrorReporter; class DocStringAnalyser: private ASTConstVisitor { public: - DocStringAnalyser(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + DocStringAnalyser(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool analyseDocStrings(SourceUnit const& _sourceUnit); private: - virtual bool visit(ContractDefinition const& _contract) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(ModifierDefinition const& _modifier) override; - virtual bool visit(EventDefinition const& _event) override; + bool visit(ContractDefinition const& _contract) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(ModifierDefinition const& _modifier) override; + bool visit(EventDefinition const& _event) override; void checkParameters( CallableDeclaration const& _callable, @@ -75,7 +78,7 @@ private: void appendError(std::string const& _description); bool m_errorOccured = false; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index b452a49a..0528a200 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -24,12 +24,13 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/TypeChecker.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <libdevcore/StringUtils.h> #include <boost/algorithm/string.hpp> using namespace std; +using namespace langutil; namespace dev { @@ -59,7 +60,7 @@ bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode { DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope); } - catch (FatalError const&) + catch (langutil::FatalError const&) { if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. @@ -129,7 +130,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi { return resolveNamesAndTypesInternal(_node, _resolveInsideCode); } - catch (FatalError const&) + catch (langutil::FatalError const&) { if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. @@ -144,7 +145,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) m_scopes[nullptr]->registerDeclaration(_declaration, nullptr, false, true); solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope."); } - catch (FatalError const&) + catch (langutil::FatalError const&) { if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. @@ -491,9 +492,9 @@ bool DeclarationRegistrationHelper::registerDeclaration( Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name); solAssert(conflictingDeclaration, ""); bool const comparable = - _errorLocation->sourceName && - conflictingDeclaration->location().sourceName && - *_errorLocation->sourceName == *conflictingDeclaration->location().sourceName; + _errorLocation->source && + conflictingDeclaration->location().source && + _errorLocation->source->name() == conflictingDeclaration->location().source->name(); if (comparable && _errorLocation->start < conflictingDeclaration->location().start) { firstDeclarationLocation = *_errorLocation; diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index a72c21e3..1b034ef4 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -30,13 +30,16 @@ #include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTAnnotations.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; - /** * Resolves name references, typenames and sets the (explicitly given) types for all variable * declarations. @@ -50,7 +53,7 @@ public: NameAndTypeResolver( std::vector<Declaration const*> const& _globals, std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, - ErrorReporter& _errorReporter + langutil::ErrorReporter& _errorReporter ); /// Registers all declarations found in the AST node, usually a source unit. /// @returns false in case of error. @@ -125,7 +128,7 @@ private: std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes; DeclarationContainer* m_currentScope = nullptr; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; /** @@ -142,7 +145,7 @@ public: DeclarationRegistrationHelper( std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, - ErrorReporter& _errorReporter, + langutil::ErrorReporter& _errorReporter, ASTNode const* _currentScope = nullptr ); @@ -150,10 +153,10 @@ public: DeclarationContainer& _container, Declaration const& _declaration, std::string const* _name, - SourceLocation const* _errorLocation, + langutil::SourceLocation const* _errorLocation, bool _warnOnShadow, bool _inactive, - ErrorReporter& _errorReporter + langutil::ErrorReporter& _errorReporter ); private: @@ -194,7 +197,7 @@ private: std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index 240d7973..27cbcd45 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -18,7 +18,7 @@ #include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/SemVerHandler.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <libsolidity/interface/Version.h> #include <libdevcore/Algorithms.h> @@ -29,6 +29,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index 4f9dac6e..e428b81a 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -23,13 +23,17 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + namespace dev { namespace solidity { -class ErrorReporter; - /** * This module performs analyses on the AST that are done after type checking and assignments of types: * - whether there are circular references in constant state variables @@ -39,25 +43,25 @@ class PostTypeChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - PostTypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool check(ASTNode const& _astRoot); private: /// Adds a new error to the list of errors. - void typeError(SourceLocation const& _location, std::string const& _description); + void typeError(langutil::SourceLocation const& _location, std::string const& _description); - virtual bool visit(ContractDefinition const& _contract) override; - virtual void endVisit(ContractDefinition const& _contract) override; + bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; - virtual bool visit(VariableDeclaration const& _variable) override; - virtual void endVisit(VariableDeclaration const& _variable) override; + bool visit(VariableDeclaration const& _variable) override; + void endVisit(VariableDeclaration const& _variable) override; - virtual bool visit(Identifier const& _identifier) override; + bool visit(Identifier const& _identifier) override; VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; VariableDeclaration const* m_currentConstVariable = nullptr; std::vector<VariableDeclaration const*> m_constVariables; ///< Required for determinism. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 2adc8e77..c4931d98 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -23,12 +23,12 @@ #include <libsolidity/analysis/ReferencesResolver.h> #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/NameAndTypeResolver.h> -#include <libsolidity/interface/Exceptions.h> #include <libsolidity/analysis/ConstantEvaluator.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/StringUtils.h> @@ -36,9 +36,12 @@ #include <boost/range/adaptor/transformed.hpp> using namespace std; -using namespace dev; -using namespace dev::solidity; +using namespace langutil; +namespace dev +{ +namespace solidity +{ bool ReferencesResolver::resolve(ASTNode const& _root) { @@ -270,7 +273,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) ErrorList errors; ErrorReporter errorsIgnored(errors); yul::ExternalIdentifierAccess::Resolver resolver = - [&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) { + [&](yul::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); @@ -311,9 +314,9 @@ 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. - assembly::AsmAnalysisInfo analysisInfo; + yul::AsmAnalysisInfo analysisInfo; boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError; - assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); + yul::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, yul::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } @@ -454,3 +457,6 @@ void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, m_errorOccurred = true; m_errorReporter.fatalDeclarationError(_location, _description); } + +} +} diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 24ec4643..32c0553f 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -28,12 +28,17 @@ #include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTAnnotations.h> +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + namespace dev { namespace solidity { -class ErrorReporter; class NameAndTypeResolver; /** @@ -44,7 +49,7 @@ class ReferencesResolver: private ASTConstVisitor { public: ReferencesResolver( - ErrorReporter& _errorReporter, + langutil::ErrorReporter& _errorReporter, NameAndTypeResolver& _resolver, bool _resolveInsideCode = false ): @@ -57,38 +62,38 @@ public: bool resolve(ASTNode const& _root); private: - virtual bool visit(Block const& _block) override; - virtual void endVisit(Block const& _block) override; - virtual bool visit(ForStatement const& _for) override; - virtual void endVisit(ForStatement const& _for) override; - virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; - virtual bool visit(Identifier const& _identifier) override; - virtual bool visit(ElementaryTypeName const& _typeName) override; - virtual bool visit(FunctionDefinition const& _functionDefinition) override; - virtual void endVisit(FunctionDefinition const& _functionDefinition) override; - virtual bool visit(ModifierDefinition const& _modifierDefinition) override; - virtual void endVisit(ModifierDefinition const& _modifierDefinition) override; - virtual void endVisit(UserDefinedTypeName const& _typeName) override; - virtual void endVisit(FunctionTypeName const& _typeName) override; - virtual void endVisit(Mapping const& _typeName) override; - virtual void endVisit(ArrayTypeName const& _typeName) override; - virtual bool visit(InlineAssembly const& _inlineAssembly) override; - virtual bool visit(Return const& _return) override; - virtual void endVisit(VariableDeclaration const& _variable) override; + bool visit(Block const& _block) override; + void endVisit(Block const& _block) override; + bool visit(ForStatement const& _for) override; + void endVisit(ForStatement const& _for) override; + void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; + bool visit(Identifier const& _identifier) override; + bool visit(ElementaryTypeName const& _typeName) override; + bool visit(FunctionDefinition const& _functionDefinition) override; + void endVisit(FunctionDefinition const& _functionDefinition) override; + bool visit(ModifierDefinition const& _modifierDefinition) override; + void endVisit(ModifierDefinition const& _modifierDefinition) override; + void endVisit(UserDefinedTypeName const& _typeName) override; + void endVisit(FunctionTypeName const& _typeName) override; + void endVisit(Mapping const& _typeName) override; + void endVisit(ArrayTypeName const& _typeName) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(Return const& _return) override; + void endVisit(VariableDeclaration const& _variable) override; /// Adds a new error to the list of errors. - void typeError(SourceLocation const& _location, std::string const& _description); + void typeError(langutil::SourceLocation const& _location, std::string const& _description); /// Adds a new error to the list of errors and throws to abort reference resolving. - void fatalTypeError(SourceLocation const& _location, std::string const& _description); + void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description); /// Adds a new error to the list of errors. - void declarationError(SourceLocation const& _location, std::string const& _description); + void declarationError(langutil::SourceLocation const& _location, std::string const& _description); /// Adds a new error to the list of errors and throws to abort reference resolving. - void fatalDeclarationError(SourceLocation const& _location, std::string const& _description); + void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; NameAndTypeResolver& m_resolver; /// Stack of return parameters. std::vector<ParameterList const*> m_returnParameters; diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h index 03a557c5..80185612 100644 --- a/libsolidity/analysis/SemVerHandler.h +++ b/libsolidity/analysis/SemVerHandler.h @@ -22,8 +22,9 @@ #pragma once -#include <vector> #include <libsolidity/parsing/Token.h> +#include <string> +#include <vector> namespace dev { diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 487a5cca..38391841 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -23,11 +23,12 @@ #include <libsolidity/analysis/StaticAnalyzer.h> #include <libsolidity/analysis/ConstantEvaluator.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <memory> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit) diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 7f5c743a..ff33fa3a 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -28,6 +28,11 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity @@ -44,7 +49,7 @@ class StaticAnalyzer: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - explicit StaticAnalyzer(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} /// Performs static analysis on the given source unit and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings @@ -52,25 +57,25 @@ public: private: - virtual bool visit(ContractDefinition const& _contract) override; - virtual void endVisit(ContractDefinition const& _contract) override; + bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual void endVisit(FunctionDefinition const& _function) override; + bool visit(FunctionDefinition const& _function) override; + void endVisit(FunctionDefinition const& _function) override; - virtual bool visit(ExpressionStatement const& _statement) override; - virtual bool visit(VariableDeclaration const& _variable) override; - virtual bool visit(Identifier const& _identifier) override; - virtual bool visit(Return const& _return) override; - virtual bool visit(MemberAccess const& _memberAccess) override; - virtual bool visit(InlineAssembly const& _inlineAssembly) override; - virtual bool visit(BinaryOperation const& _operation) override; - virtual bool visit(FunctionCall const& _functionCall) override; + bool visit(ExpressionStatement const& _statement) override; + bool visit(VariableDeclaration const& _variable) override; + bool visit(Identifier const& _identifier) override; + bool visit(Return const& _return) override; + bool visit(MemberAccess const& _memberAccess) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(BinaryOperation const& _operation) override; + bool visit(FunctionCall const& _functionCall) override; /// @returns the size of this type in storage, including all sub-types. static bigint structureSizeEstimate(Type const& _type, std::set<StructDefinition const*>& _structsSeen); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; /// Flag that indicates whether the current contract definition is a library. bool m_library = false; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 3f9f8373..a73d7e5c 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -20,7 +20,7 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/ast/ExperimentalFeatures.h> #include <libsolidity/analysis/SemVerHandler.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <libsolidity/interface/Version.h> #include <boost/algorithm/cxx11/all_of.hpp> @@ -29,6 +29,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index f5716bf9..c2463955 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -23,6 +23,11 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity @@ -39,49 +44,49 @@ class SyntaxChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - SyntaxChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + SyntaxChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool checkSyntax(ASTNode const& _astRoot); private: - virtual bool visit(SourceUnit const& _sourceUnit) override; - virtual void endVisit(SourceUnit const& _sourceUnit) override; - virtual bool visit(PragmaDirective const& _pragma) override; + bool visit(SourceUnit const& _sourceUnit) override; + void endVisit(SourceUnit const& _sourceUnit) override; + bool visit(PragmaDirective const& _pragma) override; - virtual bool visit(ModifierDefinition const& _modifier) override; - virtual void endVisit(ModifierDefinition const& _modifier) override; + bool visit(ModifierDefinition const& _modifier) override; + void endVisit(ModifierDefinition const& _modifier) override; /// Reports an error if _statement is a VariableDeclarationStatement. /// Used by if/while/for to check for single statement variable declarations /// without a block. void checkSingleStatementVariableDeclaration(ASTNode const& _statement); - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual void endVisit(WhileStatement const& _whileStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual void endVisit(ForStatement const& _forStatement) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(WhileStatement const& _whileStatement) override; + void endVisit(WhileStatement const& _whileStatement) override; + bool visit(ForStatement const& _forStatement) override; + void endVisit(ForStatement const& _forStatement) override; - virtual bool visit(Continue const& _continueStatement) override; - virtual bool visit(Break const& _breakStatement) override; + bool visit(Continue const& _continueStatement) override; + bool visit(Break const& _breakStatement) override; - virtual bool visit(Throw const& _throwStatement) override; + bool visit(Throw const& _throwStatement) override; - virtual bool visit(UnaryOperation const& _operation) override; + bool visit(UnaryOperation const& _operation) override; - virtual bool visit(PlaceholderStatement const& _placeholderStatement) override; + bool visit(PlaceholderStatement const& _placeholderStatement) override; - virtual bool visit(ContractDefinition const& _contract) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(FunctionTypeName const& _node) override; + bool visit(ContractDefinition const& _contract) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(FunctionTypeName const& _node) override; - virtual bool visit(VariableDeclarationStatement const& _statement) override; + bool visit(VariableDeclarationStatement const& _statement) override; - virtual bool visit(StructDefinition const& _struct) override; - virtual bool visit(Literal const& _literal) override; + bool visit(StructDefinition const& _struct) override; + bool visit(Literal const& _literal) override; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; /// Flag that indicates whether a function modifier actually contains '_'. bool m_placeholderFound = false; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c5e6488b..9350df05 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -21,20 +21,27 @@ */ #include <libsolidity/analysis/TypeChecker.h> -#include <memory> +#include <libsolidity/ast/AST.h> + +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> + +#include <liblangutil/ErrorReporter.h> + +#include <libdevcore/Algorithms.h> +#include <libdevcore/StringUtils.h> + #include <boost/algorithm/cxx11/all_of.hpp> #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/join.hpp> -#include <boost/range/adaptor/reversed.hpp> -#include <libsolidity/ast/AST.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/interface/ErrorReporter.h> -#include <libdevcore/Algorithms.h> + +#include <memory> +#include <vector> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace @@ -81,418 +88,14 @@ bool TypeChecker::visit(ContractDefinition const& _contract) { m_scope = &_contract; - // We force our own visiting order here. The structs have to be excluded below. - set<ASTNode const*> visited; - for (auto const& s: _contract.definedStructs()) - visited.insert(s); - ASTNode::listAccept(_contract.definedStructs(), *this); ASTNode::listAccept(_contract.baseContracts(), *this); - checkContractDuplicateFunctions(_contract); - checkContractDuplicateEvents(_contract); - checkContractIllegalOverrides(_contract); - checkContractAbstractFunctions(_contract); - checkContractBaseConstructorArguments(_contract); - - FunctionDefinition const* function = _contract.constructor(); - if (function) - { - if (!function->returnParameters().empty()) - m_errorReporter.typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); - if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable) - m_errorReporter.typeError( - function->location(), - "Constructor must be payable or non-payable, but is \"" + - stateMutabilityToString(function->stateMutability()) + - "\"." - ); - if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal) - m_errorReporter.typeError(function->location(), "Constructor must be public or internal."); - } - - for (FunctionDefinition const* function: _contract.definedFunctions()) - if (function->isFallback()) - { - if (_contract.isLibrary()) - m_errorReporter.typeError(function->location(), "Libraries cannot have fallback functions."); - if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable) - m_errorReporter.typeError( - function->location(), - "Fallback function must be payable or non-payable, but is \"" + - stateMutabilityToString(function->stateMutability()) + - "\"." - ); - if (!function->parameters().empty()) - m_errorReporter.typeError(function->parameterList().location(), "Fallback function cannot take parameters."); - if (!function->returnParameters().empty()) - m_errorReporter.typeError(function->returnParameterList()->location(), "Fallback function cannot return values."); - if (function->visibility() != FunctionDefinition::Visibility::External) - m_errorReporter.typeError(function->location(), "Fallback function must be defined as \"external\"."); - } - for (auto const& n: _contract.subNodes()) - if (!visited.count(n.get())) - n->accept(*this); - - checkContractExternalTypeClashes(_contract); - // check for hash collisions in function signatures - set<FixedHash<4>> hashes; - for (auto const& it: _contract.interfaceFunctionList()) - { - FixedHash<4> const& hash = it.first; - if (hashes.count(hash)) - m_errorReporter.typeError( - _contract.location(), - string("Function signature hash collision for ") + it.second->externalSignature() - ); - hashes.insert(hash); - } - - if (_contract.isLibrary()) - checkLibraryRequirements(_contract); + n->accept(*this); return false; } -void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract) -{ - /// Checks that two functions with the same name defined in this contract have different - /// argument types and that there is at most one constructor. - map<string, vector<FunctionDefinition const*>> functions; - FunctionDefinition const* constructor = nullptr; - FunctionDefinition const* fallback = nullptr; - for (FunctionDefinition const* function: _contract.definedFunctions()) - if (function->isConstructor()) - { - if (constructor) - m_errorReporter.declarationError( - function->location(), - SecondarySourceLocation().append("Another declaration is here:", constructor->location()), - "More than one constructor defined." - ); - constructor = function; - } - else if (function->isFallback()) - { - if (fallback) - m_errorReporter.declarationError( - function->location(), - SecondarySourceLocation().append("Another declaration is here:", fallback->location()), - "Only one fallback function is allowed." - ); - fallback = function; - } - else - { - solAssert(!function->name().empty(), ""); - functions[function->name()].push_back(function); - } - - findDuplicateDefinitions(functions, "Function with same name and arguments defined twice."); -} - -void TypeChecker::checkContractDuplicateEvents(ContractDefinition const& _contract) -{ - /// Checks that two events with the same name defined in this contract have different - /// argument types - map<string, vector<EventDefinition const*>> events; - for (EventDefinition const* event: _contract.events()) - events[event->name()].push_back(event); - - findDuplicateDefinitions(events, "Event with same name and arguments defined twice."); -} - -template <class T> -void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message) -{ - for (auto const& it: _definitions) - { - vector<T> const& overloads = it.second; - set<size_t> reported; - for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) - { - SecondarySourceLocation ssl; - - for (size_t j = i + 1; j < overloads.size(); ++j) - if (FunctionType(*overloads[i]).hasEqualParameterTypes(FunctionType(*overloads[j]))) - { - ssl.append("Other declaration is here:", overloads[j]->location()); - reported.insert(j); - } - - if (ssl.infos.size() > 0) - { - ssl.limitSize(_message); - - m_errorReporter.declarationError( - overloads[i]->location(), - ssl, - _message - ); - } - } - } -} - -void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _contract) -{ - // Mapping from name to function definition (exactly one per argument type equality class) and - // flag to indicate whether it is fully implemented. - using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>; - map<string, vector<FunTypeAndFlag>> functions; - - // Search from base to derived - for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) - for (FunctionDefinition const* function: contract->definedFunctions()) - { - // Take constructors out of overload hierarchy - if (function->isConstructor()) - continue; - auto& overloads = functions[function->name()]; - FunctionTypePointer funType = make_shared<FunctionType>(*function); - auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) - { - return funType->hasEqualParameterTypes(*_funAndFlag.first); - }); - if (it == overloads.end()) - overloads.push_back(make_pair(funType, function->isImplemented())); - else if (it->second) - { - if (!function->isImplemented()) - m_errorReporter.typeError(function->location(), "Redeclaring an already implemented function as abstract"); - } - else if (function->isImplemented()) - it->second = true; - } - - // Set to not fully implemented if at least one flag is false. - for (auto const& it: functions) - for (auto const& funAndFlag: it.second) - if (!funAndFlag.second) - { - FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration()); - solAssert(function, ""); - _contract.annotation().unimplementedFunctions.push_back(function); - break; - } -} - -void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract) -{ - vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; - - // Determine the arguments that are used for the base constructors. - for (ContractDefinition const* contract: bases) - { - if (FunctionDefinition const* constructor = contract->constructor()) - for (auto const& modifier: constructor->modifiers()) - if (auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name()))) - { - if (modifier->arguments()) - { - if (baseContract->constructor()) - annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); - } - else - m_errorReporter.declarationError( - modifier->location(), - "Modifier-style base constructor call without arguments." - ); - } - - for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) - { - auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name())); - solAssert(baseContract, ""); - - if (baseContract->constructor() && base->arguments() && !base->arguments()->empty()) - annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get()); - } - } - - // check that we get arguments for all base constructors that need it. - // If not mark the contract as abstract (not fully implemented) - for (ContractDefinition const* contract: bases) - if (FunctionDefinition const* constructor = contract->constructor()) - if (contract != &_contract && !constructor->parameters().empty()) - if (!_contract.annotation().baseConstructorArguments.count(constructor)) - _contract.annotation().unimplementedFunctions.push_back(constructor); -} - -void TypeChecker::annotateBaseConstructorArguments( - ContractDefinition const& _currentContract, - FunctionDefinition const* _baseConstructor, - ASTNode const* _argumentNode -) -{ - solAssert(_baseConstructor, ""); - solAssert(_argumentNode, ""); - - auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert( - std::make_pair(_baseConstructor, _argumentNode) - ); - if (!insertionResult.second) - { - ASTNode const* previousNode = insertionResult.first->second; - - SourceLocation const* mainLocation = nullptr; - SecondarySourceLocation ssl; - - if ( - _currentContract.location().contains(previousNode->location()) || - _currentContract.location().contains(_argumentNode->location()) - ) - { - mainLocation = &previousNode->location(); - ssl.append("Second constructor call is here:", _argumentNode->location()); - } - else - { - mainLocation = &_currentContract.location(); - ssl.append("First constructor call is here: ", _argumentNode->location()); - ssl.append("Second constructor call is here: ", previousNode->location()); - } - - m_errorReporter.declarationError( - *mainLocation, - ssl, - "Base constructor arguments given twice." - ); - } - -} - -void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract) -{ - // TODO unify this at a later point. for this we need to put the constness and the access specifier - // into the types - map<string, vector<FunctionDefinition const*>> functions; - map<string, ModifierDefinition const*> modifiers; - - // We search from derived to base, so the stored item causes the error. - for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) - { - for (FunctionDefinition const* function: contract->definedFunctions()) - { - if (function->isConstructor()) - continue; // constructors can neither be overridden nor override anything - string const& name = function->name(); - if (modifiers.count(name)) - m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier."); - - for (FunctionDefinition const* overriding: functions[name]) - checkFunctionOverride(*overriding, *function); - - functions[name].push_back(function); - } - for (ModifierDefinition const* modifier: contract->functionModifiers()) - { - string const& name = modifier->name(); - ModifierDefinition const*& override = modifiers[name]; - if (!override) - override = modifier; - else if (ModifierType(*override) != ModifierType(*modifier)) - m_errorReporter.typeError(override->location(), "Override changes modifier signature."); - if (!functions[name].empty()) - m_errorReporter.typeError(override->location(), "Override changes modifier to function."); - } - } -} - -void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super) -{ - FunctionType functionType(function); - FunctionType superType(super); - - if (!functionType.hasEqualParameterTypes(superType)) - return; - - if (!function.annotation().superFunction) - function.annotation().superFunction = &super; - - if (function.visibility() != super.visibility()) - { - // visibility is enforced to be external in interfaces, but a contract can override that with public - if ( - super.inContractKind() == ContractDefinition::ContractKind::Interface && - function.inContractKind() != ContractDefinition::ContractKind::Interface && - function.visibility() == FunctionDefinition::Visibility::Public - ) - return; - overrideError(function, super, "Overriding function visibility differs."); - } - - else if (function.stateMutability() != super.stateMutability()) - overrideError( - function, - super, - "Overriding function changes state mutability from \"" + - stateMutabilityToString(super.stateMutability()) + - "\" to \"" + - stateMutabilityToString(function.stateMutability()) + - "\"." - ); - - else if (functionType != superType) - overrideError(function, super, "Overriding function return types differ."); -} - -void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message) -{ - m_errorReporter.typeError( - function.location(), - SecondarySourceLocation().append("Overridden function is here:", super.location()), - message - ); -} - -void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract) -{ - map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations; - for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) - { - for (FunctionDefinition const* f: contract->definedFunctions()) - if (f->isPartOfExternalInterface()) - { - auto functionType = make_shared<FunctionType>(*f); - // under non error circumstances this should be true - if (functionType->interfaceFunctionType()) - externalDeclarations[functionType->externalSignature()].push_back( - make_pair(f, functionType) - ); - } - for (VariableDeclaration const* v: contract->stateVariables()) - if (v->isPartOfExternalInterface()) - { - auto functionType = make_shared<FunctionType>(*v); - // under non error circumstances this should be true - if (functionType->interfaceFunctionType()) - externalDeclarations[functionType->externalSignature()].push_back( - make_pair(v, functionType) - ); - } - } - for (auto const& it: externalDeclarations) - for (size_t i = 0; i < it.second.size(); ++i) - for (size_t j = i + 1; j < it.second.size(); ++j) - if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second)) - m_errorReporter.typeError( - it.second[j].first->location(), - "Function overload clash during conversion to external types for arguments." - ); -} - -void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract) -{ - solAssert(_contract.isLibrary(), ""); - if (!_contract.baseContracts().empty()) - m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit."); - - for (auto const& var: _contract.stateVariables()) - if (!var->isConstant()) - m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables"); -} - void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment) { TupleType const& lhs = dynamic_cast<TupleType const&>(*type(_assignment.leftHandSide())); @@ -697,20 +300,22 @@ bool TypeChecker::visit(FunctionDefinition const& _function) } for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) { + if (type(*var)->category() == Type::Category::Mapping) + { + if (!type(*var)->dataStoredIn(DataLocation::Storage)) + m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"." ); + else if (!isLibraryFunction && _function.isPublic()) + m_errorReporter.typeError(var->location(), "Mapping types for parameters or return variables can only be used in internal or library functions."); + } + else + { + if (!type(*var)->canLiveOutsideStorage() && _function.isPublic()) + m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); + if (_function.isPublic() && !(type(*var)->interfaceType(isLibraryFunction))) + m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); + } if ( - type(*var)->category() == Type::Category::Mapping && - !type(*var)->dataStoredIn(DataLocation::Storage) - ) - m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"."); - else if ( - !type(*var)->canLiveOutsideStorage() && - _function.visibility() > FunctionDefinition::Visibility::Internal - ) - m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); - if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) - m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); - if ( - _function.visibility() > FunctionDefinition::Visibility::Internal && + _function.isPublic() && !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && !typeSupportedByOldABIEncoder(*type(*var)) ) @@ -775,15 +380,10 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) ) m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); - // Variables can be declared without type (with "var"), in which case the first assignment - // sets the type. - // Note that assignments before the first declaration are legal because of the special scoping - // rules inherited from JavaScript. - // type is filled either by ReferencesResolver directly from the type name or by // TypeChecker at the VariableDeclarationStatement level. TypePointer varType = _variable.annotation().type; - solAssert(!!varType, "Failed to infer variable type."); + solAssert(!!varType, "Variable type not provided."); if (_variable.value()) expectType(*_variable.value(), *varType); @@ -812,11 +412,25 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!varType->canLiveOutsideStorage()) m_errorReporter.typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage."); } - else if ( - _variable.visibility() >= VariableDeclaration::Visibility::Public && - !FunctionType(_variable).interfaceFunctionType() - ) - m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); + else if (_variable.visibility() >= VariableDeclaration::Visibility::Public) + { + FunctionType getter(_variable); + if (!_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)) + { + vector<string> unsupportedTypes; + for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes()) + if (!typeSupportedByOldABIEncoder(*param)) + unsupportedTypes.emplace_back(param->toString()); + if (!unsupportedTypes.empty()) + m_errorReporter.typeError(_variable.location(), + "The following types are only supported for getters in the new experimental ABI encoder: " + + joinHumanReadable(unsupportedTypes) + + ". Either remove \"public\" or use \"pragma experimental ABIEncoderV2;\" to enable the feature." + ); + } + if (!getter.interfaceFunctionType()) + m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); + } switch (varType->category()) { @@ -953,7 +567,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) // External references have already been resolved in a prior stage and stored in the annotation. // We run the resolve step again regardless. yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( - assembly::Identifier const& _identifier, + yul::Identifier const& _identifier, yul::IdentifierContext _context, bool ) @@ -1038,13 +652,13 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return size_t(1); }; solAssert(!_inlineAssembly.annotation().analysisInfo, ""); - _inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); - assembly::AsmAnalyzer analyzer( + _inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>(); + yul::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, m_evmVersion, Error::Type::SyntaxError, - assembly::AsmFlavour::Loose, + yul::AsmFlavour::Loose, identifierAccess ); if (!analyzer.analyze(_inlineAssembly.operations())) @@ -1590,6 +1204,9 @@ bool TypeChecker::visit(TupleExpression const& _tuple) { if (!inlineArrayType) m_errorReporter.fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements."); + else if (!inlineArrayType->canLiveOutsideStorage()) + m_errorReporter.fatalTypeError(_tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage."); + _tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size()); } else @@ -1806,26 +1423,6 @@ void TypeChecker::typeCheckFunctionCall( "\"staticcall\" is not supported by the VM version." ); - // Check for deprecated function names - if (_functionType->kind() == FunctionType::Kind::KECCAK256) - { - if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) - if (functionName->name() == "sha3") - m_errorReporter.typeError( - _functionCall.location(), - "\"sha3\" has been deprecated in favour of \"keccak256\"" - ); - } - else if (_functionType->kind() == FunctionType::Kind::Selfdestruct) - { - if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) - if (functionName->name() == "suicide") - m_errorReporter.typeError( - _functionCall.location(), - "\"suicide\" has been deprecated in favour of \"selfdestruct\"" - ); - } - // Check for event outside of emit statement if (!m_insideEmitStatement && _functionType->kind() == FunctionType::Kind::Event) m_errorReporter.typeError( @@ -2488,7 +2085,7 @@ bool TypeChecker::visit(IndexAccess const& _access) } else { - expectType(*index, IntegerType(256)); + expectType(*index, IntegerType::uint256()); if (!m_errorReporter.hasErrors()) if (auto numberType = dynamic_cast<RationalNumberType const*>(type(*index).get())) { @@ -2519,7 +2116,7 @@ bool TypeChecker::visit(IndexAccess const& _access) resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType())); else { - expectType(*index, IntegerType(256)); + expectType(*index, IntegerType::uint256()); if (auto length = dynamic_cast<RationalNumberType const*>(type(*index).get())) resultType = make_shared<TypeType>(make_shared<ArrayType>( DataLocation::Memory, @@ -2538,7 +2135,7 @@ bool TypeChecker::visit(IndexAccess const& _access) m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); else { - if (!expectType(*index, IntegerType(256))) + if (!expectType(*index, IntegerType::uint256())) m_errorReporter.fatalTypeError(_access.location(), "Index expression cannot be represented as an unsigned integer."); if (auto integerType = dynamic_cast<RationalNumberType const*>(type(*index).get())) if (bytesType.numBytes() <= integerType->literalValue(nullptr)) @@ -2621,6 +2218,23 @@ bool TypeChecker::visit(Identifier const& _identifier) else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration)) if (dynamic_cast<FunctionType const*>(annotation.type.get())) annotation.isPure = true; + + // Check for deprecated function names. + // The check is done here for the case without an actual function call. + if (FunctionType const* fType = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get())) + { + if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256) + m_errorReporter.typeError( + _identifier.location(), + "\"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\"" + ); + } + return false; } diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index c76fa466..ebfcdadc 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -22,20 +22,23 @@ #pragma once -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libsolidity/ast/Types.h> #include <libsolidity/ast/ASTAnnotations.h> #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; - /** * The module that performs type analysis on the AST, checks the applicability of operations on * those types and stores errors for invalid operations. @@ -45,7 +48,7 @@ class TypeChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - TypeChecker(EVMVersion _evmVersion, ErrorReporter& _errorReporter): + TypeChecker(EVMVersion _evmVersion, langutil::ErrorReporter& _errorReporter): m_evmVersion(_evmVersion), m_errorReporter(_errorReporter) {} @@ -62,28 +65,7 @@ public: private: - virtual bool visit(ContractDefinition const& _contract) override; - /// Checks that two functions defined in this contract with the same name have different - /// arguments and that there is at most one constructor. - void checkContractDuplicateFunctions(ContractDefinition const& _contract); - void checkContractDuplicateEvents(ContractDefinition const& _contract); - void checkContractIllegalOverrides(ContractDefinition const& _contract); - /// Reports a type error with an appropriate message if overridden function signature differs. - /// Also stores the direct super function in the AST annotations. - void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); - void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); - void checkContractAbstractFunctions(ContractDefinition const& _contract); - void checkContractBaseConstructorArguments(ContractDefinition const& _contract); - void annotateBaseConstructorArguments( - ContractDefinition const& _currentContract, - FunctionDefinition const* _baseConstructor, - ASTNode const* _argumentNode - ); - /// Checks that different functions with external visibility end up having different - /// external argument types (i.e. different signature). - void checkContractExternalTypeClashes(ContractDefinition const& _contract); - /// Checks that all requirements for a library are fulfilled if this is a library. - void checkLibraryRequirements(ContractDefinition const& _contract); + bool visit(ContractDefinition const& _contract) override; /// Checks (and warns) if a tuple assignment might cause unexpected overwrites in storage. /// Should only be called if the left hand side is tuple-typed. void checkDoubleStorageAssignment(Assignment const& _assignment); @@ -122,40 +104,37 @@ private: FunctionTypePointer _functionType ); - virtual void endVisit(InheritanceSpecifier const& _inheritance) override; - virtual void endVisit(UsingForDirective const& _usingFor) override; - virtual bool visit(StructDefinition const& _struct) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(VariableDeclaration const& _variable) override; + void endVisit(InheritanceSpecifier const& _inheritance) override; + void endVisit(UsingForDirective const& _usingFor) override; + bool visit(StructDefinition const& _struct) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(VariableDeclaration const& _variable) override; /// We need to do this manually because we want to pass the bases of the current contract in /// case this is a base constructor call. void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases); - virtual bool visit(EventDefinition const& _eventDef) override; - virtual void endVisit(FunctionTypeName const& _funType) override; - virtual bool visit(InlineAssembly const& _inlineAssembly) override; - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual void endVisit(Return const& _return) override; - virtual bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; } - virtual void endVisit(EmitStatement const& _emit) override; - virtual bool visit(VariableDeclarationStatement const& _variable) override; - virtual void endVisit(ExpressionStatement const& _statement) override; - virtual bool visit(Conditional const& _conditional) override; - virtual bool visit(Assignment const& _assignment) override; - virtual bool visit(TupleExpression const& _tuple) override; - virtual void endVisit(BinaryOperation const& _operation) override; - virtual bool visit(UnaryOperation const& _operation) override; - virtual bool visit(FunctionCall const& _functionCall) override; - virtual void endVisit(NewExpression const& _newExpression) override; - virtual bool visit(MemberAccess const& _memberAccess) override; - virtual bool visit(IndexAccess const& _indexAccess) override; - virtual bool visit(Identifier const& _identifier) override; - virtual void endVisit(ElementaryTypeNameExpression const& _expr) override; - virtual void endVisit(Literal const& _literal) override; - - template <class T> - void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message); + bool visit(EventDefinition const& _eventDef) override; + void endVisit(FunctionTypeName const& _funType) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(WhileStatement const& _whileStatement) override; + bool visit(ForStatement const& _forStatement) override; + void endVisit(Return const& _return) override; + bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; } + void endVisit(EmitStatement const& _emit) override; + bool visit(VariableDeclarationStatement const& _variable) override; + void endVisit(ExpressionStatement const& _statement) override; + bool visit(Conditional const& _conditional) override; + bool visit(Assignment const& _assignment) override; + bool visit(TupleExpression const& _tuple) override; + void endVisit(BinaryOperation const& _operation) override; + bool visit(UnaryOperation const& _operation) override; + bool visit(FunctionCall const& _functionCall) override; + void endVisit(NewExpression const& _newExpression) override; + bool visit(MemberAccess const& _memberAccess) override; + bool visit(IndexAccess const& _indexAccess) override; + bool visit(Identifier const& _identifier) override; + void endVisit(ElementaryTypeNameExpression const& _expr) override; + void endVisit(Literal const& _literal) override; bool contractDependenciesAreCyclic( ContractDefinition const& _contract, @@ -183,7 +162,7 @@ private: /// Flag indicating whether we are currently inside a StructDefinition. bool m_insideStruct = false; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index b0cacc43..1112d682 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -19,13 +19,16 @@ #include <libevmasm/SemanticInformation.h> -#include <libsolidity/inlineasm/AsmData.h> #include <libsolidity/ast/ExperimentalFeatures.h> +#include <libyul/AsmData.h> + +#include <liblangutil/ErrorReporter.h> #include <functional> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace @@ -37,48 +40,48 @@ public: explicit AssemblyViewPureChecker(std::function<void(StateMutability, SourceLocation const&)> _reportMutability): m_reportMutability(_reportMutability) {} - void operator()(assembly::Label const&) { } - void operator()(assembly::Instruction const& _instruction) + void operator()(yul::Label const&) { } + void operator()(yul::Instruction const& _instruction) { checkInstruction(_instruction.location, _instruction.instruction); } - void operator()(assembly::Literal const&) {} - void operator()(assembly::Identifier const&) {} - void operator()(assembly::FunctionalInstruction const& _instr) + void operator()(yul::Literal const&) {} + void operator()(yul::Identifier const&) {} + void operator()(yul::FunctionalInstruction const& _instr) { checkInstruction(_instr.location, _instr.instruction); for (auto const& arg: _instr.arguments) boost::apply_visitor(*this, arg); } - void operator()(assembly::ExpressionStatement const& _expr) + void operator()(yul::ExpressionStatement const& _expr) { boost::apply_visitor(*this, _expr.expression); } - void operator()(assembly::StackAssignment const&) {} - void operator()(assembly::Assignment const& _assignment) + void operator()(yul::StackAssignment const&) {} + void operator()(yul::Assignment const& _assignment) { boost::apply_visitor(*this, *_assignment.value); } - void operator()(assembly::VariableDeclaration const& _varDecl) + void operator()(yul::VariableDeclaration const& _varDecl) { if (_varDecl.value) boost::apply_visitor(*this, *_varDecl.value); } - void operator()(assembly::FunctionDefinition const& _funDef) + void operator()(yul::FunctionDefinition const& _funDef) { (*this)(_funDef.body); } - void operator()(assembly::FunctionCall const& _funCall) + void operator()(yul::FunctionCall const& _funCall) { for (auto const& arg: _funCall.arguments) boost::apply_visitor(*this, arg); } - void operator()(assembly::If const& _if) + void operator()(yul::If const& _if) { boost::apply_visitor(*this, *_if.condition); (*this)(_if.body); } - void operator()(assembly::Switch const& _switch) + void operator()(yul::Switch const& _switch) { boost::apply_visitor(*this, *_switch.expression); for (auto const& _case: _switch.cases) @@ -88,14 +91,14 @@ public: (*this)(_case.body); } } - void operator()(assembly::ForLoop const& _for) + void operator()(yul::ForLoop const& _for) { (*this)(_for.pre); boost::apply_visitor(*this, *_for.condition); (*this)(_for.body); (*this)(_for.post); } - void operator()(assembly::Block const& _block) + void operator()(yul::Block const& _block) { for (auto const& s: _block.statements) boost::apply_visitor(*this, s); diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index faa5b698..fd2432a7 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -21,11 +21,15 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> -#include <libsolidity/interface/ErrorReporter.h> - #include <map> #include <memory> +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + namespace dev { namespace solidity @@ -34,7 +38,7 @@ namespace solidity class ViewPureChecker: private ASTConstVisitor { public: - ViewPureChecker(std::vector<std::shared_ptr<ASTNode>> const& _ast, ErrorReporter& _errorReporter): + ViewPureChecker(std::vector<std::shared_ptr<ASTNode>> const& _ast, langutil::ErrorReporter& _errorReporter): m_ast(_ast), m_errorReporter(_errorReporter) {} bool check(); @@ -43,34 +47,34 @@ private: struct MutabilityAndLocation { StateMutability mutability; - SourceLocation location; + langutil::SourceLocation location; }; - virtual bool visit(FunctionDefinition const& _funDef) override; - virtual void endVisit(FunctionDefinition const& _funDef) override; - virtual bool visit(ModifierDefinition const& _modifierDef) override; - virtual void endVisit(ModifierDefinition const& _modifierDef) override; - virtual void endVisit(Identifier const& _identifier) override; - virtual bool visit(MemberAccess const& _memberAccess) override; - virtual void endVisit(MemberAccess const& _memberAccess) override; - virtual void endVisit(IndexAccess const& _indexAccess) override; - virtual void endVisit(ModifierInvocation const& _modifier) override; - virtual void endVisit(FunctionCall const& _functionCall) override; - virtual void endVisit(InlineAssembly const& _inlineAssembly) override; + bool visit(FunctionDefinition const& _funDef) override; + void endVisit(FunctionDefinition const& _funDef) override; + bool visit(ModifierDefinition const& _modifierDef) override; + void endVisit(ModifierDefinition const& _modifierDef) override; + void endVisit(Identifier const& _identifier) override; + bool visit(MemberAccess const& _memberAccess) override; + void endVisit(MemberAccess const& _memberAccess) override; + void endVisit(IndexAccess const& _indexAccess) override; + void endVisit(ModifierInvocation const& _modifier) override; + void endVisit(FunctionCall const& _functionCall) override; + void endVisit(InlineAssembly const& _inlineAssembly) override; /// Called when an element of mutability @a _mutability is encountered. /// Creates appropriate warnings and errors and sets @a m_currentBestMutability. void reportMutability( StateMutability _mutability, - SourceLocation const& _location, - boost::optional<SourceLocation> const& _nestedLocation = {} + langutil::SourceLocation const& _location, + boost::optional<langutil::SourceLocation> const& _nestedLocation = {} ); std::vector<std::shared_ptr<ASTNode>> const& m_ast; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; bool m_errors = false; - MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, SourceLocation()}; + MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, langutil::SourceLocation()}; FunctionDefinition const* m_currentFunction = nullptr; std::map<ModifierDefinition const*, MutabilityAndLocation> m_inferredMutability; }; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4fd2bcb8..2f418b09 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -23,13 +23,13 @@ #pragma once -#include <libsolidity/ast/ASTForward.h> #include <libsolidity/parsing/Token.h> +#include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/Types.h> #include <libsolidity/ast/ASTAnnotations.h> #include <libsolidity/ast/ASTEnums.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include <libevmasm/Instruction.h> #include <libdevcore/FixedHash.h> @@ -41,6 +41,12 @@ #include <vector> #include <memory> +namespace yul +{ +// Forward-declaration to <yul/AsmData.h> +struct Block; +} + namespace dev { namespace solidity @@ -58,6 +64,8 @@ class ASTConstVisitor; class ASTNode: private boost::noncopyable { public: + using SourceLocation = langutil::SourceLocation; + explicit ASTNode(SourceLocation const& _location); virtual ~ASTNode(); @@ -126,9 +134,9 @@ public: SourceUnit(SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes): ASTNode(_location), m_nodes(_nodes) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual SourceUnitAnnotation& annotation() const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + SourceUnitAnnotation& annotation() const override; std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; } @@ -242,8 +250,8 @@ public: ): ASTNode(_location), m_tokens(_tokens), m_literals(_literals) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<Token> const& tokens() const { return m_tokens; } std::vector<ASTString> const& literals() const { return m_literals; } @@ -279,17 +287,17 @@ public: m_symbolAliases(_symbolAliases) { } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; ASTString const& path() const { return *m_path; } std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> const& symbolAliases() const { return m_symbolAliases; } - virtual ImportAnnotation& annotation() const override; + ImportAnnotation& annotation() const override; - virtual TypePointer type() const override; + TypePointer type() const override; private: ASTPointer<ASTString> m_path; @@ -375,8 +383,8 @@ public: m_contractKind(_contractKind) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<InheritanceSpecifier>> const& baseContracts() const { return m_baseContracts; } std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; } @@ -407,9 +415,9 @@ public: std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } - virtual TypePointer type() const override; + TypePointer type() const override; - virtual ContractDefinitionAnnotation& annotation() const override; + ContractDefinitionAnnotation& annotation() const override; ContractKind contractKind() const { return m_contractKind; } @@ -434,8 +442,8 @@ public: ): ASTNode(_location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; UserDefinedTypeName const& name() const { return *m_baseName; } // Returns nullptr if no argument list was given (``C``). @@ -463,8 +471,8 @@ public: ): ASTNode(_location), m_libraryName(_libraryName), m_typeName(_typeName) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; UserDefinedTypeName const& libraryName() const { return *m_libraryName; } /// @returns the type name the library is attached to, null for `*`. @@ -485,14 +493,14 @@ public: ): Declaration(_location, _name), m_members(_members) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; } - virtual TypePointer type() const override; + TypePointer type() const override; - virtual TypeDeclarationAnnotation& annotation() const override; + TypeDeclarationAnnotation& annotation() const override; private: std::vector<ASTPointer<VariableDeclaration>> m_members; @@ -507,14 +515,14 @@ public: std::vector<ASTPointer<EnumValue>> const& _members ): Declaration(_location, _name), m_members(_members) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; } - virtual TypePointer type() const override; + TypePointer type() const override; - virtual TypeDeclarationAnnotation& annotation() const override; + TypeDeclarationAnnotation& annotation() const override; private: std::vector<ASTPointer<EnumValue>> m_members; @@ -529,10 +537,10 @@ public: EnumValue(SourceLocation const& _location, ASTPointer<ASTString> const& _name): Declaration(_location, _name) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; - virtual TypePointer type() const override; + TypePointer type() const override; }; /** @@ -548,8 +556,8 @@ public: std::vector<ASTPointer<VariableDeclaration>> const& _parameters ): ASTNode(_location), m_parameters(_parameters) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<VariableDeclaration>> const& parameters() const { return m_parameters; } @@ -610,8 +618,8 @@ public: m_body(_body) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; StateMutability stateMutability() const { return m_stateMutability; } bool isConstructor() const { return m_isConstructor; } @@ -620,11 +628,11 @@ public: std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } Block const& body() const { solAssert(m_body, ""); return *m_body; } - virtual bool isVisibleInContract() const override + bool isVisibleInContract() const override { return Declaration::isVisibleInContract() && !isConstructor() && !isFallback(); } - virtual bool isPartOfExternalInterface() const override { return isPublic() && !isConstructor() && !isFallback(); } + bool isPartOfExternalInterface() const override { return isPublic() && !isConstructor() && !isFallback(); } /// @returns the external signature of the function /// That consists of the name of the function followed by the types of the @@ -633,13 +641,13 @@ public: ContractDefinition::ContractKind inContractKind() const; - virtual TypePointer type() const override; + TypePointer type() const override; /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. /// @returns null when it is not accessible as a function. - virtual FunctionTypePointer functionType(bool /*_internal*/) const override; + FunctionTypePointer functionType(bool /*_internal*/) const override; - virtual FunctionDefinitionAnnotation& annotation() const override; + FunctionDefinitionAnnotation& annotation() const override; private: StateMutability m_stateMutability; @@ -676,14 +684,14 @@ public: m_isConstant(_isConstant), m_location(_referenceLocation) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; TypeName* typeName() const { return m_typeName.get(); } ASTPointer<Expression> const& value() const { return m_value; } - virtual bool isLValue() const override; - virtual bool isPartOfExternalInterface() const override { return isPublic(); } + bool isLValue() const override; + bool isPartOfExternalInterface() const override { return isPublic(); } /// @returns true iff this variable is the parameter (or return parameter) of a function /// (or function type name or event) or declared inside a function body. @@ -717,13 +725,13 @@ public: /// @returns a set of allowed storage locations for the variable. std::set<Location> allowedDataLocations() const; - virtual TypePointer type() const override; + TypePointer type() const override; /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. /// @returns null when it is not accessible as a function. - virtual FunctionTypePointer functionType(bool /*_internal*/) const override; + FunctionTypePointer functionType(bool /*_internal*/) const override; - virtual VariableDeclarationAnnotation& annotation() const override; + VariableDeclarationAnnotation& annotation() const override; protected: Visibility defaultVisibility() const override { return Visibility::Internal; } @@ -758,14 +766,14 @@ public: { } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Block const& body() const { return *m_body; } - virtual TypePointer type() const override; + TypePointer type() const override; - virtual ModifierDefinitionAnnotation& annotation() const override; + ModifierDefinitionAnnotation& annotation() const override; private: ASTPointer<Block> m_body; @@ -784,8 +792,8 @@ public: ): ASTNode(_location), m_modifierName(_name), m_arguments(std::move(_arguments)) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; ASTPointer<Identifier> const& name() const { return m_modifierName; } // Returns nullptr if no argument list was given (``mod``). @@ -817,15 +825,15 @@ public: { } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; bool isAnonymous() const { return m_anonymous; } - virtual TypePointer type() const override; - virtual FunctionTypePointer functionType(bool /*_internal*/) const override; + TypePointer type() const override; + FunctionTypePointer functionType(bool /*_internal*/) const override; - virtual EventDefinitionAnnotation& annotation() const override; + EventDefinitionAnnotation& annotation() const override; private: bool m_anonymous = false; @@ -840,21 +848,21 @@ class MagicVariableDeclaration: public Declaration public: MagicVariableDeclaration(ASTString const& _name, std::shared_ptr<Type const> const& _type): Declaration(SourceLocation(), std::make_shared<ASTString>(_name)), m_type(_type) {} - virtual void accept(ASTVisitor&) override + void accept(ASTVisitor&) override { solAssert(false, "MagicVariableDeclaration used inside real AST."); } - virtual void accept(ASTConstVisitor&) const override + void accept(ASTConstVisitor&) const override { solAssert(false, "MagicVariableDeclaration used inside real AST."); } - virtual FunctionTypePointer functionType(bool) const override + FunctionTypePointer functionType(bool) const override { solAssert(m_type->category() == Type::Category::Function, ""); return std::dynamic_pointer_cast<FunctionType const>(m_type); } - virtual TypePointer type() const override { return m_type; } + TypePointer type() const override { return m_type; } private: std::shared_ptr<Type const> m_type; @@ -872,7 +880,7 @@ protected: explicit TypeName(SourceLocation const& _location): ASTNode(_location) {} public: - virtual TypeNameAnnotation& annotation() const override; + TypeNameAnnotation& annotation() const override; }; /** @@ -891,8 +899,8 @@ public: solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, ""); } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; ElementaryTypeNameToken const& typeName() const { return m_type; } @@ -911,12 +919,12 @@ class UserDefinedTypeName: public TypeName public: UserDefinedTypeName(SourceLocation const& _location, std::vector<ASTString> const& _namePath): TypeName(_location), m_namePath(_namePath) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTString> const& namePath() const { return m_namePath; } - virtual UserDefinedTypeNameAnnotation& annotation() const override; + UserDefinedTypeNameAnnotation& annotation() const override; private: std::vector<ASTString> m_namePath; @@ -938,8 +946,8 @@ public: TypeName(_location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes), m_visibility(_visibility), m_stateMutability(_stateMutability) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<VariableDeclaration>> const& parameterTypes() const { return m_parameterTypes->parameters(); } std::vector<ASTPointer<VariableDeclaration>> const& returnParameterTypes() const { return m_returnTypes->parameters(); } @@ -972,8 +980,8 @@ public: ASTPointer<TypeName> const& _valueType ): TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; ElementaryTypeName const& keyType() const { return *m_keyType; } TypeName const& valueType() const { return *m_valueType; } @@ -995,8 +1003,8 @@ public: ASTPointer<Expression> const& _length ): TypeName(_location), m_baseType(_baseType), m_length(_length) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; TypeName const& baseType() const { return *m_baseType; } Expression const* length() const { return m_length.get(); } @@ -1023,15 +1031,9 @@ public: ASTPointer<ASTString> const& _docString ): ASTNode(_location), Documented(_docString) {} - virtual StatementAnnotation& annotation() const override; + StatementAnnotation& annotation() const override; }; -namespace assembly -{ -// Forward-declaration to AsmData.h -struct Block; -} - /** * Inline assembly. */ @@ -1041,18 +1043,18 @@ public: InlineAssembly( SourceLocation const& _location, ASTPointer<ASTString> const& _docString, - std::shared_ptr<assembly::Block> const& _operations + std::shared_ptr<yul::Block> const& _operations ): Statement(_location, _docString), m_operations(_operations) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; - assembly::Block const& operations() const { return *m_operations; } + yul::Block const& operations() const { return *m_operations; } - virtual InlineAssemblyAnnotation& annotation() const override; + InlineAssemblyAnnotation& annotation() const override; private: - std::shared_ptr<assembly::Block> m_operations; + std::shared_ptr<yul::Block> m_operations; }; /** @@ -1067,8 +1069,8 @@ public: std::vector<ASTPointer<Statement>> const& _statements ): Statement(_location, _docString), m_statements(_statements) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<Statement>> const& statements() const { return m_statements; } @@ -1088,8 +1090,8 @@ public: ASTPointer<ASTString> const& _docString ): Statement(_location, _docString) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; }; /** @@ -1111,8 +1113,8 @@ public: m_trueBody(_trueBody), m_falseBody(_falseBody) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& condition() const { return *m_condition; } Statement const& trueStatement() const { return *m_trueBody; } @@ -1149,8 +1151,8 @@ public: ): BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body), m_isDoWhile(_isDoWhile) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& condition() const { return *m_condition; } Statement const& body() const { return *m_body; } @@ -1182,8 +1184,8 @@ public: m_loopExpression(_loopExpression), m_body(_body) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Statement const* initializationExpression() const { return m_initExpression.get(); } Expression const* condition() const { return m_condExpression.get(); } @@ -1206,8 +1208,8 @@ class Continue: public Statement public: explicit Continue(SourceLocation const& _location, ASTPointer<ASTString> const& _docString): Statement(_location, _docString) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; }; class Break: public Statement @@ -1215,8 +1217,8 @@ class Break: public Statement public: explicit Break(SourceLocation const& _location, ASTPointer<ASTString> const& _docString): Statement(_location, _docString) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; }; class Return: public Statement @@ -1227,12 +1229,12 @@ public: ASTPointer<ASTString> const& _docString, ASTPointer<Expression> _expression ): Statement(_location, _docString), m_expression(_expression) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const* expression() const { return m_expression.get(); } - virtual ReturnAnnotation& annotation() const override; + ReturnAnnotation& annotation() const override; private: ASTPointer<Expression> m_expression; ///< value to return, optional @@ -1246,8 +1248,8 @@ class Throw: public Statement public: explicit Throw(SourceLocation const& _location, ASTPointer<ASTString> const& _docString): Statement(_location, _docString) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; }; /** @@ -1262,8 +1264,8 @@ public: ASTPointer<FunctionCall> const& _functionCall ): Statement(_location, _docString), m_eventCall(_functionCall) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; FunctionCall const& eventCall() const { return *m_eventCall; } @@ -1289,8 +1291,8 @@ public: ASTPointer<Expression> const& _initialValue ): Statement(_location, _docString), m_variables(_variables), m_initialValue(_initialValue) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; } Expression const* initialValue() const { return m_initialValue.get(); } @@ -1317,8 +1319,8 @@ public: ASTPointer<Expression> _expression ): Statement(_location, _docString), m_expression(_expression) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& expression() const { return *m_expression; } @@ -1358,8 +1360,8 @@ public: m_trueExpression(_trueExpression), m_falseExpression(_falseExpression) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& condition() const { return *m_condition; } Expression const& trueExpression() const { return *m_trueExpression; } @@ -1389,8 +1391,8 @@ public: { solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), ""); } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& leftHandSide() const { return *m_leftHandSide; } Token assignmentOperator() const { return m_assigmentOperator; } @@ -1421,8 +1423,8 @@ public: Expression(_location), m_components(_components), m_isArray(_isArray) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<Expression>> const& components() const { return m_components; } bool isInlineArray() const { return m_isArray; } @@ -1452,8 +1454,8 @@ public: { solAssert(TokenTraits::isUnaryOp(_operator), ""); } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Token getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } @@ -1482,8 +1484,8 @@ public: { solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), ""); } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& leftExpression() const { return *m_left; } Expression const& rightExpression() const { return *m_right; } @@ -1510,14 +1512,14 @@ public: std::vector<ASTPointer<ASTString>> const& _names ): Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& expression() const { return *m_expression; } std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; } std::vector<ASTPointer<ASTString>> const& names() const { return m_names; } - virtual FunctionCallAnnotation& annotation() const override; + FunctionCallAnnotation& annotation() const override; private: ASTPointer<Expression> m_expression; @@ -1537,8 +1539,8 @@ public: ASTPointer<TypeName> const& _typeName ): Expression(_location), m_typeName(_typeName) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; TypeName const& typeName() const { return *m_typeName; } @@ -1558,12 +1560,12 @@ public: ASTPointer<ASTString> const& _memberName ): Expression(_location), m_expression(_expression), m_memberName(_memberName) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& expression() const { return *m_expression; } ASTString const& memberName() const { return *m_memberName; } - virtual MemberAccessAnnotation& annotation() const override; + MemberAccessAnnotation& annotation() const override; private: ASTPointer<Expression> m_expression; @@ -1582,8 +1584,8 @@ public: ASTPointer<Expression> const& _index ): Expression(_location), m_base(_base), m_index(_index) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Expression const& baseExpression() const { return *m_base; } Expression const* indexExpression() const { return m_index.get(); } @@ -1614,12 +1616,12 @@ public: ASTPointer<ASTString> const& _name ): PrimaryExpression(_location), m_name(_name) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; ASTString const& name() const { return *m_name; } - virtual IdentifierAnnotation& annotation() const override; + IdentifierAnnotation& annotation() const override; private: ASTPointer<ASTString> m_name; @@ -1636,8 +1638,8 @@ public: ElementaryTypeNameExpression(SourceLocation const& _location, ElementaryTypeNameToken const& _type): PrimaryExpression(_location), m_typeToken(_type) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; ElementaryTypeNameToken const& typeName() const { return m_typeToken; } @@ -1672,8 +1674,8 @@ public: SubDenomination _sub = SubDenomination::None ): PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; Token token() const { return m_token; } /// @returns the non-parsed value of the literal diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index e0b3f492..e9cc905e 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -30,6 +30,12 @@ #include <vector> #include <set> +namespace yul +{ + struct AsmAnalysisInfo; + struct Identifier; +} + namespace dev { namespace solidity @@ -120,12 +126,6 @@ struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation { }; -namespace assembly -{ - struct AsmAnalysisInfo; - struct Identifier; -} - struct InlineAssemblyAnnotation: StatementAnnotation { struct ExternalIdentifierInfo @@ -137,9 +137,9 @@ struct InlineAssemblyAnnotation: StatementAnnotation }; /// Mapping containing resolved references to external identifiers and their value size - std::map<assembly::Identifier const*, ExternalIdentifierInfo> externalReferences; + std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences; /// Information generated during analysis phase. - std::shared_ptr<assembly::AsmAnalysisInfo> analysisInfo; + std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo; }; struct ReturnAnnotation: StatementAnnotation diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h index 5ba21907..d47a5f05 100644 --- a/libsolidity/ast/ASTEnums.h +++ b/libsolidity/ast/ASTEnums.h @@ -21,7 +21,7 @@ #pragma once -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <string> diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 2d26ce8a..cfb13271 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -23,10 +23,11 @@ #include <boost/algorithm/string/join.hpp> #include <libdevcore/UTF8.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libyul/AsmData.h> +#include <libyul/AsmPrinter.h> using namespace std; +using namespace langutil; namespace dev { @@ -121,8 +122,8 @@ void ASTJsonConverter::setJsonNode( string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) const { int sourceIndex{-1}; - if (_location.sourceName && m_sourceIndices.count(*_location.sourceName)) - sourceIndex = m_sourceIndices.at(*_location.sourceName); + if (_location.source && m_sourceIndices.count(_location.source->name())) + sourceIndex = m_sourceIndices.at(_location.source->name()); int length = -1; if (_location.start >= 0 && _location.end >= 0) length = _location.end - _location.start; @@ -171,7 +172,7 @@ void ASTJsonConverter::appendExpressionAttributes( _attributes += exprAttributes; } -Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<assembly::Identifier const* ,InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const +Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifier const* ,InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const { Json::Value tuple(Json::objectValue); tuple["src"] = sourceLocationToString(_info.first->location); @@ -464,7 +465,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node) } } setJsonNode(_node, "InlineAssembly", { - make_pair("operations", Json::Value(assembly::AsmPrinter()(_node.operations()))), + make_pair("operations", Json::Value(yul::AsmPrinter()(_node.operations()))), make_pair("externalReferences", std::move(externalReferences)) }); return false; diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 8429708c..ef0a217a 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -25,10 +25,15 @@ #include <ostream> #include <stack> #include <libsolidity/ast/ASTVisitor.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/ast/ASTAnnotations.h> #include <json/json.h> +namespace langutil +{ +struct SourceLocation; +} + namespace dev { namespace solidity @@ -120,7 +125,7 @@ private: std::string const& _nodeName, std::vector<std::pair<std::string, Json::Value>>&& _attributes ); - std::string sourceLocationToString(SourceLocation const& _location) const; + std::string sourceLocationToString(langutil::SourceLocation const& _location) const; static std::string namePathToString(std::vector<ASTString> const& _namePath); static Json::Value idOrNull(ASTNode const* _pt) { @@ -130,7 +135,7 @@ private: { return _node ? toJson(*_node) : Json::nullValue; } - Json::Value inlineAssemblyIdentifierToJson(std::pair<assembly::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const; + Json::Value inlineAssemblyIdentifierToJson(std::pair<yul::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const; static std::string location(VariableDeclaration::Location _location); static std::string contractKind(ContractDefinition::ContractKind _kind); static std::string functionCallKind(FunctionCallKind _kind); diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 255cb9be..cdc6ae7d 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -28,6 +28,7 @@ #include <boost/algorithm/string/join.hpp> using namespace std; +using namespace langutil; namespace dev { diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 6c0ce6f8..1a761032 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -262,8 +262,8 @@ public: ): m_onVisit(_onVisit), m_onEndVisit(_onEndVisit) {} protected: - virtual bool visitNode(ASTNode const& _n) override { return m_onVisit ? m_onVisit(_n) : true; } - virtual void endVisitNode(ASTNode const& _n) override { m_onEndVisit(_n); } + bool visitNode(ASTNode const& _n) override { return m_onVisit ? m_onVisit(_n) : true; } + void endVisitNode(ASTNode const& _n) override { m_onEndVisit(_n); } private: std::function<bool(ASTNode const&)> m_onVisit; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 4b31d2e8..6cadb5f3 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -45,6 +45,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace @@ -441,10 +442,11 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function)) continue; seenFunctions.insert(function); - FunctionType funType(*function, false); - if (auto fun = funType.asMemberFunction(true, true)) - if (_type.isImplicitlyConvertibleTo(*fun->selfType())) - members.push_back(MemberList::Member(function->name(), fun, function)); + if (function->parameters().empty()) + continue; + FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true); + if (_type.isImplicitlyConvertibleTo(*fun->selfType())) + members.push_back(MemberList::Member(function->name(), fun, function)); } } return members; @@ -1941,8 +1943,9 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con for (ContractDefinition const* base: bases | boost::adaptors::sliced(1, bases.size())) for (FunctionDefinition const* function: base->definedFunctions()) { - if (!function->isVisibleInDerivedContracts()) + if (!function->isVisibleInDerivedContracts() || !function->isImplemented()) continue; + auto functionType = make_shared<FunctionType>(*function, true); bool functionWithEqualArgumentsFound = false; for (auto const& member: members) @@ -1969,7 +1972,7 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con for (auto const& it: m_contract.interfaceFunctions()) members.push_back(MemberList::Member( it.second->declaration().name(), - it.second->asMemberFunction(m_contract.isLibrary()), + it.second->asCallableFunction(m_contract.isLibrary()), &it.second->declaration() )); } @@ -2122,9 +2125,15 @@ bool StructType::canBeUsedExternally(bool _inLibrary) const // We pass "false" to canBeUsedExternally (_inLibrary), because this struct will be // passed by value and thus the encoding does not differ, but it will disallow // mappings. + // Also return false if at least one struct member does not have a type. + // This might happen, for example, if the type of the member does not exist, + // which is reported as an error. for (auto const& var: m_struct.members()) { - solAssert(var->annotation().type, ""); + // If the struct member does not have a type return false. + // A TypeError is expected in this case. + if (!var->annotation().type) + return false; if (!var->annotation().type->canBeUsedExternally(false)) return false; } @@ -3058,10 +3067,10 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con ); } -FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) const +FunctionTypePointer FunctionType::asCallableFunction(bool _inLibrary, bool _bound) const { - if (_bound && m_parameterTypes.empty()) - return FunctionTypePointer(); + if (_bound) + solAssert(!m_parameterTypes.empty(), ""); TypePointers parameterTypes; for (auto const& t: m_parameterTypes) @@ -3200,7 +3209,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current if (function->isVisibleAsLibraryMember()) members.push_back(MemberList::Member( function->name(), - FunctionType(*function).asMemberFunction(true), + FunctionType(*function).asCallableFunction(true), function )); if (isBase) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 24ace447..0f0548d3 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -22,7 +22,7 @@ #pragma once -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTEnums.h> #include <libsolidity/parsing/Token.h> @@ -331,31 +331,31 @@ public: static AddressType& address() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::NonPayable)); return *addr; } static AddressType& addressPayable() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::Payable)); return *addr; } - virtual Category category() const override { return Category::Address; } + Category category() const override { return Category::Address; } explicit AddressType(StateMutability _stateMutability); - virtual std::string richIdentifier() const override; - virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + std::string richIdentifier() const override; + bool isImplicitlyConvertibleTo(Type const& _other) const override; + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypePointer unaryOperatorResult(Token _operator) const override; + TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; - virtual bool operator==(Type const& _other) const override; + bool operator==(Type const& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; } - virtual unsigned storageBytes() const override { return 160 / 8; } - virtual bool isValueType() const override { return true; } + unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; } + unsigned storageBytes() const override { return 160 / 8; } + bool isValueType() const override { return true; } - virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; - virtual std::string toString(bool _short) const override; - virtual std::string canonicalName() const override; + std::string toString(bool _short) const override; + std::string canonicalName() const override; - virtual u256 literalValue(Literal const* _literal) const override; + u256 literalValue(Literal const* _literal) const override; - virtual TypePointer encodingType() const override { return shared_from_this(); } - virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + TypePointer encodingType() const override { return shared_from_this(); } + TypePointer interfaceType(bool) const override { return shared_from_this(); } StateMutability stateMutability(void) const { return m_stateMutability; } @@ -374,26 +374,28 @@ public: Unsigned, Signed }; - virtual Category category() const override { return Category::Integer; } + static IntegerType& uint256() { static std::shared_ptr<IntegerType> uint256(std::make_shared<IntegerType>(256)); return *uint256; } + + Category category() const override { return Category::Integer; } explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned); - virtual std::string richIdentifier() const override; - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + std::string richIdentifier() const override; + bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypePointer unaryOperatorResult(Token _operator) const override; + TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; - virtual bool operator==(Type const& _other) const override; + bool operator==(Type const& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; } - virtual unsigned storageBytes() const override { return m_bits / 8; } - virtual bool isValueType() const override { return true; } + unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; } + unsigned storageBytes() const override { return m_bits / 8; } + bool isValueType() const override { return true; } - virtual std::string toString(bool _short) const override; + std::string toString(bool _short) const override; - virtual TypePointer encodingType() const override { return shared_from_this(); } - virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + TypePointer encodingType() const override { return shared_from_this(); } + TypePointer interfaceType(bool) const override { return shared_from_this(); } unsigned numBits() const { return m_bits; } bool isSigned() const { return m_modifier == Modifier::Signed; } @@ -416,26 +418,26 @@ public: { Unsigned, Signed }; - virtual Category category() const override { return Category::FixedPoint; } + Category category() const override { return Category::FixedPoint; } explicit FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, Modifier _modifier = Modifier::Unsigned); - virtual std::string richIdentifier() const override; - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + std::string richIdentifier() const override; + bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypePointer unaryOperatorResult(Token _operator) const override; + TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; - virtual bool operator==(Type const& _other) const override; + bool operator==(Type const& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_totalBits / 8; } - virtual unsigned storageBytes() const override { return m_totalBits / 8; } - virtual bool isValueType() const override { return true; } + unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_totalBits / 8; } + unsigned storageBytes() const override { return m_totalBits / 8; } + bool isValueType() const override { return true; } - virtual std::string toString(bool _short) const override; + std::string toString(bool _short) const override; - virtual TypePointer encodingType() const override { return shared_from_this(); } - virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + TypePointer encodingType() const override { return shared_from_this(); } + TypePointer interfaceType(bool) const override { return shared_from_this(); } /// Number of bits used for this type in total. unsigned numBits() const { return m_totalBits; } @@ -467,7 +469,7 @@ class RationalNumberType: public Type { public: - virtual Category category() const override { return Category::RationalNumber; } + Category category() const override { return Category::RationalNumber; } static TypePointer forLiteral(Literal const& _literal); @@ -475,20 +477,20 @@ public: m_value(_value), m_compatibleBytesType(_compatibleBytesType) {} - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypePointer unaryOperatorResult(Token _operator) const override; + TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; - virtual bool canBeStored() const override { return false; } - virtual bool canLiveOutsideStorage() const override { return false; } + bool canBeStored() const override { return false; } + bool canLiveOutsideStorage() const override { return false; } - virtual std::string toString(bool _short) const override; - virtual u256 literalValue(Literal const* _literal) const override; - virtual TypePointer mobileType() const override; + std::string toString(bool _short) const override; + u256 literalValue(Literal const* _literal) const override; + TypePointer mobileType() const override; /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr<IntegerType const> integerType() const; @@ -530,25 +532,25 @@ private: class StringLiteralType: public Type { public: - virtual Category category() const override { return Category::StringLiteral; } + Category category() const override { return Category::StringLiteral; } explicit StringLiteralType(Literal const& _literal); - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override + bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; - virtual bool canBeStored() const override { return false; } - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned sizeOnStack() const override { return 0; } + bool canBeStored() const override { return false; } + bool canLiveOutsideStorage() const override { return false; } + unsigned sizeOnStack() const override { return 0; } - virtual std::string toString(bool) const override; - virtual TypePointer mobileType() const override; + std::string toString(bool) const override; + TypePointer mobileType() const override; bool isValidUTF8() const; @@ -564,25 +566,25 @@ private: class FixedBytesType: public Type { public: - virtual Category category() const override { return Category::FixedBytes; } + Category category() const override { return Category::FixedBytes; } explicit FixedBytesType(unsigned _bytes); - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + TypePointer unaryOperatorResult(Token _operator) const override; + TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } - virtual unsigned storageBytes() const override { return m_bytes; } - virtual bool isValueType() const override { return true; } + unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } + unsigned storageBytes() const override { return m_bytes; } + bool isValueType() const override { return true; } - virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); } - virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; - virtual TypePointer encodingType() const override { return shared_from_this(); } - virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); } + MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + TypePointer encodingType() const override { return shared_from_this(); } + TypePointer interfaceType(bool) const override { return shared_from_this(); } unsigned numBytes() const { return m_bytes; } @@ -597,19 +599,19 @@ class BoolType: public Type { public: BoolType() {} - virtual Category category() const override { return Category::Bool; } - virtual std::string richIdentifier() const override { return "t_bool"; } - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; - - virtual unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; } - virtual unsigned storageBytes() const override { return 1; } - virtual bool isValueType() const override { return true; } - - virtual std::string toString(bool) const override { return "bool"; } - virtual u256 literalValue(Literal const* _literal) const override; - virtual TypePointer encodingType() const override { return shared_from_this(); } - virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + Category category() const override { return Category::Bool; } + std::string richIdentifier() const override { return "t_bool"; } + TypePointer unaryOperatorResult(Token _operator) const override; + TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + + unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; } + unsigned storageBytes() const override { return 1; } + bool isValueType() const override { return true; } + + std::string toString(bool) const override { return "bool"; } + u256 literalValue(Literal const* _literal) const override; + TypePointer encodingType() const override { return shared_from_this(); } + TypePointer interfaceType(bool) const override { return shared_from_this(); } }; /** @@ -622,20 +624,20 @@ public: explicit ReferenceType(DataLocation _location): m_location(_location) {} DataLocation location() const { return m_location; } - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override + TypePointer unaryOperatorResult(Token _operator) const override; + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual unsigned memoryHeadSize() const override { return 32; } + unsigned memoryHeadSize() const override { return 32; } /// @returns a copy of this type with location (recursively) changed to @a _location, /// whereas isPointer is only shallowly changed - the deep copy is always a bound reference. virtual TypePointer copyForLocation(DataLocation _location, bool _isPointer) const = 0; - virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); } - virtual bool dataStoredIn(DataLocation _location) const override { return m_location == _location; } - virtual bool hasSimpleZeroValueInMemory() const override { return false; } + TypePointer mobileType() const override { return copyForLocation(m_location, true); } + bool dataStoredIn(DataLocation _location) const override { return m_location == _location; } + bool hasSimpleZeroValueInMemory() const override { return false; } /// Storage references can be pointers or bound references. In general, local variables are of /// pointer type, state variables are bound references. Assignments to pointers or deleting @@ -677,7 +679,7 @@ public: static ArrayType& bytesMemory() { static std::shared_ptr<ArrayType> addr(std::make_shared<ArrayType>(DataLocation::Memory)); return *addr; } static ArrayType& stringMemory() { static std::shared_ptr<ArrayType> addr(std::make_shared<ArrayType>(DataLocation::Memory, true)); return *addr; } - virtual Category category() const override { return Category::Array; } + Category category() const override { return Category::Array; } /// Constructor for a byte array ("bytes") and string. explicit ArrayType(DataLocation _location, bool _isString = false): @@ -700,24 +702,24 @@ public: m_length(_length) {} - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual std::string richIdentifier() const override; - virtual bool operator==(const Type& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded) const override; - virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } - virtual bool isDynamicallyEncoded() const override; - virtual u256 storageSize() const override; - virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); } - virtual unsigned sizeOnStack() const override; - virtual std::string toString(bool _short) const override; - virtual std::string canonicalName() const override; - virtual std::string signatureInExternalFunction(bool _structsByName) const override; - virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; - virtual TypePointer encodingType() const override; - virtual TypePointer decodingType() const override; - virtual TypePointer interfaceType(bool _inLibrary) const override; - virtual bool canBeUsedExternally(bool _inLibrary) const override; + bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + std::string richIdentifier() const override; + bool operator==(const Type& _other) const override; + unsigned calldataEncodedSize(bool _padded) const override; + bool isDynamicallySized() const override { return m_hasDynamicLength; } + bool isDynamicallyEncoded() const override; + u256 storageSize() const override; + bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); } + unsigned sizeOnStack() const override; + std::string toString(bool _short) const override; + std::string canonicalName() const override; + std::string signatureInExternalFunction(bool _structsByName) const override; + MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + TypePointer encodingType() const override; + TypePointer decodingType() const override; + TypePointer interfaceType(bool _inLibrary) const override; + bool canBeUsedExternally(bool _inLibrary) const override; /// @returns true if this is valid to be stored in calldata bool validForCalldata() const; @@ -751,36 +753,36 @@ private: class ContractType: public Type { public: - virtual Category category() const override { return Category::Contract; } + Category category() const override { return Category::Contract; } explicit ContractType(ContractDefinition const& _contract, bool _super = false): m_contract(_contract), m_super(_super) {} /// Contracts can be implicitly converted only to base contracts. - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; /// Contracts can only be explicitly converted to address types and base contracts. - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded ) const override + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypePointer unaryOperatorResult(Token _operator) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + unsigned calldataEncodedSize(bool _padded ) const override { solAssert(!isSuper(), ""); return encodingType()->calldataEncodedSize(_padded); } - virtual unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } - virtual bool canLiveOutsideStorage() const override { return !isSuper(); } - virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; } - virtual bool isValueType() const override { return !isSuper(); } - virtual std::string toString(bool _short) const override; - virtual std::string canonicalName() const override; - - virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; - virtual TypePointer encodingType() const override + unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } + bool canLiveOutsideStorage() const override { return !isSuper(); } + unsigned sizeOnStack() const override { return m_super ? 0 : 1; } + bool isValueType() const override { return !isSuper(); } + std::string toString(bool _short) const override; + std::string canonicalName() const override; + + MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + TypePointer encodingType() const override { if (isSuper()) return TypePointer{}; return std::make_shared<AddressType>(isPayable() ? StateMutability::Payable : StateMutability::NonPayable); } - virtual TypePointer interfaceType(bool _inLibrary) const override + TypePointer interfaceType(bool _inLibrary) const override { if (isSuper()) return TypePointer{}; @@ -816,31 +818,31 @@ private: class StructType: public ReferenceType { public: - virtual Category category() const override { return Category::Struct; } + Category category() const override { return Category::Struct; } explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): ReferenceType(_location), m_struct(_struct) {} - virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded) const override; - virtual bool isDynamicallyEncoded() const override; + bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + unsigned calldataEncodedSize(bool _padded) const override; + bool isDynamicallyEncoded() const override; u256 memorySize() const; - virtual u256 storageSize() const override; - virtual bool canLiveOutsideStorage() const override { return true; } - virtual std::string toString(bool _short) const override; + u256 storageSize() const override; + bool canLiveOutsideStorage() const override { return true; } + std::string toString(bool _short) const override; - virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; - virtual TypePointer encodingType() const override + MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + TypePointer encodingType() const override { return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : shared_from_this(); } - virtual TypePointer interfaceType(bool _inLibrary) const override; - virtual bool canBeUsedExternally(bool _inLibrary) const override; + TypePointer interfaceType(bool _inLibrary) const override; + bool canBeUsedExternally(bool _inLibrary) const override; TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override; - virtual std::string canonicalName() const override; - virtual std::string signatureInExternalFunction(bool _structsByName) const override; + std::string canonicalName() const override; + std::string signatureInExternalFunction(bool _structsByName) const override; /// @returns a function that performs the type conversion between a list of struct members /// and a memory struct of this type. @@ -872,27 +874,27 @@ private: class EnumType: public Type { public: - virtual Category category() const override { return Category::Enum; } + Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded) const override + TypePointer unaryOperatorResult(Token _operator) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + unsigned calldataEncodedSize(bool _padded) const override { return encodingType()->calldataEncodedSize(_padded); } - virtual unsigned storageBytes() const override; - virtual bool canLiveOutsideStorage() const override { return true; } - virtual std::string toString(bool _short) const override; - virtual std::string canonicalName() const override; - virtual bool isValueType() const override { return true; } - - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer encodingType() const override + unsigned storageBytes() const override; + bool canLiveOutsideStorage() const override { return true; } + std::string toString(bool _short) const override; + std::string canonicalName() const override; + bool isValueType() const override { return true; } + + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypePointer encodingType() const override { return std::make_shared<IntegerType>(8 * int(storageBytes())); } - virtual TypePointer interfaceType(bool _inLibrary) const override + TypePointer interfaceType(bool _inLibrary) const override { return _inLibrary ? shared_from_this() : encodingType(); } @@ -913,21 +915,21 @@ private: class TupleType: public Type { public: - virtual Category category() const override { return Category::Tuple; } + Category category() const override { return Category::Tuple; } explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {} - virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual std::string toString(bool) const override; - virtual bool canBeStored() const override { return false; } - virtual u256 storageSize() const override; - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned sizeOnStack() const override; - virtual bool hasSimpleZeroValueInMemory() const override { return false; } - virtual TypePointer mobileType() const override; + bool isImplicitlyConvertibleTo(Type const& _other) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + std::string toString(bool) const override; + bool canBeStored() const override { return false; } + u256 storageSize() const override; + bool canLiveOutsideStorage() const override { return false; } + unsigned sizeOnStack() const override; + bool hasSimpleZeroValueInMemory() const override { return false; } + TypePointer mobileType() const override; /// Converts components to their temporary types and performs some wildcard matching. - virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const override; + TypePointer closestTemporaryType(TypePointer const& _targetType) const override; std::vector<TypePointer> const& components() const { return m_components; } @@ -987,7 +989,7 @@ public: GasLeft, ///< gasleft() }; - virtual Category category() const override { return Category::Function; } + Category category() const override { return Category::Function; } /// Creates the type of a function. explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); @@ -1061,25 +1063,25 @@ public: /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token _operator) const override; - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override; - virtual std::string canonicalName() const override; - virtual std::string toString(bool _short) const override; - virtual unsigned calldataEncodedSize(bool _padded) const override; - virtual bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } - virtual u256 storageSize() const override; - virtual unsigned storageBytes() const override; - virtual bool isValueType() const override { return true; } - virtual bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } - virtual unsigned sizeOnStack() const override; - virtual bool hasSimpleZeroValueInMemory() const override { return false; } - virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; - virtual TypePointer encodingType() const override; - virtual TypePointer interfaceType(bool _inLibrary) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypePointer unaryOperatorResult(Token _operator) const override; + TypePointer binaryOperatorResult(Token, TypePointer const&) const override; + std::string canonicalName() const override; + std::string toString(bool _short) const override; + unsigned calldataEncodedSize(bool _padded) const override; + bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } + u256 storageSize() const override; + unsigned storageBytes() const override; + bool isValueType() const override { return true; } + bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } + unsigned sizeOnStack() const override; + bool hasSimpleZeroValueInMemory() const override { return false; } + MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + TypePointer encodingType() const override; + TypePointer interfaceType(bool _inLibrary) const override; /// @returns TypePointer of a new FunctionType object. All input/return parameters are an /// appropriate external types (i.e. the interfaceType()s) of input/return parameters of @@ -1154,14 +1156,13 @@ public: /// of the parameters to false. TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const; - /// @returns a copy of this function type where all return parameters of dynamic size are - /// removed and the location of reference types is changed from CallData to Memory. - /// This is needed if external functions are called on other contracts, as they cannot return - /// dynamic values. - /// Returns empty shared pointer on a failure. Namely, if a bound function has no parameters. + /// @returns a copy of this function type where the location of reference types is changed + /// from CallData to Memory. This is the type that would be used when the function is + /// called, as opposed to the parameter types that are available inside the function body. + /// Also supports variants to be used for library or bound calls. /// @param _inLibrary if true, uses DelegateCall as location. - /// @param _bound if true, the arguments are placed as `arg1.functionName(arg2, ..., argn)`. - FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const; + /// @param _bound if true, the function type is set to be bound. + FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const; private: static TypePointers parseElementaryTypeVector(strings const& _types); @@ -1187,27 +1188,27 @@ private: class MappingType: public Type { public: - virtual Category category() const override { return Category::Mapping; } + Category category() const override { return Category::Mapping; } MappingType(TypePointer const& _keyType, TypePointer const& _valueType): m_keyType(_keyType), m_valueType(_valueType) {} - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual std::string toString(bool _short) const override; - virtual std::string canonicalName() const override; - virtual bool canLiveOutsideStorage() const override { return false; } - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual TypePointer encodingType() const override + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + std::string toString(bool _short) const override; + std::string canonicalName() const override; + bool canLiveOutsideStorage() const override { return false; } + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + TypePointer encodingType() const override { return std::make_shared<IntegerType>(256); } - virtual TypePointer interfaceType(bool _inLibrary) const override + TypePointer interfaceType(bool _inLibrary) const override { return _inLibrary ? shared_from_this() : TypePointer(); } - virtual bool dataStoredIn(DataLocation _location) const override { return _location == DataLocation::Storage; } + bool dataStoredIn(DataLocation _location) const override { return _location == DataLocation::Storage; } /// Cannot be stored in memory, but just in case. - virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } + bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } TypePointer const& keyType() const { return m_keyType; } TypePointer const& valueType() const { return m_valueType; } @@ -1225,20 +1226,20 @@ private: class TypeType: public Type { public: - virtual Category category() const override { return Category::TypeType; } + Category category() const override { return Category::TypeType; } explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {} TypePointer const& actualType() const { return m_actualType; } - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual bool canBeStored() const override { return false; } - virtual u256 storageSize() const override; - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned sizeOnStack() const override; - virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } - virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + bool canBeStored() const override { return false; } + u256 storageSize() const override; + bool canLiveOutsideStorage() const override { return false; } + unsigned sizeOnStack() const override; + bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } + std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } + MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; private: TypePointer m_actualType; @@ -1251,18 +1252,18 @@ private: class ModifierType: public Type { public: - virtual Category category() const override { return Category::Modifier; } + Category category() const override { return Category::Modifier; } explicit ModifierType(ModifierDefinition const& _modifier); - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual bool canBeStored() const override { return false; } - virtual u256 storageSize() const override; - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned sizeOnStack() const override { return 0; } - virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual std::string toString(bool _short) const override; + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + bool canBeStored() const override { return false; } + u256 storageSize() const override; + bool canLiveOutsideStorage() const override { return false; } + unsigned sizeOnStack() const override { return 0; } + bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + std::string toString(bool _short) const override; private: TypePointers m_parameterTypes; @@ -1276,20 +1277,20 @@ private: class ModuleType: public Type { public: - virtual Category category() const override { return Category::Module; } + Category category() const override { return Category::Module; } explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {} - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual bool canBeStored() const override { return false; } - virtual bool canLiveOutsideStorage() const override { return true; } - virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - virtual unsigned sizeOnStack() const override { return 0; } - virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + bool canBeStored() const override { return false; } + bool canLiveOutsideStorage() const override { return true; } + bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } + unsigned sizeOnStack() const override { return 0; } + MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; - virtual std::string toString(bool _short) const override; + std::string toString(bool _short) const override; private: SourceUnit const& m_sourceUnit; @@ -1303,24 +1304,24 @@ class MagicType: public Type { public: enum class Kind { Block, Message, Transaction, ABI }; - virtual Category category() const override { return Category::Magic; } + Category category() const override { return Category::Magic; } explicit MagicType(Kind _kind): m_kind(_kind) {} - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual std::string richIdentifier() const override; - virtual bool operator==(Type const& _other) const override; - virtual bool canBeStored() const override { return false; } - virtual bool canLiveOutsideStorage() const override { return true; } - virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - virtual unsigned sizeOnStack() const override { return 0; } - virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + bool canBeStored() const override { return false; } + bool canLiveOutsideStorage() const override { return true; } + bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } + unsigned sizeOnStack() const override { return 0; } + MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; - virtual std::string toString(bool _short) const override; + std::string toString(bool _short) const override; Kind kind() const { return m_kind; } @@ -1335,20 +1336,20 @@ private: class InaccessibleDynamicType: public Type { public: - virtual Category category() const override { return Category::InaccessibleDynamic; } - - virtual std::string richIdentifier() const override { return "t_inaccessible"; } - virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } - virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } - virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } - virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } - virtual bool canBeStored() const override { return false; } - virtual bool canLiveOutsideStorage() const override { return false; } - virtual bool isValueType() const override { return true; } - virtual unsigned sizeOnStack() const override { return 1; } - virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - virtual std::string toString(bool) const override { return "inaccessible dynamic type"; } - virtual TypePointer decodingType() const override { return std::make_shared<IntegerType>(256); } + Category category() const override { return Category::InaccessibleDynamic; } + + std::string richIdentifier() const override { return "t_inaccessible"; } + bool isImplicitlyConvertibleTo(Type const&) const override { return false; } + bool isExplicitlyConvertibleTo(Type const&) const override { return false; } + TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } + bool canBeStored() const override { return false; } + bool canLiveOutsideStorage() const override { return false; } + bool isValueType() const override { return true; } + unsigned sizeOnStack() const override { return 1; } + bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } + std::string toString(bool) const override { return "inaccessible dynamic type"; } + TypePointer decodingType() const override { return std::make_shared<IntegerType>(256); } }; } diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index bd29b382..b02623de 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -558,7 +558,7 @@ string ABIFunctions::abiEncodingFunction( // special case: convert storage reference type to value type - this is only // possible for library calls where we just forward the storage reference solAssert(_encodeAsLibraryTypes, ""); - solAssert(to == IntegerType(256), ""); + solAssert(to == IntegerType::uint256(), ""); templ("cleanupConvert", "value"); } else diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index e9ffe4fb..d2132258 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -22,7 +22,7 @@ #pragma once -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libsolidity/ast/ASTForward.h> diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index d33f749c..4878f9f3 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -25,11 +25,12 @@ #include <libsolidity/codegen/CompilerContext.h> #include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/ast/Types.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/codegen/LValue.h> using namespace std; using namespace dev; +using namespace langutil; using namespace solidity; void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 4028ae63..48d9e9d6 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -23,7 +23,7 @@ #pragma once #include <libsolidity/codegen/CompilerContext.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libevmasm/Assembly.h> diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 6e14d68a..5a3a233c 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -25,14 +25,14 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/codegen/Compiler.h> #include <libsolidity/interface/Version.h> -#include <libsolidity/interface/ErrorReporter.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmCodeGen.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <liblangutil/SourceReferenceFormatter.h> +#include <libyul/AsmParser.h> +#include <libyul/AsmCodeGen.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> #include <libyul/YulString.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Scanner.h> #include <boost/algorithm/string/replace.hpp> @@ -42,11 +42,12 @@ // Change to "define" to output all intermediate code #undef SOL_OUTPUT_ASM #ifdef SOL_OUTPUT_ASM -#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libyul/AsmPrinter.h> #endif using namespace std; +using namespace langutil; namespace dev { @@ -322,7 +323,7 @@ void CompilerContext::appendInlineAssembly( yul::ExternalIdentifierAccess identifierAccess; identifierAccess.resolve = [&]( - assembly::Identifier const& _identifier, + yul::Identifier const& _identifier, yul::IdentifierContext, bool ) @@ -331,7 +332,7 @@ void CompilerContext::appendInlineAssembly( return it == _localVariables.end() ? size_t(-1) : 1; }; identifierAccess.generateCode = [&]( - assembly::Identifier const& _identifier, + yul::Identifier const& _identifier, yul::IdentifierContext _context, yul::AbstractAssembly& _assembly ) @@ -359,20 +360,20 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--"); - auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false); + auto scanner = make_shared<langutil::Scanner>(langutil::CharStream(_assembly, "--CODEGEN--")); + auto parserResult = yul::Parser(errorReporter, yul::AsmFlavour::Strict).parse(scanner, false); #ifdef SOL_OUTPUT_ASM - cout << assembly::AsmPrinter()(*parserResult) << endl; + cout << yul::AsmPrinter()(*parserResult) << endl; #endif - assembly::AsmAnalysisInfo analysisInfo; + yul::AsmAnalysisInfo analysisInfo; bool analyzerResult = false; if (parserResult) - analyzerResult = assembly::AsmAnalyzer( + analyzerResult = yul::AsmAnalyzer( analysisInfo, errorReporter, m_evmVersion, boost::none, - assembly::AsmFlavour::Strict, + yul::AsmFlavour::Strict, identifierAccess.resolve ).analyze(*parserResult); if (!parserResult || !errorReporter.errors().empty() || !analyzerResult) @@ -394,7 +395,7 @@ void CompilerContext::appendInlineAssembly( } solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block."); - assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); + yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); // Reset the source location to the one of the node (instead of the CODEGEN source location) updateSourceLocation(); @@ -413,7 +414,7 @@ FunctionDefinition const& CompilerContext::resolveVirtualFunction( if ( function->name() == name && !function->isConstructor() && - FunctionType(*function).hasEqualParameterTypes(functionType) + FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType) ) return *function; solAssert(false, "Super function " + name + " not found."); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 5bdc1d19..02369813 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -24,7 +24,7 @@ #include <libsolidity/codegen/ABIFunctions.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/Types.h> @@ -167,7 +167,10 @@ public: /// the data. CompilerContext& appendConditionalRevert(bool _forwardReturnData = false); /// Appends a JUMP to a specific tag - CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; } + CompilerContext& appendJumpTo( + eth::AssemblyItem const& _tag, + eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary + ) { *m_asm << _tag.pushTag(); return appendJump(_jumpType); } /// Appends pushing of a new tag and @returns the new tag. eth::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); } /// @returns a new tag without pushing any opcodes or data diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 90eb74fe..7d2ad9d2 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -32,6 +32,7 @@ #include <libdevcore/Whiskers.h> using namespace std; +using namespace langutil; namespace dev { @@ -135,7 +136,7 @@ void CompilerUtils::loadFromMemoryDynamic( void CompilerUtils::storeInMemory(unsigned _offset) { - unsigned numBytes = prepareMemoryStore(IntegerType(256), true); + unsigned numBytes = prepareMemoryStore(IntegerType::uint256(), true); if (numBytes > 0) m_context << u256(_offset) << Instruction::MSTORE; } @@ -149,7 +150,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound ref->location() == DataLocation::Memory, "Only in-memory reference type can be stored." ); - storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries); + storeInMemoryDynamic(IntegerType::uint256(), _padToWordBoundaries); } else if (auto str = dynamic_cast<StringLiteralType const*>(&_type)) { @@ -265,7 +266,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem if (calldataType->isDynamicallySized()) { // put on stack: data_pointer length - loadFromMemoryDynamic(IntegerType(256), !_fromMemory); + loadFromMemoryDynamic(IntegerType::uint256(), !_fromMemory); m_context << Instruction::SWAP1; // stack: input_end base_offset next_pointer data_offset m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"}); @@ -276,7 +277,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem {"input_end", "base_offset", "next_ptr", "array_head_ptr"} ); // retrieve length - loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); + loadFromMemoryDynamic(IntegerType::uint256(), !_fromMemory, true); // stack: input_end base_offset next_pointer array_length data_pointer m_context << Instruction::SWAP2; // stack: input_end base_offset data_pointer array_length next_pointer @@ -429,7 +430,7 @@ void CompilerUtils::encodeToMemory( { auto const& strType = dynamic_cast<StringLiteralType const&>(*_givenTypes[i]); m_context << u256(strType.value().size()); - storeInMemoryDynamic(IntegerType(256), true); + storeInMemoryDynamic(IntegerType::uint256(), true); // stack: ... <end_of_mem'> storeInMemoryDynamic(strType, _padToWordBoundaries); } @@ -444,7 +445,7 @@ void CompilerUtils::encodeToMemory( m_context << dupInstruction(1 + arrayType.sizeOnStack()); ArrayUtils(m_context).retrieveLength(arrayType, 1); // stack: ... <end_of_mem> <value...> <end_of_mem'> <length> - storeInMemoryDynamic(IntegerType(256), true); + storeInMemoryDynamic(IntegerType::uint256(), true); // stack: ... <end_of_mem> <value...> <end_of_mem''> // copy the new memory pointer m_context << swapInstruction(arrayType.sizeOnStack() + 1) << Instruction::POP; @@ -806,7 +807,7 @@ void CompilerUtils::convertType( allocateMemory(); // stack: mempos m_context << Instruction::DUP1 << u256(data.size()); - storeInMemoryDynamic(IntegerType(256)); + storeInMemoryDynamic(IntegerType::uint256()); // stack: mempos datapos storeStringData(data); } @@ -855,7 +856,7 @@ void CompilerUtils::convertType( if (targetType.isDynamicallySized()) { m_context << Instruction::DUP2; - storeInMemoryDynamic(IntegerType(256)); + storeInMemoryDynamic(IntegerType::uint256()); } // stack: <mem start> <source ref> (variably sized) <length> <mem data pos> if (targetType.baseType()->isValueType()) @@ -1209,7 +1210,7 @@ void CompilerUtils::storeStringData(bytesConstRef _data) for (unsigned i = 0; i < _data.size(); i += 32) { m_context << h256::Arith(h256(_data.cropped(i), h256::AlignLeft)); - storeInMemoryDynamic(IntegerType(256)); + storeInMemoryDynamic(IntegerType::uint256()); } m_context << Instruction::POP; } @@ -1236,6 +1237,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda } solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested."); m_context << (_fromCalldata ? Instruction::CALLDATALOAD : Instruction::MLOAD); + bool cleanupNeeded = true; if (isExternalFunctionType) splitExternalFunctionType(true); else if (numBytes != 32) @@ -1245,10 +1247,16 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda int shiftFactor = (32 - numBytes) * 8; rightShiftNumberOnStack(shiftFactor); if (leftAligned) + { leftShiftNumberOnStack(shiftFactor); + cleanupNeeded = false; + } + else if (IntegerType const* intType = dynamic_cast<IntegerType const*>(&_type)) + if (!intType->isSigned()) + cleanupNeeded = false; } if (_fromCalldata) - convertType(_type, _type, true, false, true); + convertType(_type, _type, cleanupNeeded, false, true); return numBytes; } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index bd8170ad..5f7dce22 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -69,7 +69,7 @@ public: /// @returns the number of bytes consumed in memory. unsigned loadFromMemory( unsigned _offset, - Type const& _type = IntegerType(256), + Type const& _type = IntegerType::uint256(), bool _fromCalldata = false, bool _padToWords = false ); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 1fdf3483..aabdbb79 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -21,11 +21,11 @@ */ #include <libsolidity/codegen/ContractCompiler.h> -#include <libsolidity/inlineasm/AsmCodeGen.h> -#include <libsolidity/ast/AST.h> -#include <libsolidity/interface/ErrorReporter.h> #include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/codegen/CompilerUtils.h> +#include <libsolidity/ast/AST.h> +#include <libyul/AsmCodeGen.h> +#include <liblangutil/ErrorReporter.h> #include <libevmasm/Instruction.h> #include <libevmasm/Assembly.h> @@ -37,6 +37,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace @@ -343,7 +344,10 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac m_context << Instruction::DUP1 << Instruction::CALLDATASIZE << Instruction::SUB; CompilerUtils(m_context).abiDecode(functionType->parameterTypes()); } - m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); + m_context.appendJumpTo( + m_context.functionEntryLabel(functionType->declaration()), + eth::AssemblyItem::JumpType::IntoFunction + ); m_context << returnTag; // Return tag and input parameters get consumed. m_context.adjustStackOffset( @@ -495,14 +499,14 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { unsigned startStackHeight = m_context.stackHeight(); yul::ExternalIdentifierAccess identifierAccess; - identifierAccess.resolve = [&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool) + identifierAccess.resolve = [&](yul::Identifier const& _identifier, yul::IdentifierContext, bool) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); if (ref == _inlineAssembly.annotation().externalReferences.end()) return size_t(-1); return ref->second.valueSize; }; - identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, yul::IdentifierContext _context, yul::AbstractAssembly& _assembly) + identifierAccess.generateCode = [&](yul::Identifier const& _identifier, yul::IdentifierContext _context, yul::AbstractAssembly& _assembly) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), ""); @@ -614,7 +618,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } }; solAssert(_inlineAssembly.annotation().analysisInfo, ""); - assembly::CodeGenerator::assemble( + yul::CodeGenerator::assemble( _inlineAssembly.operations(), *_inlineAssembly.annotation().analysisInfo, m_context.nonConstAssembly(), diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 5fa650b1..001aec7c 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -88,22 +88,22 @@ private: void registerStateVariables(ContractDefinition const& _contract); void initializeStateVariables(ContractDefinition const& _contract); - virtual bool visit(VariableDeclaration const& _variableDeclaration) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(InlineAssembly const& _inlineAssembly) override; - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual bool visit(Continue const& _continueStatement) override; - virtual bool visit(Break const& _breakStatement) override; - virtual bool visit(Return const& _return) override; - virtual bool visit(Throw const& _throw) override; - virtual bool visit(EmitStatement const& _emit) override; - virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; - virtual bool visit(ExpressionStatement const& _expressionStatement) override; - virtual bool visit(PlaceholderStatement const&) override; - virtual bool visit(Block const& _block) override; - virtual void endVisit(Block const& _block) override; + bool visit(VariableDeclaration const& _variableDeclaration) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(WhileStatement const& _whileStatement) override; + bool visit(ForStatement const& _forStatement) override; + bool visit(Continue const& _continueStatement) override; + bool visit(Break const& _breakStatement) override; + bool visit(Return const& _return) override; + bool visit(Throw const& _throw) override; + bool visit(EmitStatement const& _emit) override; + bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; + bool visit(ExpressionStatement const& _expressionStatement) override; + bool visit(PlaceholderStatement const&) override; + bool visit(Block const& _block) override; + void endVisit(Block const& _block) override; /// Repeatedly visits all function which are referenced but which are not compiled yet. void appendMissingFunctions(); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index bdf91fbf..121585d9 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -36,6 +36,7 @@ #include <libdevcore/Whiskers.h> using namespace std; +using namespace langutil; namespace dev { @@ -528,6 +529,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { bool shortcutTaken = false; if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression())) + { + solAssert(!function.bound(), ""); if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration)) { // Do not directly visit the identifier, because this way, we can avoid @@ -536,6 +539,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef), false); shortcutTaken = true; } + } if (!shortcutTaken) _functionCall.expression().accept(*this); @@ -627,7 +631,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.expression().accept(*this); arguments.front()->accept(*this); - utils().convertType(*arguments.front()->annotation().type, IntegerType(256), true); + utils().convertType(*arguments.front()->annotation().type, IntegerType::uint256(), true); // Note that function is not the original function, but the ".gas" function. // Its values of gasSet and valueSet is equal to the original function's though. unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0); @@ -810,13 +814,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case FunctionType::Kind::MulMod: { arguments[2]->accept(*this); - utils().convertType(*arguments[2]->annotation().type, IntegerType(256)); + utils().convertType(*arguments[2]->annotation().type, IntegerType::uint256()); m_context << Instruction::DUP1 << Instruction::ISZERO; m_context.appendConditionalInvalid(); for (unsigned i = 1; i < 3; i ++) { arguments[2 - i]->accept(*this); - utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256)); + utils().convertType(*arguments[2 - i]->annotation().type, IntegerType::uint256()); } if (function.kind() == FunctionType::Kind::AddMod) m_context << Instruction::ADDMOD; @@ -900,7 +904,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Fetch requested length. arguments[0]->accept(*this); - utils().convertType(*arguments[0]->annotation().type, IntegerType(256)); + utils().convertType(*arguments[0]->annotation().type, IntegerType::uint256()); // Stack: requested_length utils().fetchFreeMemoryPointer(); @@ -1149,7 +1153,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) if (dynamic_cast<ContractType const*>(type->actualType().get())) { solAssert(_memberAccess.annotation().type, "_memberAccess has no type"); - if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) + if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) + appendVariable(*variable, static_cast<Expression const&>(_memberAccess)); + else if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) { switch (funType->kind()) { @@ -1195,8 +1201,6 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { // no-op } - else if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) - appendVariable(*variable, static_cast<Expression const&>(_memberAccess)); else _memberAccess.expression().accept(*this); } @@ -1448,7 +1452,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) TypePointers{keyType} ); m_context << Instruction::SWAP1; - utils().storeInMemoryDynamic(IntegerType(256)); + utils().storeInMemoryDynamic(IntegerType::uint256()); utils().toSizeAfterFreeMemoryPointer(); } else @@ -1457,7 +1461,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) appendExpressionCopyToMemory(*keyType, *_indexAccess.indexExpression()); m_context << Instruction::SWAP1; solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); - utils().storeInMemoryDynamic(IntegerType(256)); + utils().storeInMemoryDynamic(IntegerType::uint256()); m_context << u256(0); } m_context << Instruction::KECCAK256; @@ -1470,7 +1474,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) solAssert(_indexAccess.indexExpression(), "Index expression expected."); _indexAccess.indexExpression()->accept(*this); - utils().convertType(*_indexAccess.indexExpression()->annotation().type, IntegerType(256), true); + utils().convertType(*_indexAccess.indexExpression()->annotation().type, IntegerType::uint256(), true); // stack layout: <base_ref> [<length>] <index> ArrayUtils(m_context).accessIndex(arrayType); switch (arrayType.location()) @@ -1506,7 +1510,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) solAssert(_indexAccess.indexExpression(), "Index expression expected."); _indexAccess.indexExpression()->accept(*this); - utils().convertType(*_indexAccess.indexExpression()->annotation().type, IntegerType(256), true); + utils().convertType(*_indexAccess.indexExpression()->annotation().type, IntegerType::uint256(), true); // stack layout: <value> <index> // check out-of-bounds access m_context << u256(fixedBytesType.numBytes()); @@ -1869,6 +1873,8 @@ void ExpressionCompiler::appendExternalFunctionCall( retSize = 0; break; } + else if (retType->decodingType()) + retSize += retType->decodingType()->calldataEncodedSize(); else retSize += retType->calldataEncodedSize(); } diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 3d8e8682..2bfaab43 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -25,10 +25,10 @@ #include <memory> #include <boost/noncopyable.hpp> #include <libdevcore/Common.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/codegen/LValue.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> namespace dev { namespace eth @@ -71,17 +71,17 @@ public: void appendConstStateVariableAccessor(const VariableDeclaration& _varDecl); private: - virtual bool visit(Conditional const& _condition) override; - virtual bool visit(Assignment const& _assignment) override; - virtual bool visit(TupleExpression const& _tuple) override; - virtual bool visit(UnaryOperation const& _unaryOperation) override; - virtual bool visit(BinaryOperation const& _binaryOperation) override; - virtual bool visit(FunctionCall const& _functionCall) override; - virtual bool visit(NewExpression const& _newExpression) override; - virtual bool visit(MemberAccess const& _memberAccess) override; - virtual bool visit(IndexAccess const& _indexAccess) override; - virtual void endVisit(Identifier const& _identifier) override; - virtual void endVisit(Literal const& _literal) override; + bool visit(Conditional const& _condition) override; + bool visit(Assignment const& _assignment) override; + bool visit(TupleExpression const& _tuple) override; + bool visit(UnaryOperation const& _unaryOperation) override; + bool visit(BinaryOperation const& _binaryOperation) override; + bool visit(FunctionCall const& _functionCall) override; + bool visit(NewExpression const& _newExpression) override; + bool visit(MemberAccess const& _memberAccess) override; + bool visit(IndexAccess const& _indexAccess) override; + void endVisit(Identifier const& _identifier) override; + void endVisit(Literal const& _literal) override; ///@{ ///@name Append code for various operator types diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 790ab309..6d71d36f 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -28,6 +28,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace solidity; diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h index c576f9de..d854857b 100644 --- a/libsolidity/codegen/LValue.h +++ b/libsolidity/codegen/LValue.h @@ -24,7 +24,7 @@ #include <memory> #include <vector> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include <libsolidity/codegen/ArrayUtils.h> namespace dev @@ -55,17 +55,17 @@ public: /// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true, /// also removes the reference from the stack. /// @a _location source location of the current expression, used for error reporting. - virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const = 0; + virtual void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const = 0; /// Moves a value from the stack to the lvalue. Removes the value if @a _move is true. /// @a _location is the source location of the expression that caused this operation. /// Stack pre: value [lvalue_ref] /// Stack post: if !_move: value_of(lvalue_ref) virtual void storeValue(Type const& _sourceType, - SourceLocation const& _location = SourceLocation(), bool _move = false) const = 0; + langutil::SourceLocation const& _location = {}, bool _move = false) const = 0; /// Stores zero in the lvalue. Removes the reference from the stack if @a _removeReference is true. /// @a _location is the source location of the requested operation virtual void setToZero( - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const = 0; @@ -82,15 +82,15 @@ class StackVariable: public LValue public: StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration); - virtual unsigned sizeOnStack() const override { return 0; } - virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; + unsigned sizeOnStack() const override { return 0; } + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; virtual void storeValue( Type const& _sourceType, - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _move = false ) const override; virtual void setToZero( - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -108,15 +108,15 @@ class MemoryItem: public LValue { public: MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded = true); - virtual unsigned sizeOnStack() const override { return 1; } - virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; + unsigned sizeOnStack() const override { return 1; } + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; virtual void storeValue( Type const& _sourceType, - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _move = false ) const override; virtual void setToZero( - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; private: @@ -136,15 +136,15 @@ public: StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration); /// Constructs the LValue and assumes that the storage reference is already on the stack. StorageItem(CompilerContext& _compilerContext, Type const& _type); - virtual unsigned sizeOnStack() const override { return 2; } - virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; + unsigned sizeOnStack() const override { return 2; } + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; virtual void storeValue( Type const& _sourceType, - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _move = false ) const override; virtual void setToZero( - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; }; @@ -158,15 +158,15 @@ class StorageByteArrayElement: public LValue public: /// Constructs the LValue and assumes that the storage reference is already on the stack. StorageByteArrayElement(CompilerContext& _compilerContext); - virtual unsigned sizeOnStack() const override { return 2; } - virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; + unsigned sizeOnStack() const override { return 2; } + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; virtual void storeValue( Type const& _sourceType, - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _move = false ) const override; virtual void setToZero( - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; }; @@ -181,14 +181,14 @@ class StorageArrayLength: public LValue public: /// Constructs the LValue, assumes that the reference to the array head is already on the stack. StorageArrayLength(CompilerContext& _compilerContext, ArrayType const& _arrayType); - virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; virtual void storeValue( Type const& _sourceType, - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _move = false ) const override; virtual void setToZero( - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -205,15 +205,15 @@ public: /// Constructs the LValue assuming that the other LValues are present on the stack. /// Empty unique_ptrs are possible if e.g. some values should be ignored during assignment. TupleObject(CompilerContext& _compilerContext, std::vector<std::unique_ptr<LValue>>&& _lvalues); - virtual unsigned sizeOnStack() const override; - virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; + unsigned sizeOnStack() const override; + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; virtual void storeValue( Type const& _sourceType, - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _move = false ) const override; virtual void setToZero( - SourceLocation const& _location = SourceLocation(), + langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; diff --git a/libsolidity/formal/CVC4Interface.cpp b/libsolidity/formal/CVC4Interface.cpp index 6cb91483..de5e4430 100644 --- a/libsolidity/formal/CVC4Interface.cpp +++ b/libsolidity/formal/CVC4Interface.cpp @@ -17,7 +17,7 @@ #include <libsolidity/formal/CVC4Interface.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/CommonIO.h> @@ -33,8 +33,7 @@ CVC4Interface::CVC4Interface(): void CVC4Interface::reset() { - m_constants.clear(); - m_functions.clear(); + m_variables.clear(); m_solver.reset(); m_solver.setOption("produce-models", true); m_solver.setTimeLimit(queryTimeout); @@ -50,25 +49,10 @@ void CVC4Interface::pop() m_solver.pop(); } -void CVC4Interface::declareFunction(string _name, Sort _domain, Sort _codomain) +void CVC4Interface::declareVariable(string const& _name, Sort const& _sort) { - if (!m_functions.count(_name)) - { - CVC4::Type fType = m_context.mkFunctionType(cvc4Sort(_domain), cvc4Sort(_codomain)); - m_functions.insert({_name, m_context.mkVar(_name.c_str(), fType)}); - } -} - -void CVC4Interface::declareInteger(string _name) -{ - if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.integerType())}); -} - -void CVC4Interface::declareBool(string _name) -{ - if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.booleanType())}); + if (!m_variables.count(_name)) + m_variables.insert({_name, m_context.mkVar(_name.c_str(), cvc4Sort(_sort))}); } void CVC4Interface::addAssertion(Expression const& _expr) @@ -129,20 +113,19 @@ pair<CheckResult, vector<string>> CVC4Interface::check(vector<Expression> const& CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr) { - if (_expr.arguments.empty() && m_constants.count(_expr.name)) - return m_constants.at(_expr.name); + // Variable + if (_expr.arguments.empty() && m_variables.count(_expr.name)) + return m_variables.at(_expr.name); + vector<CVC4::Expr> arguments; for (auto const& arg: _expr.arguments) arguments.push_back(toCVC4Expr(arg)); string const& n = _expr.name; - if (m_functions.count(n)) - return m_context.mkExpr(CVC4::kind::APPLY_UF, m_functions[n], arguments); - else if (m_constants.count(n)) - { - solAssert(arguments.empty(), ""); - return m_constants.at(n); - } + // Function application + if (!arguments.empty() && m_variables.count(_expr.name)) + return m_context.mkExpr(CVC4::kind::APPLY_UF, m_variables.at(n), arguments); + // Literal else if (arguments.empty()) { if (n == "true") @@ -181,19 +164,33 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr) return m_context.mkExpr(CVC4::kind::MULT, arguments[0], arguments[1]); else if (n == "/") return m_context.mkExpr(CVC4::kind::INTS_DIVISION_TOTAL, arguments[0], arguments[1]); + else if (n == "select") + return m_context.mkExpr(CVC4::kind::SELECT, arguments[0], arguments[1]); + else if (n == "store") + return m_context.mkExpr(CVC4::kind::STORE, arguments[0], arguments[1], arguments[2]); // Cannot reach here. solAssert(false, ""); return arguments[0]; } -CVC4::Type CVC4Interface::cvc4Sort(Sort _sort) +CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort) { - switch (_sort) + switch (_sort.kind) { - case Sort::Bool: + case Kind::Bool: return m_context.booleanType(); - case Sort::Int: + case Kind::Int: return m_context.integerType(); + case Kind::Function: + { + FunctionSort const& fSort = dynamic_cast<FunctionSort const&>(_sort); + return m_context.mkFunctionType(cvc4Sort(fSort.domain), cvc4Sort(*fSort.codomain)); + } + case Kind::Array: + { + auto const& arraySort = dynamic_cast<ArraySort const&>(_sort); + return m_context.mkArrayType(cvc4Sort(*arraySort.domain), cvc4Sort(*arraySort.range)); + } default: break; } @@ -201,3 +198,11 @@ CVC4::Type CVC4Interface::cvc4Sort(Sort _sort) // Cannot be reached. return m_context.integerType(); } + +vector<CVC4::Type> CVC4Interface::cvc4Sort(vector<SortPointer> const& _sorts) +{ + vector<CVC4::Type> cvc4Sorts; + for (auto const& _sort: _sorts) + cvc4Sorts.push_back(cvc4Sort(*_sort)); + return cvc4Sorts; +} diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h index cd6d761d..bbe23855 100644 --- a/libsolidity/formal/CVC4Interface.h +++ b/libsolidity/formal/CVC4Interface.h @@ -51,21 +51,19 @@ public: void push() override; void pop() override; - void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; - void declareInteger(std::string _name) override; - void declareBool(std::string _name) override; + void declareVariable(std::string const&, Sort const&) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; private: CVC4::Expr toCVC4Expr(Expression const& _expr); - CVC4::Type cvc4Sort(smt::Sort _sort); + CVC4::Type cvc4Sort(smt::Sort const& _sort); + std::vector<CVC4::Type> cvc4Sort(std::vector<smt::SortPointer> const& _sorts); CVC4::ExprManager m_context; CVC4::SmtEngine m_solver; - std::map<std::string, CVC4::Expr> m_constants; - std::map<std::string, CVC4::Expr> m_functions; + std::map<std::string, CVC4::Expr> m_variables; }; } diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index cc580021..ebb09f0a 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -22,19 +22,29 @@ #include <libsolidity/formal/VariableUsage.h> #include <libsolidity/formal/SymbolicTypes.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <boost/range/adaptor/map.hpp> #include <boost/algorithm/string/replace.hpp> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; -SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readFileCallback): - m_interface(make_shared<smt::SMTPortfolio>(_readFileCallback)), +SMTChecker::SMTChecker(ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses): + m_interface(make_shared<smt::SMTPortfolio>(_smtlib2Responses)), m_errorReporter(_errorReporter) { +#if defined (HAVE_Z3) || defined (HAVE_CVC4) + if (!_smtlib2Responses.empty()) + m_errorReporter.warning( + "SMT-LIB2 query responses were given in the auxiliary input, " + "but this Solidity binary uses an SMT solver (Z3/CVC4) directly." + "These responses will be ignored." + "Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses." + ); +#endif } void SMTChecker::analyze(SourceUnit const& _source, shared_ptr<Scanner> const& _scanner) @@ -78,6 +88,9 @@ bool SMTChecker::visit(FunctionDefinition const& _function) m_interface->reset(); m_pathConditions.clear(); m_expressions.clear(); + m_specialVariables.clear(); + m_uninterpretedFunctions.clear(); + m_uninterpretedTerms.clear(); resetStateVariables(); initializeLocalVariables(_function); } @@ -310,9 +323,6 @@ void SMTChecker::endVisit(UnaryOperation const& _op) ); break; } - case Token::Add: // + - defineExpr(_op, expr(_op.subExpression())); - break; case Token::Sub: // - { defineExpr(_op, 0 - expr(_op.subExpression())); @@ -405,16 +415,24 @@ void SMTChecker::visitGasLeft(FunctionCall const& _funCall) auto const& symbolicVar = m_specialVariables.at(gasLeft); unsigned index = symbolicVar->index(); // We set the current value to unknown anyway to add type constraints. - symbolicVar->setUnknownValue(); + setUnknownValue(*symbolicVar); if (index > 0) m_interface->addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1)); } void SMTChecker::visitBlockHash(FunctionCall const& _funCall) { - string blockHash = "blockhash()"; - // TODO Define blockhash as an uninterpreted function - defineSpecialVariable(blockHash, _funCall); + string blockHash = "blockhash"; + auto const& arguments = _funCall.arguments(); + solAssert(arguments.size() == 1, ""); + smt::SortPointer paramSort = smtSort(*arguments.at(0)->annotation().type); + smt::SortPointer returnSort = smtSort(*_funCall.annotation().type); + defineUninterpretedFunction( + blockHash, + make_shared<smt::FunctionSort>(vector<smt::SortPointer>{paramSort}, returnSort) + ); + defineExpr(_funCall, m_uninterpretedFunctions.at(blockHash)({expr(*arguments.at(0))})); + m_uninterpretedTerms.push_back(&_funCall); } void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall) @@ -451,6 +469,14 @@ void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall) else if (_funDef && _funDef->isImplemented()) { vector<smt::Expression> funArgs; + auto const& funType = dynamic_cast<FunctionType const*>(_calledExpr->annotation().type.get()); + solAssert(funType, ""); + if (funType->bound()) + { + auto const& boundFunction = dynamic_cast<MemberAccess const*>(_calledExpr); + solAssert(boundFunction, ""); + funArgs.push_back(expr(boundFunction->expression())); + } for (auto arg: _funCall.arguments()) funArgs.push_back(expr(*arg)); initializeFunctionCallParameters(*_funDef, funArgs); @@ -542,6 +568,10 @@ void SMTChecker::endVisit(Return const& _return) bool SMTChecker::visit(MemberAccess const& _memberAccess) { + auto const& accessType = _memberAccess.annotation().type; + if (accessType->category() == Type::Category::Function) + return true; + auto const& exprType = _memberAccess.expression().annotation().type; solAssert(exprType, ""); if (exprType->category() == Type::Category::Magic) @@ -573,7 +603,7 @@ void SMTChecker::defineSpecialVariable(string const& _name, Expression const& _e { auto result = newSymbolicVariable(*_expr.annotation().type, _name, *m_interface); m_specialVariables.emplace(_name, result.second); - result.second->setUnknownValue(); + setUnknownValue(*result.second); if (result.first) m_errorReporter.warning( _expr.location(), @@ -587,6 +617,11 @@ void SMTChecker::defineSpecialVariable(string const& _name, Expression const& _e defineExpr(_expr, m_specialVariables.at(_name)->currentValue()); } +void SMTChecker::defineUninterpretedFunction(string const& _name, smt::SortPointer _sort) +{ + if (!m_uninterpretedFunctions.count(_name)) + m_uninterpretedFunctions.emplace(_name, m_interface->newVariable(_name, _sort)); +} void SMTChecker::arithmeticOperation(BinaryOperation const& _op) { @@ -770,6 +805,11 @@ void SMTChecker::checkCondition( expressionsToEvaluate.emplace_back(var.second->currentValue()); expressionNames.push_back(var.first); } + for (auto const& uf: m_uninterpretedTerms) + { + expressionsToEvaluate.emplace_back(expr(*uf)); + expressionNames.push_back(m_scanner->sourceAt(uf->location())); + } } smt::CheckResult result; vector<string> values; @@ -846,6 +886,10 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co { // everything fine. } + else if (positiveResult == smt::CheckResult::UNKNOWN || negatedResult == smt::CheckResult::UNKNOWN) + { + // can't do anything. + } else if (positiveResult == smt::CheckResult::UNSATISFIABLE && negatedResult == smt::CheckResult::UNSATISFIABLE) m_errorReporter.warning(_condition.location(), "Condition unreachable."); else @@ -1037,13 +1081,23 @@ smt::Expression SMTChecker::newValue(VariableDeclaration const& _decl) void SMTChecker::setZeroValue(VariableDeclaration const& _decl) { solAssert(knownVariable(_decl), ""); - m_variables.at(&_decl)->setZeroValue(); + setZeroValue(*m_variables.at(&_decl)); +} + +void SMTChecker::setZeroValue(SymbolicVariable& _variable) +{ + smt::setSymbolicZeroValue(_variable, *m_interface); } void SMTChecker::setUnknownValue(VariableDeclaration const& _decl) { solAssert(knownVariable(_decl), ""); - m_variables.at(&_decl)->setUnknownValue(); + setUnknownValue(*m_variables.at(&_decl)); +} + +void SMTChecker::setUnknownValue(SymbolicVariable& _variable) +{ + smt::setSymbolicUnknownValue(_variable, *m_interface); } smt::Expression SMTChecker::expr(Expression const& _e) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index a7f955dd..34724848 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -25,50 +25,60 @@ #include <libsolidity/interface/ReadFile.h> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <unordered_map> #include <string> #include <vector> +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + namespace dev { namespace solidity { class VariableUsage; -class ErrorReporter; class SMTChecker: private ASTConstVisitor { public: - SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readCallback); + SMTChecker(langutil::ErrorReporter& _errorReporter, std::map<h256, std::string> const& _smtlib2Responses); + + void analyze(SourceUnit const& _sources, std::shared_ptr<langutil::Scanner> const& _scanner); - void analyze(SourceUnit const& _sources, std::shared_ptr<Scanner> const& _scanner); + /// This is used if the SMT solver is not directly linked into this binary. + /// @returns a list of inputs to the SMT solver that were not part of the argument to + /// the constructor. + std::vector<std::string> unhandledQueries() { return m_interface->unhandledQueries(); } private: // TODO: Check that we do not have concurrent reads and writes to a variable, // because the order of expression evaluation is undefined // TODO: or just force a certain order, but people might have a different idea about that. - virtual bool visit(ContractDefinition const& _node) override; - virtual void endVisit(ContractDefinition const& _node) override; - virtual void endVisit(VariableDeclaration const& _node) override; - virtual bool visit(FunctionDefinition const& _node) override; - virtual void endVisit(FunctionDefinition const& _node) override; - virtual bool visit(IfStatement const& _node) override; - virtual bool visit(WhileStatement const& _node) override; - virtual bool visit(ForStatement const& _node) override; - virtual void endVisit(VariableDeclarationStatement const& _node) override; - virtual void endVisit(Assignment const& _node) override; - virtual void endVisit(TupleExpression const& _node) override; - virtual void endVisit(UnaryOperation const& _node) override; - virtual void endVisit(BinaryOperation const& _node) override; - virtual void endVisit(FunctionCall const& _node) override; - virtual void endVisit(Identifier const& _node) override; - virtual void endVisit(Literal const& _node) override; - virtual void endVisit(Return const& _node) override; - virtual bool visit(MemberAccess const& _node) override; + bool visit(ContractDefinition const& _node) override; + void endVisit(ContractDefinition const& _node) override; + void endVisit(VariableDeclaration const& _node) override; + bool visit(FunctionDefinition const& _node) override; + void endVisit(FunctionDefinition const& _node) override; + bool visit(IfStatement const& _node) override; + bool visit(WhileStatement const& _node) override; + bool visit(ForStatement const& _node) override; + void endVisit(VariableDeclarationStatement const& _node) override; + void endVisit(Assignment const& _node) override; + void endVisit(TupleExpression const& _node) override; + void endVisit(UnaryOperation const& _node) override; + void endVisit(BinaryOperation const& _node) override; + void endVisit(FunctionCall const& _node) override; + void endVisit(Identifier const& _node) override; + void endVisit(Literal const& _node) override; + void endVisit(Return const& _node) override; + bool visit(MemberAccess const& _node) override; void arithmeticOperation(BinaryOperation const& _op); void compareOperation(BinaryOperation const& _op); @@ -83,13 +93,14 @@ private: void inlineFunctionCall(FunctionCall const&); void defineSpecialVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false); + void defineUninterpretedFunction(std::string const& _name, smt::SortPointer _sort); /// Division expression in the given type. Requires special treatment because /// of rounding for signed division. smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type); - void assignment(VariableDeclaration const& _variable, Expression const& _value, SourceLocation const& _location); - void assignment(VariableDeclaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); + void assignment(VariableDeclaration const& _variable, Expression const& _value, langutil::SourceLocation const& _location); + void assignment(VariableDeclaration const& _variable, smt::Expression const& _value, langutil::SourceLocation const& _location); /// Maps a variable to an SSA index. using VariableIndices = std::unordered_map<VariableDeclaration const*, int>; @@ -103,7 +114,7 @@ private: /// Check that a condition can be satisfied. void checkCondition( smt::Expression _condition, - SourceLocation const& _location, + langutil::SourceLocation const& _location, std::string const& _description, std::string const& _additionalValueName = "", smt::Expression* _additionalValue = nullptr @@ -116,7 +127,7 @@ private: std::string const& _description ); /// Checks that the value is in the range given by the type. - void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, SourceLocation const& _location); + void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, langutil::SourceLocation const& _location); std::pair<smt::CheckResult, std::vector<std::string>> @@ -151,8 +162,10 @@ private: /// Sets the value of the declaration to zero. void setZeroValue(VariableDeclaration const& _decl); + void setZeroValue(SymbolicVariable& _variable); /// Resets the variable to an unknown value (in its range). void setUnknownValue(VariableDeclaration const& decl); + void setUnknownValue(SymbolicVariable& _variable); /// Returns the expression corresponding to the AST node. Throws if the expression does not exist. smt::Expression expr(Expression const& _e); @@ -193,9 +206,14 @@ private: std::unordered_map<Expression const*, std::shared_ptr<SymbolicVariable>> m_expressions; std::unordered_map<VariableDeclaration const*, std::shared_ptr<SymbolicVariable>> m_variables; std::unordered_map<std::string, std::shared_ptr<SymbolicVariable>> m_specialVariables; + /// Stores the declaration of an Uninterpreted Function. + std::unordered_map<std::string, smt::Expression> m_uninterpretedFunctions; + /// Stores the instances of an Uninterpreted Function applied to arguments. + /// Used to retrieve models. + std::vector<Expression const*> m_uninterpretedTerms; std::vector<smt::Expression> m_pathConditions; - ErrorReporter& m_errorReporter; - std::shared_ptr<Scanner> m_scanner; + langutil::ErrorReporter& m_errorReporter; + std::shared_ptr<langutil::Scanner> m_scanner; /// Stores the current path of function calls. std::vector<FunctionDefinition const*> m_functionPath; diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index a6c1f87c..3cfa01b1 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -17,9 +17,11 @@ #include <libsolidity/formal/SMTLib2Interface.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/interface/ReadFile.h> +#include <libdevcore/Keccak256.h> + #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/join.hpp> #include <boost/filesystem/operations.hpp> @@ -37,8 +39,8 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::smt; -SMTLib2Interface::SMTLib2Interface(ReadCallback::Callback const& _queryCallback): - m_queryCallback(_queryCallback) +SMTLib2Interface::SMTLib2Interface(map<h256, string> const& _queryResponses): + m_queryResponses(_queryResponses) { reset(); } @@ -47,8 +49,7 @@ void SMTLib2Interface::reset() { m_accumulatedOutput.clear(); m_accumulatedOutput.emplace_back(); - m_constants.clear(); - m_functions.clear(); + m_variables.clear(); write("(set-option :produce-models true)"); write("(set-logic QF_UFLIA)"); } @@ -64,42 +65,39 @@ void SMTLib2Interface::pop() m_accumulatedOutput.pop_back(); } -void SMTLib2Interface::declareFunction(string _name, Sort _domain, Sort _codomain) +void SMTLib2Interface::declareVariable(string const& _name, Sort const& _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) + ')'); + } +} + +void SMTLib2Interface::declareFunction(string const& _name, Sort const& _sort) { + solAssert(_sort.kind == smt::Kind::Function, ""); // TODO Use domain and codomain as key as well - if (!m_functions.count(_name)) + if (!m_variables.count(_name)) { - m_functions.insert(_name); + FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort); + string domain = toSmtLibSort(fSort.domain); + string codomain = toSmtLibSort(*fSort.codomain); + m_variables.insert(_name); write( "(declare-fun |" + _name + - "| (" + - (_domain == Sort::Int ? "Int" : "Bool") + - ") " + - (_codomain == Sort::Int ? "Int" : "Bool") + + "| " + + domain + + " " + + codomain + ")" ); } } -void SMTLib2Interface::declareInteger(string _name) -{ - if (!m_constants.count(_name)) - { - m_constants.insert(_name); - write("(declare-const |" + _name + "| Int)"); - } -} - -void SMTLib2Interface::declareBool(string _name) -{ - if (!m_constants.count(_name)) - { - m_constants.insert(_name); - write("(declare-const |" + _name + "| Bool)"); - } -} - void SMTLib2Interface::addAssertion(Expression const& _expr) { write("(assert " + toSExpr(_expr) + ")"); @@ -140,6 +138,33 @@ string SMTLib2Interface::toSExpr(Expression const& _expr) return sexpr; } +string SMTLib2Interface::toSmtLibSort(Sort const& _sort) +{ + switch (_sort.kind) + { + case Kind::Int: + return "Int"; + case Kind::Bool: + return "Bool"; + case Kind::Array: + { + auto const& arraySort = dynamic_cast<ArraySort const&>(_sort); + return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')'; + } + default: + solAssert(false, "Invalid SMT sort"); + } +} + +string SMTLib2Interface::toSmtLibSort(vector<SortPointer> const& _sorts) +{ + string ssort("("); + for (auto const& sort: _sorts) + ssort += toSmtLibSort(*sort) + " "; + ssort += ")"; + return ssort; +} + void SMTLib2Interface::write(string _data) { solAssert(!m_accumulatedOutput.empty(), ""); @@ -157,8 +182,8 @@ string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _ for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) { auto const& e = _expressionsToEvaluate.at(i); - solAssert(e.sort == Sort::Int || e.sort == Sort::Bool, "Invalid sort for expression to evaluate."); - command += "(declare-const |EVALEXPR_" + to_string(i) + "| " + (e.sort == Sort::Int ? "Int" : "Bool") + ")\n"; + solAssert(e.sort->kind == Kind::Int || e.sort->kind == Kind::Bool, "Invalid sort for expression to evaluate."); + command += "(declare-const |EVALEXPR_" + to_string(i) + "| " + (e.sort->kind == Kind::Int ? "Int" : "Bool") + ")\n"; command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + toSExpr(e) + "))\n"; } command += "(check-sat)\n"; @@ -189,11 +214,12 @@ vector<string> SMTLib2Interface::parseValues(string::const_iterator _start, stri string SMTLib2Interface::querySolver(string const& _input) { - if (!m_queryCallback) - BOOST_THROW_EXCEPTION(SolverError() << errinfo_comment("No SMT solver available.")); - - ReadCallback::Result queryResult = m_queryCallback(_input); - if (!queryResult.success) - BOOST_THROW_EXCEPTION(SolverError() << errinfo_comment(queryResult.responseOrErrorMessage)); - return queryResult.responseOrErrorMessage; + 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/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index eb876a7f..55fc4096 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -19,9 +19,11 @@ #include <libsolidity/formal/SolverInterface.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/interface/ReadFile.h> +#include <libdevcore/FixedHash.h> + #include <libdevcore/Common.h> #include <boost/noncopyable.hpp> @@ -42,22 +44,26 @@ namespace smt class SMTLib2Interface: public SolverInterface, public boost::noncopyable { public: - explicit SMTLib2Interface(ReadCallback::Callback const& _queryCallback); + explicit SMTLib2Interface(std::map<h256, std::string> const& _queryResponses); void reset() override; void push() override; void pop() override; - void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; - void declareInteger(std::string _name) override; - void declareBool(std::string _name) override; + void declareVariable(std::string const&, Sort const&) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; + std::vector<std::string> unhandledQueries() override { return m_unhandledQueries; } + private: + void declareFunction(std::string const&, Sort const&); + std::string toSExpr(Expression const& _expr); + std::string toSmtLibSort(Sort const& _sort); + std::string toSmtLibSort(std::vector<SortPointer> const& _sort); void write(std::string _data); @@ -67,10 +73,11 @@ private: /// Communicates with the solver via the callback. Throws SMTSolverError on error. std::string querySolver(std::string const& _input); - ReadCallback::Callback m_queryCallback; std::vector<std::string> m_accumulatedOutput; - std::set<std::string> m_constants; - std::set<std::string> m_functions; + std::set<std::string> m_variables; + + std::map<h256, std::string> const& m_queryResponses; + std::vector<std::string> m_unhandledQueries; }; } diff --git a/libsolidity/formal/SMTPortfolio.cpp b/libsolidity/formal/SMTPortfolio.cpp index 8b9fe9ce..2a109b89 100644 --- a/libsolidity/formal/SMTPortfolio.cpp +++ b/libsolidity/formal/SMTPortfolio.cpp @@ -23,27 +23,22 @@ #ifdef HAVE_CVC4 #include <libsolidity/formal/CVC4Interface.h> #endif -#if !defined (HAVE_Z3) && !defined (HAVE_CVC4) #include <libsolidity/formal/SMTLib2Interface.h> -#endif using namespace std; using namespace dev; using namespace dev::solidity; using namespace dev::solidity::smt; -SMTPortfolio::SMTPortfolio(ReadCallback::Callback const& _readCallback) +SMTPortfolio::SMTPortfolio(map<h256, string> const& _smtlib2Responses) { + m_solvers.emplace_back(make_shared<smt::SMTLib2Interface>(_smtlib2Responses)); #ifdef HAVE_Z3 m_solvers.emplace_back(make_shared<smt::Z3Interface>()); #endif #ifdef HAVE_CVC4 m_solvers.emplace_back(make_shared<smt::CVC4Interface>()); #endif -#if !defined (HAVE_Z3) && !defined (HAVE_CVC4) - m_solvers.emplace_back(make_shared<smt::SMTLib2Interface>(_readCallback)), -#endif - (void)_readCallback; } void SMTPortfolio::reset() @@ -64,22 +59,10 @@ void SMTPortfolio::pop() s->pop(); } -void SMTPortfolio::declareFunction(string _name, Sort _domain, Sort _codomain) -{ - for (auto s : m_solvers) - s->declareFunction(_name, _domain, _codomain); -} - -void SMTPortfolio::declareInteger(string _name) -{ - for (auto s : m_solvers) - s->declareInteger(_name); -} - -void SMTPortfolio::declareBool(string _name) +void SMTPortfolio::declareVariable(string const& _name, Sort const& _sort) { for (auto s : m_solvers) - s->declareBool(_name); + s->declareVariable(_name, _sort); } void SMTPortfolio::addAssertion(Expression const& _expr) diff --git a/libsolidity/formal/SMTPortfolio.h b/libsolidity/formal/SMTPortfolio.h index 96c7ff57..7f5ba37e 100644 --- a/libsolidity/formal/SMTPortfolio.h +++ b/libsolidity/formal/SMTPortfolio.h @@ -22,8 +22,11 @@ #include <libsolidity/interface/ReadFile.h> +#include <libdevcore/FixedHash.h> + #include <boost/noncopyable.hpp> +#include <map> #include <vector> namespace dev @@ -42,20 +45,19 @@ namespace smt class SMTPortfolio: public SolverInterface, public boost::noncopyable { public: - SMTPortfolio(ReadCallback::Callback const& _readCallback); + SMTPortfolio(std::map<h256, std::string> const& _smtlib2Responses); void reset() override; void push() override; void pop() override; - void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; - void declareInteger(std::string _name) override; - void declareBool(std::string _name) override; + void declareVariable(std::string const&, Sort const&) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; + std::vector<std::string> unhandledQueries() override { return m_solvers.at(0)->unhandledQueries(); } private: static bool solverAnswered(CheckResult result); diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index af1cc8e4..4a4b3fb1 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -17,7 +17,7 @@ #pragma once -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/interface/ReadFile.h> #include <libdevcore/Common.h> @@ -42,12 +42,68 @@ enum class CheckResult SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR }; -enum class Sort +enum class Kind { Int, Bool, - IntIntFun, // Function of one Int returning a single Int - IntBoolFun // Function of one Int returning a single Bool + Function, + Array +}; + +struct Sort +{ + Sort(Kind _kind): + kind(_kind) {} + virtual ~Sort() = default; + virtual bool operator==(Sort const& _other) const { return kind == _other.kind; } + + Kind const kind; +}; +using SortPointer = std::shared_ptr<Sort>; + +struct FunctionSort: public Sort +{ + FunctionSort(std::vector<SortPointer> _domain, SortPointer _codomain): + Sort(Kind::Function), domain(std::move(_domain)), codomain(std::move(_codomain)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherFunction = dynamic_cast<FunctionSort const*>(&_other); + solAssert(_otherFunction, ""); + if (domain.size() != _otherFunction->domain.size()) + return false; + if (!std::equal( + domain.begin(), + domain.end(), + _otherFunction->domain.begin(), + [&](SortPointer _a, SortPointer _b) { return *_a == *_b; } + )) + return false; + return *codomain == *_otherFunction->codomain; + } + + std::vector<SortPointer> domain; + SortPointer codomain; +}; + +struct ArraySort: public Sort +{ + /// _domain is the sort of the indices + /// _range is the sort of the values + ArraySort(SortPointer _domain, SortPointer _range): + Sort(Kind::Array), domain(std::move(_domain)), range(std::move(_range)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherArray = dynamic_cast<ArraySort const*>(&_other); + solAssert(_otherArray, ""); + return *domain == *_otherArray->domain && *range == *_otherArray->range; + } + + SortPointer domain; + SortPointer range; }; /// C++ representation of an SMTLIB2 expression. @@ -55,10 +111,10 @@ class Expression { friend class SolverInterface; public: - explicit Expression(bool _v): name(_v ? "true" : "false"), sort(Sort::Bool) {} - Expression(size_t _number): name(std::to_string(_number)), sort(Sort::Int) {} - Expression(u256 const& _number): name(_number.str()), sort(Sort::Int) {} - Expression(bigint const& _number): name(_number.str()), sort(Sort::Int) {} + explicit Expression(bool _v): Expression(_v ? "true" : "false", Kind::Bool) {} + Expression(size_t _number): Expression(std::to_string(_number), Kind::Int) {} + Expression(u256 const& _number): Expression(_number.str(), Kind::Int) {} + Expression(bigint const& _number): Expression(_number.str(), Kind::Int) {} Expression(Expression const&) = default; Expression(Expression&&) = default; @@ -80,17 +136,20 @@ public: {"+", 2}, {"-", 2}, {"*", 2}, - {"/", 2} + {"/", 2}, + {"select", 2}, + {"store", 3} }; return operatorsArity.count(name) && operatorsArity.at(name) == arguments.size(); } static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue) { - solAssert(_trueValue.sort == _falseValue.sort, ""); + solAssert(*_trueValue.sort == *_falseValue.sort, ""); + SortPointer sort = _trueValue.sort; return Expression("ite", std::vector<Expression>{ std::move(_condition), std::move(_trueValue), std::move(_falseValue) - }, _trueValue.sort); + }, std::move(sort)); } static Expression implies(Expression _a, Expression _b) @@ -98,21 +157,51 @@ public: return !std::move(_a) || std::move(_b); } + /// select is the SMT representation of an array index access. + static Expression select(Expression _array, Expression _index) + { + solAssert(_array.sort->kind == Kind::Array, ""); + auto const& arraySort = dynamic_cast<ArraySort const*>(_array.sort.get()); + solAssert(arraySort, ""); + solAssert(*arraySort->domain == *_index.sort, ""); + return Expression( + "select", + std::vector<Expression>{std::move(_array), std::move(_index)}, + arraySort->range + ); + } + + /// store is the SMT representation of an assignment to array index. + /// The function is pure and returns the modified array. + static Expression store(Expression _array, Expression _index, Expression _element) + { + solAssert(_array.sort->kind == Kind::Array, ""); + auto const& arraySort = dynamic_cast<ArraySort const*>(_array.sort.get()); + solAssert(arraySort, ""); + solAssert(*arraySort->domain == *_index.sort, ""); + solAssert(*arraySort->range == *_element.sort, ""); + return Expression( + "store", + std::vector<Expression>{std::move(_array), std::move(_index), std::move(_element)}, + _array.sort + ); + } + friend Expression operator!(Expression _a) { - return Expression("not", std::move(_a), Sort::Bool); + return Expression("not", std::move(_a), Kind::Bool); } friend Expression operator&&(Expression _a, Expression _b) { - return Expression("and", std::move(_a), std::move(_b), Sort::Bool); + return Expression("and", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator||(Expression _a, Expression _b) { - return Expression("or", std::move(_a), std::move(_b), Sort::Bool); + return Expression("or", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator==(Expression _a, Expression _b) { - return Expression("=", std::move(_a), std::move(_b), Sort::Bool); + return Expression("=", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator!=(Expression _a, Expression _b) { @@ -120,72 +209,64 @@ public: } friend Expression operator<(Expression _a, Expression _b) { - return Expression("<", std::move(_a), std::move(_b), Sort::Bool); + return Expression("<", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator<=(Expression _a, Expression _b) { - return Expression("<=", std::move(_a), std::move(_b), Sort::Bool); + return Expression("<=", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator>(Expression _a, Expression _b) { - return Expression(">", std::move(_a), std::move(_b), Sort::Bool); + return Expression(">", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator>=(Expression _a, Expression _b) { - return Expression(">=", std::move(_a), std::move(_b), Sort::Bool); + return Expression(">=", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator+(Expression _a, Expression _b) { - return Expression("+", std::move(_a), std::move(_b), Sort::Int); + return Expression("+", std::move(_a), std::move(_b), Kind::Int); } friend Expression operator-(Expression _a, Expression _b) { - return Expression("-", std::move(_a), std::move(_b), Sort::Int); + return Expression("-", std::move(_a), std::move(_b), Kind::Int); } friend Expression operator*(Expression _a, Expression _b) { - return Expression("*", std::move(_a), std::move(_b), Sort::Int); + return Expression("*", std::move(_a), std::move(_b), Kind::Int); } friend Expression operator/(Expression _a, Expression _b) { - return Expression("/", std::move(_a), std::move(_b), Sort::Int); + return Expression("/", std::move(_a), std::move(_b), Kind::Int); } - Expression operator()(Expression _a) const + Expression operator()(std::vector<Expression> _arguments) const { solAssert( - arguments.empty(), + sort->kind == Kind::Function, "Attempted function application to non-function." ); - switch (sort) - { - case Sort::IntIntFun: - return Expression(name, _a, Sort::Int); - case Sort::IntBoolFun: - return Expression(name, _a, Sort::Bool); - default: - solAssert( - false, - "Attempted function application to invalid type." - ); - break; - } + auto fSort = dynamic_cast<FunctionSort const*>(sort.get()); + solAssert(fSort, ""); + return Expression(name, std::move(_arguments), fSort->codomain); } std::string name; std::vector<Expression> arguments; - Sort sort; + SortPointer sort; private: - /// Manual constructor, should only be used by SolverInterface and this class itself. - Expression(std::string _name, std::vector<Expression> _arguments, Sort _sort): - name(std::move(_name)), arguments(std::move(_arguments)), sort(_sort) {} - - explicit Expression(std::string _name, Sort _sort): - Expression(std::move(_name), std::vector<Expression>{}, _sort) {} - Expression(std::string _name, Expression _arg, Sort _sort): - Expression(std::move(_name), std::vector<Expression>{std::move(_arg)}, _sort) {} - Expression(std::string _name, Expression _arg1, Expression _arg2, Sort _sort): - Expression(std::move(_name), std::vector<Expression>{std::move(_arg1), std::move(_arg2)}, _sort) {} + /// Manual constructors, should only be used by SolverInterface and this class itself. + Expression(std::string _name, std::vector<Expression> _arguments, SortPointer _sort): + name(std::move(_name)), arguments(std::move(_arguments)), sort(std::move(_sort)) {} + Expression(std::string _name, std::vector<Expression> _arguments, Kind _kind): + Expression(std::move(_name), std::move(_arguments), std::make_shared<Sort>(_kind)) {} + + explicit Expression(std::string _name, Kind _kind): + Expression(std::move(_name), std::vector<Expression>{}, _kind) {} + Expression(std::string _name, Expression _arg, Kind _kind): + Expression(std::move(_name), std::vector<Expression>{std::move(_arg)}, _kind) {} + Expression(std::string _name, Expression _arg1, Expression _arg2, Kind _kind): + Expression(std::move(_name), std::vector<Expression>{std::move(_arg1), std::move(_arg2)}, _kind) {} }; DEV_SIMPLE_EXCEPTION(SolverError); @@ -199,36 +280,12 @@ public: virtual void push() = 0; virtual void pop() = 0; - virtual void declareFunction(std::string _name, Sort _domain, Sort _codomain) = 0; - Expression newFunction(std::string _name, Sort _domain, Sort _codomain) - { - declareFunction(_name, _domain, _codomain); - solAssert(_domain == Sort::Int, "Function sort not supported."); - // Subclasses should do something here - switch (_codomain) - { - case Sort::Int: - return Expression(std::move(_name), {}, Sort::IntIntFun); - case Sort::Bool: - return Expression(std::move(_name), {}, Sort::IntBoolFun); - default: - solAssert(false, "Function sort not supported."); - break; - } - } - virtual void declareInteger(std::string _name) = 0; - Expression newInteger(std::string _name) + virtual void declareVariable(std::string const& _name, Sort const& _sort) = 0; + Expression newVariable(std::string _name, SortPointer _sort) { // Subclasses should do something here - declareInteger(_name); - return Expression(std::move(_name), {}, Sort::Int); - } - virtual void declareBool(std::string _name) = 0; - Expression newBool(std::string _name) - { - // Subclasses should do something here - declareBool(_name); - return Expression(std::move(_name), {}, Sort::Bool); + declareVariable(_name, *_sort); + return Expression(std::move(_name), {}, std::move(_sort)); } virtual void addAssertion(Expression const& _expr) = 0; @@ -238,6 +295,9 @@ public: virtual std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) = 0; + /// @returns a list of queries that the system was not able to respond to. + virtual std::vector<std::string> unhandledQueries() { return {}; } + protected: // SMT query timeout in milliseconds. static int const queryTimeout = 10000; diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index 3eb1c1ce..c297c807 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -24,6 +24,50 @@ using namespace std; using namespace dev::solidity; +smt::SortPointer dev::solidity::smtSort(Type const& _type) +{ + switch (smtKind(_type.category())) + { + case smt::Kind::Int: + return make_shared<smt::Sort>(smt::Kind::Int); + case smt::Kind::Bool: + return make_shared<smt::Sort>(smt::Kind::Bool); + case smt::Kind::Function: + { + auto fType = dynamic_cast<FunctionType const*>(&_type); + solAssert(fType, ""); + vector<smt::SortPointer> parameterSorts = smtSort(fType->parameterTypes()); + auto returnTypes = fType->returnParameterTypes(); + // TODO remove this when we support tuples. + solAssert(returnTypes.size() == 1, ""); + smt::SortPointer returnSort = smtSort(*returnTypes.at(0)); + return make_shared<smt::FunctionSort>(parameterSorts, returnSort); + } + case smt::Kind::Array: + { + solUnimplementedAssert(false, "Invalid type"); + } + } + solAssert(false, "Invalid type"); +} + +vector<smt::SortPointer> dev::solidity::smtSort(vector<TypePointer> const& _types) +{ + vector<smt::SortPointer> sorts; + for (auto const& type: _types) + sorts.push_back(smtSort(*type)); + return sorts; +} + +smt::Kind dev::solidity::smtKind(Type::Category _category) +{ + if (isNumber(_category)) + return smt::Kind::Int; + else if (isBool(_category)) + return smt::Kind::Bool; + solAssert(false, "Invalid type"); +} + bool dev::solidity::isSupportedType(Type::Category _category) { return isNumber(_category) || @@ -125,3 +169,32 @@ smt::Expression dev::solidity::maxValue(IntegerType const& _type) { return smt::Expression(_type.maxValue()); } + +void dev::solidity::smt::setSymbolicZeroValue(SymbolicVariable const& _variable, smt::SolverInterface& _interface) +{ + setSymbolicZeroValue(_variable.currentValue(), _variable.type(), _interface); +} + +void dev::solidity::smt::setSymbolicZeroValue(smt::Expression _expr, TypePointer const& _type, smt::SolverInterface& _interface) +{ + if (isInteger(_type->category())) + _interface.addAssertion(_expr == 0); + else if (isBool(_type->category())) + _interface.addAssertion(_expr == smt::Expression(false)); +} + +void dev::solidity::smt::setSymbolicUnknownValue(SymbolicVariable const& _variable, smt::SolverInterface& _interface) +{ + setSymbolicUnknownValue(_variable.currentValue(), _variable.type(), _interface); +} + +void dev::solidity::smt::setSymbolicUnknownValue(smt::Expression _expr, TypePointer const& _type, smt::SolverInterface& _interface) +{ + if (isInteger(_type->category())) + { + auto intType = dynamic_cast<IntegerType const*>(_type.get()); + solAssert(intType, ""); + _interface.addAssertion(_expr >= minValue(*intType)); + _interface.addAssertion(_expr <= maxValue(*intType)); + } +} diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h index dcdd9ea4..984653b3 100644 --- a/libsolidity/formal/SymbolicTypes.h +++ b/libsolidity/formal/SymbolicTypes.h @@ -28,6 +28,12 @@ namespace dev namespace solidity { +/// Returns the SMT sort that models the Solidity type _type. +smt::SortPointer smtSort(Type const& _type); +std::vector<smt::SortPointer> smtSort(std::vector<TypePointer> const& _types); +/// Returns the SMT kind that models the Solidity type type category _category. +smt::Kind smtKind(Type::Category _category); + /// So far int, bool and address are supported. /// Returns true if type is supported. bool isSupportedType(Type::Category _category); @@ -49,5 +55,15 @@ std::pair<bool, std::shared_ptr<SymbolicVariable>> newSymbolicVariable(Type cons smt::Expression minValue(IntegerType const& _type); smt::Expression maxValue(IntegerType const& _type); +namespace smt +{ + +void setSymbolicZeroValue(SymbolicVariable const& _variable, smt::SolverInterface& _interface); +void setSymbolicZeroValue(smt::Expression _expr, TypePointer const& _type, smt::SolverInterface& _interface); +void setSymbolicUnknownValue(SymbolicVariable const& _variable, smt::SolverInterface& _interface); +void setSymbolicUnknownValue(smt::Expression _expr, TypePointer const& _type, smt::SolverInterface& _interface); + +} + } } diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 85818ba0..efaeb97a 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -37,6 +37,11 @@ SymbolicVariable::SymbolicVariable( { } +string SymbolicVariable::currentName() const +{ + return uniqueSymbol(m_ssa->index()); +} + string SymbolicVariable::uniqueSymbol(unsigned _index) const { return m_uniqueName + "_" + to_string(_index); @@ -54,16 +59,7 @@ SymbolicBoolVariable::SymbolicBoolVariable( smt::Expression SymbolicBoolVariable::valueAtIndex(int _index) const { - return m_interface.newBool(uniqueSymbol(_index)); -} - -void SymbolicBoolVariable::setZeroValue() -{ - m_interface.addAssertion(currentValue() == smt::Expression(false)); -} - -void SymbolicBoolVariable::setUnknownValue() -{ + return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Bool)); } SymbolicIntVariable::SymbolicIntVariable( @@ -78,20 +74,7 @@ SymbolicIntVariable::SymbolicIntVariable( smt::Expression SymbolicIntVariable::valueAtIndex(int _index) const { - return m_interface.newInteger(uniqueSymbol(_index)); -} - -void SymbolicIntVariable::setZeroValue() -{ - m_interface.addAssertion(currentValue() == 0); -} - -void SymbolicIntVariable::setUnknownValue() -{ - auto intType = dynamic_cast<IntegerType const*>(m_type.get()); - solAssert(intType, ""); - m_interface.addAssertion(currentValue() >= minValue(*intType)); - m_interface.addAssertion(currentValue() <= maxValue(*intType)); + return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Int)); } SymbolicAddressVariable::SymbolicAddressVariable( diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index 4fd9b245..fcf32760 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -51,6 +51,8 @@ public: return valueAtIndex(m_ssa->index()); } + std::string currentName() const; + virtual smt::Expression valueAtIndex(int _index) const = 0; smt::Expression increaseIndex() @@ -62,20 +64,15 @@ public: unsigned index() const { return m_ssa->index(); } unsigned& index() { return m_ssa->index(); } - /// Sets the var to the default value of its type. - /// Inherited types must implement. - virtual void setZeroValue() = 0; - /// The unknown value is the full range of valid values. - /// It is sub-type dependent, but not mandatory. - virtual void setUnknownValue() {} + TypePointer const& type() const { return m_type; } protected: std::string uniqueSymbol(unsigned _index) const; - TypePointer m_type = nullptr; + TypePointer m_type; std::string m_uniqueName; smt::SolverInterface& m_interface; - std::shared_ptr<SSAVariable> m_ssa = nullptr; + std::shared_ptr<SSAVariable> m_ssa; }; /** @@ -90,11 +87,6 @@ public: smt::SolverInterface& _interface ); - /// Sets the var to false. - void setZeroValue(); - /// Does nothing since the SMT solver already knows the valid values for Bool. - void setUnknownValue(); - protected: smt::Expression valueAtIndex(int _index) const; }; @@ -111,11 +103,6 @@ public: smt::SolverInterface& _interface ); - /// Sets the var to 0. - void setZeroValue(); - /// Sets the variable to the full valid value range. - void setUnknownValue(); - protected: smt::Expression valueAtIndex(int _index) const; }; diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 9a0ccf48..cb01dc61 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -17,7 +17,7 @@ #include <libsolidity/formal/Z3Interface.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/CommonIO.h> @@ -51,22 +51,22 @@ void Z3Interface::pop() m_solver.pop(); } -void Z3Interface::declareFunction(string _name, Sort _domain, Sort _codomain) +void Z3Interface::declareVariable(string const& _name, Sort const& _sort) { - if (!m_functions.count(_name)) - m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(_domain), z3Sort(_codomain))}); -} - -void Z3Interface::declareInteger(string _name) -{ - if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.int_const(_name.c_str())}); + 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))}); } -void Z3Interface::declareBool(string _name) +void Z3Interface::declareFunction(string const& _name, Sort const& _sort) { - if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.bool_const(_name.c_str())}); + solAssert(_sort.kind == smt::Kind::Function, ""); + if (!m_functions.count(_name)) + { + FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort); + m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain))}); + } } void Z3Interface::addAssertion(Expression const& _expr) @@ -163,19 +163,28 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return arguments[0] * arguments[1]; else if (n == "/") return arguments[0] / arguments[1]; + else if (n == "select") + return z3::select(arguments[0], arguments[1]); + else if (n == "store") + return z3::store(arguments[0], arguments[1], arguments[2]); // Cannot reach here. solAssert(false, ""); return arguments[0]; } -z3::sort Z3Interface::z3Sort(Sort _sort) +z3::sort Z3Interface::z3Sort(Sort const& _sort) { - switch (_sort) + switch (_sort.kind) { - case Sort::Bool: + case Kind::Bool: return m_context.bool_sort(); - case Sort::Int: + case Kind::Int: return m_context.int_sort(); + case Kind::Array: + { + auto const& arraySort = dynamic_cast<ArraySort const&>(_sort); + return m_context.array_sort(z3Sort(*arraySort.domain), z3Sort(*arraySort.range)); + } default: break; } @@ -183,3 +192,11 @@ z3::sort Z3Interface::z3Sort(Sort _sort) // Cannot be reached. return m_context.int_sort(); } + +z3::sort_vector Z3Interface::z3Sort(vector<SortPointer> const& _sorts) +{ + z3::sort_vector z3Sorts(m_context); + for (auto const& _sort: _sorts) + z3Sorts.push_back(z3Sort(*_sort)); + return z3Sorts; +} diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h index 84880ff3..86e1badd 100644 --- a/libsolidity/formal/Z3Interface.h +++ b/libsolidity/formal/Z3Interface.h @@ -40,16 +40,17 @@ public: void push() override; void pop() override; - void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; - void declareInteger(std::string _name) override; - void declareBool(std::string _name) override; + void declareVariable(std::string const& _name, Sort const& _sort) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; private: + void declareFunction(std::string const& _name, Sort const& _sort); + z3::expr toZ3Expr(Expression const& _expr); - z3::sort z3Sort(smt::Sort _sort); + z3::sort z3Sort(smt::Sort const& _sort); + z3::sort_vector z3Sort(std::vector<smt::SortPointer> const& _sorts); z3::context m_context; z3::solver m_solver; diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h deleted file mode 100644 index 72048975..00000000 --- a/libsolidity/inlineasm/AsmPrinter.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2017 - * Converts a parsed assembly into its textual form. - */ - -#pragma once - -#include <libsolidity/inlineasm/AsmDataForward.h> - -#include <libyul/YulString.h> - -#include <boost/variant.hpp> - -namespace dev -{ -namespace solidity -{ -namespace assembly -{ - -class AsmPrinter: public boost::static_visitor<std::string> -{ -public: - explicit AsmPrinter(bool _yul = false): m_yul(_yul) {} - - std::string operator()(assembly::Instruction const& _instruction); - std::string operator()(assembly::Literal const& _literal); - std::string operator()(assembly::Identifier const& _identifier); - std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction); - std::string operator()(assembly::ExpressionStatement const& _expr); - std::string operator()(assembly::Label const& _label); - std::string operator()(assembly::StackAssignment const& _assignment); - std::string operator()(assembly::Assignment const& _assignment); - std::string operator()(assembly::VariableDeclaration const& _variableDeclaration); - std::string operator()(assembly::FunctionDefinition const& _functionDefinition); - std::string operator()(assembly::FunctionCall const& _functionCall); - std::string operator()(assembly::If const& _if); - std::string operator()(assembly::Switch const& _switch); - std::string operator()(assembly::ForLoop const& _forLoop); - std::string operator()(assembly::Block const& _block); - -private: - std::string formatTypedName(TypedName _variable) const; - std::string appendTypeName(yul::YulString _type) const; - - bool m_yul = false; -}; - -} -} -} diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 26496de7..f5eb7e41 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -22,37 +22,40 @@ #include <libsolidity/interface/AssemblyStack.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/inlineasm/AsmPrinter.h> -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmCodeGen.h> +#include <liblangutil/Scanner.h> +#include <libyul/AsmPrinter.h> +#include <libyul/AsmParser.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmCodeGen.h> +#include <libyul/backends/evm/EVMCodeTransform.h> +#include <libyul/backends/evm/EVMAssembly.h> +#include <libyul/ObjectParser.h> #include <libevmasm/Assembly.h> -#include <libyul/backends/evm/EVMCodeTransform.h> -#include <libyul/backends/evm/EVMAssembly.h> +#include <libyul/optimiser/Suite.h> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace { -assembly::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language) +yul::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language) { switch (_language) { case AssemblyStack::Language::Assembly: - return assembly::AsmFlavour::Loose; + return yul::AsmFlavour::Loose; case AssemblyStack::Language::StrictAssembly: - return assembly::AsmFlavour::Strict; + return yul::AsmFlavour::Strict; case AssemblyStack::Language::Yul: - return assembly::AsmFlavour::Yul; + return yul::AsmFlavour::Yul; } solAssert(false, ""); - return assembly::AsmFlavour::Yul; + return yul::AsmFlavour::Yul; } } @@ -68,31 +71,30 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string { m_errors.clear(); m_analysisSuccessful = false; - m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName); - m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); + m_scanner = make_shared<Scanner>(CharStream(_source, _sourceName)); + m_parserResult = yul::ObjectParser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); if (!m_errorReporter.errors().empty()) return false; solAssert(m_parserResult, ""); + solAssert(m_parserResult->code, ""); return analyzeParsed(); } -bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scanner) +void AssemblyStack::optimize() { - m_errors.clear(); - m_analysisSuccessful = false; - if (_scanner) - m_scanner = make_shared<Scanner>(*_scanner); - m_parserResult = make_shared<assembly::Block>(_block); - - return analyzeParsed(); + solAssert(m_language != Language::Assembly, "Optimization requested for loose assembly."); + yul::OptimiserSuite::run(*m_parserResult->code, *m_parserResult->analysisInfo); + solAssert(analyzeParsed(), "Invalid source code after optimization."); } bool AssemblyStack::analyzeParsed() { - m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); - assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language)); - m_analysisSuccessful = analyzer.analyze(*m_parserResult); + solAssert(m_parserResult, ""); + solAssert(m_parserResult->code, ""); + m_parserResult->analysisInfo = make_shared<yul::AsmAnalysisInfo>(); + yul::AsmAnalyzer analyzer(*m_parserResult->analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language)); + m_analysisSuccessful = analyzer.analyze(*m_parserResult->code); return m_analysisSuccessful; } @@ -100,7 +102,8 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { solAssert(m_analysisSuccessful, ""); solAssert(m_parserResult, ""); - solAssert(m_analysisInfo, ""); + solAssert(m_parserResult->code, ""); + solAssert(m_parserResult->analysisInfo, ""); switch (_machine) { @@ -108,7 +111,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { MachineAssemblyObject object; eth::Assembly assembly; - assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly); + yul::CodeGenerator::assemble(*m_parserResult->code, *m_parserResult->analysisInfo, assembly); object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble()); object.assembly = assembly.assemblyString(); return object; @@ -117,7 +120,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { MachineAssemblyObject object; yul::EVMAssembly assembly(true); - yul::CodeTransform(assembly, *m_analysisInfo, m_language == Language::Yul, true)(*m_parserResult); + yul::CodeTransform(assembly, *m_parserResult->analysisInfo, m_language == Language::Yul, true)(*m_parserResult->code); object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize()); /// TODO: fill out text representation return object; @@ -132,5 +135,6 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const string AssemblyStack::print() const { solAssert(m_parserResult, ""); - return assembly::AsmPrinter(m_language == Language::Yul)(*m_parserResult); + solAssert(m_parserResult->code, ""); + return m_parserResult->toString(m_language == Language::Yul) + "\n"; } diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 8132ce63..0d04ffec 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -21,24 +21,26 @@ #pragma once -#include <libsolidity/interface/ErrorReporter.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/EVMVersion.h> + +#include <libyul/Object.h> +#include <libyul/ObjectParser.h> #include <libevmasm/LinkerObject.h> #include <string> #include <memory> +namespace langutil +{ +class Scanner; +} + namespace dev { namespace solidity { -class Scanner; -namespace assembly -{ -struct AsmAnalysisInfo; -struct Block; -} struct MachineAssemblyObject { @@ -61,21 +63,20 @@ public: {} /// @returns the scanner used during parsing - Scanner const& scanner() const; + langutil::Scanner const& scanner() const; /// Runs parsing and analysis steps, returns false if input cannot be assembled. /// Multiple calls overwrite the previous state. bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source); - /// Runs analysis step on the supplied block, returns false if input cannot be assembled. - /// Multiple calls overwrite the previous state. - bool analyze(assembly::Block const& _block, Scanner const* _scanner = nullptr); + /// Run the optimizer suite. Can only be used with Yul or strict assembly. + void optimize(); /// Run the assembly step (should only be called after parseAndAnalyze). MachineAssemblyObject assemble(Machine _machine) const; /// @returns the errors generated during parsing, analysis (and potentially assembly). - ErrorList const& errors() const { return m_errors; } + langutil::ErrorList const& errors() const { return m_errors; } /// Pretty-print the input after having parsed it. std::string print() const; @@ -86,13 +87,12 @@ private: Language m_language = Language::Assembly; EVMVersion m_evmVersion; - std::shared_ptr<Scanner> m_scanner; + std::shared_ptr<langutil::Scanner> m_scanner; bool m_analysisSuccessful = false; - std::shared_ptr<assembly::Block> m_parserResult; - std::shared_ptr<assembly::AsmAnalysisInfo> m_analysisInfo; - ErrorList m_errors; - ErrorReporter m_errorReporter; + std::shared_ptr<yul::Object> m_parserResult; + langutil::ErrorList m_errors; + langutil::ErrorReporter m_errorReporter; }; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 441c7897..610caea1 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -27,8 +27,8 @@ #include <libsolidity/interface/Version.h> #include <libsolidity/analysis/SemVerHandler.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Parser.h> +#include <libsolidity/analysis/ContractLevelChecker.h> #include <libsolidity/analysis/ControlFlowAnalyzer.h> #include <libsolidity/analysis/ControlFlowGraph.h> #include <libsolidity/analysis/GlobalContext.h> @@ -45,10 +45,12 @@ #include <libsolidity/interface/Natspec.h> #include <libsolidity/interface/GasEstimator.h> -#include <libevmasm/Exceptions.h> - #include <libyul/YulString.h> +#include <liblangutil/Scanner.h> + +#include <libevmasm/Exceptions.h> + #include <libdevcore/SwarmHash.h> #include <libdevcore/JSON.h> @@ -58,6 +60,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping) @@ -106,6 +109,8 @@ void CompilerStack::reset(bool _keepSources) m_stackState = Empty; m_sources.clear(); } + m_smtlib2Responses.clear(); + m_unhandledSMTLib2Queries.clear(); m_libraries.clear(); m_evmVersion = EVMVersion(); m_optimize = false; @@ -121,7 +126,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool { bool existed = m_sources.count(_name) != 0; reset(true); - m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name); + m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content, _name)); m_sources[_name].isLibrary = _isLibrary; m_stackState = SourcesSet; return existed; @@ -156,7 +161,7 @@ bool CompilerStack::parse() { string const& newPath = newSource.first; string const& newContents = newSource.second; - m_sources[newPath].scanner = make_shared<Scanner>(CharStream(newContents), newPath); + m_sources[newPath].scanner = make_shared<Scanner>(CharStream(newContents, newPath)); sourcesToParse.push_back(newPath); } } @@ -221,8 +226,21 @@ bool CompilerStack::analyze() m_contracts[contract->fullyQualifiedName()].contract = contract; } - // This cannot be done in the above loop, because cross-contract types couldn't be resolved. - // A good example is `LibraryName.TypeName x;`. + // Next, we check inheritance, overrides, function collisions and other things at + // contract or function level. + // This also calculates whether a contract is abstract, which is needed by the + // type checker. + ContractLevelChecker contractLevelChecker(m_errorReporter); + for (Source const* source: m_sourceOrder) + for (ASTPointer<ASTNode> const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) + if (!contractLevelChecker.check(*contract)) + noErrors = false; + + // New we run full type checks that go down to the expression level. This + // cannot be done earlier, because we need cross-contract types and information + // about whether a contract is abstract for the `new` expression. + // This populates the `type` annotation for all expressions. // // Note: this does not resolve overloaded functions. In order to do that, types of arguments are needed, // which is only done one step later. @@ -282,9 +300,10 @@ bool CompilerStack::analyze() if (noErrors) { - SMTChecker smtChecker(m_errorReporter, m_smtQuery); + SMTChecker smtChecker(m_errorReporter, m_smtlib2Responses); for (Source const* source: m_sourceOrder) smtChecker.analyze(*source->ast, source->scanner); + m_unhandledSMTLib2Queries += smtChecker.unhandledQueries(); } } catch(FatalError const&) @@ -329,13 +348,14 @@ bool CompilerStack::compile() if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) if (isRequestedContract(*contract)) compileContract(*contract, compiledContracts); - this->link(); m_stackState = CompilationSuccessful; + this->link(); return true; } void CompilerStack::link() { + solAssert(m_stackState >= CompilationSuccessful, ""); for (auto& contract: m_contracts) { contract.second.object.link(m_libraries); @@ -353,6 +373,19 @@ vector<string> CompilerStack::contractNames() const return contractNames; } +string const CompilerStack::lastContractName() const +{ + if (m_stackState < AnalysisSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + // try to find some user-supplied contract + string contractName; + for (auto const& it: m_sources) + for (ASTPointer<ASTNode> const& node: it.second.ast->nodes()) + if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) + contractName = contract->fullyQualifiedName(); + return contractName; +} + eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const { Contract const& currentContract = contract(_contractName); @@ -389,6 +422,9 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const { + if (m_stackState < AnalysisSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); + // Look up the contract (by its fully-qualified name) Contract const& matchContract = m_contracts.at(_contractName); // Check to see if it could collide on name @@ -576,14 +612,15 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati int startColumn; int endLine; int endColumn; - tie(startLine, startColumn) = scanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.start); - tie(endLine, endColumn) = scanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.end); + tie(startLine, startColumn) = scanner(_sourceLocation.source->name()).translatePositionToLineColumn(_sourceLocation.start); + tie(endLine, endColumn) = scanner(_sourceLocation.source->name()).translatePositionToLineColumn(_sourceLocation.end); return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn); } StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath) { + solAssert(m_stackState < ParsingSuccessful, ""); StringMap newSources; for (auto const& node: _ast.nodes()) if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) @@ -617,6 +654,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string string CompilerStack::applyRemapping(string const& _path, string const& _context) { + solAssert(m_stackState < ParsingSuccessful, ""); // Try to find the longest prefix match in all remappings that are active in the current context. auto isPrefixOf = [](string const& _a, string const& _b) { @@ -658,6 +696,8 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context void CompilerStack::resolveImports() { + solAssert(m_stackState == ParsingSuccessful, ""); + // topological sorting (depth first search) of the import graph, cutting potential cycles vector<Source const*> sourceOrder; set<Source const*> sourcesSeen; @@ -702,6 +742,8 @@ void CompilerStack::compileContract( map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts ) { + solAssert(m_stackState >= AnalysisSuccessful, ""); + if ( _compiledContracts.count(&_contract) || !_contract.annotation().unimplementedFunctions.empty() || @@ -757,23 +799,9 @@ void CompilerStack::compileContract( _compiledContracts[compiledContract.contract] = &compiler->assembly(); } -string const CompilerStack::lastContractName() const -{ - if (m_contracts.empty()) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); - // try to find some user-supplied contract - string contractName; - for (auto const& it: m_sources) - for (ASTPointer<ASTNode> const& node: it.second.ast->nodes()) - if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) - contractName = contract->fullyQualifiedName(); - return contractName; -} - CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const { - if (m_contracts.empty()) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); + solAssert(m_stackState >= AnalysisSuccessful, ""); auto it = m_contracts.find(_contractName); if (it != m_contracts.end()) @@ -908,8 +936,8 @@ string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) con SourceLocation const& location = item.location(); int length = location.start != -1 && location.end != -1 ? location.end - location.start : -1; int sourceIndex = - location.sourceName && sourceIndicesMap.count(*location.sourceName) ? - sourceIndicesMap.at(*location.sourceName) : + location.source && sourceIndicesMap.count(location.source->name()) ? + sourceIndicesMap.at(location.source->name()) : -1; char jump = '-'; if (item.getJumpType() == eth::AssemblyItem::JumpType::IntoFunction) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 9a15fbf0..2c7add3b 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -23,11 +23,12 @@ #pragma once -#include <libsolidity/interface/ErrorReporter.h> #include <libsolidity/interface/ReadFile.h> -#include <libsolidity/interface/EVMVersion.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/EVMVersion.h> +#include <liblangutil/SourceLocation.h> + #include <libevmasm/LinkerObject.h> #include <libdevcore/Common.h> @@ -43,6 +44,11 @@ #include <vector> #include <functional> +namespace langutil +{ +class Scanner; +} + namespace dev { @@ -57,7 +63,6 @@ namespace solidity { // forward declarations -class Scanner; class ASTNode; class ContractDefinition; class FunctionDefinition; @@ -65,7 +70,6 @@ class SourceUnit; class Compiler; class GlobalContext; class Natspec; -class Error; class DeclarationContainer; /** @@ -100,7 +104,7 @@ public: m_errorReporter(m_errorList) {} /// @returns the list of errors that occurred during parsing and type checking. - ErrorList const& errors() const { return m_errorReporter.errors(); } + langutil::ErrorList const& errors() const { return m_errorReporter.errors(); } /// @returns the current state. State state() const { return m_stackState; } @@ -149,6 +153,9 @@ public: /// @returns true if a source object by the name already existed and was replaced. bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false); + /// Adds a response to an SMTLib2 query (identified by the hash of the query input). + void addSMTLib2Response(h256 const& _hash, std::string const& _response) { m_smtlib2Responses[_hash] = _response; } + /// Parses all source units that were added /// @returns false on error. bool parse(); @@ -174,7 +181,7 @@ public: std::map<std::string, unsigned> sourceIndices() const; /// @returns the previously used scanner, useful for counting lines during error reporting. - Scanner const& scanner(std::string const& _sourceName) const; + langutil::Scanner const& scanner(std::string const& _sourceName) const; /// @returns the parsed source unit with the supplied name. SourceUnit const& ast(std::string const& _sourceName) const; @@ -182,7 +189,11 @@ public: /// Helper function for logs printing. Do only use in error cases, it's quite expensive. /// line and columns are numbered starting from 1 with following order: /// start line, start column, end line, end column - std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const; + std::tuple<int, int, int, int> positionFromSourceLocation(langutil::SourceLocation const& _sourceLocation) const; + + /// @returns a list of unhandled queries to the SMT solver (has to be supplied in a second run + /// by calling @a addSMTLib2Response). + std::vector<std::string> const& unhandledSMTLib2Queries() const { return m_unhandledSMTLib2Queries; } /// @returns a list of the contract names in the sources. std::vector<std::string> contractNames() const; @@ -248,7 +259,7 @@ private: /// The state per source unit. Filled gradually during parsing. struct Source { - std::shared_ptr<Scanner> scanner; + std::shared_ptr<langutil::Scanner> scanner; std::shared_ptr<SourceUnit> ast; bool isLibrary = false; void reset() { scanner.reset(); ast.reset(); } @@ -330,7 +341,6 @@ private: ) const; ReadCallback::Callback m_readFile; - ReadCallback::Callback m_smtQuery; bool m_optimize = false; unsigned m_optimizeRuns = 200; EVMVersion m_evmVersion; @@ -340,13 +350,15 @@ private: /// "context:prefix=target" std::vector<Remapping> m_remappings; std::map<std::string const, Source> m_sources; + std::vector<std::string> m_unhandledSMTLib2Queries; + std::map<h256, std::string> m_smtlib2Responses; std::shared_ptr<GlobalContext> m_globalContext; std::vector<Source const*> m_sourceOrder; /// This is updated during compilation. std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes; std::map<std::string const, Contract> m_contracts; - ErrorList m_errorList; - ErrorReporter m_errorReporter; + langutil::ErrorList m_errorList; + langutil::ErrorReporter m_errorReporter; bool m_metadataLiteralSources = false; State m_stackState = Empty; }; diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 1f20366e..de6b2ce5 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -35,6 +35,7 @@ using namespace std; using namespace dev; using namespace dev::eth; +using namespace langutil; using namespace dev::solidity; GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation( diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h index ea94d988..214a3e58 100644 --- a/libsolidity/interface/GasEstimator.h +++ b/libsolidity/interface/GasEstimator.h @@ -22,7 +22,7 @@ #pragma once -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libevmasm/GasMeter.h> #include <libevmasm/Assembly.h> diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index c8b03a94..0eef50d2 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -21,7 +21,7 @@ */ #include <libsolidity/interface/StandardCompiler.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <liblangutil/SourceReferenceFormatter.h> #include <libsolidity/ast/ASTJsonConverter.h> #include <libevmasm/Instruction.h> #include <libdevcore/JSON.h> @@ -31,6 +31,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace { @@ -84,9 +85,9 @@ Json::Value formatErrorWithException( message = _message; Json::Value sourceLocation; - if (location && location->sourceName) + if (location && location->source && location->source->name() != "") { - sourceLocation["file"] = *location->sourceName; + sourceLocation["file"] = location->source->name(); sourceLocation["start"] = location->start; sourceLocation["end"] = location->end; } @@ -318,6 +319,27 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) return formatFatalError("JSONError", "Invalid input source specified."); } + Json::Value const& auxInputs = _input["auxiliaryInput"]; + if (!!auxInputs) + { + Json::Value const& smtlib2Responses = auxInputs["smtlib2responses"]; + if (!!smtlib2Responses) + for (auto const& hashString: smtlib2Responses.getMemberNames()) + { + h256 hash; + try + { + hash = h256(hashString); + } + catch (dev::BadHexCharacter const&) + { + return formatFatalError("JSONError", "Invalid hex encoding of SMTLib2 auxiliary input."); + } + + m_compilerStack.addSMTLib2Response(hash, smtlib2Responses[hashString].asString()); + } + } + Json::Value const& settings = _input.get("settings", Json::Value()); if (settings.isMember("evmVersion")) @@ -411,7 +433,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value outputSelection = settings.get("outputSelection", Json::Value()); m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection)); - auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); }; + auto scannerFromSourceName = [&](string const& _sourceName) -> Scanner const& { return m_compilerStack.scanner(_sourceName); }; try { @@ -517,6 +539,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (errors.size() > 0) output["errors"] = errors; + if (!m_compilerStack.unhandledSMTLib2Queries().empty()) + for (string const& query: m_compilerStack.unhandledSMTLib2Queries()) + output["auxiliaryInputRequested"]["smtlib2queries"]["0x" + keccak256(query).hex()] = query; + output["sources"] = Json::objectValue; unsigned sourceIndex = 0; for (string const& sourceName: analysisSuccess ? m_compilerStack.sourceNames() : vector<string>()) diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index b5f68ce8..b785d557 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -24,7 +24,7 @@ #include <string> #include <libdevcore/CommonData.h> #include <libdevcore/Common.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <solidity/BuildInfo.h> using namespace dev; diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index d9588e5c..d8927fea 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -1,13 +1,14 @@ #include <libsolidity/parsing/DocStringParser.h> -#include <libsolidity/interface/ErrorReporter.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Exceptions.h> #include <boost/range/irange.hpp> #include <boost/range/algorithm.hpp> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; diff --git a/libsolidity/parsing/DocStringParser.h b/libsolidity/parsing/DocStringParser.h index 5f2819cc..c83b416d 100644 --- a/libsolidity/parsing/DocStringParser.h +++ b/libsolidity/parsing/DocStringParser.h @@ -25,19 +25,22 @@ #include <string> #include <libsolidity/ast/ASTAnnotations.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; - class DocStringParser { public: /// Parse the given @a _docString and stores the parsed components internally. /// @returns false on error and appends the error to @a _errors. - bool parse(std::string const& _docString, ErrorReporter& _errorReporter); + bool parse(std::string const& _docString, langutil::ErrorReporter& _errorReporter); std::multimap<std::string, DocTag> const& tags() const { return m_docTags; } @@ -63,7 +66,7 @@ private: /// Mapping tag name -> content. std::multimap<std::string, DocTag> m_docTags; DocTag* m_lastTag = nullptr; - ErrorReporter* m_errorReporter = nullptr; + langutil::ErrorReporter* m_errorReporter = nullptr; bool m_errorsOccurred = false; }; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index b17dad9a..6cab7be3 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -22,13 +22,14 @@ #include <cctype> #include <vector> -#include <libevmasm/SourceLocation.h> #include <libsolidity/parsing/Parser.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <libyul/AsmParser.h> +#include <liblangutil/SourceLocation.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Scanner.h> using namespace std; +using namespace langutil; namespace dev { @@ -41,7 +42,7 @@ class Parser::ASTNodeFactory { public: explicit ASTNodeFactory(Parser const& _parser): - m_parser(_parser), m_location(_parser.position(), -1, _parser.sourceName()) {} + m_parser(_parser), m_location(_parser.position(), -1, _parser.source()) {} ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode): m_parser(_parser), m_location(_childNode->location()) {} @@ -54,7 +55,7 @@ public: template <class NodeType, typename... Args> ASTPointer<NodeType> createNode(Args&& ... _args) { - solAssert(m_location.sourceName, ""); + solAssert(m_location.source, ""); if (m_location.end < 0) markEndPosition(); return make_shared<NodeType>(m_location, std::forward<Args>(_args)...); @@ -1011,8 +1012,8 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con m_scanner->next(); } - assembly::Parser asmParser(m_errorReporter); - shared_ptr<assembly::Block> block = asmParser.parse(m_scanner, true); + yul::Parser asmParser(m_errorReporter); + shared_ptr<yul::Block> block = asmParser.parse(m_scanner, true); nodeFactory.markEndPosition(); return nodeFactory.createNode<InlineAssembly>(_docString, block); } @@ -1554,8 +1555,8 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() expression = nodeFactory.createNode<TupleExpression>(components, isArray); break; } - case Token::IllegalHex: - fatalParserError("Expected even number of hex-nibbles within double-quotes."); + case Token::Illegal: + fatalParserError(to_string(m_scanner->currentError())); break; default: if (TokenTraits::isElementaryTypeName(token)) diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index fa974171..15852096 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -23,21 +23,24 @@ #pragma once #include <libsolidity/ast/AST.h> -#include <libsolidity/parsing/ParserBase.h> +#include <liblangutil/ParserBase.h> + +namespace langutil +{ +class Scanner; +} namespace dev { namespace solidity { -class Scanner; - -class Parser: public ParserBase +class Parser: public langutil::ParserBase { public: - explicit Parser(ErrorReporter& _errorReporter): ParserBase(_errorReporter) {} + explicit Parser(langutil::ErrorReporter& _errorReporter): ParserBase(_errorReporter) {} - ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner); + ASTPointer<SourceUnit> parse(std::shared_ptr<langutil::Scanner> const& _scanner); private: class ASTNodeFactory; @@ -146,7 +149,7 @@ private: struct IndexAccessedPath { std::vector<ASTPointer<PrimaryExpression>> path; - std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> indices; + std::vector<std::pair<ASTPointer<Expression>, langutil::SourceLocation>> indices; bool empty() const; }; diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 81e8dd98..d61aefb6 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -1,378 +1,36 @@ -// Copyright 2006-2012, the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Modifications as part of solidity under the following license: -// -// solidity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// solidity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with solidity. If not, see <http://www.gnu.org/licenses/>. - +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Solidity and Yul both share the same Token (and Scanner) API. + * + * This may (or may not) change in the future. But for the time being, we've put both + * at a shared place, and *just* import them. +*/ #pragma once -#include <libdevcore/Common.h> -#include <libsolidity/interface/Exceptions.h> -#include <libsolidity/parsing/UndefMacros.h> -#include <iosfwd> +#include <liblangutil/Token.h> namespace dev { namespace solidity { +namespace TokenTraits = ::langutil::TokenTraits; -// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the -// same signature M(name, string, precedence), where name is the -// symbolic token name, string is the corresponding syntactic symbol -// (or NULL, for literals), and precedence is the precedence (or 0). -// The parameters are invoked for token categories as follows: -// -// T: Non-keyword tokens -// K: Keyword tokens - -// IGNORE_TOKEN is a convenience macro that can be supplied as -// an argument (at any position) for a TOKEN_LIST call. It does -// nothing with tokens belonging to the respective category. - -#define IGNORE_TOKEN(name, string, precedence) - -#define TOKEN_LIST(T, K) \ - /* End of source indicator. */ \ - T(EOS, "EOS", 0) \ - \ - /* Punctuators (ECMA-262, section 7.7, page 15). */ \ - T(LParen, "(", 0) \ - T(RParen, ")", 0) \ - T(LBrack, "[", 0) \ - T(RBrack, "]", 0) \ - T(LBrace, "{", 0) \ - T(RBrace, "}", 0) \ - T(Colon, ":", 0) \ - T(Semicolon, ";", 0) \ - T(Period, ".", 0) \ - T(Conditional, "?", 3) \ - T(Arrow, "=>", 0) \ - \ - /* Assignment operators. */ \ - /* IsAssignmentOp() relies on this block of enum values being */ \ - /* contiguous and sorted in the same order!*/ \ - T(Assign, "=", 2) \ - /* The following have to be in exactly the same order as the simple binary operators*/ \ - T(AssignBitOr, "|=", 2) \ - T(AssignBitXor, "^=", 2) \ - T(AssignBitAnd, "&=", 2) \ - T(AssignShl, "<<=", 2) \ - T(AssignSar, ">>=", 2) \ - T(AssignShr, ">>>=", 2) \ - T(AssignAdd, "+=", 2) \ - T(AssignSub, "-=", 2) \ - T(AssignMul, "*=", 2) \ - T(AssignDiv, "/=", 2) \ - T(AssignMod, "%=", 2) \ - \ - /* Binary operators sorted by precedence. */ \ - /* IsBinaryOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(Comma, ",", 1) \ - T(Or, "||", 4) \ - T(And, "&&", 5) \ - T(BitOr, "|", 8) \ - T(BitXor, "^", 9) \ - T(BitAnd, "&", 10) \ - T(SHL, "<<", 11) \ - T(SAR, ">>", 11) \ - T(SHR, ">>>", 11) \ - T(Add, "+", 12) \ - T(Sub, "-", 12) \ - T(Mul, "*", 13) \ - T(Div, "/", 13) \ - T(Mod, "%", 13) \ - T(Exp, "**", 14) \ - \ - /* Compare operators sorted by precedence. */ \ - /* IsCompareOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(Equal, "==", 6) \ - T(NotEqual, "!=", 6) \ - T(LessThan, "<", 7) \ - T(GreaterThan, ">", 7) \ - T(LessThanOrEqual, "<=", 7) \ - T(GreaterThanOrEqual, ">=", 7) \ - \ - /* Unary operators. */ \ - /* IsUnaryOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(Not, "!", 0) \ - T(BitNot, "~", 0) \ - T(Inc, "++", 0) \ - T(Dec, "--", 0) \ - K(Delete, "delete", 0) \ - \ - /* Keywords */ \ - K(Anonymous, "anonymous", 0) \ - K(As, "as", 0) \ - K(Assembly, "assembly", 0) \ - K(Break, "break", 0) \ - K(Constant, "constant", 0) \ - K(Constructor, "constructor", 0) \ - K(Continue, "continue", 0) \ - K(Contract, "contract", 0) \ - K(Do, "do", 0) \ - K(Else, "else", 0) \ - K(Enum, "enum", 0) \ - K(Emit, "emit", 0) \ - K(Event, "event", 0) \ - K(External, "external", 0) \ - K(For, "for", 0) \ - K(Function, "function", 0) \ - K(Hex, "hex", 0) \ - K(If, "if", 0) \ - K(Indexed, "indexed", 0) \ - K(Interface, "interface", 0) \ - K(Internal, "internal", 0) \ - K(Import, "import", 0) \ - K(Is, "is", 0) \ - K(Library, "library", 0) \ - K(Mapping, "mapping", 0) \ - K(Memory, "memory", 0) \ - K(Modifier, "modifier", 0) \ - K(New, "new", 0) \ - K(Payable, "payable", 0) \ - K(Public, "public", 0) \ - K(Pragma, "pragma", 0) \ - K(Private, "private", 0) \ - K(Pure, "pure", 0) \ - K(Return, "return", 0) \ - K(Returns, "returns", 0) \ - K(Storage, "storage", 0) \ - K(CallData, "calldata", 0) \ - K(Struct, "struct", 0) \ - K(Throw, "throw", 0) \ - K(Using, "using", 0) \ - K(Var, "var", 0) \ - K(View, "view", 0) \ - K(While, "while", 0) \ - \ - /* Ether subdenominations */ \ - K(SubWei, "wei", 0) \ - K(SubSzabo, "szabo", 0) \ - K(SubFinney, "finney", 0) \ - K(SubEther, "ether", 0) \ - K(SubSecond, "seconds", 0) \ - K(SubMinute, "minutes", 0) \ - K(SubHour, "hours", 0) \ - K(SubDay, "days", 0) \ - K(SubWeek, "weeks", 0) \ - K(SubYear, "years", 0) \ - /* type keywords*/ \ - K(Int, "int", 0) \ - K(UInt, "uint", 0) \ - K(Bytes, "bytes", 0) \ - K(Byte, "byte", 0) \ - K(String, "string", 0) \ - K(Address, "address", 0) \ - K(Bool, "bool", 0) \ - K(Fixed, "fixed", 0) \ - K(UFixed, "ufixed", 0) \ - T(IntM, "intM", 0) \ - T(UIntM, "uintM", 0) \ - T(BytesM, "bytesM", 0) \ - T(FixedMxN, "fixedMxN", 0) \ - T(UFixedMxN, "ufixedMxN", 0) \ - T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ - \ - /* Literals */ \ - K(TrueLiteral, "true", 0) \ - K(FalseLiteral, "false", 0) \ - T(Number, NULL, 0) \ - T(StringLiteral, NULL, 0) \ - T(CommentLiteral, NULL, 0) \ - \ - /* Identifiers (not keywords or future reserved words). */ \ - T(Identifier, NULL, 0) \ - \ - /* Keywords reserved for future use. */ \ - K(Abstract, "abstract", 0) \ - K(After, "after", 0) \ - K(Alias, "alias", 0) \ - K(Apply, "apply", 0) \ - K(Auto, "auto", 0) \ - K(Case, "case", 0) \ - K(Catch, "catch", 0) \ - K(CopyOf, "copyof", 0) \ - K(Default, "default", 0) \ - K(Define, "define", 0) \ - K(Final, "final", 0) \ - K(Immutable, "immutable", 0) \ - K(Implements, "implements", 0) \ - K(In, "in", 0) \ - K(Inline, "inline", 0) \ - K(Let, "let", 0) \ - K(Macro, "macro", 0) \ - K(Match, "match", 0) \ - K(Mutable, "mutable", 0) \ - K(NullLiteral, "null", 0) \ - K(Of, "of", 0) \ - K(Override, "override", 0) \ - K(Partial, "partial", 0) \ - K(Promise, "promise", 0) \ - K(Reference, "reference", 0) \ - K(Relocatable, "relocatable", 0) \ - K(Sealed, "sealed", 0) \ - K(Sizeof, "sizeof", 0) \ - K(Static, "static", 0) \ - K(Supports, "supports", 0) \ - K(Switch, "switch", 0) \ - K(Try, "try", 0) \ - K(Type, "type", 0) \ - K(Typedef, "typedef", 0) \ - K(TypeOf, "typeof", 0) \ - K(Unchecked, "unchecked", 0) \ - \ - /* Illegal token - not able to scan. */ \ - T(Illegal, "ILLEGAL", 0) \ - /* Illegal hex token */ \ - T(IllegalHex, "ILLEGAL_HEX", 0) \ - \ - /* Scanner-internal use only. */ \ - T(Whitespace, NULL, 0) - -// All token values. -// attention! msvc issue: -// http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 -// @todo: avoid TOKEN_LIST macro -enum class Token : unsigned int { -#define T(name, string, precedence) name, - TOKEN_LIST(T, T) - NUM_TOKENS -#undef T -}; - -namespace TokenTraits -{ - constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); } - - // Predicates - constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; } - constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; } - constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } - constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd || - op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; } - constexpr bool isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; } - constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; } - - constexpr bool isBitOp(Token op) { return (Token::BitOr <= op && op <= Token::BitAnd) || op == Token::BitNot; } - constexpr bool isBooleanOp(Token op) { return (Token::Or <= op && op <= Token::And) || op == Token::Not; } - constexpr bool isUnaryOp(Token op) { return (Token::Not <= op && op <= Token::Delete) || op == Token::Add || op == Token::Sub; } - constexpr bool isCountOp(Token op) { return op == Token::Inc || op == Token::Dec; } - constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); } - constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; } - constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; } - constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; } - - constexpr bool isStateMutabilitySpecifier(Token op, bool _allowConstant = true) - { - return (op == Token::Constant && _allowConstant) - || op == Token::Pure || op == Token::View || op == Token::Payable; - } - - constexpr bool isEtherSubdenomination(Token op) { return op == Token::SubWei || op == Token::SubSzabo || op == Token::SubFinney || op == Token::SubEther; } - constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; } - constexpr bool isReservedKeyword(Token op) { return (Token::Abstract <= op && op <= Token::Unchecked); } - - inline Token AssignmentToBinaryOp(Token op) - { - solAssert(isAssignmentOp(op) && op != Token::Assign, ""); - return static_cast<Token>(static_cast<int>(op) + (static_cast<int>(Token::BitOr) - static_cast<int>(Token::AssignBitOr))); - } - - // @returns the precedence > 0 for binary and compare - // operators; returns 0 otherwise. - int precedence(Token tok); - - std::tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal); - - // @returns a string corresponding to the C++ token name - // (e.g. "LT" for the token LT). - char const* name(Token tok); - - // @returns a string corresponding to the JS token string - // (.e., "<" for the token LT) or NULL if the token doesn't - // have a (unique) string (e.g. an IDENTIFIER). - char const* toString(Token tok); - - std::string friendlyName(Token tok); -} - -inline std::ostream& operator<<(std::ostream& os, Token token) -{ - os << TokenTraits::friendlyName(token); - return os; -} - -class ElementaryTypeNameToken -{ -public: - ElementaryTypeNameToken(Token _token, unsigned const& _firstNumber, unsigned const& _secondNumber) - { - assertDetails(_token, _firstNumber, _secondNumber); - } - - unsigned int firstNumber() const { return m_firstNumber; } - unsigned int secondNumber() const { return m_secondNumber; } - Token token() const { return m_token; } - - ///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type - std::string toString(bool const& tokenValue = false) const - { - std::string name = TokenTraits::toString(m_token); - if (tokenValue || (firstNumber() == 0 && secondNumber() == 0)) - return name; - solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here."); - if (m_token == Token::FixedMxN || m_token == Token::UFixedMxN) - return name.substr(0, name.size() - 3) + std::to_string(m_firstNumber) + "x" + std::to_string(m_secondNumber); - else - return name.substr(0, name.size() - 1) + std::to_string(m_firstNumber); - } - -private: - Token m_token; - unsigned int m_firstNumber; - unsigned int m_secondNumber; - /// throws if type is not properly sized - void assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second); -}; - +using ::langutil::Token; +using ::langutil::ElementaryTypeNameToken; } } diff --git a/libyul/ASTDataForward.h b/libyul/ASTDataForward.h deleted file mode 100644 index 8c49e68f..00000000 --- a/libyul/ASTDataForward.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Pull in some identifiers from the solidity::assembly namespace. - */ - -#pragma once - -#include <libsolidity/inlineasm/AsmDataForward.h> - -namespace dev -{ -namespace yul -{ - -using Instruction = solidity::assembly::Instruction; -using Literal = solidity::assembly::Literal; -using Label = solidity::assembly::Label; -using StackAssignment = solidity::assembly::StackAssignment; -using Identifier = solidity::assembly::Identifier; -using Assignment = solidity::assembly::Assignment; -using VariableDeclaration = solidity::assembly::VariableDeclaration; -using FunctionalInstruction = solidity::assembly::FunctionalInstruction; -using FunctionDefinition = solidity::assembly::FunctionDefinition; -using FunctionCall = solidity::assembly::FunctionCall; -using If = solidity::assembly::If; -using Case = solidity::assembly::Case; -using Switch = solidity::assembly::Switch; -using ForLoop = solidity::assembly::ForLoop; -using ExpressionStatement = solidity::assembly::ExpressionStatement; -using Block = solidity::assembly::Block; - -using TypedName = solidity::assembly::TypedName; -class YulString; - -using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; -using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; - -} -} diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index ac019c06..d3f6de84 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -18,14 +18,14 @@ * Analyzer part of inline assembly. */ -#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libyul/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/inlineasm/AsmScopeFiller.h> -#include <libsolidity/inlineasm/AsmScope.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> +#include <libyul/AsmScopeFiller.h> +#include <libyul/AsmScope.h> +#include <libyul/AsmAnalysisInfo.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <boost/range/adaptor/reversed.hpp> #include <boost/algorithm/string.hpp> @@ -35,8 +35,10 @@ using namespace std; using namespace dev; +using namespace langutil; +using namespace yul; +using namespace dev; using namespace dev::solidity; -using namespace dev::solidity::assembly; namespace { @@ -64,7 +66,7 @@ bool AsmAnalyzer::operator()(Label const& _label) return true; } -bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) +bool AsmAnalyzer::operator()(yul::Instruction const& _instruction) { checkLooseFeature( _instruction.location, @@ -77,11 +79,11 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) return true; } -bool AsmAnalyzer::operator()(assembly::Literal const& _literal) +bool AsmAnalyzer::operator()(Literal const& _literal) { expectValidType(_literal.type.str(), _literal.location); ++m_stackHeight; - if (_literal.kind == assembly::LiteralKind::String && _literal.value.str().size() > 32) + if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32) { m_errorReporter.typeError( _literal.location, @@ -89,7 +91,7 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) ); return false; } - else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value.str()) > u256(-1)) + else if (_literal.kind == LiteralKind::Number && bigint(_literal.value.str()) > u256(-1)) { m_errorReporter.typeError( _literal.location, @@ -97,7 +99,7 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) ); return false; } - else if (_literal.kind == assembly::LiteralKind::Boolean) + else if (_literal.kind == LiteralKind::Boolean) { solAssert(m_flavour == AsmFlavour::Yul, ""); solAssert(_literal.value == YulString{string("true")} || _literal.value == YulString{string("false")}, ""); @@ -106,7 +108,7 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) return true; } -bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) +bool AsmAnalyzer::operator()(Identifier const& _identifier) { solAssert(!_identifier.name.empty(), ""); size_t numErrorsBefore = m_errorReporter.errors().size(); @@ -176,7 +178,7 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) return success; } -bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) +bool AsmAnalyzer::operator()(ExpressionStatement const& _statement) { int initialStackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, _statement.expression); @@ -197,7 +199,7 @@ bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) return success; } -bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) +bool AsmAnalyzer::operator()(StackAssignment const& _assignment) { checkLooseFeature( _assignment.location, @@ -208,7 +210,7 @@ bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) return success; } -bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment) +bool AsmAnalyzer::operator()(Assignment const& _assignment) { solAssert(_assignment.value, ""); int const expectedItems = _assignment.variableNames.size(); @@ -234,7 +236,7 @@ bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment) return success; } -bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) +bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) { bool success = true; int const numVariables = _varDecl.variables.size(); @@ -260,7 +262,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) return success; } -bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) +bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) { solAssert(!_funDef.name.empty(), ""); Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); @@ -282,7 +284,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) return success; } -bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) +bool AsmAnalyzer::operator()(FunctionCall const& _funCall) { solAssert(!_funCall.functionName.name.empty(), ""); bool success = true; @@ -396,7 +398,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch) return success; } -bool AsmAnalyzer::operator()(assembly::ForLoop const& _for) +bool AsmAnalyzer::operator()(ForLoop const& _for) { solAssert(_for.condition, ""); @@ -485,7 +487,7 @@ bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation con return true; } -bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t _valueSize) +bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize) { solAssert(!_variable.name.empty(), ""); bool success = true; diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libyul/AsmAnalysis.h index a8673efa..34e32eb0 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -20,14 +20,14 @@ #pragma once -#include <libsolidity/interface/Exceptions.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/Exceptions.h> +#include <liblangutil/EVMVersion.h> -#include <libsolidity/inlineasm/AsmScope.h> +#include <libyul/AsmScope.h> #include <libyul/backends/evm/AbstractAssembly.h> -#include <libsolidity/inlineasm/AsmDataForward.h> +#include <libyul/AsmDataForward.h> #include <boost/variant.hpp> #include <boost/optional.hpp> @@ -35,12 +35,13 @@ #include <functional> #include <memory> -namespace dev -{ -namespace solidity +namespace langutil { class ErrorReporter; -namespace assembly +struct SourceLocation; +} + +namespace yul { struct AsmAnalysisInfo; @@ -55,11 +56,11 @@ class AsmAnalyzer: public boost::static_visitor<bool> public: explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, - ErrorReporter& _errorReporter, - EVMVersion _evmVersion, - boost::optional<Error::Type> _errorTypeForLoose, + langutil::ErrorReporter& _errorReporter, + dev::solidity::EVMVersion _evmVersion, + boost::optional<langutil::Error::Type> _errorTypeForLoose, AsmFlavour _flavour = AsmFlavour::Loose, - yul::ExternalIdentifierAccess::Resolver const& _resolver = yul::ExternalIdentifierAccess::Resolver() + ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver() ): m_resolver(_resolver), m_info(_analysisInfo), @@ -69,41 +70,41 @@ public: m_errorTypeForLoose(_errorTypeForLoose) {} - bool analyze(assembly::Block const& _block); - - bool operator()(assembly::Instruction const&); - bool operator()(assembly::Literal const& _literal); - bool operator()(assembly::Identifier const&); - bool operator()(assembly::FunctionalInstruction const& _functionalInstruction); - bool operator()(assembly::Label const& _label); - bool operator()(assembly::ExpressionStatement const&); - bool operator()(assembly::StackAssignment const&); - bool operator()(assembly::Assignment const& _assignment); - bool operator()(assembly::VariableDeclaration const& _variableDeclaration); - bool operator()(assembly::FunctionDefinition const& _functionDefinition); - bool operator()(assembly::FunctionCall const& _functionCall); - bool operator()(assembly::If const& _if); - bool operator()(assembly::Switch const& _switch); - bool operator()(assembly::ForLoop const& _forLoop); - bool operator()(assembly::Block const& _block); + bool analyze(Block const& _block); + + bool operator()(Instruction const&); + bool operator()(Literal const& _literal); + bool operator()(Identifier const&); + bool operator()(FunctionalInstruction const& _functionalInstruction); + bool operator()(Label const& _label); + bool operator()(ExpressionStatement const&); + bool operator()(StackAssignment const&); + bool operator()(Assignment const& _assignment); + bool operator()(VariableDeclaration const& _variableDeclaration); + bool operator()(FunctionDefinition const& _functionDefinition); + bool operator()(FunctionCall const& _functionCall); + bool operator()(If const& _if); + bool operator()(Switch const& _switch); + bool operator()(ForLoop const& _forLoop); + bool operator()(Block const& _block); private: /// Visits the statement and expects it to deposit one item onto the stack. bool expectExpression(Expression const& _expr); - bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location); + bool expectDeposit(int _deposit, int _oldHeight, langutil::SourceLocation const& _location); /// Verifies that a variable to be assigned to exists and has the same size /// as the value, @a _valueSize, unless that is equal to -1. - bool checkAssignment(assembly::Identifier const& _assignment, size_t _valueSize = size_t(-1)); + bool checkAssignment(Identifier const& _assignment, size_t _valueSize = size_t(-1)); - Scope& scope(assembly::Block const* _block); - void expectValidType(std::string const& type, SourceLocation const& _location); - void warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location); + Scope& scope(Block const* _block); + void expectValidType(std::string const& type, langutil::SourceLocation const& _location); + void warnOnInstructions(dev::solidity::Instruction _instr, langutil::SourceLocation const& _location); /// Depending on @a m_flavour and @a m_errorTypeForLoose, throws an internal compiler /// exception (if the flavour is not Loose), reports an error/warning /// (if m_errorTypeForLoose is set) or does nothing. - void checkLooseFeature(SourceLocation const& _location, std::string const& _description); + void checkLooseFeature(langutil::SourceLocation const& _location, std::string const& _description); int m_stackHeight = 0; yul::ExternalIdentifierAccess::Resolver m_resolver; @@ -112,12 +113,10 @@ private: /// "part of the scope but not yet declared") std::set<Scope::Variable const*> m_activeVariables; AsmAnalysisInfo& m_info; - ErrorReporter& m_errorReporter; - EVMVersion m_evmVersion; + langutil::ErrorReporter& m_errorReporter; + dev::solidity::EVMVersion m_evmVersion; AsmFlavour m_flavour = AsmFlavour::Loose; - boost::optional<Error::Type> m_errorTypeForLoose; + boost::optional<langutil::Error::Type> m_errorTypeForLoose; }; } -} -} diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.cpp b/libyul/AsmAnalysisInfo.cpp index 22318b12..450c0f8f 100644 --- a/libsolidity/inlineasm/AsmAnalysisInfo.cpp +++ b/libyul/AsmAnalysisInfo.cpp @@ -18,9 +18,9 @@ * Information generated during analyzer part of inline assembly. */ -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libyul/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmScope.h> +#include <libyul/AsmScope.h> #include <ostream> diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.h b/libyul/AsmAnalysisInfo.h index bd3b28c4..08a35ade 100644 --- a/libsolidity/inlineasm/AsmAnalysisInfo.h +++ b/libyul/AsmAnalysisInfo.h @@ -20,7 +20,7 @@ #pragma once -#include <libsolidity/inlineasm/AsmDataForward.h> +#include <libyul/AsmDataForward.h> #include <boost/variant.hpp> @@ -28,11 +28,7 @@ #include <memory> #include <vector> -namespace dev -{ -namespace solidity -{ -namespace assembly +namespace yul { struct Scope; @@ -40,13 +36,11 @@ struct Scope; struct AsmAnalysisInfo { using StackHeightInfo = std::map<void const*, int>; - using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>; + using Scopes = std::map<Block const*, std::shared_ptr<Scope>>; Scopes scopes; StackHeightInfo stackHeightInfo; /// Virtual blocks which will be used for scopes for function arguments and return values. - std::map<FunctionDefinition const*, std::shared_ptr<assembly::Block const>> virtualBlocks; + std::map<FunctionDefinition const*, std::shared_ptr<Block const>> virtualBlocks; }; } -} -} diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libyul/AsmCodeGen.cpp index 3a62b232..23bf395d 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libyul/AsmCodeGen.cpp @@ -20,20 +20,21 @@ * Code-generating part of inline assembly. */ -#include <libsolidity/inlineasm/AsmCodeGen.h> +#include <libyul/AsmCodeGen.h> -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/inlineasm/AsmScope.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libyul/AsmParser.h> +#include <libyul/AsmData.h> +#include <libyul/AsmScope.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> + +#include <libyul/backends/evm/AbstractAssembly.h> +#include <libyul/backends/evm/EVMCodeTransform.h> #include <libevmasm/Assembly.h> -#include <libevmasm/SourceLocation.h> #include <libevmasm/Instruction.h> -#include <libyul/backends/evm/AbstractAssembly.h> -#include <libyul/backends/evm/EVMCodeTransform.h> +#include <liblangutil/SourceLocation.h> #include <libdevcore/CommonIO.h> @@ -46,10 +47,11 @@ using namespace std; using namespace dev; +using namespace langutil; +using namespace yul; using namespace dev::solidity; -using namespace dev::solidity::assembly; -class EthAssemblyAdapter: public yul::AbstractAssembly +class EthAssemblyAdapter: public AbstractAssembly { public: explicit EthAssemblyAdapter(eth::Assembly& _assembly): @@ -141,16 +143,16 @@ private: eth::Assembly& m_assembly; }; -void assembly::CodeGenerator::assemble( +void CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - yul::ExternalIdentifierAccess const& _identifierAccess, + ExternalIdentifierAccess const& _identifierAccess, bool _useNamedLabelsForFunctions ) { EthAssemblyAdapter assemblyAdapter(_assembly); - yul::CodeTransform( + CodeTransform( assemblyAdapter, _analysisInfo, false, diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libyul/AsmCodeGen.h index bbc31397..fd5ac0a1 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libyul/AsmCodeGen.h @@ -22,7 +22,7 @@ #pragma once -#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libyul/AsmAnalysis.h> #include <functional> @@ -32,9 +32,9 @@ namespace eth { class Assembly; } -namespace solidity -{ -namespace assembly +} + +namespace yul { struct Block; @@ -45,12 +45,10 @@ public: static void assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, - eth::Assembly& _assembly, + dev::eth::Assembly& _assembly, yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(), bool _useNamedLabelsForFunctions = false ); }; } -} -} diff --git a/libsolidity/inlineasm/AsmData.h b/libyul/AsmData.h index a8d5e327..86c373a4 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libyul/AsmData.h @@ -22,12 +22,11 @@ #pragma once -#include <libsolidity/inlineasm/AsmDataForward.h> +#include <libyul/AsmDataForward.h> +#include <libyul/YulString.h> #include <libevmasm/Instruction.h> -#include <libevmasm/SourceLocation.h> - -#include <libyul/YulString.h> +#include <liblangutil/SourceLocation.h> #include <boost/variant.hpp> #include <boost/noncopyable.hpp> @@ -35,70 +34,63 @@ #include <map> #include <memory> -namespace dev -{ -namespace solidity -{ -namespace assembly +namespace yul { -using YulString = dev::yul::YulString; using Type = YulString; -struct TypedName { SourceLocation location; YulString name; Type type; }; +struct TypedName { langutil::SourceLocation location; YulString name; Type type; }; using TypedNameList = std::vector<TypedName>; /// Direct EVM instruction (except PUSHi and JUMPDEST) -struct Instruction { SourceLocation location; solidity::Instruction instruction; }; +struct Instruction { langutil::SourceLocation location; dev::solidity::Instruction instruction; }; /// Literal number or string (up to 32 bytes) enum class LiteralKind { Number, Boolean, String }; -struct Literal { SourceLocation location; LiteralKind kind; YulString value; Type type; }; +struct Literal { langutil::SourceLocation location; LiteralKind kind; YulString value; Type type; }; /// External / internal identifier or label reference -struct Identifier { SourceLocation location; YulString name; }; +struct Identifier { langutil::SourceLocation location; YulString name; }; /// Jump label ("name:") -struct Label { SourceLocation location; YulString name; }; +struct Label { langutil::SourceLocation location; YulString name; }; /// Assignment from stack (":= x", moves stack top into x, potentially multiple slots) -struct StackAssignment { SourceLocation location; Identifier variableName; }; +struct StackAssignment { langutil::SourceLocation location; Identifier variableName; }; /// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand /// side and requires x to occupy exactly one stack slot. /// /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// a single stack slot and expects a single expression on the right hand returning /// the same amount of items as the number of variables. -struct Assignment { SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; }; +struct Assignment { langutil::SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" -struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector<Expression> arguments; }; -struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Expression> arguments; }; +struct FunctionalInstruction { langutil::SourceLocation location; dev::solidity::Instruction instruction; std::vector<Expression> arguments; }; +struct FunctionCall { langutil::SourceLocation location; Identifier functionName; std::vector<Expression> arguments; }; /// Statement that contains only a single expression -struct ExpressionStatement { SourceLocation location; Expression expression; }; +struct ExpressionStatement { langutil::SourceLocation location; Expression expression; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> value; }; +struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> value; }; /// Block that creates a scope (frees declared stack variables) -struct Block { SourceLocation location; std::vector<Statement> statements; }; +struct Block { langutil::SourceLocation location; std::vector<Statement> statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; +struct FunctionDefinition { langutil::SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. -struct If { SourceLocation location; std::shared_ptr<Expression> condition; Block body; }; +struct If { langutil::SourceLocation location; std::shared_ptr<Expression> condition; Block body; }; /// Switch case or default case -struct Case { SourceLocation location; std::shared_ptr<Literal> value; Block body; }; +struct Case { langutil::SourceLocation location; std::shared_ptr<Literal> value; Block body; }; /// Switch statement -struct Switch { SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; }; -struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; }; +struct Switch { langutil::SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; }; +struct ForLoop { langutil::SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; }; -struct LocationExtractor: boost::static_visitor<SourceLocation> +struct LocationExtractor: boost::static_visitor<langutil::SourceLocation> { - template <class T> SourceLocation operator()(T const& _node) const + template <class T> langutil::SourceLocation operator()(T const& _node) const { return _node.location; } }; /// Extracts the source location from an inline assembly node. -template <class T> inline SourceLocation locationOf(T const& _node) +template <class T> inline langutil::SourceLocation locationOf(T const& _node) { return boost::apply_visitor(LocationExtractor(), _node); } } -} -} diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libyul/AsmDataForward.h index 69cf8f1d..046c8248 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libyul/AsmDataForward.h @@ -24,11 +24,7 @@ #include <boost/variant.hpp> -namespace dev -{ -namespace solidity -{ -namespace assembly +namespace yul { struct Instruction; @@ -61,5 +57,3 @@ enum class AsmFlavour }; } -} -} diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libyul/AsmParser.cpp index 1f399edc..2ce94f85 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -20,9 +20,9 @@ * Solidity inline assembly parser. */ -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <libyul/AsmParser.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/ErrorReporter.h> #include <boost/algorithm/string.hpp> @@ -31,10 +31,11 @@ using namespace std; using namespace dev; +using namespace langutil; +using namespace yul; using namespace dev::solidity; -using namespace dev::solidity::assembly; -shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner) +shared_ptr<Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner) { m_recursionDepth = 0; try @@ -53,10 +54,10 @@ shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scann return nullptr; } -assembly::Block Parser::parseBlock() +Block Parser::parseBlock() { RecursionGuard recursionGuard(*this); - assembly::Block block = createWithLocation<Block>(); + Block block = createWithLocation<Block>(); expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); @@ -65,7 +66,7 @@ assembly::Block Parser::parseBlock() return block; } -assembly::Statement Parser::parseStatement() +Statement Parser::parseStatement() { RecursionGuard recursionGuard(*this); switch (currentToken()) @@ -78,7 +79,7 @@ assembly::Statement Parser::parseStatement() return parseBlock(); case Token::If: { - assembly::If _if = createWithLocation<assembly::If>(); + If _if = createWithLocation<If>(); m_scanner->next(); _if.condition = make_shared<Expression>(parseExpression()); _if.body = parseBlock(); @@ -86,7 +87,7 @@ assembly::Statement Parser::parseStatement() } case Token::Switch: { - assembly::Switch _switch = createWithLocation<assembly::Switch>(); + Switch _switch = createWithLocation<Switch>(); m_scanner->next(); _switch.expression = make_shared<Expression>(parseExpression()); while (m_scanner->currentToken() == Token::Case) @@ -108,7 +109,7 @@ assembly::Statement Parser::parseStatement() { if (m_flavour != AsmFlavour::Loose) break; - assembly::StackAssignment assignment = createWithLocation<assembly::StackAssignment>(); + StackAssignment assignment = createWithLocation<StackAssignment>(); advance(); expectToken(Token::Colon); assignment.variableName.location = location(); @@ -138,9 +139,9 @@ assembly::Statement Parser::parseStatement() { // if a comma follows, a multiple assignment is assumed - if (elementary.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(Identifier)) fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); - assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary); + Identifier const& identifier = boost::get<Identifier>(elementary); Assignment assignment = createWithLocation<Assignment>(identifier.location); assignment.variableNames.emplace_back(identifier); @@ -149,9 +150,9 @@ assembly::Statement Parser::parseStatement() { expectToken(Token::Comma); elementary = parseElementaryOperation(); - if (elementary.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(Identifier)) fatalParserError("Variable name expected in multiple assignment."); - assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(elementary)); + assignment.variableNames.emplace_back(boost::get<Identifier>(elementary)); } while (currentToken() == Token::Comma); @@ -164,15 +165,15 @@ assembly::Statement Parser::parseStatement() } case Token::Colon: { - if (elementary.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(Identifier)) fatalParserError("Label name / variable name must precede \":\"."); - assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary); + Identifier const& identifier = boost::get<Identifier>(elementary); advance(); // identifier:=: should be parsed as identifier: =: (i.e. a label), // while identifier:= (being followed by a non-colon) as identifier := (assignment). if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { - assembly::Assignment assignment = createWithLocation<assembly::Assignment>(identifier.location); + Assignment assignment = createWithLocation<Assignment>(identifier.location); if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name.str())) fatalParserError("Cannot use instruction names for identifier names."); advance(); @@ -196,36 +197,36 @@ assembly::Statement Parser::parseStatement() fatalParserError("Call or assignment expected."); break; } - if (elementary.type() == typeid(assembly::Identifier)) + if (elementary.type() == typeid(Identifier)) { - Expression expr = boost::get<assembly::Identifier>(elementary); + Expression expr = boost::get<Identifier>(elementary); return ExpressionStatement{locationOf(expr), expr}; } - else if (elementary.type() == typeid(assembly::Literal)) + else if (elementary.type() == typeid(Literal)) { - Expression expr = boost::get<assembly::Literal>(elementary); + Expression expr = boost::get<Literal>(elementary); return ExpressionStatement{locationOf(expr), expr}; } else { - solAssert(elementary.type() == typeid(assembly::Instruction), "Invalid elementary operation."); - return boost::get<assembly::Instruction>(elementary); + solAssert(elementary.type() == typeid(Instruction), "Invalid elementary operation."); + return boost::get<Instruction>(elementary); } } -assembly::Case Parser::parseCase() +Case Parser::parseCase() { RecursionGuard recursionGuard(*this); - assembly::Case _case = createWithLocation<assembly::Case>(); + Case _case = createWithLocation<Case>(); if (m_scanner->currentToken() == Token::Default) m_scanner->next(); else if (m_scanner->currentToken() == Token::Case) { m_scanner->next(); ElementaryOperation literal = parseElementaryOperation(); - if (literal.type() != typeid(assembly::Literal)) + if (literal.type() != typeid(Literal)) fatalParserError("Literal expected."); - _case.value = make_shared<Literal>(boost::get<assembly::Literal>(std::move(literal))); + _case.value = make_shared<Literal>(boost::get<Literal>(std::move(literal))); } else fatalParserError("Case or default case expected."); @@ -234,7 +235,7 @@ assembly::Case Parser::parseCase() return _case; } -assembly::ForLoop Parser::parseForLoop() +ForLoop Parser::parseForLoop() { RecursionGuard recursionGuard(*this); ForLoop forLoop = createWithLocation<ForLoop>(); @@ -247,7 +248,7 @@ assembly::ForLoop Parser::parseForLoop() return forLoop; } -assembly::Expression Parser::parseExpression() +Expression Parser::parseExpression() { RecursionGuard recursionGuard(*this); // In strict mode, this might parse a plain Instruction, but @@ -292,12 +293,12 @@ assembly::Expression Parser::parseExpression() Instruction& instr = boost::get<Instruction>(operation); return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; } - else if (operation.type() == typeid(assembly::Identifier)) - return boost::get<assembly::Identifier>(operation); + else if (operation.type() == typeid(Identifier)) + return boost::get<Identifier>(operation); else { - solAssert(operation.type() == typeid(assembly::Literal), ""); - return boost::get<assembly::Literal>(operation); + solAssert(operation.type() == typeid(Literal), ""); + return boost::get<Literal>(operation); } } @@ -419,7 +420,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() return ret; } -assembly::VariableDeclaration Parser::parseVariableDeclaration() +VariableDeclaration Parser::parseVariableDeclaration() { RecursionGuard recursionGuard(*this); VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); @@ -444,7 +445,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() return varDecl; } -assembly::FunctionDefinition Parser::parseFunctionDefinition() +FunctionDefinition Parser::parseFunctionDefinition() { RecursionGuard recursionGuard(*this); FunctionDefinition funDef = createWithLocation<FunctionDefinition>(); @@ -476,7 +477,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() return funDef; } -assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) +Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) { RecursionGuard recursionGuard(*this); if (_initialOp.type() == typeid(Instruction)) diff --git a/libsolidity/inlineasm/AsmParser.h b/libyul/AsmParser.h index 41117228..52166a20 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libyul/AsmParser.h @@ -24,32 +24,30 @@ #include <memory> #include <vector> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/parsing/ParserBase.h> +#include <libyul/AsmData.h> +#include <liblangutil/SourceLocation.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/ParserBase.h> -namespace dev -{ -namespace solidity -{ -namespace assembly +namespace yul { -class Parser: public ParserBase +class Parser: public langutil::ParserBase { public: - explicit Parser(ErrorReporter& _errorReporter, AsmFlavour _flavour = AsmFlavour::Loose): + explicit Parser(langutil::ErrorReporter& _errorReporter, AsmFlavour _flavour = AsmFlavour::Loose): ParserBase(_errorReporter), m_flavour(_flavour) {} /// Parses an inline assembly block starting with `{` and ending with `}`. /// @param _reuseScanner if true, do check for end of input after the `}`. /// @returns an empty shared pointer on error. - std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner); + std::shared_ptr<Block> parse(std::shared_ptr<langutil::Scanner> const& _scanner, bool _reuseScanner); protected: - using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>; + using ElementaryOperation = boost::variant<Instruction, Literal, Identifier>; /// Creates an inline assembly node with the given source location. - template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) const + template <class T> T createWithLocation(langutil::SourceLocation const& _loc = {}) const { T r; r.location = _loc; @@ -58,18 +56,18 @@ protected: r.location.start = position(); r.location.end = endPosition(); } - if (!r.location.sourceName) - r.location.sourceName = sourceName(); + if (!r.location.source) + r.location.source = m_scanner->charStream(); return r; } - SourceLocation location() const { return SourceLocation(position(), endPosition(), sourceName()); } + langutil::SourceLocation location() const { return {position(), endPosition(), m_scanner->charStream()}; } Block parseBlock(); Statement parseStatement(); Case parseCase(); ForLoop parseForLoop(); /// Parses a functional expression that has to push exactly one stack element - assembly::Expression parseExpression(); + Expression parseExpression(); static std::map<std::string, dev::solidity::Instruction> const& instructions(); static std::map<dev::solidity::Instruction, std::string> const& instructionNames(); /// Parses an elementary operation, i.e. a literal, identifier or instruction. @@ -78,7 +76,7 @@ protected: ElementaryOperation parseElementaryOperation(); VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); - assembly::Expression parseCall(ElementaryOperation&& _initialOp); + Expression parseCall(ElementaryOperation&& _initialOp); TypedName parseTypedName(); std::string expectAsmIdentifier(); @@ -89,5 +87,3 @@ private: }; } -} -} diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index ae0bd1eb..eaaba9f3 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -20,9 +20,9 @@ * Converts a parsed assembly into its textual form. */ -#include <libsolidity/inlineasm/AsmPrinter.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/interface/Exceptions.h> +#include <libyul/AsmPrinter.h> +#include <libyul/AsmData.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/CommonData.h> @@ -35,19 +35,19 @@ using namespace std; using namespace dev; +using namespace yul; using namespace dev::solidity; -using namespace dev::solidity::assembly; //@TODO source locations -string AsmPrinter::operator()(assembly::Instruction const& _instruction) +string AsmPrinter::operator()(yul::Instruction const& _instruction) { solAssert(!m_yul, ""); solAssert(isValidInstruction(_instruction.instruction), "Invalid instruction"); return boost::to_lower_copy(instructionInfo(_instruction.instruction).name); } -string AsmPrinter::operator()(assembly::Literal const& _literal) +string AsmPrinter::operator()(Literal const& _literal) { switch (_literal.kind) { @@ -90,13 +90,13 @@ string AsmPrinter::operator()(assembly::Literal const& _literal) return "\"" + out + "\"" + appendTypeName(_literal.type); } -string AsmPrinter::operator()(assembly::Identifier const& _identifier) +string AsmPrinter::operator()(Identifier const& _identifier) { solAssert(!_identifier.name.empty(), "Invalid identifier."); return _identifier.name.str(); } -string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functionalInstruction) +string AsmPrinter::operator()(FunctionalInstruction const& _functionalInstruction) { solAssert(!m_yul, ""); solAssert(isValidInstruction(_functionalInstruction.instruction), "Invalid instruction"); @@ -114,21 +114,21 @@ string AsmPrinter::operator()(ExpressionStatement const& _statement) return boost::apply_visitor(*this, _statement.expression); } -string AsmPrinter::operator()(assembly::Label const& _label) +string AsmPrinter::operator()(Label const& _label) { solAssert(!m_yul, ""); solAssert(!_label.name.empty(), "Invalid label."); return _label.name.str() + ":"; } -string AsmPrinter::operator()(assembly::StackAssignment const& _assignment) +string AsmPrinter::operator()(StackAssignment const& _assignment) { solAssert(!m_yul, ""); solAssert(!_assignment.variableName.name.empty(), "Invalid variable name."); return "=: " + (*this)(_assignment.variableName); } -string AsmPrinter::operator()(assembly::Assignment const& _assignment) +string AsmPrinter::operator()(Assignment const& _assignment) { solAssert(_assignment.variableNames.size() >= 1, ""); string variables = (*this)(_assignment.variableNames.front()); @@ -137,7 +137,7 @@ string AsmPrinter::operator()(assembly::Assignment const& _assignment) return variables + " := " + boost::apply_visitor(*this, *_assignment.value); } -string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration) +string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) { string out = "let "; out += boost::algorithm::join( @@ -154,7 +154,7 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl return out; } -string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition) +string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) { solAssert(!_functionDefinition.name.empty(), "Invalid function name."); string out = "function " + _functionDefinition.name.str() + "("; @@ -179,7 +179,7 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin return out + "\n" + (*this)(_functionDefinition.body); } -string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall) +string AsmPrinter::operator()(FunctionCall const& _functionCall) { return (*this)(_functionCall.functionName) + "(" + @@ -210,7 +210,7 @@ string AsmPrinter::operator()(Switch const& _switch) return out; } -string AsmPrinter::operator()(assembly::ForLoop const& _forLoop) +string AsmPrinter::operator()(ForLoop const& _forLoop) { solAssert(_forLoop.condition, "Invalid for loop condition."); string out = "for "; diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h new file mode 100644 index 00000000..61dfc18c --- /dev/null +++ b/libyul/AsmPrinter.h @@ -0,0 +1,62 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2017 + * Converts a parsed assembly into its textual form. + */ + +#pragma once + +#include <libyul/AsmDataForward.h> + +#include <libyul/YulString.h> + +#include <boost/variant.hpp> + +namespace yul +{ + +class AsmPrinter: public boost::static_visitor<std::string> +{ +public: + explicit AsmPrinter(bool _yul = false): m_yul(_yul) {} + + std::string operator()(Instruction const& _instruction); + std::string operator()(Literal const& _literal); + std::string operator()(Identifier const& _identifier); + std::string operator()(FunctionalInstruction const& _functionalInstruction); + std::string operator()(ExpressionStatement const& _expr); + std::string operator()(Label const& _label); + std::string operator()(StackAssignment const& _assignment); + std::string operator()(Assignment const& _assignment); + std::string operator()(VariableDeclaration const& _variableDeclaration); + std::string operator()(FunctionDefinition const& _functionDefinition); + std::string operator()(FunctionCall const& _functionCall); + std::string operator()(If const& _if); + std::string operator()(Switch const& _switch); + std::string operator()(ForLoop const& _forLoop); + std::string operator()(Block const& _block); + +private: + std::string formatTypedName(TypedName _variable) const; + std::string appendTypeName(YulString _type) const; + + bool m_yul = false; +}; + +} diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libyul/AsmScope.cpp index 10893b96..b71f2367 100644 --- a/libsolidity/inlineasm/AsmScope.cpp +++ b/libyul/AsmScope.cpp @@ -18,13 +18,13 @@ * Scopes for identifiers. */ -#include <libsolidity/inlineasm/AsmScope.h> +#include <libyul/AsmScope.h> using namespace std; using namespace dev; -using namespace dev::solidity::assembly; +using namespace yul; -bool Scope::registerLabel(yul::YulString _name) +bool Scope::registerLabel(YulString _name) { if (exists(_name)) return false; @@ -32,7 +32,7 @@ bool Scope::registerLabel(yul::YulString _name) return true; } -bool Scope::registerVariable(yul::YulString _name, YulType const& _type) +bool Scope::registerVariable(YulString _name, YulType const& _type) { if (exists(_name)) return false; @@ -42,7 +42,7 @@ bool Scope::registerVariable(yul::YulString _name, YulType const& _type) return true; } -bool Scope::registerFunction(yul::YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns) +bool Scope::registerFunction(YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns) { if (exists(_name)) return false; @@ -50,7 +50,7 @@ bool Scope::registerFunction(yul::YulString _name, std::vector<YulType> const& _ return true; } -Scope::Identifier* Scope::lookup(yul::YulString _name) +Scope::Identifier* Scope::lookup(YulString _name) { bool crossedFunctionBoundary = false; for (Scope* s = this; s; s = s->superScope) @@ -70,7 +70,7 @@ Scope::Identifier* Scope::lookup(yul::YulString _name) return nullptr; } -bool Scope::exists(yul::YulString _name) const +bool Scope::exists(YulString _name) const { if (identifiers.count(_name)) return true; diff --git a/libsolidity/inlineasm/AsmScope.h b/libyul/AsmScope.h index 65848018..2a8ef49e 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libyul/AsmScope.h @@ -20,7 +20,7 @@ #pragma once -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libyul/YulString.h> @@ -32,16 +32,12 @@ #include <functional> #include <memory> -namespace dev -{ -namespace solidity -{ -namespace assembly +namespace yul { struct Scope { - using YulType = yul::YulString; + using YulType = YulString; using LabelID = size_t; struct Variable { YulType type; }; @@ -53,13 +49,13 @@ struct Scope }; using Identifier = boost::variant<Variable, Label, Function>; - using Visitor = GenericVisitor<Variable const, Label const, Function const>; - using NonconstVisitor = GenericVisitor<Variable, Label, Function>; + using Visitor = dev::GenericVisitor<Variable const, Label const, Function const>; + using NonconstVisitor = dev::GenericVisitor<Variable, Label, Function>; - bool registerVariable(yul::YulString _name, YulType const& _type); - bool registerLabel(yul::YulString _name); + bool registerVariable(YulString _name, YulType const& _type); + bool registerLabel(YulString _name); bool registerFunction( - yul::YulString _name, + YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns ); @@ -69,12 +65,12 @@ struct Scope /// will any lookups across assembly boundaries. /// The pointer will be invalidated if the scope is modified. /// @param _crossedFunction if true, we already crossed a function boundary during recursive lookup - Identifier* lookup(yul::YulString _name); + Identifier* lookup(YulString _name); /// Looks up the identifier in this and super scopes (will not find variables across function /// boundaries and generally stops at assembly boundaries) and calls the visitor, returns /// false if not found. template <class V> - bool lookup(yul::YulString _name, V const& _visitor) + bool lookup(YulString _name, V const& _visitor) { if (Identifier* id = lookup(_name)) { @@ -86,7 +82,7 @@ struct Scope } /// @returns true if the name exists in this scope or in super scopes (also searches /// across function and assembly boundaries). - bool exists(yul::YulString _name) const; + bool exists(YulString _name) const; /// @returns the number of variables directly registered inside the scope. size_t numberOfVariables() const; @@ -97,9 +93,7 @@ struct Scope /// If true, variables from the super scope are not visible here (other identifiers are), /// but they are still taken into account to prevent shadowing. bool functionScope = false; - std::map<yul::YulString, Identifier> identifiers; + std::map<YulString, Identifier> identifiers; }; } -} -} diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libyul/AsmScopeFiller.cpp index d1f98083..ee797d6a 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libyul/AsmScopeFiller.cpp @@ -18,14 +18,14 @@ * Module responsible for registering identifiers inside their scopes. */ -#include <libsolidity/inlineasm/AsmScopeFiller.h> +#include <libyul/AsmScopeFiller.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/inlineasm/AsmScope.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> +#include <libyul/AsmScope.h> +#include <libyul/AsmAnalysisInfo.h> -#include <libsolidity/interface/ErrorReporter.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/CommonData.h> @@ -36,8 +36,9 @@ using namespace std; using namespace dev; +using namespace langutil; +using namespace yul; using namespace dev::solidity; -using namespace dev::solidity::assembly; ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): m_info(_info), m_errorReporter(_errorReporter) @@ -64,7 +65,7 @@ bool ScopeFiller::operator()(Label const& _item) return true; } -bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl) +bool ScopeFiller::operator()(VariableDeclaration const& _varDecl) { for (auto const& variable: _varDecl.variables) if (!registerVariable(variable, _varDecl.location, *m_currentScope)) @@ -72,7 +73,7 @@ bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl) return true; } -bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) +bool ScopeFiller::operator()(FunctionDefinition const& _funDef) { bool success = true; vector<Scope::YulType> arguments; diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libyul/AsmScopeFiller.h index bb023f61..e8fb88d5 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.h +++ b/libyul/AsmScopeFiller.h @@ -20,20 +20,20 @@ #pragma once -#include <libsolidity/inlineasm/AsmDataForward.h> +#include <libyul/AsmDataForward.h> #include <boost/variant.hpp> #include <functional> #include <memory> -namespace dev -{ -struct SourceLocation; -namespace solidity +namespace langutil { class ErrorReporter; -namespace assembly +struct SourceLocation; +} + +namespace yul { struct TypedName; @@ -47,38 +47,36 @@ struct AsmAnalysisInfo; class ScopeFiller: public boost::static_visitor<bool> { public: - ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter); - - bool operator()(assembly::Instruction const&) { return true; } - bool operator()(assembly::Literal const&) { return true; } - bool operator()(assembly::Identifier const&) { return true; } - bool operator()(assembly::FunctionalInstruction const&) { return true; } - bool operator()(assembly::ExpressionStatement const& _expr); - bool operator()(assembly::Label const& _label); - bool operator()(assembly::StackAssignment const&) { return true; } - bool operator()(assembly::Assignment const&) { return true; } - bool operator()(assembly::VariableDeclaration const& _variableDeclaration); - bool operator()(assembly::FunctionDefinition const& _functionDefinition); - bool operator()(assembly::FunctionCall const&) { return true; } - bool operator()(assembly::If const& _if); - bool operator()(assembly::Switch const& _switch); - bool operator()(assembly::ForLoop const& _forLoop); - bool operator()(assembly::Block const& _block); + ScopeFiller(AsmAnalysisInfo& _info, langutil::ErrorReporter& _errorReporter); + + bool operator()(Instruction const&) { return true; } + bool operator()(Literal const&) { return true; } + bool operator()(Identifier const&) { return true; } + bool operator()(FunctionalInstruction const&) { return true; } + bool operator()(ExpressionStatement const& _expr); + bool operator()(Label const& _label); + bool operator()(StackAssignment const&) { return true; } + bool operator()(Assignment const&) { return true; } + bool operator()(VariableDeclaration const& _variableDeclaration); + bool operator()(FunctionDefinition const& _functionDefinition); + bool operator()(FunctionCall const&) { return true; } + bool operator()(If const& _if); + bool operator()(Switch const& _switch); + bool operator()(ForLoop const& _forLoop); + bool operator()(Block const& _block); private: bool registerVariable( TypedName const& _name, - SourceLocation const& _location, + langutil::SourceLocation const& _location, Scope& _scope ); - Scope& scope(assembly::Block const* _block); + Scope& scope(Block const* _block); Scope* m_currentScope = nullptr; AsmAnalysisInfo& m_info; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } -} -} diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt new file mode 100644 index 00000000..7ed84ff5 --- /dev/null +++ b/libyul/CMakeLists.txt @@ -0,0 +1,45 @@ +add_library(yul + AsmAnalysis.cpp + AsmAnalysisInfo.cpp + AsmCodeGen.cpp + AsmParser.cpp + AsmPrinter.cpp + AsmScope.cpp + AsmScopeFiller.cpp + Object.cpp + ObjectParser.cpp + backends/evm/EVMAssembly.cpp + backends/evm/EVMCodeTransform.cpp + optimiser/ASTCopier.cpp + optimiser/ASTWalker.cpp + optimiser/BlockFlattener.cpp + optimiser/CommonSubexpressionEliminator.cpp + optimiser/DataFlowAnalyzer.cpp + optimiser/Disambiguator.cpp + optimiser/ExpressionInliner.cpp + optimiser/ExpressionJoiner.cpp + optimiser/ExpressionSimplifier.cpp + optimiser/ExpressionSplitter.cpp + optimiser/ForLoopInitRewriter.cpp + optimiser/FullInliner.cpp + optimiser/FunctionGrouper.cpp + optimiser/FunctionHoister.cpp + optimiser/InlinableExpressionFunctionFinder.cpp + optimiser/MainFunction.cpp + optimiser/Metrics.cpp + optimiser/NameCollector.cpp + optimiser/NameDispenser.cpp + optimiser/RedundantAssignEliminator.cpp + optimiser/Rematerialiser.cpp + optimiser/SSATransform.cpp + optimiser/SSAValueTracker.cpp + optimiser/Semantics.cpp + optimiser/SimplificationRules.cpp + optimiser/Substitution.cpp + optimiser/Suite.cpp + optimiser/SyntacticalEquality.cpp + optimiser/UnusedPruner.cpp + optimiser/Utilities.cpp + optimiser/VarDeclPropagator.cpp +) +target_link_libraries(yul PUBLIC devcore) diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h index 0c421dbf..e10e53ef 100644 --- a/libyul/Exceptions.h +++ b/libyul/Exceptions.h @@ -23,18 +23,15 @@ #include <libdevcore/Exceptions.h> #include <libdevcore/Assertions.h> -namespace dev -{ namespace yul { -struct YulException: virtual Exception {}; +struct YulException: virtual dev::Exception {}; struct OptimizerException: virtual YulException {}; struct YulAssertion: virtual YulException {}; /// Assertion that throws an YulAssertion containing the given description if it is not met. #define yulAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::dev::yul::YulException, DESCRIPTION) + assertThrow(CONDITION, ::yul::YulException, DESCRIPTION) } -} diff --git a/libyul/Object.cpp b/libyul/Object.cpp new file mode 100644 index 00000000..a5228793 --- /dev/null +++ b/libyul/Object.cpp @@ -0,0 +1,61 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Yul code and data object container. + */ + +#include <libyul/Object.h> + +#include <libyul/AsmPrinter.h> +#include <libyul/Exceptions.h> + +#include <libdevcore/Visitor.h> +#include <libdevcore/CommonData.h> + +#include <boost/algorithm/string/replace.hpp> + +using namespace dev; +using namespace yul; +using namespace std; + +namespace +{ + +string indent(std::string const& _input) +{ + if (_input.empty()) + return _input; + return boost::replace_all_copy(" " + _input, "\n", "\n "); +} + +} + +string Data::toString(bool) const +{ + return "data \"" + name.str() + "\" hex\"" + dev::toHex(data) + "\""; +} + +string Object::toString(bool _yul) const +{ + yulAssert(code, "No code"); + string inner = "code " + AsmPrinter{_yul}(*code); + + for (auto const& obj: subObjects) + inner += "\n" + obj->toString(_yul); + + return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}"; +} diff --git a/libyul/Object.h b/libyul/Object.h new file mode 100644 index 00000000..cfd8d02d --- /dev/null +++ b/libyul/Object.h @@ -0,0 +1,72 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Yul code and data object container. + */ + +#pragma once + +#include <libyul/AsmDataForward.h> +#include <libyul/YulString.h> + +#include <libdevcore/Common.h> + +#include <memory> + +namespace yul +{ +struct AsmAnalysisInfo; + + +/** + * Generic base class for both Yul objects and Yul data. + */ +struct ObjectNode +{ + virtual ~ObjectNode() {} + virtual std::string toString(bool _yul) const = 0; + + YulString name; +}; + +/** + * Named data in Yul objects. + */ +struct Data: ObjectNode +{ + Data(YulString _name, dev::bytes _data): data(std::move(_data)) { name = _name; } + std::string toString(bool _yul) const override; + + dev::bytes data; +}; + +/** + * Yul code and data object container. + */ +struct Object: ObjectNode +{ +public: + /// @returns a (parseable) string representation. Includes types if @a _yul is set. + std::string toString(bool _yul) const override; + + std::shared_ptr<Block> code; + std::vector<std::shared_ptr<ObjectNode>> subObjects; + std::map<YulString, size_t> subIndexByName; + std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo; +}; + +} diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp new file mode 100644 index 00000000..43dd4be9 --- /dev/null +++ b/libyul/ObjectParser.cpp @@ -0,0 +1,146 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Parser for Yul code and data object container. + */ + +#include <libyul/ObjectParser.h> + +#include <libyul/AsmParser.h> +#include <libyul/Exceptions.h> + +#include <liblangutil/Token.h> + +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace std; + + +shared_ptr<Object> ObjectParser::parse(shared_ptr<Scanner> const& _scanner, bool _reuseScanner) +{ + m_recursionDepth = 0; + try + { + shared_ptr<Object> object; + m_scanner = _scanner; + if (currentToken() == Token::LBrace) + { + // Special case: Code-only form. + object = make_shared<Object>(); + object->name = YulString{"object"}; + object->code = parseBlock(); + if (!object->code) + return nullptr; + } + else + object = parseObject(); + if (object && !_reuseScanner) + expectToken(Token::EOS); + return object; + } + catch (FatalError const&) + { + if (m_errorReporter.errors().empty()) + throw; // Something is weird here, rather throw again. + } + return nullptr; +} + +shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject) +{ + RecursionGuard guard(*this); + + if (currentToken() != Token::Identifier || currentLiteral() != "object") + fatalParserError("Expected keyword \"object\"."); + advance(); + + shared_ptr<Object> ret = make_shared<Object>(); + ret->name = parseUniqueName(_containingObject); + + expectToken(Token::LBrace); + + ret->code = parseCode(); + + while (currentToken() != Token::RBrace) + { + if (currentToken() == Token::Identifier && currentLiteral() == "object") + parseObject(ret.get()); + else if (currentToken() == Token::Identifier && currentLiteral() == "data") + parseData(*ret); + else + fatalParserError("Expected keyword \"data\" or \"object\" or \"}\"."); + } + if (_containingObject) + addNamedSubObject(*_containingObject, ret->name, ret); + + expectToken(Token::RBrace); + + return ret; +} + +shared_ptr<Block> ObjectParser::parseCode() +{ + if (currentToken() != Token::Identifier || currentLiteral() != "code") + fatalParserError("Expected keyword \"code\"."); + advance(); + + return parseBlock(); +} + +shared_ptr<Block> ObjectParser::parseBlock() +{ + Parser parser(m_errorReporter, m_flavour); + shared_ptr<Block> block = parser.parse(m_scanner, true); + yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); + return block; +} + +void ObjectParser::parseData(Object& _containingObject) +{ + solAssert( + currentToken() == Token::Identifier && currentLiteral() == "data", + "parseData called on wrong input." + ); + advance(); + + YulString name = parseUniqueName(&_containingObject); + + expectToken(Token::StringLiteral, false); + addNamedSubObject(_containingObject, name, make_shared<Data>(name, asBytes(currentLiteral()))); + advance(); +} + +YulString ObjectParser::parseUniqueName(Object const* _containingObject) +{ + expectToken(Token::StringLiteral, false); + YulString name{currentLiteral()}; + if (name.empty()) + parserError("Object name cannot be empty."); + else if (_containingObject && _containingObject->name == name) + parserError("Object name cannot be the same as the name of the containing object."); + else if (_containingObject && _containingObject->subIndexByName.count(name)) + parserError("Object name \"" + name.str() + "\" already exists inside the containing object."); + advance(); + return name; +} + +void ObjectParser::addNamedSubObject(Object& _container, YulString _name, shared_ptr<ObjectNode> _subObject) +{ + _container.subIndexByName[_name] = _container.subObjects.size(); + _container.subObjects.emplace_back(std::move(_subObject)); +} diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h new file mode 100644 index 00000000..1d88a119 --- /dev/null +++ b/libyul/ObjectParser.h @@ -0,0 +1,72 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Parser for Yul code and data object container. + */ + +#pragma once + +#include <libyul/YulString.h> +#include <libyul/Object.h> + +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/ParserBase.h> + +#include <libdevcore/Common.h> + +#include <memory> + +namespace langutil +{ +class Scanner; +} + +namespace yul +{ + +/** + * Yul object parser. Invokes the inline assembly parser. + */ +class ObjectParser: public langutil::ParserBase +{ +public: + explicit ObjectParser( + langutil::ErrorReporter& _errorReporter, + yul::AsmFlavour _flavour = yul::AsmFlavour::Loose + ): + ParserBase(_errorReporter), m_flavour(_flavour) {} + + /// Parses a Yul object. + /// Falls back to code-only parsing if the source starts with `{`. + /// @param _reuseScanner if true, do check for end of input after the last `}`. + /// @returns an empty shared pointer on error. + std::shared_ptr<Object> parse(std::shared_ptr<langutil::Scanner> const& _scanner, bool _reuseScanner); + +private: + std::shared_ptr<Object> parseObject(Object* _containingObject = nullptr); + std::shared_ptr<Block> parseCode(); + std::shared_ptr<Block> parseBlock(); + void parseData(Object& _containingObject); + + /// Tries to parse a name that is non-empty and unique inside the containing object. + YulString parseUniqueName(Object const* _containingObject); + void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr<ObjectNode> _subObject); + + yul::AsmFlavour m_flavour; +}; + +} diff --git a/libyul/YulString.h b/libyul/YulString.h index ad900a70..2179c23b 100644 --- a/libyul/YulString.h +++ b/libyul/YulString.h @@ -27,8 +27,6 @@ #include <vector> #include <string> -namespace dev -{ namespace yul { @@ -130,4 +128,3 @@ private: }; } -} diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index d75058f7..97b1d305 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -22,24 +22,28 @@ #pragma once +#include <libdevcore/Common.h> #include <libdevcore/CommonData.h> #include <functional> -namespace dev +namespace langutil { struct SourceLocation; +} + +namespace dev +{ namespace solidity { enum class Instruction: uint8_t; -namespace assembly -{ -struct Instruction; -struct Identifier; } } + namespace yul { +struct Instruction; +struct Identifier; /// /// Assembly class that abstracts both the libevmasm assembly and the new Yul assembly. @@ -52,14 +56,14 @@ public: virtual ~AbstractAssembly() {} /// Set a new source location valid starting from the next instruction. - virtual void setSourceLocation(SourceLocation const& _location) = 0; + virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0; /// Retrieve the current height of the stack. This does not have to be zero /// at the beginning. virtual int stackHeight() const = 0; /// Append an EVM instruction. - virtual void appendInstruction(solidity::Instruction _instruction) = 0; + virtual void appendInstruction(dev::solidity::Instruction _instruction) = 0; /// Append a constant. - virtual void appendConstant(u256 const& _constant) = 0; + virtual void appendConstant(dev::u256 const& _constant) = 0; /// Append a label. virtual void appendLabel(LabelID _labelId) = 0; /// Append a label reference. @@ -102,18 +106,15 @@ enum class IdentifierContext { LValue, RValue }; /// to inline assembly (not used in standalone assembly mode). struct ExternalIdentifierAccess { - using Resolver = std::function<size_t(solidity::assembly::Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>; + using Resolver = std::function<size_t(Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>; /// Resolve an external reference given by the identifier in the given context. /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. Resolver resolve; - using CodeGenerator = std::function<void(solidity::assembly::Identifier const&, IdentifierContext, yul::AbstractAssembly&)>; + using CodeGenerator = std::function<void(Identifier const&, IdentifierContext, yul::AbstractAssembly&)>; /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. CodeGenerator generateCode; }; - - -} } diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index b37a3231..99506317 100644 --- a/libyul/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -22,11 +22,12 @@ #include <libevmasm/Instruction.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace langutil; +using namespace yul; namespace { diff --git a/libyul/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h index 556ed5a5..d0a437cc 100644 --- a/libyul/backends/evm/EVMAssembly.h +++ b/libyul/backends/evm/EVMAssembly.h @@ -26,8 +26,11 @@ #include <map> -namespace dev +namespace langutil { +struct SourceLocation; +} + namespace yul { @@ -38,55 +41,55 @@ public: virtual ~EVMAssembly() {} /// Set a new source location valid starting from the next instruction. - virtual void setSourceLocation(SourceLocation const& _location) override; + void setSourceLocation(langutil::SourceLocation const& _location) override; /// Retrieve the current height of the stack. This does not have to be zero /// at the beginning. - virtual int stackHeight() const override { return m_stackHeight; } + int stackHeight() const override { return m_stackHeight; } /// Append an EVM instruction. - virtual void appendInstruction(solidity::Instruction _instruction) override; + void appendInstruction(dev::solidity::Instruction _instruction) override; /// Append a constant. - virtual void appendConstant(u256 const& _constant) override; + void appendConstant(dev::u256 const& _constant) override; /// Append a label. - virtual void appendLabel(LabelID _labelId) override; + void appendLabel(LabelID _labelId) override; /// Append a label reference. - virtual void appendLabelReference(LabelID _labelId) override; + void appendLabelReference(LabelID _labelId) override; /// Generate a new unique label. - virtual LabelID newLabelId() override; + LabelID newLabelId() override; /// Returns a label identified by the given name. Creates it if it does not yet exist. - virtual LabelID namedLabel(std::string const& _name) override; + LabelID namedLabel(std::string const& _name) override; /// Append a reference to a to-be-linked symbol. /// Currently, we assume that the value is always a 20 byte number. - virtual void appendLinkerSymbol(std::string const& _name) override; + void appendLinkerSymbol(std::string const& _name) override; /// Append a jump instruction. /// @param _stackDiffAfter the stack adjustment after this instruction. - virtual void appendJump(int _stackDiffAfter) override; + void appendJump(int _stackDiffAfter) override; /// Append a jump-to-immediate operation. - virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; + void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; /// Append a jump-to-if-immediate operation. - virtual void appendJumpToIf(LabelID _labelId) override; + void appendJumpToIf(LabelID _labelId) override; /// Start a subroutine. - virtual void appendBeginsub(LabelID _labelId, int _arguments) override; + void appendBeginsub(LabelID _labelId, int _arguments) override; /// Call a subroutine. - virtual void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override; + void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override; /// Return from a subroutine. - virtual void appendReturnsub(int _returns, int _stackDiffAfter) override; + void appendReturnsub(int _returns, int _stackDiffAfter) override; /// Append the assembled size as a constant. - virtual void appendAssemblySize() override; + void appendAssemblySize() override; /// Resolves references inside the bytecode and returns the linker object. - eth::LinkerObject finalize(); + dev::eth::LinkerObject finalize(); private: void setLabelToCurrentPosition(AbstractAssembly::LabelID _labelId); void appendLabelReferenceInternal(AbstractAssembly::LabelID _labelId); - void updateReference(size_t pos, size_t size, u256 value); + void updateReference(size_t pos, size_t size, dev::u256 value); bool m_evm15 = false; ///< if true, switch to evm1.5 mode LabelID m_nextLabelId = 0; int m_stackHeight = 0; - bytes m_bytecode; + dev::bytes m_bytecode; std::map<std::string, LabelID> m_namedLabels; std::map<LabelID, size_t> m_labelPositions; std::map<size_t, LabelID> m_labelReferences; @@ -94,4 +97,3 @@ private: }; } -} diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 650a8c0a..12abd754 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -20,20 +20,18 @@ #include <libyul/backends/evm/EVMCodeTransform.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <boost/range/adaptor/reversed.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; -using Scope = dev::solidity::assembly::Scope; - void CodeTransform::operator()(VariableDeclaration const& _varDecl) { solAssert(m_scope, ""); @@ -147,7 +145,7 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction) solAssert(_instruction.arguments.size() == 1, ""); } m_assembly.setSourceLocation(_instruction.location); - auto label = labelFromIdentifier(boost::get<assembly::Identifier>(_instruction.arguments.at(0))); + auto label = labelFromIdentifier(boost::get<Identifier>(_instruction.arguments.at(0))); if (isJumpI) m_assembly.appendJumpToIf(label); else @@ -163,7 +161,7 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction) checkStackHeight(&_instruction); } -void CodeTransform::operator()(assembly::Identifier const& _identifier) +void CodeTransform::operator()(Identifier const& _identifier) { m_assembly.setSourceLocation(_identifier.location); // First search internals, then externals. @@ -197,12 +195,12 @@ void CodeTransform::operator()(assembly::Identifier const& _identifier) checkStackHeight(&_identifier); } -void CodeTransform::operator()(assembly::Literal const& _literal) +void CodeTransform::operator()(Literal const& _literal) { m_assembly.setSourceLocation(_literal.location); - if (_literal.kind == assembly::LiteralKind::Number) + if (_literal.kind == LiteralKind::Number) m_assembly.appendConstant(u256(_literal.value.str())); - else if (_literal.kind == assembly::LiteralKind::Boolean) + else if (_literal.kind == LiteralKind::Boolean) { if (_literal.value.str() == "true") m_assembly.appendConstant(u256(1)); @@ -217,7 +215,7 @@ void CodeTransform::operator()(assembly::Literal const& _literal) checkStackHeight(&_literal); } -void CodeTransform::operator()(assembly::Instruction const& _instruction) +void CodeTransform::operator()(yul::Instruction const& _instruction) { solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMP, "Bare JUMP instruction used for EVM1.5"); solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMPI, "Bare JUMPI instruction used for EVM1.5"); @@ -522,7 +520,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) } } -int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const +int CodeTransform::variableHeightDiff(Scope::Variable const& _var, bool _forSwap) const { solAssert(m_context->variableStackHeights.count(&_var), ""); int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index c0de8ad6..d559f85a 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -20,25 +20,21 @@ #include <libyul/backends/evm/EVMAssembly.h> -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> -#include <libsolidity/inlineasm/AsmScope.h> +#include <libyul/AsmScope.h> #include <boost/variant.hpp> #include <boost/optional.hpp> -namespace dev -{ -namespace solidity +namespace langutil { class ErrorReporter; -namespace assembly -{ -struct AsmAnalysisInfo; -} } + namespace yul { +struct AsmAnalysisInfo; class EVMAssembly; class CodeTransform: public boost::static_visitor<> @@ -47,8 +43,8 @@ public: /// Create the code transformer. /// @param _identifierAccess used to resolve identifiers external to the inline assembly CodeTransform( - yul::AbstractAssembly& _assembly, - solidity::assembly::AsmAnalysisInfo& _analysisInfo, + AbstractAssembly& _assembly, + AsmAnalysisInfo& _analysisInfo, bool _yul = false, bool _evm15 = false, ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), @@ -69,15 +65,14 @@ public: protected: struct Context { - using Scope = solidity::assembly::Scope; std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs; std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs; std::map<Scope::Variable const*, int> variableStackHeights; }; CodeTransform( - yul::AbstractAssembly& _assembly, - solidity::assembly::AsmAnalysisInfo& _analysisInfo, + AbstractAssembly& _assembly, + AsmAnalysisInfo& _analysisInfo, bool _yul, bool _evm15, ExternalIdentifierAccess const& _identifierAccess, @@ -116,8 +111,8 @@ private: AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier); /// @returns the label ID corresponding to the given label, allocating a new one if /// necessary. - AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); - AbstractAssembly::LabelID functionEntryID(YulString _name, solidity::assembly::Scope::Function const& _function); + AbstractAssembly::LabelID labelID(Scope::Label const& _label); + AbstractAssembly::LabelID functionEntryID(YulString _name, Scope::Function const& _function); /// Generates code for an expression that is supposed to return a single value. void visitExpression(Expression const& _expression); @@ -133,15 +128,15 @@ private: /// Determines the stack height difference to the given variables. Throws /// if it is not yet in scope or the height difference is too large. Returns /// the (positive) stack height difference otherwise. - int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const; + int variableHeightDiff(Scope::Variable const& _var, bool _forSwap) const; void expectDeposit(int _deposit, int _oldHeight) const; void checkStackHeight(void const* _astElement) const; - yul::AbstractAssembly& m_assembly; - solidity::assembly::AsmAnalysisInfo& m_info; - solidity::assembly::Scope* m_scope = nullptr; + AbstractAssembly& m_assembly; + AsmAnalysisInfo& m_info; + Scope* m_scope = nullptr; bool m_yul = false; bool m_evm15 = false; bool m_useNamedLabelsForFunctions = false; @@ -155,4 +150,3 @@ private: }; } -} diff --git a/libyul/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp index d0c8dd45..f18b0e6b 100644 --- a/libyul/optimiser/ASTCopier.cpp +++ b/libyul/optimiser/ASTCopier.cpp @@ -22,13 +22,13 @@ #include <libyul/Exceptions.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/Common.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; Statement ASTCopier::operator()(Instruction const&) { diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index b6aceee3..4d2f18ae 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -20,7 +20,7 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/YulString.h> @@ -31,8 +31,6 @@ #include <set> #include <memory> -namespace dev -{ namespace yul { @@ -71,21 +69,21 @@ class ASTCopier: public ExpressionCopier, public StatementCopier { public: virtual ~ASTCopier() = default; - virtual Expression operator()(Literal const& _literal) override; - virtual Statement operator()(Instruction const& _instruction) override; - virtual Expression operator()(Identifier const& _identifier) override; - virtual Expression operator()(FunctionalInstruction const& _instr) override; - virtual Expression operator()(FunctionCall const&) override; - virtual Statement operator()(ExpressionStatement const& _statement) override; - virtual Statement operator()(Label const& _label) override; - virtual Statement operator()(StackAssignment const& _assignment) override; - virtual Statement operator()(Assignment const& _assignment) override; - virtual Statement operator()(VariableDeclaration const& _varDecl) override; - virtual Statement operator()(If const& _if) override; - virtual Statement operator()(Switch const& _switch) override; - virtual Statement operator()(FunctionDefinition const&) override; - virtual Statement operator()(ForLoop const&) override; - virtual Statement operator()(Block const& _block) override; + Expression operator()(Literal const& _literal) override; + Statement operator()(Instruction const& _instruction) override; + Expression operator()(Identifier const& _identifier) override; + Expression operator()(FunctionalInstruction const& _instr) override; + Expression operator()(FunctionCall const&) override; + Statement operator()(ExpressionStatement const& _statement) override; + Statement operator()(Label const& _label) override; + Statement operator()(StackAssignment const& _assignment) override; + Statement operator()(Assignment const& _assignment) override; + Statement operator()(VariableDeclaration const& _varDecl) override; + Statement operator()(If const& _if) override; + Statement operator()(Switch const& _switch) override; + Statement operator()(FunctionDefinition const&) override; + Statement operator()(ForLoop const&) override; + Statement operator()(Block const& _block) override; virtual Expression translate(Expression const& _expression); virtual Statement translate(Statement const& _statement); @@ -123,4 +121,3 @@ std::vector<T> ASTCopier::translateVector(std::vector<T> const& _values) } -} diff --git a/libyul/optimiser/ASTWalker.cpp b/libyul/optimiser/ASTWalker.cpp index e29dda6b..0d568007 100644 --- a/libyul/optimiser/ASTWalker.cpp +++ b/libyul/optimiser/ASTWalker.cpp @@ -20,13 +20,13 @@ #include <libyul/optimiser/ASTWalker.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <boost/range/adaptor/reversed.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h index 38cb85ea..b59b405e 100644 --- a/libyul/optimiser/ASTWalker.h +++ b/libyul/optimiser/ASTWalker.h @@ -20,7 +20,7 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/Exceptions.h> #include <libyul/YulString.h> @@ -32,8 +32,6 @@ #include <set> #include <map> -namespace dev -{ namespace yul { @@ -120,4 +118,3 @@ protected: }; } -} diff --git a/libyul/optimiser/BlockFlattener.cpp b/libyul/optimiser/BlockFlattener.cpp index 04f3ad7f..e6f08524 100644 --- a/libyul/optimiser/BlockFlattener.cpp +++ b/libyul/optimiser/BlockFlattener.cpp @@ -15,14 +15,14 @@ along with solidity. If not, see <http://www.gnu.org/licenses/>. */ #include <libyul/optimiser/BlockFlattener.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/Visitor.h> #include <libdevcore/CommonData.h> #include <functional> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void BlockFlattener::operator()(Block& _block) { diff --git a/libyul/optimiser/BlockFlattener.h b/libyul/optimiser/BlockFlattener.h index 88c49dda..b732422d 100644 --- a/libyul/optimiser/BlockFlattener.h +++ b/libyul/optimiser/BlockFlattener.h @@ -18,8 +18,6 @@ #include <libyul/optimiser/ASTWalker.h> -namespace dev -{ namespace yul { @@ -31,4 +29,3 @@ public: }; } -} diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 64605362..9b851333 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -24,12 +24,11 @@ #include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/SyntacticalEquality.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void CommonSubexpressionEliminator::visit(Expression& _e) { diff --git a/libyul/optimiser/CommonSubexpressionEliminator.h b/libyul/optimiser/CommonSubexpressionEliminator.h index f8aa0ee1..ac1ebe3a 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.h +++ b/libyul/optimiser/CommonSubexpressionEliminator.h @@ -23,8 +23,6 @@ #include <libyul/optimiser/DataFlowAnalyzer.h> -namespace dev -{ namespace yul { @@ -38,8 +36,7 @@ class CommonSubexpressionEliminator: public DataFlowAnalyzer { protected: using ASTModifier::visit; - virtual void visit(Expression& _e) override; + void visit(Expression& _e) override; }; } -} diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 134777d0..64c67b38 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -25,8 +25,7 @@ #include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/Semantics.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -34,7 +33,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void DataFlowAnalyzer::operator()(Assignment& _assignment) { diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index a0c21eee..cd134d48 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -23,14 +23,11 @@ #pragma once #include <libyul/optimiser/ASTWalker.h> - #include <libyul/YulString.h> #include <map> #include <set> -namespace dev -{ namespace yul { @@ -45,13 +42,13 @@ class DataFlowAnalyzer: public ASTModifier { public: using ASTModifier::operator(); - virtual void operator()(Assignment& _assignment) override; - virtual void operator()(VariableDeclaration& _varDecl) override; - virtual void operator()(If& _if) override; - virtual void operator()(Switch& _switch) override; - virtual void operator()(FunctionDefinition&) override; - virtual void operator()(ForLoop&) override; - virtual void operator()(Block& _block) override; + void operator()(Assignment& _assignment) override; + void operator()(VariableDeclaration& _varDecl) override; + void operator()(If& _if) override; + void operator()(Switch& _switch) override; + void operator()(FunctionDefinition&) override; + void operator()(ForLoop&) override; + void operator()(Block& _block) override; protected: /// Registers the assignment. @@ -88,4 +85,3 @@ protected: }; } -} diff --git a/libyul/optimiser/Disambiguator.cpp b/libyul/optimiser/Disambiguator.cpp index 4303f412..fda5895b 100644 --- a/libyul/optimiser/Disambiguator.cpp +++ b/libyul/optimiser/Disambiguator.cpp @@ -21,17 +21,14 @@ #include <libyul/optimiser/Disambiguator.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/inlineasm/AsmScope.h> +#include <libyul/AsmData.h> +#include <libyul/AsmScope.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; -using Scope = dev::solidity::assembly::Scope; - YulString Disambiguator::translateIdentifier(YulString _originalName) { if ((m_externallyUsedIdentifiers.count(_originalName))) diff --git a/libyul/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h index bfb65682..bb83417b 100644 --- a/libyul/optimiser/Disambiguator.h +++ b/libyul/optimiser/Disambiguator.h @@ -20,20 +20,16 @@ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> +#include <libyul/AsmAnalysisInfo.h> #include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/NameDispenser.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> - #include <boost/variant.hpp> #include <boost/optional.hpp> #include <set> -namespace dev -{ namespace yul { @@ -44,7 +40,7 @@ class Disambiguator: public ASTCopier { public: explicit Disambiguator( - solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + AsmAnalysisInfo const& _analysisInfo, std::set<YulString> const& _externallyUsedIdentifiers = {} ): m_info(_analysisInfo), m_externallyUsedIdentifiers(_externallyUsedIdentifiers), m_nameDispenser(m_externallyUsedIdentifiers) @@ -52,22 +48,21 @@ public: } protected: - virtual void enterScope(Block const& _block) override; - virtual void leaveScope(Block const& _block) override; - virtual void enterFunction(FunctionDefinition const& _function) override; - virtual void leaveFunction(FunctionDefinition const& _function) override; - virtual YulString translateIdentifier(YulString _name) override; + void enterScope(Block const& _block) override; + void leaveScope(Block const& _block) override; + void enterFunction(FunctionDefinition const& _function) override; + void leaveFunction(FunctionDefinition const& _function) override; + YulString translateIdentifier(YulString _name) override; - void enterScopeInternal(solidity::assembly::Scope& _scope); - void leaveScopeInternal(solidity::assembly::Scope& _scope); + void enterScopeInternal(Scope& _scope); + void leaveScopeInternal(Scope& _scope); - solidity::assembly::AsmAnalysisInfo const& m_info; + AsmAnalysisInfo const& m_info; std::set<YulString> const& m_externallyUsedIdentifiers; - std::vector<solidity::assembly::Scope*> m_scopes; + std::vector<Scope*> m_scopes; std::map<void const*, YulString> m_translations; NameDispenser m_nameDispenser; }; } -} diff --git a/libyul/optimiser/ExpressionInliner.cpp b/libyul/optimiser/ExpressionInliner.cpp index 07e88191..27d43ac0 100644 --- a/libyul/optimiser/ExpressionInliner.cpp +++ b/libyul/optimiser/ExpressionInliner.cpp @@ -23,14 +23,13 @@ #include <libyul/optimiser/InlinableExpressionFunctionFinder.h> #include <libyul/optimiser/Substitution.h> #include <libyul/optimiser/Semantics.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <boost/algorithm/cxx11/all_of.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void ExpressionInliner::run() diff --git a/libyul/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h index d903664f..14e80c0a 100644 --- a/libyul/optimiser/ExpressionInliner.h +++ b/libyul/optimiser/ExpressionInliner.h @@ -20,16 +20,13 @@ #pragma once #include <libyul/optimiser/ASTWalker.h> - -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <boost/variant.hpp> #include <boost/optional.hpp> #include <set> -namespace dev -{ namespace yul { @@ -54,9 +51,9 @@ public: void run(); using ASTModifier::operator(); - virtual void operator()(FunctionDefinition& _fun) override; + void operator()(FunctionDefinition& _fun) override; - virtual void visit(Expression& _expression) override; + void visit(Expression& _expression) override; private: std::map<YulString, FunctionDefinition const*> m_inlinableFunctions; @@ -69,4 +66,3 @@ private: } -} diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp index 7e57a629..de2b5d53 100644 --- a/libyul/optimiser/ExpressionJoiner.cpp +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -24,8 +24,7 @@ #include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/Utilities.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -33,7 +32,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void ExpressionJoiner::operator()(FunctionalInstruction& _instruction) diff --git a/libyul/optimiser/ExpressionJoiner.h b/libyul/optimiser/ExpressionJoiner.h index 0cc61981..643d62b0 100644 --- a/libyul/optimiser/ExpressionJoiner.h +++ b/libyul/optimiser/ExpressionJoiner.h @@ -20,14 +20,11 @@ */ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <map> -namespace dev -{ namespace yul { @@ -99,4 +96,3 @@ private: }; } -} diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index 64e9d7e7..cda44e8e 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -23,14 +23,13 @@ #include <libyul/optimiser/SimplificationRules.h> #include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/SSAValueTracker.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index 5965a1bb..fe3507f8 100644 --- a/libyul/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -20,12 +20,10 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> -namespace dev -{ namespace yul { @@ -52,4 +50,3 @@ private: }; } -} diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index a4b7a909..a3b2dc11 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -23,7 +23,7 @@ #include <libyul/optimiser/ASTWalker.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -31,7 +31,8 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace langutil; +using namespace yul; using namespace dev::solidity; void ExpressionSplitter::operator()(FunctionalInstruction& _instruction) diff --git a/libyul/optimiser/ExpressionSplitter.h b/libyul/optimiser/ExpressionSplitter.h index 339acbf0..d4d2b3f6 100644 --- a/libyul/optimiser/ExpressionSplitter.h +++ b/libyul/optimiser/ExpressionSplitter.h @@ -20,15 +20,13 @@ */ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/NameDispenser.h> #include <vector> -namespace dev -{ namespace yul { @@ -63,12 +61,12 @@ public: m_nameDispenser(_nameDispenser) { } - virtual void operator()(FunctionalInstruction&) override; - virtual void operator()(FunctionCall&) override; - virtual void operator()(If&) override; - virtual void operator()(Switch&) override; - virtual void operator()(ForLoop&) override; - virtual void operator()(Block& _block) override; + void operator()(FunctionalInstruction&) override; + void operator()(FunctionCall&) override; + void operator()(If&) override; + void operator()(Switch&) override; + void operator()(ForLoop&) override; + void operator()(Block& _block) override; private: /// Replaces the expression by a variable if it is a function call or functional @@ -83,4 +81,3 @@ private: }; } -} diff --git a/libyul/optimiser/ForLoopInitRewriter.cpp b/libyul/optimiser/ForLoopInitRewriter.cpp new file mode 100644 index 00000000..80d39248 --- /dev/null +++ b/libyul/optimiser/ForLoopInitRewriter.cpp @@ -0,0 +1,43 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <libyul/optimiser/ForLoopInitRewriter.h> +#include <libyul/AsmData.h> +#include <libdevcore/CommonData.h> +#include <functional> + +using namespace std; +using namespace dev; +using namespace yul; + +void ForLoopInitRewriter::operator()(Block& _block) +{ + iterateReplacing( + _block.statements, + [](Statement& _stmt) -> boost::optional<vector<Statement>> + { + if (_stmt.type() == typeid(ForLoop)) + { + auto& forLoop = boost::get<ForLoop>(_stmt); + vector<Statement> rewrite; + swap(rewrite, forLoop.pre.statements); + rewrite.emplace_back(move(forLoop)); + return rewrite; + } + return {}; + } + ); +} diff --git a/libyul/optimiser/ForLoopInitRewriter.h b/libyul/optimiser/ForLoopInitRewriter.h new file mode 100644 index 00000000..e925c6c2 --- /dev/null +++ b/libyul/optimiser/ForLoopInitRewriter.h @@ -0,0 +1,36 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +namespace yul +{ + +/** + * Rewrites ForLoop by moving the pre statement block in front of the ForLoop. + * Requirements: + * - The Disambiguator must be run upfront. + */ +class ForLoopInitRewriter: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(Block& _block) override; +}; + +} diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index c9057cf3..8ae26fbb 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -27,8 +27,7 @@ #include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/SSAValueTracker.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> #include <libdevcore/Visitor.h> @@ -37,7 +36,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index 66ce8e2f..a8fe76c6 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -19,22 +19,20 @@ */ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/NameDispenser.h> #include <libyul/Exceptions.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include <boost/variant.hpp> #include <boost/optional.hpp> #include <set> -namespace dev -{ namespace yul { @@ -110,7 +108,7 @@ public: m_nameDispenser(_nameDispenser) { } - virtual void operator()(Block& _block) override; + void operator()(Block& _block) override; private: boost::optional<std::vector<Statement>> tryInlineStatement(Statement& _statement); @@ -141,10 +139,10 @@ public: using ASTCopier::operator (); - virtual Statement operator()(VariableDeclaration const& _varDecl) override; - virtual Statement operator()(FunctionDefinition const& _funDef) override; + Statement operator()(VariableDeclaration const& _varDecl) override; + Statement operator()(FunctionDefinition const& _funDef) override; - virtual YulString translateIdentifier(YulString _name) override; + YulString translateIdentifier(YulString _name) override; NameDispenser& m_nameDispenser; YulString m_varNamePrefix; @@ -153,4 +151,3 @@ public: } -} diff --git a/libyul/optimiser/FunctionGrouper.cpp b/libyul/optimiser/FunctionGrouper.cpp index 3d2e5322..02ce22cd 100644 --- a/libyul/optimiser/FunctionGrouper.cpp +++ b/libyul/optimiser/FunctionGrouper.cpp @@ -21,13 +21,13 @@ #include <libyul/optimiser/FunctionGrouper.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <boost/range/algorithm_ext/erase.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; diff --git a/libyul/optimiser/FunctionGrouper.h b/libyul/optimiser/FunctionGrouper.h index 63cfbfb1..3b3f48a7 100644 --- a/libyul/optimiser/FunctionGrouper.h +++ b/libyul/optimiser/FunctionGrouper.h @@ -21,10 +21,8 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> -namespace dev -{ namespace yul { @@ -43,4 +41,3 @@ public: }; } -} diff --git a/libyul/optimiser/FunctionHoister.cpp b/libyul/optimiser/FunctionHoister.cpp index c196dead..bd1c781b 100644 --- a/libyul/optimiser/FunctionHoister.cpp +++ b/libyul/optimiser/FunctionHoister.cpp @@ -22,14 +22,13 @@ #include <libyul/optimiser/FunctionHoister.h> #include <libyul/optimiser/Utilities.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void FunctionHoister::operator()(Block& _block) diff --git a/libyul/optimiser/FunctionHoister.h b/libyul/optimiser/FunctionHoister.h index 823b9e2b..31092069 100644 --- a/libyul/optimiser/FunctionHoister.h +++ b/libyul/optimiser/FunctionHoister.h @@ -21,12 +21,9 @@ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> -namespace dev -{ namespace yul { @@ -49,4 +46,3 @@ private: }; } -} diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp index deaaee97..662cdf25 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp @@ -21,12 +21,11 @@ #include <libyul/optimiser/InlinableExpressionFunctionFinder.h> #include <libyul/optimiser/Utilities.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void InlinableExpressionFunctionFinder::operator()(Identifier const& _identifier) { diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.h b/libyul/optimiser/InlinableExpressionFunctionFinder.h index baf4bbfc..afde8a2a 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.h +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.h @@ -20,13 +20,11 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <set> -namespace dev -{ namespace yul { @@ -49,9 +47,9 @@ public: } using ASTWalker::operator(); - virtual void operator()(Identifier const& _identifier) override; - virtual void operator()(FunctionCall const& _funCall) override; - virtual void operator()(FunctionDefinition const& _function) override; + void operator()(Identifier const& _identifier) override; + void operator()(FunctionCall const& _funCall) override; + void operator()(FunctionDefinition const& _function) override; private: void checkAllowed(YulString _name) @@ -66,4 +64,3 @@ private: }; } -} diff --git a/libyul/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp index f3306598..63eea2db 100644 --- a/libyul/optimiser/MainFunction.cpp +++ b/libyul/optimiser/MainFunction.cpp @@ -24,13 +24,13 @@ #include <libyul/optimiser/NameCollector.h> #include <libyul/Exceptions.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void MainFunction::operator()(Block& _block) diff --git a/libyul/optimiser/MainFunction.h b/libyul/optimiser/MainFunction.h index 4a73283a..96acc0ac 100644 --- a/libyul/optimiser/MainFunction.h +++ b/libyul/optimiser/MainFunction.h @@ -21,10 +21,8 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> -namespace dev -{ namespace yul { @@ -38,4 +36,3 @@ public: }; } -} diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index 066c6b58..a5557fb3 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -20,10 +20,10 @@ #include <libyul/optimiser/Metrics.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace dev; -using namespace dev::yul; +using namespace yul; size_t CodeSize::codeSize(Statement const& _statement) { diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h index 47c7ec79..ca244600 100644 --- a/libyul/optimiser/Metrics.h +++ b/libyul/optimiser/Metrics.h @@ -22,8 +22,6 @@ #include <libyul/optimiser/ASTWalker.h> -namespace dev -{ namespace yul { @@ -41,12 +39,11 @@ public: static size_t codeSize(Block const& _block); private: - virtual void visit(Statement const& _statement) override; - virtual void visit(Expression const& _expression) override; + void visit(Statement const& _statement) override; + void visit(Expression const& _expression) override; private: size_t m_size = 0; }; } -} diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index 36f55b99..f9079827 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -20,11 +20,11 @@ #include <libyul/optimiser/NameCollector.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void NameCollector::operator()(VariableDeclaration const& _varDecl) { diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index b76eec30..c177a399 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -25,8 +25,6 @@ #include <map> #include <set> -namespace dev -{ namespace yul { @@ -42,8 +40,8 @@ public: } using ASTWalker::operator (); - virtual void operator()(VariableDeclaration const& _varDecl) override; - virtual void operator()(FunctionDefinition const& _funDef) override; + void operator()(VariableDeclaration const& _varDecl) override; + void operator()(FunctionDefinition const& _funDef) override; std::set<YulString> names() const { return m_names; } private: @@ -75,7 +73,7 @@ class Assignments: public ASTWalker { public: using ASTWalker::operator (); - virtual void operator()(Assignment const& _assignment) override; + void operator()(Assignment const& _assignment) override; std::set<YulString> const& names() const { return m_names; } private: @@ -83,4 +81,3 @@ private: }; } -} diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index 3c870fa5..e7cdc60f 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -21,12 +21,11 @@ #include <libyul/optimiser/NameDispenser.h> #include <libyul/optimiser/NameCollector.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; NameDispenser::NameDispenser(Block const& _ast): NameDispenser(NameCollector(_ast).names()) diff --git a/libyul/optimiser/NameDispenser.h b/libyul/optimiser/NameDispenser.h index 7311440b..664a5265 100644 --- a/libyul/optimiser/NameDispenser.h +++ b/libyul/optimiser/NameDispenser.h @@ -19,14 +19,12 @@ */ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/YulString.h> #include <set> -namespace dev -{ namespace yul { @@ -58,4 +56,3 @@ private: }; } -} diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp index b7217074..7b18e8ca 100644 --- a/libyul/optimiser/RedundantAssignEliminator.cpp +++ b/libyul/optimiser/RedundantAssignEliminator.cpp @@ -22,8 +22,7 @@ #include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/Semantics.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -31,7 +30,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void RedundantAssignEliminator::operator()(Identifier const& _identifier) diff --git a/libyul/optimiser/RedundantAssignEliminator.h b/libyul/optimiser/RedundantAssignEliminator.h index 76106aae..54d65823 100644 --- a/libyul/optimiser/RedundantAssignEliminator.h +++ b/libyul/optimiser/RedundantAssignEliminator.h @@ -21,14 +21,11 @@ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <map> -namespace dev -{ namespace yul { @@ -190,4 +187,3 @@ private: }; } -} diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index 38d50ef4..4180bfc3 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -23,12 +23,11 @@ #include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/ASTCopier.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void Rematerialiser::visit(Expression& _e) { diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index f82465eb..b3841519 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -22,8 +22,6 @@ #include <libyul/optimiser/DataFlowAnalyzer.h> -namespace dev -{ namespace yul { @@ -36,9 +34,8 @@ class Rematerialiser: public DataFlowAnalyzer { protected: using ASTModifier::visit; - virtual void visit(Expression& _e) override; + void visit(Expression& _e) override; }; } -} diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index f209ee7b..928c0859 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -23,15 +23,15 @@ #include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameDispenser.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace langutil; +using namespace yul; using namespace dev::solidity; void SSATransform::operator()(Identifier& _identifier) diff --git a/libyul/optimiser/SSATransform.h b/libyul/optimiser/SSATransform.h index bb642549..4cb62f23 100644 --- a/libyul/optimiser/SSATransform.h +++ b/libyul/optimiser/SSATransform.h @@ -20,14 +20,11 @@ */ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <vector> -namespace dev -{ namespace yul { @@ -95,4 +92,3 @@ private: }; } -} diff --git a/libyul/optimiser/SSAValueTracker.cpp b/libyul/optimiser/SSAValueTracker.cpp index 491117da..35b29b04 100644 --- a/libyul/optimiser/SSAValueTracker.cpp +++ b/libyul/optimiser/SSAValueTracker.cpp @@ -21,11 +21,11 @@ #include <libyul/optimiser/SSAValueTracker.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void SSAValueTracker::operator()(Assignment const& _assignment) { diff --git a/libyul/optimiser/SSAValueTracker.h b/libyul/optimiser/SSAValueTracker.h index d1539c86..e182e013 100644 --- a/libyul/optimiser/SSAValueTracker.h +++ b/libyul/optimiser/SSAValueTracker.h @@ -26,8 +26,6 @@ #include <map> #include <set> -namespace dev -{ namespace yul { @@ -41,8 +39,8 @@ class SSAValueTracker: public ASTWalker { public: using ASTWalker::operator(); - virtual void operator()(VariableDeclaration const& _varDecl) override; - virtual void operator()(Assignment const& _assignment) override; + void operator()(VariableDeclaration const& _varDecl) override; + void operator()(Assignment const& _assignment) override; std::map<YulString, Expression const*> const& values() const { return m_values; } Expression const* value(YulString _name) const { return m_values.at(_name); } @@ -54,4 +52,3 @@ private: }; } -} diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 3c49016e..91bb2709 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -21,8 +21,7 @@ #include <libyul/optimiser/Semantics.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libevmasm/SemanticInformation.h> @@ -30,7 +29,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; MovableChecker::MovableChecker(Expression const& _expression) { diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 620a91cb..70c50806 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -24,8 +24,6 @@ #include <set> -namespace dev -{ namespace yul { @@ -38,12 +36,12 @@ public: MovableChecker() = default; explicit MovableChecker(Expression const& _expression); - virtual void operator()(Identifier const& _identifier) override; - virtual void operator()(FunctionalInstruction const& _functionalInstruction) override; - virtual void operator()(FunctionCall const& _functionCall) override; + void operator()(Identifier const& _identifier) override; + void operator()(FunctionalInstruction const& _functionalInstruction) override; + void operator()(FunctionCall const& _functionCall) override; /// Disallow visiting anything apart from Expressions (this throws). - virtual void visit(Statement const&) override; + void visit(Statement const&) override; using ASTWalker::visit; bool movable() const { return m_movable; } @@ -57,4 +55,3 @@ private: }; } -} diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 5721042f..b3190fef 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -24,14 +24,14 @@ #include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/SyntacticalEquality.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libevmasm/RuleList.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace langutil; +using namespace yul; SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch( @@ -123,7 +123,7 @@ bool Pattern::matches(Expression const& _expr, map<YulString, Expression const*> if (expr->type() != typeid(Literal)) return false; Literal const& literal = boost::get<Literal>(*expr); - if (literal.kind != assembly::LiteralKind::Number) + if (literal.kind != LiteralKind::Number) return false; if (m_data && *m_data != u256(literal.value.str())) return false; @@ -193,7 +193,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const if (m_kind == PatternKind::Constant) { assertThrow(m_data, OptimizerException, "No match group and no constant value given."); - return Literal{_location, assembly::LiteralKind::Number, YulString{formatNumber(*m_data)}, {}}; + return Literal{_location, LiteralKind::Number, YulString{formatNumber(*m_data)}, {}}; } else if (m_kind == PatternKind::Operation) { @@ -208,7 +208,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const u256 Pattern::d() const { Literal const& literal = boost::get<Literal>(matchGroupValue()); - assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); + assertThrow(literal.kind == LiteralKind::Number, OptimizerException, ""); assertThrow(isValidDecimal(literal.value.str()) || isValidHex(literal.value.str()), OptimizerException, ""); return u256(literal.value.str()); } diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index b608ca91..16aaba04 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -23,17 +23,14 @@ #include <libevmasm/ExpressionClasses.h> #include <libevmasm/SimplificationRule.h> -#include <libyul/ASTDataForward.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmDataForward.h> +#include <libyul/AsmData.h> #include <boost/noncopyable.hpp> #include <functional> #include <vector> -namespace dev -{ namespace yul { @@ -86,11 +83,11 @@ public: /// Matches any expression. Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {} // Matches a specific constant value. - Pattern(unsigned _value): Pattern(u256(_value)) {} + Pattern(unsigned _value): Pattern(dev::u256(_value)) {} // Matches a specific constant value. - Pattern(u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<u256>(_value)) {} + Pattern(dev::u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<dev::u256>(_value)) {} // Matches a given instruction with given arguments - Pattern(solidity::Instruction _instruction, std::vector<Pattern> const& _arguments = {}); + Pattern(dev::solidity::Instruction _instruction, std::vector<Pattern> const& _arguments = {}); /// Sets this pattern to be part of the match group with the identifier @a _group. /// Inside one rule, all patterns in the same match group have to match expressions from the /// same expression equivalence class. @@ -101,24 +98,23 @@ public: std::vector<Pattern> arguments() const { return m_arguments; } /// @returns the data of the matched expression if this pattern is part of a match group. - u256 d() const; + dev::u256 d() const; - solidity::Instruction instruction() const; + dev::solidity::Instruction instruction() const; /// Turns this pattern into an actual expression. Should only be called /// for patterns resulting from an action, i.e. with match groups assigned. - Expression toExpression(SourceLocation const& _location) const; + Expression toExpression(langutil::SourceLocation const& _location) const; private: Expression const& matchGroupValue() const; PatternKind m_kind = PatternKind::Any; - solidity::Instruction m_instruction; ///< Only valid if m_kind is Operation - std::shared_ptr<u256> m_data; ///< Only valid if m_kind is Constant + dev::solidity::Instruction m_instruction; ///< Only valid if m_kind is Operation + std::shared_ptr<dev::u256> m_data; ///< Only valid if m_kind is Constant std::vector<Pattern> m_arguments; unsigned m_matchGroup = 0; std::map<unsigned, Expression const*>* m_matchGroups = nullptr; }; } -} diff --git a/libyul/optimiser/Substitution.cpp b/libyul/optimiser/Substitution.cpp index 9b3d4c03..bc9efe96 100644 --- a/libyul/optimiser/Substitution.cpp +++ b/libyul/optimiser/Substitution.cpp @@ -20,11 +20,11 @@ #include <libyul/optimiser/Substitution.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; Expression Substitution::translate(Expression const& _expression) { diff --git a/libyul/optimiser/Substitution.h b/libyul/optimiser/Substitution.h index 59ee4620..41f73b92 100644 --- a/libyul/optimiser/Substitution.h +++ b/libyul/optimiser/Substitution.h @@ -21,13 +21,10 @@ #pragma once #include <libyul/optimiser/ASTCopier.h> - #include <libyul/YulString.h> #include <map> -namespace dev -{ namespace yul { @@ -40,11 +37,10 @@ public: Substitution(std::map<YulString, Expression const*> const& _substitutions): m_substitutions(_substitutions) {} - virtual Expression translate(Expression const& _expression) override; + Expression translate(Expression const& _expression) override; private: std::map<YulString, Expression const*> const& m_substitutions; }; } -} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 7d52a5a8..36f0e1eb 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -27,6 +27,7 @@ #include <libyul/optimiser/ExpressionJoiner.h> #include <libyul/optimiser/ExpressionInliner.h> #include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/ForLoopInitRewriter.h> #include <libyul/optimiser/Rematerialiser.h> #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionSimplifier.h> @@ -34,21 +35,19 @@ #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/VarDeclPropagator.h> - -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> +#include <libyul/AsmPrinter.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void OptimiserSuite::run( Block& _ast, - solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + AsmAnalysisInfo const& _analysisInfo, set<YulString> const& _externallyUsedIdentifiers ) { @@ -58,6 +57,7 @@ void OptimiserSuite::run( (FunctionHoister{})(ast); (FunctionGrouper{})(ast); + (ForLoopInitRewriter{})(ast); NameDispenser dispenser{ast}; diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index 5b564c56..795326b4 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -20,23 +20,16 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/YulString.h> #include <set> -namespace dev -{ -namespace solidity -{ -namespace assembly -{ -struct AsmAnalysisInfo; -} -} namespace yul { +struct AsmAnalysisInfo; + /** * Optimiser suite that combines all steps and also provides the settings for the heuristics */ @@ -45,11 +38,10 @@ class OptimiserSuite public: static void run( Block& _ast, - solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + AsmAnalysisInfo const& _analysisInfo, std::set<YulString> const& _externallyUsedIdentifiers = {} ); }; } -} diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index 66912383..99ce06e5 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -21,14 +21,13 @@ #include <libyul/optimiser/SyntacticalEquality.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& _e2) { diff --git a/libyul/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h index e9fbebe0..63c51b4f 100644 --- a/libyul/optimiser/SyntacticalEquality.h +++ b/libyul/optimiser/SyntacticalEquality.h @@ -20,12 +20,10 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <vector> -namespace dev -{ namespace yul { @@ -47,4 +45,3 @@ protected: }; } -} diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index 71e86798..31aead82 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -24,14 +24,13 @@ #include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Utilities.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <boost/algorithm/cxx11/none_of.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; UnusedPruner::UnusedPruner(Block& _ast, set<YulString> const& _externallyUsedFunctions) { diff --git a/libyul/optimiser/UnusedPruner.h b/libyul/optimiser/UnusedPruner.h index b5aea3dd..64e02b35 100644 --- a/libyul/optimiser/UnusedPruner.h +++ b/libyul/optimiser/UnusedPruner.h @@ -26,8 +26,6 @@ #include <map> #include <set> -namespace dev -{ namespace yul { @@ -45,7 +43,7 @@ public: explicit UnusedPruner(Block& _ast, std::set<YulString> const& _externallyUsedFunctions = {}); using ASTModifier::operator(); - virtual void operator()(Block& _block) override; + void operator()(Block& _block) override; // @returns true iff the code changed in the previous run. bool shouldRunAgain() const { return m_shouldRunAgain; } @@ -62,4 +60,3 @@ private: }; } -} diff --git a/libyul/optimiser/Utilities.cpp b/libyul/optimiser/Utilities.cpp index df01ed39..b8cdd339 100644 --- a/libyul/optimiser/Utilities.cpp +++ b/libyul/optimiser/Utilities.cpp @@ -20,7 +20,7 @@ #include <libyul/optimiser/Utilities.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -28,9 +28,9 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; -void dev::yul::removeEmptyBlocks(Block& _block) +void yul::removeEmptyBlocks(Block& _block) { auto isEmptyBlock = [](Statement const& _st) -> bool { return _st.type() == typeid(Block) && boost::get<Block>(_st).statements.empty(); diff --git a/libyul/optimiser/Utilities.h b/libyul/optimiser/Utilities.h index 5b18a27c..c543b119 100644 --- a/libyul/optimiser/Utilities.h +++ b/libyul/optimiser/Utilities.h @@ -20,10 +20,8 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> -namespace dev -{ namespace yul { @@ -31,4 +29,3 @@ namespace yul void removeEmptyBlocks(Block& _block); } -} diff --git a/libyul/optimiser/VarDeclPropagator.cpp b/libyul/optimiser/VarDeclPropagator.cpp index 537b7020..bf974f44 100644 --- a/libyul/optimiser/VarDeclPropagator.cpp +++ b/libyul/optimiser/VarDeclPropagator.cpp @@ -16,7 +16,7 @@ */ #include <libyul/optimiser/VarDeclPropagator.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> #include <boost/range/algorithm_ext/erase.hpp> #include <algorithm> @@ -24,10 +24,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; - -using dev::solidity::assembly::TypedName; -using dev::solidity::assembly::TypedNameList; +using namespace yul; void VarDeclPropagator::operator()(Block& _block) { diff --git a/libyul/optimiser/VarDeclPropagator.h b/libyul/optimiser/VarDeclPropagator.h index 4522d23a..1908c214 100644 --- a/libyul/optimiser/VarDeclPropagator.h +++ b/libyul/optimiser/VarDeclPropagator.h @@ -17,16 +17,14 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <libyul/Exceptions.h> -#include <libsolidity/inlineasm/AsmDataForward.h> +#include <libyul/AsmDataForward.h> #include <vector> #include <set> #include <map> -namespace dev -{ namespace yul { @@ -60,4 +58,3 @@ private: }; } -} diff --git a/scripts/Dockerfile_alpine b/scripts/Dockerfile_alpine new file mode 100644 index 00000000..21bba456 --- /dev/null +++ b/scripts/Dockerfile_alpine @@ -0,0 +1,5 @@ +FROM alpine +MAINTAINER chriseth <chris@ethereum.org> + +COPY upload/solc-static-linux /usr/local/bin/solc +ENTRYPOINT ["/usr/local/bin/solc"] diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index cddcd4f8..14c497ae 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -30,5 +30,5 @@ set -e if [[ "$OSTYPE" != "darwin"* ]]; then ./scripts/travis-emscripten/install_deps.sh - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.35.4-64bit ./scripts/travis-emscripten/build_emscripten.sh + docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.37.21-64bit ./scripts/travis-emscripten/build_emscripten.sh fi diff --git a/scripts/bytecodecompare/storebytecode.sh b/scripts/bytecodecompare/storebytecode.sh index ccf6e60e..4208d67f 100755 --- a/scripts/bytecodecompare/storebytecode.sh +++ b/scripts/bytecodecompare/storebytecode.sh @@ -58,7 +58,7 @@ for (var optimize of [false, true]) if (filename !== undefined) { var inputs = {} - inputs[filename] = fs.readFileSync(filename).toString() + inputs[filename] = { content: fs.readFileSync(filename).toString() } var input = { language: 'Solidity', sources: inputs, @@ -68,16 +68,22 @@ for (var optimize of [false, true]) } } var result = JSON.parse(compiler.compile(JSON.stringify(input))) - if (!('contracts' in result) || Object.keys(result['contracts']).length === 0) + if ( + !('contracts' in result) || + Object.keys(result['contracts']).length === 0 || + !result['contracts'][filename] || + Object.keys(result['contracts'][filename]).length === 0 + ) { + // NOTE: do not exit here because this may be run on source which cannot be compiled console.log(filename + ': ERROR') } else { - for (var contractName in result['contracts']) + for (var contractName in result['contracts'][filename]) { - console.log(contractName + ' ' + result['contracts'][contractName].evm.bytecode.object) - console.log(contractName + ' ' + result['contracts'][contractName].metadata) + console.log(filename + ':' + contractName + ' ' + result['contracts'][filename][contractName].evm.bytecode.object) + console.log(filename + ':' + contractName + ' ' + result['contracts'][filename][contractName].metadata) } } } diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 4f716d66..171867f9 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +. scripts/report_errors.sh + ( REPO_ROOT="$(dirname "$0")"/.. cd $REPO_ROOT @@ -8,8 +10,8 @@ WHITESPACE=$(git grep -n -I -E "^.*[[:space:]]+$" | grep -v "test/libsolidity/AS if [[ "$WHITESPACE" != "" ]] then - echo "Error: Trailing whitespace found:" >&2 - echo "$WHITESPACE" >&2 + echo "Error: Trailing whitespace found:" | tee -a $ERROR_LOG + echo "$WHITESPACE" | tee -a $ERROR_LOG exit 1 fi @@ -22,8 +24,8 @@ git grep -nIE "\<if\>\s*\(.*\)\s*\{\s*$" -- '*.h' '*.cpp' if [[ "$FORMATERROR" != "" ]] then - echo "Error: Format error for if/for:" >&2 - echo "$FORMATERROR" >&2 + echo "Error: Format error for if/for:" | tee -a $ERROR_LOG + echo "$FORMATERROR" | tee -a $ERROR_LOG exit 1 fi ) diff --git a/scripts/docker_build.sh b/scripts/docker_build.sh index 22657a8c..9eedec34 100755 --- a/scripts/docker_build.sh +++ b/scripts/docker_build.sh @@ -2,7 +2,11 @@ set -e +# Scratch image docker build -t ethereum/solc:build -f scripts/Dockerfile . tmp_container=$(docker create ethereum/solc:build sh) mkdir -p upload docker cp ${tmp_container}:/usr/bin/solc upload/solc-static-linux + +# Alpine image +docker build -t ethereum/solc:build-alpine -f scripts/Dockerfile_alpine . diff --git a/scripts/docker_deploy.sh b/scripts/docker_deploy.sh index 00705725..36e918cf 100755 --- a/scripts/docker_deploy.sh +++ b/scripts/docker_deploy.sh @@ -2,20 +2,28 @@ set -e +image="ethereum/solc" + +tag_and_push() +{ + docker tag "$image:$1" "$image:$2" + docker push "$image:$2" +} + docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"; version=$($(dirname "$0")/get_version.sh) if [ "$TRAVIS_BRANCH" = "develop" ] then - docker tag ethereum/solc:build ethereum/solc:nightly; - docker tag ethereum/solc:build ethereum/solc:nightly-"$version"-"$TRAVIS_COMMIT" - docker push ethereum/solc:nightly-"$version"-"$TRAVIS_COMMIT"; - docker push ethereum/solc:nightly; + tag_and_push build nightly + tag_and_push build nightly-"$version"-"$TRAVIS_COMMIT" + tag_and_push build-alpine nightly-alpine + tag_and_push build-alpine nightly-alpine-"$version"-"$TRAVIS_COMMIT" elif [ "$TRAVIS_TAG" = v"$version" ] then - docker tag ethereum/solc:build ethereum/solc:stable; - docker tag ethereum/solc:build ethereum/solc:"$version"; - docker push ethereum/solc:stable; - docker push ethereum/solc:"$version"; + tag_and_push build stable + tag_and_push build "$version" + tag_and_push build-alpine stable-alpine + tag_and_push build-alpine "$version"-alpine else echo "Not publishing docker image from branch $TRAVIS_BRANCH or tag $TRAVIS_TAG" fi diff --git a/scripts/docker_deploy_manual.sh b/scripts/docker_deploy_manual.sh index c098f4ee..0393d22d 100755 --- a/scripts/docker_deploy_manual.sh +++ b/scripts/docker_deploy_manual.sh @@ -7,6 +7,7 @@ then echo "Usage: $0 <tag/branch>" exit 1 fi +image="ethereum/solc" branch="$1" #docker login @@ -27,21 +28,27 @@ else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt fi +tag_and_push() +{ + docker tag "$image:$1" "$image:$2" + docker push "$image:$2" +} + rm -rf .git -docker build -t ethereum/solc:build -f scripts/Dockerfile . -tmp_container=$(docker create ethereum/solc:build sh) +docker build -t "$image":build -f scripts/Dockerfile . +tmp_container=$(docker create "$image":build sh) if [ "$branch" = "develop" ] then - docker tag ethereum/solc:build ethereum/solc:nightly; - docker tag ethereum/solc:build ethereum/solc:nightly-"$version"-"$commithash" - docker push ethereum/solc:nightly-"$version"-"$commithash"; - docker push ethereum/solc:nightly; + tag_and_push build nightly + tag_and_push build nightly-"$version"-"$commithash" + tag_and_push build-alpine nightly-alpine + tag_and_push build-alpine nightly-alpine-"$version"-"$commithash" elif [ "$branch" = v"$version" ] then - docker tag ethereum/solc:build ethereum/solc:stable; - docker tag ethereum/solc:build ethereum/solc:"$version"; - docker push ethereum/solc:stable; - docker push ethereum/solc:"$version"; + tag_and_push build stable + tag_and_push build "$version" + tag_and_push build-alpine stable-alpine + tag_and_push build-alpine "$version"-alpine else echo "Not publishing docker image from branch or tag $branch" fi diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index b107f7c9..09d5a249 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -55,7 +55,7 @@ detect_linux_distro() { DISTRO=$(lsb_release -is) elif [ -f /etc/os-release ]; then # extract 'foo' from NAME=foo, only on the line with NAME=foo - DISTRO=$(sed -n -e 's/^NAME="\(.*\)\"/\1/p' /etc/os-release) + DISTRO=$(sed -n -e 's/^NAME="\?\([^"]*\)"\?$/\1/p' /etc/os-release) elif [ -f /etc/centos-release ]; then DISTRO=CentOS else diff --git a/scripts/report_errors.sh b/scripts/report_errors.sh new file mode 100755 index 00000000..55fc2e8c --- /dev/null +++ b/scripts/report_errors.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +export ERROR_LOG="/tmp/error.log" + +function report_error_to_github +{ + if [ $? -eq 0 ] + then + exit 0 + fi + + if [ -z $CIRCLE_PR_NUMBER ] + then + CIRCLE_PR_NUMBER="${CIRCLE_PULL_REQUEST//[^0-9]/}" + fi + + ERROR_MSG=$(cat $ERROR_LOG) + + echo $ERROR_MSG + + if [ ! -z $CI ] + then + echo "posting error message to github" + post_error_to_github + fi +} + +function post_error_to_github +{ + if [ -z $CIRCLE_PR_NUMBER ] + then + CIRCLE_PR_NUMBER="${CIRCLE_PULL_REQUEST//[^0-9]/}" + fi + + GITHUB_API_URL="https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/issues/$CIRCLE_PR_NUMBER/comments" + + ESCAPED_ERROR_MSG=$(cat -e $ERROR_LOG | sed 's/\\/\\\\/g' | sed 's/"/\\\"/g') + + FORMATTED_ERROR_MSG=$(echo $ESCAPED_ERROR_MSG | sed 's/\$/\\n/g' | tr -d '\n') + + curl --request POST \ + --url $GITHUB_API_URL \ + --header 'accept: application/vnd.github.v3+json' \ + --header 'content-type: application/json' \ + -u stackenbotten:$GITHUB_ACCESS_TOKEN \ + --data "{\"body\": \"There was an error when running \`$CIRCLE_JOB\` for commit \`$CIRCLE_SHA1\`:\n\`\`\`\n$FORMATTED_ERROR_MSG\n\`\`\`\nPlease check that your changes are working as intended.\"}" +} + +trap report_error_to_github EXIT diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index f49ff5b2..32899903 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -56,18 +56,14 @@ else echo 'NODE_JS=["nodejs", "--stack_size=8192"]' > ~/.emscripten fi - # Boost echo -en 'travis_fold:start:compiling_boost\\r' -cd "$WORKSPACE"/boost_1_57_0 +cd "$WORKSPACE"/boost_1_67_0 # if b2 exists, it is a fresh checkout, otherwise it comes from the cache # and is already compiled test -e b2 && ( -sed -i 's|using gcc ;|using gcc : : em++ ;|g' ./project-config.jam -sed -i 's|$(archiver\[1\])|emar|g' ./tools/build/src/tools/gcc.jam -sed -i 's|$(ranlib\[1\])|emranlib|g' ./tools/build/src/tools/gcc.jam -./b2 link=static variant=release threading=single runtime-link=static \ - system regex filesystem unit_test_framework program_options +./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \ + system regex filesystem unit_test_framework program_options cxxflags="-Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" find . -name 'libboost*.a' -exec cp {} . \; rm -rf b2 libs doc tools more bin.v2 status ) @@ -89,17 +85,12 @@ cmake \ -DBoost_FOUND=1 \ -DBoost_USE_STATIC_LIBS=1 \ -DBoost_USE_STATIC_RUNTIME=1 \ - -DBoost_INCLUDE_DIR="$WORKSPACE"/boost_1_57_0/ \ - -DBoost_FILESYSTEM_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_filesystem.a \ - -DBoost_FILESYSTEM_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_filesystem.a \ - -DBoost_PROGRAM_OPTIONS_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_program_options.a \ - -DBoost_PROGRAM_OPTIONS_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_program_options.a \ - -DBoost_REGEX_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_regex.a \ - -DBoost_REGEX_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_regex.a \ - -DBoost_SYSTEM_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_system.a \ - -DBoost_SYSTEM_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_system.a \ - -DBoost_UNIT_TEST_FRAMEWORK_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \ - -DBoost_UNIT_TEST_FRAMEWORK_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \ + -DBoost_INCLUDE_DIR="$WORKSPACE"/boost_1_67_0/ \ + -DBoost_FILESYSTEM_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_filesystem.a \ + -DBoost_PROGRAM_OPTIONS_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_program_options.a \ + -DBoost_REGEX_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_regex.a \ + -DBoost_SYSTEM_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_system.a \ + -DBoost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_unit_test_framework.a \ -DTESTS=0 \ .. make -j 4 diff --git a/scripts/travis-emscripten/install_deps.sh b/scripts/travis-emscripten/install_deps.sh index 45c16a9f..155506e4 100755 --- a/scripts/travis-emscripten/install_deps.sh +++ b/scripts/travis-emscripten/install_deps.sh @@ -30,11 +30,19 @@ set -ev echo -en 'travis_fold:start:installing_dependencies\\r' -test -e boost_1_57_0 -a -e boost_1_57_0/boost || ( -wget 'https://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.gz/download'\ - -O - | tar xz -cd boost_1_57_0 -./bootstrap.sh --with-toolset=gcc --with-libraries=thread,system,regex,date_time,chrono,filesystem,program_options,random +test -e boost_1_67_0 -a -e boost_1_67_0/boost || ( +rm -rf boost_1_67_0 +rm -f boost.tar.xz +wget -q 'https://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.gz/download'\ + -O boost.tar.xz +test "$(shasum boost.tar.xz)" = "77e73c9fd7bf85b14067767b9e8fdc39b49ee0f2 boost.tar.xz" +tar -xzf boost.tar.xz +rm boost.tar.xz +cd boost_1_67_0 +./bootstrap.sh +wget -q 'https://raw.githubusercontent.com/tee3/boost-build-emscripten/master/emscripten.jam' +test "$(shasum emscripten.jam)" = "a7e13fc2c1e53b0e079ef440622f879aa6da3049 emscripten.jam" +echo "using emscripten : : em++ ;" >> project-config.jam ) cd .. echo -en 'travis_fold:end:installing_dependencies\\r' diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 844cef90..38e778c6 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -26,15 +26,15 @@ #include "license.h" #include <libsolidity/interface/Version.h> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <libsolidity/parsing/Parser.h> #include <libsolidity/ast/ASTPrinter.h> #include <libsolidity/ast/ASTJsonConverter.h> #include <libsolidity/analysis/NameAndTypeResolver.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/StandardCompiler.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <liblangutil/SourceReferenceFormatter.h> #include <libsolidity/interface/GasEstimator.h> #include <libsolidity/interface/AssemblyStack.h> @@ -62,6 +62,7 @@ #include <fstream> using namespace std; +using namespace langutil; namespace po = boost::program_options; namespace dev @@ -69,6 +70,24 @@ namespace dev namespace solidity { +bool g_hasOutput = false; + +std::ostream& sout() +{ + g_hasOutput = true; + return cout; +} + +std::ostream& serr(bool _used = true) +{ + if (_used) + g_hasOutput = true; + return cerr; +} + +#define cout +#define cerr + static string const g_stdinFileNameStr = "<stdin>"; static string const g_strAbi = "abi"; static string const g_strAllowPaths = "allow-paths"; @@ -180,7 +199,7 @@ static set<string> const g_machineArgs static void version() { - cout << + sout() << "solc, the solidity compiler commandline interface" << endl << "Version: " << @@ -191,9 +210,9 @@ static void version() static void license() { - cout << otherLicenses << endl; + sout() << otherLicenses << endl; // This is a static variable generated by cmake from LICENSE.txt - cout << licenseText << endl; + sout() << licenseText << endl; exit(0); } @@ -229,8 +248,8 @@ void CommandLineInterface::handleBinary(string const& _contract) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); else { - cout << "Binary: " << endl; - cout << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; + sout() << "Binary: " << endl; + sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; } } if (m_args.count(g_argBinaryRuntime)) @@ -239,8 +258,8 @@ void CommandLineInterface::handleBinary(string const& _contract) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); else { - cout << "Binary of the runtime part: " << endl; - cout << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl; + sout() << "Binary of the runtime part: " << endl; + sout() << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl; } } } @@ -251,9 +270,9 @@ void CommandLineInterface::handleOpcode(string const& _contract) createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", solidity::disassemble(m_compiler->object(_contract).bytecode)); else { - cout << "Opcodes: " << endl; - cout << solidity::disassemble(m_compiler->object(_contract).bytecode); - cout << endl; + sout() << "Opcodes: " << endl; + sout() << solidity::disassemble(m_compiler->object(_contract).bytecode); + sout() << endl; } } @@ -278,7 +297,7 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out); else - cout << "Function signatures: " << endl << out; + sout() << "Function signatures: " << endl << out; } void CommandLineInterface::handleMetadata(string const& _contract) @@ -290,7 +309,7 @@ void CommandLineInterface::handleMetadata(string const& _contract) if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data); else - cout << "Metadata: " << endl << data << endl; + sout() << "Metadata: " << endl << data << endl; } void CommandLineInterface::handleABI(string const& _contract) @@ -302,7 +321,7 @@ void CommandLineInterface::handleABI(string const& _contract) if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); else - cout << "Contract JSON ABI " << endl << data << endl; + sout() << "Contract JSON ABI " << endl << data << endl; } void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) @@ -336,8 +355,8 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output); else { - cout << title << endl; - cout << output << endl; + sout() << title << endl; + sout() << output << endl; } } @@ -346,39 +365,39 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra void CommandLineInterface::handleGasEstimation(string const& _contract) { Json::Value estimates = m_compiler->gasEstimates(_contract); - cout << "Gas estimation:" << endl; + sout() << "Gas estimation:" << endl; if (estimates["creation"].isObject()) { Json::Value creation = estimates["creation"]; - cout << "construction:" << endl; - cout << " " << creation["executionCost"].asString(); - cout << " + " << creation["codeDepositCost"].asString(); - cout << " = " << creation["totalCost"].asString() << endl; + sout() << "construction:" << endl; + sout() << " " << creation["executionCost"].asString(); + sout() << " + " << creation["codeDepositCost"].asString(); + sout() << " = " << creation["totalCost"].asString() << endl; } if (estimates["external"].isObject()) { Json::Value externalFunctions = estimates["external"]; - cout << "external:" << endl; + sout() << "external:" << endl; for (auto const& name: externalFunctions.getMemberNames()) { if (name.empty()) - cout << " fallback:\t"; + sout() << " fallback:\t"; else - cout << " " << name << ":\t"; - cout << externalFunctions[name].asString() << endl; + sout() << " " << name << ":\t"; + sout() << externalFunctions[name].asString() << endl; } } if (estimates["internal"].isObject()) { Json::Value internalFunctions = estimates["internal"]; - cout << "internal:" << endl; + sout() << "internal:" << endl; for (auto const& name: internalFunctions.getMemberNames()) { - cout << " " << name << ":\t"; - cout << internalFunctions[name].asString() << endl; + sout() << " " << name << ":\t"; + sout() << internalFunctions[name].asString() << endl; } } } @@ -400,7 +419,7 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() } else { - cerr << "Invalid remapping: \"" << path << "\"." << endl; + serr() << "Invalid remapping: \"" << path << "\"." << endl; return false; } } @@ -413,11 +432,11 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() { if (!ignoreMissing) { - cerr << infile << " is not found." << endl; + serr() << infile << " is not found." << endl; return false; } else - cerr << infile << " is not found. Skipping." << endl; + serr() << infile << " is not found. Skipping." << endl; continue; } @@ -426,11 +445,11 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() { if (!ignoreMissing) { - cerr << infile << " is not a valid file." << endl; + serr() << infile << " is not a valid file." << endl; return false; } else - cerr << infile << " is not a valid file. Skipping." << endl; + serr() << infile << " is not a valid file. Skipping." << endl; continue; } @@ -444,7 +463,7 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() m_sourceCodes[g_stdinFileName] = dev::readStandardInput(); if (m_sourceCodes.size() == 0) { - cerr << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; + serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; return false; } @@ -475,7 +494,7 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) auto colon = lib.rfind(':'); if (colon == string::npos) { - cerr << "Colon separator missing in library address specifier \"" << lib << "\"" << endl; + serr() << "Colon separator missing in library address specifier \"" << lib << "\"" << endl; return false; } string libName(lib.begin(), lib.begin() + colon); @@ -486,26 +505,26 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) addrString = addrString.substr(2); if (addrString.empty()) { - cerr << "Empty address provided for library \"" << libName << "\": " << endl; - cerr << "Note that there should not be any whitespace after the colon." << endl; + serr() << "Empty address provided for library \"" << libName << "\": " << endl; + serr() << "Note that there should not be any whitespace after the colon." << endl; return false; } else if (addrString.length() != 40) { - cerr << "Invalid length for address for library \"" << libName << "\": " << addrString.length() << " instead of 40 characters." << endl; + serr() << "Invalid length for address for library \"" << libName << "\": " << addrString.length() << " instead of 40 characters." << endl; return false; } if (!passesAddressChecksum(addrString, false)) { - cerr << "Invalid checksum on address for library \"" << libName << "\": " << addrString << endl; - cerr << "The correct checksum is " << dev::getChecksummedAddress(addrString) << endl; + serr() << "Invalid checksum on address for library \"" << libName << "\": " << addrString << endl; + serr() << "The correct checksum is " << dev::getChecksummedAddress(addrString) << endl; return false; } bytes binAddr = fromHex(addrString); h160 address(binAddr, h160::AlignRight); if (binAddr.size() > 20 || address == h160()) { - cerr << "Invalid address for library \"" << libName << "\": " << addrString << endl; + serr() << "Invalid address for library \"" << libName << "\": " << addrString << endl; return false; } m_libraries[libName] = address; @@ -525,7 +544,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da string pathName = (p / _fileName).string(); if (fs::exists(pathName) && !m_args.count(g_strOverwrite)) { - cerr << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl; + serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl; m_error = true; return; } @@ -542,6 +561,8 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js bool CommandLineInterface::parseArguments(int _argc, char** _argv) { + g_hasOutput = false; + // Declare the supported options. po::options_description desc(R"(solc, the Solidity commandline compiler. @@ -605,15 +626,15 @@ Allowed options)", ) ( g_argAssemble.c_str(), - "Switch to assembly mode, ignoring all options except --machine and assumes input is assembly." + "Switch to assembly mode, ignoring all options except --machine and --optimize and assumes input is assembly." ) ( g_argYul.c_str(), - "Switch to Yul mode, ignoring all options except --machine and assumes input is Yul." + "Switch to Yul mode, ignoring all options except --machine and --optimize and assumes input is Yul." ) ( g_argStrictAssembly.c_str(), - "Switch to strict assembly mode, ignoring all options except --machine and assumes input is strict assembly." + "Switch to strict assembly mode, ignoring all options except --machine and --optimize and assumes input is strict assembly." ) ( g_argMachine.c_str(), @@ -666,13 +687,13 @@ Allowed options)", } catch (po::error const& _exception) { - cerr << _exception.what() << endl; + serr() << _exception.what() << endl; return false; } if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) { - cout << desc; + sout() << desc; return false; } @@ -694,7 +715,7 @@ Allowed options)", for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(","))) if (!g_combinedJsonArgs.count(item)) { - cerr << "Invalid option to --combined-json: " << item << endl; + serr() << "Invalid option to --combined-json: " << item << endl; return false; } } @@ -758,9 +779,7 @@ bool CommandLineInterface::processInput() // path comparison in later parts of the code, so we need to strip // it. if (filesystem_path.filename() == ".") - { filesystem_path.remove_filename(); - } m_allowedDirectories.push_back(filesystem_path); } } @@ -769,7 +788,7 @@ bool CommandLineInterface::processInput() { string input = dev::readStandardInput(); StandardCompiler compiler(fileReader); - cout << compiler.compile(input) << endl; + sout() << compiler.compile(input) << endl; return true; } @@ -787,7 +806,7 @@ bool CommandLineInterface::processInput() boost::optional<EVMVersion> versionOption = EVMVersion::fromString(versionOptionStr); if (!versionOption) { - cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; + serr() << "Invalid option for --evm-version: " << versionOptionStr << endl; return false; } m_evmVersion = *versionOption; @@ -801,6 +820,7 @@ bool CommandLineInterface::processInput() using Machine = AssemblyStack::Machine; Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); Machine targetMachine = Machine::EVM; + bool optimize = m_args.count(g_argOptimize); if (m_args.count(g_argMachine)) { string machine = m_args[g_argMachine].as<string>(); @@ -812,11 +832,22 @@ bool CommandLineInterface::processInput() targetMachine = Machine::eWasm; else { - cerr << "Invalid option for --machine: " << machine << endl; + serr() << "Invalid option for --machine: " << machine << endl; return false; } } - return assemble(inputLanguage, targetMachine); + if (optimize && inputLanguage == Input::Assembly) + { + serr() << + "Optimizer cannot be used for loose assembly. Use --" << + g_strStrictAssembly << + " or --" << + g_strYul << + "." << + endl; + return false; + } + return assemble(inputLanguage, targetMachine, optimize); } if (m_args.count(g_argLink)) { @@ -827,8 +858,8 @@ bool CommandLineInterface::processInput() m_compiler.reset(new CompilerStack(fileReader)); - auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; - SourceReferenceFormatter formatter(cerr, scannerFromSourceName); + auto scannerFromSourceName = [&](string const& _sourceName) -> Scanner const& { return m_compiler->scanner(_sourceName); }; + SourceReferenceFormatter formatter(serr(false), scannerFromSourceName); try { @@ -849,48 +880,55 @@ bool CommandLineInterface::processInput() bool successful = m_compiler->compile(); for (auto const& error: m_compiler->errors()) + { + g_hasOutput = true; formatter.printExceptionInformation( *error, (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); + } if (!successful) return false; } catch (CompilerError const& _exception) { + g_hasOutput = true; formatter.printExceptionInformation(_exception, "Compiler error"); return false; } catch (InternalCompilerError const& _exception) { - cerr << "Internal compiler error during compilation:" << endl + serr() << "Internal compiler error during compilation:" << endl << boost::diagnostic_information(_exception); return false; } catch (UnimplementedFeatureError const& _exception) { - cerr << "Unimplemented feature:" << endl + serr() << "Unimplemented feature:" << endl << boost::diagnostic_information(_exception); return false; } catch (Error const& _error) { if (_error.type() == Error::Type::DocstringParsingError) - cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl; + serr() << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl; else + { + g_hasOutput = true; formatter.printExceptionInformation(_error, _error.typeName()); + } return false; } catch (Exception const& _exception) { - cerr << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; + serr() << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; return false; } catch (...) { - cerr << "Unknown exception during compilation." << endl; + serr() << "Unknown exception during compilation." << endl; return false; } @@ -971,7 +1009,7 @@ void CommandLineInterface::handleCombinedJSON() if (m_args.count(g_argOutputDir)) createJson("combined", json); else - cout << json << endl; + sout() << json << endl; } void CommandLineInterface::handleAst(string const& _argStr) @@ -996,12 +1034,16 @@ void CommandLineInterface::handleAst(string const& _argStr) map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts; for (auto const& contract : m_compiler->contractNames()) { - auto ret = GasEstimator::breakToStatementLevel( - GasEstimator(m_evmVersion).structuralEstimation(*m_compiler->runtimeAssemblyItems(contract), asts), - asts - ); - for (auto const& it: ret) - gasCosts[it.first] += it.second; + if (auto const* assemblyItems = m_compiler->runtimeAssemblyItems(contract)) + { + auto ret = GasEstimator::breakToStatementLevel( + GasEstimator(m_evmVersion).structuralEstimation(*assemblyItems, asts), + asts + ); + for (auto const& it: ret) + gasCosts[it.first] += it.second; + } + } bool legacyFormat = !m_args.count(g_argAstCompactJson); @@ -1027,10 +1069,10 @@ void CommandLineInterface::handleAst(string const& _argStr) } else { - cout << title << endl << endl; + sout() << title << endl << endl; for (auto const& sourceCode: m_sourceCodes) { - cout << endl << "======= " << sourceCode.first << " =======" << endl; + sout() << endl << "======= " << sourceCode.first << " =======" << endl; if (_argStr == g_argAst) { ASTPrinter printer( @@ -1038,10 +1080,10 @@ void CommandLineInterface::handleAst(string const& _argStr) sourceCode.second, gasCosts ); - printer.print(cout); + printer.print(sout()); } else - ASTJsonConverter(legacyFormat, m_compiler->sourceIndices()).print(cout, m_compiler->ast(sourceCode.first)); + ASTJsonConverter(legacyFormat, m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first)); } } } @@ -1089,7 +1131,7 @@ bool CommandLineInterface::link() if (it == end) break; if (end - it < placeholderSize) { - cerr << "Error in binary object file " << src.first << " at position " << (end - src.second.begin()) << endl; + serr() << "Error in binary object file " << src.first << " at position " << (end - src.second.begin()) << endl; return false; } @@ -1100,7 +1142,7 @@ bool CommandLineInterface::link() copy(hexStr.begin(), hexStr.end(), it); } else - cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl; + serr() << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl; it += placeholderSize; } // Remove hints for resolved libraries. @@ -1116,17 +1158,18 @@ void CommandLineInterface::writeLinkedFiles() { for (auto const& src: m_sourceCodes) if (src.first == g_stdinFileName) - cout << src.second << endl; + sout() << src.second << endl; else { ofstream outFile(src.first); outFile << src.second; if (!outFile) { - cerr << "Could not write to file " << src.first << ". Aborting." << endl; + serr() << "Could not write to file " << src.first << ". Aborting." << endl; return; } } + sout() << "Linking completed." << endl; } string CommandLineInterface::libraryPlaceholderHint(string const& _libraryName) @@ -1148,7 +1191,8 @@ string CommandLineInterface::objectWithLinkRefsHex(eth::LinkerObject const& _obj bool CommandLineInterface::assemble( AssemblyStack::Language _language, - AssemblyStack::Machine _targetMachine + AssemblyStack::Machine _targetMachine, + bool _optimize ) { bool successful = true; @@ -1160,15 +1204,17 @@ bool CommandLineInterface::assemble( { if (!stack.parseAndAnalyze(src.first, src.second)) successful = false; + else if (_optimize) + stack.optimize(); } catch (Exception const& _exception) { - cerr << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl; + serr() << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl; return false; } catch (...) { - cerr << "Unknown exception in assembler." << endl; + serr() << "Unknown exception in assembler." << endl; return false; } } @@ -1177,13 +1223,16 @@ bool CommandLineInterface::assemble( { auto const& stack = sourceAndStack.second; auto scannerFromSourceName = [&](string const&) -> Scanner const& { return stack.scanner(); }; - SourceReferenceFormatter formatter(cerr, scannerFromSourceName); + SourceReferenceFormatter formatter(serr(false), scannerFromSourceName); for (auto const& error: stack.errors()) + { + g_hasOutput = true; formatter.printExceptionInformation( *error, (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); + } if (!Error::containsOnlyWarnings(stack.errors())) successful = false; } @@ -1197,11 +1246,11 @@ bool CommandLineInterface::assemble( _targetMachine == AssemblyStack::Machine::EVM ? "EVM" : _targetMachine == AssemblyStack::Machine::EVM15 ? "EVM 1.5" : "eWasm"; - cout << endl << "======= " << src.first << " (" << machine << ") =======" << endl; + sout() << endl << "======= " << src.first << " (" << machine << ") =======" << endl; AssemblyStack& stack = assemblyStacks[src.first]; - cout << endl << "Pretty printed source:" << endl; - cout << stack.print() << endl; + sout() << endl << "Pretty printed source:" << endl; + sout() << stack.print() << endl; MachineAssemblyObject object; try @@ -1210,26 +1259,26 @@ bool CommandLineInterface::assemble( } catch (Exception const& _exception) { - cerr << "Exception while assembling: " << boost::diagnostic_information(_exception) << endl; + serr() << "Exception while assembling: " << boost::diagnostic_information(_exception) << endl; return false; } catch (...) { - cerr << "Unknown exception while assembling." << endl; + serr() << "Unknown exception while assembling." << endl; return false; } - cout << endl << "Binary representation:" << endl; + sout() << endl << "Binary representation:" << endl; if (object.bytecode) - cout << object.bytecode->toHex() << endl; + sout() << object.bytecode->toHex() << endl; else - cerr << "No binary representation found." << endl; + serr() << "No binary representation found." << endl; - cout << endl << "Text representation:" << endl; + sout() << endl << "Text representation:" << endl; if (!object.assembly.empty()) - cout << object.assembly << endl; + sout() << object.assembly << endl; else - cerr << "No text representation found." << endl; + serr() << "No text representation found." << endl; } return true; @@ -1248,7 +1297,7 @@ void CommandLineInterface::outputCompilationResults() for (string const& contract: contracts) { if (needsHumanTargetedStdout(m_args)) - cout << endl << "======= " << contract << " =======" << endl; + sout() << endl << "======= " << contract << " =======" << endl; // do we need EVM assembly? if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson)) @@ -1265,7 +1314,7 @@ void CommandLineInterface::outputCompilationResults() } else { - cout << "EVM assembly:" << endl << ret << endl; + sout() << "EVM assembly:" << endl << ret << endl; } } @@ -1279,6 +1328,14 @@ void CommandLineInterface::outputCompilationResults() handleNatspec(true, contract); handleNatspec(false, contract); } // end of contracts iteration + + if (!g_hasOutput) + { + if (m_args.count(g_argOutputDir)) + sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_args.at(g_argOutputDir).as<string>() << "." << endl; + else + serr() << "Compiler run successful, no output requested." << endl; + } } } diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index aa49383a..0b22ca29 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -23,7 +23,7 @@ #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/AssemblyStack.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <boost/program_options.hpp> #include <boost/filesystem/path.hpp> @@ -59,7 +59,7 @@ private: /// @returns the full object with library placeholder hints in hex. static std::string objectWithLinkRefsHex(eth::LinkerObject const& _obj); - bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); + bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine, bool _optimize); void outputCompilationResults(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 91c1b200..10b78bdc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,24 +5,34 @@ 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") -file(GLOB liblll_sources "liblll/*.cpp") -file(GLOB liblll_headers "liblll/*.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") 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} ) -target_link_libraries(soltest PRIVATE libsolc solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +target_link_libraries(soltest PRIVATE libsolc yul solidity evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) + +if (LLL) + target_link_libraries(soltest PRIVATE lll) + target_compile_definitions(soltest PRIVATE HAVE_LLL=1) +endif() if (NOT Boost_USE_STATIC_LIBS) target_compile_definitions(soltest PUBLIC -DBOOST_TEST_DYN_LINK) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index d6de95a3..86c1bcca 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -25,7 +25,7 @@ #include <test/Options.h> #include <test/RPCSession.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libdevcore/FixedHash.h> #include <libdevcore/Keccak256.h> diff --git a/test/Options.cpp b/test/Options.cpp index da08eb37..9f0d6752 100644 --- a/test/Options.cpp +++ b/test/Options.cpp @@ -23,8 +23,8 @@ #include <test/Common.h> -#include <libsolidity/interface/EVMVersion.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/EVMVersion.h> +#include <liblangutil/Exceptions.h> #include <boost/test/framework.hpp> #include <boost/filesystem.hpp> diff --git a/test/Options.h b/test/Options.h index 0e8a51da..af6f06ee 100644 --- a/test/Options.h +++ b/test/Options.h @@ -19,7 +19,7 @@ #pragma once -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <boost/test/unit_test.hpp> #include <boost/filesystem.hpp> diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 9ac24972..0aae21a7 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -23,7 +23,7 @@ #include <test/Options.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libdevcore/CommonData.h> diff --git a/test/libsolidity/TestCase.cpp b/test/TestCase.cpp index 17972269..e9e2c9f2 100644 --- a/test/libsolidity/TestCase.cpp +++ b/test/TestCase.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see <http://www.gnu.org/licenses/>. */ -#include <test/libsolidity/TestCase.h> +#include <test/TestCase.h> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/predicate.hpp> diff --git a/test/libsolidity/TestCase.h b/test/TestCase.h index 3c05ae4e..3c05ae4e 100644 --- a/test/libsolidity/TestCase.h +++ b/test/TestCase.h diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 34eeaec9..7cb0c143 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -38,6 +38,7 @@ #include <test/Options.h> #include <test/libsolidity/ASTJSONTest.h> #include <test/libsolidity/SyntaxTest.h> +#include <test/libsolidity/SMTCheckerJSONTest.h> #include <test/libyul/YulOptimizerTest.h> #include <boost/algorithm/string.hpp> @@ -143,15 +144,24 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) master, dev::test::Options::get().testPath / "libyul", "yulOptimizerTests", - dev::yul::test::YulOptimizerTest::create + yul::test::YulOptimizerTest::create ) > 0, "no Yul Optimizer tests found"); if (!dev::test::Options::get().disableSMT) + { solAssert(registerTests( master, dev::test::Options::get().testPath / "libsolidity", "smtCheckerTests", SyntaxTest::create ) > 0, "no SMT checker tests found"); + + solAssert(registerTests( + master, + dev::test::Options::get().testPath / "libsolidity", + "smtCheckerTestsJSON", + SMTCheckerTest::create + ) > 0, "no SMT checker JSON tests found"); + } if (dev::test::Options::get().disableIPC) { for (auto suite: { @@ -160,9 +170,11 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) "SolidityAuctionRegistrar", "SolidityFixedFeeRegistrar", "SolidityWallet", +#if HAVE_LLL "LLLERC20", "LLLENS", "LLLEndToEndTest", +#endif "GasMeterTests", "SolidityEndToEndTest", "SolidityOptimizer" diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index b0b545f3..fdc9fbe3 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -124,7 +124,7 @@ test_solc_file_input_failures() { set -e sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" - sed -i -e 's/ \?Consider adding "pragma .*$//' "$stderr_path" + sed -i -e 's/ Consider adding "pragma .*$//' "$stderr_path" if [[ $exitCode -eq 0 ]]; then printError "Incorrect exit code. Expected failure (non-zero) but got success (0)." @@ -262,10 +262,45 @@ SOLTMPDIR=$(mktemp -d) ) rm -rf "$SOLTMPDIR" -printTask "Testing assemble, yul, strict-assembly..." -echo '{}' | "$SOLC" - --assemble &>/dev/null -echo '{}' | "$SOLC" - --yul &>/dev/null -echo '{}' | "$SOLC" - --strict-assembly &>/dev/null +test_solc_assembly_output() { + local input="${1}" + local expected="${2}" + local solc_args="${3}" + + local expected_object="object \"object\" { code "${expected}" }" + + output=$(echo "${input}" | "$SOLC" - ${solc_args} 2>/dev/null) + empty=$(echo $output | sed -ne '/'"${expected_object}"'/p') + if [ -z "$empty" ] + then + printError "Incorrect assembly output. Expected: " + echo -e ${expected} + printError "with arguments ${solc_args}, but got:" + echo "${output}" + exit 1 + fi +} + +printTask "Testing assemble, yul, strict-assembly and optimize..." +( + echo '{}' | "$SOLC" - --assemble &>/dev/null + echo '{}' | "$SOLC" - --yul &>/dev/null + echo '{}' | "$SOLC" - --strict-assembly &>/dev/null + + # Test options above in conjunction with --optimize. + # Using both, --assemble and --optimize should fail. + ! echo '{}' | "$SOLC" - --assemble --optimize &>/dev/null + + # Test yul and strict assembly output + # Non-empty code results in non-empty binary representation with optimizations turned off, + # while it results in empty binary representation with optimizations turned on. + test_solc_assembly_output "{ let x:u256 := 0:u256 }" "{ let x:u256 := 0:u256 }" "--yul" + test_solc_assembly_output "{ let x:u256 := 0:u256 }" "{ }" "--yul --optimize" + + test_solc_assembly_output "{ let x := 0 }" "{ let x := 0 }" "--strict-assembly" + test_solc_assembly_output "{ let x := 0 }" "{ }" "--strict-assembly --optimize" +) + printTask "Testing standard input..." SOLTMPDIR=$(mktemp -d) diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index eb274c09..6ce61a6e 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -112,7 +112,7 @@ contract GlobalRegistrar is Registrar, AuctionSystem { uint constant c_renewalInterval = 365 days; uint constant c_freeBytes = 12; - function Registrar() public { + constructor() public { // TODO: Populate with hall-of-fame. } diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 1c041596..bece2be4 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -30,6 +30,7 @@ #include <memory> using namespace std; +using namespace langutil; using namespace dev::eth; namespace dev @@ -54,10 +55,12 @@ BOOST_AUTO_TEST_SUITE(Assembler) BOOST_AUTO_TEST_CASE(all_assembly_items) { Assembly _assembly; - _assembly.setSourceLocation(SourceLocation(1, 3, make_shared<string>("root.asm"))); + auto root_asm = make_shared<CharStream>("", "root.asm"); + _assembly.setSourceLocation(SourceLocation(1, 3, root_asm)); Assembly _subAsm; - _subAsm.setSourceLocation(SourceLocation(6, 8, make_shared<string>("sub.asm"))); + auto sub_asm = make_shared<CharStream>("", "sub.asm"); + _subAsm.setSourceLocation(SourceLocation(6, 8, sub_asm)); _subAsm.append(Instruction::INVALID); shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm); diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index c01e8758..7d102948 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -36,6 +36,7 @@ #include <memory> using namespace std; +using namespace langutil; using namespace dev::eth; namespace dev @@ -52,7 +53,7 @@ namespace // add dummy locations to each item so that we can check that they are not deleted AssemblyItems input = _input; for (AssemblyItem& item: input) - item.setLocation(SourceLocation(1, 3, make_shared<string>(""))); + item.setLocation(SourceLocation(1, 3, nullptr)); return input; } diff --git a/test/libevmasm/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index 764da3cd..ef4103da 100644 --- a/test/libevmasm/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -20,13 +20,11 @@ * Unit tests for the SourceLocation class. */ -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include <test/Options.h> -namespace dev -{ -namespace solidity +namespace langutil { namespace test { @@ -35,16 +33,19 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest) BOOST_AUTO_TEST_CASE(test_fail) { + auto const source = std::make_shared<CharStream>("", "source"); + auto const sourceA = std::make_shared<CharStream>("", "sourceA"); + auto const sourceB = std::make_shared<CharStream>("", "sourceB"); + BOOST_CHECK(SourceLocation() == SourceLocation()); - BOOST_CHECK(SourceLocation(0, 3, std::make_shared<std::string>("sourceA")) != SourceLocation(0, 3, std::make_shared<std::string>("sourceB"))); - BOOST_CHECK(SourceLocation(0, 3, std::make_shared<std::string>("source")) == SourceLocation(0, 3, std::make_shared<std::string>("source"))); - BOOST_CHECK(SourceLocation(3, 7, std::make_shared<std::string>("source")).contains(SourceLocation(4, 6, std::make_shared<std::string>("source")))); - BOOST_CHECK(!SourceLocation(3, 7, std::make_shared<std::string>("sourceA")).contains(SourceLocation(4, 6, std::make_shared<std::string>("sourceB")))); - BOOST_CHECK(SourceLocation(3, 7, std::make_shared<std::string>("sourceA")) < SourceLocation(4, 6, std::make_shared<std::string>("sourceB"))); + BOOST_CHECK(SourceLocation(0, 3, sourceA) != SourceLocation(0, 3, sourceB)); + BOOST_CHECK(SourceLocation(0, 3, source) == SourceLocation(0, 3, source)); + BOOST_CHECK(SourceLocation(3, 7, source).contains(SourceLocation(4, 6, source))); + BOOST_CHECK(!SourceLocation(3, 7, sourceA).contains(SourceLocation(4, 6, sourceB))); + BOOST_CHECK(SourceLocation(3, 7, sourceA) < SourceLocation(4, 6, sourceB)); } BOOST_AUTO_TEST_SUITE_END() } -} } // end namespaces diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index 60aef7b0..aad89b91 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -109,6 +109,19 @@ BOOST_AUTO_TEST_CASE(variables) BOOST_CHECK(callFallback() == encodeArgs(u256(488))); } +BOOST_AUTO_TEST_CASE(with) +{ + char const* sourceCode = R"( + (returnlll + (seq + (set 'x 11) + (with 'y 22 { [0]:(+ (get 'x) (get 'y)) }) + (return 0 32))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == toBigEndian(u256(33))); +} + BOOST_AUTO_TEST_CASE(when) { char const* sourceCode = R"( diff --git a/test/contracts/LLL_ENS.cpp b/test/liblll/LLL_ENS.cpp index cfd6970c..cfd6970c 100644 --- a/test/contracts/LLL_ENS.cpp +++ b/test/liblll/LLL_ENS.cpp diff --git a/test/contracts/LLL_ERC20.cpp b/test/liblll/LLL_ERC20.cpp index 6c6762dd..6c6762dd 100644 --- a/test/contracts/LLL_ERC20.cpp +++ b/test/liblll/LLL_ERC20.cpp diff --git a/test/libsolidity/ABIDecoderTests.cpp b/test/libsolidity/ABIDecoderTests.cpp index 94319985..291ed15a 100644 --- a/test/libsolidity/ABIDecoderTests.cpp +++ b/test/libsolidity/ABIDecoderTests.cpp @@ -22,7 +22,7 @@ #include <string> #include <tuple> #include <boost/test/unit_test.hpp> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <test/libsolidity/SolidityExecutionFramework.h> #include <test/libsolidity/ABITestsCommon.h> diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index d2125cc7..b6e6cedd 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -22,7 +22,7 @@ #include <string> #include <tuple> #include <boost/test/unit_test.hpp> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <test/libsolidity/SolidityExecutionFramework.h> #include <test/libsolidity/ABITestsCommon.h> diff --git a/test/libsolidity/ASTJSONTest.h b/test/libsolidity/ASTJSONTest.h index 6f24bb60..dcdaf221 100644 --- a/test/libsolidity/ASTJSONTest.h +++ b/test/libsolidity/ASTJSONTest.h @@ -18,7 +18,7 @@ #pragma once #include <test/libsolidity/FormattedScope.h> -#include <test/libsolidity/TestCase.h> +#include <test/TestCase.h> #include <iosfwd> #include <string> @@ -39,10 +39,10 @@ public: { return std::unique_ptr<TestCase>(new ASTJSONTest(_filename)); } ASTJSONTest(std::string const& _filename); - virtual bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; + bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; - virtual void printSource(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) const override; - virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; + void printSource(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) const override; + void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; private: std::vector<std::pair<std::string, std::string>> m_sources; std::string m_expectation; diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index ec98026c..bd24115c 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -23,11 +23,11 @@ #include <test/Options.h> #include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <liblangutil/SourceReferenceFormatter.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <libdevcore/Keccak256.h> @@ -35,6 +35,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; using namespace dev::solidity::test; @@ -127,7 +128,7 @@ string AnalysisFramework::formatError(Error const& _error) const return SourceReferenceFormatter::formatExceptionInformation( _error, (_error.type() == Error::Type::Warning) ? "Warning" : "Error", - [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } + [&](std::string const& _sourceName) -> Scanner const& { return m_compiler.scanner(_sourceName); } ); } diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index a904617d..391a21da 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -45,7 +45,7 @@ class AnalysisFramework { protected: - virtual std::pair<SourceUnit const*, ErrorList> + virtual std::pair<SourceUnit const*, langutil::ErrorList> parseAnalyseAndReturnError( std::string const& _source, bool _reportWarnings = false, @@ -56,10 +56,10 @@ protected: SourceUnit const* parseAndAnalyse(std::string const& _source); bool success(std::string const& _source); - ErrorList expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false); + langutil::ErrorList expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false); std::string formatErrors() const; - std::string formatError(Error const& _error) const; + std::string formatError(langutil::Error const& _error) const; static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name); static FunctionTypePointer retrieveFunctionBySignature( @@ -68,7 +68,7 @@ protected: ); // filter out the warnings in m_warningsToFilter or all warnings if _includeWarnings is false - ErrorList filterErrors(ErrorList const& _errorList, bool _includeWarnings) const; + langutil::ErrorList filterErrors(langutil::ErrorList const& _errorList, bool _includeWarnings) const; std::vector<std::string> m_warningsToFilter = {"This is a pre-release compiler version"}; dev::solidity::CompilerStack m_compiler; diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index e815d7d5..aa10147c 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -22,16 +22,16 @@ #include <test/Options.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include <libevmasm/Assembly.h> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <libsolidity/parsing/Parser.h> #include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/codegen/Compiler.h> #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/TypeChecker.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <boost/test/unit_test.hpp> @@ -39,6 +39,7 @@ #include <iostream> using namespace std; +using namespace langutil; using namespace dev::eth; namespace dev @@ -51,13 +52,13 @@ namespace test namespace { -eth::AssemblyItems compileContract(string const& _sourceCode) +eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode) { ErrorList errors; ErrorReporter errorReporter(errors); Parser parser(errorReporter); ASTPointer<SourceUnit> sourceUnit; - BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode)))); + BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(_sourceCode))); BOOST_CHECK(!!sourceUnit); map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; @@ -103,7 +104,7 @@ void printAssemblyLocations(AssemblyItems const& _items) ", " << _loc.end << ", make_shared<string>(\"" << - *_loc.sourceName << + _loc.source->name() << "\"))) +" << endl; }; @@ -134,7 +135,8 @@ void checkAssemblyLocations(AssemblyItems const& _items, vector<SourceLocation> BOOST_CHECK_EQUAL(_items.size(), _locations.size()); for (size_t i = 0; i < min(_items.size(), _locations.size()); ++i) { - if (_items[i].location() != _locations[i]) + if (_items[i].location().start != _locations[i].start || + _items[i].location().end != _locations[i].end) { BOOST_CHECK_MESSAGE(false, "Location mismatch for item " + to_string(i) + ". Found the following locations:"); printAssemblyLocations(_items); @@ -150,29 +152,32 @@ BOOST_AUTO_TEST_SUITE(Assembly) BOOST_AUTO_TEST_CASE(location_test) { - char const* sourceCode = R"( + auto sourceCode = make_shared<CharStream>(R"( contract test { function f() public returns (uint256 a) { return 16; } } - )"; + )", ""); AssemblyItems items = compileContract(sourceCode); bool hasShifts = dev::test::Options::get().evmVersion().hasBitwiseShifting(); + + auto codegenCharStream = make_shared<CharStream>("", "--CODEGEN--"); + vector<SourceLocation> locations = - vector<SourceLocation>(hasShifts ? 23 : 24, SourceLocation(2, 82, make_shared<string>(""))) + - vector<SourceLocation>(2, SourceLocation(20, 79, make_shared<string>(""))) + - vector<SourceLocation>(1, SourceLocation(8, 17, make_shared<string>("--CODEGEN--"))) + - vector<SourceLocation>(3, SourceLocation(5, 7, make_shared<string>("--CODEGEN--"))) + - vector<SourceLocation>(1, SourceLocation(30, 31, make_shared<string>("--CODEGEN--"))) + - vector<SourceLocation>(1, SourceLocation(27, 28, make_shared<string>("--CODEGEN--"))) + - vector<SourceLocation>(1, SourceLocation(20, 32, make_shared<string>("--CODEGEN--"))) + - vector<SourceLocation>(1, SourceLocation(5, 7, make_shared<string>("--CODEGEN--"))) + - vector<SourceLocation>(24, SourceLocation(20, 79, make_shared<string>(""))) + - vector<SourceLocation>(1, SourceLocation(49, 58, make_shared<string>(""))) + - vector<SourceLocation>(1, SourceLocation(72, 74, make_shared<string>(""))) + - vector<SourceLocation>(2, SourceLocation(65, 74, make_shared<string>(""))) + - vector<SourceLocation>(2, SourceLocation(20, 79, make_shared<string>(""))); + vector<SourceLocation>(hasShifts ? 21 : 22, SourceLocation(2, 82, sourceCode)) + + vector<SourceLocation>(2, SourceLocation(20, 79, sourceCode)) + + vector<SourceLocation>(1, SourceLocation(8, 17, codegenCharStream)) + + vector<SourceLocation>(3, SourceLocation(5, 7, codegenCharStream)) + + vector<SourceLocation>(1, SourceLocation(30, 31, codegenCharStream)) + + vector<SourceLocation>(1, SourceLocation(27, 28, codegenCharStream)) + + vector<SourceLocation>(1, SourceLocation(20, 32, codegenCharStream)) + + vector<SourceLocation>(1, SourceLocation(5, 7, codegenCharStream)) + + vector<SourceLocation>(24, SourceLocation(20, 79, sourceCode)) + + vector<SourceLocation>(1, SourceLocation(49, 58, sourceCode)) + + vector<SourceLocation>(1, SourceLocation(72, 74, sourceCode)) + + vector<SourceLocation>(2, SourceLocation(65, 74, sourceCode)) + + vector<SourceLocation>(2, SourceLocation(20, 79, sourceCode)); checkAssemblyLocations(items, locations); } diff --git a/test/libsolidity/ErrorCheck.cpp b/test/libsolidity/ErrorCheck.cpp index fba2c897..e1f48fb2 100644 --- a/test/libsolidity/ErrorCheck.cpp +++ b/test/libsolidity/ErrorCheck.cpp @@ -27,6 +27,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace diff --git a/test/libsolidity/ErrorCheck.h b/test/libsolidity/ErrorCheck.h index 8ad81f85..c3a2f522 100644 --- a/test/libsolidity/ErrorCheck.h +++ b/test/libsolidity/ErrorCheck.h @@ -21,7 +21,7 @@ #pragma once -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <vector> #include <tuple> @@ -30,10 +30,10 @@ namespace dev { namespace solidity { -bool searchErrorMessage(Error const& _err, std::string const& _substr); +bool searchErrorMessage(langutil::Error const& _err, std::string const& _substr); /// Checks that all provided errors are of the given type and have a given substring in their /// description. /// If the expectations are not met, returns a nonempty description, otherwise an empty string. -std::string searchErrors(ErrorList const& _errors, std::vector<std::pair<Error::Type, std::string>> const& _expectations); +std::string searchErrors(langutil::ErrorList const& _errors, std::vector<std::pair<langutil::Error::Type, std::string>> const& _expectations); } } diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 4887dd5b..601948bc 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -26,9 +26,10 @@ #include <libevmasm/PathGasMeter.h> #include <libsolidity/ast/AST.h> #include <libsolidity/interface/GasEstimator.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <liblangutil/SourceReferenceFormatter.h> using namespace std; +using namespace langutil; using namespace dev::eth; using namespace dev::solidity; using namespace dev::test; diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index dc33d577..abd659b6 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -23,7 +23,7 @@ #include <test/libsolidity/ErrorCheck.h> #include <test/Options.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/interface/CompilerStack.h> #include <boost/test/unit_test.hpp> diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index a9ce6e49..11d4c59f 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -23,8 +23,8 @@ #include <test/Options.h> #include <libsolidity/interface/AssemblyStack.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/ast/AST.h> #include <test/libsolidity/ErrorCheck.h> #include <libevmasm/Assembly.h> @@ -36,6 +36,7 @@ #include <memory> using namespace std; +using namespace langutil; namespace dev { @@ -123,7 +124,8 @@ void parsePrintCompare(string const& _source, bool _canWarn = false) BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors())); else BOOST_REQUIRE(stack.errors().empty()); - BOOST_CHECK_EQUAL(stack.print(), _source); + string expectation = "object \"object\" {\n code " + boost::replace_all_copy(_source, "\n", "\n ") + "\n}\n"; + BOOST_CHECK_EQUAL(stack.print(), expectation); } } @@ -566,12 +568,14 @@ BOOST_AUTO_TEST_CASE(print_string_literals) BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; - string parsed = "{\n let x := \"\\xe1\\xae\\xac\"\n}"; + string parsed = "object \"object\" {\n code {\n let x := \"\\xe1\\xae\\xac\"\n }\n}\n"; AssemblyStack stack(dev::test::Options::get().evmVersion()); BOOST_REQUIRE(stack.parseAndAnalyze("", source)); BOOST_REQUIRE(stack.errors().empty()); BOOST_CHECK_EQUAL(stack.print(), parsed); - parsePrintCompare(parsed); + + string parsedInner = "{\n let x := \"\\xe1\\xae\\xac\"\n}"; + parsePrintCompare(parsedInner); } BOOST_AUTO_TEST_CASE(print_if) diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 195004cb..a49618bd 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -25,6 +25,7 @@ #include <string> using namespace std; +using namespace langutil; namespace dev { @@ -55,599 +56,6 @@ protected: BOOST_FIXTURE_TEST_SUITE(SMTChecker, SMTCheckerFramework) -BOOST_AUTO_TEST_CASE(smoke_test) -{ - string text = R"( - contract C { } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(simple_overflow) -{ - string text = R"( - contract C { - function f(uint a, uint b) public pure returns (uint) { return a + b; } - } - )"; - CHECK_WARNING(text, "Overflow (resulting value larger than"); -} - -BOOST_AUTO_TEST_CASE(warn_on_typecast) -{ - string text = R"( - contract C { - function f() public pure returns (uint) { - return uint8(1); - } - } - )"; - CHECK_WARNING(text, "Assertion checker does not yet implement this expression."); -} - -BOOST_AUTO_TEST_CASE(warn_on_struct) -{ - string text = R"( - pragma experimental ABIEncoderV2; - contract C { - struct A { uint a; uint b; } - function f() public pure returns (A memory) { - return A({ a: 1, b: 2 }); - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ - "Experimental feature", - "Assertion checker does not yet implement this expression.", - "Assertion checker does not yet support the type of this variable." - })); -} - -BOOST_AUTO_TEST_CASE(simple_assert) -{ - string text = R"( - contract C { - function f(uint a) public pure { assert(a == 2); } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); -} - -BOOST_AUTO_TEST_CASE(simple_assert_with_require) -{ - string text = R"( - contract C { - function f(uint a) public pure { require(a < 10); assert(a < 20); } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(assignment_in_declaration) -{ - string text = R"( - contract C { - function f() public pure { uint a = 2; assert(a == 2); } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(branches_merge_variables) -{ - // Branch does not touch variable a - string text = R"( - contract C { - function f(uint x) public pure { - uint a = 3; - if (x > 10) { - } - assert(a == 3); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - // Positive branch touches variable a, but assertion should still hold. - text = R"( - contract C { - function f(uint x) public pure { - uint a = 3; - if (x > 10) { - a = 3; - } - assert(a == 3); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - // Negative branch touches variable a, but assertion should still hold. - text = R"( - contract C { - function f(uint x) public pure { - uint a = 3; - if (x > 10) { - } else { - a = 3; - } - assert(a == 3); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - // Variable is not merged, if it is only read. - text = R"( - contract C { - function f(uint x) public pure { - uint a = 3; - if (x > 10) { - assert(a == 3); - } else { - assert(a == 3); - } - assert(a == 3); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - // Variable is reset in both branches - text = R"( - contract C { - function f(uint x) public pure { - uint a = 2; - if (x > 10) { - a = 3; - } else { - a = 3; - } - assert(a == 3); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - // Variable is reset in both branches - text = R"( - contract C { - function f(uint x) public pure { - uint a = 2; - if (x > 10) { - a = 3; - } else { - a = 4; - } - assert(a >= 3); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(branches_assert_condition) -{ - string text = R"( - contract C { - function f(uint x) public pure { - if (x > 10) { - assert(x > 9); - } - else - { - assert(x < 11); - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(uint x) public pure { - if (x > 10) { - assert(x > 9); - } - else if (x > 2) - { - assert(x <= 10 && x > 2); - } - else - { - assert(0 <= x && x <= 2); - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(ways_to_merge_variables) -{ - string text = R"( - contract C { - function f(uint x) public pure { - uint a = 3; - if (x > 10) { - a++; - } - assert(a == 3); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - text = R"( - contract C { - function f(uint x) public pure { - uint a = 3; - if (x > 10) { - ++a; - } - assert(a == 3); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - text = R"( - contract C { - function f(uint x) public pure { - uint a = 3; - if (x > 10) { - a = 5; - } - assert(a == 3); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); -} - -BOOST_AUTO_TEST_CASE(bool_simple) -{ - string text = R"( - contract C { - function f(bool x) public pure { - assert(x); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - text = R"( - contract C { - function f(bool x, bool y) public pure { - assert(x == y); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - text = R"( - contract C { - function f(bool x, bool y) public pure { - bool z = x || y; - assert(!(x && y) || z); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(bool x) public pure { - if (x) { - assert(x); - } else { - assert(!x); - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(bool x) public pure { - bool y = x; - assert(x == y); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(bool x) public pure { - require(x); - bool y; - y = false; - assert(x || y); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(bool_int_mixed) -{ - string text = R"( - contract C { - function f(bool x) public pure { - uint a; - if (x) - a = 1; - assert(!x || a > 0); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(bool x, uint a) public pure { - require(!x || a > 0); - uint b = a; - assert(!x || b > 0); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(bool x, bool y) public pure { - uint a; - if (x) { - if (y) { - a = 0; - } else { - a = 1; - } - } else { - if (y) { - a = 1; - } else { - a = 0; - } - } - bool xor_x_y = (x && !y) || (!x && y); - assert(!xor_x_y || a > 0); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(storage_value_vars) -{ - string text = R"( - contract C - { - address a; - bool b; - uint c; - function f(uint x) public { - if (x == 0) - { - a = 0x0000000000000000000000000000000000000100; - b = true; - } - else - { - a = 0x0000000000000000000000000000000000000200; - b = false; - } - assert(a > 0x0000000000000000000000000000000000000000 && b); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - text = R"( - contract C - { - address a; - bool b; - uint c; - function f() public view { - assert(c > 0); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - text = R"( - contract C - { - function f(uint x) public { - if (x == 0) - { - a = 0x0000000000000000000000000000000000000100; - b = true; - } - else - { - a = 0x0000000000000000000000000000000000000200; - b = false; - } - assert(b == (a < 0x0000000000000000000000000000000000000200)); - } - - function g() public view { - require(a < 0x0000000000000000000000000000000000000100); - assert(c >= 0); - } - address a; - bool b; - uint c; - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C - { - function f() public view { - assert(c > 0); - } - uint c; - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - -} - -BOOST_AUTO_TEST_CASE(while_loop_simple) -{ - // Check that variables are cleared - string text = R"( - contract C { - function f(uint x) public pure { - x = 2; - while (x > 1) { - x = 2; - } - assert(x == 2); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - // Check that condition is assumed. - text = R"( - contract C { - function f(uint x) public pure { - while (x == 2) { - assert(x == 2); - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - // Check that condition is not assumed after the body anymore - text = R"( - contract C { - function f(uint x) public pure { - while (x == 2) { - } - assert(x == 2); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - // Check that negation of condition is not assumed after the body anymore - text = R"( - contract C { - function f(uint x) public pure { - while (x == 2) { - } - assert(x != 2); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - // Check that side-effects of condition are taken into account - text = R"( - contract C { - function f(uint x, uint y) public pure { - x = 7; - while ((x = y) > 0) { - } - assert(x == 7); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); -} - -BOOST_AUTO_TEST_CASE(constant_condition) -{ - string text = R"( - contract C { - function f(uint x) public pure { - if (x >= 0) { revert(); } - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ - "Condition is always true", - "Assertion checker does not yet implement this type of function call" - })); - text = R"( - contract C { - function f(uint x) public pure { - if (x >= 10) { if (x < 10) { revert(); } } - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ - "Condition is always false", - "Assertion checker does not yet implement this type of function call" - })); - // a plain literal constant is fine - text = R"( - contract C { - function f(uint) public pure { - if (true) { revert(); } - } - } - )"; - CHECK_WARNING(text, "Assertion checker does not yet implement this type of function call"); -} - - -BOOST_AUTO_TEST_CASE(for_loop) -{ - string text = R"( - contract C { - function f(uint x) public pure { - require(x == 2); - for (;;) {} - assert(x == 2); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(uint x) public pure { - for (; x == 2; ) { - assert(x == 2); - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(uint x) public pure { - for (uint y = 2; x < 10; ) { - assert(y == 2); - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(uint x) public pure { - for (uint y = 2; x < 10; y = 3) { - assert(y == 2); - } - } - } - )"; - CHECK_WARNING(text, "Assertion violation"); - text = R"( - contract C { - function f(uint x) public pure { - uint y; - for (y = 2; x < 10; ) { - y = 3; - } - assert(y == 3); - } - } - )"; - CHECK_WARNING(text, "Assertion violation"); - text = R"( - contract C { - function f(uint x) public pure { - uint y; - for (y = 2; x < 10; ) { - y = 3; - } - assert(y == 2); - } - } - )"; - CHECK_WARNING(text, "Assertion violation"); -} - BOOST_AUTO_TEST_CASE(division) { string text = R"( diff --git a/test/libsolidity/SMTCheckerJSONTest.cpp b/test/libsolidity/SMTCheckerJSONTest.cpp new file mode 100644 index 00000000..e9204cc4 --- /dev/null +++ b/test/libsolidity/SMTCheckerJSONTest.cpp @@ -0,0 +1,153 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <test/libsolidity/SMTCheckerJSONTest.h> +#include <test/Options.h> +#include <libsolidity/interface/StandardCompiler.h> +#include <libdevcore/JSON.h> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/join.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/throw_exception.hpp> +#include <fstream> +#include <memory> +#include <stdexcept> +#include <sstream> + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace dev::solidity::test::formatting; +using namespace std; +using namespace boost::unit_test; + +SMTCheckerTest::SMTCheckerTest(string const& _filename) +: SyntaxTest(_filename) +{ + if (!boost::algorithm::ends_with(_filename, ".sol")) + BOOST_THROW_EXCEPTION(runtime_error("Invalid test contract file name: \"" + _filename + "\".")); + + string jsonFilename = _filename.substr(0, _filename.size() - 4) + ".json"; + if ( + !jsonParseFile(jsonFilename, m_smtResponses) || + !m_smtResponses.isObject() + ) + BOOST_THROW_EXCEPTION(runtime_error("Invalid JSON file.")); +} + +bool SMTCheckerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + StandardCompiler compiler; + + // Run the compiler and retrieve the smtlib2queries (1st run) + string versionPragma = "pragma solidity >=0.0;\n"; + Json::Value input = buildJson(versionPragma); + Json::Value result = compiler.compile(input); + + // This is the list of query hashes requested by the 1st run + vector<string> outHashes = hashesFromJson(result, "auxiliaryInputRequested", "smtlib2queries"); + + // This is the list of responses provided in the test + string auxInput("auxiliaryInput"); + if (!m_smtResponses.isMember(auxInput)) + BOOST_THROW_EXCEPTION(runtime_error("JSON file does not contain field \"auxiliaryInput\".")); + + vector<string> inHashes = hashesFromJson(m_smtResponses, auxInput, "smtlib2responses"); + + // Ensure that the provided list matches the requested one + if (outHashes != inHashes) + BOOST_THROW_EXCEPTION(runtime_error( + "SMT query hashes differ: " + + boost::algorithm::join(outHashes, ", ") + + " x " + + boost::algorithm::join(inHashes, ", ") + )); + + // Rerun the compiler with the provided hashed (2nd run) + input[auxInput] = m_smtResponses[auxInput]; + Json::Value endResult = compiler.compile(input); + + if (endResult.isMember("errors") && endResult["errors"].isArray()) + { + Json::Value const& errors = endResult["errors"]; + for (auto const& error: errors) + { + if ( + !error.isMember("type") || + !error["type"].isString() + ) + BOOST_THROW_EXCEPTION(runtime_error("Error must have a type.")); + if ( + !error.isMember("message") || + !error["message"].isString() + ) + BOOST_THROW_EXCEPTION(runtime_error("Error must have a message.")); + if (!error.isMember("sourceLocation")) + continue; + Json::Value const& location = error["sourceLocation"]; + if ( + !location.isMember("start") || + !location["start"].isInt() || + !location.isMember("end") || + !location["end"].isInt() + ) + BOOST_THROW_EXCEPTION(runtime_error("Error must have a SourceLocation with start and end.")); + int start = location["start"].asInt(); + int end = location["end"].asInt(); + if (start >= static_cast<int>(versionPragma.size())) + start -= versionPragma.size(); + if (end >= static_cast<int>(versionPragma.size())) + end -= versionPragma.size(); + m_errorList.emplace_back(SyntaxTestError{ + error["type"].asString(), + error["message"].asString(), + start, + end + }); + } + } + + return printExpectationAndError(_stream, _linePrefix, _formatted); +} + +vector<string> SMTCheckerTest::hashesFromJson(Json::Value const& _jsonObj, string const& _auxInput, string const& _smtlib) +{ + vector<string> hashes; + Json::Value const& auxInputs = _jsonObj[_auxInput]; + if (!!auxInputs) + { + Json::Value const& smtlib = auxInputs[_smtlib]; + if (!!smtlib) + for (auto const& hashString: smtlib.getMemberNames()) + hashes.push_back(hashString); + } + return hashes; +} + +Json::Value SMTCheckerTest::buildJson(string const& _extra) +{ + string language = "\"language\": \"Solidity\""; + string sourceName = "\"A\""; + string sourceContent = "\"" + _extra + m_source + "\""; + string sourceObj = "{ \"content\": " + sourceContent + "}"; + string sources = " \"sources\": { " + sourceName + ": " + sourceObj + "}"; + string input = "{" + language + ", " + sources + "}"; + Json::Value source; + if (!jsonParse(input, source)) + BOOST_THROW_EXCEPTION(runtime_error("Could not build JSON from string.")); + return source; +} diff --git a/test/libsolidity/SMTCheckerJSONTest.h b/test/libsolidity/SMTCheckerJSONTest.h new file mode 100644 index 00000000..cf41acac --- /dev/null +++ b/test/libsolidity/SMTCheckerJSONTest.h @@ -0,0 +1,53 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <test/libsolidity/SyntaxTest.h> + +#include <libdevcore/JSON.h> + +#include <string> + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +class SMTCheckerTest: public SyntaxTest +{ +public: + static std::unique_ptr<TestCase> create(std::string const& _filename) + { + return std::unique_ptr<TestCase>(new SMTCheckerTest(_filename)); + } + SMTCheckerTest(std::string const& _filename); + + bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; + +private: + std::vector<std::string> hashesFromJson(Json::Value const& _jsonObj, std::string const& _auxInput, std::string const& _smtlib); + Json::Value buildJson(std::string const& _extra); + + Json::Value m_smtResponses; +}; + +} +} +} diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 43951f73..2980acd1 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -23,11 +23,12 @@ #include <string> #include <vector> #include <tuple> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <libsolidity/analysis/SemVerHandler.h> #include <test/Options.h> using namespace std; +using namespace langutil; namespace dev { @@ -40,7 +41,7 @@ BOOST_AUTO_TEST_SUITE(SemVerMatcher) SemVerMatchExpression parseExpression(string const& _input) { - Scanner scanner{CharStream(_input)}; + Scanner scanner{CharStream(_input, "")}; vector<string> literals; vector<Token> tokens; while (scanner.currentToken() != Token::EOS) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 87646737..c6135a72 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -25,8 +25,8 @@ #include <test/Options.h> -#include <libsolidity/interface/Exceptions.h> -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/Exceptions.h> +#include <liblangutil/EVMVersion.h> #include <libevmasm/Assembly.h> @@ -8207,6 +8207,58 @@ BOOST_AUTO_TEST_CASE(inherited_function) { ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(1))); } +BOOST_AUTO_TEST_CASE(inherited_function_calldata_memory) { + char const* sourceCode = R"( + contract A { function f(uint[] calldata a) external returns (uint) { return a[0]; } } + contract B is A { + function f(uint[] memory a) public returns (uint) { return a[1]; } + function g() public returns (uint) { + uint[] memory m = new uint[](2); + m[0] = 42; + m[1] = 23; + return A(this).f(m); + } + } + )"; + + compileAndRun(sourceCode, 0, "B"); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(23))); +} + +BOOST_AUTO_TEST_CASE(inherited_function_calldata_memory_interface) { + char const* sourceCode = R"( + interface I { function f(uint[] calldata a) external returns (uint); } + contract A is I { function f(uint[] memory a) public returns (uint) { return 42; } } + contract B { + function f(uint[] memory a) public returns (uint) { return a[1]; } + function g() public returns (uint) { + I i = I(new A()); + return i.f(new uint[](2)); + } + } + )"; + + compileAndRun(sourceCode, 0, "B"); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(inherited_function_calldata_calldata_interface) { + char const* sourceCode = R"( + interface I { function f(uint[] calldata a) external returns (uint); } + contract A is I { function f(uint[] calldata a) external returns (uint) { return 42; } } + contract B { + function f(uint[] memory a) public returns (uint) { return a[1]; } + function g() public returns (uint) { + I i = I(new A()); + return i.f(new uint[](2)); + } + } + )"; + + compileAndRun(sourceCode, 0, "B"); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(42))); +} + BOOST_AUTO_TEST_CASE(inherited_function_from_a_library) { char const* sourceCode = R"( library A { function f() internal returns (uint) { return 1; } } @@ -8658,6 +8710,90 @@ BOOST_AUTO_TEST_CASE(mapping_returns_in_library_named) ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(17))); } +BOOST_AUTO_TEST_CASE(using_library_mappings_public) +{ + char const* sourceCode = R"( + library Lib { + function set(mapping(uint => uint) storage m, uint key, uint value) public + { + m[key] = value; + } + } + contract Test { + mapping(uint => uint) m1; + mapping(uint => uint) m2; + function f() public returns (uint, uint, uint, uint, uint, uint) + { + Lib.set(m1, 0, 1); + Lib.set(m1, 2, 42); + Lib.set(m2, 0, 23); + Lib.set(m2, 2, 99); + return (m1[0], m1[1], m1[2], m2[0], m2[1], m2[2]); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99))); +} + +BOOST_AUTO_TEST_CASE(using_library_mappings_external) +{ + char const* libSourceCode = R"( + library Lib { + function set(mapping(uint => uint) storage m, uint key, uint value) external + { + m[key] = value * 2; + } + } + )"; + char const* sourceCode = R"( + library Lib { + function set(mapping(uint => uint) storage m, uint key, uint value) external; + } + contract Test { + mapping(uint => uint) m1; + mapping(uint => uint) m2; + function f() public returns (uint, uint, uint, uint, uint, uint) + { + Lib.set(m1, 0, 1); + Lib.set(m1, 2, 42); + Lib.set(m2, 0, 23); + Lib.set(m2, 2, 99); + return (m1[0], m1[1], m1[2], m2[0], m2[1], m2[2]); + } + } + )"; + compileAndRun(libSourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2), u256(0), u256(84), u256(46), u256(0), u256(198))); +} + +BOOST_AUTO_TEST_CASE(using_library_mappings_return) +{ + char const* sourceCode = R"( + library Lib { + function choose(mapping(uint => mapping(uint => uint)) storage m, uint key) external returns (mapping(uint => uint) storage) { + return m[key]; + } + } + contract Test { + mapping(uint => mapping(uint => uint)) m; + function f() public returns (uint, uint, uint, uint, uint, uint) + { + Lib.choose(m, 0)[0] = 1; + Lib.choose(m, 0)[2] = 42; + Lib.choose(m, 1)[0] = 23; + Lib.choose(m, 1)[2] = 99; + return (m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2]); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99))); +} + BOOST_AUTO_TEST_CASE(using_library_structs) { char const* sourceCode = R"( @@ -9388,6 +9524,25 @@ BOOST_AUTO_TEST_CASE(using_for_by_name) ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); } +BOOST_AUTO_TEST_CASE(bound_function_in_function) +{ + char const* sourceCode = R"( + library L { + function g(function() internal returns (uint) _t) internal returns (uint) { return _t(); } + } + contract C { + using L for *; + function f() public returns (uint) { + return t.g(); + } + function t() public pure returns (uint) { return 7; } + } + )"; + compileAndRun(sourceCode, 0, "L"); + compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); +} + BOOST_AUTO_TEST_CASE(bound_function_in_var) { char const* sourceCode = R"( @@ -14043,6 +14198,41 @@ BOOST_AUTO_TEST_CASE(flipping_sign_tests) ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(external_public_override) +{ + char const* sourceCode = R"( + contract A { + function f() external returns (uint) { return 1; } + } + contract B is A { + function f() public returns (uint) { return 2; } + function g() public returns (uint) { return f(); } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(2)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); +} + +BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables) +{ + char const* sourceCode = R"( + contract C { + function () internal returns (uint) x; + function set() public { + C.x = g; + } + function g() public pure returns (uint) { return 2; } + function h() public returns (uint) { return C.x(); } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); + ABI_CHECK(callContractFunction("h()"), encodeArgs()); + ABI_CHECK(callContractFunction("set()"), encodeArgs()); + ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 12687dd1..cedbf51a 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -27,8 +27,8 @@ #include <test/ExecutionFramework.h> #include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/interface/Exceptions.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <liblangutil/Exceptions.h> +#include <liblangutil/SourceReferenceFormatter.h> namespace dev { @@ -72,13 +72,13 @@ public: m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); if (!m_compiler.compile()) { - auto scannerFromSourceName = [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); }; - SourceReferenceFormatter formatter(std::cerr, scannerFromSourceName); + auto scannerFromSourceName = [&](std::string const& _sourceName) -> langutil::Scanner const& { return m_compiler.scanner(_sourceName); }; + langutil::SourceReferenceFormatter formatter(std::cerr, scannerFromSourceName); for (auto const& error: m_compiler.errors()) formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error" + (error->type() == langutil::Error::Type::Warning) ? "Warning" : "Error" ); BOOST_ERROR("Compiling contract failed"); } diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 309cbf0b..8bce26c1 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -22,17 +22,18 @@ #include <string> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <libsolidity/parsing/Parser.h> #include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/codegen/CompilerContext.h> #include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/TypeChecker.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <test/Options.h> using namespace std; +using namespace langutil; namespace dev { @@ -100,7 +101,7 @@ bytes compileFirstExpression( { ErrorList errors; ErrorReporter errorReporter(errors); - sourceUnit = Parser(errorReporter).parse(make_shared<Scanner>(CharStream(_sourceCode))); + sourceUnit = Parser(errorReporter).parse(make_shared<Scanner>(CharStream(_sourceCode, ""))); if (!sourceUnit) return bytes(); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 5ec010c7..75726027 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -33,6 +33,7 @@ #include <string> using namespace std; +using namespace langutil; namespace dev { diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index d77050cb..d930f697 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -24,9 +24,11 @@ #include <string> #include <libdevcore/JSON.h> #include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/Exceptions.h> +using namespace langutil; + namespace dev { namespace solidity diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 5432e9b5..d86d3d39 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -22,13 +22,14 @@ #include <string> #include <memory> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <libsolidity/parsing/Parser.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <test/Options.h> #include <test/libsolidity/ErrorCheck.h> using namespace std; +using namespace langutil; namespace dev { @@ -42,7 +43,7 @@ namespace ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors) { ErrorReporter errorReporter(_errors); - ASTPointer<SourceUnit> sourceUnit = Parser(errorReporter).parse(std::make_shared<Scanner>(CharStream(_source))); + ASTPointer<SourceUnit> sourceUnit = Parser(errorReporter).parse(std::make_shared<Scanner>(CharStream(_source, ""))); if (!sourceUnit) return ASTPointer<ContractDefinition>(); for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 6965d886..5ce4eda2 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -20,10 +20,11 @@ * Unit tests for the solidity scanner. */ -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <boost/test/unit_test.hpp> using namespace std; +using namespace langutil; namespace dev { @@ -36,13 +37,13 @@ BOOST_AUTO_TEST_SUITE(SolidityScanner) BOOST_AUTO_TEST_CASE(test_empty) { - Scanner scanner(CharStream("")); + Scanner scanner(CharStream{}); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); } BOOST_AUTO_TEST_CASE(smoke_test) { - Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1")); + Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Break); BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); @@ -60,7 +61,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_AUTO_TEST_CASE(string_escapes) { - Scanner scanner(CharStream(" { \"a\\x61\"")); + Scanner scanner(CharStream(" { \"a\\x61\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa"); @@ -68,7 +69,7 @@ BOOST_AUTO_TEST_CASE(string_escapes) BOOST_AUTO_TEST_CASE(string_escapes_all) { - Scanner scanner(CharStream(" { \"a\\x61\\b\\f\\n\\r\\t\\v\"")); + Scanner scanner(CharStream(" { \"a\\x61\\b\\f\\n\\r\\t\\v\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa\b\f\n\r\t\v"); @@ -76,7 +77,7 @@ BOOST_AUTO_TEST_CASE(string_escapes_all) BOOST_AUTO_TEST_CASE(string_escapes_with_zero) { - Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"")); + Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("aa\0abc", 6)); @@ -84,9 +85,10 @@ BOOST_AUTO_TEST_CASE(string_escapes_with_zero) BOOST_AUTO_TEST_CASE(string_escape_illegal) { - Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)")); + Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); BOOST_CHECK_EQUAL(scanner.currentLiteral(), ""); // TODO recovery from illegal tokens should be improved BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); @@ -97,7 +99,7 @@ BOOST_AUTO_TEST_CASE(string_escape_illegal) BOOST_AUTO_TEST_CASE(hex_numbers) { - Scanner scanner(CharStream("var x = 0x765432536763762734623472346;")); + Scanner scanner(CharStream("var x = 0x765432536763762734623472346;", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); @@ -105,34 +107,34 @@ BOOST_AUTO_TEST_CASE(hex_numbers) BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x765432536763762734623472346"); BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("0x1234"), ""); + scanner.reset(CharStream("0x1234", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x1234"); - scanner.reset(CharStream("0X1234"), ""); + scanner.reset(CharStream("0X1234", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); } BOOST_AUTO_TEST_CASE(octal_numbers) { - Scanner scanner(CharStream("07")); + Scanner scanner(CharStream("07", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); - scanner.reset(CharStream("007"), ""); + scanner.reset(CharStream("007", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); - scanner.reset(CharStream("-07"), ""); + scanner.reset(CharStream("-07", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Sub); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); - scanner.reset(CharStream("-.07"), ""); + scanner.reset(CharStream("-.07", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Sub); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); - scanner.reset(CharStream("0"), ""); + scanner.reset(CharStream("0", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); - scanner.reset(CharStream("0.1"), ""); + scanner.reset(CharStream("0.1", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); } BOOST_AUTO_TEST_CASE(scientific_notation) { - Scanner scanner(CharStream("var x = 2e10;")); + Scanner scanner(CharStream("var x = 2e10;", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); @@ -144,19 +146,19 @@ BOOST_AUTO_TEST_CASE(scientific_notation) BOOST_AUTO_TEST_CASE(trailing_dot) { - Scanner scanner(CharStream("2.5")); + Scanner scanner(CharStream("2.5", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("2.5e10"), ""); + scanner.reset(CharStream("2.5e10", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(".5"), ""); + scanner.reset(CharStream(".5", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(".5e10"), ""); + scanner.reset(CharStream(".5e10", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("2."), ""); + scanner.reset(CharStream("2.", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -165,7 +167,7 @@ BOOST_AUTO_TEST_CASE(trailing_dot) BOOST_AUTO_TEST_CASE(leading_underscore_decimal_is_identifier) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("_1.2")); + Scanner scanner(CharStream("_1.2", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -174,11 +176,11 @@ BOOST_AUTO_TEST_CASE(leading_underscore_decimal_is_identifier) BOOST_AUTO_TEST_CASE(leading_underscore_decimal_after_dot_illegal) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("1._2")); + Scanner scanner(CharStream("1._2", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("1._"), ""); + scanner.reset(CharStream("1._", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -186,7 +188,7 @@ BOOST_AUTO_TEST_CASE(leading_underscore_decimal_after_dot_illegal) BOOST_AUTO_TEST_CASE(leading_underscore_exp_are_identifier) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("_1e2")); + Scanner scanner(CharStream("_1e2", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -194,7 +196,7 @@ BOOST_AUTO_TEST_CASE(leading_underscore_exp_are_identifier) BOOST_AUTO_TEST_CASE(leading_underscore_exp_after_e_illegal) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("1e_2")); + Scanner scanner(CharStream("1e_2", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1e_2"); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -202,7 +204,7 @@ BOOST_AUTO_TEST_CASE(leading_underscore_exp_after_e_illegal) BOOST_AUTO_TEST_CASE(leading_underscore_hex_illegal) { - Scanner scanner(CharStream("0x_abc")); + Scanner scanner(CharStream("0x_abc", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -211,7 +213,7 @@ BOOST_AUTO_TEST_CASE(leading_underscore_hex_illegal) BOOST_AUTO_TEST_CASE(fixed_number_invalid_underscore_front) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("12._1234_1234")); + Scanner scanner(CharStream("12._1234_1234", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -219,22 +221,22 @@ BOOST_AUTO_TEST_CASE(fixed_number_invalid_underscore_front) BOOST_AUTO_TEST_CASE(number_literals_with_trailing_underscore_at_eos) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("0x123_")); + Scanner scanner(CharStream("0x123_", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("123_"), ""); + scanner.reset(CharStream("123_", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("12.34_"), ""); + scanner.reset(CharStream("12.34_", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(negative_numbers) { - Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9 + 2e-2;")); + Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9 + 2e-2;", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); @@ -261,7 +263,7 @@ BOOST_AUTO_TEST_CASE(negative_numbers) BOOST_AUTO_TEST_CASE(locations) { - Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); + Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLocation().start, 0); BOOST_CHECK_EQUAL(scanner.currentLocation().end, 19); @@ -284,7 +286,7 @@ BOOST_AUTO_TEST_CASE(locations) BOOST_AUTO_TEST_CASE(ambiguities) { // test scanning of some operators which need look-ahead - Scanner scanner(CharStream("<=" "<" "+ +=a++ =>" "<<" ">>" " >>=" ">>>" ">>>=" " >>>>>=><<=")); + Scanner scanner(CharStream("<=" "<" "+ +=a++ =>" "<<" ">>" " >>=" ">>>" ">>>=" " >>>>>=><<=", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LessThanOrEqual); BOOST_CHECK_EQUAL(scanner.next(), Token::LessThan); BOOST_CHECK_EQUAL(scanner.next(), Token::Add); @@ -306,21 +308,21 @@ BOOST_AUTO_TEST_CASE(ambiguities) BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin) { - Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user")); + Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin) { - Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/")); + Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(documentation_comments_parsed) { - Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user")); + Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -332,7 +334,7 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) { Scanner scanner(CharStream("some other tokens /**\n" "* Send $(value / 1000) chocolates to the user\n" - "*/")); + "*/", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -344,7 +346,7 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars) { Scanner scanner(CharStream("some other tokens /**\n" " Send $(value / 1000) chocolates to the user\n" - "*/")); + "*/", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -356,7 +358,7 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell) { Scanner scanner(CharStream("some other tokens /** \t \r \n" "\t \r * Send $(value / 1000) chocolates to the user\n" - "*/")); + "*/", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -366,28 +368,28 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell) BOOST_AUTO_TEST_CASE(comment_before_eos) { - Scanner scanner(CharStream("//")); + Scanner scanner(CharStream("//", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(documentation_comment_before_eos) { - Scanner scanner(CharStream("///")); + Scanner scanner(CharStream("///", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(empty_multiline_comment) { - Scanner scanner(CharStream("/**/")); + Scanner scanner(CharStream("/**/", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(empty_multiline_documentation_comment_before_eos) { - Scanner scanner(CharStream("/***/")); + Scanner scanner(CharStream("/***/", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } @@ -396,7 +398,7 @@ BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) { Scanner scanner(CharStream("hello_world ///documentation comment \n" "//simple comment \n" - "<<")); + "<<", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "documentation comment "); @@ -404,7 +406,7 @@ BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) BOOST_AUTO_TEST_CASE(ether_subdenominations) { - Scanner scanner(CharStream("wei szabo finney ether")); + Scanner scanner(CharStream("wei szabo finney ether", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubWei); BOOST_CHECK_EQUAL(scanner.next(), Token::SubSzabo); BOOST_CHECK_EQUAL(scanner.next(), Token::SubFinney); @@ -413,7 +415,7 @@ BOOST_AUTO_TEST_CASE(ether_subdenominations) BOOST_AUTO_TEST_CASE(time_subdenominations) { - Scanner scanner(CharStream("seconds minutes hours days weeks years")); + Scanner scanner(CharStream("seconds minutes hours days weeks years", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubSecond); BOOST_CHECK_EQUAL(scanner.next(), Token::SubMinute); BOOST_CHECK_EQUAL(scanner.next(), Token::SubHour); @@ -424,7 +426,7 @@ BOOST_AUTO_TEST_CASE(time_subdenominations) BOOST_AUTO_TEST_CASE(empty_comment) { - Scanner scanner(CharStream("//\ncontract{}")); + Scanner scanner(CharStream("//\ncontract{}", "")); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Contract); BOOST_CHECK_EQUAL(scanner.next(), Token::LBrace); @@ -434,7 +436,7 @@ BOOST_AUTO_TEST_CASE(empty_comment) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape) { - Scanner scanner(CharStream("{ \"\\u00DAnicode\"")); + Scanner scanner(CharStream("{ \"\\u00DAnicode\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xC3\x9Anicode", 8)); @@ -442,7 +444,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7f) { - Scanner scanner(CharStream("{ \"\\u007Fnicode\"")); + Scanner scanner(CharStream("{ \"\\u007Fnicode\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x7Fnicode", 7)); @@ -450,7 +452,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7f) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7ff) { - Scanner scanner(CharStream("{ \"\\u07FFnicode\"")); + Scanner scanner(CharStream("{ \"\\u07FFnicode\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xDF\xBFnicode", 8)); @@ -458,7 +460,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7ff) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_ffff) { - Scanner scanner(CharStream("{ \"\\uFFFFnicode\"")); + Scanner scanner(CharStream("{ \"\\uFFFFnicode\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xEF\xBF\xBFnicode", 9)); @@ -466,7 +468,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_ffff) BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) { - Scanner scanner(CharStream("{ \"\\uFFnicode\"")); + Scanner scanner(CharStream("{ \"\\uFFnicode\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } @@ -475,7 +477,7 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) BOOST_AUTO_TEST_CASE(valid_hex_literal) { - Scanner scanner(CharStream("{ hex\"00112233FF\"")); + Scanner scanner(CharStream("{ hex\"00112233FF\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); @@ -483,30 +485,34 @@ BOOST_AUTO_TEST_CASE(valid_hex_literal) BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) { - Scanner scanner(CharStream("{ hex\"00112233F\"")); + Scanner scanner(CharStream("{ hex\"00112233F\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) { - Scanner scanner(CharStream("{ hex\"00112233FF \"")); + Scanner scanner(CharStream("{ hex\"00112233FF \"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) { - Scanner scanner(CharStream("{ hex\"00112233FF'")); + Scanner scanner(CharStream("{ hex\"00112233FF'", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) { - Scanner scanner(CharStream("{ hex\"hello\"")); + Scanner scanner(CharStream("{ hex\"hello\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); } // COMMENTS @@ -514,7 +520,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) { // This used to parse as "comment", "identifier" - Scanner scanner(CharStream("/** / x")); + Scanner scanner(CharStream("/** / x", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -522,14 +528,14 @@ BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) BOOST_AUTO_TEST_CASE(multiline_doc_comment_at_eos) { // This used to parse as "whitespace" - Scanner scanner(CharStream("/**")); + Scanner scanner(CharStream("/**", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(multiline_comment_at_eos) { - Scanner scanner(CharStream("/*")); + Scanner scanner(CharStream("/*", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -538,7 +544,7 @@ BOOST_AUTO_TEST_CASE(regular_line_break_in_single_line_comment) { for (auto const& nl: {"\r", "\n"}) { - Scanner scanner(CharStream("// abc " + string(nl) + " def ")); + Scanner scanner(CharStream("// abc " + string(nl) + " def ", "")); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -550,7 +556,7 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_comment) { for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) { - Scanner scanner(CharStream("// abc " + string(nl) + " def ")); + Scanner scanner(CharStream("// abc " + string(nl) + " def ", "")); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); for (size_t i = 0; i < string(nl).size() - 1; i++) @@ -565,7 +571,7 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_single_line_doc_comment) { for (auto const& nl: {"\r", "\n"}) { - Scanner scanner(CharStream("/// abc " + string(nl) + " def ")); + Scanner scanner(CharStream("/// abc " + string(nl) + " def ", "")); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -577,7 +583,7 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_doc_comment) { for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) { - Scanner scanner(CharStream("/// abc " + string(nl) + " def ")); + Scanner scanner(CharStream("/// abc " + string(nl) + " def ", "")); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); for (size_t i = 0; i < string(nl).size() - 1; i++) @@ -592,7 +598,7 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_strings) { for (auto const& nl: {"\n", "\r"}) { - Scanner scanner(CharStream("\"abc " + string(nl) + " def\"")); + Scanner scanner(CharStream("\"abc " + string(nl) + " def\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -605,7 +611,7 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_strings) { for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) { - Scanner scanner(CharStream("\"abc " + string(nl) + " def\"")); + Scanner scanner(CharStream("\"abc " + string(nl) + " def\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); for (size_t i = 0; i < string(nl).size(); i++) BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 6f9d4ce8..c839afd4 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -26,6 +26,7 @@ #include <boost/test/unit_test.hpp> using namespace std; +using namespace langutil; namespace dev { diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 91d1681f..c47ea599 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -25,6 +25,7 @@ #include <stdexcept> using namespace dev; +using namespace langutil; using namespace solidity; using namespace dev::solidity::test; using namespace dev::solidity::test::formatting; @@ -91,6 +92,11 @@ bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _fo }); } + return printExpectationAndError(_stream, _linePrefix, _formatted); +} + +bool SyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ if (m_expectations != m_errorList) { string nextIndentLevel = _linePrefix + " "; diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index e9e36aa6..12c14087 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -19,8 +19,8 @@ #include <test/libsolidity/AnalysisFramework.h> #include <test/libsolidity/FormattedScope.h> -#include <test/libsolidity/TestCase.h> -#include <libsolidity/interface/Exceptions.h> +#include <test/TestCase.h> +#include <liblangutil/Exceptions.h> #include <iosfwd> #include <string> @@ -57,17 +57,17 @@ public: { return std::unique_ptr<TestCase>(new SyntaxTest(_filename)); } SyntaxTest(std::string const& _filename); - virtual bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; + bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; - virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const override; - virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override + void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const override; + void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override { if (!m_errorList.empty()) printErrorList(_stream, m_errorList, _linePrefix, false); } static std::string errorMessage(Exception const& _e); -private: +protected: static void printErrorList( std::ostream& _stream, std::vector<SyntaxTestError> const& _errors, @@ -75,6 +75,8 @@ private: bool const _formatted = false ); + virtual bool printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false); + static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream); std::string m_source; diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index b7ea1efc..64e52604 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -28,6 +28,7 @@ #include <tuple> using namespace std; +using namespace langutil; namespace dev { diff --git a/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol b/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol new file mode 100644 index 00000000..6d9afe7c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + struct A { uint a; uint b; } + function f() public pure returns (uint) { + A memory a = A({ a: 1, b: 2 }); + } +} +// ---- +// Warning: (133-143): Unused local variable. +// Warning: (133-143): Assertion checker does not yet support the type of this variable. +// Warning: (146-163): Assertion checker does not yet implement this expression. +// Warning: (146-163): Internal error: Expression undefined for SMT solver. +// Warning: (146-163): Assertion checker does not yet implement this type. diff --git a/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol b/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol new file mode 100644 index 00000000..be785414 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (uint) { + return uint8(1); + } +} +// ---- +// Warning: (106-114): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/control_flow/assignment_in_declaration.sol b/test/libsolidity/smtCheckerTests/control_flow/assignment_in_declaration.sol new file mode 100644 index 00000000..0c701672 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/assignment_in_declaration.sol @@ -0,0 +1,4 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure { uint a = 2; assert(a == 2); } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_assert_condition_1.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_assert_condition_1.sol new file mode 100644 index 00000000..64f6e012 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_assert_condition_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + if (x > 10) { + assert(x > 9); + } + else + { + assert(x < 11); + } + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_assert_condition_2.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_assert_condition_2.sol new file mode 100644 index 00000000..e39ab844 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_assert_condition_2.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + if (x > 10) { + assert(x > 9); + } + else if (x > 2) + { + assert(x <= 10 && x > 2); + } + else + { + assert(0 <= x && x <= 2); + } + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_1.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_1.sol new file mode 100644 index 00000000..f93e32e4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +// Branch does not touch variable a +contract C { + function f(uint x) public pure { + uint a = 3; + if (x > 10) { + } + assert(a == 3); + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_2.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_2.sol new file mode 100644 index 00000000..c00ef787 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +// Positive branch touches variable a, but assertion should still hold. +contract C { + function f(uint x) public pure { + uint a = 3; + if (x > 10) { + a = 3; + } + assert(a == 3); + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_3.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_3.sol new file mode 100644 index 00000000..4e18aa88 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +// Negative branch touches variable a, but assertion should still hold. +contract C { + function f(uint x) public pure { + uint a = 3; + if (x > 10) { + } else { + a = 3; + } + assert(a == 3); + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_4.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_4.sol new file mode 100644 index 00000000..e3a02704 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_4.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +// Variable is not merged, if it is only read. +contract C { + function f(uint x) public pure { + uint a = 3; + if (x > 10) { + assert(a == 3); + } else { + assert(a == 3); + } + assert(a == 3); + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_5.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_5.sol new file mode 100644 index 00000000..0bd1cf3a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_5.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +// Variable is reset in both branches +contract C { + function f(uint x) public pure { + uint a = 2; + if (x > 10) { + a = 3; + } else { + a = 3; + } + assert(a == 3); + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_6.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_6.sol new file mode 100644 index 00000000..8e477179 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_merge_variables_6.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +// Variable is reset in both branches +contract C { + function f(uint x) public pure { + uint a = 2; + if (x > 10) { + a = 3; + } else { + a = 4; + } + assert(a >= 3); + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_1.sol b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_1.sol new file mode 100644 index 00000000..16d6fdfe --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + uint a = 3; + if (x > 10) { + a++; + } + assert(a == 3); + } +} +// ---- +// Warning: (159-173): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_2.sol b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_2.sol new file mode 100644 index 00000000..e25ab20f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_2.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + uint a = 3; + if (x > 10) { + ++a; + } + assert(a == 3); + } +} +// ---- +// Warning: (159-173): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_3.sol b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_3.sol new file mode 100644 index 00000000..03ae7216 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + uint a = 3; + if (x > 10) { + a = 5; + } + assert(a == 3); + } +} +// ---- +// Warning: (161-175): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol new file mode 100644 index 00000000..b4260224 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + function f() public { + uint a = 3; + this.f(); + assert(a == 3); + f(); + assert(a == 3); + } +} +// ---- +// Warning: (99-107): Assertion checker does not yet implement this type of function call. +// Warning: (141-144): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_bound_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_bound_1.sol new file mode 100644 index 00000000..5e9722de --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_bound_1.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +library L +{ + function add(uint x, uint y) internal pure returns (uint) { + require(x < 1000); + require(y < 1000); + return x + y; + } +} + +contract C +{ + using L for uint; + function f(uint x) public pure { + uint y = x.add(999); + assert(y < 10000); + } +} diff --git a/test/libsolidity/smtCheckerTests/functions/functions_bound_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_bound_1_fail.sol new file mode 100644 index 00000000..99c785d0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_bound_1_fail.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +library L +{ + function add(uint x, uint y) internal pure returns (uint) { + require(x < 1000); + require(y < 1000); + return x + y; + } +} + +contract C +{ + using L for uint; + function f(uint x) public pure { + uint y = x.add(999); + assert(y < 1000); + } +} +// ---- +// Warning: (261-277): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol new file mode 100644 index 00000000..2ceb9e60 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +library L +{ + function add(uint x, uint y) internal pure returns (uint) { + require(x < 1000); + require(y < 1000); + return x + y; + } +} + +contract C +{ + function f(uint x) public pure { + uint y = L.add(x, 999); + assert(y < 10000); + } +} diff --git a/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol new file mode 100644 index 00000000..32419700 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +library L +{ + function add(uint x, uint y) internal pure returns (uint) { + require(x < 1000); + require(y < 1000); + return x + y; + } +} + +contract C +{ + function f(uint x) public pure { + uint y = L.add(x, 999); + assert(y < 1000); + } +} +// ---- +// Warning: (245-261): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_1.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_1.sol new file mode 100644 index 00000000..8988efad --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_1.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + require(x == 2); + for (;;) {} + assert(x == 2); + } +} diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_2.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_2.sol new file mode 100644 index 00000000..58c9f3a7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_2.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + for (; x == 2; ) { + assert(x == 2); + } + } +} diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_3.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_3.sol new file mode 100644 index 00000000..8bf9bdc7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_3.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + for (uint y = 2; x < 10; ) { + assert(y == 2); + } + } +} diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_4.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_4.sol new file mode 100644 index 00000000..4d082026 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_4.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + for (uint y = 2; x < 10; y = 3) { + assert(y == 2); + } + } +} +// ---- +// Warning: (136-150): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol new file mode 100644 index 00000000..2c84960f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + uint y; + for (y = 2; x < 10; ) { + y = 3; + } + assert(y == 3); + } +} +// ---- +// Warning: (167-181): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require(). diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_6.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_6.sol new file mode 100644 index 00000000..90c4c328 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_6.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + uint y; + for (y = 2; x < 10; ) { + y = 3; + } + assert(y == 2); + } +} +// ---- +// Warning: (167-181): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require(). diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol new file mode 100644 index 00000000..074be86f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +// Check that variables are cleared +contract C { + function f(uint x) public pure { + x = 2; + while (x > 1) { + x = 2; + } + assert(x == 2); + } +} +// ---- +// Warning: (194-208): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require(). diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_2.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_2.sol new file mode 100644 index 00000000..92a3f0fe --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_2.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +// Check that condition is assumed. +contract C { + function f(uint x) public pure { + while (x == 2) { + assert(x == 2); + } + } +} diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol new file mode 100644 index 00000000..a37df888 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +// Check that condition is not assumed after the body anymore +contract C { + function f(uint x) public pure { + while (x == 2) { + } + assert(x == 2); + } +} +// ---- +// Warning: (187-201): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require(). diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_4.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_4.sol new file mode 100644 index 00000000..f71da865 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_4.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +// Check that negation of condition is not assumed after the body anymore +contract C { + function f(uint x) public pure { + while (x == 2) { + } + assert(x != 2); + } +} +// ---- +// Warning: (199-213): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require(). diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol new file mode 100644 index 00000000..41559c99 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +// Check that side-effects of condition are taken into account +contract C { + function f(uint x, uint y) public pure { + x = 7; + while ((x = y) > 0) { + } + assert(x == 7); + } +} +// ---- +// Warning: (216-230): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require(). diff --git a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol new file mode 100644 index 00000000..894ff1a4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol @@ -0,0 +1,6 @@ +pragma experimental SMTChecker; +contract C { + function f(uint a, uint b) public pure returns (uint) { return a + b; } +} +// ---- +// Warning: (112-117): Overflow (resulting value larger than 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) happens here diff --git a/test/libsolidity/smtCheckerTests/simple/smoke_test.sol b/test/libsolidity/smtCheckerTests/simple/smoke_test.sol new file mode 100644 index 00000000..8b7b77da --- /dev/null +++ b/test/libsolidity/smtCheckerTests/simple/smoke_test.sol @@ -0,0 +1,3 @@ +pragma experimental SMTChecker; +contract C { +} diff --git a/test/libsolidity/smtCheckerTests/special/blockhash.sol b/test/libsolidity/smtCheckerTests/special/blockhash.sol index 1c693914..59a52922 100644 --- a/test/libsolidity/smtCheckerTests/special/blockhash.sol +++ b/test/libsolidity/smtCheckerTests/special/blockhash.sol @@ -2,9 +2,13 @@ pragma experimental SMTChecker; contract C { - function f() public payable { + function f(uint x) public payable { + assert(blockhash(x) > 0); assert(blockhash(2) > 0); + uint y = x; + assert(blockhash(x) == blockhash(y)); } } // ---- -// Warning: (79-103): Assertion violation happens here +// Warning: (85-109): Assertion violation happens here +// Warning: (113-137): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/bool_int_mixed_1.sol b/test/libsolidity/smtCheckerTests/types/bool_int_mixed_1.sol new file mode 100644 index 00000000..d611cc17 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_int_mixed_1.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x) public pure { + uint a; + if(x) + a = 1; + assert(!x || a > 0); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/bool_int_mixed_2.sol b/test/libsolidity/smtCheckerTests/types/bool_int_mixed_2.sol new file mode 100644 index 00000000..24640c5a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_int_mixed_2.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x, uint a) public pure { + require(!x || a > 0); + uint b = a; + assert(!x || b > 0); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/bool_int_mixed_3.sol b/test/libsolidity/smtCheckerTests/types/bool_int_mixed_3.sol new file mode 100644 index 00000000..f872e82f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_int_mixed_3.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x, bool y) public pure { + uint a; + if (x) { + if (y) { + a = 0; + } else { + a = 1; + } + } else { + if (y) { + a = 1; + } else { + a = 0; + } + } + bool xor_x_y = (x && !y) || (!x && y); + assert(!xor_x_y || a > 0); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_1.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_1.sol new file mode 100644 index 00000000..76b4b08b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_1.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x) public pure { + assert(x); + } +} +// ---- +// Warning: (90-99): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol new file mode 100644 index 00000000..5c166c02 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x, bool y) public pure { + assert(x == y); + } +} +// ---- +// Warning: (98-112): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_3.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_3.sol new file mode 100644 index 00000000..1d2ab49f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_3.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x, bool y) public pure { + bool z = x || y; + assert(!(x && y) || z); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_4.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_4.sol new file mode 100644 index 00000000..c40404a4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_4.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x) public pure { + if(x) { + assert(x); + } else { + assert(!x); + } + } +} diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_5.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_5.sol new file mode 100644 index 00000000..4cecebbc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_5.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x) public pure { + bool y = x; + assert(x == y); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_6.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_6.sol new file mode 100644 index 00000000..90350bb6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_6.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + function f(bool x) public pure { + require(x); + bool y; + y = false; + assert(x || y); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol new file mode 100644 index 00000000..84f6c77e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; +contract C +{ + address a; + bool b; + uint c; + function f(uint x) public { + if (x == 0) + { + a = 0x0000000000000000000000000000000000000100; + b = true; + } + else + { + a = 0x0000000000000000000000000000000000000200; + b = false; + } + assert(a > 0x0000000000000000000000000000000000000000 && b); + } +} +// ---- +// Warning: (362-421): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol new file mode 100644 index 00000000..bceddb38 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C +{ + address a; + bool b; + uint c; + function f() public view { + assert(c > 0); + } +} +// ---- +// Warning: (123-136): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_3.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_3.sol new file mode 100644 index 00000000..39049b99 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_3.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; +contract C +{ + function f(uint x) public { + if (x == 0) + { + a = 0x0000000000000000000000000000000000000100; + b = true; + } + else + { + a = 0x0000000000000000000000000000000000000200; + b = false; + } + assert(b == (a < 0x0000000000000000000000000000000000000200)); + } + + function g() public view { + require(a < 0x0000000000000000000000000000000000000100); + assert(c >= 0); + } + address a; + bool b; + uint c; +} diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_4.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_4.sol new file mode 100644 index 00000000..88b6b0ae --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_4.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract C +{ + function f() public view { + assert(c > 0); + } + uint c; +} +// ---- +// Warning: (84-97): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_1.sol b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_1.sol new file mode 100644 index 00000000..b9fae4ee --- /dev/null +++ b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_1.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + if (x >= 0) { revert(); } + } +} +// ---- +// Warning: (94-100): Condition is always true. +// Warning: (104-112): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_2.sol b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_2.sol new file mode 100644 index 00000000..aaa613ea --- /dev/null +++ b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_2.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + if (x >= 10) { if (x < 10) { revert(); } } + } +} +// ---- +// Warning: (109-115): Condition is always false. +// Warning: (119-127): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_3.sol b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_3.sol new file mode 100644 index 00000000..f22cd65e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_3.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +// a plain literal constant is fine +contract C { + function f(uint) public pure { + if (true) { revert(); } + } +} +// ---- +// Warning: (136-144): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/verification_target/simple_assert.sol b/test/libsolidity/smtCheckerTests/verification_target/simple_assert.sol new file mode 100644 index 00000000..8bd6e61a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/verification_target/simple_assert.sol @@ -0,0 +1,6 @@ +pragma experimental SMTChecker; +contract C { + function f(uint a) public pure { assert(a == 2); } +} +// ---- +// Warning: (82-96): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/verification_target/simple_assert_with_require.sol b/test/libsolidity/smtCheckerTests/verification_target/simple_assert_with_require.sol new file mode 100644 index 00000000..b66ae245 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/verification_target/simple_assert_with_require.sol @@ -0,0 +1,4 @@ +pragma experimental SMTChecker; +contract C { + function f(uint a) public pure { require(a < 10); assert(a < 20); } +} diff --git a/test/libsolidity/smtCheckerTestsJSON/multi.json b/test/libsolidity/smtCheckerTestsJSON/multi.json new file mode 100644 index 00000000..2ed5150d --- /dev/null +++ b/test/libsolidity/smtCheckerTestsJSON/multi.json @@ -0,0 +1,11 @@ +{ + "auxiliaryInput": + { + "smtlib2responses": + { + "0x0426cd198d1e7123a28ffac2b759a666b86508ad046babf5166500dd6d8ed308": "unsat\n(error \"line 31 column 26: model is not available\")", + "0xa51ca41ae407f5a727f27101cbc079834743cc8955f9f585582034ca634953f6": "sat\n((|EVALEXPR_0| 1))", + "0xe9477f683ff20aa57fcb08682150f86c5917e1d4c0686b278ab9b73446d0682c": "sat\n((|EVALEXPR_0| 0))" + } + } +} diff --git a/test/libsolidity/smtCheckerTestsJSON/multi.sol b/test/libsolidity/smtCheckerTestsJSON/multi.sol new file mode 100644 index 00000000..e0d69b1d --- /dev/null +++ b/test/libsolidity/smtCheckerTestsJSON/multi.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x) public pure { + assert(x > 0); + assert(x > 100); + assert(x >= 0); + } +} +// ---- +// Warning: (82-95): Assertion violation happens here +// Warning: (99-114): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTestsJSON/simple.json b/test/libsolidity/smtCheckerTestsJSON/simple.json new file mode 100644 index 00000000..fd976b63 --- /dev/null +++ b/test/libsolidity/smtCheckerTestsJSON/simple.json @@ -0,0 +1,9 @@ +{ + "auxiliaryInput": + { + "smtlib2responses": + { + "0xe9477f683ff20aa57fcb08682150f86c5917e1d4c0686b278ab9b73446d0682c": "sat\n((|EVALEXPR_0| 0))" + } + } +} diff --git a/test/libsolidity/smtCheckerTestsJSON/simple.sol b/test/libsolidity/smtCheckerTestsJSON/simple.sol new file mode 100644 index 00000000..6bc7193d --- /dev/null +++ b/test/libsolidity/smtCheckerTestsJSON/simple.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint x) public pure { + assert(x > 0); + } +} +// ---- +// Warning: (82-95): Assertion violation happens here diff --git a/test/libsolidity/syntaxTests/bound/bound_all.sol b/test/libsolidity/syntaxTests/bound/bound_all.sol new file mode 100644 index 00000000..29f55b88 --- /dev/null +++ b/test/libsolidity/syntaxTests/bound/bound_all.sol @@ -0,0 +1,10 @@ +library L { + function g(function() internal returns (uint) _t) internal returns (uint) { return _t(); } +} +contract C { + using L for *; + function f() public returns (uint) { + return t.g(); + } + function t() public pure returns (uint) { return 7; } +} diff --git a/test/libsolidity/syntaxTests/bound/bound_call.sol b/test/libsolidity/syntaxTests/bound/bound_call.sol new file mode 100644 index 00000000..281f19b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/bound/bound_call.sol @@ -0,0 +1,7 @@ +library D { function double(uint self) internal pure returns (uint) { return 2*self; } } +contract C { + using D for uint; + function f(uint a) public pure { + a.double(); + } +} diff --git a/test/libsolidity/syntaxTests/bound/bound_no_call.sol b/test/libsolidity/syntaxTests/bound/bound_no_call.sol new file mode 100644 index 00000000..dcb3c3c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/bound/bound_no_call.sol @@ -0,0 +1,7 @@ +library D { function double(uint self) public pure returns (uint) { return 2*self; } } +contract C { + using D for uint; + function f(uint a) public pure { + a.double; + } +} diff --git a/test/libsolidity/syntaxTests/constructor/abstract_creation_forward_reference.sol b/test/libsolidity/syntaxTests/constructor/abstract_creation_forward_reference.sol new file mode 100644 index 00000000..2e6aeaa5 --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/abstract_creation_forward_reference.sol @@ -0,0 +1,15 @@ +// This used to cause an internal error because of the visitation order. +contract Test { + function createChild() public { + Child asset = new Child(); + } +} + +contract Parent { + constructor(address _address) public {} +} + +contract Child is Parent { +} +// ---- +// TypeError: (146-155): Trying to create an instance of an abstract contract. diff --git a/test/libsolidity/syntaxTests/deprecated_functions.sol b/test/libsolidity/syntaxTests/deprecated_functions.sol index 62dfcff9..c5764e96 100644 --- a/test/libsolidity/syntaxTests/deprecated_functions.sol +++ b/test/libsolidity/syntaxTests/deprecated_functions.sol @@ -8,5 +8,5 @@ contract test { } } // ---- -// TypeError: (58-66): "sha3" has been deprecated in favour of "keccak256" -// TypeError: (101-152): "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/functionTypes/function_type_struct.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_struct.sol new file mode 100644 index 00000000..a367996e --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_struct.sol @@ -0,0 +1,8 @@ +library L +{ + struct Nested + { + uint y; + } + function f(function(Nested memory) external) external pure {} +} diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_struct_undefined_member.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_struct_undefined_member.sol new file mode 100644 index 00000000..ca08afe5 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_struct_undefined_member.sol @@ -0,0 +1,11 @@ +library L +{ + struct Nested + { + Non y; + } + function f(function(Nested memory) external) external pure {} +} +// ---- +// DeclarationError: (32-35): Identifier not found or not unique. +// TypeError: (63-76): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/getter/complex_struct.sol b/test/libsolidity/syntaxTests/getter/complex_struct.sol new file mode 100644 index 00000000..3fa8111c --- /dev/null +++ b/test/libsolidity/syntaxTests/getter/complex_struct.sol @@ -0,0 +1,7 @@ +contract C { + struct Y { + uint a; + uint b; + } + mapping(uint256 => Y) public m; +} diff --git a/test/libsolidity/syntaxTests/getter/nested_structs.sol b/test/libsolidity/syntaxTests/getter/nested_structs.sol new file mode 100644 index 00000000..1068f287 --- /dev/null +++ b/test/libsolidity/syntaxTests/getter/nested_structs.sol @@ -0,0 +1,11 @@ +contract C { + struct Y { + uint b; + } + struct X { + Y a; + } + mapping(uint256 => X) public m; +} +// ---- +// TypeError: (88-118): The following types are only supported for getters in the new experimental ABI encoder: struct C.Y memory. Either remove "public" or use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/getter/recursive_struct.sol b/test/libsolidity/syntaxTests/getter/recursive_struct.sol new file mode 100644 index 00000000..d81cac60 --- /dev/null +++ b/test/libsolidity/syntaxTests/getter/recursive_struct.sol @@ -0,0 +1,8 @@ +contract C { + struct Y { + Y[] x; + } + mapping(uint256 => Y) public m; +} +// ---- +// TypeError: (53-83): Internal or recursive type is not allowed for public state variables. diff --git a/test/libsolidity/syntaxTests/getter/simple_struct.sol b/test/libsolidity/syntaxTests/getter/simple_struct.sol new file mode 100644 index 00000000..c7a23ae9 --- /dev/null +++ b/test/libsolidity/syntaxTests/getter/simple_struct.sol @@ -0,0 +1,6 @@ +contract C { + struct Y { + uint b; + } + mapping(uint256 => Y) public m; +} diff --git a/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol new file mode 100644 index 00000000..37b60e5e --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol @@ -0,0 +1,8 @@ +contract C +{ + function f(bytes memory data) public pure { + sha3; + } +} +// ---- +// TypeError: (60-64): "sha3" has been deprecated in favour of "keccak256" diff --git a/test/libsolidity/syntaxTests/globalFunctions/sha3_override.sol b/test/libsolidity/syntaxTests/globalFunctions/sha3_override.sol new file mode 100644 index 00000000..909c2dc3 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/sha3_override.sol @@ -0,0 +1,11 @@ +contract C +{ + function sha3() public pure returns (bool) { + return true; + } + function f() public pure returns (bool) { + return sha3(); + } +} +// ---- +// Warning: (14-76): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/globalFunctions/sha3_var.sol b/test/libsolidity/syntaxTests/globalFunctions/sha3_var.sol new file mode 100644 index 00000000..19ee72d9 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/sha3_var.sol @@ -0,0 +1,9 @@ +contract C +{ + function f() public pure returns (bool) { + bool sha3 = true; + return sha3; + } +} +// ---- +// Warning: (58-67): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol new file mode 100644 index 00000000..bf3f5ebc --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol @@ -0,0 +1,8 @@ +contract C +{ + function f(bytes memory data) public pure { + suicide; + } +} +// ---- +// TypeError: (60-67): "suicide" has been deprecated in favour of "selfdestruct" diff --git a/test/libsolidity/syntaxTests/globalFunctions/suicide_override.sol b/test/libsolidity/syntaxTests/globalFunctions/suicide_override.sol new file mode 100644 index 00000000..7350da39 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/suicide_override.sol @@ -0,0 +1,11 @@ +contract C +{ + function suicide() public pure returns (bool) { + return true; + } + function f() public pure returns (bool) { + return suicide(); + } +} +// ---- +// Warning: (14-79): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/globalFunctions/suicide_var.sol b/test/libsolidity/syntaxTests/globalFunctions/suicide_var.sol new file mode 100644 index 00000000..3549a563 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/suicide_var.sol @@ -0,0 +1,9 @@ +contract C +{ + function f() public pure returns (bool) { + bool suicide = true; + return suicide; + } +} +// ---- +// Warning: (58-70): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory.sol new file mode 100644 index 00000000..e683ef39 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory.sol @@ -0,0 +1,13 @@ +contract A { + uint dummy; + function f(uint[] calldata) external pure {} + function g(uint[] calldata) external view { dummy; } + function h(uint[] calldata) external { dummy = 42; } + function i(uint[] calldata) external payable {} +} +contract B is A { + function f(uint[] memory) public pure {} + function g(uint[] memory) public view { dummy; } + function h(uint[] memory) public { dummy = 42; } + function i(uint[] memory) public payable {} +} diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_conflict.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_conflict.sol new file mode 100644 index 00000000..dc734d36 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_conflict.sol @@ -0,0 +1,26 @@ +contract A { + uint dummy; + function f(uint[] calldata) external pure {} + function g(uint[] calldata) external view { dummy; } + function h(uint[] calldata) external { dummy = 42; } + function i(uint[] calldata) external payable {} +} +contract B is A { + function f(uint[] calldata) external pure {} + function g(uint[] calldata) external view { dummy; } + function h(uint[] calldata) external { dummy = 42; } + function i(uint[] calldata) external payable {} + function f(uint[] memory) public pure {} + function g(uint[] memory) public view { dummy; } + function h(uint[] memory) public { dummy = 42; } + function i(uint[] memory) public payable {} +} +// ---- +// DeclarationError: (268-312): Function with same name and arguments defined twice. +// DeclarationError: (317-369): Function with same name and arguments defined twice. +// DeclarationError: (374-426): Function with same name and arguments defined twice. +// DeclarationError: (431-478): Function with same name and arguments defined twice. +// TypeError: (268-312): Overriding function visibility differs. +// TypeError: (317-369): Overriding function visibility differs. +// TypeError: (374-426): Overriding function visibility differs. +// TypeError: (431-478): Overriding function visibility differs. diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface.sol new file mode 100644 index 00000000..7eecc079 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface.sol @@ -0,0 +1,13 @@ +interface I { + function f(uint[] calldata) external pure; + function g(uint[] calldata) external view; + function h(uint[] calldata) external; + function i(uint[] calldata) external payable; +} +contract C is I { + uint dummy; + function f(uint[] memory) public pure {} + function g(uint[] memory) public view { dummy; } + function h(uint[] memory) public { dummy = 42; } + function i(uint[] memory) public payable {} +} diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_instantiate.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_instantiate.sol new file mode 100644 index 00000000..4cdc3924 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_instantiate.sol @@ -0,0 +1,12 @@ +interface I { + function f(uint[] calldata) external pure; +} +contract A is I { + function f(uint[] memory) public pure {} +} +contract C { + function f() public { + I i = I(new A()); + i.f(new uint[](1)); + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol new file mode 100644 index 00000000..49b27fd7 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol @@ -0,0 +1,17 @@ +pragma experimental ABIEncoderV2; +interface I { + struct S { int a; } + function f(S calldata) external pure; + function g(S calldata) external view; + function h(S calldata) external; + function i(S calldata) external payable; +} +contract C is I { + uint dummy; + function f(S memory) public pure {} + function g(S memory) public view { dummy; } + function h(S memory) public { dummy = 42; } + function i(S memory) public payable {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol new file mode 100644 index 00000000..42aebf30 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol @@ -0,0 +1,17 @@ +pragma experimental ABIEncoderV2; +contract A { + uint dummy; + struct S { int a; } + function f(S calldata) external pure {} + function g(S calldata) external view { dummy; } + function h(S calldata) external { dummy = 42; } + function i(S calldata) external payable {} +} +contract B is A { + function f(S memory) public pure {} + function g(S memory) public view { dummy; } + function h(S memory) public { dummy = 42; } + function i(S memory) public payable {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/inheritance/override/change_return_types_in_interface.sol b/test/libsolidity/syntaxTests/inheritance/override/change_return_types_in_interface.sol new file mode 100644 index 00000000..804a1810 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/change_return_types_in_interface.sol @@ -0,0 +1,10 @@ +interface I { + function f() external pure returns (uint); +} +contract B is I { + // The compiler used to have a bug where changing + // the return type was fine in this situation. + function f() public pure returns (uint, uint) {} +} +// ---- +// TypeError: (182-230): Overriding function return types differ. diff --git a/test/libsolidity/syntaxTests/inheritance/override/external_turns_public_no_params.sol b/test/libsolidity/syntaxTests/inheritance/override/external_turns_public_no_params.sol new file mode 100644 index 00000000..3d0394f5 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/external_turns_public_no_params.sol @@ -0,0 +1,7 @@ +contract A { + function f() external pure {} +} +contract B is A { + function f() public pure { + } +} diff --git a/test/libsolidity/syntaxTests/inheritance/override/implement_interface_by_public_variable.sol b/test/libsolidity/syntaxTests/inheritance/override/implement_interface_by_public_variable.sol new file mode 100644 index 00000000..49f7c33b --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/implement_interface_by_public_variable.sol @@ -0,0 +1,7 @@ +interface X { function test() external returns (uint256); } +contract Y is X { + uint256 public test = 42; +} +contract T { + constructor() public { new Y(); } +} diff --git a/test/libsolidity/syntaxTests/inheritance/override/implement_internal_function_by_public_variable.sol b/test/libsolidity/syntaxTests/inheritance/override/implement_internal_function_by_public_variable.sol new file mode 100644 index 00000000..32fac25c --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/implement_internal_function_by_public_variable.sol @@ -0,0 +1,9 @@ +contract X { function test() internal returns (uint256); } +contract Y is X { + uint256 public test = 42; +} +contract T { + constructor() public { new Y(); } +} +// ---- +// DeclarationError: (81-105): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/inheritance/override/implement_private_function_by_public_variable.sol b/test/libsolidity/syntaxTests/inheritance/override/implement_private_function_by_public_variable.sol new file mode 100644 index 00000000..c58e24b6 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/implement_private_function_by_public_variable.sol @@ -0,0 +1,7 @@ +contract X { function test() private returns (uint256); } +contract Y is X { + uint256 public test = 42; +} +contract T { + constructor() public { new Y(); } +} diff --git a/test/libsolidity/syntaxTests/inheritance/override/implement_public_function_by_public_variable.sol b/test/libsolidity/syntaxTests/inheritance/override/implement_public_function_by_public_variable.sol new file mode 100644 index 00000000..7a59c137 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/implement_public_function_by_public_variable.sol @@ -0,0 +1,9 @@ +contract X { function test() public returns (uint256); } +contract Y is X { + uint256 public test = 42; +} +contract T { + constructor() public { new Y(); } +} +// ---- +// DeclarationError: (79-103): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/inheritance/override/internal_external.sol b/test/libsolidity/syntaxTests/inheritance/override/internal_external.sol new file mode 100644 index 00000000..90973ee7 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/internal_external.sol @@ -0,0 +1,7 @@ +contract A { + function f(uint[] calldata) external pure {} + function f(uint[] memory) internal pure {} +} +// ---- +// DeclarationError: (17-61): Function with same name and arguments defined twice. +// TypeError: (17-61): Overriding function visibility differs. diff --git a/test/libsolidity/syntaxTests/inheritance/override/internal_external_inheritance.sol b/test/libsolidity/syntaxTests/inheritance/override/internal_external_inheritance.sol new file mode 100644 index 00000000..c09a8000 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/internal_external_inheritance.sol @@ -0,0 +1,9 @@ +contract A { + function f(uint[] calldata) external pure {} +} +contract B { + function f(uint[] memory) internal pure {} +} +contract C is A, B {} +// ---- +// TypeError: (81-123): Overriding function visibility differs. diff --git a/test/libsolidity/syntaxTests/inheritance/override/state_variable_function.sol b/test/libsolidity/syntaxTests/inheritance/override/state_variable_function.sol index 0f05cc8e..fb7f3fbd 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/state_variable_function.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/state_variable_function.sol @@ -6,3 +6,4 @@ contract C is A { } // ---- // DeclarationError: (50-85): Identifier already declared. +// TypeError: (50-85): Redeclaring an already implemented function as abstract diff --git a/test/libsolidity/syntaxTests/inheritance/super_on_external.sol b/test/libsolidity/syntaxTests/inheritance/super_on_external.sol new file mode 100644 index 00000000..21f3b1c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/super_on_external.sol @@ -0,0 +1,10 @@ +contract A { + function f() external pure {} +} +contract B is A { + function f() public pure { + super.f(); + } +} +// ---- +// TypeError: (106-113): Member "f" not found or not visible after argument-dependent lookup in contract super B. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol index 715913de..b44d09e3 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol @@ -7,4 +7,4 @@ contract C { } // ---- // ParserError: (72-73): Literal, identifier or instruction expected. -// ParserError: (72-73): Expected primary expression. +// ParserError: (72-73): Octal numbers not allowed. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/277_dynamic_inline_array.sol b/test/libsolidity/syntaxTests/inline_arrays/dynamic_inline_array.sol index e613758b..e613758b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/277_dynamic_inline_array.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/dynamic_inline_array.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/269_inline_array_declaration_and_passing_implicit_conversion.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion.sol index 023404f7..023404f7 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/269_inline_array_declaration_and_passing_implicit_conversion.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/270_inline_array_declaration_and_passing_implicit_conversion_strings.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion_strings.sol index 025244d3..025244d3 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/270_inline_array_declaration_and_passing_implicit_conversion_strings.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion_strings.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/271_inline_array_declaration_const_int_conversion.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_int_conversion.sol index e7036bdf..e7036bdf 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/271_inline_array_declaration_const_int_conversion.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_int_conversion.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/272_inline_array_declaration_const_string_conversion.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_string_conversion.sol index 4e92f6e1..4e92f6e1 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/272_inline_array_declaration_const_string_conversion.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_string_conversion.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/273_inline_array_declaration_no_type.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type.sol index 4d3e6aed..4d3e6aed 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/273_inline_array_declaration_no_type.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/274_inline_array_declaration_no_type_strings.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type_strings.sol index 6d36942d..6d36942d 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/274_inline_array_declaration_no_type_strings.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type_strings.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/325_inline_array_fixed_types.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_fixed_types.sol index c46297c3..c46297c3 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/325_inline_array_fixed_types.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_fixed_types.sol diff --git a/test/libsolidity/syntaxTests/inline_arrays/inline_array_of_mapping_type.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_of_mapping_type.sol new file mode 100644 index 00000000..59a88130 --- /dev/null +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_of_mapping_type.sol @@ -0,0 +1,8 @@ +contract C { + mapping(int => int) a; + function f() public { + [a]; + } +} +// ---- +// TypeError: (66-69): Type mapping(int256 => int256) is only valid in storage. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/326_inline_array_rationals.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_rationals.sol index bdc3c2c1..bdc3c2c1 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/326_inline_array_rationals.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_rationals.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/276_invalid_types_in_inline_array.sol b/test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol index 03d7266a..03d7266a 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/276_invalid_types_in_inline_array.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/278_lvalues_as_inline_array.sol b/test/libsolidity/syntaxTests/inline_arrays/lvalues_as_inline_array.sol index 5a39f550..5a39f550 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/278_lvalues_as_inline_array.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/lvalues_as_inline_array.sol diff --git a/test/libsolidity/syntaxTests/memberLookup/internal_function_type.sol b/test/libsolidity/syntaxTests/memberLookup/internal_function_type.sol new file mode 100644 index 00000000..560a6c2a --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/internal_function_type.sol @@ -0,0 +1,7 @@ +contract C { + function () internal returns (uint) x; + constructor() public { + C.x = g; + } + function g() public pure returns (uint) {} +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/182_equal_overload.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/182_equal_overload.sol index cb9eb3fa..86585518 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/182_equal_overload.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/182_equal_overload.sol @@ -4,4 +4,4 @@ contract C { } // ---- // DeclarationError: (17-66): Function with same name and arguments defined twice. -// TypeError: (17-66): Overriding function visibility differs. +// TypeError: (17-66): Overriding function return types differ. diff --git a/test/libsolidity/syntaxTests/parsing/invalid_fixed_conversion_leading_zeroes_check.sol b/test/libsolidity/syntaxTests/parsing/invalid_fixed_conversion_leading_zeroes_check.sol index fb267ba3..352b5f8f 100644 --- a/test/libsolidity/syntaxTests/parsing/invalid_fixed_conversion_leading_zeroes_check.sol +++ b/test/libsolidity/syntaxTests/parsing/invalid_fixed_conversion_leading_zeroes_check.sol @@ -4,4 +4,4 @@ contract test { } } // ---- -// ParserError: (44-47): Expected primary expression. +// ParserError: (44-47): Identifier-start is not allowed at end of a number. diff --git a/test/libsolidity/syntaxTests/string/string_new_line.sol b/test/libsolidity/syntaxTests/string/string_new_line.sol index da2240f7..4cbc71a5 100644 --- a/test/libsolidity/syntaxTests/string/string_new_line.sol +++ b/test/libsolidity/syntaxTests/string/string_new_line.sol @@ -6,4 +6,4 @@ contract test { } } // ---- -// ParserError: (100-112): Expected primary expression. +// ParserError: (100-112): Expected string end-quote. diff --git a/test/libsolidity/syntaxTests/string/string_terminated_by_backslash.sol b/test/libsolidity/syntaxTests/string/string_terminated_by_backslash.sol index 3eaba6af..fba53a03 100644 --- a/test/libsolidity/syntaxTests/string/string_terminated_by_backslash.sol +++ b/test/libsolidity/syntaxTests/string/string_terminated_by_backslash.sol @@ -5,4 +5,4 @@ contract test { } } // ---- -// ParserError: (100-109): Expected primary expression.
\ No newline at end of file +// ParserError: (100-109): Expected string end-quote. diff --git a/test/libsolidity/syntaxTests/string/string_unterminated.sol b/test/libsolidity/syntaxTests/string/string_unterminated.sol index 3291781e..e893f4b1 100644 --- a/test/libsolidity/syntaxTests/string/string_unterminated.sol +++ b/test/libsolidity/syntaxTests/string/string_unterminated.sol @@ -4,4 +4,4 @@ contract test { } } // ---- -// ParserError: (100-112): Expected primary expression.
\ No newline at end of file +// ParserError: (100-112): Expected string end-quote. diff --git a/test/libsolidity/syntaxTests/string/string_unterminated_no_new_line.sol b/test/libsolidity/syntaxTests/string/string_unterminated_no_new_line.sol index e7be50d2..b100396b 100644 --- a/test/libsolidity/syntaxTests/string/string_unterminated_no_new_line.sol +++ b/test/libsolidity/syntaxTests/string/string_unterminated_no_new_line.sol @@ -1,4 +1,4 @@ contract test { function f() pure public { "abc\ // ---- -// ParserError: (47-53): Expected primary expression.
\ No newline at end of file +// ParserError: (47-53): Expected string end-quote. diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol new file mode 100644 index 00000000..d2a411ec --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol @@ -0,0 +1,11 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(Data.S memory a) public {} +} +contract Data { + struct S { S x; } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (63-78): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_argument_external.sol b/test/libsolidity/syntaxTests/types/mapping/library_argument_external.sol index 1098008d..ebd05d86 100644 --- a/test/libsolidity/syntaxTests/types/mapping/library_argument_external.sol +++ b/test/libsolidity/syntaxTests/types/mapping/library_argument_external.sol @@ -3,4 +3,3 @@ library L { } } // ---- -// TypeError: (27-56): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_argument_public.sol b/test/libsolidity/syntaxTests/types/mapping/library_argument_public.sol index dedd4f68..36e833b7 100644 --- a/test/libsolidity/syntaxTests/types/mapping/library_argument_public.sol +++ b/test/libsolidity/syntaxTests/types/mapping/library_argument_public.sol @@ -3,4 +3,3 @@ library L { } } // ---- -// TypeError: (27-56): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_return_external.sol b/test/libsolidity/syntaxTests/types/mapping/library_return_external.sol index 1e756819..6c145801 100644 --- a/test/libsolidity/syntaxTests/types/mapping/library_return_external.sol +++ b/test/libsolidity/syntaxTests/types/mapping/library_return_external.sol @@ -5,6 +5,3 @@ library L } } // ---- -// TypeError: (27-58): Type is required to live outside storage. -// TypeError: (60-91): Type is required to live outside storage. -// TypeError: (123-152): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_return_public.sol b/test/libsolidity/syntaxTests/types/mapping/library_return_public.sol index 357751a0..a98458cd 100644 --- a/test/libsolidity/syntaxTests/types/mapping/library_return_public.sol +++ b/test/libsolidity/syntaxTests/types/mapping/library_return_public.sol @@ -5,6 +5,3 @@ library L } } // ---- -// TypeError: (27-58): Type is required to live outside storage. -// TypeError: (60-91): Type is required to live outside storage. -// TypeError: (121-150): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_external.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_external.sol index c050f8e9..fa3757fa 100644 --- a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_external.sol +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_external.sol @@ -2,5 +2,5 @@ contract c { function f1(mapping(uint => uint) calldata) pure external returns (mapping(uint => uint) memory) {} } // ---- -// TypeError: (29-59): Type is required to live outside storage. -// TypeError: (29-59): Internal or recursive type is not allowed for public or external functions. +// TypeError: (29-59): Mapping types for parameters or return variables can only be used in internal or library functions. +// TypeError: (84-112): Mapping types for parameters or return variables can only be used in internal or library functions. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_public.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_public.sol index b63868b8..8c73b5ae 100644 --- a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_public.sol +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_public.sol @@ -2,5 +2,4 @@ contract c { function f3(mapping(uint => uint) memory) view public {} } // ---- -// TypeError: (29-57): Type is required to live outside storage. -// TypeError: (29-57): Internal or recursive type is not allowed for public or external functions. +// TypeError: (29-57): Mapping types for parameters or return variables can only be used in internal or library functions. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_return_public_memory.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_return_public_memory.sol index 35c3abc9..7378324a 100644 --- a/test/libsolidity/syntaxTests/types/mapping/mapping_return_public_memory.sol +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_return_public_memory.sol @@ -3,5 +3,4 @@ contract C { } } // ---- -// TypeError: (51-79): Type is required to live outside storage. -// TypeError: (51-79): Internal or recursive type is not allowed for public or external functions. +// TypeError: (51-79): Mapping types for parameters or return variables can only be used in internal or library functions. diff --git a/test/libsolidity/syntaxTests/unicode_escape_literals.sol b/test/libsolidity/syntaxTests/unicode_escape_literals.sol index a340487b..4415d493 100644 --- a/test/libsolidity/syntaxTests/unicode_escape_literals.sol +++ b/test/libsolidity/syntaxTests/unicode_escape_literals.sol @@ -28,4 +28,4 @@ contract test { } // ---- -// ParserError: (678-681): Expected primary expression. +// ParserError: (678-681): Invalid escape sequence. diff --git a/test/libsolidity/syntaxTests/unimplemented_super_function.sol b/test/libsolidity/syntaxTests/unimplemented_super_function.sol new file mode 100644 index 00000000..356727ae --- /dev/null +++ b/test/libsolidity/syntaxTests/unimplemented_super_function.sol @@ -0,0 +1,8 @@ +contract a { + function f() public; +} +contract b is a { + function f() public { super.f(); } +} +// ---- +// TypeError: (84-91): Member "f" not found or not visible after argument-dependent lookup in contract super b. diff --git a/test/libsolidity/syntaxTests/unimplemented_super_function_derived.sol b/test/libsolidity/syntaxTests/unimplemented_super_function_derived.sol new file mode 100644 index 00000000..88acbdf0 --- /dev/null +++ b/test/libsolidity/syntaxTests/unimplemented_super_function_derived.sol @@ -0,0 +1,12 @@ +contract a { + function f() public; +} +contract b is a { + function f() public { super.f(); } +} +contract c is a,b { + // No error here. + function f() public { super.f(); } +} +// ---- +// TypeError: (84-91): Member "f" not found or not visible after argument-dependent lookup in contract super b. diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index d224bdcd..a247a169 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -23,24 +23,24 @@ #include <test/Options.h> -#include <libyul/optimiser/Disambiguator.h> - -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/SourceReferenceFormatter.h> -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libyul/optimiser/Disambiguator.h> +#include <libyul/AsmParser.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmPrinter.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/ErrorReporter.h> #include <boost/test/unit_test.hpp> using namespace std; -using namespace dev::yul; +using namespace langutil; +using namespace yul; using namespace dev::solidity; -void dev::yul::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) +void yul::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) { SourceReferenceFormatter formatter(cout, [&](std::string const&) -> Scanner const& { return _scanner; }); @@ -52,18 +52,18 @@ void dev::yul::test::printErrors(ErrorList const& _errors, Scanner const& _scann } -pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::yul::test::parse(string const& _source, bool _yul) +pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(string const& _source, bool _yul) { - auto flavour = _yul ? assembly::AsmFlavour::Yul : assembly::AsmFlavour::Strict; + auto flavour = _yul ? yul::AsmFlavour::Yul : yul::AsmFlavour::Strict; ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared<Scanner>(CharStream(_source), ""); - auto parserResult = assembly::Parser(errorReporter, flavour).parse(scanner, false); + auto scanner = make_shared<Scanner>(CharStream(_source, "")); + auto parserResult = yul::Parser(errorReporter, flavour).parse(scanner, false); if (parserResult) { BOOST_REQUIRE(errorReporter.errors().empty()); - auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); - assembly::AsmAnalyzer analyzer( + auto analysisInfo = make_shared<yul::AsmAnalysisInfo>(); + yul::AsmAnalyzer analyzer( *analysisInfo, errorReporter, dev::test::Options::get().evmVersion(), @@ -83,13 +83,13 @@ pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::yul::test::p return {}; } -assembly::Block dev::yul::test::disambiguate(string const& _source, bool _yul) +yul::Block yul::test::disambiguate(string const& _source, bool _yul) { auto result = parse(_source, _yul); return boost::get<Block>(Disambiguator(*result.second, {})(*result.first)); } -string dev::yul::test::format(string const& _source, bool _yul) +string yul::test::format(string const& _source, bool _yul) { - return assembly::AsmPrinter(_yul)(*parse(_source, _yul).first); + return yul::AsmPrinter(_yul)(*parse(_source, _yul).first); } diff --git a/test/libyul/Common.h b/test/libyul/Common.h index ee191494..a1c64ca5 100644 --- a/test/libyul/Common.h +++ b/test/libyul/Common.h @@ -21,35 +21,34 @@ #pragma once -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <string> #include <vector> #include <memory> -namespace dev -{ -namespace solidity +namespace langutil { class Scanner; class Error; using ErrorList = std::vector<std::shared_ptr<Error const>>; -namespace assembly +} + +namespace yul { struct AsmAnalysisInfo; } -} + namespace yul { namespace test { -void printErrors(solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); -std::pair<std::shared_ptr<solidity::assembly::Block>, std::shared_ptr<solidity::assembly::AsmAnalysisInfo>> +void printErrors(langutil::ErrorList const& _errors, langutil::Scanner const& _scanner); +std::pair<std::shared_ptr<Block>, std::shared_ptr<AsmAnalysisInfo>> parse(std::string const& _source, bool _yul = true); -solidity::assembly::Block disambiguate(std::string const& _source, bool _yul = true); +Block disambiguate(std::string const& _source, bool _yul = true); std::string format(std::string const& _source, bool _yul = true); } } -} diff --git a/test/libyul/Inliner.cpp b/test/libyul/Inliner.cpp index 66810298..631cda08 100644 --- a/test/libyul/Inliner.cpp +++ b/test/libyul/Inliner.cpp @@ -26,8 +26,7 @@ #include <libyul/optimiser/FullInliner.h> #include <libyul/optimiser/FunctionHoister.h> #include <libyul/optimiser/FunctionGrouper.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libyul/AsmPrinter.h> #include <boost/test/unit_test.hpp> @@ -36,8 +35,8 @@ using namespace std; using namespace dev; -using namespace dev::yul; -using namespace dev::yul::test; +using namespace yul; +using namespace yul::test; using namespace dev::solidity; namespace diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp new file mode 100644 index 00000000..bb88e4da --- /dev/null +++ b/test/libyul/ObjectParser.cpp @@ -0,0 +1,257 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2018 + * Unit tests for the Yul object parser. + */ + +#include <test/Options.h> + +#include <test/libsolidity/ErrorCheck.h> + +#include <libsolidity/interface/AssemblyStack.h> + +#include <boost/optional.hpp> +#include <boost/algorithm/string/replace.hpp> + +#include <string> +#include <memory> + +using namespace std; +using namespace langutil; + +namespace dev +{ +namespace yul +{ +namespace test +{ + +namespace +{ + +std::pair<bool, ErrorList> parse(string const& _source) +{ + try + { + solidity::AssemblyStack asmStack( + dev::test::Options::get().evmVersion(), + solidity::AssemblyStack::Language::StrictAssembly + ); + bool success = asmStack.parseAndAnalyze("source", _source); + return {success, asmStack.errors()}; + } + catch (FatalError const&) + { + BOOST_FAIL("Fatal error leaked."); + } + return {false, {}}; +} + +boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) +{ + bool success; + ErrorList errors; + tie(success, errors) = parse(_source); + if (!success) + { + BOOST_REQUIRE_EQUAL(errors.size(), 1); + return *errors.front(); + } + else + { + // If success is true, there might still be an error in the assembly stage. + if (_allowWarnings && Error::containsOnlyWarnings(errors)) + return {}; + else if (!errors.empty()) + { + if (!_allowWarnings) + BOOST_CHECK_EQUAL(errors.size(), 1); + return *errors.front(); + } + } + return {}; +} + +bool successParse(std::string const& _source, bool _allowWarnings = true) +{ + return !parseAndReturnFirstError(_source, _allowWarnings); +} + +Error expectError(std::string const& _source, bool _allowWarnings = false) +{ + + auto error = parseAndReturnFirstError(_source, _allowWarnings); + BOOST_REQUIRE(error); + return *error; +} + +} + +#define CHECK_ERROR(text, typ, substring) \ +do \ +{ \ + Error err = expectError((text), false); \ + BOOST_CHECK(err.type() == (Error::Type::typ)); \ + BOOST_CHECK(solidity::searchErrorMessage(err, (substring))); \ +} while(0) + +BOOST_AUTO_TEST_SUITE(YulObjectParser) + +BOOST_AUTO_TEST_CASE(empty_code) +{ + BOOST_CHECK(successParse("{ }")); +} + +BOOST_AUTO_TEST_CASE(object_with_empty_code) +{ + BOOST_CHECK(successParse("object \"a\" { code { } }")); +} + +BOOST_AUTO_TEST_CASE(non_object) +{ + CHECK_ERROR("code {}", ParserError, "Expected keyword \"object\""); +} + +BOOST_AUTO_TEST_CASE(empty_name) +{ + CHECK_ERROR("object \"\" { code {} }", ParserError, "Object name cannot be empty"); +} + +BOOST_AUTO_TEST_CASE(recursion_depth) +{ + string input; + for (size_t i = 0; i < 20000; i++) + input += "object \"a" + to_string(i) + "\" { code {} "; + for (size_t i = 0; i < 20000; i++) + input += "}"; + + CHECK_ERROR(input, ParserError, "recursion"); +} + +BOOST_AUTO_TEST_CASE(object_with_code) +{ + BOOST_CHECK(successParse("object \"a\" { code { let x := mload(0) sstore(0, x) } }")); +} + +BOOST_AUTO_TEST_CASE(object_with_code_and_data) +{ + BOOST_CHECK(successParse("object \"a\" { code { let x := mload(0) sstore(0, x) } data \"b\" hex\"01010202\" }")); +} + +BOOST_AUTO_TEST_CASE(object_with_non_code_at_start) +{ + CHECK_ERROR("object \"a\" { data \"d\" hex\"0102\" code { } }", ParserError, "Expected keyword \"code\""); +} + +BOOST_AUTO_TEST_CASE(nested_object) +{ + string code = R"( + object "outer" { + code { let x := mload(0) } + data "x" "stringdata" + object "inner" { + code { mstore(0, 1) } + object "inner inner" { code {} } + data "innerx" "abc" + data "innery" "def" + } + } + )"; + BOOST_CHECK(successParse(code)); +} + +BOOST_AUTO_TEST_CASE(incomplete) +{ + CHECK_ERROR("object \"abc\" { code {} } object", ParserError, "Expected end of source"); +} + +BOOST_AUTO_TEST_CASE(reuse_object_name) +{ + string code = R"( + object "outer" { + code { } + data "outer" "stringdata" + } + )"; + CHECK_ERROR(code, ParserError, "Object name cannot be the same as the name of the containing object"); +} + +BOOST_AUTO_TEST_CASE(reuse_object_in_subobject) +{ + string code = R"( + object "outer" { + code { } + object "outer" { code {} } + } + )"; + CHECK_ERROR(code, ParserError, "Object name cannot be the same as the name of the containing object"); +} + +BOOST_AUTO_TEST_CASE(reuse_object_of_sibling) +{ + string code = R"( + object "O" { + code { } + object "i" { code {} } + data "i" "abc" + } + )"; + CHECK_ERROR(code, ParserError, "already exists inside the"); +} + +BOOST_AUTO_TEST_CASE(to_string) +{ + string code = R"( + object "O" { + code { let x := mload(0) if x { sstore(0, 1) } } + object "i" { code {} data "j" "def" } + data "j" "abc" + data "k" hex"010203" + } + )"; + string expectation = R"(object "O" { + code { + let x := mload(0) + if x + { + sstore(0, 1) + } + } + object "i" { + code { + } + data "j" hex"646566" + } + data "j" hex"616263" + data "k" hex"010203" +} +)"; + expectation = boost::replace_all_copy(expectation, "\t", " "); + solidity::AssemblyStack asmStack( + dev::test::Options::get().evmVersion(), + solidity::AssemblyStack::Language::StrictAssembly + ); + BOOST_REQUIRE(asmStack.parseAndAnalyze("source", code)); + BOOST_CHECK_EQUAL(asmStack.print(), expectation); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 3f329d28..caaf2719 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -23,11 +23,11 @@ #include <test/libsolidity/ErrorCheck.h> -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <libyul/AsmParser.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/ErrorReporter.h> #include <boost/optional.hpp> #include <boost/algorithm/string/replace.hpp> @@ -36,10 +36,10 @@ #include <memory> using namespace std; +using namespace dev; +using namespace langutil; -namespace dev -{ -namespace solidity +namespace yul { namespace test { @@ -51,17 +51,17 @@ bool parse(string const& _source, ErrorReporter& errorReporter) { try { - auto scanner = make_shared<Scanner>(CharStream(_source)); - auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Yul).parse(scanner, false); + auto scanner = make_shared<Scanner>(CharStream(_source, "")); + auto parserResult = yul::Parser(errorReporter, yul::AsmFlavour::Yul).parse(scanner, false); if (parserResult) { - assembly::AsmAnalysisInfo analysisInfo; - return (assembly::AsmAnalyzer( + yul::AsmAnalysisInfo analysisInfo; + return (yul::AsmAnalyzer( analysisInfo, errorReporter, dev::test::Options::get().evmVersion(), boost::none, - assembly::AsmFlavour::Yul + yul::AsmFlavour::Yul )).analyze(*parserResult); } } @@ -116,7 +116,7 @@ do \ { \ Error err = expectError((text), false); \ BOOST_CHECK(err.type() == (Error::Type::typ)); \ - BOOST_CHECK(searchErrorMessage(err, (substring))); \ + BOOST_CHECK(dev::solidity::searchErrorMessage(err, (substring))); \ } while(0) BOOST_AUTO_TEST_SUITE(YulParser) @@ -302,5 +302,4 @@ BOOST_AUTO_TEST_CASE(if_statement_invalid) BOOST_AUTO_TEST_SUITE_END() } -} } // end namespaces diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 03cd6446..15d70faa 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -31,6 +31,7 @@ #include <libyul/optimiser/FunctionHoister.h> #include <libyul/optimiser/ExpressionInliner.h> #include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/ForLoopInitRewriter.h> #include <libyul/optimiser/MainFunction.h> #include <libyul/optimiser/Rematerialiser.h> #include <libyul/optimiser/ExpressionSimplifier.h> @@ -39,13 +40,13 @@ #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/Suite.h> +#include <libyul/AsmPrinter.h> +#include <libyul/AsmParser.h> +#include <libyul/AsmAnalysis.h> +#include <liblangutil/SourceReferenceFormatter.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/inlineasm/AsmPrinter.h> -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Scanner.h> #include <boost/test/unit_test.hpp> #include <boost/algorithm/string.hpp> @@ -53,8 +54,9 @@ #include <fstream> using namespace dev; -using namespace dev::yul; -using namespace dev::yul::test; +using namespace langutil; +using namespace yul; +using namespace yul::test; using namespace dev::solidity; using namespace dev::solidity::test; using namespace std; @@ -90,9 +92,9 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename) bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { - assembly::AsmPrinter printer{m_yul}; + yul::AsmPrinter printer{m_yul}; shared_ptr<Block> ast; - shared_ptr<assembly::AsmAnalysisInfo> analysisInfo; + shared_ptr<yul::AsmAnalysisInfo> analysisInfo; if (!parse(_stream, _linePrefix, _formatted)) return false; @@ -108,6 +110,11 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con disambiguate(); VarDeclPropagator{}(*m_ast); } + else if (m_optimizerStep == "forLoopInitRewriter") + { + disambiguate(); + ForLoopInitRewriter{}(*m_ast); + } else if (m_optimizerStep == "commonSubexpressionEliminator") { disambiguate(); @@ -249,19 +256,19 @@ void YulOptimizerTest::printIndented(ostream& _stream, string const& _output, st bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - assembly::AsmFlavour flavour = m_yul ? assembly::AsmFlavour::Yul : assembly::AsmFlavour::Strict; + yul::AsmFlavour flavour = m_yul ? yul::AsmFlavour::Yul : yul::AsmFlavour::Strict; ErrorList errors; ErrorReporter errorReporter(errors); - shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(m_source), ""); - m_ast = assembly::Parser(errorReporter, flavour).parse(scanner, false); + shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(m_source, "")); + m_ast = yul::Parser(errorReporter, flavour).parse(scanner, false); if (!m_ast || !errorReporter.errors().empty()) { FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; printErrors(_stream, errorReporter.errors(), *scanner); return false; } - m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); - assembly::AsmAnalyzer analyzer( + m_analysisInfo = make_shared<yul::AsmAnalysisInfo>(); + yul::AsmAnalyzer analyzer( *m_analysisInfo, errorReporter, dev::test::Options::get().evmVersion(), diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index 7db17ceb..90026e24 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -17,28 +17,27 @@ #pragma once -#include <test/libsolidity/TestCase.h> +#include <test/TestCase.h> - -namespace dev -{ -namespace solidity +namespace langutil { class Scanner; class Error; using ErrorList = std::vector<std::shared_ptr<Error const>>; -namespace assembly +} + +namespace yul { struct AsmAnalysisInfo; struct Block; } -} + namespace yul { namespace test { -class YulOptimizerTest: public solidity::test::TestCase +class YulOptimizerTest: public dev::solidity::test::TestCase { public: static std::unique_ptr<TestCase> create(std::string const& _filename) @@ -58,18 +57,17 @@ private: bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); void disambiguate(); - static void printErrors(std::ostream& _stream, solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); + static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors, langutil::Scanner const& _scanner); std::string m_source; bool m_yul = false; std::string m_optimizerStep; std::string m_expectation; - std::shared_ptr<solidity::assembly::Block> m_ast; - std::shared_ptr<solidity::assembly::AsmAnalysisInfo> m_analysisInfo; + std::shared_ptr<Block> m_ast; + std::shared_ptr<AsmAnalysisInfo> m_analysisInfo; std::string m_obtainedResult; }; } } -} diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul new file mode 100644 index 00000000..3ca00f55 --- /dev/null +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul @@ -0,0 +1,23 @@ +{ + let random := 42 + for { let a := 1 + let b := 1 } iszero(eq(a, 10)) { a := add(a, b) } { + a := add(a, 1) + } +} +// ---- +// forLoopInitRewriter +// { +// let random := 42 +// let a := 1 +// let b := 1 +// for { +// } +// iszero(eq(a, 10)) +// { +// a := add(a, b) +// } +// { +// a := add(a, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul new file mode 100644 index 00000000..05aceb52 --- /dev/null +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul @@ -0,0 +1,20 @@ +{ + let a := 1 + for {} iszero(eq(a, 10)) { a := add(a, 1) } { + a := add(a, 1) + } +} +// ---- +// forLoopInitRewriter +// { +// let a := 1 +// for { +// } +// iszero(eq(a, 10)) +// { +// a := add(a, 1) +// } +// { +// a := add(a, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul new file mode 100644 index 00000000..565b7725 --- /dev/null +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul @@ -0,0 +1,21 @@ +{ + let random := 42 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + a := add(a, 1) + } +} +// ---- +// forLoopInitRewriter +// { +// let random := 42 +// let a := 1 +// for { +// } +// iszero(eq(a, 10)) +// { +// a := add(a, 1) +// } +// { +// a := add(a, 1) +// } +// } diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 19a1d958..736212fc 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -4,7 +4,7 @@ target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_L add_executable(yulopti yulopti.cpp) target_link_libraries(yulopti PRIVATE solidity ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) -add_executable(isoltest isoltest.cpp ../Options.cpp ../Common.cpp ../libsolidity/TestCase.cpp ../libsolidity/SyntaxTest.cpp +add_executable(isoltest isoltest.cpp ../Options.cpp ../Common.cpp ../TestCase.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/AnalysisFramework.cpp ../libsolidity/SolidityExecutionFramework.cpp ../ExecutionFramework.cpp - ../RPCSession.cpp ../libsolidity/ASTJSONTest.cpp ../libyul/YulOptimizerTest.cpp) + ../RPCSession.cpp ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerJSONTest.cpp ../libyul/YulOptimizerTest.cpp) target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 1b6fd54a..f8e2dc58 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -21,6 +21,7 @@ #include <test/libsolidity/AnalysisFramework.h> #include <test/libsolidity/SyntaxTest.h> #include <test/libsolidity/ASTJSONTest.h> +#include <test/libsolidity/SMTCheckerJSONTest.h> #include <test/libyul/YulOptimizerTest.h> #include <boost/algorithm/string.hpp> @@ -412,6 +413,17 @@ Allowed options)", global_stats += *stats; else return 1; + + if (auto stats = runTestSuite( + "SMT Checker JSON", + testPath / "libsolidity", + "smtCheckerTestsJSON", + SMTCheckerTest::create, + formatted + )) + global_stats += *stats; + else + return 1; } cout << endl << "Summary: "; diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 348c5f4a..5273bbb9 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -19,15 +19,15 @@ */ #include <libdevcore/CommonIO.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Scanner.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> #include <libsolidity/parsing/Parser.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmPrinter.h> -#include <libsolidity/interface/SourceReferenceFormatter.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <libyul/AsmData.h> +#include <libyul/AsmParser.h> +#include <libyul/AsmPrinter.h> +#include <liblangutil/SourceReferenceFormatter.h> #include <libyul/optimiser/BlockFlattener.h> #include <libyul/optimiser/Disambiguator.h> @@ -38,6 +38,7 @@ #include <libyul/optimiser/FunctionHoister.h> #include <libyul/optimiser/ExpressionInliner.h> #include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/ForLoopInitRewriter.h> #include <libyul/optimiser/MainFunction.h> #include <libyul/optimiser/Rematerialiser.h> #include <libyul/optimiser/ExpressionSimplifier.h> @@ -57,9 +58,9 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; -using namespace dev::solidity::assembly; -using namespace dev::yul; +using namespace yul; namespace po = boost::program_options; @@ -80,15 +81,15 @@ public: bool parse(string const& _input) { ErrorReporter errorReporter(m_errors); - shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(_input), ""); - m_ast = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false); + shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(_input, "")); + m_ast = yul::Parser(errorReporter, yul::AsmFlavour::Strict).parse(scanner, false); if (!m_ast || !errorReporter.errors().empty()) { cout << "Error parsing source." << endl; printErrors(*scanner); return false; } - m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + m_analysisInfo = make_shared<yul::AsmAnalysisInfo>(); AsmAnalyzer analyzer( *m_analysisInfo, errorReporter, @@ -116,14 +117,14 @@ public: return; if (!disambiguated) { - *m_ast = boost::get<assembly::Block>(Disambiguator(*m_analysisInfo)(*m_ast)); + *m_ast = boost::get<yul::Block>(Disambiguator(*m_analysisInfo)(*m_ast)); m_analysisInfo.reset(); m_nameDispenser = make_shared<NameDispenser>(*m_ast); disambiguated = true; } cout << "(q)quit/(f)flatten/(c)se/propagate var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; - cout << " (r)edundant assign elim./re(m)aterializer? "; + cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter? "; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -134,6 +135,9 @@ public: case 'f': BlockFlattener{}(*m_ast); break; + case 'o': + ForLoopInitRewriter{}(*m_ast); + break; case 'c': (CommonSubexpressionEliminator{})(*m_ast); break; @@ -182,7 +186,7 @@ public: private: ErrorList m_errors; - shared_ptr<assembly::Block> m_ast; + shared_ptr<yul::Block> m_ast; shared_ptr<AsmAnalysisInfo> m_analysisInfo; shared_ptr<NameDispenser> m_nameDispenser; }; |