diff --git a/.circleci/config.yml b/.circleci/config.yml index 054a60a28..4bf85f60c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,20 +9,20 @@ version: 2.1 parameters: ubuntu-2004-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-6 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:da44d7f78e093f7f0415abf07f7c1fd1c2ed4fa65fefea428821a05186c42ec9" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-8 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9c3cdfc1d573d1ca3edacd892590a9a83487a1f746a6ca2093d7e009818c5179" ubuntu-2004-clang-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-6 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c78dd9c48d393b57afe053aeb2d0d358a9f31ac85039a181724c2f8408d0bcf8" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-8 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:61232feea23c8c57e82cf5fae890f8b86bbec353cdc04f2fcba383ca589e1d8b" ubuntu-1604-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-9 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:5078e1d74ab6f4329e9218c2d8c0ebe2d42817a3d4c3c62ce887100cbe9bc739" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-11 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:4acb2674eab3e7939d6dc6caa0b8320f4dd79484325242b58473ca2875792d90" emscripten-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:emscripten-5 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d28afb9624c2352ea40f157d1a321ffac77f54a21e33a8e8744f9126b780ded4" + # solbuildpackpusher/solidity-buildpack-deps:emscripten-6 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154" orbs: win: circleci/windows@2.2.0 @@ -811,7 +811,7 @@ jobs: t_ems_solcjs: docker: - - image: buildpack-deps:latest + - image: << pipeline.parameters.ubuntu-2004-docker-image >> environment: TERM: xterm steps: @@ -822,7 +822,7 @@ jobs: name: Install test dependencies command: | apt-get update - apt-get install -qqy --no-install-recommends nodejs npm cvc4 + apt-get install -qqy --no-install-recommends nodejs npm - run: name: Test solcjs no_output_timeout: 30m diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index f1dd4f11b..0505aa6db 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -48,21 +48,23 @@ then ./scripts/install_obsolete_jsoncpp_1_7_4.sh # z3 - wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.10/z3-4.8.10-x64-osx-10.15.7.zip - unzip z3-4.8.10-x64-osx-10.15.7.zip - rm -f z3-4.8.10-x64-osx-10.15.7.zip - cp z3-4.8.10-x64-osx-10.15.7/bin/libz3.a /usr/local/lib - cp z3-4.8.10-x64-osx-10.15.7/bin/z3 /usr/local/bin - cp z3-4.8.10-x64-osx-10.15.7/include/* /usr/local/include - rm -rf z3-4.8.10-x64-osx-10.15.7 + z3_version="z3-4.8.12" + osx_version="osx-10.15.7" + wget "https://github.com/Z3Prover/z3/releases/download/$z3_version/$z3_version-x64-$osx_version.zip" + unzip "$z3_version-x64-$osx_version.zip" + rm -f "$z3_version-x64-$osx_version.zip" + cp "$z3_version-x64-$osx_version/bin/libz3.a" /usr/local/lib + cp "$z3_version-x64-$osx_version/bin/z3" /usr/local/bin + cp "$z3_version-x64-$osx_version"/include/* /usr/local/include + rm -rf "$z3_version-x64-$osx_version" # evmone - wget https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-darwin-x86_64.tar.gz - tar xzpf evmone-0.7.0-darwin-x86_64.tar.gz -C /usr/local - rm -f evmone-0.7.0-darwin-x86_64.tar.gz + wget https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-darwin-x86_64.tar.gz + tar xzpf evmone-0.8.0-darwin-x86_64.tar.gz -C /usr/local + rm -f evmone-0.8.0-darwin-x86_64.tar.gz # hera - wget https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz - tar xzpf hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz -C /usr/local - rm -f hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz + wget https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-darwin-x86_64.tar.gz + tar xzpf hera-0.5.0-darwin-x86_64.tar.gz -C /usr/local + rm -f hera-0.5.0-darwin-x86_64.tar.gz fi diff --git a/Changelog.md b/Changelog.md index 2f10594ea..583da3cbb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,14 +14,22 @@ Compiler Features: * Yul EVM Code Transform: Do not reuse stack slots that immediately become unreachable. * Yul EVM Code Transform: Also pop unused argument slots for functions without return variables (under the same restrictions as for functions with return variables). * Yul Optimizer: Move function arguments and return variables to memory with the experimental Stack Limit Evader (which is not enabled by default). + * Commandline Interface: option ``--pretty-json`` works also with ``--standard--json``. + * SMTChecker: Unproved targets are hidden by default, and the SMTChecker only states how many unproved targets there are. They can be listed using the command line option ``--model-checker-show-unproved`` or the JSON option ``settings.modelChecker.showUnproved``. Bugfixes: * Code Generator: Fix crash when passing an empty string literal to ``bytes.concat()``. * Code Generator: Fix internal compiler error when calling functions bound to calldata structs and arrays. * Code Generator: Fix internal compiler error when passing a 32-byte hex literal or a zero literal to ``bytes.concat()`` by disallowing such literals. + * Commandline Interface: Fix crash when a directory path is passed to ``--standard-json``. + * Commandline Interface: Read JSON from standard input when ``--standard-json`` gets ``-`` as a file name. + * Standard JSON: Include source location for errors in files with empty name. * Type Checker: Fix internal error and prevent static calls to unimplemented modifiers. * Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation. + * Yul Code Generator: Fix source location references for calls to builtin functions. + * Yul Parser: Fix source location references for ``if`` statements. + * Commandline Interface: Apply ``--optimizer-runs`` option in assembly / yul mode. ### 0.8.6 (2021-06-22) diff --git a/docs/060-breaking-changes.rst b/docs/060-breaking-changes.rst index 32397677c..915a9f335 100644 --- a/docs/060-breaking-changes.rst +++ b/docs/060-breaking-changes.rst @@ -89,14 +89,14 @@ New Features This section lists things that were not possible prior to Solidity 0.6.0 or were more difficult to achieve. - * The :ref:`try/catch statement ` allows you to react on failed external calls. - * ``struct`` and ``enum`` types can be declared at file level. - * Array slices can be used for calldata arrays, for example ``abi.decode(msg.data[4:], (uint, uint))`` - is a low-level way to decode the function call payload. - * Natspec supports multiple return parameters in developer documentation, enforcing the same naming check as ``@param``. - * Yul and Inline Assembly have a new statement called ``leave`` that exits the current function. - * Conversions from ``address`` to ``address payable`` are now possible via ``payable(x)``, where - ``x`` must be of type ``address``. +* The :ref:`try/catch statement ` allows you to react on failed external calls. +* ``struct`` and ``enum`` types can be declared at file level. +* Array slices can be used for calldata arrays, for example ``abi.decode(msg.data[4:], (uint, uint))`` + is a low-level way to decode the function call payload. +* Natspec supports multiple return parameters in developer documentation, enforcing the same naming check as ``@param``. +* Yul and Inline Assembly have a new statement called ``leave`` that exits the current function. +* Conversions from ``address`` to ``address payable`` are now possible via ``payable(x)``, where + ``x`` must be of type ``address``. Interface Changes diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index d412a2a2e..1b61351fa 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -77,8 +77,9 @@ The following (fixed-size) array type exists: - ``[M]``: a fixed-length array of ``M`` elements, ``M >= 0``, of the given type. - .. note:: - While this ABI specification can express fixed-length arrays with zero elements, they're not supported by the compiler. + .. note:: + + While this ABI specification can express fixed-length arrays with zero elements, they're not supported by the compiler. The following non-fixed-size types exist: @@ -124,13 +125,13 @@ Design Criteria for the Encoding The encoding is designed to have the following properties, which are especially useful if some arguments are nested arrays: - 1. The number of reads necessary to access a value is at most the depth of the value - inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a - previous version of the ABI, the number of reads scaled linearly with the total number of dynamic - parameters in the worst case. +1. The number of reads necessary to access a value is at most the depth of the value + inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a + previous version of the ABI, the number of reads scaled linearly with the total number of dynamic + parameters in the worst case. - 2. The data of a variable or array element is not interleaved with other data and it is - relocatable, i.e. it only uses relative "addresses". +2. The data of a variable or array element is not interleaved with other data and it is + relocatable, i.e. it only uses relative "addresses". Formal Specification of the Encoding @@ -236,6 +237,7 @@ Examples Given the contract: .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.16 <0.9.0; @@ -312,21 +314,21 @@ these are directly the values we want to pass, whereas for the dynamic types ``u we use the offset in bytes to the start of their data area, measured from the start of the value encoding (i.e. not counting the first four bytes containing the hash of the function signature). These are: - - ``0x0000000000000000000000000000000000000000000000000000000000000123`` (``0x123`` padded to 32 bytes) - - ``0x0000000000000000000000000000000000000000000000000000000000000080`` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part) - - ``0x3132333435363738393000000000000000000000000000000000000000000000`` (``"1234567890"`` padded to 32 bytes on the right) - - ``0x00000000000000000000000000000000000000000000000000000000000000e0`` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below)) +- ``0x0000000000000000000000000000000000000000000000000000000000000123`` (``0x123`` padded to 32 bytes) +- ``0x0000000000000000000000000000000000000000000000000000000000000080`` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part) +- ``0x3132333435363738393000000000000000000000000000000000000000000000`` (``"1234567890"`` padded to 32 bytes on the right) +- ``0x00000000000000000000000000000000000000000000000000000000000000e0`` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below)) After this, the data part of the first dynamic argument, ``[0x456, 0x789]`` follows: - - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements of the array, 2) - - ``0x0000000000000000000000000000000000000000000000000000000000000456`` (first element) - - ``0x0000000000000000000000000000000000000000000000000000000000000789`` (second element) +- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements of the array, 2) +- ``0x0000000000000000000000000000000000000000000000000000000000000456`` (first element) +- ``0x0000000000000000000000000000000000000000000000000000000000000789`` (second element) Finally, we encode the data part of the second dynamic argument, ``"Hello, world!"``: - - ``0x000000000000000000000000000000000000000000000000000000000000000d`` (number of elements (bytes in this case): 13) - - ``0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000`` (``"Hello, world!"`` padded to 32 bytes on the right) +- ``0x000000000000000000000000000000000000000000000000000000000000000d`` (number of elements (bytes in this case): 13) +- ``0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000`` (``"Hello, world!"`` padded to 32 bytes on the right) All together, the encoding is (newline after function selector and each 32-bytes for clarity): @@ -348,14 +350,14 @@ with values ``([[1, 2], [3]], ["one", "two", "three"])`` but start from the most First we encode the length and data of the first embedded dynamic array ``[1, 2]`` of the first root array ``[[1, 2], [3]]``: - - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first array, 2; the elements themselves are ``1`` and ``2``) - - ``0x0000000000000000000000000000000000000000000000000000000000000001`` (first element) - - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (second element) +- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first array, 2; the elements themselves are ``1`` and ``2``) +- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (first element) +- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (second element) Then we encode the length and data of the second embedded dynamic array ``[3]`` of the first root array ``[[1, 2], [3]]``: - - ``0x0000000000000000000000000000000000000000000000000000000000000001`` (number of elements in the second array, 1; the element is ``3``) - - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (first element) +- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (number of elements in the second array, 1; the element is ``3``) +- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (first element) Then we need to find the offsets ``a`` and ``b`` for their respective dynamic arrays ``[1, 2]`` and ``[3]``. To calculate the offsets we can take a look at the encoded data of the first root array ``[[1, 2], [3]]`` @@ -380,12 +382,12 @@ thus ``b = 0x00000000000000000000000000000000000000000000000000000000000000a0``. Then we encode the embedded strings of the second root array: - - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"one"``) - - ``0x6f6e650000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"one"``) - - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"two"``) - - ``0x74776f0000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"two"``) - - ``0x0000000000000000000000000000000000000000000000000000000000000005`` (number of characters in word ``"three"``) - - ``0x7468726565000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"three"``) +- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"one"``) +- ``0x6f6e650000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"one"``) +- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"two"``) +- ``0x74776f0000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"two"``) +- ``0x0000000000000000000000000000000000000000000000000000000000000005`` (number of characters in word ``"three"``) +- ``0x7468726565000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"three"``) In parallel to the first root array, since strings are dynamic elements we need to find their offsets ``c``, ``d`` and ``e``: @@ -416,11 +418,11 @@ and have the same encodings for a function with a signature ``g(string[],uint[][ Then we encode the length of the first root array: - - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first root array, 2; the elements themselves are ``[1, 2]`` and ``[3]``) +- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first root array, 2; the elements themselves are ``[1, 2]`` and ``[3]``) Then we encode the length of the second root array: - - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of strings in the second root array, 3; the strings themselves are ``"one"``, ``"two"`` and ``"three"``) +- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of strings in the second root array, 3; the strings themselves are ``"one"``, ``"two"`` and ``"three"``) Finally we find the offsets ``f`` and ``g`` for their respective root dynamic arrays ``[[1, 2], [3]]`` and ``["one", "two", "three"]``, and assemble parts in the correct order: @@ -529,12 +531,12 @@ i.e. ``0xcf479181``, ``uint256(0)``, ``uint256(amount)``. The error selectors ``0x00000000`` and ``0xffffffff`` are reserved for future use. .. warning:: - Never trust error data. - The error data by default bubbles up through the chain of external calls, which - means that a contract may receive an error not defined in any of the contracts - it calls directly. - Furthermore, any contract can fake any error by returning data that matches - an error signature, even if the error is not defined anywhere. + Never trust error data. + The error data by default bubbles up through the chain of external calls, which + means that a contract may receive an error not defined in any of the contracts + it calls directly. + Furthermore, any contract can fake any error by returning data that matches + an error signature, even if the error is not defined anywhere. .. _abi_json: @@ -618,24 +620,24 @@ would result in the JSON: .. code-block:: json - [{ - "type":"error", - "inputs": [{"name":"available","type":"uint256"},{"name":"required","type":"uint256"}], - "name":"InsufficientBalance" - }, { - "type":"event", - "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], - "name":"Event" - }, { - "type":"event", - "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], - "name":"Event2" - }, { - "type":"function", - "inputs": [{"name":"a","type":"uint256"}], - "name":"foo", - "outputs": [] - }] + [{ + "type":"error", + "inputs": [{"name":"available","type":"uint256"},{"name":"required","type":"uint256"}], + "name":"InsufficientBalance" + }, { + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event" + }, { + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event2" + }, { + "type":"function", + "inputs": [{"name":"a","type":"uint256"}], + "name":"foo", + "outputs": [] + }] Handling tuple types -------------------- @@ -656,7 +658,7 @@ As an example, the code .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.4 <0.9.0; + pragma solidity >=0.7.5 <0.9.0; pragma abicoder v2; contract Test { @@ -670,61 +672,61 @@ would result in the JSON: .. code-block:: json - [ - { - "name": "f", - "type": "function", - "inputs": [ - { - "name": "s", - "type": "tuple", - "components": [ - { - "name": "a", - "type": "uint256" - }, - { - "name": "b", - "type": "uint256[]" - }, - { - "name": "c", - "type": "tuple[]", - "components": [ - { - "name": "x", - "type": "uint256" - }, - { - "name": "y", - "type": "uint256" - } - ] - } - ] - }, - { - "name": "t", - "type": "tuple", - "components": [ - { - "name": "x", - "type": "uint256" - }, - { - "name": "y", - "type": "uint256" - } - ] - }, - { - "name": "a", - "type": "uint256" - } - ], - "outputs": [] - } - ] + [ + { + "name": "f", + "type": "function", + "inputs": [ + { + "name": "s", + "type": "tuple", + "components": [ + { + "name": "a", + "type": "uint256" + }, + { + "name": "b", + "type": "uint256[]" + }, + { + "name": "c", + "type": "tuple[]", + "components": [ + { + "name": "x", + "type": "uint256" + }, + { + "name": "y", + "type": "uint256" + } + ] + } + ] + }, + { + "name": "t", + "type": "tuple", + "components": [ + { + "name": "x", + "type": "uint256" + }, + { + "name": "y", + "type": "uint256" + } + ] + }, + { + "name": "a", + "type": "uint256" + } + ], + "outputs": [] + } + ] .. _abi_packed_mode: @@ -761,18 +763,19 @@ As an example, the encoding of ``int16(-1), bytes1(0x42), uint16(0x03), string(" ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field More specifically: - - During the encoding, everything is encoded in-place. This means that there is - no distinction between head and tail, as in the ABI encoding, and the length - of an array is not encoded. - - The direct arguments of ``abi.encodePacked`` are encoded without padding, - as long as they are not arrays (or ``string`` or ``bytes``). - - The encoding of an array is the concatenation of the - encoding of its elements **with** padding. - - Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded - without their length field. - - The encoding of ``string`` or ``bytes`` does not apply padding at the end - unless it is part of an array or struct (then it is padded to a multiple of - 32 bytes). + +- During the encoding, everything is encoded in-place. This means that there is + no distinction between head and tail, as in the ABI encoding, and the length + of an array is not encoded. +- The direct arguments of ``abi.encodePacked`` are encoded without padding, + as long as they are not arrays (or ``string`` or ``bytes``). +- The encoding of an array is the concatenation of the + encoding of its elements **with** padding. +- Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded + without their length field. +- The encoding of ``string`` or ``bytes`` does not apply padding at the end + unless it is part of an array or struct (then it is padded to a multiple of + 32 bytes). In general, the encoding is ambiguous as soon as there are two dynamically-sized elements, because of the missing length field. @@ -784,12 +787,12 @@ for prepending a function selector. Since the encoding is ambiguous, there is no .. warning:: - If you use ``keccak256(abi.encodePacked(a, b))`` and both ``a`` and ``b`` are dynamic types, - it is easy to craft collisions in the hash value by moving parts of ``a`` into ``b`` and - vice-versa. More specifically, ``abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c")``. - If you use ``abi.encodePacked`` for signatures, authentication or data integrity, make - sure to always use the same types and check that at most one of them is dynamic. - Unless there is a compelling reason, ``abi.encode`` should be preferred. + If you use ``keccak256(abi.encodePacked(a, b))`` and both ``a`` and ``b`` are dynamic types, + it is easy to craft collisions in the hash value by moving parts of ``a`` into ``b`` and + vice-versa. More specifically, ``abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c")``. + If you use ``abi.encodePacked`` for signatures, authentication or data integrity, make + sure to always use the same types and check that at most one of them is dynamic. + Unless there is a compelling reason, ``abi.encode`` should be preferred. .. _indexed_event_encoding: @@ -801,13 +804,13 @@ Indexed event parameters that are not value types, i.e. arrays and structs are n stored directly but instead a keccak256-hash of an encoding is stored. This encoding is defined as follows: - - the encoding of a ``bytes`` and ``string`` value is just the string contents - without any padding or length prefix. - - the encoding of a struct is the concatenation of the encoding of its members, - always padded to a multiple of 32 bytes (even ``bytes`` and ``string``). - - the encoding of an array (both dynamically- and statically-sized) is - the concatenation of the encoding of its elements, always padded to a multiple - of 32 bytes (even ``bytes`` and ``string``) and without any length prefix +- the encoding of a ``bytes`` and ``string`` value is just the string contents + without any padding or length prefix. +- the encoding of a struct is the concatenation of the encoding of its members, + always padded to a multiple of 32 bytes (even ``bytes`` and ``string``). +- the encoding of an array (both dynamically- and statically-sized) is + the concatenation of the encoding of its elements, always padded to a multiple + of 32 bytes (even ``bytes`` and ``string``) and without any length prefix In the above, as usual, a negative number is padded by sign extension and not zero padded. ``bytesNN`` types are padded on the right while ``uintNN`` / ``intNN`` are padded on the left. diff --git a/docs/assembly.rst b/docs/assembly.rst index 4e35ca702..9da8ebb8b 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -234,7 +234,7 @@ This means that the allocatable memory starts at ``0x80``, which is the initial of the free memory pointer. Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this is -even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory +even true for ``bytes1[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory arrays are pointers to memory arrays. The length of a dynamic array is stored at the first slot of the array and followed by the array elements. diff --git a/docs/bugs.rst b/docs/bugs.rst index 73700adf3..75a23e499 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -19,16 +19,16 @@ which can be used to check which bugs affect a specific version of the compiler. Contract source verification tools and also other tools interacting with contracts should consult this list according to the following criteria: - - It is mildly suspicious if a contract was compiled with a nightly - compiler version instead of a released version. This list does not keep - track of unreleased or nightly versions. - - It is also mildly suspicious if a contract was compiled with a version that was - not the most recent at the time the contract was created. For contracts - created from other contracts, you have to follow the creation chain - back to a transaction and use the date of that transaction as creation date. - - It is highly suspicious if a contract was compiled with a compiler that - contains a known bug and the contract was created at a time where a newer - compiler version containing a fix was already released. +- It is mildly suspicious if a contract was compiled with a nightly + compiler version instead of a released version. This list does not keep + track of unreleased or nightly versions. +- It is also mildly suspicious if a contract was compiled with a version that was + not the most recent at the time the contract was created. For contracts + created from other contracts, you have to follow the creation chain + back to a transaction and use the date of that transaction as creation date. +- It is highly suspicious if a contract was compiled with a compiler that + contains a known bug and the contract was created at a time where a newer + compiler version containing a fix was already released. The JSON file of known bugs below is an array of objects, one for each bug, with the following keys: diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 10a7ce105..c39209e37 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -158,7 +158,8 @@ Global Variables Function Visibility Specifiers ============================== -:: +.. code-block:: solidity + :force: function myFunction() returns (bool) { return true; @@ -192,8 +193,8 @@ Reserved Keywords These keywords are reserved in Solidity. They might become part of the syntax in the future: -``after``, ``alias``, ``apply``, ``auto``, ``case``, ``copyof``, ``default``, -``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, +``after``, ``alias``, ``apply``, ``auto``, ``byte``, ``case``, ``copyof``, ``default``, +``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, ``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``, ``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``, -``unchecked``. +``var``. diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 58fb39ea2..86c81dbfb 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -130,6 +130,7 @@ The use of **function modifiers** makes these restrictions highly readable. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; @@ -293,6 +294,7 @@ function finishes. will run even if the function explicitly returns. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; diff --git a/docs/conf.py b/docs/conf.py index 4c7a67164..573da2465 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,9 +23,11 @@ from pygments_lexer_solidity import SolidityLexer, YulLexer # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. +ROOT_PATH = os.path.dirname(os.path.realpath(__file__)) + +sys.path.insert(0, os.path.join(ROOT_PATH, 'ext')) + def setup(sphinx): - thisdir = os.path.dirname(os.path.realpath(__file__)) - sys.path.insert(0, thisdir + '/utils') sphinx.add_lexer('Solidity', SolidityLexer) sphinx.add_lexer('Yul', YulLexer) @@ -39,7 +41,10 @@ def setup(sphinx): # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ 'sphinx_a4doc' ] +extensions = [ + 'sphinx_a4doc', + 'html_extra_template_renderer', +] a4_base_path = os.path.dirname(__file__) + '/grammar' @@ -156,7 +161,20 @@ html_js_files = ["js/toggle.js"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -html_extra_path = ["_static/css", "_static/robots.txt"] +html_extra_path = ["_static/css"] + +# List of templates of static files to be included in the HTML output. +# Keys represent paths to input files and values are dicts containing: +# - target: The path where the rendered template should be placed. +# - context: A dictionary listing variables that can be used inside the template. +# All paths must be absolute. +# Rendered templates are automatically added to html_extra_path setting. +html_extra_templates = { + os.path.join(ROOT_PATH, "robots.txt.template"): { + 'target': os.path.join(ROOT_PATH, "_static/robots.txt"), + 'context': {'LATEST_VERSION': version}, + } +} # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index aae16fb8b..6e99a332b 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -73,7 +73,7 @@ four indexed arguments rather than three. In particular, it is possible to "fake" the signature of another event using an anonymous event. -:: +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.21 <0.9.0; @@ -97,7 +97,7 @@ four indexed arguments rather than three. The use in the JavaScript API is as follows: -:: +.. code-block:: javascript var abi = /* abi as generated by the compiler */; var ClientReceipt = web3.eth.contract(abi); @@ -124,17 +124,17 @@ The output of the above looks like the following (trimmed): .. code-block:: json - { - "returnValues": { - "_from": "0x1111…FFFFCCCC", - "_id": "0x50…sd5adb20", - "_value": "0x420042" - }, - "raw": { - "data": "0x7f…91385", - "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] - } - } + { + "returnValues": { + "_from": "0x1111…FFFFCCCC", + "_id": "0x50…sd5adb20", + "_value": "0x420042" + }, + "raw": { + "data": "0x7f…91385", + "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] + } + } Additional Resources for Understanding Events ============================================== diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst index 262d80321..0d4c8e8c2 100644 --- a/docs/contracts/function-modifiers.rst +++ b/docs/contracts/function-modifiers.rst @@ -18,7 +18,7 @@ if they are marked ``virtual``. For details, please see .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0 <0.9.0; + pragma solidity >=0.7.1 <0.9.0; contract owned { constructor() { owner = payable(msg.sender); } diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 56bc85a05..93283e62c 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -15,7 +15,7 @@ that call them, similar to internal library functions. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0 <0.9.0; + pragma solidity >=0.7.1 <0.9.0; function sum(uint[] memory _arr) pure returns (uint s) { for (uint i = 0; i < _arr.length; i++) diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index eee9de765..4f0f03fec 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -130,9 +130,10 @@ internal functions in libraries in order to implement custom types without the overhead of external function calls: .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.8 <0.9.0; + pragma solidity ^0.8.0; struct bigint { uint[] limbs; @@ -150,11 +151,14 @@ custom types without the overhead of external function calls: for (uint i = 0; i < r.limbs.length; ++i) { uint a = limb(_a, i); uint b = limb(_b, i); - r.limbs[i] = a + b + carry; - if (a + b < a || (a + b == type(uint).max && carry > 0)) - carry = 1; - else - carry = 0; + unchecked { + r.limbs[i] = a + b + carry; + + if (a + b < a || (a + b == type(uint).max && carry > 0)) + carry = 1; + else + carry = 0; + } } if (carry > 0) { // too bad, we have to add a limb @@ -223,14 +227,14 @@ following an internal naming schema and arguments of types not supported in the The following identifiers are used for the types in the signatures: - - Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI. - - Non-storage array types follow the same convention as in the contract ABI, i.e. ``[]`` for dynamic arrays and - ``[M]`` for fixed-size arrays of ``M`` elements. - - Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``. - - Storage pointer mappings use ``mapping( => ) storage`` where ```` and ```` are - the identifiers for the key and value types of the mapping, respectively. - - Other storage pointer types use the type identifier of their corresponding non-storage type, but append a single space - followed by ``storage`` to it. +- Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI. +- Non-storage array types follow the same convention as in the contract ABI, i.e. ``[]`` for dynamic arrays and + ``[M]`` for fixed-size arrays of ``M`` elements. +- Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``. +- Storage pointer mappings use ``mapping( => ) storage`` where ```` and ```` are + the identifiers for the key and value types of the mapping, respectively. +- Other storage pointer types use the type identifier of their corresponding non-storage type, but append a single space + followed by ``storage`` to it. The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a ``uint256`` value referring to the storage slot to which they point. diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index a865f54d4..f04945cfd 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -188,16 +188,24 @@ The next example is more complex: uint a; bytes3 b; mapping (uint => uint) map; + uint[3] c; + uint[] d; + bytes e; } mapping (uint => mapping(bool => Data[])) public data; } -It generates a function of the following form. The mapping in the struct is omitted -because there is no good way to provide the key for the mapping: +It generates a function of the following form. The mapping and arrays (with the +exception of byte arrays) in the struct are omitted because there is no good way +to select individual struct members or provide a key for the mapping: .. code-block:: solidity - function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) { + function data(uint arg1, bool arg2, uint arg3) + public + returns (uint a, bytes3 b, bytes memory e) + { a = data[arg1][arg2][arg3].a; b = data[arg1][arg2][arg3].b; + e = data[arg1][arg2][arg3].e; } diff --git a/docs/contributing.rst b/docs/contributing.rst index eaae6db3e..1d621d4a9 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -112,7 +112,7 @@ starting from the current directory. The required file is called ``libevmone.so` ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``, ``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from -`GitHub `_ +`GitHub `_ and place it in the project root path or inside the ``deps`` folder. If the ``libz3`` library is not installed on your system, you should disable the @@ -324,7 +324,7 @@ from the documentation or the other tests: # extract from tests: path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp # extract from documentation: - path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs docs + path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs The AFL documentation states that the corpus (the initial input files) should not be too large. The files themselves should not be larger than 1 kB and there should be diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 9da26ca26..95a55f6c6 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -660,6 +660,7 @@ The following example shows how you can use ``require`` to check conditions on i and ``assert`` for internal error checking. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0 <0.9.0; @@ -786,7 +787,7 @@ A failure in an external call can be caught using a try/catch statement, as foll .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + pragma solidity >=0.8.1; interface DataFeed { function getData(address token) external returns (uint value); } diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst index ce72cdf18..c00bbf212 100644 --- a/docs/examples/blind-auction.rst +++ b/docs/examples/blind-auction.rst @@ -192,6 +192,7 @@ invalid bids. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; diff --git a/docs/ext/html_extra_template_renderer.py b/docs/ext/html_extra_template_renderer.py new file mode 100644 index 000000000..a48f20e5e --- /dev/null +++ b/docs/ext/html_extra_template_renderer.py @@ -0,0 +1,45 @@ +import os.path + + +def render_html_extra_templates(app): + if app.builder.format != 'html': + # Non-HTML builders do not provide .templates.render_string(). Note that a HTML + # builder is still used also when building some other formats like json or epub. + return + + for input_path, template_config in app.config.html_extra_templates.items(): + # Requiring absolute paths simplifies the implementation. + if not os.path.isabs(input_path): + raise RuntimeError(f"Template input path is not absolute: {input_path}") + if not os.path.isabs(template_config['target']): + raise RuntimeError(f"Template target path is not absolute: {template_config['target']}") + + with open(input_path, 'r') as input_file: + # This runs Jinja2, which supports rendering {{ }} tags among other things. + rendered_template = app.builder.templates.render_string( + input_file.read(), + template_config['context'], + ) + + with open(template_config['target'], 'w') as target_file: + target_file.write(rendered_template) + + app.config.html_extra_path.append(template_config['target']) + + +def setup(app): + app.add_config_value('html_extra_templates', default={}, rebuild='', types=dict) + + # Register a handler for the env-before-read-docs event. Any event that's fired before static + # files get copied would do. + app.connect( + 'env-before-read-docs', + lambda app, env, docnames: render_html_extra_templates(app) + ) + + return { + # NOTE: Need to access _raw_config here because setup() runs before app.config is ready. + 'version': app.config._raw_config['version'], # pylint: disable=protected-access + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/docs/internals/layout_in_memory.rst b/docs/internals/layout_in_memory.rst index 66623572f..8c20b6252 100644 --- a/docs/internals/layout_in_memory.rst +++ b/docs/internals/layout_in_memory.rst @@ -19,7 +19,7 @@ Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future). Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this -is even true for ``byte[]``, but not for ``bytes`` and ``string``). +is even true for ``bytes1[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory arrays are pointers to memory arrays. The length of a dynamic array is stored at the first slot of the array and followed by the array elements. diff --git a/docs/internals/layout_in_storage.rst b/docs/internals/layout_in_storage.rst index a9de8f6c9..b1b89a6a3 100644 --- a/docs/internals/layout_in_storage.rst +++ b/docs/internals/layout_in_storage.rst @@ -127,7 +127,7 @@ The type of the value is ``uint256``, so it uses a single slot. ------------------------ ``bytes`` and ``string`` are encoded identically. -In general, the encoding is similar to ``byte1[]``, in the sense that there is a slot for the array itself and +In general, the encoding is similar to ``bytes1[]``, in the sense that there is a slot for the array itself and a data area that is computed using a ``keccak256`` hash of that slot's position. However, for short values (shorter than 32 bytes) the array elements are stored together with the length in the same slot. diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index c1107d1b4..64684046c 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -49,6 +49,8 @@ differences, for example, functions may be inlined, combined, or rewritten to el redundancies, etc. (compare the output between the flags ``--ir`` and ``--optimize --ir-optimized``). +.. _optimizer-parameter-runs: + Optimizer Parameter Runs ======================== @@ -89,21 +91,21 @@ the sequence: .. code-block:: none - PUSH 32 - PUSH 0 - CALLDATALOAD - PUSH 100 - DUP2 - MSTORE - KECCAK256 + PUSH 32 + PUSH 0 + CALLDATALOAD + PUSH 100 + DUP2 + MSTORE + KECCAK256 or the equivalent Yul .. code-block:: yul - let x := calldataload(0) - mstore(x, 100) - let value := keccak256(x, 32) + let x := calldataload(0) + mstore(x, 100) + let value := keccak256(x, 32) In this case, the optimizer tracks the value at a memory location ``calldataload(0)`` and then realizes that the Keccak-256 hash can be evaluated at compile time. This only works if there is no @@ -116,14 +118,14 @@ For example, .. code-block:: yul - let x := calldataload(0) - mstore(x, 100) - // Current knowledge memory location x -> 100 - let y := add(x, 32) - // Does not clear the knowledge that x -> 100, since y does not write to [x, x + 32) - mstore(y, 200) - // This Keccak-256 can now be evaluated - let value := keccak256(x, 32) + let x := calldataload(0) + mstore(x, 100) + // Current knowledge memory location x -> 100 + let y := add(x, 32) + // Does not clear the knowledge that x -> 100, since y does not write to [x, x + 32) + mstore(y, 200) + // This Keccak-256 can now be evaluated + let value := keccak256(x, 32) Therefore, modifications to storage and memory locations, of say location ``l``, must erase knowledge about storage or memory locations which may be equal to ``l``. More specifically, for @@ -239,8 +241,8 @@ for all references to ``tag_f`` leaving it unused, s.t. it can be removed, yield .. code-block:: text - ...body of function f... - ...opcodes after call to f... + ...body of function f... + ...opcodes after call to f... So the call to function ``f`` is inlined and the original definition of ``f`` can be removed. @@ -269,11 +271,11 @@ backtracking. All components of the Yul-based optimizer module are explained below. The following transformation steps are the main components: - - SSA Transform - - Common Subexpression Eliminator - - Expression Simplifier - - Redundant Assign Eliminator - - Full Function Inliner +- SSA Transform +- Common Subexpression Eliminator +- Expression Simplifier +- Redundant Assign Eliminator +- Full Function Inliner Optimizer Steps --------------- @@ -281,36 +283,36 @@ Optimizer Steps This is a list of all steps the Yul-based optimizer sorted alphabetically. You can find more information on the individual steps and their sequence below. - - :ref:`block-flattener`. - - :ref:`circular-reference-pruner`. - - :ref:`common-subexpression-eliminator`. - - :ref:`conditional-simplifier`. - - :ref:`conditional-unsimplifier`. - - :ref:`control-flow-simplifier`. - - :ref:`dead-code-eliminator`. - - :ref:`equivalent-function-combiner`. - - :ref:`expression-joiner`. - - :ref:`expression-simplifier`. - - :ref:`expression-splitter`. - - :ref:`for-loop-condition-into-body`. - - :ref:`for-loop-condition-out-of-body`. - - :ref:`for-loop-init-rewriter`. - - :ref:`functional-inliner`. - - :ref:`function-grouper`. - - :ref:`function-hoister`. - - :ref:`function-specializer`. - - :ref:`literal-rematerialiser`. - - :ref:`load-resolver`. - - :ref:`loop-invariant-code-motion`. - - :ref:`redundant-assign-eliminator`. - - :ref:`reasoning-based-simplifier`. - - :ref:`rematerialiser`. - - :ref:`SSA-reverser`. - - :ref:`SSA-transform`. - - :ref:`structural-simplifier`. - - :ref:`unused-function-parameter-pruner`. - - :ref:`unused-pruner`. - - :ref:`var-decl-initializer`. +- :ref:`block-flattener`. +- :ref:`circular-reference-pruner`. +- :ref:`common-subexpression-eliminator`. +- :ref:`conditional-simplifier`. +- :ref:`conditional-unsimplifier`. +- :ref:`control-flow-simplifier`. +- :ref:`dead-code-eliminator`. +- :ref:`equivalent-function-combiner`. +- :ref:`expression-joiner`. +- :ref:`expression-simplifier`. +- :ref:`expression-splitter`. +- :ref:`for-loop-condition-into-body`. +- :ref:`for-loop-condition-out-of-body`. +- :ref:`for-loop-init-rewriter`. +- :ref:`functional-inliner`. +- :ref:`function-grouper`. +- :ref:`function-hoister`. +- :ref:`function-specializer`. +- :ref:`literal-rematerialiser`. +- :ref:`load-resolver`. +- :ref:`loop-invariant-code-motion`. +- :ref:`redundant-assign-eliminator`. +- :ref:`reasoning-based-simplifier`. +- :ref:`rematerialiser`. +- :ref:`SSA-reverser`. +- :ref:`SSA-transform`. +- :ref:`structural-simplifier`. +- :ref:`unused-function-parameter-pruner`. +- :ref:`unused-pruner`. +- :ref:`var-decl-initializer`. Selecting Optimizations ----------------------- @@ -375,7 +377,7 @@ After this step, a program has the following normal form: .. code-block:: text - { I F... } + { I F... } Where ``I`` is a (potentially empty) block that does not contain any function definitions (not even recursively) and ``F`` is a list of function definitions such that no function contains a function definition. @@ -519,7 +521,7 @@ compact again at the end. ExpressionSplitter ^^^^^^^^^^^^^^^^^^ -The expression splitter turns expressions like ``add(mload(x), mul(mload(y), 0x20))`` +The expression splitter turns expressions like ``add(mload(0x123), mul(mload(0x456), 0x20))`` into a sequence of declarations of unique variables that are assigned sub-expressions of that expression so that each function call has only variables or literals as arguments. @@ -529,9 +531,9 @@ The above would be transformed into .. code-block:: yul { - let _1 := mload(y) + let _1 := mload(0x123) let _2 := mul(_1, 0x20) - let _3 := mload(x) + let _3 := mload(0x456) let z := add(_3, _2) } @@ -589,8 +591,8 @@ For any variable ``a`` that is assigned to somewhere in the code (variables that are declared with value and never re-assigned are not modified) perform the following transforms: - - replace ``let a := v`` by ``let a_i := v let a := a_i`` - - replace ``a := v`` by ``let a_i := v a := a_i`` where ``i`` is a number such that ``a_i`` is yet unused. +- replace ``let a := v`` by ``let a_i := v let a := a_i`` +- replace ``a := v`` by ``let a_i := v a := a_i`` where ``i`` is a number such that ``a_i`` is yet unused. Furthermore, always record the current value of ``i`` used for ``a`` and replace each reference to ``a`` by ``a_i``. @@ -633,7 +635,7 @@ The SSA transform converts this snippet to the following: { let a_1 := 1 - a := a_1 + let a := a_1 let a_2 := mload(a_1) a := a_2 let a_3 := sload(a_2) @@ -677,9 +679,9 @@ joins, the two mappings coming from the two branches are combined in the followi Statements that are only in one mapping or have the same state are used unchanged. Conflicting values are resolved in the following way: - - "unused", "undecided" -> "undecided" - - "unused", "used" -> "used" - - "undecided, "used" -> "used" +- "unused", "undecided" -> "undecided" +- "unused", "used" -> "used" +- "undecided, "used" -> "used" For for-loops, the condition, body and post-part are visited twice, taking the joining control-flow at the condition into account. @@ -696,7 +698,7 @@ operation where ``unused = 0``, ``undecided = 1`` and ``used = 2``. The proper way would be to compute -:: +.. code-block:: none max(s, f(s), f(f(s)), f(f(f(s))), ...) @@ -705,7 +707,7 @@ iterating it has to reach a cycle after at most three iterations, and thus ``f(f(f(s)))`` has to equal one of ``s``, ``f(s)``, or ``f(f(s))`` and thus -:: +.. code-block:: none max(s, f(s), f(f(s))) = max(s, f(s), f(f(s)), f(f(f(s))), ...). @@ -735,10 +737,10 @@ is side-effect free and its evaluation only depends on the values of variables and the call-constant state of the environment. Most expressions are movable. The following parts make an expression non-movable: - - function calls (might be relaxed in the future if all statements in the function are movable) - - opcodes that (can) have side-effects (like ``call`` or ``selfdestruct``) - - opcodes that read or write memory, storage or external state information - - opcodes that depend on the current PC, memory size or returndata size +- function calls (might be relaxed in the future if all statements in the function are movable) +- opcodes that (can) have side-effects (like ``call`` or ``selfdestruct``) +- opcodes that read or write memory, storage or external state information +- opcodes that depend on the current PC, memory size or returndata size DataflowAnalyzer ^^^^^^^^^^^^^^^^ @@ -836,8 +838,8 @@ ReasoningBasedSimplifier This optimizer uses SMT solvers to check whether ``if`` conditions are constant. - - If ``constraints AND condition`` is UNSAT, the condition is never true and the whole body can be removed. - - If ``constraints AND NOT condition`` is UNSAT, the condition is always true and can be replaced by ``1``. +- If ``constraints AND condition`` is UNSAT, the condition is never true and the whole body can be removed. +- If ``constraints AND NOT condition`` is UNSAT, the condition is always true and can be replaced by ``1``. The simplifications above can only be applied if the condition is movable. @@ -872,13 +874,13 @@ we cannot assign a specific value. Current features: - - switch cases: insert " := " - - after if statement with terminating control-flow, insert " := 0" +- switch cases: insert " := " +- after if statement with terminating control-flow, insert " := 0" Future features: - - allow replacements by "1" - - take termination of user-defined functions into account +- allow replacements by "1" +- take termination of user-defined functions into account Works best with SSA form and if dead code removal has run before. @@ -898,15 +900,15 @@ ControlFlowSimplifier Simplifies several control-flow structures: - - replace if with empty body with pop(condition) - - remove empty default switch case - - remove empty switch case if no default case exists - - replace switch with no cases with pop(expression) - - turn switch with single case into if - - replace switch with only default case with pop(expression) and body - - replace switch with const expr with matching case body - - replace ``for`` with terminating control flow and without other break/continue by ``if`` - - remove ``leave`` at the end of a function. +- replace if with empty body with pop(condition) +- remove empty default switch case +- remove empty switch case if no default case exists +- replace switch with no cases with pop(expression) +- turn switch with single case into if +- replace switch with only default case with pop(expression) and body +- replace switch with const expr with matching case body +- replace ``for`` with terminating control flow and without other break/continue by ``if`` +- remove ``leave`` at the end of a function. None of these operations depend on the data flow. The StructuralSimplifier performs similar tasks that do depend on data flow. @@ -956,13 +958,13 @@ StructuralSimplifier This is a general step that performs various kinds of simplifications on a structural level: - - replace if statement with empty body by ``pop(condition)`` - - replace if statement with true condition by its body - - remove if statement with false condition - - turn switch with single case into if - - replace switch with only default case by ``pop(expression)`` and body - - replace switch with literal expression by matching case body - - replace for loop with false condition by its initialization part +- replace if statement with empty body by ``pop(condition)`` +- replace if statement with true condition by its body +- remove if statement with false condition +- turn switch with single case into if +- replace switch with only default case by ``pop(expression)`` and body +- replace switch with literal expression by matching case body +- replace for loop with false condition by its initialization part This component uses the Dataflow Analyzer. @@ -1008,8 +1010,8 @@ declarations inside conditional branches will not be moved out of the loop. Requirements: - - The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront. - - Expression splitter and SSA transform should be run upfront to obtain better result. +- The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront. +- Expression splitter and SSA transform should be run upfront to obtain better result. Function-Level Optimizations @@ -1053,8 +1055,8 @@ remove the parameter and create a new "linking" function as follows: .. code-block:: yul - function f(a,b) -> x { x := div(a,b) } - function f2(a,b,c) -> x, y { x := f(a,b) } + function f(a,b) -> x { x := div(a,b) } + function f2(a,b,c) -> x, y { x := f(a,b) } and replace all references to ``f`` by ``f2``. The inliner should be run afterwards to make sure that all references to ``f2`` are replaced by @@ -1089,15 +1091,15 @@ FunctionalInliner This component of the optimizer performs restricted function inlining by inlining functions that can be inlined inside functional expressions, i.e. functions that: - - return a single value. - - have a body like ``r := ``. - - neither reference themselves nor ``r`` in the right hand side. +- return a single value. +- have a body like ``r := ``. +- neither reference themselves nor ``r`` in the right hand side. Furthermore, for all parameters, all of the following need to be true: - - The argument is movable. - - The parameter is either referenced less than twice in the function body, or the argument is rather cheap - ("cost" of at most 1, like a constant up to 0xff). +- The argument is movable. +- The parameter is either referenced less than twice in the function body, or the argument is rather cheap + ("cost" of at most 1, like a constant up to 0xff). Example: The function to be inlined has the form of ``function f(...) -> r { r := E }`` where ``E`` is an expression that does not reference ``r`` and all arguments in the function call are movable expressions. @@ -1186,16 +1188,18 @@ The SSA transform rewrites .. code-block:: yul - a := E + let a := calldataload(0) mstore(a, 1) to .. code-block:: yul - let a_1 := E - a := a_1 + let a_1 := calldataload(0) + let a := a_1 mstore(a_1, 1) + let a_2 := calldataload(0x20) + a := a_2 The problem is that instead of ``a``, the variable ``a_1`` is used whenever ``a`` was referenced. The SSA transform changes statements @@ -1204,9 +1208,11 @@ snippet is turned into .. code-block:: yul - a := E + let a := calldataload(0) let a_1 := a mstore(a_1, 1) + a := calldataload(0x20) + let a_2 := a This is a very simple equivalence transform, but when we now run the Common Subexpression Eliminator, it will replace all occurrences of ``a_1`` @@ -1259,7 +1265,7 @@ Reverses the transformation of ForLoopConditionIntoBody. For any movable ``c``, it turns -:: +.. code-block:: none for { ... } 1 { ... } { if iszero(c) { break } @@ -1268,7 +1274,7 @@ For any movable ``c``, it turns into -:: +.. code-block:: none for { ... } c { ... } { ... @@ -1276,7 +1282,7 @@ into and it turns -:: +.. code-block:: none for { ... } 1 { ... } { if c { break } @@ -1285,7 +1291,7 @@ and it turns into -:: +.. code-block:: none for { ... } iszero(c) { ... } { ... diff --git a/docs/internals/source_mappings.rst b/docs/internals/source_mappings.rst index 0ee215533..fbb84e2d1 100644 --- a/docs/internals/source_mappings.rst +++ b/docs/internals/source_mappings.rst @@ -56,8 +56,8 @@ used in a single modifier. In order to compress these source mappings especially for bytecode, the following rules are used: - - If a field is empty, the value of the preceding element is used. - - If a ``:`` is missing, all following fields are considered empty. +- If a field is empty, the value of the preceding element is used. +- If a ``:`` is missing, all following fields are considered empty. This means the following source mappings represent the same information: diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 6b427226c..c22d4e6fa 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -140,7 +140,9 @@ of a keypair belonging to :ref:`external accounts`. The keyword ``public`` automatically generates a function that allows you to access the current value of the state variable from outside of the contract. Without this keyword, other contracts have no way to access the variable. The code of the function generated by the compiler is equivalent -to the following (ignore ``external`` and ``view`` for now):: +to the following (ignore ``external`` and ``view`` for now): + +.. code-block:: solidity function minter() external view returns (address) { return minter; } @@ -162,7 +164,9 @@ even better, keep a list, or use a more suitable data type. The :ref:`getter function` created by the ``public`` keyword is more complex in the case of a mapping. It looks like the -following:: +following: + +.. code-block:: solidity function balances(address _account) external view returns (uint) { return balances[_account]; diff --git a/docs/ir/ir-breaking-changes.rst b/docs/ir/ir-breaking-changes.rst index 7d6b6d54f..9223b3763 100644 --- a/docs/ir/ir-breaking-changes.rst +++ b/docs/ir/ir-breaking-changes.rst @@ -1,6 +1,6 @@ -******************************** +********************************* Solidity IR-based Codegen Changes -******************************** +********************************* This section highlights the main differences between the old and the IR-based codegen, along with the reasoning behind the changes and how to update affected code. @@ -11,180 +11,211 @@ Semantic Only Changes This section lists the changes that are semantic-only, thus potentially hiding new and different behavior in existing code. - * When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched. -Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past). +- When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched. + Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past). -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0; + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.1; - contract C { - struct S { - uint64 y; - uint64 z; - } - S s; - function f() public { - // ... - delete s; - // s occupies only first 16 bytes of the 32 bytes slot - // delete will write zero to the full slot - } - } + contract C { + struct S { + uint64 y; + uint64 z; + } + S s; + function f() public { + // ... + delete s; + // s occupies only first 16 bytes of the 32 bytes slot + // delete will write zero to the full slot + } + } -We have the same behavior for implicit delete, for example when array of structs is shortened. + We have the same behavior for implicit delete, for example when array of structs is shortened. - * Function modifiers are implemented in a slightly different way regarding function parameters. - This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier. - In the old code generator, each function parameter has a fixed slot on the stack. If the function - is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the - function parameter's value is visible in the next execution of the function. - The new code generator implements modifiers using actual functions and passes function parameters on. - This means that multiple executions of a function will get the same values for the parameters. +- Function modifiers are implemented in a slightly different way regarding function parameters. + This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier. + In the old code generator, each function parameter has a fixed slot on the stack. If the function + is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the + function parameter's value is visible in the next execution of the function. + The new code generator implements modifiers using actual functions and passes function parameters on. + This means that multiple executions of a function will get the same values for the parameters. -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.7.0; - contract C { - function f(uint _a) public pure mod() returns (uint _r) { - _r = _a++; - } - modifier mod() { _; _; } - } + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0; + contract C { + function f(uint _a) public pure mod() returns (uint _r) { + _r = _a++; + } + modifier mod() { _; _; } + } -If you execute ``f(0)`` in the old code generator, it will return ``2``, while -it will return ``1`` when using the new code generator. + If you execute ``f(0)`` in the old code generator, it will return ``2``, while + it will return ``1`` when using the new code generator. - * The order of contract initialization has changed in case of inheritance. +- The order of contract initialization has changed in case of inheritance. -The order used to be: - - All state variables are zero-initialized at the beginning. - - Evaluate base constructor arguments from most derived to most base contract. - - Initialize all state variables in the whole inheritance hierarchy from most base to most derived. - - Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived. + The order used to be: -New order: - - All state variables are zero-initialized at the beginning. - - Evaluate base constructor arguments from most derived to most base contract. - - For every contract in order from most base to most derived in the linearized hierarchy execute: - 1. If present at declaration, initial values are assigned to state variables. - 2. Constructor, if present. + - All state variables are zero-initialized at the beginning. + - Evaluate base constructor arguments from most derived to most base contract. + - Initialize all state variables in the whole inheritance hierarchy from most base to most derived. + - Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived. + + New order: + + - All state variables are zero-initialized at the beginning. + - Evaluate base constructor arguments from most derived to most base contract. + - For every contract in order from most base to most derived in the linearized hierarchy execute: + + 1. If present at declaration, initial values are assigned to state variables. + 2. Constructor, if present. This causes differences in some contracts, for example: -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0; + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.1; - contract A { - uint x; - constructor() { - x = 42; - } - function f() public view returns(uint256) { - return x; - } - } - contract B is A { - uint public y = f(); - } + contract A { + uint x; + constructor() { + x = 42; + } + function f() public view returns(uint256) { + return x; + } + } + contract B is A { + uint public y = f(); + } -Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well. -With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42. + Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well. + With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42. - * Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot). -This causes differences in some contracts, for example: +- Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot). + This causes differences in some contracts, for example: -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.1; - contract C { - bytes x; - function f() public returns (uint _r) { - bytes memory m = "tmp"; - assembly { - mstore(m, 8) - mstore(add(m, 32), "deadbeef15dead") - } - x = m; - assembly { - _r := sload(x.slot) - } - } - } + contract C { + bytes x; + function f() public returns (uint _r) { + bytes memory m = "tmp"; + assembly { + mstore(m, 8) + mstore(add(m, 32), "deadbeef15dead") + } + x = m; + assembly { + _r := sload(x.slot) + } + } + } -Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010`` (it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly). -Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data). + Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010`` (it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly). + Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data). -.. index:: ! evaluation order; expression + .. index:: ! evaluation order; expression -* For the old code generator, the evaluation order of expressions is unspecified. +- For the old code generator, the evaluation order of expressions is unspecified. For the new code generator, we try to evaluate in source order (left to right), but do not guarantee it. This can lead to semantic differences. -For example: + For example: -.. code-block:: solidity + .. code-block:: solidity - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; - contract C { - function preincr_u8(uint8 _a) public pure returns (uint8) { - return ++_a + _a; - } - } + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.1; + contract C { + function preincr_u8(uint8 _a) public pure returns (uint8) { + return ++_a + _a; + } + } -The function ``preincr_u8(1)`` returns the following values: -- Old code generator: 3 (``1 + 2``) but the return value is unspecified in general -- New code generator: 4 (``2 + 2``) but the return value is not guaranteed + The function ``preincr_u8(1)`` returns the following values: -.. index:: ! evaluation order; function arguments + - Old code generator: 3 (``1 + 2``) but the return value is unspecified in general + - New code generator: 4 (``2 + 2``) but the return value is not guaranteed -On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``. -For example: + .. index:: ! evaluation order; function arguments -.. code-block:: solidity + On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``. + For example: - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; - contract C { - function add(uint8 _a, uint8 _b) public pure returns (uint8) { - return _a + _b; - } - function g(uint8 _a, uint8 _b) public pure returns (uint8) { - return add(++_a + ++_b, _a + _b); - } - } + .. code-block:: solidity -The function ``g(1, 2)`` returns the following values: -- Old code generator: ``10`` (``add(2 + 3, 2 + 3)``) but the return value is unspecified in general -- New code generator: ``10`` but the return value is not guaranteed + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.1; + contract C { + function add(uint8 _a, uint8 _b) public pure returns (uint8) { + return _a + _b; + } + function g(uint8 _a, uint8 _b) public pure returns (uint8) { + return add(++_a + ++_b, _a + _b); + } + } -The arguments to the global functions ``addmod`` and ``mulmod`` are evaluated right-to-left by the old code generator -and left-to-right by the new code generator. -For example: + The function ``g(1, 2)`` returns the following values: -:: - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; - contract C { - function f() public pure returns (uint256 aMod, uint256 mMod) { - uint256 x = 3; - // Old code gen: add/mulmod(5, 4, 3) - // New code gen: add/mulmod(4, 5, 5) - aMod = addmod(++x, ++x, x); - mMod = mulmod(++x, ++x, x); - } - } + - Old code generator: ``10`` (``add(2 + 3, 2 + 3)``) but the return value is unspecified in general + - New code generator: ``10`` but the return value is not guaranteed -The function ``f()`` returns the following values: -- Old code generator: ``aMod = 0`` and ``mMod = 2`` -- New code generator: ``aMod = 4`` and ``mMod = 0`` + The arguments to the global functions ``addmod`` and ``mulmod`` are evaluated right-to-left by the old code generator + and left-to-right by the new code generator. + For example: + + .. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.1; + contract C { + function f() public pure returns (uint256 aMod, uint256 mMod) { + uint256 x = 3; + // Old code gen: add/mulmod(5, 4, 3) + // New code gen: add/mulmod(4, 5, 5) + aMod = addmod(++x, ++x, x); + mMod = mulmod(++x, ++x, x); + } + } + + The function ``f()`` returns the following values: + + - Old code generator: ``aMod = 0`` and ``mMod = 2`` + - New code generator: ``aMod = 4`` and ``mMod = 0`` + +- The new code generator imposes a hard limit of ``type(uint64).max`` (``0xffffffffffffffff``) for the free memory pointer. Allocations that would increase its value beyond this limit revert. The old code generator does not have this limit. + + For example: + + .. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.8.0; + contract C { + function f() public { + uint[] memory arr; + // allocation size: 576460752303423481 + // assumes freeMemPtr points to 0x80 initially + uint solYulMaxAllocationBeforeMemPtrOverflow = (type(uint64).max - 0x80 - 31) / 32; + // freeMemPtr overflows UINT64_MAX + arr = new uint[](solYulMaxAllocationBeforeMemPtrOverflow); + } + } + + The function `f()` behaves as follows: + + - Old code generator: runs out of gas while zeroing the array contents after the large memory allocation + - New code generator: reverts due to free memory pointer overflow (does not run out of gas) Internals @@ -219,9 +250,10 @@ The new code generator performs cleanup after any operation that can result in d For example: .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.0; + pragma solidity >=0.8.1; contract C { function f(uint8 _a) public pure returns (uint _r1, uint _r2) { @@ -234,6 +266,7 @@ For example: } The function ``f(1)`` returns the following values: + - Old code generator: (``fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe``, ``00000000000000000000000000000000000000000000000000000000000000fe``) - New code generator: (``00000000000000000000000000000000000000000000000000000000000000fe``, ``00000000000000000000000000000000000000000000000000000000000000fe``) diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 22624c410..cd031bb01 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -180,9 +180,9 @@ a `default export , "solc": }`` is stored contain more keys (see below) and the beginning of that encoding is not easy to find, its length is added in a two-byte big-endian encoding. The current version of the Solidity compiler usually adds the following -to the end of the deployed bytecode:: +to the end of the deployed bytecode + +.. code-block:: text 0xa2 0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash> diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index 7007fe4a1..297bbc8c6 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -58,7 +58,7 @@ The following example shows a contract and a function using all available tags. This may change in the future. -.. code:: Solidity +.. code-block:: Solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.2 < 0.9.0; @@ -166,9 +166,9 @@ Inheritance Notes Functions without NatSpec will automatically inherit the documentation of their base function. Exceptions to this are: - * When the parameter names are different. - * When there is more than one base function. - * When there is an explicit ``@inheritdoc`` tag which specifies which contract should be used to inherit. +* When the parameter names are different. +* When there is more than one base function. +* When there is an explicit ``@inheritdoc`` tag which specifies which contract should be used to inherit. .. _header-output: diff --git a/docs/path-resolution.rst b/docs/path-resolution.rst index 5848e3f9a..1d4b200de 100644 --- a/docs/path-resolution.rst +++ b/docs/path-resolution.rst @@ -180,7 +180,7 @@ Direct Imports An import that does not start with ``./`` or ``../`` is a *direct import*. -:: +.. code-block:: solidity import "/project/lib/util.sol"; // source unit name: /project/lib/util.sol import "lib/util.sol"; // source unit name: lib/util.sol @@ -464,7 +464,8 @@ Here are the detailed rules governing the behaviour of remappings: #. **Prefix cannot be empty but context and target are optional.** - If ``target`` is omitted, it defaults to the value of the ``prefix``. + - If ``target`` is the empty string, ``prefix`` is simply removed from import paths. + - Empty ``context`` means that the remapping applies to all imports in all source units. .. index:: Remix IDE, file:// diff --git a/docs/requirements.txt b/docs/requirements.txt index 038c22b7c..23c8f9043 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,8 @@ -sphinx_rtd_theme>=0.3.1 +# Older versions of sphinx-rtd-theme do not work with never docutils but have a bug in the dependency +# which could result in it being installed anyway and the style (especially bullet points) being broken. +# See https://github.com/readthedocs/sphinx_rtd_theme/issues/1115 +sphinx_rtd_theme>=0.5.2 + pygments-lexer-solidity>=0.7.0 sphinx-a4doc>=1.2.1 diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index c59f4c750..e348dd915 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -211,6 +211,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like } function transferTo(address payable dest, uint amount) public { + // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin require(tx.origin == owner); dest.transfer(amount); } diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index 6fc0a1c57..e2b61cf35 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -15,7 +15,7 @@ difference between what you did (the specification) and how you did it is what you wanted and that you did not miss any unintended effects of it. Solidity implements a formal verification approach based on -`SMT `_ and +`SMT (Satisfiability Modulo Theories) `_ and `Horn `_ solving. The SMTChecker module automatically tries to prove that the code satisfies the specification given by ``require`` and ``assert`` statements. That is, it considers @@ -44,23 +44,24 @@ where the default is no engine. Selecting the engine enables the SMTChecker on a .. note:: - Prior to Solidity 0.8.4, the default way to enable the SMTChecker was via - ``pragma experimental SMTChecker;`` and only the contracts containing the - pragma would be analyzed. That pragma has been deprecated, and although it - still enables the SMTChecker for backwards compatibility, it will be removed - in Solidity 0.9.0. Note also that now using the pragma even in a single file - enables the SMTChecker for all files. + Prior to Solidity 0.8.4, the default way to enable the SMTChecker was via + ``pragma experimental SMTChecker;`` and only the contracts containing the + pragma would be analyzed. That pragma has been deprecated, and although it + still enables the SMTChecker for backwards compatibility, it will be removed + in Solidity 0.9.0. Note also that now using the pragma even in a single file + enables the SMTChecker for all files. .. note:: - The lack of warnings for a verification target represents an undisputed - mathematical proof of correctness, assuming no bugs in the SMTChecker and - the underlying solver. Keep in mind that these problems are - *very hard* and sometimes *impossible* to solve automatically in the - general case. Therefore, several properties might not be solved or might - lead to false positives for large contracts. Every proven property should - be seen as an important achievement. For advanced users, see :ref:`SMTChecker Tuning ` - to learn a few options that might help proving more complex - properties. + + The lack of warnings for a verification target represents an undisputed + mathematical proof of correctness, assuming no bugs in the SMTChecker and + the underlying solver. Keep in mind that these problems are + *very hard* and sometimes *impossible* to solve automatically in the + general case. Therefore, several properties might not be solved or might + lead to false positives for large contracts. Every proven property should + be seen as an important achievement. For advanced users, see :ref:`SMTChecker Tuning ` + to learn a few options that might help proving more complex + properties. ******** Tutorial @@ -96,7 +97,7 @@ The SMTChecker will, by default, check every reachable arithmetic operation in the contract for potential underflow and overflow. Here, it reports the following: -.. code-block:: bash +.. code-block:: text Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: @@ -202,8 +203,9 @@ Note that in this example the SMTChecker will automatically try to prove three p 3. The assertion is always true. .. note:: - The properties involve loops, which makes it *much much* harder than the previous - examples, so beware of loops! + + The properties involve loops, which makes it *much much* harder than the previous + examples, so beware of loops! All the properties are correctly proven safe. Feel free to change the properties and/or add restrictions on the array to see different results. @@ -231,20 +233,20 @@ For example, changing the code to gives us: -.. code-block:: bash +.. code-block:: text - Warning: CHC: Assertion violation happens here. - Counterexample: + Warning: CHC: Assertion violation happens here. + Counterexample: - _a = [0, 0, 0, 0, 0] - = 0 + _a = [0, 0, 0, 0, 0] + = 0 - Transaction trace: - Test.constructor() - Test.max([0, 0, 0, 0, 0]) - --> max.sol:14:4: - | - 14 | assert(m > _a[i]); + Transaction trace: + Test.constructor() + Test.max([0, 0, 0, 0, 0]) + --> max.sol:14:4: + | + 14 | assert(m > _a[i]); State Properties @@ -321,28 +323,28 @@ reachable, by adding the following function. This property is false, and while proving that the property is false, the SMTChecker tells us exactly *how* to reach (2, 4): -.. code-block:: bash +.. code-block:: text - Warning: CHC: Assertion violation happens here. - Counterexample: - x = 2, y = 4 + Warning: CHC: Assertion violation happens here. + Counterexample: + x = 2, y = 4 - Transaction trace: - Robot.constructor() - State: x = 0, y = 0 - Robot.moveLeftUp() - State: x = (- 1), y = 1 - Robot.moveRightUp() - State: x = 0, y = 2 - Robot.moveRightUp() - State: x = 1, y = 3 - Robot.moveRightUp() - State: x = 2, y = 4 - Robot.reach_2_4() - --> r.sol:35:4: - | - 35 | assert(!(x == 2 && y == 4)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Transaction trace: + Robot.constructor() + State: x = 0, y = 0 + Robot.moveLeftUp() + State: x = (- 1), y = 1 + Robot.moveRightUp() + State: x = 0, y = 2 + Robot.moveRightUp() + State: x = 1, y = 3 + Robot.moveRightUp() + State: x = 2, y = 4 + Robot.reach_2_4() + --> r.sol:35:4: + | + 35 | assert(!(x == 2 && y == 4)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Note that the path above is not necessarily deterministic, as there are other paths that could reach (2, 4). The choice of which path is shown @@ -367,36 +369,36 @@ anything, including reenter the caller contract. pragma solidity >=0.8.0; interface Unknown { - function run() external; + function run() external; } contract Mutex { - uint x; - bool lock; + uint x; + bool lock; - Unknown immutable unknown; + Unknown immutable unknown; - constructor(Unknown _u) { - require(address(_u) != address(0)); - unknown = _u; - } + constructor(Unknown _u) { + require(address(_u) != address(0)); + unknown = _u; + } - modifier mutex { - require(!lock); - lock = true; - _; - lock = false; - } + modifier mutex { + require(!lock); + lock = true; + _; + lock = false; + } - function set(uint _x) mutex public { - x = _x; - } + function set(uint _x) mutex public { + x = _x; + } - function run() mutex public { - uint xPre = x; - unknown.run(); - assert(xPre == x); - } + function run() mutex public { + uint xPre = x; + unknown.run(); + assert(xPre == x); + } } The example above shows a contract that uses a mutex flag to forbid reentrancy. @@ -408,22 +410,22 @@ If we "forget" to use the ``mutex`` modifier on function ``set``, the SMTChecker is able to synthesize the behavior of the externally called code so that the assertion fails: -.. code-block:: bash +.. code-block:: text - Warning: CHC: Assertion violation happens here. - Counterexample: - x = 1, lock = true, unknown = 1 + Warning: CHC: Assertion violation happens here. + Counterexample: + x = 1, lock = true, unknown = 1 - Transaction trace: - Mutex.constructor(1) - State: x = 0, lock = false, unknown = 1 - Mutex.run() - unknown.run() -- untrusted external call, synthesized as: - Mutex.set(1) -- reentrant call - --> m.sol:32:3: - | - 32 | assert(xPre == x); - | ^^^^^^^^^^^^^^^^^ + Transaction trace: + Mutex.constructor(1) + State: x = 0, lock = false, unknown = 1 + Mutex.run() + unknown.run() -- untrusted external call, synthesized as: + Mutex.set(1) -- reentrant call + --> m.sol:32:3: + | + 32 | assert(xPre == x); + | ^^^^^^^^^^^^^^^^^ .. _smtchecker_options: @@ -472,6 +474,14 @@ A common subset of targets might be, for example: There is no precise heuristic on how and when to split verification targets, but it can be useful especially when dealing with large contracts. +Unproved Targets +================ + +If there are any unproved targets, the SMTChecker issues one warning stating +how many unproved targets there are. If the user wishes to see all the specific +unproved targets, the CLI option ``--model-checker-show-unproved true`` and +the JSON option ``settings.modelChecker.showUnproved = true`` can be used. + Verified Contracts ================== @@ -492,14 +502,12 @@ allowed) of : pairs in the CLI: and via the object ``settings.modelChecker.contracts`` in the :ref:`JSON input`, which has the following form: -.. code-block:: none - - contracts - { - "source1.sol": ["contract1"], - "source2.sol": ["contract2", "contract3"] - } +.. code-block:: json + "contracts": { + "source1.sol": ["contract1"], + "source2.sol": ["contract2", "contract3"] + } .. _smtchecker_engines: @@ -557,6 +565,39 @@ calls assume the called code is unknown and can do anything. The CHC engine is much more powerful than BMC in terms of what it can prove, and might require more computing resources. +SMT and Horn solvers +==================== + +The two engines detailed above use automated theorem provers as their logical +backends. BMC uses an SMT solver, whereas CHC uses a Horn solver. Often the +same tool can act as both, as seen in `z3 `_, +which is primarily an SMT solver and makes `Spacer +`_ available as a Horn solver, and `Eldarica +`_ which does both. + +The user can choose which solvers should be used, if available, via the CLI +option ``--model-checker-solvers {all,cvc4,smtlib2,z3}`` or the JSON option +``settings.modelChecker.solvers=[smtlib2,z3]``, where: + +- ``cvc4`` is only available if the ``solc`` binary is compiled with it. Only BMC uses ``cvc4``. +- ``smtlib2`` outputs SMT/Horn queries in the `smtlib2 `_ format. + These can be used together with the compiler's `callback mechanism `_ so that + any solver binary from the system can be employed to synchronously return the results of the queries to the compiler. + This is currently the only way to use Eldarica, for example, since it does not have a C++ API. + This can be used by both BMC and CHC depending on which solvers are called. +- ``z3`` is available + + - if ``solc`` is compiled with it; + - if a dynamic ``z3`` library of version 4.8.x is installed in a Linux system (from Solidity 0.7.6); + - statically in ``soljson.js`` (from Solidity 0.6.9), that is, the Javascript binary of the compiler. + +Since both BMC and CHC use ``z3``, and ``z3`` is available in a greater variety +of environments, including in the browser, most users will almost never need to be +concerned about this option. More advanced users might apply this option to try +alternative solvers on more complex problems. + +Please note that certain combinations of chosen engine and solver will lead to +the SMTChecker doing nothing, for example choosing CHC and ``cvc4``. ******************************* Abstraction and False Positives diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index e776c4eca..740cce861 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -50,7 +50,7 @@ contracts. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.7.0 <0.9.0; + pragma solidity >=0.7.1 <0.9.0; contract SimpleAuction { function bid() public payable { // Function diff --git a/docs/style-guide.rst b/docs/style-guide.rst index edb3b25ee..9e0fba5f6 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -410,7 +410,9 @@ No: spam( ham[ 1 ], Coin( { name: "ham" } ) ); -Exception:: +Exception: + +.. code-block:: solidity function singleLine() public { spam(); } @@ -996,6 +998,7 @@ No: Yes: .. code-block:: solidity + :force: x = 3; x = 100 / 10; @@ -1005,6 +1008,7 @@ Yes: No: .. code-block:: solidity + :force: x=3; x = 100/10; diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index 29a00a196..c026d73f2 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -122,6 +122,7 @@ top of them and iterate over that. For example, the code below implements an the ``sum`` function iterates over to sum all the values. .. code-block:: solidity + :force: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.8 <0.9.0; diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index b6a36fc6e..cc6fe2d8a 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -27,13 +27,6 @@ Every reference type has an additional annotation, the "data location", about where it is stored. There are three data locations: ``memory``, ``storage`` and ``calldata``. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. -It is required for parameters of external functions but can also be used for other variables. - - -.. note:: - Prior to version 0.5.0 the data location could be omitted, and would default to different locations - depending on the kind of variable, function type, etc., but all complex types must now give an explicit - data location. .. note:: If you can, try to use ``calldata`` as data location because it will avoid copies and @@ -41,6 +34,17 @@ It is required for parameters of external functions but can also be used for oth data location can also be returned from functions, but it is not possible to allocate such types. +.. note:: + Prior to version 0.6.9 data location for reference-type arguments was limited to + ``calldata`` in external functions, ``memory`` in public functions and either + ``memory`` or ``storage`` in internal and private ones. + Now ``memory`` and ``calldata`` are allowed in all functions regardless of their visibility. + +.. note:: + Prior to version 0.5.0 the data location could be omitted, and would default to different locations + depending on the kind of variable, function type, etc., but all complex types must now give an explicit + data location. + .. _data-location-assignment: Data location and assignment behaviour @@ -139,7 +143,7 @@ a reference to it. ``bytes`` and ``string`` as Arrays ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, +Variables of type ``bytes`` and ``string`` are special arrays. The ``bytes`` type is similar to ``bytes1[]``, but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow length or index access. @@ -148,8 +152,8 @@ third-party string libraries. You can also compare two strings by their keccak25 ``keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))`` and concatenate two strings using ``bytes.concat(bytes(s1), bytes(s2))``. -You should use ``bytes`` over ``byte[]`` because it is cheaper, -since ``byte[]`` adds 31 padding bytes between the elements. As a general rule, +You should use ``bytes`` over ``bytes1[]`` because it is cheaper, +since ``bytes1[]`` adds 31 padding bytes between the elements. As a general rule, use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper. @@ -492,7 +496,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.8.4 <0.9.0; + pragma solidity >=0.8.5 <0.9.0; contract Proxy { /// @dev Address of the client contract managed by proxy i.e., this contract address client; @@ -559,7 +563,7 @@ shown in the following example: function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable // We cannot use "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)" - // because the RHS creates a memory-struct "Campaign" that contains a mapping. + // because the right hand side creates a memory-struct "Campaign" that contains a mapping. Campaign storage c = campaigns[campaignID]; c.beneficiary = beneficiary; c.fundingGoal = goal; diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index d6a30d106..008d31683 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -128,10 +128,10 @@ The modulo operation ``a % n`` yields the remainder ``r`` after the division of by the operand ``n``, where ``q = int(a / n)`` and ``r = a - (n * q)``. This means that modulo results in the same sign as its left operand (or zero) and ``a % n == -(-a % n)`` holds for negative ``a``: - * ``int256(5) % int256(2) == int256(1)`` - * ``int256(5) % int256(-2) == int256(1)`` - * ``int256(-5) % int256(2) == int256(-1)`` - * ``int256(-5) % int256(-2) == int256(-1)`` +* ``int256(5) % int256(2) == int256(1)`` +* ``int256(5) % int256(-2) == int256(1)`` +* ``int256(-5) % int256(2) == int256(-1)`` +* ``int256(-5) % int256(-2) == int256(-1)`` .. note:: Modulo with zero causes a :ref:`Panic error`. This check can **not** be disabled through ``unchecked { ... }``. @@ -184,8 +184,8 @@ Address The address type comes in two flavours, which are largely identical: - - ``address``: Holds a 20 byte value (size of an Ethereum address). - - ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. +- ``address``: Holds a 20 byte value (size of an Ethereum address). +- ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. The idea behind this distinction is that ``address payable`` is an address you can send Ether to, while a plain ``address`` cannot be sent Ether. @@ -239,6 +239,7 @@ It is possible to query the balance of an address using the property ``balance`` and to send Ether (in units of wei) to a payable address using the ``transfer`` function: .. code-block:: solidity + :force: address payable x = address(0x123); address myAddress = address(this); @@ -398,7 +399,7 @@ Members: * ``.length`` yields the fixed length of the byte array (read-only). .. note:: - The type ``byte[]`` is an array of bytes, but due to padding rules, it wastes + The type ``bytes1[]`` is an array of bytes, but due to padding rules, it wastes 31 bytes of space for each element (except in storage). It is better to use the ``bytes`` type instead. @@ -510,27 +511,32 @@ String literals can only contain printable ASCII characters, which means the cha Additionally, string literals also support the following escape characters: - - ``\`` (escapes an actual newline) - - ``\\`` (backslash) - - ``\'`` (single quote) - - ``\"`` (double quote) - - ``\b`` (backspace) - - ``\f`` (form feed) - - ``\n`` (newline) - - ``\r`` (carriage return) - - ``\t`` (tab) - - ``\v`` (vertical tab) - - ``\xNN`` (hex escape, see below) - - ``\uNNNN`` (unicode escape, see below) +- ``\`` (escapes an actual newline) +- ``\\`` (backslash) +- ``\'`` (single quote) +- ``\"`` (double quote) +- ``\n`` (newline) +- ``\r`` (carriage return) +- ``\t`` (tab) +- ``\xNN`` (hex escape, see below) +- ``\uNNNN`` (unicode escape, see below) ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. +.. note:: + + Until version 0.8.0 there were three additional escape sequences: ``\b``, ``\f`` and ``\v``. + They are commonly available in other languages but rarely needed in practice. + If you do need them, they can still be inserted via hexadecimal escapes, i.e. ``\x08``, ``\x0c`` + and ``\x0b``, respectively, just as any other ASCII character. + The string in the following example has a length of ten bytes. It starts with a newline byte, followed by a double quote, a single quote a backslash character and then (without separator) the character sequence ``abcdef``. -:: +.. code-block:: solidity + :force: "\n\"\'\\abc\ def" @@ -637,6 +643,7 @@ be passed via and returned from external function calls. Function types are notated as follows: .. code-block:: solidity + :force: function () {internal|external} [pure|view|payable] [returns ()] @@ -656,9 +663,9 @@ their parameter types are identical, their return types are identical, their internal/external property is identical and the state mutability of ``A`` is more restrictive than the state mutability of ``B``. In particular: - - ``pure`` functions can be converted to ``view`` and ``non-payable`` functions - - ``view`` functions can be converted to ``non-payable`` functions - - ``payable`` functions can be converted to ``non-payable`` functions +- ``pure`` functions can be converted to ``view`` and ``non-payable`` functions +- ``view`` functions can be converted to ``non-payable`` functions +- ``payable`` functions can be converted to ``non-payable`` functions No other conversions between function types are possible. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 2e912ae84..0e2ce34a2 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -10,6 +10,7 @@ Ether Units A literal number can take a suffix of ``wei``, ``gwei`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei. .. code-block:: solidity + :force: assert(1 wei == 1); assert(1 gwei == 1e9); @@ -29,11 +30,11 @@ Suffixes like ``seconds``, ``minutes``, ``hours``, ``days`` and ``weeks`` after literal numbers can be used to specify units of time where seconds are the base unit and units are considered naively in the following way: - * ``1 == 1 seconds`` - * ``1 minutes == 60 seconds`` - * ``1 hours == 60 minutes`` - * ``1 days == 24 hours`` - * ``1 weeks == 7 days`` +* ``1 == 1 seconds`` +* ``1 minutes == 60 seconds`` +* ``1 hours == 60 minutes`` +* ``1 days == 24 hours`` +* ``1 weeks == 7 days`` Take care if you perform calendar calculations using these units, because not every year equals 365 days and not even every day has 24 hours diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index a023a8877..381f150cd 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -30,8 +30,8 @@ set it to ``--optimize-runs=1``. If you expect many transactions and do not care output size, set ``--optimize-runs`` to a high number. This parameter has effects on the following (this might change in the future): - - the size of the binary search in the function dispatch routine - - the way constants like large numbers or strings are stored +- the size of the binary search in the function dispatch routine +- the way constants like large numbers or strings are stored .. index:: allowed paths, --allow-paths, base path, --base-path @@ -41,7 +41,7 @@ Base Path and Import Remapping The commandline compiler will automatically read imported files from the filesystem, but it is also possible to provide :ref:`path redirects ` using ``prefix=path`` in the following way: -:: +.. code-block:: bash solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ file.sol @@ -134,15 +134,15 @@ On the command line, you can select the EVM version as follows: In the :ref:`standard JSON interface `, use the ``"evmVersion"`` key in the ``"settings"`` field: -.. code-block:: none +.. code-block:: javascript - { - "sources": { ... }, - "settings": { - "optimizer": { ... }, - "evmVersion": "" + { + "sources": {/* ... */}, + "settings": { + "optimizer": {/* ... */}, + "evmVersion": "" + } } - } Target Options -------------- @@ -198,7 +198,7 @@ Comments are of course not permitted and used here only for explanatory purposes Input Description ----------------- -.. code-block:: none +.. code-block:: javascript { // Required: Source code language. Currently supported are "Solidity" and "Yul". @@ -310,7 +310,7 @@ Input Description // "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now. // "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented) "revertStrings": "default" - } + }, // Metadata settings (optional) "metadata": { // Use only literal content and not URLs (false by default) @@ -331,7 +331,7 @@ Input Description "myFile.sol": { "MyLib": "0x123123..." } - } + }, // The following can be used to select desired outputs based // on file and contract names. // If this field is omitted, then the compiler loads and does type checking, @@ -395,13 +395,15 @@ Input Description "modelChecker": { // Chose which contracts should be analyzed as the deployed one. - contracts: + "contracts": { "source1.sol": ["contract1"], "source2.sol": ["contract2", "contract3"] }, // Choose which model checker engine to use: all (default), bmc, chc, none. "engine": "chc", + // Choose whether to output all unproved targets. The default is `false`. + "showUnproved": true, // Choose which targets should be checked: constantCondition, // underflow, overflow, divByZero, balance, assert, popEmptyArray, outOfBounds. // If the option is not given all targets are checked by default. @@ -420,7 +422,7 @@ Input Description Output Description ------------------ -.. code-block:: none +.. code-block:: javascript { // Optional: not present if no errors/warnings were encountered @@ -431,7 +433,7 @@ Output Description "file": "sourceFile.sol", "start": 0, "end": 100 - ], + }, // Optional: Further locations (e.g. places of conflicting declarations) "secondarySourceLocations": [ { @@ -463,7 +465,7 @@ Output Description // Identifier of the source (used in source maps) "id": 1, // The AST object - "ast": {}, + "ast": {} } }, // This contains the contract-level outputs. @@ -476,7 +478,7 @@ Output Description // See https://docs.soliditylang.org/en/develop/abi-spec.html "abi": [], // See the Metadata Output documentation (serialised JSON string) - "metadata": "{...}", + "metadata": "{/* ... */}", // User documentation (natspec) "userdoc": {}, // Developer documentation (natspec) @@ -484,7 +486,7 @@ Output Description // Intermediate representation (string) "ir": "", // See the Storage Layout documentation. - "storageLayout": {"storage": [...], "types": {...} }, + "storageLayout": {"storage": [/* ... */], "types": {/* ... */} }, // EVM-related outputs "evm": { // Assembly (string) @@ -514,14 +516,14 @@ Output Description // contains a single Yul file. "generatedSources": [{ // Yul AST - "ast": { ... } + "ast": {/* ... */}, // Source file in its text form (may contain comments) "contents":"{ function abi_decode(start, end) -> data { data := calldataload(start) } }", // Source file ID, used for source references, same "namespace" as the Solidity source files "id": 2, "language": "Yul", "name": "#utility.yul" - }] + }], // If given, this is an unlinked object. "linkReferences": { "libraryFile.sol": { @@ -535,7 +537,7 @@ Output Description } }, "deployedBytecode": { - ..., // The same layout as above. + /* ..., */ // The same layout as above. "immutableReferences": { // There are two references to the immutable with AST ID 3, both 32 bytes long. One is // at bytecode offset 42, the other at bytecode offset 80. @@ -779,9 +781,9 @@ Running the Upgrade It is recommended to explicitly specify the upgrade modules by using ``--modules`` argument. -.. code-block:: none +.. code-block:: bash - $ solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol + solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol The command above applies all changes as shown below. Please review them carefully (the pragmas will have to be updated manually.) diff --git a/docs/yul.rst b/docs/yul.rst index 55e8b824f..0f7d3cef3 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -157,16 +157,16 @@ where an object is expected. Inside a code block, the following elements can be used (see the later sections for more details): - - literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) - - calls to builtin functions, e.g. ``add(1, mload(0))`` - - variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned) - - identifiers (variables), e.g. ``add(3, x)`` - - assignments, e.g. ``x := add(y, 3)`` - - blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }`` - - if statements, e.g. ``if lt(a, b) { sstore(0, 1) }`` - - switch statements, e.g. ``switch mload(0) case 0 { revert() } default { mstore(0, 1) }`` - - for loops, e.g. ``for { let i := 0} lt(i, 10) { i := add(i, 1) } { mstore(i, 7) }`` - - function definitions, e.g. ``function f(a, b) -> c { c := add(a, b) }``` +- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) +- calls to builtin functions, e.g. ``add(1, mload(0))`` +- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned) +- identifiers (variables), e.g. ``add(3, x)`` +- assignments, e.g. ``x := add(y, 3)`` +- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }`` +- if statements, e.g. ``if lt(a, b) { sstore(0, 1) }`` +- switch statements, e.g. ``switch mload(0) case 0 { revert() } default { mstore(0, 1) }`` +- for loops, e.g. ``for { let i := 0} lt(i, 10) { i := add(i, 1) } { mstore(i, 7) }`` +- function definitions, e.g. ``function f(a, b) -> c { c := add(a, b) }``` Multiple syntactical elements can follow each other simply separated by whitespace, i.e. there is no terminating ``;`` or newline required. @@ -198,7 +198,8 @@ has to be specified after a colon: .. code-block:: yul - let x := and("abc":uint32, add(3:uint256, 2:uint256)) + // This will not compile (u32 and u256 type not implemented yet) + let x := and("abc":u32, add(3:u256, 2:u256)) Function Calls @@ -212,10 +213,9 @@ they have to be assigned to local variables. .. code-block:: yul + function f(x, y) -> a, b { /* ... */ } mstore(0x80, add(mload(0x80), 3)) - // Here, the user-defined function `f` returns - // two values. The definition of the function - // is missing from the example. + // Here, the user-defined function `f` returns two values. let x, y := f(1, mload(0)) For built-in functions of the EVM, functional expressions @@ -271,9 +271,10 @@ that returns multiple values. .. code-block:: yul + // This will not compile (u32 and u256 type not implemented yet) { - let zero:uint32 := 0:uint32 - let v:uint256, t:uint32 := f() + let zero:u32 := 0:u32 + let v:u256, t:u32 := f() let x, y := g() } @@ -314,7 +315,7 @@ you need multiple alternatives. .. code-block:: yul - if eq(value, 0) { revert(0, 0) } + if lt(calldatasize(), 4) { revert(0, 0) } The curly braces for the body are required. @@ -545,11 +546,18 @@ as explained below) and all declarations introduce new identifiers into these scopes. Identifiers are visible in -the block they are defined in (including all sub-nodes and sub-blocks). +the block they are defined in (including all sub-nodes and sub-blocks): +Functions are visible in the whole block (even before their definitions) while +variables are only visible starting from the statement after the ``VariableDeclaration``. -As an exception, the scope of the "init" part of the or-loop +In particular, +variables cannot be referenced in the right hand side of their own variable +declaration. +Functions can be referenced already before their declaration (if they are visible). + +As an exception to the general scoping rule, the scope of the "init" part of the for-loop (the first block) extends across all other parts of the for loop. -This means that variables declared in the init part (but not inside a +This means that variables (and functions) declared in the init part (but not inside a block inside the init part) are visible in all other parts of the for-loop. Identifiers declared in the other parts of the for loop respect the regular @@ -558,21 +566,15 @@ syntactical scoping rules. This means a for-loop of the form ``for { I... } C { P... } { B... }`` is equivalent to ``{ I... for {} C { P... } { B... } }``. - The parameters and return parameters of functions are visible in the function body and their names have to be distinct. -Variables can only be referenced after their declaration. In particular, -variables cannot be referenced in the right hand side of their own variable -declaration. -Functions can be referenced already before their declaration (if they are visible). +Inside functions, it is not possible to reference a variable that was declared +outside of that function. Shadowing is disallowed, i.e. you cannot declare an identifier at a point where another identifier with the same name is also visible, even if it is -not accessible. - -Inside functions, it is not possible to access a variable that was declared -outside of that function. +not possible to reference it because it was declared outside the current function. Formal Specification -------------------- @@ -984,9 +986,10 @@ that are not known to the Yul compiler. It also allows you to create bytecode sequences that will not be modified by the optimizer. The functions are ``verbatim_i_o("", ...)``, where - - ``n`` is a decimal between 0 and 99 that specifies the number of input stack slots / variables - - ``m`` is a decimal between 0 and 99 that specifies the number of output stack slots / variables - - ``data`` is a string literal that contains the sequence of bytes + +- ``n`` is a decimal between 0 and 99 that specifies the number of input stack slots / variables +- ``m`` is a decimal between 0 and 99 that specifies the number of output stack slots / variables +- ``data`` is a string literal that contains the sequence of bytes If you for example want to define a function that multiplies the input by two, without the optimizer touching the constant two, you can use @@ -1021,15 +1024,15 @@ verbatim bytecode that are not checked by the compiler. Violations of these restrictions can result in undefined behaviour. - - Control-flow should not jump into or out of verbatim blocks, - but it can jump within the same verbatim block. - - Stack contents apart from the input and output parameters - should not be accessed. - - The stack height difference should be exactly ``m - n`` - (output slots minus input slots). - - Verbatim bytecode cannot make any assumptions about the - surrounding bytecode. All required parameters have to be - passed in as stack variables. +- Control-flow should not jump into or out of verbatim blocks, + but it can jump within the same verbatim block. +- Stack contents apart from the input and output parameters + should not be accessed. +- The stack height difference should be exactly ``m - n`` + (output slots minus input slots). +- Verbatim bytecode cannot make any assumptions about the + surrounding bytecode. All required parameters have to be + passed in as stack variables. The optimizer does not analyze verbatim bytecode and always assumes that it modifies all aspects of state and thus can only @@ -1175,11 +1178,13 @@ intermediate states. This allows for easy debugging and verification of the opti Please refer to the general :ref:`optimizer documentation ` for more details about the different optimization stages and how to use the optimizer. -If you want to use Solidity in stand-alone Yul mode, you activate the optimizer using ``--optimize``: +If you want to use Solidity in stand-alone Yul mode, you activate the optimizer using ``--optimize`` +and optionally specify the :ref:`expected number of contract executions ` with +``--optimize-runs``: .. code-block:: sh - solc --strict-assembly --optimize + solc --strict-assembly --optimize --optimize-runs 200 In Solidity mode, the Yul optimizer is activated together with the regular optimizer. diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index f63f3ede6..dad9635d7 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -76,15 +76,15 @@ namespace string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) { if (!_location.hasText() || _sourceCodes.empty()) - return ""; + return {}; - auto it = _sourceCodes.find(_location.source->name()); + auto it = _sourceCodes.find(*_location.sourceName); if (it == _sourceCodes.end()) - return ""; + return {}; string const& source = it->second; if (static_cast(_location.start) >= source.size()) - return ""; + return {}; string cut = source.substr(static_cast(_location.start), static_cast(_location.end - _location.start)); auto newLinePos = cut.find_first_of("\n"); @@ -152,8 +152,8 @@ public: if (!m_location.isValid()) return; m_out << m_prefix << " /*"; - if (m_location.source) - m_out << " \"" + m_location.source->name() + "\""; + if (m_location.sourceName) + m_out << " " + escapeAndQuoteString(*m_location.sourceName); if (m_location.hasText()) m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end); m_out << " " << locationFromSources(m_sourceCodes, m_location); @@ -235,9 +235,9 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) for (AssemblyItem const& i: m_items) { int sourceIndex = -1; - if (i.location().source) + if (i.location().sourceName) { - auto iter = _sourceIndices.find(i.location().source->name()); + auto iter = _sourceIndices.find(*i.location().sourceName); if (iter != _sourceIndices.end()) sourceIndex = static_cast(iter->second); } diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index b4acf6f87..75c0e4dca 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -350,8 +350,8 @@ std::string AssemblyItem::computeSourceMapping( SourceLocation const& location = item.location(); int length = location.start != -1 && location.end != -1 ? location.end - location.start : -1; int sourceIndex = - location.source && _sourceIndicesMap.count(location.source->name()) ? - static_cast(_sourceIndicesMap.at(location.source->name())) : + (location.sourceName && _sourceIndicesMap.count(*location.sourceName)) ? + static_cast(_sourceIndicesMap.at(*location.sourceName)) : -1; char jump = '-'; if (item.getJumpType() == evmasm::AssemblyItem::JumpType::IntoFunction) diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt index 477d3695c..c98877963 100644 --- a/liblangutil/CMakeLists.txt +++ b/liblangutil/CMakeLists.txt @@ -13,6 +13,7 @@ set(sources ParserBase.h Scanner.cpp Scanner.h + CharStreamProvider.h SemVerHandler.cpp SemVerHandler.h SourceLocation.h diff --git a/liblangutil/CharStream.cpp b/liblangutil/CharStream.cpp index 12e4f642c..8ab99fca8 100644 --- a/liblangutil/CharStream.cpp +++ b/liblangutil/CharStream.cpp @@ -45,9 +45,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** - * @author Christian - * @date 2014 - * Solidity scanner. + * Character stream / input file. */ #include @@ -118,3 +116,15 @@ tuple CharStream::translatePositionToLineColumn(int _position) const } return tuple(lineNumber, searchPosition - lineStart); } + +string_view CharStream::text(SourceLocation const& _location) const +{ + if (!_location.hasText()) + return {}; + solAssert(_location.sourceName && *_location.sourceName == m_name, ""); + solAssert(static_cast(_location.end) <= m_source.size(), ""); + return string_view{m_source}.substr( + static_cast(_location.start), + static_cast(_location.end - _location.start) + ); +} diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h index 23a0781fc..4b729771f 100644 --- a/liblangutil/CharStream.h +++ b/liblangutil/CharStream.h @@ -45,9 +45,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** - * @author Christian - * @date 2014 - * Solidity scanner. + * Character stream / input file. */ #pragma once @@ -60,6 +58,8 @@ namespace solidity::langutil { +struct SourceLocation; + /** * Bidirectional stream of characters. * @@ -69,8 +69,8 @@ class CharStream { public: CharStream() = default; - explicit CharStream(std::string _source, std::string name): - m_source(std::move(_source)), m_name(std::move(name)) {} + CharStream(std::string _source, std::string _name): + m_source(std::move(_source)), m_name(std::move(_name)) {} size_t position() const { return m_position; } bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); } @@ -90,6 +90,8 @@ public: std::string const& source() const noexcept { return m_source; } std::string const& name() const noexcept { return m_name; } + size_t size() const { return m_source.size(); } + ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors @@ -112,6 +114,10 @@ public: return true; } + /// @returns the substring of the source that the source location references. + /// Returns an empty string view if the source location does not `hasText()`. + std::string_view text(SourceLocation const& _location) const; + private: std::string m_source; std::string m_name; diff --git a/liblangutil/CharStreamProvider.h b/liblangutil/CharStreamProvider.h new file mode 100644 index 000000000..1f645f7ef --- /dev/null +++ b/liblangutil/CharStreamProvider.h @@ -0,0 +1,57 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Interface to retrieve the character stream by a source name. + */ + +#pragma once + +#include +#include + +#include + +namespace solidity::langutil +{ + +/** + * Interface to retrieve a CharStream (source) from a source name. + * Used especially for printing error information. + */ +class CharStreamProvider +{ +public: + virtual ~CharStreamProvider() = default; + virtual CharStream const& charStream(std::string const& _sourceName) const = 0; +}; + +class SingletonCharStreamProvider: public CharStreamProvider +{ +public: + explicit SingletonCharStreamProvider(CharStream const& _charStream): + m_charStream(_charStream) {} + CharStream const& charStream(std::string const& _sourceName) const override + { + solAssert(m_charStream.name() == _sourceName, ""); + return m_charStream; + } +private: + CharStream const& m_charStream; +}; + +} diff --git a/liblangutil/ParserBase.h b/liblangutil/ParserBase.h index c3fb1056d..6c5ce081b 100644 --- a/liblangutil/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -24,7 +24,6 @@ #pragma once #include -#include #include #include @@ -33,6 +32,7 @@ namespace solidity::langutil class ErrorReporter; class Scanner; +struct SourceLocation; struct ErrorId; class ParserBase @@ -47,7 +47,7 @@ public: m_parserErrorRecovery = _parserErrorRecovery; } - std::shared_ptr source() const { return m_scanner->charStream(); } + virtual ~ParserBase() = default; protected: /// Utility class that creates an error and throws an exception if the diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 5dbc2529c..87be458fd 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -135,24 +135,11 @@ private: bool m_complete; }; -void Scanner::reset(CharStream _source) -{ - m_source = make_shared(std::move(_source)); - reset(); -} - -void Scanner::reset(shared_ptr _source) -{ - solAssert(_source.get() != nullptr, "You MUST provide a CharStream when resetting."); - m_source = std::move(_source); - reset(); -} - void Scanner::reset() { - m_source->reset(); + m_source.reset(); m_kind = ScannerKind::Solidity; - m_char = m_source->get(); + m_char = m_source.get(); skipWhitespace(); next(); next(); @@ -161,7 +148,7 @@ void Scanner::reset() void Scanner::setPosition(size_t _offset) { - m_char = m_source->setPosition(_offset); + m_char = m_source.setPosition(_offset); scanToken(); next(); next(); @@ -227,7 +214,7 @@ void Scanner::rescan() rollbackTo = static_cast(m_tokens[Current].location.start); else rollbackTo = static_cast(m_skippedComments[Current].location.start); - m_char = m_source->rollback(m_source->position() - rollbackTo); + m_char = m_source.rollback(m_source.position() - rollbackTo); next(); next(); next(); @@ -322,12 +309,12 @@ Token Scanner::skipSingleLineComment() { // Line terminator is not part of the comment. If it is a // non-ascii line terminator, it will result in a parser error. - size_t startPosition = m_source->position(); + size_t startPosition = m_source.position(); while (!isUnicodeLinebreak()) if (!advance()) break; - ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + ScannerError unicodeDirectionError = validateBiDiMarkup(m_source, startPosition); if (unicodeDirectionError != ScannerError::NoError) return setError(unicodeDirectionError); @@ -360,28 +347,28 @@ bool Scanner::tryScanEndOfLine() size_t Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); - size_t endPosition = m_source->position(); + size_t endPosition = m_source.position(); skipWhitespaceExceptUnicodeLinebreak(); while (!isSourcePastEndOfInput()) { - endPosition = m_source->position(); + endPosition = m_source.position(); if (tryScanEndOfLine()) { // Check if next line is also a single-line comment. // If any whitespaces were skipped, use source position before. if (!skipWhitespaceExceptUnicodeLinebreak()) - endPosition = m_source->position(); + endPosition = m_source.position(); - 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) == '/') { - if (!m_source->isPastEndOfInput(4) && m_source->get(3) == '/') + if (!m_source.isPastEndOfInput(4) && m_source.get(3) == '/') break; // "////" is not a documentation comment - m_char = m_source->advanceAndGet(3); + m_char = m_source.advanceAndGet(3); if (atEndOfLine()) continue; addCommentLiteralChar('\n'); @@ -402,7 +389,7 @@ size_t Scanner::scanSingleLineDocComment() Token Scanner::skipMultiLineComment() { - size_t startPosition = m_source->position(); + size_t startPosition = m_source.position(); while (!isSourcePastEndOfInput()) { char prevChar = m_char; @@ -413,7 +400,7 @@ Token Scanner::skipMultiLineComment() // multi-line comments are treated as whitespace. if (prevChar == '*' && m_char == '/') { - ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + ScannerError unicodeDirectionError = validateBiDiMarkup(m_source, startPosition); if (unicodeDirectionError != ScannerError::NoError) return setError(unicodeDirectionError); @@ -440,22 +427,22 @@ Token Scanner::scanMultiLineDocComment() if (atEndOfLine()) { 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 - m_char = m_source->advanceAndGet(1); + m_char = m_source.advanceAndGet(1); if (atEndOfLine()) // ignores empty lines continue; if (charsAdded) addCommentLiteralChar('\n'); // corresponds to the end of previous line } - 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; } @@ -463,9 +450,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; } @@ -497,7 +484,7 @@ Token Scanner::scanSlash() return skipSingleLineComment(); // doxygen style /// comment m_skippedComments[NextNext].location.start = firstSlashPosition; - m_skippedComments[NextNext].location.source = m_source; + m_skippedComments[NextNext].location.sourceName = m_sourceName; m_skippedComments[NextNext].token = Token::CommentLiteral; m_skippedComments[NextNext].location.end = static_cast(scanSingleLineDocComment()); return Token::Whitespace; @@ -526,7 +513,7 @@ Token Scanner::scanSlash() return skipMultiLineComment(); // we actually have a multiline documentation comment m_skippedComments[NextNext].location.start = firstSlashPosition; - m_skippedComments[NextNext].location.source = m_source; + m_skippedComments[NextNext].location.sourceName = m_sourceName; Token comment = scanMultiLineDocComment(); m_skippedComments[NextNext].location.end = static_cast(sourcePos()); m_skippedComments[NextNext].token = comment; @@ -766,7 +753,7 @@ void Scanner::scanToken() } while (token == Token::Whitespace); m_tokens[NextNext].location.end = static_cast(sourcePos()); - m_tokens[NextNext].location.source = m_source; + m_tokens[NextNext].location.sourceName = m_sourceName; m_tokens[NextNext].token = token; m_tokens[NextNext].extendedTokenInfo = make_tuple(m, n); } @@ -820,11 +807,11 @@ bool Scanner::isUnicodeLinebreak() if (0x0a <= m_char && m_char <= 0x0d) // line feed, vertical tab, form feed, carriage return return true; - if (!m_source->isPastEndOfInput(1) && uint8_t(m_source->get(0)) == 0xc2 && uint8_t(m_source->get(1)) == 0x85) + 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; - 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 + 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 @@ -834,7 +821,7 @@ bool Scanner::isUnicodeLinebreak() Token Scanner::scanString(bool const _isUnicode) { - size_t startPosition = m_source->position(); + size_t startPosition = m_source.position(); char const quote = m_char; advance(); // consume quote LiteralScope literal(this, LITERAL_TYPE_STRING); @@ -865,7 +852,7 @@ Token Scanner::scanString(bool const _isUnicode) if (_isUnicode) { - ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + ScannerError unicodeDirectionError = validateBiDiMarkup(m_source, startPosition); if (unicodeDirectionError != ScannerError::NoError) return setError(unicodeDirectionError); } @@ -919,7 +906,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. } @@ -965,7 +952,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. @@ -973,7 +960,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(); @@ -990,7 +977,7 @@ Token Scanner::scanNumber(char _charSeen) solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); if (kind != DECIMAL) return setError(ScannerError::IllegalExponent); - else if (!m_source->isPastEndOfInput(1) && m_source->get(1) == '_') + 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. diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 97f60bbda..e9957850c 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -55,8 +55,6 @@ #include #include #include -#include -#include #include #include @@ -102,17 +100,13 @@ class Scanner { friend class LiteralScope; public: - explicit Scanner(std::shared_ptr _source) { reset(std::move(_source)); } - explicit Scanner(CharStream _source = CharStream()) { reset(std::move(_source)); } + explicit Scanner(CharStream& _source): + m_source(_source), + m_sourceName{std::make_shared(_source.name())} + { + reset(); + } - std::string const& source() const noexcept { return m_source->source(); } - - std::shared_ptr charStream() noexcept { return m_source; } - std::shared_ptr charStream() const noexcept { return m_source; } - - /// Resets the scanner as if newly constructed with _source as input. - void reset(CharStream _source); - void reset(std::shared_ptr _source); /// Resets scanner to the start of input. void reset(); @@ -125,6 +119,8 @@ public: rescan(); } + CharStream const& charStream() const noexcept { return m_source; } + /// @returns the next token and advances input Token next(); @@ -177,14 +173,6 @@ public: Token peekNextNextToken() const { return m_tokens[NextNext].token; } ///@} - ///@{ - ///@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 translatePositionToLineColumn(int _position) const { return m_source->translatePositionToLineColumn(_position); } - ///@} - private: inline Token setError(ScannerError _error) noexcept @@ -211,8 +199,8 @@ private: void addUnicodeAsUTF8(unsigned codepoint); ///@} - bool advance() { m_char = m_source->advanceAndGet(); return !m_source->isPastEndOfInput(); } - void rollback(size_t _amount) { m_char = m_source->rollback(_amount); } + bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } + void rollback(size_t _amount) { m_char = m_source.rollback(_amount); } /// Rolls back to the start of the current token and re-runs the scanner. void rescan(); @@ -261,15 +249,16 @@ private: bool isUnicodeLinebreak(); /// Return the current source position. - size_t sourcePos() const { return m_source->position(); } - bool isSourcePastEndOfInput() const { return m_source->isPastEndOfInput(); } + size_t sourcePos() const { return m_source.position(); } + bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } enum TokenIndex { Current, Next, NextNext }; TokenDesc m_skippedComments[3] = {}; // desc for the current, next and nextnext skipped comment TokenDesc m_tokens[3] = {}; // desc for the current, next and nextnext token - std::shared_ptr m_source; + CharStream& m_source; + std::shared_ptr m_sourceName; ScannerKind m_kind = ScannerKind::Solidity; diff --git a/liblangutil/SemVerHandler.cpp b/liblangutil/SemVerHandler.cpp index af408789c..ede1cd7be 100644 --- a/liblangutil/SemVerHandler.cpp +++ b/liblangutil/SemVerHandler.cpp @@ -23,12 +23,20 @@ #include +#include + #include using namespace std; using namespace solidity; using namespace solidity::langutil; +SemVerMatchExpressionParser::SemVerMatchExpressionParser(vector _tokens, vector _literals): + m_tokens(std::move(_tokens)), m_literals(std::move(_literals)) +{ + solAssert(m_tokens.size() == m_literals.size(), ""); +} + SemVerVersion::SemVerVersion(string const& _versionString) { auto i = _versionString.begin(); diff --git a/liblangutil/SemVerHandler.h b/liblangutil/SemVerHandler.h index 0c4f57a03..bce4d736b 100644 --- a/liblangutil/SemVerHandler.h +++ b/liblangutil/SemVerHandler.h @@ -85,11 +85,7 @@ struct SemVerMatchExpression class SemVerMatchExpressionParser { public: - SemVerMatchExpressionParser(std::vector _tokens, std::vector _literals): - m_tokens(std::move(_tokens)), m_literals(std::move(_literals)) - { - solAssert(m_tokens.size() == m_literals.size(), ""); - } + SemVerMatchExpressionParser(std::vector _tokens, std::vector _literals); /// Returns an expression if it was parseable, or nothing otherwise. std::optional parse(); diff --git a/liblangutil/SourceLocation.cpp b/liblangutil/SourceLocation.cpp index 8f3187437..72f2506e8 100644 --- a/liblangutil/SourceLocation.cpp +++ b/liblangutil/SourceLocation.cpp @@ -21,16 +21,18 @@ #include #include -using namespace solidity; -namespace solidity::langutil -{ +#include -SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex) +using namespace solidity; +using namespace solidity::langutil; +using namespace std; + +SourceLocation solidity::langutil::parseSourceLocation(string const& _input, vector> const& _sourceNames) { // Expected input: "start:length:sourceindex" - enum SrcElem : size_t { Start, Length, Index }; + enum SrcElem: size_t { Start, Length, Index }; - std::vector pos; + vector pos; boost::algorithm::split(pos, _input, boost::is_any_of(":")); @@ -38,19 +40,28 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string auto const sourceIndex = stoi(pos[Index]); astAssert( - sourceIndex == -1 || _maxIndex >= static_cast(sourceIndex), + sourceIndex == -1 || (0 <= sourceIndex && static_cast(sourceIndex) < _sourceNames.size()), "'src'-field ill-formatted or src-index too high" ); int start = stoi(pos[Start]); int end = start + stoi(pos[Length]); - // ASSUMPTION: only the name of source is used from here on, the m_source of the CharStream-Object can be empty - std::shared_ptr source; + SourceLocation result{start, end, {}}; if (sourceIndex != -1) - source = std::make_shared("", _sourceName); - - return SourceLocation{start, end, source}; + result.sourceName = _sourceNames.at(static_cast(sourceIndex)); + return result; } +std::ostream& solidity::langutil::operator<<(std::ostream& _out, SourceLocation const& _location) +{ + if (!_location.isValid()) + return _out << "NO_LOCATION_SPECIFIED"; + + if (_location.sourceName) + _out << *_location.sourceName; + + _out << "[" << _location.start << "," << _location.end << "]"; + + return _out; } diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index eb858bd06..66cef52fb 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -23,18 +23,14 @@ #pragma once -#include -#include - -#include - -#include +#include #include #include +#include +#include namespace solidity::langutil { -struct SourceLocationError: virtual util::Exception {}; /** * Representation of an interval of source positions. @@ -44,51 +40,44 @@ struct SourceLocation { bool operator==(SourceLocation const& _other) const { - return source.get() == _other.source.get() && start == _other.start && end == _other.end; + return start == _other.start && end == _other.end && equalSources(_other); } bool operator!=(SourceLocation const& _other) const { return !operator==(_other); } - inline bool operator<(SourceLocation const& _other) const + bool operator<(SourceLocation const& _other) const { - if (!source|| !_other.source) - return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end); + if (!sourceName || !_other.sourceName) + return std::make_tuple(int(!!sourceName), start, end) < std::make_tuple(int(!!_other.sourceName), _other.start, _other.end); else - return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end); + return std::make_tuple(*sourceName, start, end) < std::make_tuple(*_other.sourceName, _other.start, _other.end); } - inline bool contains(SourceLocation const& _other) const + bool contains(SourceLocation const& _other) const { - if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) + if (!hasText() || !_other.hasText() || !equalSources(_other)) return false; return start <= _other.start && _other.end <= end; } - inline bool intersects(SourceLocation const& _other) const + bool intersects(SourceLocation const& _other) const { - if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) + if (!hasText() || !_other.hasText() || !equalSources(_other)) return false; return _other.start < end && start < _other.end; } - bool isValid() const { return source || start != -1 || end != -1; } - - bool hasText() const + bool equalSources(SourceLocation const& _other) const { - return - source && - 0 <= start && - start <= end && - end <= int(source->source().length()); + if (!!sourceName != !!_other.sourceName) + return false; + if (sourceName && *sourceName != *_other.sourceName) + return false; + return true; } - std::string text() const - { - assertThrow(source, SourceLocationError, "Requested text from null source."); - assertThrow(0 <= start, SourceLocationError, "Invalid source location."); - assertThrow(start <= end, SourceLocationError, "Invalid source location."); - assertThrow(end <= int(source->source().length()), SourceLocationError, "Invalid source location."); - return source->source().substr(size_t(start), size_t(end - start)); - } + bool isValid() const { return sourceName || start != -1 || end != -1; } + + bool hasText() const { return sourceName && 0 <= start && start <= end; } /// @returns the smallest SourceLocation that contains both @param _a and @param _b. /// Assumes that @param _a and @param _b refer to the same source (exception: if the source of either one @@ -97,8 +86,8 @@ struct SourceLocation /// @param _b, then start resp. end of the result will be -1 as well). static SourceLocation smallestCovering(SourceLocation _a, SourceLocation const& _b) { - if (!_a.source) - _a.source = _b.source; + if (!_a.sourceName) + _a.sourceName = _b.sourceName; if (_a.start < 0) _a.start = _b.start; @@ -112,27 +101,15 @@ struct SourceLocation int start = -1; int end = -1; - std::shared_ptr source; + std::shared_ptr sourceName; }; -SourceLocation const parseSourceLocation( +SourceLocation parseSourceLocation( std::string const& _input, - std::string const& _sourceName, - size_t _maxIndex = std::numeric_limits::max() + std::vector> const& _sourceNames ); /// Stream output for Location (used e.g. in boost exceptions). -inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location) -{ - if (!_location.isValid()) - return _out << "NO_LOCATION_SPECIFIED"; - - if (_location.source) - _out << _location.source->name(); - - _out << "[" << _location.start << "," << _location.end << "]"; - - return _out; -} +std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location); } diff --git a/liblangutil/SourceReferenceExtractor.cpp b/liblangutil/SourceReferenceExtractor.cpp index 3eac873aa..717e590df 100644 --- a/liblangutil/SourceReferenceExtractor.cpp +++ b/liblangutil/SourceReferenceExtractor.cpp @@ -16,8 +16,9 @@ */ // SPDX-License-Identifier: GPL-3.0 #include -#include #include +#include +#include #include #include @@ -26,46 +27,57 @@ using namespace std; using namespace solidity; using namespace solidity::langutil; -SourceReferenceExtractor::Message SourceReferenceExtractor::extract(util::Exception const& _exception, string _category) +SourceReferenceExtractor::Message SourceReferenceExtractor::extract( + CharStreamProvider const& _charStreamProvider, + util::Exception const& _exception, + string _category +) { SourceLocation const* location = boost::get_error_info(_exception); string const* message = boost::get_error_info(_exception); - SourceReference primary = extract(location, message ? *message : ""); + SourceReference primary = extract(_charStreamProvider, location, message ? *message : ""); std::vector secondary; auto secondaryLocation = boost::get_error_info(_exception); if (secondaryLocation && !secondaryLocation->infos.empty()) for (auto const& info: secondaryLocation->infos) - secondary.emplace_back(extract(&info.second, info.first)); + secondary.emplace_back(extract(_charStreamProvider, &info.second, info.first)); return Message{std::move(primary), _category, std::move(secondary), nullopt}; } -SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Error const& _error) +SourceReferenceExtractor::Message SourceReferenceExtractor::extract( + CharStreamProvider const& _charStreamProvider, + Error const& _error +) { string category = (_error.type() == Error::Type::Warning) ? "Warning" : "Error"; - Message message = extract(_error, category); + Message message = extract(_charStreamProvider, _error, category); message.errorId = _error.errorId(); return message; } -SourceReference SourceReferenceExtractor::extract(SourceLocation const* _location, std::string message) +SourceReference SourceReferenceExtractor::extract( + CharStreamProvider const& _charStreamProvider, + SourceLocation const* _location, + std::string message +) { - if (!_location || !_location->source.get()) // Nothing we can extract here + if (!_location || !_location->sourceName) // Nothing we can extract here return SourceReference::MessageOnly(std::move(message)); if (!_location->hasText()) // No source text, so we can only extract the source name - return SourceReference::MessageOnly(std::move(message), _location->source->name()); + return SourceReference::MessageOnly(std::move(message), *_location->sourceName); - shared_ptr const& source = _location->source; + CharStream const& charStream = _charStreamProvider.charStream(*_location->sourceName); - LineColumn const interest = source->translatePositionToLineColumn(_location->start); + LineColumn const interest = charStream.translatePositionToLineColumn(_location->start); LineColumn start = interest; - LineColumn end = source->translatePositionToLineColumn(_location->end); + LineColumn end = charStream.translatePositionToLineColumn(_location->end); bool const isMultiline = start.line != end.line; - string line = source->lineAtPosition(_location->start); + string line = charStream.lineAtPosition(_location->start); int locationLength = isMultiline ? @@ -102,7 +114,7 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio return SourceReference{ std::move(message), - source->name(), + *_location->sourceName, interest, isMultiline, line, diff --git a/liblangutil/SourceReferenceExtractor.h b/liblangutil/SourceReferenceExtractor.h index 7eb54845c..d26a941f5 100644 --- a/liblangutil/SourceReferenceExtractor.h +++ b/liblangutil/SourceReferenceExtractor.h @@ -28,6 +28,8 @@ namespace solidity::langutil { +class CharStreamProvider; + struct LineColumn { int line = {-1}; @@ -67,9 +69,9 @@ namespace SourceReferenceExtractor std::optional errorId; }; - Message extract(util::Exception const& _exception, std::string _category); - Message extract(Error const& _error); - SourceReference extract(SourceLocation const* _location, std::string message = ""); + Message extract(CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, std::string _category); + Message extract(CharStreamProvider const& _charStreamProvider, Error const& _error); + SourceReference extract(CharStreamProvider const& _charStreamProvider, SourceLocation const* _location, std::string message = ""); } } diff --git a/liblangutil/SourceReferenceFormatter.cpp b/liblangutil/SourceReferenceFormatter.cpp index cc2afc24b..20d0f3306 100644 --- a/liblangutil/SourceReferenceFormatter.cpp +++ b/liblangutil/SourceReferenceFormatter.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include #include @@ -45,6 +47,14 @@ std::string replaceNonTabs(std::string_view _utf8Input, char _filler) } +std::string SourceReferenceFormatter::formatErrorInformation(Error const& _error, CharStream const& _charStream) +{ + return formatErrorInformation( + _error, + SingletonCharStreamProvider(_charStream) + ); +} + AnsiColorized SourceReferenceFormatter::normalColored() const { return AnsiColorized(m_stream, m_colored, {WHITE}); @@ -173,10 +183,16 @@ void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtracto void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category) { - printExceptionInformation(SourceReferenceExtractor::extract(_exception, _category)); + printExceptionInformation(SourceReferenceExtractor::extract(m_charStreamProvider, _exception, _category)); +} + +void SourceReferenceFormatter::printErrorInformation(ErrorList const& _errors) +{ + for (auto const& error: _errors) + printErrorInformation(*error); } void SourceReferenceFormatter::printErrorInformation(Error const& _error) { - printExceptionInformation(SourceReferenceExtractor::extract(_error)); + printExceptionInformation(SourceReferenceExtractor::extract(m_charStreamProvider, _error)); } diff --git a/liblangutil/SourceReferenceFormatter.h b/liblangutil/SourceReferenceFormatter.h index dac76912d..fc1f418a2 100644 --- a/liblangutil/SourceReferenceFormatter.h +++ b/liblangutil/SourceReferenceFormatter.h @@ -32,42 +32,58 @@ namespace solidity::langutil { + +class CharStream; +class CharStreamProvider; struct SourceLocation; class SourceReferenceFormatter { public: - SourceReferenceFormatter(std::ostream& _stream, bool _colored, bool _withErrorIds): - m_stream(_stream), m_colored(_colored), m_withErrorIds(_withErrorIds) + SourceReferenceFormatter( + std::ostream& _stream, + CharStreamProvider const& _charStreamProvider, + bool _colored, + bool _withErrorIds + ): + m_stream(_stream), m_charStreamProvider(_charStreamProvider), m_colored(_colored), m_withErrorIds(_withErrorIds) {} /// Prints source location if it is given. void printSourceLocation(SourceReference const& _ref); void printExceptionInformation(SourceReferenceExtractor::Message const& _msg); void printExceptionInformation(util::Exception const& _exception, std::string const& _category); + void printErrorInformation(langutil::ErrorList const& _errors); void printErrorInformation(Error const& _error); static std::string formatExceptionInformation( util::Exception const& _exception, std::string const& _name, + CharStreamProvider const& _charStreamProvider, bool _colored = false, bool _withErrorIds = false ) { std::ostringstream errorOutput; - SourceReferenceFormatter formatter(errorOutput, _colored, _withErrorIds); + SourceReferenceFormatter formatter(errorOutput, _charStreamProvider, _colored, _withErrorIds); formatter.printExceptionInformation(_exception, _name); return errorOutput.str(); } - static std::string formatErrorInformation(Error const& _error) + static std::string formatErrorInformation( + Error const& _error, + CharStreamProvider const& _charStreamProvider + ) { return formatExceptionInformation( _error, - (_error.type() == Error::Type::Warning) ? "Warning" : "Error" + (_error.type() == Error::Type::Warning) ? "Warning" : "Error", + _charStreamProvider ); } + static std::string formatErrorInformation(Error const& _error, CharStream const& _charStream); + private: util::AnsiColorized normalColored() const; util::AnsiColorized frameColored() const; @@ -79,6 +95,7 @@ private: private: std::ostream& m_stream; + CharStreamProvider const& m_charStreamProvider; bool m_colored; bool m_withErrorIds; }; diff --git a/liblangutil/Token.cpp b/liblangutil/Token.cpp index b6cb1e0ed..8bdaff1fd 100644 --- a/liblangutil/Token.cpp +++ b/liblangutil/Token.cpp @@ -41,6 +41,7 @@ // along with solidity. If not, see . #include +#include #include using namespace std; @@ -48,6 +49,24 @@ using namespace std; namespace solidity::langutil { +Token TokenTraits::AssignmentToBinaryOp(Token op) +{ + solAssert(isAssignmentOp(op) && op != Token::Assign, ""); + return static_cast(static_cast(op) + (static_cast(Token::BitOr) - static_cast(Token::AssignBitOr))); +} + +std::string ElementaryTypeNameToken::toString(bool const& tokenValue) 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); +} + void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second) { solAssert(TokenTraits::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(TokenTraits::toString(_baseType))); diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 7ac607aaa..9e534684a 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -42,8 +42,6 @@ #pragma once -#include -#include #include #include @@ -330,11 +328,7 @@ namespace TokenTraits bool isYulKeyword(std::string const& _literal); - inline Token AssignmentToBinaryOp(Token op) - { - solAssert(isAssignmentOp(op) && op != Token::Assign, ""); - return static_cast(static_cast(op) + (static_cast(Token::BitOr) - static_cast(Token::AssignBitOr))); - } + Token AssignmentToBinaryOp(Token op); // @returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. @@ -394,17 +388,7 @@ public: 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); - } + std::string toString(bool const& tokenValue = false) const; private: Token m_token; diff --git a/libsmtutil/SMTPortfolio.cpp b/libsmtutil/SMTPortfolio.cpp index de00c1f79..aec9b0a1c 100644 --- a/libsmtutil/SMTPortfolio.cpp +++ b/libsmtutil/SMTPortfolio.cpp @@ -40,7 +40,8 @@ SMTPortfolio::SMTPortfolio( ): SolverInterface(_queryTimeout) { - m_solvers.emplace_back(make_unique(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout)); + if (_enabledSolvers.smtlib2) + m_solvers.emplace_back(make_unique(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout)); #ifdef HAVE_Z3 if (_enabledSolvers.z3 && Z3Interface::available()) m_solvers.emplace_back(make_unique(m_queryTimeout)); @@ -143,10 +144,11 @@ pair> SMTPortfolio::check(vector const& vector SMTPortfolio::unhandledQueries() { // This code assumes that the constructor guarantees that - // SmtLib2Interface is in position 0. - smtAssert(!m_solvers.empty(), ""); - smtAssert(dynamic_cast(m_solvers.front().get()), ""); - return m_solvers.front()->unhandledQueries(); + // SmtLib2Interface is in position 0, if enabled. + if (!m_solvers.empty()) + if (auto smtlib2 = dynamic_cast(m_solvers.front().get())) + return smtlib2->unhandledQueries(); + return {}; } bool SMTPortfolio::solverAnswered(CheckResult result) diff --git a/libsmtutil/SolverInterface.h b/libsmtutil/SolverInterface.h index 6eb6982fb..21648c245 100644 --- a/libsmtutil/SolverInterface.h +++ b/libsmtutil/SolverInterface.h @@ -23,10 +23,13 @@ #include +#include + #include #include #include #include +#include #include #include @@ -36,16 +39,71 @@ namespace solidity::smtutil struct SMTSolverChoice { bool cvc4 = false; + bool smtlib2 = false; bool z3 = false; - static constexpr SMTSolverChoice All() { return {true, true}; } - static constexpr SMTSolverChoice CVC4() { return {true, false}; } - static constexpr SMTSolverChoice Z3() { return {false, true}; } - static constexpr SMTSolverChoice None() { return {false, false}; } + static constexpr SMTSolverChoice All() { return {true, true, true}; } + static constexpr SMTSolverChoice CVC4() { return {true, false, false}; } + static constexpr SMTSolverChoice SMTLIB2() { return {false, true, false}; } + static constexpr SMTSolverChoice Z3() { return {false, false, true}; } + static constexpr SMTSolverChoice None() { return {false, false, false}; } + + static std::optional fromString(std::string const& _solvers) + { + SMTSolverChoice solvers; + if (_solvers == "all") + { + smtAssert(solvers.setSolver("cvc4"), ""); + smtAssert(solvers.setSolver("smtlib2"), ""); + smtAssert(solvers.setSolver("z3"), ""); + } + else + for (auto&& s: _solvers | ranges::views::split(',') | ranges::to>()) + if (!solvers.setSolver(s)) + return {}; + + return solvers; + } + + SMTSolverChoice& operator&(SMTSolverChoice const& _other) + { + cvc4 &= _other.cvc4; + smtlib2 &= _other.smtlib2; + z3 &= _other.z3; + return *this; + } + + SMTSolverChoice& operator&=(SMTSolverChoice const& _other) + { + return *this & _other; + } + + bool operator!=(SMTSolverChoice const& _other) const noexcept { return !(*this == _other); } + + bool operator==(SMTSolverChoice const& _other) const noexcept + { + return cvc4 == _other.cvc4 && + smtlib2 == _other.smtlib2 && + z3 == _other.z3; + } + + bool setSolver(std::string const& _solver) + { + static std::set const solvers{"cvc4", "smtlib2", "z3"}; + if (!solvers.count(_solver)) + return false; + if (_solver == "cvc4") + cvc4 = true; + else if (_solver == "smtlib2") + smtlib2 = true; + else if (_solver == "z3") + z3 = true; + return true; + } bool none() { return !some(); } - bool some() { return cvc4 || z3; } - bool all() { return cvc4 && z3; } + bool some() { return cvc4 || smtlib2 || z3; } + bool all() { return cvc4 && smtlib2 && z3; } }; enum class CheckResult diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 97308f7e7..20c4f2c46 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -520,9 +520,9 @@ bool DeclarationRegistrationHelper::registerDeclaration( Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name); solAssert(conflictingDeclaration, ""); bool const comparable = - _errorLocation->source && - conflictingDeclaration->location().source && - _errorLocation->source->name() == conflictingDeclaration->location().source->name(); + _errorLocation->sourceName && + conflictingDeclaration->location().sourceName && + *_errorLocation->sourceName == *conflictingDeclaration->location().sourceName; if (comparable && _errorLocation->start < conflictingDeclaration->location().start) { firstDeclarationLocation = *_errorLocation; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 04745344f..412ad1aa9 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -68,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) string(";\""); // when reporting the warning, print the source name only - m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString); + m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().sourceName}, errorString); } if (!m_sourceUnit->annotation().useABICoderV2.set()) m_sourceUnit->annotation().useABICoderV2 = true; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index ea8662010..bae1b7d62 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -110,8 +110,8 @@ void ASTJsonConverter::setJsonNode( optional ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const { - if (_location.source && m_sourceIndices.count(_location.source->name())) - return m_sourceIndices.at(_location.source->name()); + if (_location.sourceName && m_sourceIndices.count(*_location.sourceName)) + return m_sourceIndices.at(*_location.sourceName); else return nullopt; } diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 916dbac14..5d7ba1211 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -57,14 +57,12 @@ ASTPointer ASTJsonImporter::nullOrCast(Json::Value const& _json) map> ASTJsonImporter::jsonToSourceUnit(map const& _sourceList) { - m_sourceList = _sourceList; for (auto const& src: _sourceList) - m_sourceLocations.emplace_back(make_shared(src.first)); - for (auto const& srcPair: m_sourceList) + m_sourceNames.emplace_back(make_shared(src.first)); + for (auto const& srcPair: _sourceList) { astAssert(!srcPair.second.isNull(), ""); astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'."); - m_currentSourceName = srcPair.first; m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first); } return m_sourceUnits; @@ -94,14 +92,14 @@ SourceLocation const ASTJsonImporter::createSourceLocation(Json::Value const& _n { astAssert(member(_node, "src").isString(), "'src' must be a string"); - return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_currentSourceName, m_sourceLocations.size()); + return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames); } SourceLocation ASTJsonImporter::createNameSourceLocation(Json::Value const& _node) { astAssert(member(_node, "nameLocation").isString(), "'nameLocation' must be a string"); - return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_currentSourceName, m_sourceLocations.size()); + return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_sourceNames); } template @@ -616,7 +614,7 @@ ASTPointer ASTJsonImporter::createInlineAssembly(Json::Value con astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!"); yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value()); - shared_ptr operations = make_shared(yul::AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST"))); + shared_ptr operations = make_shared(yul::AsmJsonImporter(m_sourceNames).createBlock(member(_node, "AST"))); return createASTNode( _node, nullOrASTString(_node, "documentation"), @@ -960,7 +958,8 @@ Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _nam Token ASTJsonImporter::scanSingleToken(Json::Value const& _node) { - langutil::Scanner scanner{langutil::CharStream(_node.asString(), "")}; + langutil::CharStream charStream(_node.asString(), ""); + langutil::Scanner scanner{charStream}; astAssert(scanner.peekNextToken() == Token::EOS, "Token string is too long."); return scanner.currentToken(); } diff --git a/libsolidity/ast/ASTJsonImporter.h b/libsolidity/ast/ASTJsonImporter.h index a4d43293f..3f1f5265c 100644 --- a/libsolidity/ast/ASTJsonImporter.h +++ b/libsolidity/ast/ASTJsonImporter.h @@ -152,13 +152,10 @@ private: ///@} // =========== member variables =============== - /// Stores filepath as sourcenames to AST in JSON format - std::map m_sourceList; - /// list of filepaths (used as sourcenames) - std::vector> m_sourceLocations; + /// list of source names, order by source index + std::vector> m_sourceNames; /// filepath to AST std::map> m_sourceUnits; - std::string m_currentSourceName; /// IDs already used by the nodes std::set m_usedIDs; /// Configured EVM version diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 7b673cca6..291bbc255 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -434,14 +434,14 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared(langutil::CharStream(_assembly, _sourceName)); + langutil::CharStream charStream(_assembly, _sourceName); yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); optional locationOverride; if (!_system) locationOverride = m_asm->currentSourceLocation(); shared_ptr parserResult = yul::Parser(errorReporter, dialect, std::move(locationOverride)) - .parse(scanner, false); + .parse(charStream); #ifdef SOL_OUTPUT_ASM cout << yul::AsmPrinter(&dialect)(*parserResult) << endl; #endif @@ -455,7 +455,9 @@ void CompilerContext::appendInlineAssembly( _assembly + "\n" "------------------ Errors: ----------------\n"; for (auto const& error: errorReporter.errors()) - message += SourceReferenceFormatter::formatErrorInformation(*error); + // TODO if we have "locationOverride", it will be the wrong char stream, + // but we do not have access to the solidity scanner. + message += SourceReferenceFormatter::formatErrorInformation(*error, charStream); message += "-------------------------------------------\n"; solAssert(false, message); @@ -489,8 +491,8 @@ void CompilerContext::appendInlineAssembly( solAssert(m_generatedYulUtilityCode.empty(), ""); m_generatedYulUtilityCode = yul::AsmPrinter(dialect)(*obj.code); string code = yul::AsmPrinter{dialect}(*obj.code); - scanner = make_shared(langutil::CharStream(m_generatedYulUtilityCode, _sourceName)); - obj.code = yul::Parser(errorReporter, dialect).parse(scanner, false); + langutil::CharStream charStream(m_generatedYulUtilityCode, _sourceName); + obj.code = yul::Parser(errorReporter, dialect).parse(charStream); *obj.analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(dialect, obj); } diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index d7c31a72b..bd86dc9c2 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -4029,7 +4029,7 @@ string YulUtilFunctions::negateNumberWrappingFunction(Type const& _type) IntegerType const& type = dynamic_cast(_type); solAssert(type.isSigned(), "Expected signed type!"); - string const functionName = "negate_" + _type.identifier(); + string const functionName = "negate_wrapping_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value) -> ret { diff --git a/libsolidity/codegen/ir/Common.cpp b/libsolidity/codegen/ir/Common.cpp index 4f414cb99..e9bbaa4e0 100644 --- a/libsolidity/codegen/ir/Common.cpp +++ b/libsolidity/codegen/ir/Common.cpp @@ -129,11 +129,12 @@ string IRNames::zeroValue(Type const& _type, string const& _variableName) string sourceLocationComment(langutil::SourceLocation const& _location, IRGenerationContext const& _context) { + solAssert(_location.sourceName, ""); return "/// @src " - + to_string(_context.sourceIndices().at(_location.source->name())) + + to_string(_context.sourceIndices().at(*_location.sourceName)) + ":" + to_string(_location.start) - + "," + + ":" + to_string(_location.end); } diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 9aab3ff5d..ddbe4054b 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -33,15 +33,13 @@ #include #include -#include -#include -#include #include +#include +#include +#include #include -#include - #include #include @@ -101,7 +99,10 @@ pair IRGenerator::run( { string errorMessage; for (auto const& error: asmStack.errors()) - errorMessage += langutil::SourceReferenceFormatter::formatErrorInformation(*error); + errorMessage += langutil::SourceReferenceFormatter::formatErrorInformation( + *error, + asmStack.charStream("") + ); solAssert(false, ir + "\n\nInvalid IR generated:\n" + errorMessage + "\n"); } asmStack.optimize(); @@ -132,6 +133,7 @@ string IRGenerator::generate( }; Whiskers t(R"( + /// @use-src object "" { code { @@ -166,6 +168,16 @@ string IRGenerator::generate( for (VariableDeclaration const* var: ContractType(_contract).immutableVariables()) m_context.registerImmutableVariable(*var); + auto invertedSourceIndicies = invertMap(m_context.sourceIndices()); + + string useSrcMap = joinHumanReadable( + ranges::views::transform(invertedSourceIndicies, [](auto&& _pair) { + return to_string(_pair.first) + ":" + escapeAndQuoteString(_pair.second); + }), + ", " + ); + + t("useSrcMap", useSrcMap); t("sourceLocationComment", sourceLocationComment(_contract, m_context)); t("CreationObject", IRNames::creationObject(_contract)); @@ -267,8 +279,8 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin string funName = IRNames::internalDispatch(arity); m_context.functionCollector().createFunction(funName, [&]() { Whiskers templ(R"( + function (fun, ) -> { - switch fun <#cases> case @@ -278,6 +290,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin default { () } } + )"); templ("sourceLocationComment", sourceLocationComment(_contract, m_context)); templ("functionName", funName); @@ -324,14 +337,19 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) return m_context.functionCollector().createFunction(functionName, [&]() { m_context.resetLocalVariables(); Whiskers t(R"( + function () -> { - } + )"); t("sourceLocationComment", sourceLocationComment(_function, m_context)); + t( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ); t("functionName", functionName); vector params; @@ -386,12 +404,13 @@ string IRGenerator::generateModifier( return m_context.functionCollector().createFunction(functionName, [&]() { m_context.resetLocalVariables(); Whiskers t(R"( + function () -> { - } + )"); t("functionName", functionName); vector retParamsIn; @@ -416,6 +435,11 @@ string IRGenerator::generateModifier( ); solAssert(modifier, ""); t("sourceLocationComment", sourceLocationComment(*modifier, m_context)); + t( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ); + switch (*_modifierInvocation.name().annotation().requiredLookup) { case VirtualLookup::Virtual: @@ -466,13 +490,18 @@ string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const& return m_context.functionCollector().createFunction(functionName, [&]() { m_context.resetLocalVariables(); Whiskers t(R"( + function () -> { - } + )"); t("sourceLocationComment", sourceLocationComment(_function, m_context)); + t( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ); t("functionName", functionName); vector retParams; vector retParamsIn; @@ -510,12 +539,17 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) solAssert(paramTypes.empty(), ""); solUnimplementedAssert(type->sizeOnStack() == 1, ""); return Whiskers(R"( + function () -> rval { - rval := loadimmutable("") } + )") ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ) ("functionName", functionName) ("id", to_string(_varDecl.id())) .render(); @@ -524,12 +558,17 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) { solAssert(paramTypes.empty(), ""); return Whiskers(R"( + function () -> { - := () } + )") ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ) ("functionName", functionName) ("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl)) ("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack())) @@ -641,16 +680,21 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) } return Whiskers(R"( + function () -> { - } + )") ("functionName", functionName) ("params", joinHumanReadable(parameters)) ("retVariables", joinHumanReadable(returnVariables)) ("code", std::move(code)) ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ) .render(); }); } @@ -757,6 +801,7 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract) m_context.resetLocalVariables(); m_context.functionCollector().createFunction(IRNames::constructor(*contract), [&]() { Whiskers t(R"( + function () { @@ -764,6 +809,7 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract) } + )"); vector params; if (contract->constructor()) @@ -776,6 +822,10 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract) contract->location(), m_context )); + t( + "contractSourceLocationComment", + sourceLocationComment(m_context.mostDerivedContract(), m_context) + ); t("params", joinHumanReadable(params)); vector baseParams = listAllParams(baseConstructorParams); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index aea70536a..86fcddc92 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -3166,5 +3166,5 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause) string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const { solAssert(_library.isLibrary(), ""); - return "linkersymbol(" + util::escapeAndQuoteYulString(_library.fullyQualifiedName()) + ")"; + return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")"; } diff --git a/libsolidity/codegen/ir/README.md b/libsolidity/codegen/ir/README.md index 468ecd269..cfabd83b9 100644 --- a/libsolidity/codegen/ir/README.md +++ b/libsolidity/codegen/ir/README.md @@ -6,5 +6,5 @@ with EVM dialect. The main semantic differences to the legacy code generator are the following: - - Arithmetic operations cause a failing assertion if the result is not in range. - - Resizing a storage array to a length larger than 2**64 causes a failing assertion. \ No newline at end of file +- Arithmetic operations cause a failing assertion if the result is not in range. +- Resizing a storage array to a length larger than 2**64 causes a failing assertion. diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index c7e25db29..2676dffaa 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -22,6 +22,9 @@ #include +#include +#include + #ifdef HAVE_Z3_DLOPEN #include #endif @@ -37,15 +40,15 @@ BMC::BMC( ErrorReporter& _errorReporter, map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + CharStreamProvider const& _charStreamProvider ): - SMTEncoder(_context, _settings), - m_interface(make_unique(_smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout)), + SMTEncoder(_context, _settings, _charStreamProvider), + m_interface(make_unique(_smtlib2Responses, _smtCallback, _settings.solvers, _settings.timeout)), m_outerErrorReporter(_errorReporter) { #if defined (HAVE_Z3) || defined (HAVE_CVC4) - if (_enabledSolvers.some()) + if (m_settings.solvers.cvc4 || m_settings.solvers.z3) if (!_smtlib2Responses.empty()) m_errorReporter.warning( 5622_error, @@ -57,8 +60,22 @@ BMC::BMC( #endif } -void BMC::analyze(SourceUnit const& _source, map> _solvedTargets) +void BMC::analyze(SourceUnit const& _source, map, smt::EncodingContext::IdCompare> _solvedTargets) { + if (m_interface->solvers() == 0) + { + if (!m_noSolverWarning) + { + m_noSolverWarning = true; + m_outerErrorReporter.warning( + 7710_error, + SourceLocation(), + "BMC analysis was not possible since no SMT solver was found and enabled." + ); + } + return; + } + if (SMTEncoder::analyze(_source)) { m_solvedTargets = move(_solvedTargets); @@ -67,15 +84,31 @@ void BMC::analyze(SourceUnit const& _source, map 0 && !m_settings.showUnproved) + m_errorReporter.warning( + 2788_error, + {}, + "BMC: " + + to_string(m_unprovedAmt) + + " verification condition(s) could not be proved." + + " Enable the model checker option \"show unproved\" to see all of them." + + " Consider choosing a specific contract to be verified in order to reduce the solving problems." + + " Consider increasing the timeout per query." + ); } - solAssert(m_interface->solvers() > 0, ""); // If this check is true, Z3 and CVC4 are not available // and the query answers were not provided, since SMTPortfolio - // guarantees that SmtLib2Interface is the first solver. - if (!m_interface->unhandledQueries().empty() && m_interface->solvers() == 1) + // guarantees that SmtLib2Interface is the first solver, if enabled. + if ( + !m_interface->unhandledQueries().empty() && + m_interface->solvers() == 1 && + m_settings.solvers.smtlib2 + ) { if (!m_noSolverWarning) { @@ -83,7 +116,8 @@ void BMC::analyze(SourceUnit const& _source, map, vector> BMC::modelExpressions() if (uf->annotation().type->isValueType()) { expressionsToEvaluate.emplace_back(expr(*uf)); - expressionNames.push_back(uf->location().text()); + string expressionName; + if (uf->location().hasText()) + expressionName = m_charStreamProvider.charStream(*uf->location().sourceName).text( + uf->location() + ); + expressionNames.push_back(move(expressionName)); } return {expressionsToEvaluate, expressionNames}; @@ -907,16 +946,20 @@ void BMC::checkCondition( solAssert(!_callStack.empty(), ""); std::ostringstream message; message << "BMC: " << _description << " happens here."; - std::ostringstream modelMessage; - modelMessage << "Counterexample:\n"; - solAssert(values.size() == expressionNames.size(), ""); - map sortedModel; - for (size_t i = 0; i < values.size(); ++i) - if (expressionsToEvaluate.at(i).name != values.at(i)) - sortedModel[expressionNames.at(i)] = values.at(i); - for (auto const& eval: sortedModel) - modelMessage << " " << eval.first << " = " << eval.second << "\n"; + std::ostringstream modelMessage; + // Sometimes models have complex smtlib2 expressions that SMTLib2Interface fails to parse. + if (values.size() == expressionNames.size()) + { + modelMessage << "Counterexample:\n"; + map sortedModel; + for (size_t i = 0; i < values.size(); ++i) + if (expressionsToEvaluate.at(i).name != values.at(i)) + sortedModel[expressionNames.at(i)] = values.at(i); + + for (auto const& eval: sortedModel) + modelMessage << " " << eval.first << " = " << eval.second << "\n"; + } m_errorReporter.warning( _errorHappens, @@ -931,8 +974,12 @@ void BMC::checkCondition( case smtutil::CheckResult::UNSATISFIABLE: break; case smtutil::CheckResult::UNKNOWN: - m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + _description + " might happen here.", secondaryLocation); + { + ++m_unprovedAmt; + if (m_settings.showUnproved) + m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + _description + " might happen here.", secondaryLocation); break; + } case smtutil::CheckResult::CONFLICTING: m_errorReporter.warning(1584_error, _location, "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound."); break; diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index 7f718b47c..a256dbf09 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -62,11 +62,11 @@ public: langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + langutil::CharStreamProvider const& _charStreamProvider ); - void analyze(SourceUnit const& _sources, std::map> _solvedTargets); + void analyze(SourceUnit const& _sources, std::map, smt::EncodingContext::IdCompare> _solvedTargets); /// 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 @@ -192,7 +192,10 @@ private: std::vector m_verificationTargets; /// Targets that were already proven. - std::map> m_solvedTargets; + std::map, smt::EncodingContext::IdCompare> m_solvedTargets; + + /// Number of verification conditions that could not be proved. + size_t m_unprovedAmt = 0; }; } diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 23967ecd2..7cc963956 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -56,25 +56,38 @@ CHC::CHC( ErrorReporter& _errorReporter, [[maybe_unused]] map const& _smtlib2Responses, [[maybe_unused]] ReadCallback::Callback const& _smtCallback, - SMTSolverChoice _enabledSolvers, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + CharStreamProvider const& _charStreamProvider ): - SMTEncoder(_context, _settings), - m_outerErrorReporter(_errorReporter), - m_enabledSolvers(_enabledSolvers) + SMTEncoder(_context, _settings, _charStreamProvider), + m_outerErrorReporter(_errorReporter) { - bool usesZ3 = _enabledSolvers.z3; + bool usesZ3 = m_settings.solvers.z3; #ifdef HAVE_Z3 usesZ3 = usesZ3 && Z3Interface::available(); #else usesZ3 = false; #endif - if (!usesZ3) + if (!usesZ3 && m_settings.solvers.smtlib2) m_interface = make_unique(_smtlib2Responses, _smtCallback, m_settings.timeout); } void CHC::analyze(SourceUnit const& _source) { + if (!m_settings.solvers.z3 && !m_settings.solvers.smtlib2) + { + if (!m_noSolverWarning) + { + m_noSolverWarning = true; + m_outerErrorReporter.warning( + 7649_error, + SourceLocation(), + "CHC analysis was not possible since no Horn solver was enabled." + ); + } + return; + } + if (SMTEncoder::analyze(_source)) { resetSourceAnalysis(); @@ -91,6 +104,8 @@ void CHC::analyze(SourceUnit const& _source) } bool ranSolver = true; + // If ranSolver is true here it's because an SMT solver callback was + // actually given and the queries were solved. if (auto const* smtLibInterface = dynamic_cast(m_interface.get())) ranSolver = smtLibInterface->unhandledQueries().empty(); if (!ranSolver && !m_noSolverWarning) @@ -102,7 +117,8 @@ void CHC::analyze(SourceUnit const& _source) #ifdef HAVE_Z3_DLOPEN "CHC analysis was not possible since libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION) + " was not found." #else - "CHC analysis was not possible since no integrated z3 SMT solver was found." + "CHC analysis was not possible. No Horn solver was available." + " None of the installed solvers was enabled." #endif ); } @@ -917,6 +933,7 @@ void CHC::resetSourceAnalysis() { m_safeTargets.clear(); m_unsafeTargets.clear(); + m_unprovedTargets.clear(); m_functionTargetIds.clear(); m_verificationTargets.clear(); m_queryPlaceholders.clear(); @@ -932,7 +949,7 @@ void CHC::resetSourceAnalysis() bool usesZ3 = false; #ifdef HAVE_Z3 - usesZ3 = m_enabledSolvers.z3 && Z3Interface::available(); + usesZ3 = m_settings.solvers.z3 && Z3Interface::available(); if (usesZ3) { /// z3::fixedpoint does not have a reset mechanism, so we need to create another. @@ -1426,20 +1443,23 @@ pair CHC::query(smtutil::Expression c case CheckResult::SATISFIABLE: { #ifdef HAVE_Z3 - // Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete. - // We now disable those optimizations and check whether we can still solve the problem. - auto* spacer = dynamic_cast(m_interface.get()); - solAssert(spacer, ""); - spacer->setSpacerOptions(false); + if (m_settings.solvers.z3) + { + // Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete. + // We now disable those optimizations and check whether we can still solve the problem. + auto* spacer = dynamic_cast(m_interface.get()); + solAssert(spacer, ""); + spacer->setSpacerOptions(false); - CheckResult resultNoOpt; - CHCSolverInterface::CexGraph cexNoOpt; - tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); + CheckResult resultNoOpt; + CHCSolverInterface::CexGraph cexNoOpt; + tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); - if (resultNoOpt == CheckResult::SATISFIABLE) - cex = move(cexNoOpt); + if (resultNoOpt == CheckResult::SATISFIABLE) + cex = move(cexNoOpt); - spacer->setSpacerOptions(true); + spacer->setSpacerOptions(true); + } #endif break; } @@ -1575,6 +1595,32 @@ void CHC::checkVerificationTargets() checkedErrorIds.insert(target.errorId); } + auto toReport = m_unsafeTargets; + if (m_settings.showUnproved) + for (auto const& [node, targets]: m_unprovedTargets) + for (auto const& [target, info]: targets) + toReport[node].emplace(target, info); + + for (auto const& [node, targets]: toReport) + for (auto const& [target, info]: targets) + m_errorReporter.warning( + info.error, + info.location, + info.message + ); + + if (!m_settings.showUnproved && !m_unprovedTargets.empty()) + m_errorReporter.warning( + 5840_error, + {}, + "CHC: " + + to_string(m_unprovedTargets.size()) + + " verification condition(s) could not be proved." + + " Enable the model checker option \"show unproved\" to see all of them." + + " Consider choosing a specific contract to be verified in order to reduce the solving problems." + + " Consider increasing the timeout per query." + ); + // There can be targets in internal functions that are not reachable from the external interface. // These are safe by definition and are not even checked by the CHC engine, but this information // must still be reported safe by the BMC engine. @@ -1614,27 +1660,26 @@ void CHC::checkAndReportTarget( else if (result == CheckResult::SATISFIABLE) { solAssert(!_satMsg.empty(), ""); - m_unsafeTargets[_target.errorNode].insert(_target.type); auto cex = generateCounterexample(model, error().name); if (cex) - m_errorReporter.warning( + m_unsafeTargets[_target.errorNode][_target.type] = { _errorReporterId, location, "CHC: " + _satMsg + "\nCounterexample:\n" + *cex - ); + }; else - m_errorReporter.warning( + m_unsafeTargets[_target.errorNode][_target.type] = { _errorReporterId, location, "CHC: " + _satMsg - ); + }; } else if (!_unknownMsg.empty()) - m_errorReporter.warning( + m_unprovedTargets[_target.errorNode][_target.type] = { _errorReporterId, location, "CHC: " + _unknownMsg - ); + }; } /** @@ -1741,7 +1786,7 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& path.emplace_back("State: " + modelMsg); } - string txCex = summaryPredicate->formatSummaryCall(summaryArgs); + string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider); list calls; auto dfs = [&](unsigned parent, unsigned node, unsigned depth, auto&& _dfs) -> void { @@ -1753,7 +1798,7 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& if (!pred->isConstructorSummary()) for (unsigned v: callGraph[node]) _dfs(node, v, depth + 1, _dfs); - calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node))); + calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node), m_charStreamProvider)); if (pred->isInternalCall()) calls.front() += " -- internal call"; else if (pred->isExternalCallTrusted()) diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 7b6a50f14..f2e8f62a1 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -39,6 +39,8 @@ #include +#include + #include #include @@ -56,14 +58,20 @@ public: langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + langutil::CharStreamProvider const& _charStreamProvider ); void analyze(SourceUnit const& _sources); - std::map> const& safeTargets() const { return m_safeTargets; } - std::map> const& unsafeTargets() const { return m_unsafeTargets; } + struct ReportTargetInfo + { + langutil::ErrorId error; + langutil::SourceLocation location; + std::string message; + }; + std::map, smt::EncodingContext::IdCompare> const& safeTargets() const { return m_safeTargets; } + std::map, smt::EncodingContext::IdCompare> const& unsafeTargets() const { return m_unsafeTargets; } /// This is used if the Horn solver is not directly linked into this binary. /// @returns a list of inputs to the Horn solver that were not part of the argument to @@ -347,10 +355,12 @@ private: /// Helper mapping unique IDs to actual verification targets. std::map m_verificationTargets; - /// Targets proven safe. - std::map> m_safeTargets; - /// Targets proven unsafe. - std::map> m_unsafeTargets; + /// Targets proved safe. + std::map, smt::EncodingContext::IdCompare> m_safeTargets; + /// Targets proved unsafe. + std::map, smt::EncodingContext::IdCompare> m_unsafeTargets; + /// Targets not proved. + std::map, smt::EncodingContext::IdCompare> m_unprovedTargets; //@} /// Control-flow. @@ -392,9 +402,6 @@ private: /// ErrorReporter that comes from CompilerStack. langutil::ErrorReporter& m_outerErrorReporter; - - /// SMT solvers that are chosen at runtime. - smtutil::SMTSolverChoice m_enabledSolvers; }; } diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index c820729f9..9b304f8ea 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -32,16 +32,16 @@ using namespace solidity::frontend; ModelChecker::ModelChecker( ErrorReporter& _errorReporter, + langutil::CharStreamProvider const& _charStreamProvider, map const& _smtlib2Responses, ModelCheckerSettings _settings, - ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers + ReadCallback::Callback const& _smtCallback ): m_errorReporter(_errorReporter), - m_settings(_settings), + m_settings(move(_settings)), m_context(), - m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings), - m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings) + m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, m_settings, _charStreamProvider), + m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, m_settings, _charStreamProvider) { } @@ -120,8 +120,8 @@ void ModelChecker::analyze(SourceUnit const& _source) m_chc.analyze(_source); auto solvedTargets = m_chc.safeTargets(); - for (auto const& target: m_chc.unsafeTargets()) - solvedTargets[target.first] += target.second; + for (auto const& [node, targets]: m_chc.unsafeTargets()) + solvedTargets[node] += targets | ranges::views::keys; if (m_settings.engine.bmc) m_bmc.analyze(_source, solvedTargets); @@ -134,7 +134,7 @@ vector ModelChecker::unhandledQueries() solidity::smtutil::SMTSolverChoice ModelChecker::availableSolvers() { - smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::None(); + smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::SMTLIB2(); #ifdef HAVE_Z3 available.z3 = solidity::smtutil::Z3Interface::available(); #endif diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index 3bcc9ecde..8156e39e7 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -49,10 +49,10 @@ public: /// should be used, even if all are available. The default choice is to use all. ModelChecker( langutil::ErrorReporter& _errorReporter, + langutil::CharStreamProvider const& _charStreamProvider, std::map const& _smtlib2Responses, ModelCheckerSettings _settings = ModelCheckerSettings{}, - ReadCallback::Callback const& _smtCallback = ReadCallback::Callback(), - smtutil::SMTSolverChoice _enabledSolvers = smtutil::SMTSolverChoice::All() + ReadCallback::Callback const& _smtCallback = ReadCallback::Callback() ); // TODO This should be removed for 0.9.0. diff --git a/libsolidity/formal/ModelCheckerSettings.h b/libsolidity/formal/ModelCheckerSettings.h index 6b4c121ae..ff39ddc9f 100644 --- a/libsolidity/formal/ModelCheckerSettings.h +++ b/libsolidity/formal/ModelCheckerSettings.h @@ -44,6 +44,9 @@ struct ModelCheckerContracts return has(_source) && contracts.at(_source).count(_contract); } + bool operator!=(ModelCheckerContracts const& _other) const noexcept { return !(*this == _other); } + bool operator==(ModelCheckerContracts const& _other) const noexcept { return contracts == _other.contracts; } + /// Represents which contracts should be analyzed by the SMTChecker /// as the most derived. /// The key is the source file. If the map is empty, all sources must be analyzed. @@ -79,6 +82,9 @@ struct ModelCheckerEngine return engineMap.at(_engine); return {}; } + + bool operator!=(ModelCheckerEngine const& _other) const noexcept { return !(*this == _other); } + bool operator==(ModelCheckerEngine const& _other) const noexcept { return bmc == _other.bmc && chc == _other.chc; } }; enum class VerificationTargetType { ConstantCondition, Underflow, Overflow, UnderOverflow, DivByZero, Balance, Assert, PopEmptyArray, OutOfBounds }; @@ -97,6 +103,9 @@ struct ModelCheckerTargets static std::map const targetStrings; + bool operator!=(ModelCheckerTargets const& _other) const noexcept { return !(*this == _other); } + bool operator==(ModelCheckerTargets const& _other) const noexcept { return targets == _other.targets; } + std::set targets; }; @@ -104,8 +113,22 @@ struct ModelCheckerSettings { ModelCheckerContracts contracts = ModelCheckerContracts::Default(); ModelCheckerEngine engine = ModelCheckerEngine::None(); + bool showUnproved = false; + smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::All(); ModelCheckerTargets targets = ModelCheckerTargets::Default(); std::optional timeout; + + bool operator!=(ModelCheckerSettings const& _other) const noexcept { return !(*this == _other); } + bool operator==(ModelCheckerSettings const& _other) const noexcept + { + return + contracts == _other.contracts && + engine == _other.engine && + showUnproved == _other.showUnproved && + solvers == _other.solvers && + targets == _other.targets && + timeout == _other.timeout; + } }; } diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index c2b8fd607..f1dc1e080 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -20,6 +20,8 @@ #include +#include +#include #include #include @@ -196,12 +198,20 @@ bool Predicate::isInterface() const return m_type == PredicateType::Interface; } -string Predicate::formatSummaryCall(vector const& _args) const +string Predicate::formatSummaryCall( + vector const& _args, + langutil::CharStreamProvider const& _charStreamProvider +) const { solAssert(isSummary(), ""); if (auto funCall = programFunctionCall()) - return funCall->location().text(); + { + if (funCall->location().hasText()) + return string(_charStreamProvider.charStream(*funCall->location().sourceName).text(funCall->location())); + else + return {}; + } /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). /// Here we are interested in preInputVars to format the function call, diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h index ee9a13716..6f5a05a50 100644 --- a/libsolidity/formal/Predicate.h +++ b/libsolidity/formal/Predicate.h @@ -27,6 +27,11 @@ #include #include +namespace solidity::langutil +{ +class CharStreamProvider; +} + namespace solidity::frontend { @@ -142,7 +147,10 @@ public: /// @returns a formatted string representing a call to this predicate /// with _args. - std::string formatSummaryCall(std::vector const& _args) const; + std::string formatSummaryCall( + std::vector const& _args, + langutil::CharStreamProvider const& _charStreamProvider + ) const; /// @returns the values of the state variables from _args at the point /// where this summary was reached. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index eb7e2a17e..4069134c9 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -30,6 +30,8 @@ #include #include +#include + #include #include @@ -45,11 +47,13 @@ using namespace solidity::frontend; SMTEncoder::SMTEncoder( smt::EncodingContext& _context, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + langutil::CharStreamProvider const& _charStreamProvider ): m_errorReporter(m_smtErrors), m_context(_context), - m_settings(_settings) + m_settings(_settings), + m_charStreamProvider(_charStreamProvider) { } diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 9b8402cc1..9adf38ffe 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -43,6 +43,7 @@ namespace solidity::langutil { class ErrorReporter; struct SourceLocation; +class CharStreamProvider; } namespace solidity::frontend @@ -53,7 +54,8 @@ class SMTEncoder: public ASTConstVisitor public: SMTEncoder( smt::EncodingContext& _context, - ModelCheckerSettings const& _settings + ModelCheckerSettings const& _settings, + langutil::CharStreamProvider const& _charStreamProvider ); /// @returns true if engine should proceed with analysis. @@ -469,6 +471,10 @@ protected: ModelCheckerSettings const& m_settings; + /// Character stream for each source, + /// used for retrieving source text of expressions for e.g. counter-examples. + langutil::CharStreamProvider const& m_charStreamProvider; + smt::SymbolicState& state(); }; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 0aeb88d1e..cbdfff454 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -80,7 +80,6 @@ #include #include -#include #include @@ -96,7 +95,6 @@ static int g_compilerStackCounts = 0; CompilerStack::CompilerStack(ReadCallback::Callback _readFile): m_readFile{std::move(_readFile)}, - m_enabledSMTSolvers{smtutil::SMTSolverChoice::All()}, m_errorReporter{m_errorList} { // Because TypeProvider is currently a singleton API, we must ensure that @@ -229,13 +227,6 @@ void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings) m_modelCheckerSettings = _settings; } -void CompilerStack::setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSMTSolvers) -{ - if (m_stackState >= ParsedAndImported) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set enabled SMT solvers before parsing.")); - m_enabledSMTSolvers = _enabledSMTSolvers; -} - void CompilerStack::setLibraries(std::map const& _libraries) { if (m_stackState >= ParsedAndImported) @@ -300,7 +291,6 @@ void CompilerStack::reset(bool _keepSettings) m_viaIR = false; m_evmVersion = langutil::EVMVersion(); m_modelCheckerSettings = ModelCheckerSettings{}; - m_enabledSMTSolvers = smtutil::SMTSolverChoice::All(); m_generateIR = false; m_generateEwasm = false; m_revertStrings = RevertStrings::Default; @@ -323,7 +313,7 @@ void CompilerStack::setSources(StringMap _sources) if (m_stackState != Empty) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set sources before parsing.")); for (auto source: _sources) - m_sources[source.first].scanner = make_shared(CharStream(/*content*/std::move(source.second), /*name*/source.first)); + m_sources[source.first].charStream = make_unique(/*content*/std::move(source.second), /*name*/source.first); m_stackState = SourcesSet; } @@ -346,8 +336,7 @@ bool CompilerStack::parse() { string const& path = sourcesToParse[i]; Source& source = m_sources[path]; - source.scanner->reset(); - source.ast = parser.parse(source.scanner); + source.ast = parser.parse(*source.charStream); if (!source.ast) solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error."); else @@ -358,7 +347,7 @@ bool CompilerStack::parse() { string const& newPath = newSource.first; string const& newContents = newSource.second; - m_sources[newPath].scanner = make_shared(CharStream(newContents, newPath)); + m_sources[newPath].charStream = make_shared(newContents, newPath); sourcesToParse.push_back(newPath); } } @@ -387,10 +376,11 @@ void CompilerStack::importASTs(map const& _sources) string const& path = src.first; Source source; source.ast = src.second; - string srcString = util::jsonCompactPrint(m_sourceJsons[src.first]); - ASTPointer scanner = make_shared(langutil::CharStream(srcString, src.first)); - source.scanner = scanner; - m_sources[path] = source; + source.charStream = make_shared( + util::jsonCompactPrint(m_sourceJsons[src.first]), + src.first + ); + m_sources[path] = move(source); } m_stackState = ParsedAndImported; m_importedSources = true; @@ -556,7 +546,7 @@ bool CompilerStack::analyze() if (noErrors) { - ModelChecker modelChecker(m_errorReporter, m_smtlib2Responses, m_modelCheckerSettings, m_readFile, m_enabledSMTSolvers); + ModelChecker modelChecker(m_errorReporter, *this, m_smtlib2Responses, m_modelCheckerSettings, m_readFile); auto allSources = applyMap(m_sourceOrder, [](Source const* _source) { return _source->ast; }); modelChecker.enableAllEnginesIfPragmaPresent(allSources); modelChecker.checkRequestedSourcesAndContracts(allSources); @@ -764,9 +754,9 @@ Json::Value CompilerStack::generatedSources(string const& _contractName, bool _r unsigned sourceIndex = sourceIndices()[sourceName]; ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared(langutil::CharStream(source, sourceName)); + CharStream charStream(source, sourceName); yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); - shared_ptr parserResult = yul::Parser{errorReporter, dialect}.parse(scanner, false); + shared_ptr parserResult = yul::Parser{errorReporter, dialect}.parse(charStream); solAssert(parserResult, ""); sources[0]["ast"] = yul::AsmJsonConverter{sourceIndex}(*parserResult); sources[0]["name"] = sourceName; @@ -1036,12 +1026,14 @@ string const& CompilerStack::metadata(Contract const& _contract) const return _contract.metadata.init([&]{ return createMetadata(_contract); }); } -Scanner const& CompilerStack::scanner(string const& _sourceName) const +CharStream const& CompilerStack::charStream(string const& _sourceName) const { if (m_stackState < SourcesSet) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set.")); - return *source(_sourceName).scanner; + solAssert(source(_sourceName).charStream, ""); + + return *source(_sourceName).charStream; } SourceUnit const& CompilerStack::ast(string const& _sourceName) const @@ -1083,37 +1075,24 @@ size_t CompilerStack::functionEntryPoint( return 0; } -tuple CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const -{ - int startLine; - int startColumn; - int endLine; - int endColumn; - 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); -} - - h256 const& CompilerStack::Source::keccak256() const { if (keccak256HashCached == h256{}) - keccak256HashCached = util::keccak256(scanner->source()); + keccak256HashCached = util::keccak256(charStream->source()); return keccak256HashCached; } h256 const& CompilerStack::Source::swarmHash() const { if (swarmHashCached == h256{}) - swarmHashCached = util::bzzr1Hash(scanner->source()); + swarmHashCached = util::bzzr1Hash(charStream->source()); return swarmHashCached; } string const& CompilerStack::Source::ipfsUrl() const { if (ipfsUrlCached.empty()) - ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(scanner->source()); + ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(charStream->source()); return ipfsUrlCached; } @@ -1474,12 +1453,12 @@ string CompilerStack::createMetadata(Contract const& _contract) const if (!referencedSources.count(s.first)) continue; - solAssert(s.second.scanner, "Scanner not available"); + solAssert(s.second.charStream, "Character stream not available"); meta["sources"][s.first]["keccak256"] = "0x" + toHex(s.second.keccak256().asBytes()); if (optional licenseString = s.second.ast->licenseString()) meta["sources"][s.first]["license"] = *licenseString; if (m_metadataLiteralSources) - meta["sources"][s.first]["content"] = s.second.scanner->source(); + meta["sources"][s.first]["content"] = s.second.charStream->source(); else { meta["sources"][s.first]["urls"] = Json::arrayValue; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 44daa6381..9c713da24 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -56,7 +57,7 @@ namespace solidity::langutil { -class Scanner; +class CharStream; } @@ -87,7 +88,7 @@ class DeclarationContainer; * If error recovery is active, it is possible to progress through the stages even when * there are errors. In any case, producing code is only possible without errors. */ -class CompilerStack +class CompilerStack: public langutil::CharStreamProvider { public: /// Noncopyable. @@ -120,7 +121,7 @@ public: /// and must not emit exceptions. explicit CompilerStack(ReadCallback::Callback _readFile = ReadCallback::Callback()); - ~CompilerStack(); + ~CompilerStack() override; /// @returns the list of errors that occurred during parsing and type checking. langutil::ErrorList const& errors() const { return m_errorReporter.errors(); } @@ -174,8 +175,6 @@ public: /// Set model checker settings. void setModelCheckerSettings(ModelCheckerSettings _settings); - /// Set which SMT solvers should be enabled. - void setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSolvers); /// Sets the requested contract names by source. /// If empty, no filtering is performed and every contract @@ -239,8 +238,8 @@ public: /// by sourceNames(). std::map sourceIndices() const; - /// @returns the previously used scanner, useful for counting lines during error reporting. - langutil::Scanner const& scanner(std::string const& _sourceName) const; + /// @returns the previously used character stream, useful for counting lines during error reporting. + langutil::CharStream const& charStream(std::string const& _sourceName) const override; /// @returns the parsed source unit with the supplied name. SourceUnit const& ast(std::string const& _sourceName) const; @@ -249,11 +248,6 @@ public: /// does not exist. ContractDefinition const& contractDefinition(std::string const& _contractName) const; - /// 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 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 const& unhandledSMTLib2Queries() const { return m_unhandledSMTLib2Queries; } @@ -349,7 +343,7 @@ private: /// The state per source unit. Filled gradually during parsing. struct Source { - std::shared_ptr scanner; + std::shared_ptr charStream; std::shared_ptr ast; util::h256 mutable keccak256HashCached; util::h256 mutable swarmHashCached; @@ -483,7 +477,6 @@ private: bool m_viaIR = false; langutil::EVMVersion m_evmVersion; ModelCheckerSettings m_modelCheckerSettings; - smtutil::SMTSolverChoice m_enabledSMTSolvers; std::map> m_requestedContractNames; bool m_generateEvmBytecode = true; bool m_generateIR = false; diff --git a/libsolidity/interface/ImportRemapper.h b/libsolidity/interface/ImportRemapper.h index 15e2803dc..4451f0291 100644 --- a/libsolidity/interface/ImportRemapper.h +++ b/libsolidity/interface/ImportRemapper.h @@ -35,6 +35,15 @@ class ImportRemapper public: struct Remapping { + bool operator!=(Remapping const& _other) const noexcept { return !(*this == _other); } + bool operator==(Remapping const& _other) const noexcept + { + return + context == _other.context && + prefix == _other.prefix && + target == _other.target; + } + std::string context; std::string prefix; std::string target; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 1295c8411..0a12059d5 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -83,9 +83,9 @@ Json::Value formatFatalError(string const& _type, string const& _message) Json::Value formatSourceLocation(SourceLocation const* location) { Json::Value sourceLocation; - if (location && location->source && !location->source->name().empty()) + if (location && location->sourceName) { - sourceLocation["file"] = location->source->name(); + sourceLocation["file"] = *location->sourceName; sourceLocation["start"] = location->start; sourceLocation["end"] = location->end; } @@ -109,6 +109,7 @@ Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _second } Json::Value formatErrorWithException( + CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, bool const& _warning, string const& _type, @@ -119,7 +120,11 @@ Json::Value formatErrorWithException( { string message; // TODO: consider enabling color - string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type); + string formattedMessage = SourceReferenceFormatter::formatExceptionInformation( + _exception, + _type, + _charStreamProvider + ); if (string const* description = boost::get_error_info(_exception)) message = ((_message.length() > 0) ? (_message + ":") : "") + *description; @@ -437,7 +442,7 @@ std::optional checkSettingsKeys(Json::Value const& _input) std::optional checkModelCheckerSettingsKeys(Json::Value const& _input) { - static set keys{"contracts", "engine", "targets", "timeout"}; + static set keys{"contracts", "engine", "showUnproved", "solvers", "targets", "timeout"}; return checkKeys(_input, keys, "modelChecker"); } @@ -946,6 +951,32 @@ std::variant StandardCompiler: ret.modelCheckerSettings.engine = *engine; } + if (modelCheckerSettings.isMember("showUnproved")) + { + auto const& showUnproved = modelCheckerSettings["showUnproved"]; + if (!showUnproved.isBool()) + return formatFatalError("JSONError", "settings.modelChecker.showUnproved must be a Boolean value."); + ret.modelCheckerSettings.showUnproved = showUnproved.asBool(); + } + + if (modelCheckerSettings.isMember("solvers")) + { + auto const& solversArray = modelCheckerSettings["solvers"]; + if (!solversArray.isArray()) + return formatFatalError("JSONError", "settings.modelChecker.solvers must be an array."); + + smtutil::SMTSolverChoice solvers; + for (auto const& s: solversArray) + { + if (!s.isString()) + return formatFatalError("JSONError", "Every target in settings.modelChecker.solvers must be a string."); + if (!solvers.setSolver(s.asString())) + return formatFatalError("JSONError", "Invalid model checker solvers requested."); + } + + ret.modelCheckerSettings.solvers = solvers; + } + if (modelCheckerSettings.isMember("targets")) { auto const& targetsArray = modelCheckerSettings["targets"]; @@ -1017,6 +1048,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting Error const& err = dynamic_cast(*error); errors.append(formatErrorWithException( + compilerStack, *error, err.type() == Error::Type::Warning, err.typeName(), @@ -1030,6 +1062,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (Error const& _error) { errors.append(formatErrorWithException( + compilerStack, _error, false, _error.typeName(), @@ -1050,6 +1083,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (CompilerError const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "CompilerError", @@ -1060,6 +1094,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (InternalCompilerError const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "InternalCompilerError", @@ -1070,6 +1105,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (UnimplementedFeatureError const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "UnimplementedFeatureError", @@ -1080,6 +1116,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (yul::YulException const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "YulException", @@ -1090,6 +1127,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (smtutil::SMTLogicError const& _exception) { errors.append(formatErrorWithException( + compilerStack, _exception, false, "SMTLogicException", @@ -1297,6 +1335,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) auto err = dynamic_pointer_cast(error); errors.append(formatErrorWithException( + stack, *error, err->type() == Error::Type::Warning, err->typeName(), @@ -1407,7 +1446,7 @@ string StandardCompiler::compile(string const& _input) noexcept try { if (!util::jsonParseStrict(_input, input, &errors)) - return util::jsonCompactPrint(formatFatalError("JSONError", errors)); + return util::jsonPrint(formatFatalError("JSONError", errors), m_jsonPrintingFormat); } catch (...) { @@ -1420,7 +1459,7 @@ string StandardCompiler::compile(string const& _input) noexcept try { - return util::jsonCompactPrint(output); + return util::jsonPrint(output, m_jsonPrintingFormat); } catch (...) { diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index da193b3a1..4d43fdfec 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include @@ -46,8 +47,10 @@ public: /// Creates a new StandardCompiler. /// @param _readFile callback used to read files for import statements. Must return /// and must not emit exceptions. - explicit StandardCompiler(ReadCallback::Callback _readFile = ReadCallback::Callback()): - m_readFile(std::move(_readFile)) + explicit StandardCompiler(ReadCallback::Callback _readFile = ReadCallback::Callback(), + util::JsonFormat const& _format = {}): + m_readFile(std::move(_readFile)), + m_jsonPrintingFormat(std::move(_format)) { } @@ -91,6 +94,8 @@ private: Json::Value compileYul(InputsAndSettings _inputsAndSettings); ReadCallback::Callback m_readFile; + + util::JsonFormat m_jsonPrintingFormat; }; } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 03361282f..f1b2947fd 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -50,7 +50,12 @@ class Parser::ASTNodeFactory { public: explicit ASTNodeFactory(Parser& _parser): - m_parser(_parser), m_location{_parser.currentLocation().start, -1, _parser.currentLocation().source} {} + m_parser(_parser), m_location{ + _parser.currentLocation().start, + -1, + _parser.currentLocation().sourceName + } + {} ASTNodeFactory(Parser& _parser, ASTPointer const& _childNode): m_parser(_parser), m_location{_childNode->location()} {} @@ -63,7 +68,7 @@ public: template ASTPointer createNode(Args&& ... _args) { - solAssert(m_location.source, ""); + solAssert(m_location.sourceName, ""); if (m_location.end < 0) markEndPosition(); return make_shared(m_parser.nextID(), m_location, std::forward(_args)...); @@ -76,13 +81,13 @@ private: SourceLocation m_location; }; -ASTPointer Parser::parse(shared_ptr const& _scanner) +ASTPointer Parser::parse(CharStream& _charStream) { solAssert(!m_insideModifier, ""); try { m_recursionDepth = 0; - m_scanner = _scanner; + m_scanner = make_shared(_charStream); ASTNodeFactory nodeFactory(*this); vector> nodes; @@ -1284,7 +1289,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con } yul::Parser asmParser(m_errorReporter, dialect); - shared_ptr block = asmParser.parse(m_scanner, true); + shared_ptr block = asmParser.parseInline(m_scanner); if (block == nullptr) BOOST_THROW_EXCEPTION(FatalError()); @@ -2045,9 +2050,9 @@ optional Parser::findLicenseString(std::vector> cons // Search inside all parts of the source not covered by parsed nodes. // This will leave e.g. "global comments". - string const& source = m_scanner->source(); - using iter = decltype(source.begin()); + using iter = std::string::const_iterator; vector> sequencesToSearch; + string const& source = m_scanner->charStream().source(); sequencesToSearch.emplace_back(source.begin(), source.end()); for (ASTPointer const& node: _nodes) if (node->location().hasText()) @@ -2073,7 +2078,7 @@ optional Parser::findLicenseString(std::vector> cons else if (matches.empty()) parserWarning( 1878_error, - {-1, -1, m_scanner->charStream()}, + {-1, -1, m_scanner->currentLocation().sourceName}, "SPDX license identifier not provided in source file. " "Before publishing, consider adding a comment containing " "\"SPDX-License-Identifier: \" to each source file. " @@ -2083,7 +2088,7 @@ optional Parser::findLicenseString(std::vector> cons else parserError( 3716_error, - {-1, -1, m_scanner->charStream()}, + {-1, -1, m_scanner->currentLocation().sourceName}, "Multiple SPDX license identifiers found in source file. " "Use \"AND\" or \"OR\" to combine multiple licenses. " "Please see https://spdx.org for more information." diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 559f804c2..5374966d7 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -29,7 +29,7 @@ namespace solidity::langutil { -class Scanner; +class CharStream; } namespace solidity::frontend @@ -47,7 +47,7 @@ public: m_evmVersion(_evmVersion) {} - ASTPointer parse(std::shared_ptr const& _scanner); + ASTPointer parse(langutil::CharStream& _charStream); private: class ASTNodeFactory; diff --git a/libsolutil/CommonData.cpp b/libsolutil/CommonData.cpp index c6688fee8..a49f90491 100644 --- a/libsolutil/CommonData.cpp +++ b/libsolutil/CommonData.cpp @@ -192,13 +192,11 @@ string solidity::util::formatAsStringOrNumber(string const& _value) if (c <= 0x1f || c >= 0x7f || c == '"') return "0x" + h256(_value, h256::AlignLeft).hex(); - // The difference in escaping is only in characters below 0x1f and the string does not have them - // so this will work for Solidity strings too. - return escapeAndQuoteYulString(_value); + return escapeAndQuoteString(_value); } -string solidity::util::escapeAndQuoteYulString(string const& _input) +string solidity::util::escapeAndQuoteString(string const& _input) { string out; diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index 3c8b2d80c..19d66fc91 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -552,9 +552,9 @@ bool isValidDecimal(std::string const& _string); /// _value cannot be longer than 32 bytes. std::string formatAsStringOrNumber(std::string const& _value); -/// @returns a string with the usual backslash-escapes for non-ASCII +/// @returns a string with the usual backslash-escapes for non-printable and non-ASCII /// characters and surrounded by '"'-characters. -std::string escapeAndQuoteYulString(std::string const& _input); +std::string escapeAndQuoteString(std::string const& _input); template bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) diff --git a/libsolutil/JSON.cpp b/libsolutil/JSON.cpp index 81ef7f76b..d27982acb 100644 --- a/libsolutil/JSON.cpp +++ b/libsolutil/JSON.cpp @@ -115,18 +115,29 @@ Json::Value removeNullMembers(Json::Value _json) string jsonPrettyPrint(Json::Value const& _input) { - static map settings{{"indentation", " "}, {"enableYAMLCompatibility", true}}; - static StreamWriterBuilder writerBuilder(settings); - string result = print(_input, writerBuilder); - boost::replace_all(result, " \n", "\n"); - return result; + return jsonPrint(_input, JsonFormat{ JsonFormat::Pretty }); } string jsonCompactPrint(Json::Value const& _input) { - static map settings{{"indentation", ""}}; - static StreamWriterBuilder writerBuilder(settings); - return print(_input, writerBuilder); + return jsonPrint(_input, JsonFormat{ JsonFormat::Compact }); +} + +string jsonPrint(Json::Value const& _input, JsonFormat const& _format) +{ + map settings; + if (_format.format == JsonFormat::Pretty) + { + settings["indentation"] = string(_format.indent, ' '); + settings["enableYAMLCompatibility"] = true; + } + else + settings["indentation"] = ""; + StreamWriterBuilder writerBuilder(settings); + string result = print(_input, writerBuilder); + if (_format.format == JsonFormat::Pretty) + boost::replace_all(result, " \n", "\n"); + return result; } bool jsonParseStrict(string const& _input, Json::Value& _json, string* _errs /* = nullptr */) diff --git a/libsolutil/JSON.h b/libsolutil/JSON.h index c1d045e73..3a326a5e3 100644 --- a/libsolutil/JSON.h +++ b/libsolutil/JSON.h @@ -33,12 +33,33 @@ namespace solidity::util /// Removes members with null value recursively from (@a _json). Json::Value removeNullMembers(Json::Value _json); +/// JSON printing format. +struct JsonFormat +{ + enum Format + { + Compact, + Pretty + }; + + static constexpr uint32_t defaultIndent = 2; + + bool operator==(JsonFormat const& _other) const noexcept { return (format == _other.format) && (indent == _other.indent); } + bool operator!=(JsonFormat const& _other) const noexcept { return !(*this == _other); } + + Format format = Compact; + uint32_t indent = defaultIndent; +}; + /// Serialise the JSON object (@a _input) with indentation std::string jsonPrettyPrint(Json::Value const& _input); /// Serialise the JSON object (@a _input) without indentation std::string jsonCompactPrint(Json::Value const& _input); +/// Serialise the JSON object (@a _input) using specified format (@a _format) +std::string jsonPrint(Json::Value const& _input, JsonFormat const& _format); + /// Parse a JSON string (@a _input) with enabled strict-mode and writes resulting JSON object to (@a _json) /// \param _input JSON input string /// \param _json [out] resulting JSON object diff --git a/libsolutil/StringUtils.h b/libsolutil/StringUtils.h index e7a68cedc..646cda505 100644 --- a/libsolutil/StringUtils.h +++ b/libsolutil/StringUtils.h @@ -177,4 +177,22 @@ inline std::string formatNumberReadable( return str; } +/// Safely converts an usigned integer as string into an unsigned int type. +/// +/// @return the converted number or nullopt in case of an failure (including if it would not fit). +inline std::optional toUnsignedInt(std::string const& _value) +{ + try + { + auto const ulong = stoul(_value); + if (ulong > std::numeric_limits::max()) + return std::nullopt; + return static_cast(ulong); + } + catch (...) + { + return std::nullopt; + } +} + } diff --git a/libsolutil/cxx20.h b/libsolutil/cxx20.h index bbe9817aa..c65b0454b 100644 --- a/libsolutil/cxx20.h +++ b/libsolutil/cxx20.h @@ -49,4 +49,15 @@ erase_if(std::unordered_map& _c, Pred _pred) return old_size - _c.size(); } +// Taken from https://en.cppreference.com/w/cpp/container/vector/erase2 +template +constexpr typename std::vector::size_type +erase_if(std::vector& c, Pred pred) +{ + auto it = std::remove_if(c.begin(), c.end(), pred); + auto r = std::distance(it, c.end()); + c.erase(it, c.end()); + return static_cast::size_type>(r); +} + } diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index 0aacbdf84..f74fac4de 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -45,7 +46,7 @@ SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _n { yulAssert(member(_node, "src").isString(), "'src' must be a string"); - return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceName); + return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames); } template @@ -53,10 +54,7 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; SourceLocation location = createSourceLocation(_node); - yulAssert( - location.source && 0 <= location.start && location.start <= location.end, - "Invalid source location in Asm AST" - ); + yulAssert(location.hasText(), "Invalid source location in Asm AST"); r.debugData = DebugData::create(location); return r; } @@ -168,7 +166,8 @@ Literal AsmJsonImporter::createLiteral(Json::Value const& _node) if (kind == "number") { - langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; + langutil::CharStream charStream(lit.value.str(), ""); + langutil::Scanner scanner{charStream}; lit.kind = LiteralKind::Number; yulAssert( scanner.currentToken() == Token::Number, @@ -177,7 +176,8 @@ Literal AsmJsonImporter::createLiteral(Json::Value const& _node) } else if (kind == "bool") { - langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; + langutil::CharStream charStream(lit.value.str(), ""); + langutil::Scanner scanner{charStream}; lit.kind = LiteralKind::Boolean; yulAssert( scanner.currentToken() == Token::TrueLiteral || diff --git a/libyul/AsmJsonImporter.h b/libyul/AsmJsonImporter.h index e491430b6..506352fa4 100644 --- a/libyul/AsmJsonImporter.h +++ b/libyul/AsmJsonImporter.h @@ -38,7 +38,9 @@ namespace solidity::yul class AsmJsonImporter { public: - explicit AsmJsonImporter(std::string _sourceName) : m_sourceName(std::move(_sourceName)) {} + explicit AsmJsonImporter(std::vector> const& _sourceNames): + m_sourceNames(_sourceNames) + {} yul::Block createBlock(Json::Value const& _node); private: @@ -70,8 +72,7 @@ private: yul::Break createBreak(Json::Value const& _node); yul::Continue createContinue(Json::Value const& _node); - std::string m_sourceName; - + std::vector> const& m_sourceNames; }; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 27955fe3b..d1465c737 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -24,14 +24,18 @@ #include #include #include -#include #include +#include +#include #include #include +#include + #include #include +#include using namespace std; using namespace solidity; @@ -53,9 +57,43 @@ shared_ptr updateLocationEndFrom( return make_shared(updatedLocation); } +optional toInt(string const& _value) +{ + try + { + return stoi(_value); + } + catch (...) + { + return nullopt; + } } -unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) +} + +std::shared_ptr Parser::createDebugData() const +{ + switch (m_useSourceLocationFrom) + { + case UseSourceLocationFrom::Scanner: + return DebugData::create(ParserBase::currentLocation()); + case UseSourceLocationFrom::LocationOverride: + return DebugData::create(m_locationOverride); + case UseSourceLocationFrom::Comments: + return m_debugDataOverride; + } + solAssert(false, ""); +} + +unique_ptr Parser::parse(CharStream& _charStream) +{ + m_scanner = make_shared(_charStream); + unique_ptr block = parseInline(m_scanner); + expectToken(Token::EOS); + return block; +} + +unique_ptr Parser::parseInline(std::shared_ptr const& _scanner) { m_recursionDepth = 0; @@ -65,10 +103,9 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ try { m_scanner = _scanner; - auto block = make_unique(parseBlock()); - if (!_reuseScanner) - expectToken(Token::EOS); - return block; + if (m_sourceNames) + fetchSourceLocationFromComment(); + return make_unique(parseBlock()); } catch (FatalError const&) { @@ -78,6 +115,55 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ return nullptr; } +langutil::Token Parser::advance() +{ + auto const token = ParserBase::advance(); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Comments) + fetchSourceLocationFromComment(); + return token; +} + +void Parser::fetchSourceLocationFromComment() +{ + solAssert(m_sourceNames.has_value(), ""); + + if (m_scanner->currentCommentLiteral().empty()) + return; + + static regex const lineRE = std::regex( + R"~~~((^|\s*)@src\s+(-1|\d+):(-1|\d+):(-1|\d+)(\s+|$))~~~", + std::regex_constants::ECMAScript | std::regex_constants::optimize + ); + + string const text = m_scanner->currentCommentLiteral(); + auto from = sregex_iterator(text.begin(), text.end(), lineRE); + auto to = sregex_iterator(); + + for (auto const& matchResult: ranges::make_subrange(from, to)) + { + solAssert(matchResult.size() == 6, ""); + + auto const sourceIndex = toInt(matchResult[2].str()); + auto const start = toInt(matchResult[3].str()); + auto const end = toInt(matchResult[4].str()); + + auto const commentLocation = m_scanner->currentCommentLocation(); + m_debugDataOverride = DebugData::create(); + if (!sourceIndex || !start || !end) + m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification."); + else if (sourceIndex == -1) + m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, nullptr}); + else if (!(sourceIndex >= 0 && m_sourceNames->count(static_cast(*sourceIndex)))) + m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src."); + else + { + shared_ptr sourceName = m_sourceNames->at(static_cast(*sourceIndex)); + solAssert(sourceName, ""); + m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, move(sourceName)}); + } + } +} + Block Parser::parseBlock() { RecursionGuard recursionGuard(*this); @@ -85,7 +171,8 @@ Block Parser::parseBlock() expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); - block.debugData = updateLocationEndFrom(block.debugData, currentLocation()); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + block.debugData = updateLocationEndFrom(block.debugData, currentLocation()); advance(); return block; } @@ -107,6 +194,8 @@ Statement Parser::parseStatement() advance(); _if.condition = make_unique(parseExpression()); _if.body = parseBlock(); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + _if.debugData = updateLocationEndFrom(_if.debugData, _if.body.debugData->location); return Statement{move(_if)}; } case Token::Switch: @@ -124,7 +213,8 @@ Statement Parser::parseStatement() fatalParserError(4904_error, "Case not allowed after default case."); if (_switch.cases.empty()) fatalParserError(2418_error, "Switch statement without any cases."); - _switch.debugData = updateLocationEndFrom(_switch.debugData, _switch.cases.back().body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + _switch.debugData = updateLocationEndFrom(_switch.debugData, _switch.cases.back().body.debugData->location); return Statement{move(_switch)}; } case Token::For: @@ -206,7 +296,8 @@ Statement Parser::parseStatement() expectToken(Token::AssemblyAssign); assignment.value = make_unique(parseExpression()); - assignment.debugData = updateLocationEndFrom(assignment.debugData, locationOf(*assignment.value)); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + assignment.debugData = updateLocationEndFrom(assignment.debugData, locationOf(*assignment.value)); return Statement{move(assignment)}; } @@ -236,7 +327,8 @@ Case Parser::parseCase() else yulAssert(false, "Case or default case expected."); _case.body = parseBlock(); - _case.debugData = updateLocationEndFrom(_case.debugData, _case.body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + _case.debugData = updateLocationEndFrom(_case.debugData, _case.body.debugData->location); return _case; } @@ -256,7 +348,8 @@ ForLoop Parser::parseForLoop() forLoop.post = parseBlock(); m_currentForLoopComponent = ForLoopComponent::ForLoopBody; forLoop.body = parseBlock(); - forLoop.debugData = updateLocationEndFrom(forLoop.debugData, forLoop.body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + forLoop.debugData = updateLocationEndFrom(forLoop.debugData, forLoop.body.debugData->location); m_currentForLoopComponent = outerForLoopComponent; @@ -295,7 +388,7 @@ variant Parser::parseLiteralOrIdentifier() { case Token::Identifier: { - Identifier identifier{DebugData::create(currentLocation()), YulString{currentLiteral()}}; + Identifier identifier{createDebugData(), YulString{currentLiteral()}}; advance(); return identifier; } @@ -326,7 +419,7 @@ variant Parser::parseLiteralOrIdentifier() } Literal literal{ - DebugData::create(currentLocation()), + createDebugData(), kind, YulString{currentLiteral()}, kind == LiteralKind::Boolean ? m_dialect.boolType : m_dialect.defaultType @@ -335,7 +428,8 @@ variant Parser::parseLiteralOrIdentifier() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - literal.debugData = updateLocationEndFrom(literal.debugData, currentLocation()); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + literal.debugData = updateLocationEndFrom(literal.debugData, currentLocation()); literal.type = expectAsmIdentifier(); } @@ -367,9 +461,10 @@ VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::AssemblyAssign); varDecl.value = make_unique(parseExpression()); - varDecl.debugData = updateLocationEndFrom(varDecl.debugData, locationOf(*varDecl.value)); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + varDecl.debugData = updateLocationEndFrom(varDecl.debugData, locationOf(*varDecl.value)); } - else + else if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) varDecl.debugData = updateLocationEndFrom(varDecl.debugData, varDecl.variables.back().debugData->location); return varDecl; @@ -416,7 +511,8 @@ FunctionDefinition Parser::parseFunctionDefinition() m_insideFunction = true; funDef.body = parseBlock(); m_insideFunction = preInsideFunction; - funDef.debugData = updateLocationEndFrom(funDef.debugData, funDef.body.debugData->location); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + funDef.debugData = updateLocationEndFrom(funDef.debugData, funDef.body.debugData->location); m_currentForLoopComponent = outerForLoopComponent; return funDef; @@ -443,7 +539,8 @@ FunctionCall Parser::parseCall(variant&& _initialOp) ret.arguments.emplace_back(parseExpression()); } } - ret.debugData = updateLocationEndFrom(ret.debugData, currentLocation()); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + ret.debugData = updateLocationEndFrom(ret.debugData, currentLocation()); expectToken(Token::RParen); return ret; } @@ -456,7 +553,8 @@ TypedName Parser::parseTypedName() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - typedName.debugData = updateLocationEndFrom(typedName.debugData, currentLocation()); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + typedName.debugData = updateLocationEndFrom(typedName.debugData, currentLocation()); typedName.type = expectAsmIdentifier(); } else diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 896cb6d51..da6eb7226 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -23,6 +23,7 @@ #pragma once +#include #include #include @@ -30,6 +31,7 @@ #include #include +#include #include #include #include @@ -45,6 +47,11 @@ public: None, ForLoopPre, ForLoopPost, ForLoopBody }; + enum class UseSourceLocationFrom + { + Scanner, LocationOverride, Comments, + }; + explicit Parser( langutil::ErrorReporter& _errorReporter, Dialect const& _dialect, @@ -52,25 +59,62 @@ public: ): ParserBase(_errorReporter), m_dialect(_dialect), - m_locationOverride(std::move(_locationOverride)) + m_locationOverride{_locationOverride ? *_locationOverride : langutil::SourceLocation{}}, + m_debugDataOverride{}, + m_useSourceLocationFrom{ + _locationOverride ? + UseSourceLocationFrom::LocationOverride : + UseSourceLocationFrom::Scanner + } + {} + + /// Constructs a Yul parser that is using the source locations + /// from the comments (via @src). + explicit Parser( + langutil::ErrorReporter& _errorReporter, + Dialect const& _dialect, + std::optional>> _sourceNames + ): + ParserBase(_errorReporter), + m_dialect(_dialect), + m_sourceNames{std::move(_sourceNames)}, + m_debugDataOverride{DebugData::create()}, + m_useSourceLocationFrom{ + m_sourceNames.has_value() ? + UseSourceLocationFrom::Comments : + UseSourceLocationFrom::Scanner + } {} /// 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::unique_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); + std::unique_ptr parseInline(std::shared_ptr const& _scanner); + + /// Parses an assembly block starting with `{` and ending with `}` + /// and expects end of input after the '}'. + /// @returns an empty shared pointer on error. + std::unique_ptr parse(langutil::CharStream& _charStream); protected: langutil::SourceLocation currentLocation() const override { - return m_locationOverride ? *m_locationOverride : ParserBase::currentLocation(); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) + return ParserBase::currentLocation(); + return m_locationOverride; } + langutil::Token advance() override; + + void fetchSourceLocationFromComment(); + + /// Creates a DebugData object with the correct source location set. + std::shared_ptr createDebugData() const; + /// Creates an inline assembly node with the current source location. template T createWithLocation() const { T r; - r.debugData = DebugData::create(currentLocation()); + r.debugData = createDebugData(); return r; } @@ -96,7 +140,11 @@ protected: private: Dialect const& m_dialect; - std::optional m_locationOverride; + + std::optional>> m_sourceNames; + langutil::SourceLocation m_locationOverride; + std::shared_ptr m_debugDataOverride; + UseSourceLocationFrom m_useSourceLocationFrom = UseSourceLocationFrom::Scanner; ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None; bool m_insideFunction = false; }; diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index e91208feb..2a7b705f8 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -41,48 +41,55 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::yul; -//@TODO source locations - -string AsmPrinter::operator()(Literal const& _literal) const +string AsmPrinter::operator()(Literal const& _literal) { + string const locationComment = formatSourceLocationComment(_literal); + switch (_literal.kind) { case LiteralKind::Number: yulAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal"); - return _literal.value.str() + appendTypeName(_literal.type); + return locationComment + _literal.value.str() + appendTypeName(_literal.type); case LiteralKind::Boolean: yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "Invalid bool literal."); - return ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type, true); + return locationComment + ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type, true); case LiteralKind::String: break; } - return escapeAndQuoteYulString(_literal.value.str()) + appendTypeName(_literal.type); + return locationComment + escapeAndQuoteString(_literal.value.str()) + appendTypeName(_literal.type); } -string AsmPrinter::operator()(Identifier const& _identifier) const +string AsmPrinter::operator()(Identifier const& _identifier) { yulAssert(!_identifier.name.empty(), "Invalid identifier."); - return _identifier.name.str(); + return formatSourceLocationComment(_identifier) + _identifier.name.str(); } -string AsmPrinter::operator()(ExpressionStatement const& _statement) const +string AsmPrinter::operator()(ExpressionStatement const& _statement) { - return std::visit(*this, _statement.expression); + string const locationComment = formatSourceLocationComment(_statement); + + return locationComment + std::visit(*this, _statement.expression); } -string AsmPrinter::operator()(Assignment const& _assignment) const +string AsmPrinter::operator()(Assignment const& _assignment) { + string const locationComment = formatSourceLocationComment(_assignment); + yulAssert(_assignment.variableNames.size() >= 1, ""); string variables = (*this)(_assignment.variableNames.front()); for (size_t i = 1; i < _assignment.variableNames.size(); ++i) variables += ", " + (*this)(_assignment.variableNames[i]); - return variables + " := " + std::visit(*this, *_assignment.value); + + return locationComment + variables + " := " + std::visit(*this, *_assignment.value); } -string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) const +string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) { - string out = "let "; + string out = formatSourceLocationComment(_variableDeclaration); + + out += "let "; out += boost::algorithm::join( _variableDeclaration.variables | ranges::views::transform( [this](TypedName argument) { return formatTypedName(argument); } @@ -97,10 +104,12 @@ string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) c return out; } -string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) const +string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) { yulAssert(!_functionDefinition.name.empty(), "Invalid function name."); - string out = "function " + _functionDefinition.name.str() + "("; + + string out = formatSourceLocationComment(_functionDefinition); + out += "function " + _functionDefinition.name.str() + "("; out += boost::algorithm::join( _functionDefinition.parameters | ranges::views::transform( [this](TypedName argument) { return formatTypedName(argument); } @@ -122,30 +131,41 @@ string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) con return out + "\n" + (*this)(_functionDefinition.body); } -string AsmPrinter::operator()(FunctionCall const& _functionCall) const +string AsmPrinter::operator()(FunctionCall const& _functionCall) { + string const locationComment = formatSourceLocationComment(_functionCall); + string const functionName = (*this)(_functionCall.functionName); return - (*this)(_functionCall.functionName) + "(" + + locationComment + + functionName + "(" + boost::algorithm::join( _functionCall.arguments | ranges::views::transform([&](auto&& _node) { return std::visit(*this, _node); }), ", " ) + ")"; } -string AsmPrinter::operator()(If const& _if) const +string AsmPrinter::operator()(If const& _if) { yulAssert(_if.condition, "Invalid if condition."); + + string out = formatSourceLocationComment(_if); + out += "if " + std::visit(*this, *_if.condition); + string body = (*this)(_if.body); char delim = '\n'; if (body.find('\n') == string::npos) delim = ' '; - return "if " + std::visit(*this, *_if.condition) + delim + (*this)(_if.body); + + return out + delim + body; } -string AsmPrinter::operator()(Switch const& _switch) const +string AsmPrinter::operator()(Switch const& _switch) { yulAssert(_switch.expression, "Invalid expression pointer."); - string out = "switch " + std::visit(*this, *_switch.expression); + + string out = formatSourceLocationComment(_switch); + out += "switch " + std::visit(*this, *_switch.expression); + for (auto const& _case: _switch.cases) { if (!_case.value) @@ -157,12 +177,15 @@ string AsmPrinter::operator()(Switch const& _switch) const return out; } -string AsmPrinter::operator()(ForLoop const& _forLoop) const +string AsmPrinter::operator()(ForLoop const& _forLoop) { yulAssert(_forLoop.condition, "Invalid for loop condition."); + string const locationComment = formatSourceLocationComment(_forLoop); + string pre = (*this)(_forLoop.pre); string condition = std::visit(*this, *_forLoop.condition); string post = (*this)(_forLoop.post); + char delim = '\n'; if ( pre.size() + condition.size() + post.size() < 60 && @@ -171,46 +194,50 @@ string AsmPrinter::operator()(ForLoop const& _forLoop) const ) delim = ' '; return + locationComment + ("for " + move(pre) + delim + move(condition) + delim + move(post) + "\n") + (*this)(_forLoop.body); } -string AsmPrinter::operator()(Break const&) const +string AsmPrinter::operator()(Break const& _break) { - return "break"; + return formatSourceLocationComment(_break) + "break"; } -string AsmPrinter::operator()(Continue const&) const +string AsmPrinter::operator()(Continue const& _continue) { - return "continue"; + return formatSourceLocationComment(_continue) + "continue"; } -string AsmPrinter::operator()(Leave const&) const +// '_leave' and '__leave' is reserved in VisualStudio +string AsmPrinter::operator()(Leave const& leave_) { - return "leave"; + return formatSourceLocationComment(leave_) + "leave"; } -string AsmPrinter::operator()(Block const& _block) const +string AsmPrinter::operator()(Block const& _block) { + string const locationComment = formatSourceLocationComment(_block); + if (_block.statements.empty()) - return "{ }"; + return locationComment + "{ }"; string body = boost::algorithm::join( _block.statements | ranges::views::transform([&](auto&& _node) { return std::visit(*this, _node); }), "\n" ); if (body.size() < 30 && body.find('\n') == string::npos) - return "{ " + body + " }"; + return locationComment + "{ " + body + " }"; else { boost::replace_all(body, "\n", "\n "); - return "{\n " + body + "\n}"; + return locationComment + "{\n " + body + "\n}"; } } -string AsmPrinter::formatTypedName(TypedName _variable) const +string AsmPrinter::formatTypedName(TypedName _variable) { yulAssert(!_variable.name.empty(), "Invalid variable name."); - return _variable.name.str() + appendTypeName(_variable.type); + return formatSourceLocationComment(_variable) + _variable.name.str() + appendTypeName(_variable.type); } string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const @@ -228,3 +255,31 @@ string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const else return ":" + _type.str(); } + +string AsmPrinter::formatSourceLocationComment(shared_ptr const& _debugData, bool _statement) +{ + if ( + !_debugData || + m_lastLocation == _debugData->location || + m_nameToSourceIndex.empty() + ) + return ""; + + m_lastLocation = _debugData->location; + + string sourceIndex = "-1"; + if (_debugData->location.sourceName) + sourceIndex = to_string(m_nameToSourceIndex.at(*_debugData->location.sourceName)); + + string sourceLocation = + "@src " + + sourceIndex + + ":" + + to_string(_debugData->location.start) + + ":" + + to_string(_debugData->location.end); + return + _statement ? + "/// " + sourceLocation + "\n" : + "/** " + sourceLocation + " */ "; +} diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 63b1bd3a2..940f663b4 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -24,9 +24,14 @@ #pragma once #include - #include +#include + +#include + +#include + namespace solidity::yul { struct Dialect; @@ -39,29 +44,52 @@ struct Dialect; class AsmPrinter { public: - AsmPrinter() {} - explicit AsmPrinter(Dialect const& _dialect): m_dialect(&_dialect) {} + explicit AsmPrinter( + Dialect const* _dialect = nullptr, + std::optional>> _sourceIndexToName = {} + ): + m_dialect(_dialect) + { + if (_sourceIndexToName) + for (auto&& [index, name]: *_sourceIndexToName) + m_nameToSourceIndex[*name] = index; + } - std::string operator()(Literal const& _literal) const; - std::string operator()(Identifier const& _identifier) const; - std::string operator()(ExpressionStatement const& _expr) const; - std::string operator()(Assignment const& _assignment) const; - std::string operator()(VariableDeclaration const& _variableDeclaration) const; - std::string operator()(FunctionDefinition const& _functionDefinition) const; - std::string operator()(FunctionCall const& _functionCall) const; - std::string operator()(If const& _if) const; - std::string operator()(Switch const& _switch) const; - std::string operator()(ForLoop const& _forLoop) const; - std::string operator()(Break const& _break) const; - std::string operator()(Continue const& _continue) const; - std::string operator()(Leave const& _continue) const; - std::string operator()(Block const& _block) const; + + explicit AsmPrinter( + Dialect const& _dialect, + std::optional>> _sourceIndexToName = {} + ): AsmPrinter(&_dialect, _sourceIndexToName) {} + + std::string operator()(Literal const& _literal); + std::string operator()(Identifier const& _identifier); + std::string operator()(ExpressionStatement const& _expr); + 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()(Break const& _break); + std::string operator()(Continue const& _continue); + std::string operator()(Leave const& _continue); + std::string operator()(Block const& _block); private: - std::string formatTypedName(TypedName _variable) const; + std::string formatTypedName(TypedName _variable); std::string appendTypeName(YulString _type, bool _isBoolLiteral = false) const; + std::string formatSourceLocationComment(std::shared_ptr const& _debugData, bool _statement); + template + std::string formatSourceLocationComment(T const& _node) + { + bool isExpression = std::is_constructible::value; + return formatSourceLocationComment(_node.debugData, !isExpression); + } - Dialect const* m_dialect = nullptr; + Dialect const* const m_dialect = nullptr; + std::map m_nameToSourceIndex; + langutil::SourceLocation m_lastLocation = {}; }; } diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 32318edb8..8a00608cf 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -88,18 +88,20 @@ evmasm::Assembly::OptimiserSettings translateOptimiserSettings( } -Scanner const& AssemblyStack::scanner() const +CharStream const& AssemblyStack::charStream(string const& _sourceName) const { - yulAssert(m_scanner, ""); - return *m_scanner; + yulAssert(m_charStream, ""); + yulAssert(m_charStream->name() == _sourceName, ""); + return *m_charStream; } bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) { m_errors.clear(); m_analysisSuccessful = false; - m_scanner = make_shared(CharStream(_source, _sourceName)); - m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(m_scanner, false); + m_charStream = make_unique(_source, _sourceName); + shared_ptr scanner = make_shared(*m_charStream); + m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(scanner, false); if (!m_errorReporter.errors().empty()) return false; yulAssert(m_parserResult, ""); @@ -132,7 +134,8 @@ void AssemblyStack::translate(AssemblyStack::Language _targetLanguage) ); *m_parserResult = EVMToEwasmTranslator( - languageToDialect(m_language, m_evmVersion) + languageToDialect(m_language, m_evmVersion), + *this ).run(*parserResult()); m_language = _targetLanguage; @@ -241,6 +244,7 @@ AssemblyStack::assembleWithDeployed(optional _deployName) const { auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName); yulAssert(creationAssembly, ""); + yulAssert(m_charStream, ""); MachineAssemblyObject creationObject; creationObject.bytecode = make_shared(creationAssembly->assemble()); @@ -249,7 +253,7 @@ AssemblyStack::assembleWithDeployed(optional _deployName) const creationObject.sourceMappings = make_unique( evmasm::AssemblyItem::computeSourceMapping( creationAssembly->items(), - {{scanner().charStream() ? scanner().charStream()->name() : "", 0}} + {{m_charStream->name(), 0}} ) ); @@ -261,7 +265,7 @@ AssemblyStack::assembleWithDeployed(optional _deployName) const deployedObject.sourceMappings = make_unique( evmasm::AssemblyItem::computeSourceMapping( deployedAssembly->items(), - {{scanner().charStream() ? scanner().charStream()->name() : "", 0}} + {{m_charStream->name(), 0}} ) ); } diff --git a/libyul/AssemblyStack.h b/libyul/AssemblyStack.h index 925d0f87d..3fcd15998 100644 --- a/libyul/AssemblyStack.h +++ b/libyul/AssemblyStack.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -61,7 +62,7 @@ struct MachineAssemblyObject * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * Ewasm as output. */ -class AssemblyStack +class AssemblyStack: public langutil::CharStreamProvider { public: enum class Language { Yul, Assembly, StrictAssembly, Ewasm }; @@ -77,8 +78,8 @@ public: m_errorReporter(m_errors) {} - /// @returns the scanner used during parsing - langutil::Scanner const& scanner() const; + /// @returns the char stream used during parsing + langutil::CharStream const& charStream(std::string const& _sourceName) const override; /// Runs parsing and analysis steps, returns false if input cannot be assembled. /// Multiple calls overwrite the previous state. @@ -132,7 +133,7 @@ private: langutil::EVMVersion m_evmVersion; solidity::frontend::OptimiserSettings m_optimiserSettings; - std::shared_ptr m_scanner; + std::unique_ptr m_charStream; bool m_analysisSuccessful = false; std::shared_ptr m_parserResult; diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 59fb6d468..7b9d102e8 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -53,6 +53,9 @@ add_library(yul backends/evm/AsmCodeGen.h backends/evm/ConstantOptimiser.cpp backends/evm/ConstantOptimiser.h + backends/evm/ControlFlowGraph.h + backends/evm/ControlFlowGraphBuilder.cpp + backends/evm/ControlFlowGraphBuilder.h backends/evm/EthAssemblyAdapter.cpp backends/evm/EthAssemblyAdapter.h backends/evm/EVMCodeTransform.cpp diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 3dadf9cb5..05ad9cb82 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -25,11 +25,14 @@ #include #include +#include #include #include #include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; @@ -47,18 +50,36 @@ string indent(std::string const& _input) } -string Data::toString(Dialect const*) const +string Data::toString(Dialect const*, optional) const { return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\""; } string Object::toString(Dialect const* _dialect) const +{ + string useSrcComment; + + if (debugData && debugData->sourceNames) + useSrcComment = + "/// @use-src " + + joinHumanReadable(ranges::views::transform(*debugData->sourceNames, [](auto&& _pair) { + return to_string(_pair.first) + ":" + util::escapeAndQuoteString(*_pair.second); + })) + + "\n"; + return useSrcComment + toString(_dialect, debugData ? debugData->sourceNames : optional{}); +} + +string Object::toString(Dialect const* _dialect, std::optional _sourceNames) const { yulAssert(code, "No code"); - string inner = "code " + (_dialect ? AsmPrinter{*_dialect} : AsmPrinter{})(*code); + string inner = "code " + AsmPrinter{_dialect, _sourceNames}(*code); for (auto const& obj: subObjects) - inner += "\n" + obj->toString(_dialect); + { + if (auto const* o = dynamic_cast(obj.get())) + yulAssert(!o->debugData || !o->debugData->sourceNames, ""); + inner += "\n" + obj->toString(_dialect, _sourceNames); + } return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}"; } diff --git a/libyul/Object.h b/libyul/Object.h index 81406a1df..9b7519b1e 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -35,39 +35,55 @@ struct Dialect; struct AsmAnalysisInfo; +using SourceNameMap = std::map>; + +struct Object; + /** * Generic base class for both Yul objects and Yul data. */ struct ObjectNode { virtual ~ObjectNode() = default; - virtual std::string toString(Dialect const* _dialect) const = 0; - std::string toString() { return toString(nullptr); } /// Name of the object. /// Can be empty since .yul files can also just contain code, without explicitly placing it in an object. YulString name; +protected: + virtual std::string toString(Dialect const* _dialect, std::optional _sourceNames) const = 0; + + /// Object should have access to toString + friend struct Object; }; /** * Named data in Yul objects. */ -struct Data: ObjectNode +struct Data: public ObjectNode { Data(YulString _name, bytes _data): data(std::move(_data)) { name = _name; } - std::string toString(Dialect const* _dialect) const override; bytes data; + +protected: + std::string toString(Dialect const* _dialect, std::optional _sourceNames) const override; }; + +struct ObjectDebugData +{ + std::optional sourceNames = {}; +}; + + /** * Yul code and data object container. */ -struct Object: ObjectNode +struct Object: public ObjectNode { public: - /// @returns a (parseable) string representation. Includes types if @a _yul is set. - std::string toString(Dialect const* _dialect) const override; + /// @returns a (parseable) string representation. + std::string toString(Dialect const* _dialect) const; /// @returns the set of names of data objects accessible from within the code of /// this object, including the name of object itself @@ -94,8 +110,12 @@ public: std::map subIndexByName; std::shared_ptr analysisInfo; + std::shared_ptr debugData; + /// @returns the name of the special metadata data object. static std::string metadataName() { return ".metadata"; } +protected: + std::string toString(Dialect const* _dialect, std::optional _sourceNames) const override; }; } diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index d2eb1feca..8ce5896cf 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -26,6 +26,11 @@ #include #include +#include + +#include + +#include using namespace std; using namespace solidity; @@ -40,6 +45,8 @@ shared_ptr ObjectParser::parse(shared_ptr const& _scanner, bool { shared_ptr object; m_scanner = _scanner; + m_sourceNameMapping = tryParseSourceNameMapping(); + if (currentToken() == Token::LBrace) { // Special case: Code-only form. @@ -51,8 +58,9 @@ shared_ptr ObjectParser::parse(shared_ptr const& _scanner, bool } else object = parseObject(); - if (object && !_reuseScanner) + if (!_reuseScanner) expectToken(Token::EOS); + object->debugData = make_shared(ObjectDebugData{m_sourceNameMapping}); return object; } catch (FatalError const&) @@ -104,10 +112,64 @@ shared_ptr ObjectParser::parseCode() return parseBlock(); } +optional ObjectParser::tryParseSourceNameMapping() const +{ + // @use-src 0:"abc.sol", 1:"foo.sol", 2:"bar.sol" + // + // UseSrcList := UseSrc (',' UseSrc)* + // UseSrc := [0-9]+ ':' FileName + // FileName := "(([^\"]|\.)*)" + + // Matches some "@use-src TEXT". + static std::regex const lineRE = std::regex( + "(^|\\s)@use-src\\b", + std::regex_constants::ECMAScript | std::regex_constants::optimize + ); + std::smatch sm; + if (!std::regex_search(m_scanner->currentCommentLiteral(), sm, lineRE)) + return nullopt; + + solAssert(sm.size() == 2, ""); + auto text = m_scanner->currentCommentLiteral().substr(static_cast(sm.position() + sm.length())); + CharStream charStream(text, ""); + Scanner scanner(charStream); + if (scanner.currentToken() == Token::EOS) + return SourceNameMap{}; + SourceNameMap sourceNames; + + while (scanner.currentToken() != Token::EOS) + { + if (scanner.currentToken() != Token::Number) + break; + auto sourceIndex = toUnsignedInt(scanner.currentLiteral()); + if (!sourceIndex) + break; + if (scanner.next() != Token::Colon) + break; + if (scanner.next() != Token::StringLiteral) + break; + sourceNames[*sourceIndex] = make_shared(scanner.currentLiteral()); + + Token const next = scanner.next(); + if (next == Token::EOS) + return {move(sourceNames)}; + if (next != Token::Comma) + break; + scanner.next(); + } + + m_errorReporter.syntaxError( + 9804_error, + m_scanner->currentCommentLocation(), + "Error parsing arguments to @use-src. Expected: \":\" \"\", ..." + ); + return nullopt; +} + shared_ptr ObjectParser::parseBlock() { - Parser parser(m_errorReporter, m_dialect); - shared_ptr block = parser.parse(m_scanner, true); + Parser parser(m_errorReporter, m_dialect, m_sourceNameMapping); + shared_ptr block = parser.parseInline(m_scanner); yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); return block; } diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h index 6c6f527cd..86a5ef77c 100644 --- a/libyul/ObjectParser.h +++ b/libyul/ObjectParser.h @@ -55,7 +55,10 @@ public: /// @returns an empty shared pointer on error. std::shared_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); + std::optional const& sourceNameMapping() const noexcept { return m_sourceNameMapping; } + private: + std::optional tryParseSourceNameMapping() const; std::shared_ptr parseObject(Object* _containingObject = nullptr); std::shared_ptr parseCode(); std::shared_ptr parseBlock(); @@ -66,6 +69,8 @@ private: void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr _subObject); Dialect const& m_dialect; + + std::optional m_sourceNameMapping; }; } diff --git a/libyul/Scope.cpp b/libyul/Scope.cpp index 3425e81ae..4a9520f98 100644 --- a/libyul/Scope.cpp +++ b/libyul/Scope.cpp @@ -32,6 +32,7 @@ bool Scope::registerVariable(YulString _name, YulType const& _type) return false; Variable variable; variable.type = _type; + variable.name = _name; identifiers[_name] = variable; return true; } @@ -40,7 +41,7 @@ bool Scope::registerFunction(YulString _name, std::vector _arguments, s { if (exists(_name)) return false; - identifiers[_name] = Function{std::move(_arguments), std::move(_returns)}; + identifiers[_name] = Function{std::move(_arguments), std::move(_returns), _name}; return true; } diff --git a/libyul/Scope.h b/libyul/Scope.h index a64906f2d..f9ac3830a 100644 --- a/libyul/Scope.h +++ b/libyul/Scope.h @@ -37,11 +37,16 @@ struct Scope { using YulType = YulString; - struct Variable { YulType type; }; + struct Variable + { + YulType type; + YulString name; + }; struct Function { std::vector arguments; std::vector returns; + YulString name; }; using Identifier = std::variant; diff --git a/libyul/backends/evm/ControlFlowGraph.h b/libyul/backends/evm/ControlFlowGraph.h new file mode 100644 index 000000000..60bec498b --- /dev/null +++ b/libyul/backends/evm/ControlFlowGraph.h @@ -0,0 +1,210 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Control flow graph and stack layout structures used during code generation. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace solidity::yul +{ + +/// The following structs describe different kinds of stack slots. +/// Each stack slot is equality- and less-than-comparable and +/// specifies an attribute ``canBeFreelyGenerated`` that is true, +/// if a slot of this kind always has a known value at compile time and +/// therefore can safely be removed from the stack at any time and then +/// regenerated later. + +/// The label pushed as return label before a function call, i.e. the label the call is supposed to return to. +struct FunctionCallReturnLabelSlot +{ + std::reference_wrapper call; + bool operator==(FunctionCallReturnLabelSlot const& _rhs) const { return &call.get() == &_rhs.call.get(); } + bool operator<(FunctionCallReturnLabelSlot const& _rhs) const { return &call.get() < &_rhs.call.get(); } + static constexpr bool canBeFreelyGenerated = true; +}; +/// The return jump target of a function while generating the code of the function body. +/// I.e. the caller of a function pushes a ``FunctionCallReturnLabelSlot`` (see above) before jumping to the function and +/// this very slot is viewed as ``FunctionReturnLabelSlot`` inside the function body and jumped to when returning from +/// the function. +struct FunctionReturnLabelSlot +{ + bool operator==(FunctionReturnLabelSlot const&) const { return true; } + bool operator<(FunctionReturnLabelSlot const&) const { return false; } + static constexpr bool canBeFreelyGenerated = false; +}; +/// A slot containing the current value of a particular variable. +struct VariableSlot +{ + std::reference_wrapper variable; + std::shared_ptr debugData{}; + bool operator==(VariableSlot const& _rhs) const { return &variable.get() == &_rhs.variable.get(); } + bool operator<(VariableSlot const& _rhs) const { return &variable.get() < &_rhs.variable.get(); } + static constexpr bool canBeFreelyGenerated = false; +}; +/// A slot containing a literal value. +struct LiteralSlot +{ + u256 value; + std::shared_ptr debugData{}; + bool operator==(LiteralSlot const& _rhs) const { return value == _rhs.value; } + bool operator<(LiteralSlot const& _rhs) const { return value < _rhs.value; } + static constexpr bool canBeFreelyGenerated = true; +}; +/// A slot containing the index-th return value of a previous call. +struct TemporarySlot +{ + /// The call that returned this slot. + std::reference_wrapper call; + /// Specifies to which of the values returned by the call this slot refers. + /// index == 0 refers to the slot deepest in the stack after the call. + size_t index = 0; + bool operator==(TemporarySlot const& _rhs) const { return &call.get() == &_rhs.call.get() && index == _rhs.index; } + bool operator<(TemporarySlot const& _rhs) const { return std::make_pair(&call.get(), index) < std::make_pair(&_rhs.call.get(), _rhs.index); } + static constexpr bool canBeFreelyGenerated = false; +}; +/// A slot containing an arbitrary value that is always eventually popped and never used. +/// Used to maintain stack balance on control flow joins. +struct JunkSlot +{ + bool operator==(JunkSlot const&) const { return true; } + bool operator<(JunkSlot const&) const { return false; } + static constexpr bool canBeFreelyGenerated = true; +}; +using StackSlot = std::variant; +/// The stack top is usually the last element of the vector. +using Stack = std::vector; + +/// @returns true if @a _slot can be generated on the stack at any time. +inline bool canBeFreelyGenerated(StackSlot const& _slot) +{ + return std::visit([](auto const& _typedSlot) { return std::decay_t::canBeFreelyGenerated; }, _slot); +} + +/// Control flow graph consisting of ``CFG::BasicBlock``s connected by control flow. +struct CFG +{ + explicit CFG() {} + CFG(CFG const&) = delete; + CFG(CFG&&) = delete; + CFG& operator=(CFG const&) = delete; + CFG& operator=(CFG&&) = delete; + ~CFG() = default; + + struct BuiltinCall + { + std::shared_ptr debugData; + std::reference_wrapper builtin; + std::reference_wrapper functionCall; + /// Number of proper arguments with a position on the stack, excluding literal arguments. + /// Literal arguments (like the literal string in ``datasize``) do not have a location on the stack, + /// but are handled internally by the builtin's code generation function. + size_t arguments = 0; + }; + struct FunctionCall + { + std::shared_ptr debugData; + std::reference_wrapper function; + std::reference_wrapper functionCall; + }; + struct Assignment + { + std::shared_ptr debugData; + /// The variables being assigned to also occur as ``output`` in the ``Operation`` containing + /// the assignment, but are also stored here for convenience. + std::vector variables; + }; + + struct Operation + { + /// Stack slots this operation expects at the top of the stack and consumes. + Stack input; + /// Stack slots this operation leaves on the stack as output. + Stack output; + std::variant operation; + }; + + struct FunctionInfo; + /// A basic control flow block containing ``Operation``s acting on the stack. + /// Maintains a list of entry blocks and a typed exit. + struct BasicBlock + { + struct MainExit {}; + struct ConditionalJump + { + StackSlot condition; + BasicBlock* nonZero = nullptr; + BasicBlock* zero = nullptr; + }; + struct Jump + { + BasicBlock* target = nullptr; + /// The only backwards jumps are jumps from loop post to loop condition. + bool backwards = false; + }; + struct FunctionReturn { CFG::FunctionInfo* info = nullptr; }; + struct Terminated {}; + std::vector entries; + std::vector operations; + std::variant exit = MainExit{}; + }; + + struct FunctionInfo + { + std::shared_ptr debugData; + Scope::Function const& function; + BasicBlock* entry = nullptr; + std::vector parameters; + std::vector returnVariables; + }; + + /// The main entry point, i.e. the start of the outermost Yul block. + BasicBlock* entry = nullptr; + /// Subgraphs for functions. + std::map functionInfo; + /// List of functions in order of declaration. + std::list functions; + + /// Container for blocks for explicit ownership. + std::list blocks; + /// Container for generated variables for explicit ownership. + /// Ghost variables are generated to store switch conditions when transforming the control flow + /// of a switch to a sequence of conditional jumps. + std::list ghostVariables; + /// Container for generated calls for explicit ownership. + /// Ghost calls are used for the equality comparisons of the switch condition ghost variable with + /// the switch case literals when transforming the control flow of a switch to a sequence of conditional jumps. + std::list ghostCalls; + + BasicBlock& makeBlock() + { + return blocks.emplace_back(BasicBlock{}); + } +}; + +} diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.cpp b/libyul/backends/evm/ControlFlowGraphBuilder.cpp new file mode 100644 index 000000000..f3a1d5823 --- /dev/null +++ b/libyul/backends/evm/ControlFlowGraphBuilder.cpp @@ -0,0 +1,470 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Transformation of a Yul AST into a control flow graph. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace solidity; +using namespace solidity::yul; +using namespace std; + +std::unique_ptr ControlFlowGraphBuilder::build( + AsmAnalysisInfo const& _analysisInfo, + Dialect const& _dialect, + Block const& _block +) +{ + auto result = std::make_unique(); + result->entry = &result->makeBlock(); + + ControlFlowGraphBuilder builder(*result, _analysisInfo, _dialect); + builder.m_currentBlock = result->entry; + builder(_block); + + // Determine which blocks are reachable from the entry. + util::BreadthFirstSearch reachabilityCheck{{result->entry}}; + for (auto const& functionInfo: result->functionInfo | ranges::views::values) + reachabilityCheck.verticesToTraverse.emplace_back(functionInfo.entry); + + reachabilityCheck.run([&](CFG::BasicBlock* _node, auto&& _addChild) { + visit(util::GenericVisitor{ + [&](CFG::BasicBlock::Jump const& _jump) { + _addChild(_jump.target); + }, + [&](CFG::BasicBlock::ConditionalJump const& _jump) { + _addChild(_jump.zero); + _addChild(_jump.nonZero); + }, + [](CFG::BasicBlock::FunctionReturn const&) {}, + [](CFG::BasicBlock::Terminated const&) {}, + [](CFG::BasicBlock::MainExit const&) {} + }, _node->exit); + }); + + // Remove all entries from unreachable nodes from the graph. + for (CFG::BasicBlock* node: reachabilityCheck.visited) + cxx20::erase_if(node->entries, [&](CFG::BasicBlock* entry) -> bool { + return !reachabilityCheck.visited.count(entry); + }); + + // TODO: It might be worthwhile to run some further simplifications on the graph itself here. + // E.g. if there is a jump to a node that has the jumping node as its only entry, the nodes can be fused, etc. + + return result; +} + +ControlFlowGraphBuilder::ControlFlowGraphBuilder( + CFG& _graph, + AsmAnalysisInfo const& _analysisInfo, + Dialect const& _dialect +): + m_graph(_graph), + m_info(_analysisInfo), + m_dialect(_dialect) +{ +} + +StackSlot ControlFlowGraphBuilder::operator()(Literal const& _literal) +{ + return LiteralSlot{valueOfLiteral(_literal), _literal.debugData}; +} + +StackSlot ControlFlowGraphBuilder::operator()(Identifier const& _identifier) +{ + return VariableSlot{lookupVariable(_identifier.name), _identifier.debugData}; +} + +StackSlot ControlFlowGraphBuilder::operator()(Expression const& _expression) +{ + return std::visit(*this, _expression); +} + +StackSlot ControlFlowGraphBuilder::operator()(FunctionCall const& _call) +{ + CFG::Operation const& operation = visitFunctionCall(_call); + yulAssert(operation.output.size() == 1, ""); + return operation.output.front(); +} + +void ControlFlowGraphBuilder::operator()(VariableDeclaration const& _varDecl) +{ + yulAssert(m_currentBlock, ""); + auto declaredVariables = _varDecl.variables | ranges::views::transform([&](TypedName const& _var) { + return VariableSlot{lookupVariable(_var.name), _var.debugData}; + }) | ranges::to>; + Stack input; + if (_varDecl.value) + input = visitAssignmentRightHandSide(*_varDecl.value, declaredVariables.size()); + else + input = Stack(_varDecl.variables.size(), LiteralSlot{0, _varDecl.debugData}); + m_currentBlock->operations.emplace_back(CFG::Operation{ + std::move(input), + declaredVariables | ranges::to, + CFG::Assignment{_varDecl.debugData, declaredVariables} + }); +} +void ControlFlowGraphBuilder::operator()(Assignment const& _assignment) +{ + auto assignedVariables = _assignment.variableNames | ranges::views::transform([&](Identifier const& _var) { + return VariableSlot{lookupVariable(_var.name), _var.debugData}; + }) | ranges::to>; + + yulAssert(m_currentBlock, ""); + m_currentBlock->operations.emplace_back(CFG::Operation{ + // input + visitAssignmentRightHandSide(*_assignment.value, assignedVariables.size()), + // output + assignedVariables | ranges::to, + // operation + CFG::Assignment{_assignment.debugData, assignedVariables} + }); +} +void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt) +{ + yulAssert(m_currentBlock, ""); + std::visit(util::GenericVisitor{ + [&](FunctionCall const& _call) { + CFG::Operation const& operation = visitFunctionCall(_call); + yulAssert(operation.output.empty(), ""); + }, + [&](auto const&) { yulAssert(false, ""); } + }, _exprStmt.expression); + + // TODO: Ideally this would be done on the expression label and for all functions that always revert, + // not only for builtins. + if (auto const* funCall = get_if(&_exprStmt.expression)) + if (BuiltinFunction const* builtin = m_dialect.builtin(funCall->functionName.name)) + if (builtin->controlFlowSideEffects.terminates) + { + m_currentBlock->exit = CFG::BasicBlock::Terminated{}; + m_currentBlock = &m_graph.makeBlock(); + } +} + +void ControlFlowGraphBuilder::operator()(Block const& _block) +{ + ScopedSaveAndRestore saveScope(m_scope, m_info.scopes.at(&_block).get()); + for (auto const& statement: _block.statements) + std::visit(*this, statement); +} + +void ControlFlowGraphBuilder::operator()(If const& _if) +{ + auto&& [ifBranch, afterIf] = makeConditionalJump(std::visit(*this, *_if.condition)); + m_currentBlock = ifBranch; + (*this)(_if.body); + jump(*afterIf); +} + +void ControlFlowGraphBuilder::operator()(Switch const& _switch) +{ + yulAssert(m_currentBlock, ""); + auto ghostVariableId = m_graph.ghostVariables.size(); + YulString ghostVariableName("GHOST[" + to_string(ghostVariableId) + "]"); + auto& ghostVar = m_graph.ghostVariables.emplace_back(Scope::Variable{""_yulstring, ghostVariableName}); + + // Artificially generate: + // let := + VariableSlot ghostVarSlot{ghostVar, debugDataOf(*_switch.expression)}; + m_currentBlock->operations.emplace_back(CFG::Operation{ + Stack{std::visit(*this, *_switch.expression)}, + Stack{ghostVarSlot}, + CFG::Assignment{_switch.debugData, {ghostVarSlot}} + }); + + BuiltinFunction const* equalityBuiltin = m_dialect.equalityFunction({}); + yulAssert(equalityBuiltin, ""); + + // Artificially generate: + // eq(, ) + auto makeValueCompare = [&](Literal const& _value) { + yul::FunctionCall const& ghostCall = m_graph.ghostCalls.emplace_back(yul::FunctionCall{ + _value.debugData, + yul::Identifier{{}, "eq"_yulstring}, + {_value, Identifier{{}, ghostVariableName}} + }); + CFG::Operation& operation = m_currentBlock->operations.emplace_back(CFG::Operation{ + Stack{ghostVarSlot, LiteralSlot{valueOfLiteral(_value), _value.debugData}}, + Stack{TemporarySlot{ghostCall, 0}}, + CFG::BuiltinCall{_switch.debugData, *equalityBuiltin, ghostCall, 2}, + }); + return operation.output.front(); + }; + CFG::BasicBlock& afterSwitch = m_graph.makeBlock(); + yulAssert(!_switch.cases.empty(), ""); + for (auto const& switchCase: _switch.cases | ranges::views::drop_last(1)) + { + yulAssert(switchCase.value, ""); + auto&& [caseBranch, elseBranch] = makeConditionalJump(makeValueCompare(*switchCase.value)); + m_currentBlock = caseBranch; + (*this)(switchCase.body); + jump(afterSwitch); + m_currentBlock = elseBranch; + } + Case const& switchCase = _switch.cases.back(); + if (switchCase.value) + { + CFG::BasicBlock& caseBranch = m_graph.makeBlock(); + makeConditionalJump(makeValueCompare(*switchCase.value), caseBranch, afterSwitch); + m_currentBlock = &caseBranch; + } + (*this)(switchCase.body); + jump(afterSwitch); +} + +void ControlFlowGraphBuilder::operator()(ForLoop const& _loop) +{ + ScopedSaveAndRestore scopeRestore(m_scope, m_info.scopes.at(&_loop.pre).get()); + (*this)(_loop.pre); + + std::optional constantCondition; + if (auto const* literalCondition = get_if(_loop.condition.get())) + constantCondition = valueOfLiteral(*literalCondition) != 0; + + CFG::BasicBlock& loopCondition = m_graph.makeBlock(); + CFG::BasicBlock& loopBody = m_graph.makeBlock(); + CFG::BasicBlock& post = m_graph.makeBlock(); + CFG::BasicBlock& afterLoop = m_graph.makeBlock(); + + ScopedSaveAndRestore scopedSaveAndRestore(m_forLoopInfo, ForLoopInfo{afterLoop, post}); + + if (constantCondition.has_value()) + { + if (*constantCondition) + { + jump(loopBody); + (*this)(_loop.body); + jump(post); + (*this)(_loop.post); + jump(loopBody, true); + } + else + jump(afterLoop); + } + else + { + jump(loopCondition); + makeConditionalJump(std::visit(*this, *_loop.condition), loopBody, afterLoop); + m_currentBlock = &loopBody; + (*this)(_loop.body); + jump(post); + (*this)(_loop.post); + jump(loopCondition, true); + } + + m_currentBlock = &afterLoop; +} + +void ControlFlowGraphBuilder::operator()(Break const&) +{ + yulAssert(m_forLoopInfo.has_value(), ""); + jump(m_forLoopInfo->afterLoop); + m_currentBlock = &m_graph.makeBlock(); +} + +void ControlFlowGraphBuilder::operator()(Continue const&) +{ + yulAssert(m_forLoopInfo.has_value(), ""); + jump(m_forLoopInfo->post); + m_currentBlock = &m_graph.makeBlock(); +} + +void ControlFlowGraphBuilder::operator()(Leave const&) +{ + yulAssert(m_currentFunctionExit.has_value(), ""); + m_currentBlock->exit = *m_currentFunctionExit; + m_currentBlock = &m_graph.makeBlock(); +} + +void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function) +{ + yulAssert(m_scope, ""); + yulAssert(m_scope->identifiers.count(_function.name), ""); + Scope::Function& function = std::get(m_scope->identifiers.at(_function.name)); + m_graph.functions.emplace_back(&function); + + yulAssert(m_info.scopes.at(&_function.body), ""); + Scope* virtualFunctionScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); + yulAssert(virtualFunctionScope, ""); + + auto&& [it, inserted] = m_graph.functionInfo.emplace(std::make_pair(&function, CFG::FunctionInfo{ + _function.debugData, + function, + &m_graph.makeBlock(), + _function.parameters | ranges::views::transform([&](auto const& _param) { + return VariableSlot{ + std::get(virtualFunctionScope->identifiers.at(_param.name)), + _param.debugData + }; + }) | ranges::to, + _function.returnVariables | ranges::views::transform([&](auto const& _retVar) { + return VariableSlot{ + std::get(virtualFunctionScope->identifiers.at(_retVar.name)), + _retVar.debugData + }; + }) | ranges::to + })); + yulAssert(inserted, ""); + CFG::FunctionInfo& functionInfo = it->second; + + ControlFlowGraphBuilder builder{m_graph, m_info, m_dialect}; + builder.m_currentFunctionExit = CFG::BasicBlock::FunctionReturn{&functionInfo}; + builder.m_currentBlock = functionInfo.entry; + builder(_function.body); + builder.m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{&functionInfo}; +} + + +CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call) +{ + yulAssert(m_scope, ""); + yulAssert(m_currentBlock, ""); + + if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name)) + { + Stack inputs; + for (auto&& [idx, arg]: _call.arguments | ranges::views::enumerate | ranges::views::reverse) + if (!builtin->literalArgument(idx).has_value()) + inputs.emplace_back(std::visit(*this, arg)); + CFG::BuiltinCall builtinCall{_call.debugData, *builtin, _call, inputs.size()}; + return m_currentBlock->operations.emplace_back(CFG::Operation{ + // input + std::move(inputs), + // output + ranges::views::iota(0u, builtin->returns.size()) | ranges::views::transform([&](size_t _i) { + return TemporarySlot{_call, _i}; + }) | ranges::to, + // operation + move(builtinCall) + }); + } + else + { + Scope::Function const& function = lookupFunction(_call.functionName.name); + Stack inputs{FunctionCallReturnLabelSlot{_call}}; + for (auto const& arg: _call.arguments | ranges::views::reverse) + inputs.emplace_back(std::visit(*this, arg)); + return m_currentBlock->operations.emplace_back(CFG::Operation{ + // input + std::move(inputs), + // output + ranges::views::iota(0u, function.returns.size()) | ranges::views::transform([&](size_t _i) { + return TemporarySlot{_call, _i}; + }) | ranges::to, + // operation + CFG::FunctionCall{_call.debugData, function, _call} + }); + } +} + +Stack ControlFlowGraphBuilder::visitAssignmentRightHandSide(Expression const& _expression, size_t _expectedSlotCount) +{ + return std::visit(util::GenericVisitor{ + [&](FunctionCall const& _call) -> Stack { + CFG::Operation const& operation = visitFunctionCall(_call); + yulAssert(_expectedSlotCount == operation.output.size(), ""); + return operation.output; + }, + [&](auto const& _identifierOrLiteral) -> Stack { + yulAssert(_expectedSlotCount == 1, ""); + return {(*this)(_identifierOrLiteral)}; + } + }, _expression); +} + +Scope::Function const& ControlFlowGraphBuilder::lookupFunction(YulString _name) const +{ + Scope::Function const* function = nullptr; + yulAssert(m_scope->lookup(_name, util::GenericVisitor{ + [](Scope::Variable&) { yulAssert(false, "Expected function name."); }, + [&](Scope::Function& _function) { function = &_function; } + }), "Function name not found."); + yulAssert(function, ""); + return *function; +} + +Scope::Variable const& ControlFlowGraphBuilder::lookupVariable(YulString _name) const +{ + yulAssert(m_scope, ""); + Scope::Variable const* var = nullptr; + if (m_scope->lookup(_name, util::GenericVisitor{ + [&](Scope::Variable& _var) { var = &_var; }, + [](Scope::Function&) + { + yulAssert(false, "Function not removed during desugaring."); + } + })) + { + yulAssert(var, ""); + return *var; + }; + yulAssert(false, "External identifier access unimplemented."); +} + +std::pair ControlFlowGraphBuilder::makeConditionalJump(StackSlot _condition) +{ + CFG::BasicBlock& nonZero = m_graph.makeBlock(); + CFG::BasicBlock& zero = m_graph.makeBlock(); + makeConditionalJump(move(_condition), nonZero, zero); + return {&nonZero, &zero}; +} + +void ControlFlowGraphBuilder::makeConditionalJump(StackSlot _condition, CFG::BasicBlock& _nonZero, CFG::BasicBlock& _zero) +{ + yulAssert(m_currentBlock, ""); + m_currentBlock->exit = CFG::BasicBlock::ConditionalJump{ + move(_condition), + &_nonZero, + &_zero + }; + _nonZero.entries.emplace_back(m_currentBlock); + _zero.entries.emplace_back(m_currentBlock); + m_currentBlock = nullptr; +} + +void ControlFlowGraphBuilder::jump(CFG::BasicBlock& _target, bool backwards) +{ + yulAssert(m_currentBlock, ""); + m_currentBlock->exit = CFG::BasicBlock::Jump{&_target, backwards}; + _target.entries.emplace_back(m_currentBlock); + m_currentBlock = &_target; +} diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.h b/libyul/backends/evm/ControlFlowGraphBuilder.h new file mode 100644 index 000000000..2a70a9472 --- /dev/null +++ b/libyul/backends/evm/ControlFlowGraphBuilder.h @@ -0,0 +1,86 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Transformation of a Yul AST into a control flow graph. + */ +#pragma once + +#include + +namespace solidity::yul +{ + +class ControlFlowGraphBuilder +{ +public: + ControlFlowGraphBuilder(ControlFlowGraphBuilder const&) = delete; + ControlFlowGraphBuilder& operator=(ControlFlowGraphBuilder const&) = delete; + static std::unique_ptr build(AsmAnalysisInfo const& _analysisInfo, Dialect const& _dialect, Block const& _block); + + StackSlot operator()(Expression const& _literal); + StackSlot operator()(Literal const& _literal); + StackSlot operator()(Identifier const& _identifier); + StackSlot operator()(FunctionCall const&); + + void operator()(VariableDeclaration const& _varDecl); + void operator()(Assignment const& _assignment); + void operator()(ExpressionStatement const& _statement); + + void operator()(Block const& _block); + + void operator()(If const& _if); + void operator()(Switch const& _switch); + void operator()(ForLoop const&); + void operator()(Break const&); + void operator()(Continue const&); + void operator()(Leave const&); + void operator()(FunctionDefinition const&); + +private: + ControlFlowGraphBuilder( + CFG& _graph, + AsmAnalysisInfo const& _analysisInfo, + Dialect const& _dialect + ); + CFG::Operation const& visitFunctionCall(FunctionCall const&); + Stack visitAssignmentRightHandSide(Expression const& _expression, size_t _expectedSlotCount); + + Scope::Function const& lookupFunction(YulString _name) const; + Scope::Variable const& lookupVariable(YulString _name) const; + /// @returns a pair of newly created blocks, the first element being the non-zero branch, the second element the + /// zero branch. + /// Resets m_currentBlock to enforce a subsequent explicit reassignment. + std::pair makeConditionalJump(StackSlot _condition); + /// Resets m_currentBlock to enforce a subsequent explicit reassignment. + void makeConditionalJump(StackSlot _condition, CFG::BasicBlock& _nonZero, CFG::BasicBlock& _zero); + void jump(CFG::BasicBlock& _target, bool _backwards = false); + CFG& m_graph; + AsmAnalysisInfo const& m_info; + Dialect const& m_dialect; + CFG::BasicBlock* m_currentBlock = nullptr; + Scope* m_scope = nullptr; + struct ForLoopInfo + { + std::reference_wrapper afterLoop; + std::reference_wrapper post; + }; + std::optional m_forLoopInfo; + std::optional m_currentFunctionExit; +}; + +} diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index d5530c52a..bc41eb9b2 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -237,16 +237,14 @@ void CodeTransform::operator()(FunctionCall const& _call) { yulAssert(m_scope, ""); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); if (BuiltinFunctionForEVM const* builtin = m_dialect.builtin(_call.functionName.name)) builtin->generateCode(_call, m_assembly, m_builtinContext, [&](Expression const& _expression) { visitExpression(_expression); }); else { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); - AbstractAssembly::LabelID returnLabel(numeric_limits::max()); // only used for evm 1.0 - - returnLabel = m_assembly.newLabelId(); + AbstractAssembly::LabelID returnLabel = m_assembly.newLabelId(); m_assembly.appendLabelReference(returnLabel); Scope::Function* function = nullptr; @@ -320,8 +318,6 @@ void CodeTransform::operator()(If const& _if) void CodeTransform::operator()(Switch const& _switch) { - //@TODO use JUMPV in EVM1.5? - visitExpression(*_switch.expression); int expressionHeight = m_assembly.stackHeight(); map caseBodies; @@ -416,6 +412,8 @@ void CodeTransform::operator()(FunctionDefinition const& _function) subTransform.setupReturnVariablesAndFunctionExit(); subTransform(_function.body); + + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_function.debugData)); if (!subTransform.m_stackErrors.empty()) { m_assembly.markAsInvalid(); diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 90e5b2815..f4176c1b2 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -163,6 +163,7 @@ map createBuiltins(langutil::EVMVersion _evmVe ) { yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); + _assembly.setSourceLocation(_call.debugData->location); _assembly.appendLinkerSymbol(std::get(arg).value.str()); })); @@ -192,6 +193,7 @@ map createBuiltins(langutil::EVMVersion _evmVe yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); YulString dataName = std::get(arg).value; + _assembly.setSourceLocation(_call.debugData->location); if (_context.currentObject->name == dataName) _assembly.appendAssemblySize(); else @@ -214,6 +216,7 @@ map createBuiltins(langutil::EVMVersion _evmVe yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); YulString dataName = std::get(arg).value; + _assembly.setSourceLocation(_call.debugData->location); if (_context.currentObject->name == dataName) _assembly.appendConstant(0); else @@ -276,6 +279,7 @@ map createBuiltins(langutil::EVMVersion _evmVe std::function ) { yulAssert(_call.arguments.size() == 1, ""); + _assembly.setSourceLocation(_call.debugData->location); _assembly.appendImmutable(std::get(_call.arguments.front()).value.str()); } )); @@ -382,6 +386,8 @@ BuiltinFunctionForEVM const* EVMDialect::verbatimFunction(size_t _arguments, siz for (Expression const& arg: _call.arguments | ranges::views::tail | ranges::views::reverse) _visitExpression(arg); Expression const& bytecode = _call.arguments.front(); + + _assembly.setSourceLocation(_call.debugData->location); _assembly.appendVerbatim( asBytes(std::get(bytecode).value.str()), _arguments, diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index 074174532..a5d355ede 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -106,7 +107,7 @@ Object EVMToEwasmTranslator::run(Object const& _object) message += ret.toString(&WasmDialect::instance()); message += "----------------------------------\n"; for (auto const& err: errors) - message += langutil::SourceReferenceFormatter::formatErrorInformation(*err); + message += langutil::SourceReferenceFormatter::formatErrorInformation(*err, m_charStreamProvider); yulAssert(false, message); } @@ -124,7 +125,7 @@ void EVMToEwasmTranslator::parsePolyfill() { ErrorList errors; ErrorReporter errorReporter(errors); - shared_ptr scanner{make_shared(CharStream( + CharStream charStream( "{" + string(solidity::yul::wasm::polyfill::Arithmetic) + string(solidity::yul::wasm::polyfill::Bitwise) + @@ -134,13 +135,16 @@ void EVMToEwasmTranslator::parsePolyfill() string(solidity::yul::wasm::polyfill::Keccak) + string(solidity::yul::wasm::polyfill::Logical) + string(solidity::yul::wasm::polyfill::Memory) + - "}", ""))}; - m_polyfill = Parser(errorReporter, WasmDialect::instance()).parse(scanner, false); + "}", ""); + m_polyfill = Parser(errorReporter, WasmDialect::instance()).parse(charStream); if (!errors.empty()) { string message; for (auto const& err: errors) - message += langutil::SourceReferenceFormatter::formatErrorInformation(*err); + message += langutil::SourceReferenceFormatter::formatErrorInformation( + *err, + SingletonCharStreamProvider(charStream) + ); yulAssert(false, message); } diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.h b/libyul/backends/wasm/EVMToEwasmTranslator.h index 251e3352b..7f95f8d13 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.h +++ b/libyul/backends/wasm/EVMToEwasmTranslator.h @@ -25,6 +25,10 @@ #include #include +namespace solidity::langutil +{ +class CharStreamProvider; +} namespace solidity::yul { struct Object; @@ -32,13 +36,17 @@ struct Object; class EVMToEwasmTranslator: public ASTModifier { public: - EVMToEwasmTranslator(Dialect const& _evmDialect): m_dialect(_evmDialect) {} + EVMToEwasmTranslator(Dialect const& _evmDialect, langutil::CharStreamProvider const& _charStreamProvider): + m_dialect(_evmDialect), + m_charStreamProvider(_charStreamProvider) + {} Object run(Object const& _object); private: void parsePolyfill(); Dialect const& m_dialect; + langutil::CharStreamProvider const& m_charStreamProvider; std::shared_ptr m_polyfill; std::set m_polyfillFunctions; diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index 1e0e35924..667d63708 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -34,7 +34,7 @@ else BUILD_DIR="$1" fi -# solbuildpackpusher/solidity-buildpack-deps:emscripten-4 +# solbuildpackpusher/solidity-buildpack-deps:emscripten-6 docker run -v "$(pwd):/root/project" -w /root/project \ - solbuildpackpusher/solidity-buildpack-deps@sha256:434719d8104cab47712dd1f56f255994d04eb65b802c0d382790071c1a0c074b \ + solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154 \ ./scripts/ci/build_emscripten.sh "$BUILD_DIR" diff --git a/scripts/common_cmdline.sh b/scripts/common_cmdline.sh index 2637fdedb..59c334a4b 100644 --- a/scripts/common_cmdline.sh +++ b/scripts/common_cmdline.sh @@ -19,25 +19,30 @@ # (c) 2016-2019 solidity contributors. # ------------------------------------------------------------------------------ +YULARGS=(--strict-assembly) FULLARGS=(--optimize --ignore-missing --combined-json "abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc") OLDARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc") function compileFull() { local expected_exit_code=0 - local expect_output=0 - if [[ $1 = '-e' ]]; then - expected_exit_code=1 - expect_output=1 - shift; - fi - if [[ $1 = '-w' ]]; then - expect_output=1 - shift; - fi - if [[ $1 = '-o' ]]; then - expect_output=2 - shift; - fi + local expect_output='none' + + case "$1" in + '--expect-errors') + expected_exit_code=1 + expect_output='warnings-or-errors' + shift; + ;; + '--expect-warnings') + expect_output='warnings-or-errors' + shift; + ;; + '--ignore-warnings') + expect_output='any' + shift; + ;; + esac + local args=("${FULLARGS[@]}") if [[ $1 = '-v' ]]; then if (echo "$2" | grep -Po '(?<=0.4.)\d+' >/dev/null); then @@ -53,27 +58,48 @@ function compileFull() local stderr_path; stderr_path=$(mktemp) + if [ "${files: -4}" == ".yul" ] + then + args=("${YULARGS[@]}") + fi + set +e "$SOLC" "${args[@]}" "${files[@]}" >/dev/null 2>"$stderr_path" local exit_code=$? - local errors; errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path") + local errors; errors=$(grep -v -E \ + -e 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\| ' \ + -e 'Warning: Yul is still experimental. Please use the output with care.' \ + -e '^No text representation found.$' < "$stderr_path" + ) + set -e rm "$stderr_path" - if [[ \ - ("$exit_code" -ne "$expected_exit_code" || \ - ( $expect_output -eq 0 && -n "$errors" ) || \ - ( $expect_output -ne 0 && $expected_exit_code -eq 0 && $expect_output -ne 2 && -z "$errors" )) + if [[ + $exit_code != "$expected_exit_code" || + $errors != "" && $expect_output == 'none' || + $errors == "" && $expect_output != 'none' && $expect_output != 'any' && $expected_exit_code == 0 ]] then - printError "Unexpected compilation result:" - printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output" - printError "Was failure: $exit_code" + printError "TEST FAILURE" + printError "Actual exit code: $exit_code" + printError "Expected exit code: $expected_exit_code" + printError "==== Output ====" echo "$errors" + printError "== Output end ==" + printError "" + case "$expect_output" in + 'none') printError "No output was expected." ;; + 'warnings-or-errors') printError "Expected warnings or errors." ;; + esac + + printError "" printError "While calling:" - echo "\"$SOLC\" ${args[*]} ${files[*]}" + echo "\"$SOLC\" ${args[*]} ${files[*]}" printError "Inside directory:" - pwd + echo " $(pwd)" + printError "Input was:" + cat -- "${files[@]}" false fi } diff --git a/scripts/docker/buildpack-deps/Dockerfile.emscripten b/scripts/docker/buildpack-deps/Dockerfile.emscripten index cdd973fbf..790a29634 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.emscripten +++ b/scripts/docker/buildpack-deps/Dockerfile.emscripten @@ -29,12 +29,12 @@ # make version=2.0.12 build # FROM emscripten/emsdk:2.0.12 AS base -LABEL version="5" +LABEL version="6" ADD emscripten.jam /usr/src RUN set -ex; \ cd /usr/src; \ - git clone https://github.com/Z3Prover/z3.git -b z3-4.8.10 --depth 1 ; \ + git clone https://github.com/Z3Prover/z3.git -b z3-4.8.12 --depth 1 ; \ cd z3; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz index fbcc17aae..5e32ef9bf 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-clang as base -LABEL version="9" +LABEL version="11" ARG DEBIAN_FRONTEND=noninteractive @@ -60,7 +60,7 @@ RUN set -ex; \ # Z3 RUN set -ex; \ - git clone --depth 1 -b z3-4.8.10 https://github.com/Z3Prover/z3.git \ + git clone --depth 1 -b z3-4.8.12 https://github.com/Z3Prover/z3.git \ /usr/src/z3; \ cd /usr/src/z3; \ mkdir build; \ @@ -92,7 +92,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.8.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ @@ -104,7 +104,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.2-evmc8" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.5.0" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ @@ -146,4 +146,4 @@ RUN set -ex; \ FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin -COPY --from=libraries /usr/include /usr/include \ No newline at end of file +COPY --from=libraries /usr/include /usr/include diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 index 8013db7ff..63f09e513 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="6" +LABEL version="8" ARG DEBIAN_FRONTEND=noninteractive @@ -37,7 +37,7 @@ RUN set -ex; \ cmake ninja-build \ libboost-filesystem-dev libboost-test-dev libboost-system-dev \ libboost-program-options-dev \ - libcvc4-dev libz3-static-dev \ + libcvc4-dev libz3-static-dev z3-static \ ; \ apt-get install -qy python3-pip python3-sphinx; \ pip3 install codecov; \ @@ -48,7 +48,7 @@ FROM base AS libraries # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.8.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ @@ -60,7 +60,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.2-evmc8" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.5.0" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ @@ -73,4 +73,3 @@ FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin COPY --from=libraries /usr/include /usr/include - diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang index 0d816d5d7..1f96e41d5 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="6" +LABEL version="8" ARG DEBIAN_FRONTEND=noninteractive @@ -50,7 +50,7 @@ ENV CXX clang++ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.8.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ @@ -62,7 +62,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.2-evmc8" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.5.0" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ @@ -75,4 +75,3 @@ FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin COPY --from=libraries /usr/include /usr/include - diff --git a/scripts/docs.sh b/scripts/docs.sh index e2493511b..30bb96f61 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -27,8 +27,7 @@ #------------------------------------------------------------------------------ set -e -scripts/update_robotstxt.sh cd docs -pip3 install -r requirements.txt +pip3 install -r requirements.txt --upgrade --upgrade-strategy eager sphinx-build -nW -b html -d _build/doctrees . _build/html cd .. diff --git a/scripts/docs_version_pragma_check.sh b/scripts/docs_version_pragma_check.sh index 8ffd0afb6..d16645da8 100755 --- a/scripts/docs_version_pragma_check.sh +++ b/scripts/docs_version_pragma_check.sh @@ -136,7 +136,7 @@ SOLTMPDIR=$(mktemp -d) ( set -e cd "$SOLTMPDIR" - "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ getAllAvailableVersions @@ -156,11 +156,11 @@ SOLTMPDIR=$(mktemp -d) if ( ! grep -E "This will not compile after" "$f" >/dev/null && \ grep -E "This will not compile|import \"" "$f" >/dev/null ) then - opts=(-e) + opts=(--expect-errors) fi # ignore warnings in this case - opts+=(-o) + opts+=(--ignore-warnings) findMinimalVersion "$f" if [[ "$version" == "" ]] diff --git a/scripts/error_codes.py b/scripts/error_codes.py index adb579ebb..18179b4bc 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -191,6 +191,9 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): # white list of ids which are not covered by tests white_ids = { + "9804", # Tested in test/libyul/ObjectParser.cpp. + "2674", + "6367", "3805", # "This is a pre-release compiler version, please do not use it in production." # The warning may or may not exist in a compiler build. "4591", # "There are more than 256 warnings. Ignoring the rest." @@ -224,7 +227,8 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): "1988", "2066", "3356", "3893", "3996", "4010", "4802", "5272", "5622", "7128", "7400", - "7589", "7593", "8065", "8084", "8140", + "7589", "7593", "7649", "7710", + "8065", "8084", "8140", "8312", "8592", "9134", "9609", } diff --git a/scripts/install_evmone.ps1 b/scripts/install_evmone.ps1 index ea911d3da..ace3e5751 100644 --- a/scripts/install_evmone.ps1 +++ b/scripts/install_evmone.ps1 @@ -3,7 +3,7 @@ $ErrorActionPreference = "Stop" # Needed for Invoke-WebRequest to work via CI. $progressPreference = "silentlyContinue" -Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-windows-amd64.zip" -OutFile "evmone.zip" +Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-windows-amd64.zip" -OutFile "evmone.zip" tar -xf evmone.zip "bin/evmone.dll" mkdir deps mv bin/evmone.dll deps diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 494c2a7aa..f3a121f3d 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -10,7 +10,9 @@ import sys import re import os import hashlib -from os.path import join, isfile, split +from os.path import join, isfile, basename +from argparse import ArgumentParser +from textwrap import indent, dedent def extract_test_cases(path): with open(path, encoding="utf8", errors='ignore', mode='r', newline='') as file: @@ -35,84 +37,121 @@ def extract_test_cases(path): return tests -# Contract sources are indented by 4 spaces. -# Look for `pragma solidity`, `contract`, `library` or `interface` -# and abort a line not indented properly. -def extract_docs_cases(path): - insideBlock = False - insideBlockParameters = False - pastBlockParameters = False - extractedLines = [] - tests = [] - - # Collect all snippets of indented blocks - - with open(path, mode='r', errors='ignore', encoding='utf8', newline='') as f: - lines = f.read().splitlines() - for l in lines: - if l != '': - if not insideBlock and l.startswith(' '): - # start new test - extractedLines += [''] - insideBlockParameters = False - pastBlockParameters = False - insideBlock = l.startswith(' ') - if insideBlock: - if not pastBlockParameters: - # NOTE: For simplicity this allows blank lines between block parameters even - # though Sphinx does not. This does not matter since the first non-empty line in - # a Solidity file cannot start with a colon anyway. - if not l.strip().startswith(':') and (l != '' or not insideBlockParameters): - insideBlockParameters = False - pastBlockParameters = True - else: - insideBlockParameters = True - - if not insideBlockParameters: - extractedLines[-1] += l + '\n' +def extract_solidity_docs_cases(path): + tests = extract_docs_cases(path, [".. code-block:: solidity", '::']) codeStart = "(// SPDX-License-Identifier:|pragma solidity|contract.*{|library.*{|interface.*{)" - # Filter all tests that do not contain Solidity or are indented incorrectly. - for lines in extractedLines: - if re.search(r'^\s{0,3}' + codeStart, lines, re.MULTILINE): - print("Indentation error in " + path + ":") - print(lines) - exit(1) - if re.search(r'^\s{4}' + codeStart, lines, re.MULTILINE): - tests.append(lines) + # Filter out tests that are not supposed to be compilable. + return [ + test.lstrip("\n") + for test in tests + if re.search(r'^\s{4}' + codeStart, test, re.MULTILINE) is not None + ] + +def extract_yul_docs_cases(path): + tests = extract_docs_cases(path, [".. code-block:: yul"]) + + def wrap_in_object(code): + for line in code.splitlines(): + line = line.lstrip() + if line.startswith("//"): + continue + if not line.startswith("object") and not line.startswith("{"): + return indent("{{\n{}\n}}\n\n".format(code.rstrip()), " ") + break + + return code + + return [ + wrap_in_object(test) + for test in tests + if test.strip() != "" + ] + +# Extract code examples based on the 'beginMarker' parameter +# up until we reach EOF or a line that is not empty and doesn't start with 4 +# spaces. +def extract_docs_cases(path, beginMarkers): + immediatelyAfterMarker = False + insideBlock = False + tests = [] + + # Collect all snippets of indented blocks + with open(path, mode='r', errors='ignore', encoding='utf8', newline='') as f: + lines = f.read().splitlines() + + for line in lines: + if insideBlock: + if immediatelyAfterMarker: + # Skip Sphinx instructions and empty lines between them + if line == '' or line.lstrip().startswith(":"): + continue + + if line == '' or line.startswith(" "): + tests[-1] += line + "\n" + immediatelyAfterMarker = False + continue + + insideBlock = False + if any(map(line.lower().startswith, beginMarkers)): + insideBlock = True + immediatelyAfterMarker = True + tests += [''] return tests -def write_cases(f, tests): +def write_cases(f, solidityTests, yulTests): cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower() - for test in tests: + for language, test in [("sol", t) for t in solidityTests] + [("yul", t) for t in yulTests]: # When code examples are extracted they are indented by 8 spaces, which violates the style guide, # so before checking remove 4 spaces from each line. - remainder = re.sub(r'^ {4}', '', test, 0, re.MULTILINE) - sol_filename = 'test_%s_%s.sol' % (hashlib.sha256(test.encode("utf-8")).hexdigest(), cleaned_filename) + remainder = dedent(test) + sol_filename = 'test_%s_%s.%s' % (hashlib.sha256(test.encode("utf-8")).hexdigest(), cleaned_filename, language) with open(sol_filename, mode='w', encoding='utf8', newline='') as fi: fi.write(remainder) -def extract_and_write(f, path): - if docs: - cases = extract_docs_cases(path) +def extract_and_write(path, language): + assert language in ["solidity", "yul", ""] + yulCases = [] + cases = [] + + if path.lower().endswith('.rst'): + if language in ("solidity", ""): + cases = extract_solidity_docs_cases(path) + + if language in ("yul", ""): + yulCases = extract_yul_docs_cases(path) + elif path.endswith('.sol'): + if language in ("solidity", ""): + with open(path, mode='r', encoding='utf8', newline='') as f: + cases = [f.read()] else: - if f.endswith('.sol'): - with open(path, mode='r', encoding='utf8', newline='') as _f: - cases = [_f.read()] - else: - cases = extract_test_cases(path) - write_cases(f, cases) + cases = extract_test_cases(path) + + write_cases(basename(path), cases, yulCases) if __name__ == '__main__': - path = sys.argv[1] - docs = False - if len(sys.argv) > 2 and sys.argv[2] == 'docs': - docs = True + script_description = ( + "Reads Solidity, C++ or RST source files and extracts compilable solidity and yul code blocks from them. " + "Can be used to generate test cases to validade code examples. " + ) + + parser = ArgumentParser(description=script_description) + parser.add_argument(dest='path', help='Path to file or directory to look for code in.') + parser.add_argument( + '-l', '--language', + dest='language', + choices=["yul", "solidity"], + default="", + action='store', + help="Extract only code blocks in the given language" + ) + options = parser.parse_args() + path = options.path if isfile(path): - extract_and_write(path, path) + extract_and_write(path, options.language) else: for root, subdirs, files in os.walk(path): if '_build' in subdirs: @@ -120,8 +159,7 @@ if __name__ == '__main__': if 'compilationTests' in subdirs: subdirs.remove('compilationTests') for f in files: - _, tail = split(f) - if tail == "invalid_utf8_sequence.sol": + if basename(f) == "invalid_utf8_sequence.sol": continue # ignore the test with broken utf-8 encoding path = join(root, f) - extract_and_write(f, path) + extract_and_write(path, options.language) diff --git a/scripts/update_robotstxt.sh b/scripts/update_robotstxt.sh deleted file mode 100755 index cd09c1433..000000000 --- a/scripts/update_robotstxt.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -#------------------------------------------------------------------------------ -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2021 solidity contributors. -#------------------------------------------------------------------------------ - -set -eu - -repo_root="$(dirname "$0")/.." -robots_template_path="${repo_root}/docs/robots.txt.template" -robots_txt_path="${repo_root}/docs/_static/robots.txt" -latest_version=$("${repo_root}/scripts/get_version.sh") - -sed -E -e "s/\{\{[[:space:]]LATEST_VERSION[[:space:]]\}\}/${latest_version}/g" "$robots_template_path" > "$robots_txt_path" diff --git a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py index 973140e51..568c61423 100755 --- a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py +++ b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py @@ -1,4 +1,7 @@ #!/usr/bin/env python2 +# +# Not actively tested or maintained. Exists in case we want to rebuild an +# ancient release. import sys import re diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 73f4381c6..fe68d4eb9 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -1,5 +1,6 @@ set(libsolcli_sources CommandLineInterface.cpp CommandLineInterface.h + CommandLineParser.cpp CommandLineParser.h ) add_library(solcli ${libsolcli_sources}) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 7c2cad4ef..9519e679d 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -24,11 +24,8 @@ #include #include "solidity/BuildInfo.h" -#include "license.h" -#include #include -#include #include #include #include @@ -40,13 +37,11 @@ #include #include -#include #include #include #include -#include #include #include @@ -67,8 +62,6 @@ #include #include -#include - #ifdef _WIN32 // windows #include #define isatty _isatty @@ -77,290 +70,93 @@ #include #endif -#include -#include #include #if !defined(STDERR_FILENO) #define STDERR_FILENO 2 #endif + using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::langutil; -namespace po = boost::program_options; - namespace solidity::frontend { -bool g_hasOutput = false; - -namespace +ostream& CommandLineInterface::sout(bool _markAsUsed) { - -std::ostream& sout() -{ - g_hasOutput = true; - return cout; + if (_markAsUsed) + m_hasOutput = true; + return m_sout; } -std::ostream& serr(bool _used = true) +ostream& CommandLineInterface::serr(bool _markAsUsed) { - if (_used) - g_hasOutput = true; - return cerr; -} - + if (_markAsUsed) + m_hasOutput = true; + return m_serr; } +#define cin #define cout #define cerr -static string const g_stdinFileNameStr = ""; +static string const g_stdinFileName = ""; static string const g_strAbi = "abi"; -static string const g_strAllowPaths = "allow-paths"; -static string const g_strBasePath = "base-path"; static string const g_strAsm = "asm"; -static string const g_strAsmJson = "asm-json"; -static string const g_strAssemble = "assemble"; static string const g_strAst = "ast"; -static string const g_strAstCompactJson = "ast-compact-json"; static string const g_strBinary = "bin"; static string const g_strBinaryRuntime = "bin-runtime"; -static string const g_strCombinedJson = "combined-json"; -static string const g_strCompactJSON = "compact-format"; static string const g_strContracts = "contracts"; -static string const g_strErrorRecovery = "error-recovery"; -static string const g_strEVM = "evm"; -static string const g_strEVMVersion = "evm-version"; -static string const g_strEwasm = "ewasm"; -static string const g_strExperimentalViaIR = "experimental-via-ir"; -static string const g_strGeneratedSources = "generated-sources"; -static string const g_strGeneratedSourcesRuntime = "generated-sources-runtime"; -static string const g_strGas = "gas"; -static string const g_strHelp = "help"; -static string const g_strImportAst = "import-ast"; -static string const g_strInputFile = "input-file"; -static string const g_strInterface = "interface"; -static string const g_strYul = "yul"; -static string const g_strYulDialect = "yul-dialect"; -static string const g_strIR = "ir"; -static string const g_strIROptimized = "ir-optimized"; -static string const g_strIPFS = "ipfs"; -static string const g_strLicense = "license"; -static string const g_strLibraries = "libraries"; -static string const g_strLink = "link"; -static string const g_strMachine = "machine"; -static string const g_strMetadata = "metadata"; -static string const g_strMetadataHash = "metadata-hash"; -static string const g_strMetadataLiteral = "metadata-literal"; -static string const g_strModelCheckerContracts = "model-checker-contracts"; -static string const g_strModelCheckerEngine = "model-checker-engine"; -static string const g_strModelCheckerTargets = "model-checker-targets"; -static string const g_strModelCheckerTimeout = "model-checker-timeout"; -static string const g_strNatspecDev = "devdoc"; -static string const g_strNatspecUser = "userdoc"; -static string const g_strNone = "none"; -static string const g_strNoOptimizeYul = "no-optimize-yul"; -static string const g_strOpcodes = "opcodes"; -static string const g_strOptimize = "optimize"; -static string const g_strOptimizeRuns = "optimize-runs"; -static string const g_strOptimizeYul = "optimize-yul"; -static string const g_strYulOptimizations = "yul-optimizations"; -static string const g_strOutputDir = "output-dir"; -static string const g_strOverwrite = "overwrite"; -static string const g_strRevertStrings = "revert-strings"; -static string const g_strStorageLayout = "storage-layout"; -static string const g_strStopAfter = "stop-after"; -static string const g_strParsing = "parsing"; - -/// Possible arguments to for --revert-strings -static set const g_revertStringsArgs -{ - revertStringsToString(RevertStrings::Default), - revertStringsToString(RevertStrings::Strip), - revertStringsToString(RevertStrings::Debug), - revertStringsToString(RevertStrings::VerboseDebug) -}; - -static string const g_strSignatureHashes = "hashes"; -static string const g_strSources = "sources"; -static string const g_strSourceList = "sourceList"; -static string const g_strSrcMap = "srcmap"; -static string const g_strSrcMapRuntime = "srcmap-runtime"; static string const g_strFunDebug = "function-debug"; static string const g_strFunDebugRuntime = "function-debug-runtime"; -static string const g_strStandardJSON = "standard-json"; -static string const g_strStrictAssembly = "strict-assembly"; -static string const g_strSwarm = "swarm"; -static string const g_strPrettyJson = "pretty-json"; +static string const g_strGeneratedSources = "generated-sources"; +static string const g_strGeneratedSourcesRuntime = "generated-sources-runtime"; +static string const g_strNatspecDev = "devdoc"; +static string const g_strNatspecUser = "userdoc"; +static string const g_strOpcodes = "opcodes"; +static string const g_strSignatureHashes = "hashes"; +static string const g_strSourceList = "sourceList"; +static string const g_strSources = "sources"; +static string const g_strSrcMap = "srcmap"; +static string const g_strSrcMapRuntime = "srcmap-runtime"; +static string const g_strStorageLayout = "storage-layout"; static string const g_strVersion = "version"; -static string const g_strIgnoreMissingFiles = "ignore-missing"; -static string const g_strColor = "color"; -static string const g_strNoColor = "no-color"; -static string const g_strErrorIds = "error-codes"; -static string const g_argAbi = g_strAbi; -static string const g_argPrettyJson = g_strPrettyJson; -static string const g_argAllowPaths = g_strAllowPaths; -static string const g_argBasePath = g_strBasePath; -static string const g_argAsm = g_strAsm; -static string const g_argAsmJson = g_strAsmJson; -static string const g_argAssemble = g_strAssemble; -static string const g_argAstCompactJson = g_strAstCompactJson; -static string const g_argBinary = g_strBinary; -static string const g_argBinaryRuntime = g_strBinaryRuntime; -static string const g_argCombinedJson = g_strCombinedJson; -static string const g_argCompactJSON = g_strCompactJSON; -static string const g_argErrorRecovery = g_strErrorRecovery; -static string const g_argGas = g_strGas; -static string const g_argHelp = g_strHelp; -static string const g_argImportAst = g_strImportAst; -static string const g_argInputFile = g_strInputFile; -static string const g_argYul = g_strYul; -static string const g_argIR = g_strIR; -static string const g_argIROptimized = g_strIROptimized; -static string const g_argEwasm = g_strEwasm; -static string const g_argExperimentalViaIR = g_strExperimentalViaIR; -static string const g_argLibraries = g_strLibraries; -static string const g_argLink = g_strLink; -static string const g_argMachine = g_strMachine; -static string const g_argMetadata = g_strMetadata; -static string const g_argMetadataHash = g_strMetadataHash; -static string const g_argMetadataLiteral = g_strMetadataLiteral; -static string const g_argModelCheckerContracts = g_strModelCheckerContracts; -static string const g_argModelCheckerEngine = g_strModelCheckerEngine; -static string const g_argModelCheckerTargets = g_strModelCheckerTargets; -static string const g_argModelCheckerTimeout = g_strModelCheckerTimeout; -static string const g_argNatspecDev = g_strNatspecDev; -static string const g_argNatspecUser = g_strNatspecUser; -static string const g_argOpcodes = g_strOpcodes; -static string const g_argOptimize = g_strOptimize; -static string const g_argOptimizeRuns = g_strOptimizeRuns; -static string const g_argOutputDir = g_strOutputDir; -static string const g_argSignatureHashes = g_strSignatureHashes; -static string const g_argStandardJSON = g_strStandardJSON; -static string const g_argStorageLayout = g_strStorageLayout; -static string const g_argStrictAssembly = g_strStrictAssembly; -static string const g_argVersion = g_strVersion; -static string const g_stdinFileName = g_stdinFileNameStr; -static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; -static string const g_argColor = g_strColor; -static string const g_argNoColor = g_strNoColor; -static string const g_argErrorIds = g_strErrorIds; - -/// Possible arguments to for --combined-json -static set const g_combinedJsonArgs +static bool needsHumanTargetedStdout(CommandLineOptions const& _options) { - g_strAbi, - g_strAsm, - g_strAst, - g_strBinary, - g_strBinaryRuntime, - g_strCompactJSON, - g_strFunDebug, - g_strFunDebugRuntime, - g_strGeneratedSources, - g_strGeneratedSourcesRuntime, - g_strInterface, - g_strMetadata, - g_strNatspecUser, - g_strNatspecDev, - g_strOpcodes, - g_strSignatureHashes, - g_strSrcMap, - g_strSrcMapRuntime, - g_strStorageLayout -}; - -/// Possible arguments to for --machine -static set const g_machineArgs -{ - g_strEVM, - g_strEwasm -}; - -/// Possible arguments to for --yul-dialect -static set const g_yulDialectArgs -{ - g_strEVM, - g_strEwasm -}; - -/// Possible arguments to for --metadata-hash -static set const g_metadataHashArgs -{ - g_strIPFS, - g_strSwarm, - g_strNone -}; - -static void version() -{ - sout() << - "solc, the solidity compiler commandline interface" << - endl << - "Version: " << - solidity::frontend::VersionString << - endl; - exit(0); -} - -static void license() -{ - sout() << otherLicenses << endl; - // This is a static variable generated by cmake from LICENSE.txt - sout() << licenseText << endl; - exit(0); -} - -static bool needsHumanTargetedStdout(po::variables_map const& _args) -{ - if (_args.count(g_argGas)) + if (_options.compiler.estimateGas) return true; - if (_args.count(g_argOutputDir)) + if (!_options.output.dir.empty()) return false; - for (string const& arg: { - g_argAbi, - g_argAsm, - g_argAsmJson, - g_argBinary, - g_argBinaryRuntime, - g_argMetadata, - g_argNatspecUser, - g_argNatspecDev, - g_argOpcodes, - g_argSignatureHashes, - g_argStorageLayout - }) - if (_args.count(arg)) - return true; - return false; + return + _options.compiler.outputs.abi || + _options.compiler.outputs.asm_ || + _options.compiler.outputs.asmJson || + _options.compiler.outputs.binary || + _options.compiler.outputs.binaryRuntime || + _options.compiler.outputs.metadata || + _options.compiler.outputs.natspecUser || + _options.compiler.outputs.natspecDev || + _options.compiler.outputs.opcodes || + _options.compiler.outputs.signatureHashes || + _options.compiler.outputs.storageLayout; } -namespace +static bool coloredOutput(CommandLineOptions const& _options) { - -bool checkMutuallyExclusive(boost::program_options::variables_map const& args, std::string const& _optionA, std::string const& _optionB) -{ - if (args.count(_optionA) && args.count(_optionB)) - { - serr() << "Option " << _optionA << " and " << _optionB << " are mutually exclusive." << endl; - return false; - } - - return true; -} - + return + (!_options.formatting.coloredOutput.has_value() && isatty(STDERR_FILENO)) || + (_options.formatting.coloredOutput.has_value() && _options.formatting.coloredOutput.value()); } void CommandLineInterface::handleBinary(string const& _contract) { - if (m_args.count(g_argBinary)) + if (m_options.compiler.outputs.binary) { - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); else { @@ -368,9 +164,9 @@ void CommandLineInterface::handleBinary(string const& _contract) sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; } } - if (m_args.count(g_argBinaryRuntime)) + if (m_options.compiler.outputs.binaryRuntime) { - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); else { @@ -382,7 +178,7 @@ void CommandLineInterface::handleBinary(string const& _contract) void CommandLineInterface::handleOpcode(string const& _contract) { - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode)); else { @@ -394,10 +190,10 @@ void CommandLineInterface::handleOpcode(string const& _contract) void CommandLineInterface::handleIR(string const& _contractName) { - if (!m_args.count(g_argIR)) + if (!m_options.compiler.outputs.ir) return; - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName)); else { @@ -408,10 +204,10 @@ void CommandLineInterface::handleIR(string const& _contractName) void CommandLineInterface::handleIROptimized(string const& _contractName) { - if (!m_args.count(g_argIROptimized)) + if (!m_options.compiler.outputs.irOptimized) return; - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contractName) + "_opt.yul", m_compiler->yulIROptimized(_contractName)); else { @@ -422,10 +218,10 @@ void CommandLineInterface::handleIROptimized(string const& _contractName) void CommandLineInterface::handleEwasm(string const& _contractName) { - if (!m_args.count(g_argEwasm)) + if (!m_options.compiler.outputs.ewasm) return; - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) { createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->ewasm(_contractName)); createFile( @@ -443,15 +239,15 @@ void CommandLineInterface::handleEwasm(string const& _contractName) void CommandLineInterface::handleBytecode(string const& _contract) { - if (m_args.count(g_argOpcodes)) + if (m_options.compiler.outputs.opcodes) handleOpcode(_contract); - if (m_args.count(g_argBinary) || m_args.count(g_argBinaryRuntime)) + if (m_options.compiler.outputs.binary || m_options.compiler.outputs.binaryRuntime) handleBinary(_contract); } void CommandLineInterface::handleSignatureHashes(string const& _contract) { - if (!m_args.count(g_argSignatureHashes)) + if (!m_options.compiler.outputs.signatureHashes) return; Json::Value methodIdentifiers = m_compiler->methodIdentifiers(_contract); @@ -459,7 +255,7 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) for (auto const& name: methodIdentifiers.getMemberNames()) out += methodIdentifiers[name].asString() + ": " + name + "\n"; - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out); else sout() << "Function signatures:" << endl << out; @@ -467,11 +263,11 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) void CommandLineInterface::handleMetadata(string const& _contract) { - if (!m_args.count(g_argMetadata)) + if (!m_options.compiler.outputs.metadata) return; string data = m_compiler->metadata(_contract); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data); else sout() << "Metadata:" << endl << data << endl; @@ -479,11 +275,11 @@ void CommandLineInterface::handleMetadata(string const& _contract) void CommandLineInterface::handleABI(string const& _contract) { - if (!m_args.count(g_argAbi)) + if (!m_options.compiler.outputs.abi) return; string data = jsonCompactPrint(removeNullMembers(m_compiler->contractABI(_contract))); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); else sout() << "Contract JSON ABI" << endl << data << endl; @@ -491,11 +287,11 @@ void CommandLineInterface::handleABI(string const& _contract) void CommandLineInterface::handleStorageLayout(string const& _contract) { - if (!m_args.count(g_argStorageLayout)) + if (!m_options.compiler.outputs.storageLayout) return; string data = jsonCompactPrint(removeNullMembers(m_compiler->storageLayout(_contract))); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); else sout() << "Contract Storage Layout:" << endl << data << endl; @@ -503,24 +299,24 @@ void CommandLineInterface::handleStorageLayout(string const& _contract) void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { - std::string argName; + bool enabled = false; std::string suffix; std::string title; if (_natspecDev) { - argName = g_argNatspecDev; + enabled = m_options.compiler.outputs.natspecDev; suffix = ".docdev"; title = "Developer Documentation"; } else { - argName = g_argNatspecUser; + enabled = m_options.compiler.outputs.natspecUser; suffix = ".docuser"; title = "User Documentation"; } - if (m_args.count(argName)) + if (enabled) { std::string output = jsonPrettyPrint( removeNullMembers( @@ -530,7 +326,7 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra ) ); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output); else { @@ -581,175 +377,92 @@ void CommandLineInterface::handleGasEstimation(string const& _contract) } } -bool CommandLineInterface::readInputFilesAndConfigureRemappings() +bool CommandLineInterface::readInputFiles() { - bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); - bool addStdin = false; - if (m_args.count(g_argInputFile)) - for (string path: m_args[g_argInputFile].as>()) + solAssert(!m_standardJsonInput.has_value(), ""); + + m_fileReader.setBasePath(m_options.input.basePath); + + if (m_fileReader.basePath() != "") + { + if (!boost::filesystem::exists(m_fileReader.basePath())) { - auto eq = find(path.begin(), path.end(), '='); - if (eq != path.end()) - { - if (auto r = ImportRemapper::parseRemapping(path)) - m_remappings.emplace_back(std::move(*r)); - else - { - serr() << "Invalid remapping: \"" << path << "\"." << endl; - return false; - } - - string remappingTarget(eq + 1, path.end()); - m_fileReader.allowDirectory(boost::filesystem::path(remappingTarget).remove_filename()); - } - else if (path == "-") - addStdin = true; - else - { - auto infile = boost::filesystem::path(path); - if (!boost::filesystem::exists(infile)) - { - if (!ignoreMissing) - { - serr() << infile << " is not found." << endl; - return false; - } - else - serr() << infile << " is not found. Skipping." << endl; - - continue; - } - - if (!boost::filesystem::is_regular_file(infile)) - { - if (!ignoreMissing) - { - serr() << infile << " is not a valid file." << endl; - return false; - } - else - serr() << infile << " is not a valid file. Skipping." << endl; - - continue; - } - - // NOTE: we ignore the FileNotFound exception as we manually check above - m_fileReader.setSource(infile, readFileAsString(infile.string())); - m_fileReader.allowDirectory(boost::filesystem::path(boost::filesystem::canonical(infile).string()).remove_filename()); - } + serr() << "Base path does not exist: " << m_fileReader.basePath() << endl; + return false; } - if (addStdin) - m_fileReader.setSource(g_stdinFileName, readUntilEnd(cin)); + if (!boost::filesystem::is_directory(m_fileReader.basePath())) + { + serr() << "Base path is not a directory: " << m_fileReader.basePath() << endl; + return false; + } + } - if (m_fileReader.sourceCodes().size() == 0) + for (boost::filesystem::path const& allowedDirectory: m_options.input.allowedDirectories) + m_fileReader.allowDirectory(allowedDirectory); + + for (boost::filesystem::path const& infile: m_options.input.paths) { - serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; + if (!boost::filesystem::exists(infile)) + { + if (!m_options.input.ignoreMissingFiles) + { + serr() << infile << " is not found." << endl; + return false; + } + else + serr() << infile << " is not found. Skipping." << endl; + + continue; + } + + if (!boost::filesystem::is_regular_file(infile)) + { + if (!m_options.input.ignoreMissingFiles) + { + serr() << infile << " is not a valid file." << endl; + return false; + } + else + serr() << infile << " is not a valid file. Skipping." << endl; + + continue; + } + + // NOTE: we ignore the FileNotFound exception as we manually check above + string fileContent = readFileAsString(infile.string()); + if (m_options.input.mode == InputMode::StandardJson) + { + solAssert(!m_standardJsonInput.has_value(), ""); + m_standardJsonInput = move(fileContent); + } + else + { + m_fileReader.setSource(infile, move(fileContent)); + m_fileReader.allowDirectory(boost::filesystem::canonical(infile).remove_filename()); + } + } + + if (m_options.input.addStdin) + { + if (m_options.input.mode == InputMode::StandardJson) + { + solAssert(!m_standardJsonInput.has_value(), ""); + m_standardJsonInput = readUntilEnd(m_sin); + } + else + m_fileReader.setSource(g_stdinFileName, readUntilEnd(m_sin)); + } + + if (m_fileReader.sourceCodes().empty() && !m_standardJsonInput.has_value()) + { + serr() << "All specified input files either do not exist or are not regular files." << endl; return false; } return true; } -bool CommandLineInterface::parseLibraryOption(string const& _input) -{ - namespace fs = boost::filesystem; - string data = _input; - try - { - if (fs::is_regular_file(_input)) - data = readFileAsString(_input); - } - catch (fs::filesystem_error const&) - { - // Thrown e.g. if path is too long. - } - catch (FileNotFound const&) - { - // Should not happen if `fs::is_regular_file` is correct. - } - catch (NotAFile const&) - { - // Should not happen if `fs::is_regular_file` is correct. - } - - vector libraries; - boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on); - for (string const& lib: libraries) - if (!lib.empty()) - { - //search for equal sign or last colon in string as our binaries output placeholders in the form of file=Name or file:Name - //so we need to search for `=` or `:` in the string - auto separator = lib.rfind('='); - bool isSeparatorEqualSign = true; - if (separator == string::npos) - { - separator = lib.rfind(':'); - if (separator == string::npos) - { - serr() << "Equal sign separator missing in library address specifier \"" << lib << "\"" << endl; - return false; - } - else - isSeparatorEqualSign = false; // separator is colon - } - else - if (lib.rfind('=') != lib.find('=')) - { - serr() << "Only one equal sign \"=\" is allowed in the address string \"" << lib << "\"." << endl; - return false; - } - - string libName(lib.begin(), lib.begin() + static_cast(separator)); - boost::trim(libName); - if (m_libraries.count(libName)) - { - serr() << "Address specified more than once for library \"" << libName << "\"." << endl; - return false; - } - - string addrString(lib.begin() + static_cast(separator) + 1, lib.end()); - boost::trim(addrString); - if (addrString.empty()) - { - serr() << "Empty address provided for library \"" << libName << "\"." << endl; - serr() << "Note that there should not be any whitespace after the " << (isSeparatorEqualSign ? "equal sign" : "colon") << "." << endl; - return false; - } - - if (addrString.substr(0, 2) == "0x") - addrString = addrString.substr(2); - else - { - serr() << "The address " << addrString << " is not prefixed with \"0x\"." << endl; - serr() << "Note that the address must be prefixed with \"0x\"." << endl; - return false; - } - - if (addrString.length() != 40) - { - serr() << "Invalid length for address for library \"" << libName << "\": " << addrString.length() << " instead of 40 characters." << endl; - return false; - } - if (!passesAddressChecksum(addrString, false)) - { - serr() << "Invalid checksum on address for library \"" << libName << "\": " << addrString << endl; - serr() << "The correct checksum is " << getChecksummedAddress(addrString) << endl; - return false; - } - bytes binAddr = fromHex(addrString); - h160 address(binAddr, h160::AlignRight); - if (binAddr.size() > 20 || address == h160()) - { - serr() << "Invalid address for library \"" << libName << "\": " << addrString << endl; - return false; - } - m_libraries[libName] = address; - } - - return true; -} - map CommandLineInterface::parseAstFromInput() { map sourceJsons; @@ -782,17 +495,17 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da { namespace fs = boost::filesystem; - fs::path outputDir(m_args.at(g_argOutputDir).as()); + solAssert(!m_options.output.dir.empty(), ""); // NOTE: create_directories() raises an exception if the path consists solely of '.' or '..' // (or equivalent such as './././.'). Paths like 'a/b/.' and 'a/b/..' are fine though. // The simplest workaround is to use an absolute path. - fs::create_directories(fs::absolute(outputDir)); + fs::create_directories(fs::absolute(m_options.output.dir)); - string pathName = (outputDir / _fileName).string(); - if (fs::exists(pathName) && !m_args.count(g_strOverwrite)) + string pathName = (m_options.output.dir / _fileName).string(); + if (fs::exists(pathName) && !m_options.output.overwriteFiles) { - serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl; + serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl; m_error = true; return; } @@ -811,734 +524,89 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js createFile(boost::filesystem::basename(_fileName) + string(".json"), _json); } -bool CommandLineInterface::parseArguments(int _argc, char** _argv) +bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv) { - g_hasOutput = false; - - // Declare the supported options. - po::options_description desc((R"(solc, the Solidity commandline compiler. - -This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you -are welcome to redistribute it under certain conditions. See 'solc --)" + g_strLicense + R"(' -for details. - -Usage: solc [options] [input_file...] -Compiles the given Solidity input files (or the standard input if none given or -"-" is used as a file name) and outputs the components specified in the options -at standard output or in files in the output directory, if specified. -Imports are automatically read from the filesystem, but it is also possible to -remap paths using the context:prefix=path syntax. -Example: -solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol - -General Information)").c_str(), - po::options_description::m_default_line_length, - po::options_description::m_default_line_length - 23 - ); - desc.add_options() - (g_argHelp.c_str(), "Show help message and exit.") - (g_argVersion.c_str(), "Show version and exit.") - (g_strLicense.c_str(), "Show licensing information and exit.") - ; - - po::options_description inputOptions("Input Options"); - inputOptions.add_options() - ( - g_argBasePath.c_str(), - po::value()->value_name("path"), - "Use the given path as the root of the source tree instead of the root of the filesystem." - ) - ( - g_argAllowPaths.c_str(), - po::value()->value_name("path(s)"), - "Allow a given path for imports. A list of paths can be supplied by separating them with a comma." - ) - ( - g_argIgnoreMissingFiles.c_str(), - "Ignore missing files." - ) - ( - g_argErrorRecovery.c_str(), - "Enables additional parser error recovery." - ) - ; - desc.add(inputOptions); - - po::options_description outputOptions("Output Options"); - outputOptions.add_options() - ( - (g_argOutputDir + ",o").c_str(), - po::value()->value_name("path"), - "If given, creates one file per component and contract/file at the specified directory." - ) - ( - g_strOverwrite.c_str(), - "Overwrite existing files (used together with -o)." - ) - ( - g_strEVMVersion.c_str(), - po::value()->value_name("version")->default_value(EVMVersion{}.name()), - "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " - "byzantium, constantinople, petersburg, istanbul or berlin." - ) - ( - g_strExperimentalViaIR.c_str(), - "Turn on experimental compilation mode via the IR (EXPERIMENTAL)." - ) - ( - g_strRevertStrings.c_str(), - po::value()->value_name(boost::join(g_revertStringsArgs, ",")), - "Strip revert (and require) reason strings or add additional debugging information." - ) - ( - g_strStopAfter.c_str(), - po::value()->value_name("stage"), - "Stop execution after the given compiler stage. Valid options: \"parsing\"." - ) - ; - desc.add(outputOptions); - - po::options_description alternativeInputModes("Alternative Input Modes"); - alternativeInputModes.add_options() - ( - g_argStandardJSON.c_str(), - "Switch to Standard JSON input / output mode, ignoring all options. " - "It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output." - ) - ( - g_argLink.c_str(), - ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " " - "and modify binaries in place.").c_str() - ) - ( - g_argAssemble.c_str(), - ("Switch to assembly mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " - "and assumes input is assembly.").c_str() - ) - ( - g_argYul.c_str(), - ("Switch to Yul mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " - "and assumes input is Yul.").c_str() - ) - ( - g_argStrictAssembly.c_str(), - ("Switch to strict assembly mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " - "and assumes input is strict assembly.").c_str() - ) - ( - g_argImportAst.c_str(), - ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " - "Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by " - "--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str() - ) - ; - desc.add(alternativeInputModes); - - po::options_description assemblyModeOptions("Assembly Mode Options"); - assemblyModeOptions.add_options() - ( - g_argMachine.c_str(), - po::value()->value_name(boost::join(g_machineArgs, ",")), - "Target machine in assembly or Yul mode." - ) - ( - g_strYulDialect.c_str(), - po::value()->value_name(boost::join(g_yulDialectArgs, ",")), - "Input dialect to use in assembly or yul mode." - ) - ; - desc.add(assemblyModeOptions); - - po::options_description linkerModeOptions("Linker Mode Options"); - linkerModeOptions.add_options() - ( - g_argLibraries.c_str(), - po::value>()->value_name("libs"), - "Direct string or file containing library addresses. Syntax: " - "=
[, or whitespace] ...\n" - "Address is interpreted as a hex string prefixed by 0x." - ) - ; - desc.add(linkerModeOptions); - - po::options_description outputFormatting("Output Formatting"); - outputFormatting.add_options() - ( - g_argPrettyJson.c_str(), - "Output JSON in pretty format. Currently it only works with the combined JSON output." - ) - ( - g_argColor.c_str(), - "Force colored output." - ) - ( - g_argNoColor.c_str(), - "Explicitly disable colored output, disabling terminal auto-detection." - ) - ( - g_argErrorIds.c_str(), - "Output error codes." - ) - ; - desc.add(outputFormatting); - - po::options_description outputComponents("Output Components"); - outputComponents.add_options() - (g_argAstCompactJson.c_str(), "AST of all source files in a compact JSON format.") - (g_argAsm.c_str(), "EVM assembly of the contracts.") - (g_argAsmJson.c_str(), "EVM assembly of the contracts in JSON format.") - (g_argOpcodes.c_str(), "Opcodes of the contracts.") - (g_argBinary.c_str(), "Binary of the contracts in hex.") - (g_argBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") - (g_argAbi.c_str(), "ABI specification of the contracts.") - (g_argIR.c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") - (g_argIROptimized.c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") - (g_argEwasm.c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") - (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") - (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") - (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") - (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") - (g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.") - ; - desc.add(outputComponents); - - po::options_description extraOutput("Extra Output"); - extraOutput.add_options() - ( - g_argGas.c_str(), - "Print an estimate of the maximal gas usage for each function." - ) - ( - g_argCombinedJson.c_str(), - po::value()->value_name(boost::join(g_combinedJsonArgs, ",")), - "Output a single json document containing the specified information." - ) - ; - desc.add(extraOutput); - - po::options_description metadataOptions("Metadata Options"); - metadataOptions.add_options() - ( - g_argMetadataHash.c_str(), - po::value()->value_name(boost::join(g_metadataHashArgs, ",")), - "Choose hash method for the bytecode metadata or disable it." - ) - ( - g_argMetadataLiteral.c_str(), - "Store referenced sources as literal data in the metadata output." - ) - ; - desc.add(metadataOptions); - - po::options_description optimizerOptions("Optimizer Options"); - optimizerOptions.add_options() - ( - g_argOptimize.c_str(), - "Enable bytecode optimizer." - ) - ( - g_argOptimizeRuns.c_str(), - // TODO: The type in OptimiserSettings is size_t but we only accept values up to 2**32-1 - // on the CLI and in Standard JSON. We should just switch to uint32_t everywhere. - po::value()->value_name("n")->default_value(static_cast(OptimiserSettings{}.expectedExecutionsPerDeployment)), - "Set for how many contract runs to optimize. " - "Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage." - ) - ( - g_strOptimizeYul.c_str(), - ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str() - ) - ( - g_strNoOptimizeYul.c_str(), - "Disable Yul optimizer in Solidity." - ) - ( - g_strYulOptimizations.c_str(), - po::value()->value_name("steps"), - "Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one." - ) - ; - desc.add(optimizerOptions); - - po::options_description smtCheckerOptions("Model Checker Options"); - smtCheckerOptions.add_options() - ( - g_strModelCheckerContracts.c_str(), - po::value()->value_name("default,:")->default_value("default"), - "Select which contracts should be analyzed using the form :." - "Multiple pairs : can be selected at the same time, separated by a comma " - "and no spaces." - ) - ( - g_strModelCheckerEngine.c_str(), - po::value()->value_name("all,bmc,chc,none")->default_value("none"), - "Select model checker engine." - ) - ( - g_strModelCheckerTargets.c_str(), - po::value()->value_name("default,constantCondition,underflow,overflow,divByZero,balance,assert,popEmptyArray,outOfBounds")->default_value("default"), - "Select model checker verification targets. " - "Multiple targets can be selected at the same time, separated by a comma " - "and no spaces." - ) - ( - g_strModelCheckerTimeout.c_str(), - po::value()->value_name("ms"), - "Set model checker timeout per query in milliseconds. " - "The default is a deterministic resource limit. " - "A timeout of 0 means no resource/time restrictions for any query." - ) - ; - desc.add(smtCheckerOptions); - - po::options_description allOptions = desc; - allOptions.add_options()(g_argInputFile.c_str(), po::value>(), "input file"); - - // All positional options should be interpreted as input files - po::positional_options_description filesPositions; - filesPositions.add(g_argInputFile.c_str(), -1); - - // parse the compiler arguments - try - { - po::command_line_parser cmdLineParser(_argc, _argv); - cmdLineParser.style(po::command_line_style::default_style & (~po::command_line_style::allow_guessing)); - cmdLineParser.options(allOptions).positional(filesPositions); - po::store(cmdLineParser.run(), m_args); - } - catch (po::error const& _exception) - { - serr() << _exception.what() << endl; + CommandLineParser parser(sout(/* _markAsUsed */ false), serr(/* _markAsUsed */ false)); + bool success = parser.parse(_argc, _argv, isatty(fileno(stdin))); + if (!success) return false; - } - - if (!checkMutuallyExclusive(m_args, g_argColor, g_argNoColor)) - return false; - - static vector const conflictingWithStopAfter{ - g_argBinary, - g_argIR, - g_argIROptimized, - g_argEwasm, - g_argGas, - g_argAsm, - g_argAsmJson, - g_argOpcodes - }; - - for (auto& option: conflictingWithStopAfter) - if (!checkMutuallyExclusive(m_args, g_strStopAfter, option)) - return false; - - m_coloredOutput = !m_args.count(g_argNoColor) && (isatty(STDERR_FILENO) || m_args.count(g_argColor)); - - m_withErrorIds = m_args.count(g_argErrorIds); - - if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) - { - sout() << desc; - return false; - } - - if (m_args.count(g_argVersion)) - { - version(); - return false; - } - - if (m_args.count(g_strLicense)) - { - license(); - return false; - } - - if (m_args.count(g_strRevertStrings)) - { - string revertStringsString = m_args[g_strRevertStrings].as(); - std::optional revertStrings = revertStringsFromString(revertStringsString); - if (!revertStrings) - { - serr() << "Invalid option for --" << g_strRevertStrings << ": " << revertStringsString << endl; - return false; - } - if (*revertStrings == RevertStrings::VerboseDebug) - { - serr() << "Only \"default\", \"strip\" and \"debug\" are implemented for --" << g_strRevertStrings << " for now." << endl; - return false; - } - m_revertStrings = *revertStrings; - } - - if (m_args.count(g_argCombinedJson)) - { - vector requests; - for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(","))) - if (!g_combinedJsonArgs.count(item)) - { - serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl; - return false; - } - } - po::notify(m_args); + m_hasOutput = m_hasOutput || parser.hasOutput(); + m_options = parser.options(); return true; } bool CommandLineInterface::processInput() { - if (m_args.count(g_argBasePath)) + switch (m_options.input.mode) { - boost::filesystem::path const fspath{m_args[g_argBasePath].as()}; - if (!boost::filesystem::is_directory(fspath)) - { - serr() << "Base path must be a directory: \"" << fspath << "\"\n"; - return false; - } - m_fileReader.setBasePath(fspath); - } + case InputMode::StandardJson: + { + solAssert(m_standardJsonInput.has_value(), ""); - if (m_args.count(g_argAllowPaths)) - { - vector paths; - for (string const& path: boost::split(paths, m_args[g_argAllowPaths].as(), boost::is_any_of(","))) - { - auto filesystem_path = boost::filesystem::path(path); - // If the given path had a trailing slash, the Boost filesystem - // path will have it's last component set to '.'. This breaks - // path comparison in later parts of the code, so we need to strip - // it. - if (filesystem_path.filename() == ".") - filesystem_path.remove_filename(); - m_fileReader.allowDirectory(filesystem_path); - } - } - - if (m_args.count(g_strStopAfter)) - { - if (m_args[g_strStopAfter].as() != "parsing") - { - serr() << "Valid options for --" << g_strStopAfter << " are: \"parsing\".\n"; - return false; - } - else - m_stopAfter = CompilerStack::State::Parsed; - } - - vector const exclusiveModes = { - g_argStandardJSON, - g_argLink, - g_argAssemble, - g_argStrictAssembly, - g_argYul, - g_argImportAst, - }; - if (countEnabledOptions(exclusiveModes) > 1) - { - serr() << "The following options are mutually exclusive: " << joinOptionNames(exclusiveModes) << ". "; - serr() << "Select at most one." << endl; - return false; - } - - if (m_args.count(g_argStandardJSON)) - { - vector inputFiles; - string jsonFile; - if (m_args.count(g_argInputFile)) - inputFiles = m_args[g_argInputFile].as>(); - if (inputFiles.size() == 1) - jsonFile = inputFiles[0]; - else if (inputFiles.size() > 1) - { - serr() << "If --" << g_argStandardJSON << " is used, only zero or one input files are supported." << endl; - return false; - } - string input; - if (jsonFile.empty()) - input = readUntilEnd(cin); - else - { - try - { - input = readFileAsString(jsonFile); - } - catch (FileNotFound const&) - { - serr() << "File not found: " << jsonFile << endl; - return false; - } - catch (NotAFile const&) - { - serr() << "Not a regular file: " << jsonFile << endl; - return false; - } - } - StandardCompiler compiler(m_fileReader.reader()); - sout() << compiler.compile(std::move(input)) << endl; + StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json); + sout() << compiler.compile(move(m_standardJsonInput.value())) << endl; + m_standardJsonInput.reset(); return true; } - - if (!readInputFilesAndConfigureRemappings()) - return false; - - if (m_args.count(g_argLibraries)) - for (string const& library: m_args[g_argLibraries].as>()) - if (!parseLibraryOption(library)) - return false; - - if (m_args.count(g_strEVMVersion)) + case InputMode::Assembler: { - string versionOptionStr = m_args[g_strEVMVersion].as(); - std::optional versionOption = langutil::EVMVersion::fromString(versionOptionStr); - if (!versionOption) - { - serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl; - return false; - } - m_evmVersion = *versionOption; + return assemble( + m_options.assembly.inputLanguage, + m_options.assembly.targetMachine, + m_options.optimizer.enabled, + m_options.optimizer.expectedExecutionsPerDeployment, + m_options.optimizer.yulSteps + ); } - - if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argYul)) - { - vector const nonAssemblyModeOptions = { - // TODO: The list is not complete. Add more. - g_argOutputDir, - g_argGas, - g_argCombinedJson, - g_strOptimizeYul, - g_strNoOptimizeYul, - }; - if (countEnabledOptions(nonAssemblyModeOptions) >= 1) - { - auto optionEnabled = [&](string const& name){ return m_args.count(name) > 0; }; - auto enabledOptions = boost::copy_range>(nonAssemblyModeOptions | boost::adaptors::filtered(optionEnabled)); - - serr() << "The following options are invalid in assembly mode: "; - serr() << joinOptionNames(enabledOptions) << "."; - if (m_args.count(g_strOptimizeYul) || m_args.count(g_strNoOptimizeYul)) - serr() << " Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; - serr() << endl; - return false; - } - - // switch to assembly mode - m_onlyAssemble = true; - using Input = yul::AssemblyStack::Language; - using Machine = yul::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); - - optional yulOptimiserSteps; - if (m_args.count(g_strYulOptimizations)) - { - if (!optimize) - { - serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; - return false; - } - - try - { - yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); - } - catch (yul::OptimizerException const& _exception) - { - serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; - return false; - } - - yulOptimiserSteps = m_args[g_strYulOptimizations].as(); - } - - if (m_args.count(g_argMachine)) - { - string machine = m_args[g_argMachine].as(); - if (machine == g_strEVM) - targetMachine = Machine::EVM; - else if (machine == g_strEwasm) - targetMachine = Machine::Ewasm; - else - { - serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl; - return false; - } - } - if (targetMachine == Machine::Ewasm && inputLanguage == Input::StrictAssembly) - inputLanguage = Input::Ewasm; - if (m_args.count(g_strYulDialect)) - { - string dialect = m_args[g_strYulDialect].as(); - if (dialect == g_strEVM) - inputLanguage = Input::StrictAssembly; - else if (dialect == g_strEwasm) - { - inputLanguage = Input::Ewasm; - if (targetMachine != Machine::Ewasm) - { - serr() << "If you select Ewasm as --" << g_strYulDialect << ", "; - serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl; - return false; - } - } - else - { - serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl; - return false; - } - } - if (optimize && (inputLanguage != Input::StrictAssembly && inputLanguage != Input::Ewasm)) - { - serr() << - "Optimizer can only be used for strict assembly. Use --" << - g_strStrictAssembly << - "." << - endl; - return false; - } - if (targetMachine == Machine::Ewasm && inputLanguage != Input::StrictAssembly && inputLanguage != Input::Ewasm) - { - serr() << "The selected input language is not directly supported when targeting the Ewasm machine "; - serr() << "and automatic translation is not available." << endl; - return false; - } - serr() << - "Warning: Yul is still experimental. Please use the output with care." << - endl; - - return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps); - } - else if (countEnabledOptions({g_strYulDialect, g_argMachine}) >= 1) - { - serr() << "--" << g_strYulDialect << " and --" << g_argMachine << " "; - serr() << "are only valid in assembly mode." << endl; - return false; - } - - if (m_args.count(g_argLink)) - { - // switch to linker mode - m_onlyLink = true; + case InputMode::Linker: return link(); + case InputMode::Compiler: + case InputMode::CompilerWithASTImport: + return compile(); } - if (m_args.count(g_argMetadataHash)) - { - string hashStr = m_args[g_argMetadataHash].as(); - if (hashStr == g_strIPFS) - m_metadataHash = CompilerStack::MetadataHash::IPFS; - else if (hashStr == g_strSwarm) - m_metadataHash = CompilerStack::MetadataHash::Bzzr1; - else if (hashStr == g_strNone) - m_metadataHash = CompilerStack::MetadataHash::None; - else - { - serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl; - return false; - } - } + solAssert(false, ""); + return false; +} - if (m_args.count(g_argModelCheckerContracts)) - { - string contractsStr = m_args[g_argModelCheckerContracts].as(); - optional contracts = ModelCheckerContracts::fromString(contractsStr); - if (!contracts) - { - serr() << "Invalid option for --" << g_argModelCheckerContracts << ": " << contractsStr << endl; - return false; - } - m_modelCheckerSettings.contracts = move(*contracts); - } - - - if (m_args.count(g_argModelCheckerEngine)) - { - string engineStr = m_args[g_argModelCheckerEngine].as(); - optional engine = ModelCheckerEngine::fromString(engineStr); - if (!engine) - { - serr() << "Invalid option for --" << g_argModelCheckerEngine << ": " << engineStr << endl; - return false; - } - m_modelCheckerSettings.engine = *engine; - } - - if (m_args.count(g_argModelCheckerTargets)) - { - string targetsStr = m_args[g_argModelCheckerTargets].as(); - optional targets = ModelCheckerTargets::fromString(targetsStr); - if (!targets) - { - serr() << "Invalid option for --" << g_argModelCheckerTargets << ": " << targetsStr << endl; - return false; - } - m_modelCheckerSettings.targets = *targets; - } - - if (m_args.count(g_argModelCheckerTimeout)) - m_modelCheckerSettings.timeout = m_args[g_argModelCheckerTimeout].as(); +bool CommandLineInterface::compile() +{ + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); m_compiler = make_unique(m_fileReader.reader()); - SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); + SourceReferenceFormatter formatter(serr(false), *m_compiler, coloredOutput(m_options), m_options.formatting.withErrorIds); try { - if (m_args.count(g_argMetadataLiteral) > 0) + if (m_options.metadata.literalSources) m_compiler->useMetadataLiteralSources(true); - if (m_args.count(g_argMetadataHash)) - m_compiler->setMetadataHash(m_metadataHash); - if ( - m_args.count(g_argModelCheckerContracts) || - m_args.count(g_argModelCheckerEngine) || - m_args.count(g_argModelCheckerTargets) || - m_args.count(g_argModelCheckerTimeout) - ) - m_compiler->setModelCheckerSettings(m_modelCheckerSettings); - if (m_args.count(g_argInputFile)) - m_compiler->setRemappings(m_remappings); - - if (m_args.count(g_argLibraries)) - m_compiler->setLibraries(m_libraries); - if (m_args.count(g_argExperimentalViaIR)) - m_compiler->setViaIR(true); - m_compiler->setEVMVersion(m_evmVersion); - m_compiler->setRevertStringBehaviour(m_revertStrings); + m_compiler->setMetadataHash(m_options.metadata.hash); + if (m_options.modelChecker.initialize) + m_compiler->setModelCheckerSettings(m_options.modelChecker.settings); + m_compiler->setRemappings(m_options.input.remappings); + m_compiler->setLibraries(m_options.linker.libraries); + m_compiler->setViaIR(m_options.output.experimentalViaIR); + m_compiler->setEVMVersion(m_options.output.evmVersion); + m_compiler->setRevertStringBehaviour(m_options.output.revertStrings); // TODO: Perhaps we should not compile unless requested - m_compiler->enableIRGeneration(m_args.count(g_argIR) || m_args.count(g_argIROptimized)); - m_compiler->enableEwasmGeneration(m_args.count(g_argEwasm)); + m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized); + m_compiler->enableEwasmGeneration(m_options.compiler.outputs.ewasm); - OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::standard() : OptimiserSettings::minimal(); - settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as(); - if (m_args.count(g_strNoOptimizeYul)) + OptimiserSettings settings = m_options.optimizer.enabled ? OptimiserSettings::standard() : OptimiserSettings::minimal(); + if (m_options.optimizer.expectedExecutionsPerDeployment.has_value()) + settings.expectedExecutionsPerDeployment = m_options.optimizer.expectedExecutionsPerDeployment.value(); + if (m_options.optimizer.noOptimizeYul) settings.runYulOptimiser = false; - if (m_args.count(g_strYulOptimizations)) - { - if (!settings.runYulOptimiser) - { - serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; - return false; - } - try - { - yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); - } - catch (yul::OptimizerException const& _exception) - { - serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; - return false; - } - - settings.yulOptimiserSteps = m_args[g_strYulOptimizations].as(); - } + if (m_options.optimizer.yulSteps.has_value()) + settings.yulOptimiserSteps = m_options.optimizer.yulSteps.value(); settings.optimizeStackAllocation = settings.runYulOptimiser; m_compiler->setOptimiserSettings(settings); - if (m_args.count(g_argImportAst)) + if (m_options.input.mode == InputMode::CompilerWithASTImport) { try { @@ -1546,8 +614,7 @@ bool CommandLineInterface::processInput() if (!m_compiler->analyze()) { - for (auto const& error: m_compiler->errors()) - formatter.printErrorInformation(*error); + formatter.printErrorInformation(m_compiler->errors()); astAssert(false, "Analysis of the AST failed"); } } @@ -1560,29 +627,23 @@ bool CommandLineInterface::processInput() else { m_compiler->setSources(m_fileReader.sourceCodes()); - if (m_args.count(g_argErrorRecovery)) - m_compiler->setParserErrorRecovery(true); + m_compiler->setParserErrorRecovery(m_options.input.errorRecovery); } - bool successful = m_compiler->compile(m_stopAfter); + bool successful = m_compiler->compile(m_options.output.stopAfter); for (auto const& error: m_compiler->errors()) { - g_hasOutput = true; + m_hasOutput = true; formatter.printErrorInformation(*error); } if (!successful) - { - if (m_args.count(g_argErrorRecovery)) - return true; - else - return false; - } + return m_options.input.errorRecovery; } catch (CompilerError const& _exception) { - g_hasOutput = true; + m_hasOutput = true; formatter.printExceptionInformation(_exception, "Compiler error"); return false; } @@ -1616,7 +677,7 @@ bool CommandLineInterface::processInput() serr() << "Documentation parsing error: " << *boost::get_error_info(_error) << endl; else { - g_hasOutput = true; + m_hasOutput = true; formatter.printExceptionInformation(_error, _error.typeName()); } @@ -1645,14 +706,12 @@ bool CommandLineInterface::processInput() void CommandLineInterface::handleCombinedJSON() { - if (!m_args.count(g_argCombinedJson)) + if (!m_options.compiler.combinedJsonRequests.has_value()) return; Json::Value output(Json::objectValue); output[g_strVersion] = frontend::VersionString; - set requests; - boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(",")); vector contracts = m_compiler->contractNames(); if (!contracts.empty()) @@ -1660,51 +719,54 @@ void CommandLineInterface::handleCombinedJSON() for (string const& contractName: contracts) { Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; - if (requests.count(g_strAbi)) + if (m_options.compiler.combinedJsonRequests->abi) contractData[g_strAbi] = m_compiler->contractABI(contractName); - if (requests.count("metadata")) + if (m_options.compiler.combinedJsonRequests->metadata) contractData["metadata"] = m_compiler->metadata(contractName); - if (requests.count(g_strBinary) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->binary && m_compiler->compilationSuccessful()) contractData[g_strBinary] = m_compiler->object(contractName).toHex(); - if (requests.count(g_strBinaryRuntime) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->binaryRuntime && m_compiler->compilationSuccessful()) contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); - if (requests.count(g_strOpcodes) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->opcodes && m_compiler->compilationSuccessful()) contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); - if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->asm_ && m_compiler->compilationSuccessful()) contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); - if (requests.count(g_strStorageLayout) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->storageLayout && m_compiler->compilationSuccessful()) contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName); - if (requests.count(g_strGeneratedSources) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->generatedSources && m_compiler->compilationSuccessful()) contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false); - if (requests.count(g_strGeneratedSourcesRuntime) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->generatedSourcesRuntime && m_compiler->compilationSuccessful()) contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true); - if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->srcMap && m_compiler->compilationSuccessful()) { auto map = m_compiler->sourceMapping(contractName); contractData[g_strSrcMap] = map ? *map : ""; } - if (requests.count(g_strSrcMapRuntime) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->srcMapRuntime && m_compiler->compilationSuccessful()) { auto map = m_compiler->runtimeSourceMapping(contractName); contractData[g_strSrcMapRuntime] = map ? *map : ""; } - if (requests.count(g_strFunDebug) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->funDebug && m_compiler->compilationSuccessful()) contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData( m_compiler->object(contractName).functionDebugData ); - if (requests.count(g_strFunDebugRuntime) && m_compiler->compilationSuccessful()) + if (m_options.compiler.combinedJsonRequests->funDebugRuntime && m_compiler->compilationSuccessful()) contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData( m_compiler->runtimeObject(contractName).functionDebugData ); - if (requests.count(g_strSignatureHashes)) + if (m_options.compiler.combinedJsonRequests->signatureHashes) contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName); - if (requests.count(g_strNatspecDev)) + if (m_options.compiler.combinedJsonRequests->natspecDev) contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName); - if (requests.count(g_strNatspecUser)) + if (m_options.compiler.combinedJsonRequests->natspecUser) contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName); } - bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); + bool needsSourceList = + m_options.compiler.combinedJsonRequests->ast || + m_options.compiler.combinedJsonRequests->srcMap || + m_options.compiler.combinedJsonRequests->srcMapRuntime; if (needsSourceList) { // Indices into this array are used to abbreviate source names in source locations. @@ -1714,7 +776,7 @@ void CommandLineInterface::handleCombinedJSON() output[g_strSourceList].append(source); } - if (requests.count(g_strAst)) + if (m_options.compiler.combinedJsonRequests->ast) { output[g_strSources] = Json::Value(Json::objectValue); for (auto const& sourceCode: m_fileReader.sourceCodes()) @@ -1725,10 +787,8 @@ void CommandLineInterface::handleCombinedJSON() } } - string json = m_args.count(g_argPrettyJson) ? jsonPrettyPrint(removeNullMembers(std::move(output))) : - jsonCompactPrint(removeNullMembers(std::move(output))); - - if (m_args.count(g_argOutputDir)) + string json = jsonPrint(removeNullMembers(std::move(output)), m_options.formatting.json); + if (!m_options.output.dir.empty()) createJson("combined", json); else sout() << json << endl; @@ -1736,14 +796,14 @@ void CommandLineInterface::handleCombinedJSON() void CommandLineInterface::handleAst() { - if (!m_args.count(g_argAstCompactJson)) + if (!m_options.compiler.outputs.astCompactJson) return; vector asts; for (auto const& sourceCode: m_fileReader.sourceCodes()) asts.push_back(&m_compiler->ast(sourceCode.first)); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) { for (auto const& sourceCode: m_fileReader.sourceCodes()) { @@ -1768,13 +828,16 @@ void CommandLineInterface::handleAst() bool CommandLineInterface::actOnInput() { - if (m_args.count(g_argStandardJSON) || m_onlyAssemble) + if (m_options.input.mode == InputMode::StandardJson || m_options.input.mode == InputMode::Assembler) // Already done in "processInput" phase. return true; - else if (m_onlyLink) + else if (m_options.input.mode == InputMode::Linker) writeLinkedFiles(); else + { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); outputCompilationResults(); + } return !m_error; } @@ -1783,7 +846,7 @@ bool CommandLineInterface::link() // Map from how the libraries will be named inside the bytecode to their addresses. map librariesReplacements; int const placeholderSize = 40; // 20 bytes or 40 hex characters - for (auto const& library: m_libraries) + for (auto const& library: m_options.linker.libraries) { string const& name = library.first; // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'. @@ -1831,7 +894,7 @@ bool CommandLineInterface::link() it += placeholderSize; } // Remove hints for resolved libraries. - for (auto const& library: m_libraries) + for (auto const& library: m_options.linker.libraries) boost::algorithm::erase_all(src.second, "\n" + libraryPlaceholderHint(library.first)); while (!src.second.empty() && *prev(src.second.end()) == '\n') src.second.resize(src.second.size() - 1); @@ -1880,6 +943,7 @@ bool CommandLineInterface::assemble( yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, bool _optimize, + optional _expectedExecutionsPerDeployment, optional _yulOptimiserSteps ) { @@ -1890,10 +954,12 @@ bool CommandLineInterface::assemble( for (auto const& src: m_fileReader.sourceCodes()) { OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal(); + if (_expectedExecutionsPerDeployment.has_value()) + settings.expectedExecutionsPerDeployment = _expectedExecutionsPerDeployment.value(); if (_yulOptimiserSteps.has_value()) settings.yulOptimiserSteps = _yulOptimiserSteps.value(); - auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings); + auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_options.output.evmVersion, _language, settings); try { if (!stack.parseAndAnalyze(src.first, src.second)) @@ -1924,11 +990,11 @@ bool CommandLineInterface::assemble( for (auto const& sourceAndStack: assemblyStacks) { auto const& stack = sourceAndStack.second; - SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); + SourceReferenceFormatter formatter(serr(false), stack, coloredOutput(m_options), m_options.formatting.withErrorIds); for (auto const& error: stack.errors()) { - g_hasOutput = true; + m_hasOutput = true; formatter.printErrorInformation(*error); } if (!Error::containsOnlyWarnings(stack.errors())) @@ -1985,7 +1051,7 @@ bool CommandLineInterface::assemble( try { object = stack.assemble(_targetMachine); - object.bytecode->link(m_libraries); + object.bytecode->link(m_options.linker.libraries); } catch (Exception const& _exception) { @@ -2030,7 +1096,7 @@ void CommandLineInterface::outputCompilationResults() if ( !m_compiler->compilationSuccessful() && - m_stopAfter == CompilerStack::State::CompilationSuccessful + m_options.output.stopAfter == CompilerStack::State::CompilationSuccessful ) { serr() << endl << "Compilation halted after AST generation due to errors." << endl; @@ -2040,21 +1106,21 @@ void CommandLineInterface::outputCompilationResults() vector contracts = m_compiler->contractNames(); for (string const& contract: contracts) { - if (needsHumanTargetedStdout(m_args)) + if (needsHumanTargetedStdout(m_options)) sout() << endl << "======= " << contract << " =======" << endl; // do we need EVM assembly? - if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson)) + if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson) { string ret; - if (m_args.count(g_argAsmJson)) + if (m_options.compiler.outputs.asmJson) ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract))); else ret = m_compiler->assemblyString(contract, m_fileReader.sourceCodes()); - if (m_args.count(g_argOutputDir)) + if (!m_options.output.dir.empty()) { - createFile(m_compiler->filesystemFriendlyName(contract) + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), ret); + createFile(m_compiler->filesystemFriendlyName(contract) + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), ret); } else { @@ -2062,7 +1128,7 @@ void CommandLineInterface::outputCompilationResults() } } - if (m_args.count(g_argGas)) + if (m_options.compiler.estimateGas) handleGasEstimation(contract); handleBytecode(contract); @@ -2077,30 +1143,13 @@ void CommandLineInterface::outputCompilationResults() handleNatspec(false, contract); } // end of contracts iteration - if (!g_hasOutput) + if (!m_hasOutput) { - if (m_args.count(g_argOutputDir)) - sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_args.at(g_argOutputDir).as() << "." << endl; + if (!m_options.output.dir.empty()) + sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_options.output.dir << "." << endl; else serr() << "Compiler run successful, no output requested." << endl; } } -size_t CommandLineInterface::countEnabledOptions(vector const& _optionNames) const -{ - size_t count = 0; - for (string const& _option: _optionNames) - count += m_args.count(_option); - - return count; -} - -string CommandLineInterface::joinOptionNames(vector const& _optionNames, string _separator) -{ - return boost::algorithm::join( - _optionNames | ranges::views::transform([](string const& _option){ return "--" + _option; }), - _separator - ); -} - } diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 73c220e37..488ec1d71 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -22,36 +22,51 @@ */ #pragma once +#include + #include #include #include -#include #include -#include - -#include -#include +#include #include +#include namespace solidity::frontend { -//forward declaration -enum class DocumentationType: uint8_t; - class CommandLineInterface { public: + explicit CommandLineInterface( + std::istream& _sin, + std::ostream& _sout, + std::ostream& _serr, + CommandLineOptions const& _options = CommandLineOptions{} + ): + m_sin(_sin), + m_sout(_sout), + m_serr(_serr), + m_options(_options) + {} + /// Parse command line arguments and return false if we should not continue - bool parseArguments(int _argc, char** _argv); + bool parseArguments(int _argc, char const* const* _argv); + /// Read the content of all input files and initialize the file reader. + bool readInputFiles(); /// Parse the files and create source code objects bool processInput(); /// Perform actions on the input depending on provided compiler arguments /// @returns true on success. bool actOnInput(); + CommandLineOptions const& options() const { return m_options; } + FileReader const& fileReader() const { return m_fileReader; } + std::optional const& standardJsonInput() const { return m_standardJsonInput; } + private: + bool compile(); bool link(); void writeLinkedFiles(); /// @returns the ``// -> name`` hint for library placeholders. @@ -63,6 +78,7 @@ private: yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, bool _optimize, + std::optional _expectedExecutionsPerDeployment = std::nullopt, std::optional _yulOptimiserSteps = std::nullopt ); @@ -83,12 +99,6 @@ private: void handleGasEstimation(std::string const& _contract); void handleStorageLayout(std::string const& _contract); - /// Fills @a m_sourceCodes initially and @a m_redirects. - bool readInputFilesAndConfigureRemappings(); - /// Tries to read from the file @a _input or interprets _input literally if that fails. - /// It then tries to parse the contents and appends to m_libraries. - bool parseLibraryOption(std::string const& _input); - /// Tries to read @ m_sourceCodes as a JSONs holding ASTs /// such that they can be imported into the compiler (importASTs()) /// (produced by --combined-json ast,compact-format @@ -105,38 +115,23 @@ private: /// @arg _json json string to be written void createJson(std::string const& _fileName, std::string const& _json); - size_t countEnabledOptions(std::vector const& _optionNames) const; - static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); + /// Returns the stream that should receive normal output. Sets m_hasOutput to true if the + /// stream has ever been used unless @arg _markAsUsed is set to false. + std::ostream& sout(bool _markAsUsed = true); + /// Returns the stream that should receive error output. Sets m_hasOutput to true if the + /// stream has ever been used unless @arg _markAsUsed is set to false. + std::ostream& serr(bool _markAsUsed = true); + + std::istream& m_sin; + std::ostream& m_sout; + std::ostream& m_serr; + bool m_hasOutput = false; bool m_error = false; ///< If true, some error occurred. - - bool m_onlyAssemble = false; - - bool m_onlyLink = false; - FileReader m_fileReader; - - /// Compiler arguments variable map - boost::program_options::variables_map m_args; - /// list of remappings - std::vector m_remappings; - /// map of library names to addresses - std::map m_libraries; - /// Solidity compiler stack + std::optional m_standardJsonInput; std::unique_ptr m_compiler; - CompilerStack::State m_stopAfter = CompilerStack::State::CompilationSuccessful; - /// EVM version to use - langutil::EVMVersion m_evmVersion; - /// How to handle revert strings - RevertStrings m_revertStrings = RevertStrings::Default; - /// Chosen hash method for the bytecode metadata. - CompilerStack::MetadataHash m_metadataHash = CompilerStack::MetadataHash::IPFS; - /// Model checker settings. - ModelCheckerSettings m_modelCheckerSettings; - /// Whether or not to colorize diagnostics output. - bool m_coloredOutput = true; - /// Whether or not to output error IDs. - bool m_withErrorIds = false; + CommandLineOptions m_options; }; } diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp new file mode 100644 index 000000000..3654bbfc8 --- /dev/null +++ b/solc/CommandLineParser.cpp @@ -0,0 +1,1238 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include "license.h" + +#include +#include +#include + +#include + +#include + +using namespace std; +using namespace solidity::langutil; + +namespace po = boost::program_options; + +namespace solidity::frontend +{ + +ostream& CommandLineParser::sout() +{ + m_hasOutput = true; + return m_sout; +} + +ostream& CommandLineParser::serr() +{ + m_hasOutput = true; + return m_serr; +} + +#define cout +#define cerr + +static string const g_strAbi = "abi"; +static string const g_strAllowPaths = "allow-paths"; +static string const g_strBasePath = "base-path"; +static string const g_strAsm = "asm"; +static string const g_strAsmJson = "asm-json"; +static string const g_strAssemble = "assemble"; +static string const g_strAst = "ast"; +static string const g_strAstCompactJson = "ast-compact-json"; +static string const g_strBinary = "bin"; +static string const g_strBinaryRuntime = "bin-runtime"; +static string const g_strCombinedJson = "combined-json"; +static string const g_strCompactJSON = "compact-format"; +static string const g_strErrorRecovery = "error-recovery"; +static string const g_strEVM = "evm"; +static string const g_strEVMVersion = "evm-version"; +static string const g_strEwasm = "ewasm"; +static string const g_strExperimentalViaIR = "experimental-via-ir"; +static string const g_strGeneratedSources = "generated-sources"; +static string const g_strGeneratedSourcesRuntime = "generated-sources-runtime"; +static string const g_strGas = "gas"; +static string const g_strHelp = "help"; +static string const g_strImportAst = "import-ast"; +static string const g_strInputFile = "input-file"; +static string const g_strInterface = "interface"; +static string const g_strYul = "yul"; +static string const g_strYulDialect = "yul-dialect"; +static string const g_strIR = "ir"; +static string const g_strIROptimized = "ir-optimized"; +static string const g_strIPFS = "ipfs"; +static string const g_strLicense = "license"; +static string const g_strLibraries = "libraries"; +static string const g_strLink = "link"; +static string const g_strMachine = "machine"; +static string const g_strMetadata = "metadata"; +static string const g_strMetadataHash = "metadata-hash"; +static string const g_strMetadataLiteral = "metadata-literal"; +static string const g_strModelCheckerContracts = "model-checker-contracts"; +static string const g_strModelCheckerEngine = "model-checker-engine"; +static string const g_strModelCheckerShowUnproved = "model-checker-show-unproved"; +static string const g_strModelCheckerSolvers = "model-checker-solvers"; +static string const g_strModelCheckerTargets = "model-checker-targets"; +static string const g_strModelCheckerTimeout = "model-checker-timeout"; +static string const g_strNatspecDev = "devdoc"; +static string const g_strNatspecUser = "userdoc"; +static string const g_strNone = "none"; +static string const g_strNoOptimizeYul = "no-optimize-yul"; +static string const g_strOpcodes = "opcodes"; +static string const g_strOptimize = "optimize"; +static string const g_strOptimizeRuns = "optimize-runs"; +static string const g_strOptimizeYul = "optimize-yul"; +static string const g_strYulOptimizations = "yul-optimizations"; +static string const g_strOutputDir = "output-dir"; +static string const g_strOverwrite = "overwrite"; +static string const g_strRevertStrings = "revert-strings"; +static string const g_strStorageLayout = "storage-layout"; +static string const g_strStopAfter = "stop-after"; +static string const g_strParsing = "parsing"; + +/// Possible arguments to for --revert-strings +static set const g_revertStringsArgs +{ + revertStringsToString(RevertStrings::Default), + revertStringsToString(RevertStrings::Strip), + revertStringsToString(RevertStrings::Debug), + revertStringsToString(RevertStrings::VerboseDebug) +}; + +static string const g_strSignatureHashes = "hashes"; +static string const g_strSources = "sources"; +static string const g_strSourceList = "sourceList"; +static string const g_strSrcMap = "srcmap"; +static string const g_strSrcMapRuntime = "srcmap-runtime"; +static string const g_strFunDebug = "function-debug"; +static string const g_strFunDebugRuntime = "function-debug-runtime"; +static string const g_strStandardJSON = "standard-json"; +static string const g_strStrictAssembly = "strict-assembly"; +static string const g_strSwarm = "swarm"; +static string const g_strPrettyJson = "pretty-json"; +static string const g_strJsonIndent = "json-indent"; +static string const g_strVersion = "version"; +static string const g_strIgnoreMissingFiles = "ignore-missing"; +static string const g_strColor = "color"; +static string const g_strNoColor = "no-color"; +static string const g_strErrorIds = "error-codes"; + +/// Possible arguments to for --combined-json +static set const g_combinedJsonArgs +{ + g_strAbi, + g_strAsm, + g_strAst, + g_strBinary, + g_strBinaryRuntime, + g_strCompactJSON, + g_strFunDebug, + g_strFunDebugRuntime, + g_strGeneratedSources, + g_strGeneratedSourcesRuntime, + g_strInterface, + g_strMetadata, + g_strNatspecUser, + g_strNatspecDev, + g_strOpcodes, + g_strSignatureHashes, + g_strSrcMap, + g_strSrcMapRuntime, + g_strStorageLayout +}; + +/// Possible arguments to for --machine +static set const g_machineArgs +{ + g_strEVM, + g_strEwasm +}; + +/// Possible arguments to for --yul-dialect +static set const g_yulDialectArgs +{ + g_strEVM, + g_strEwasm +}; + +/// Possible arguments to for --metadata-hash +static set const g_metadataHashArgs +{ + g_strIPFS, + g_strSwarm, + g_strNone +}; + +void CommandLineParser::printVersionAndExit() +{ + sout() << + "solc, the solidity compiler commandline interface" << + endl << + "Version: " << + solidity::frontend::VersionString << + endl; + exit(EXIT_SUCCESS); +} + +void CommandLineParser::printLicenseAndExit() +{ + sout() << otherLicenses << endl; + // This is a static variable generated by cmake from LICENSE.txt + sout() << licenseText << endl; + exit(EXIT_SUCCESS); +} + + +bool CommandLineParser::checkMutuallyExclusive(vector const& _optionNames) +{ + if (countEnabledOptions(_optionNames) > 1) + { + serr() << "The following options are mutually exclusive: " << joinOptionNames(_optionNames) << ". "; + serr() << "Select at most one." << endl; + return false; + } + return true; +} + +bool CompilerOutputs::operator==(CompilerOutputs const& _other) const noexcept +{ + static_assert( + sizeof(*this) == 15 * sizeof(bool), + "Remember to update code below if you add/remove fields." + ); + + return + astCompactJson == _other.astCompactJson && + asm_ == _other.asm_ && + asmJson == _other.asmJson && + opcodes == _other.opcodes && + binary == _other.binary && + binaryRuntime == _other.binaryRuntime && + abi == _other.abi && + ir == _other.ir && + irOptimized == _other.irOptimized && + ewasm == _other.ewasm && + signatureHashes == _other.signatureHashes && + natspecUser == _other.natspecUser && + natspecDev == _other.natspecDev && + metadata == _other.metadata && + storageLayout == _other.storageLayout; +} + +bool CombinedJsonRequests::operator==(CombinedJsonRequests const& _other) const noexcept +{ + static_assert( + sizeof(*this) == 17 * sizeof(bool), + "Remember to update code below if you add/remove fields." + ); + + return + abi == _other.abi && + metadata == _other.metadata && + binary == _other.binary && + binaryRuntime == _other.binaryRuntime && + opcodes == _other.opcodes && + asm_ == _other.asm_ && + storageLayout == _other.storageLayout && + generatedSources == _other.generatedSources && + generatedSourcesRuntime == _other.generatedSourcesRuntime && + srcMap == _other.srcMap && + srcMapRuntime == _other.srcMapRuntime && + funDebug == _other.funDebug && + funDebugRuntime == _other.funDebugRuntime && + signatureHashes == _other.signatureHashes && + natspecDev == _other.natspecDev && + natspecUser == _other.natspecUser && + ast == _other.ast; +} + +bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noexcept +{ + return + input.paths == _other.input.paths && + input.remappings == _other.input.remappings && + input.addStdin == _other.input.addStdin && + input.basePath == _other.input.basePath && + input.allowedDirectories == _other.input.allowedDirectories && + input.ignoreMissingFiles == _other.input.ignoreMissingFiles && + input.errorRecovery == _other.input.errorRecovery && + output.dir == _other.output.dir && + output.overwriteFiles == _other.output.overwriteFiles && + output.evmVersion == _other.output.evmVersion && + output.experimentalViaIR == _other.output.experimentalViaIR && + output.revertStrings == _other.output.revertStrings && + output.stopAfter == _other.output.stopAfter && + input.mode == _other.input.mode && + assembly.targetMachine == _other.assembly.targetMachine && + assembly.inputLanguage == _other.assembly.inputLanguage && + linker.libraries == _other.linker.libraries && + formatting.json == _other.formatting.json && + formatting.coloredOutput == _other.formatting.coloredOutput && + formatting.withErrorIds == _other.formatting.withErrorIds && + compiler.outputs == _other.compiler.outputs && + compiler.estimateGas == _other.compiler.estimateGas && + compiler.combinedJsonRequests == _other.compiler.combinedJsonRequests && + metadata.hash == _other.metadata.hash && + metadata.literalSources == _other.metadata.literalSources && + optimizer.enabled == _other.optimizer.enabled && + optimizer.expectedExecutionsPerDeployment == _other.optimizer.expectedExecutionsPerDeployment && + optimizer.noOptimizeYul == _other.optimizer.noOptimizeYul && + optimizer.yulSteps == _other.optimizer.yulSteps && + modelChecker.initialize == _other.modelChecker.initialize && + modelChecker.settings == _other.modelChecker.settings; +} + +bool CommandLineParser::parseInputPathsAndRemappings() +{ + m_options.input.ignoreMissingFiles = (m_args.count(g_strIgnoreMissingFiles) > 0); + + if (m_args.count(g_strInputFile)) + for (string path: m_args[g_strInputFile].as>()) + { + auto eq = find(path.begin(), path.end(), '='); + if (eq != path.end()) + { + if (m_options.input.mode == InputMode::StandardJson) + { + serr() << "Import remappings are not accepted on the command line in Standard JSON mode." << endl; + serr() << "Please put them under 'settings.remappings' in the JSON input." << endl; + return false; + } + + if (auto r = ImportRemapper::parseRemapping(path)) + m_options.input.remappings.emplace_back(std::move(*r)); + else + { + serr() << "Invalid remapping: \"" << path << "\"." << endl; + return false; + } + + string remappingTarget(eq + 1, path.end()); + m_options.input.allowedDirectories.insert(boost::filesystem::path(remappingTarget).remove_filename()); + } + else if (path == "-") + m_options.input.addStdin = true; + else + m_options.input.paths.insert(path); + } + + if (m_options.input.mode == InputMode::StandardJson) + { + if (m_options.input.paths.size() > 1 || (m_options.input.paths.size() == 1 && m_options.input.addStdin)) + { + serr() << "Too many input files for --" << g_strStandardJSON << "." << endl; + serr() << "Please either specify a single file name or provide its content on standard input." << endl; + return false; + } + else if (m_options.input.paths.size() == 0) + // Standard JSON mode input used to be handled separately and zero files meant "read from stdin". + // Keep it working that way for backwards-compatibility. + m_options.input.addStdin = true; + } + else if (m_options.input.paths.size() == 0 && !m_options.input.addStdin) + { + serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; + return false; + } + + return true; +} + +bool CommandLineParser::parseLibraryOption(string const& _input) +{ + namespace fs = boost::filesystem; + string data = _input; + try + { + if (fs::is_regular_file(_input)) + data = readFileAsString(_input); + } + catch (fs::filesystem_error const&) + { + // Thrown e.g. if path is too long. + } + catch (FileNotFound const&) + { + // Should not happen if `fs::is_regular_file` is correct. + } + catch (NotAFile const&) + { + // Should not happen if `fs::is_regular_file` is correct. + } + + vector libraries; + boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on); + for (string const& lib: libraries) + if (!lib.empty()) + { + //search for equal sign or last colon in string as our binaries output placeholders in the form of file=Name or file:Name + //so we need to search for `=` or `:` in the string + auto separator = lib.rfind('='); + bool isSeparatorEqualSign = true; + if (separator == string::npos) + { + separator = lib.rfind(':'); + if (separator == string::npos) + { + serr() << "Equal sign separator missing in library address specifier \"" << lib << "\"" << endl; + return false; + } + else + isSeparatorEqualSign = false; // separator is colon + } + else + if (lib.rfind('=') != lib.find('=')) + { + serr() << "Only one equal sign \"=\" is allowed in the address string \"" << lib << "\"." << endl; + return false; + } + + string libName(lib.begin(), lib.begin() + static_cast(separator)); + boost::trim(libName); + if (m_options.linker.libraries.count(libName)) + { + serr() << "Address specified more than once for library \"" << libName << "\"." << endl; + return false; + } + + string addrString(lib.begin() + static_cast(separator) + 1, lib.end()); + boost::trim(addrString); + if (addrString.empty()) + { + serr() << "Empty address provided for library \"" << libName << "\"." << endl; + serr() << "Note that there should not be any whitespace after the " << (isSeparatorEqualSign ? "equal sign" : "colon") << "." << endl; + return false; + } + + if (addrString.substr(0, 2) == "0x") + addrString = addrString.substr(2); + else + { + serr() << "The address " << addrString << " is not prefixed with \"0x\"." << endl; + serr() << "Note that the address must be prefixed with \"0x\"." << endl; + return false; + } + + if (addrString.length() != 40) + { + serr() << "Invalid length for address for library \"" << libName << "\": " << addrString.length() << " instead of 40 characters." << endl; + return false; + } + if (!passesAddressChecksum(addrString, false)) + { + serr() << "Invalid checksum on address for library \"" << libName << "\": " << addrString << endl; + serr() << "The correct checksum is " << getChecksummedAddress(addrString) << endl; + return false; + } + bytes binAddr = fromHex(addrString); + h160 address(binAddr, h160::AlignRight); + if (binAddr.size() > 20 || address == h160()) + { + serr() << "Invalid address for library \"" << libName << "\": " << addrString << endl; + return false; + } + m_options.linker.libraries[libName] = address; + } + + return true; +} + +bool CommandLineParser::parse(int _argc, char const* const* _argv, bool interactiveTerminal) +{ + m_hasOutput = false; + + // Declare the supported options. + po::options_description desc((R"(solc, the Solidity commandline compiler. + +This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you +are welcome to redistribute it under certain conditions. See 'solc --)" + g_strLicense + R"(' +for details. + +Usage: solc [options] [input_file...] +Compiles the given Solidity input files (or the standard input if none given or +"-" is used as a file name) and outputs the components specified in the options +at standard output or in files in the output directory, if specified. +Imports are automatically read from the filesystem, but it is also possible to +remap paths using the context:prefix=path syntax. +Example: +solc --)" + g_strBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol + +General Information)").c_str(), + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23 + ); + desc.add_options() + (g_strHelp.c_str(), "Show help message and exit.") + (g_strVersion.c_str(), "Show version and exit.") + (g_strLicense.c_str(), "Show licensing information and exit.") + ; + + po::options_description inputOptions("Input Options"); + inputOptions.add_options() + ( + g_strBasePath.c_str(), + po::value()->value_name("path"), + "Use the given path as the root of the source tree instead of the root of the filesystem." + ) + ( + g_strAllowPaths.c_str(), + po::value()->value_name("path(s)"), + "Allow a given path for imports. A list of paths can be supplied by separating them with a comma." + ) + ( + g_strIgnoreMissingFiles.c_str(), + "Ignore missing files." + ) + ( + g_strErrorRecovery.c_str(), + "Enables additional parser error recovery." + ) + ; + desc.add(inputOptions); + + po::options_description outputOptions("Output Options"); + outputOptions.add_options() + ( + (g_strOutputDir + ",o").c_str(), + po::value()->value_name("path"), + "If given, creates one file per component and contract/file at the specified directory." + ) + ( + g_strOverwrite.c_str(), + "Overwrite existing files (used together with -o)." + ) + ( + g_strEVMVersion.c_str(), + po::value()->value_name("version")->default_value(EVMVersion{}.name()), + "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " + "byzantium, constantinople, petersburg, istanbul or berlin." + ) + ( + g_strExperimentalViaIR.c_str(), + "Turn on experimental compilation mode via the IR (EXPERIMENTAL)." + ) + ( + g_strRevertStrings.c_str(), + po::value()->value_name(boost::join(g_revertStringsArgs, ",")), + "Strip revert (and require) reason strings or add additional debugging information." + ) + ( + g_strStopAfter.c_str(), + po::value()->value_name("stage"), + "Stop execution after the given compiler stage. Valid options: \"parsing\"." + ) + ; + desc.add(outputOptions); + + po::options_description alternativeInputModes("Alternative Input Modes"); + alternativeInputModes.add_options() + ( + g_strStandardJSON.c_str(), + "Switch to Standard JSON input / output mode, ignoring all options. " + "It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output." + ) + ( + g_strLink.c_str(), + ("Switch to linker mode, ignoring all options apart from --" + g_strLibraries + " " + "and modify binaries in place.").c_str() + ) + ( + g_strAssemble.c_str(), + ("Switch to assembly mode, ignoring all options except " + "--" + g_strMachine + ", --" + g_strYulDialect + ", --" + g_strOptimize + " and --" + g_strYulOptimizations + " " + "and assumes input is assembly.").c_str() + ) + ( + g_strYul.c_str(), + ("Switch to Yul mode, ignoring all options except " + "--" + g_strMachine + ", --" + g_strYulDialect + ", --" + g_strOptimize + " and --" + g_strYulOptimizations + " " + "and assumes input is Yul.").c_str() + ) + ( + g_strStrictAssembly.c_str(), + ("Switch to strict assembly mode, ignoring all options except " + "--" + g_strMachine + ", --" + g_strYulDialect + ", --" + g_strOptimize + " and --" + g_strYulOptimizations + " " + "and assumes input is strict assembly.").c_str() + ) + ( + g_strImportAst.c_str(), + ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " + "Supported Inputs is the output of the --" + g_strStandardJSON + " or the one produced by " + "--" + g_strCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str() + ) + ; + desc.add(alternativeInputModes); + + po::options_description assemblyModeOptions("Assembly Mode Options"); + assemblyModeOptions.add_options() + ( + g_strMachine.c_str(), + po::value()->value_name(boost::join(g_machineArgs, ",")), + "Target machine in assembly or Yul mode." + ) + ( + g_strYulDialect.c_str(), + po::value()->value_name(boost::join(g_yulDialectArgs, ",")), + "Input dialect to use in assembly or yul mode." + ) + ; + desc.add(assemblyModeOptions); + + po::options_description linkerModeOptions("Linker Mode Options"); + linkerModeOptions.add_options() + ( + g_strLibraries.c_str(), + po::value>()->value_name("libs"), + "Direct string or file containing library addresses. Syntax: " + "=
[, or whitespace] ...\n" + "Address is interpreted as a hex string prefixed by 0x." + ) + ; + desc.add(linkerModeOptions); + + po::options_description outputFormatting("Output Formatting"); + outputFormatting.add_options() + ( + g_strPrettyJson.c_str(), + "Output JSON in pretty format." + ) + ( + g_strJsonIndent.c_str(), + po::value()->value_name("N")->default_value(util::JsonFormat::defaultIndent), + "Indent pretty-printed JSON with N spaces. Enables '--pretty-json' automatically." + ) + ( + g_strColor.c_str(), + "Force colored output." + ) + ( + g_strNoColor.c_str(), + "Explicitly disable colored output, disabling terminal auto-detection." + ) + ( + g_strErrorIds.c_str(), + "Output error codes." + ) + ; + desc.add(outputFormatting); + + po::options_description outputComponents("Output Components"); + outputComponents.add_options() + (g_strAstCompactJson.c_str(), "AST of all source files in a compact JSON format.") + (g_strAsm.c_str(), "EVM assembly of the contracts.") + (g_strAsmJson.c_str(), "EVM assembly of the contracts in JSON format.") + (g_strOpcodes.c_str(), "Opcodes of the contracts.") + (g_strBinary.c_str(), "Binary of the contracts in hex.") + (g_strBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") + (g_strAbi.c_str(), "ABI specification of the contracts.") + (g_strIR.c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (g_strIROptimized.c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (g_strEwasm.c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") + (g_strSignatureHashes.c_str(), "Function signature hashes of the contracts.") + (g_strNatspecUser.c_str(), "Natspec user documentation of all contracts.") + (g_strNatspecDev.c_str(), "Natspec developer documentation of all contracts.") + (g_strMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") + (g_strStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.") + ; + desc.add(outputComponents); + + po::options_description extraOutput("Extra Output"); + extraOutput.add_options() + ( + g_strGas.c_str(), + "Print an estimate of the maximal gas usage for each function." + ) + ( + g_strCombinedJson.c_str(), + po::value()->value_name(boost::join(g_combinedJsonArgs, ",")), + "Output a single json document containing the specified information." + ) + ; + desc.add(extraOutput); + + po::options_description metadataOptions("Metadata Options"); + metadataOptions.add_options() + ( + g_strMetadataHash.c_str(), + po::value()->value_name(boost::join(g_metadataHashArgs, ",")), + "Choose hash method for the bytecode metadata or disable it." + ) + ( + g_strMetadataLiteral.c_str(), + "Store referenced sources as literal data in the metadata output." + ) + ; + desc.add(metadataOptions); + + po::options_description optimizerOptions("Optimizer Options"); + optimizerOptions.add_options() + ( + g_strOptimize.c_str(), + "Enable bytecode optimizer." + ) + ( + g_strOptimizeRuns.c_str(), + // TODO: The type in OptimiserSettings is size_t but we only accept values up to 2**32-1 + // on the CLI and in Standard JSON. We should just switch to uint32_t everywhere. + po::value()->value_name("n")->default_value(static_cast(OptimiserSettings{}.expectedExecutionsPerDeployment)), + "Set for how many contract runs to optimize. " + "Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage." + ) + ( + g_strOptimizeYul.c_str(), + ("Legacy option, ignored. Use the general --" + g_strOptimize + " to enable Yul optimizer.").c_str() + ) + ( + g_strNoOptimizeYul.c_str(), + "Disable Yul optimizer in Solidity." + ) + ( + g_strYulOptimizations.c_str(), + po::value()->value_name("steps"), + "Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one." + ) + ; + desc.add(optimizerOptions); + + po::options_description smtCheckerOptions("Model Checker Options"); + smtCheckerOptions.add_options() + ( + g_strModelCheckerContracts.c_str(), + po::value()->value_name("default,:")->default_value("default"), + "Select which contracts should be analyzed using the form :." + "Multiple pairs : can be selected at the same time, separated by a comma " + "and no spaces." + ) + ( + g_strModelCheckerEngine.c_str(), + po::value()->value_name("all,bmc,chc,none")->default_value("none"), + "Select model checker engine." + ) + ( + g_strModelCheckerShowUnproved.c_str(), + po::value()->value_name("false,true")->default_value(false), + "Select whether to show all unproved targets." + ) + ( + g_strModelCheckerSolvers.c_str(), + po::value()->value_name("all,cvc4,z3,smtlib2")->default_value("all"), + "Select model checker solvers." + ) + ( + g_strModelCheckerTargets.c_str(), + po::value()->value_name("default,constantCondition,underflow,overflow,divByZero,balance,assert,popEmptyArray,outOfBounds")->default_value("default"), + "Select model checker verification targets. " + "Multiple targets can be selected at the same time, separated by a comma " + "and no spaces." + ) + ( + g_strModelCheckerTimeout.c_str(), + po::value()->value_name("ms"), + "Set model checker timeout per query in milliseconds. " + "The default is a deterministic resource limit. " + "A timeout of 0 means no resource/time restrictions for any query." + ) + ; + desc.add(smtCheckerOptions); + + po::options_description allOptions = desc; + allOptions.add_options()(g_strInputFile.c_str(), po::value>(), "input file"); + + // All positional options should be interpreted as input files + po::positional_options_description filesPositions; + filesPositions.add(g_strInputFile.c_str(), -1); + + // parse the compiler arguments + try + { + po::command_line_parser cmdLineParser(_argc, _argv); + cmdLineParser.style(po::command_line_style::default_style & (~po::command_line_style::allow_guessing)); + cmdLineParser.options(allOptions).positional(filesPositions); + po::store(cmdLineParser.run(), m_args); + } + catch (po::error const& _exception) + { + serr() << _exception.what() << endl; + return false; + } + + if (!checkMutuallyExclusive({g_strColor, g_strNoColor})) + return false; + + array const conflictingWithStopAfter{ + g_strBinary, + g_strIR, + g_strIROptimized, + g_strEwasm, + g_strGas, + g_strAsm, + g_strAsmJson, + g_strOpcodes + }; + + for (auto& option: conflictingWithStopAfter) + if (!checkMutuallyExclusive({g_strStopAfter, option})) + return false; + + if (m_args.count(g_strColor) > 0) + m_options.formatting.coloredOutput = true; + else if (m_args.count(g_strNoColor) > 0) + m_options.formatting.coloredOutput = false; + + m_options.formatting.withErrorIds = m_args.count(g_strErrorIds); + + if (m_args.count(g_strHelp) || (interactiveTerminal && _argc == 1)) + { + sout() << desc; + return false; + } + + if (m_args.count(g_strVersion)) + printVersionAndExit(); + + if (m_args.count(g_strLicense)) + printLicenseAndExit(); + + if (m_args.count(g_strRevertStrings)) + { + string revertStringsString = m_args[g_strRevertStrings].as(); + std::optional revertStrings = revertStringsFromString(revertStringsString); + if (!revertStrings) + { + serr() << "Invalid option for --" << g_strRevertStrings << ": " << revertStringsString << endl; + return false; + } + if (*revertStrings == RevertStrings::VerboseDebug) + { + serr() << "Only \"default\", \"strip\" and \"debug\" are implemented for --" << g_strRevertStrings << " for now." << endl; + return false; + } + m_options.output.revertStrings = *revertStrings; + } + + if (!parseCombinedJsonOption()) + return false; + + if (m_args.count(g_strOutputDir)) + m_options.output.dir = m_args.at(g_strOutputDir).as(); + + m_options.output.overwriteFiles = (m_args.count(g_strOverwrite) > 0); + + if (m_args.count(g_strPrettyJson) > 0) + { + m_options.formatting.json.format = JsonFormat::Pretty; + } + if (!m_args[g_strJsonIndent].defaulted()) + { + m_options.formatting.json.format = JsonFormat::Pretty; + m_options.formatting.json.indent = m_args[g_strJsonIndent].as(); + } + + static_assert( + sizeof(m_options.compiler.outputs) == 15 * sizeof(bool), + "Remember to update code below if you add/remove fields." + ); + m_options.compiler.outputs.astCompactJson = (m_args.count(g_strAstCompactJson) > 0); + m_options.compiler.outputs.asm_ = (m_args.count(g_strAsm) > 0); + m_options.compiler.outputs.asmJson = (m_args.count(g_strAsmJson) > 0); + m_options.compiler.outputs.opcodes = (m_args.count(g_strOpcodes) > 0); + m_options.compiler.outputs.binary = (m_args.count(g_strBinary) > 0); + m_options.compiler.outputs.binaryRuntime = (m_args.count(g_strBinaryRuntime) > 0); + m_options.compiler.outputs.abi = (m_args.count(g_strAbi) > 0); + m_options.compiler.outputs.ir = (m_args.count(g_strIR) > 0); + m_options.compiler.outputs.irOptimized = (m_args.count(g_strIROptimized) > 0); + m_options.compiler.outputs.ewasm = (m_args.count(g_strEwasm) > 0); + m_options.compiler.outputs.signatureHashes = (m_args.count(g_strSignatureHashes) > 0); + m_options.compiler.outputs.natspecUser = (m_args.count(g_strNatspecUser) > 0); + m_options.compiler.outputs.natspecDev = (m_args.count(g_strNatspecDev) > 0); + m_options.compiler.outputs.metadata = (m_args.count(g_strMetadata) > 0); + m_options.compiler.outputs.storageLayout = (m_args.count(g_strStorageLayout) > 0); + + m_options.compiler.estimateGas = (m_args.count(g_strGas) > 0); + + po::notify(m_args); + + if (m_args.count(g_strBasePath)) + m_options.input.basePath = m_args[g_strBasePath].as(); + + if (m_args.count(g_strAllowPaths)) + { + vector paths; + for (string const& path: boost::split(paths, m_args[g_strAllowPaths].as(), boost::is_any_of(","))) + { + auto filesystem_path = boost::filesystem::path(path); + // If the given path had a trailing slash, the Boost filesystem + // path will have it's last component set to '.'. This breaks + // path comparison in later parts of the code, so we need to strip + // it. + if (filesystem_path.filename() == ".") + filesystem_path.remove_filename(); + m_options.input.allowedDirectories.insert(filesystem_path); + } + } + + if (m_args.count(g_strStopAfter)) + { + if (m_args[g_strStopAfter].as() != "parsing") + { + serr() << "Valid options for --" << g_strStopAfter << " are: \"parsing\".\n"; + return false; + } + else + m_options.output.stopAfter = CompilerStack::State::Parsed; + } + + if (!checkMutuallyExclusive({ + g_strStandardJSON, + g_strLink, + g_strAssemble, + g_strStrictAssembly, + g_strYul, + g_strImportAst, + })) + return false; + + if (m_args.count(g_strStandardJSON) > 0) + m_options.input.mode = InputMode::StandardJson; + else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0) + m_options.input.mode = InputMode::Assembler; + else if (m_args.count(g_strLink) > 0) + m_options.input.mode = InputMode::Linker; + else if (m_args.count(g_strImportAst) > 0) + m_options.input.mode = InputMode::CompilerWithASTImport; + else + m_options.input.mode = InputMode::Compiler; + + if (!parseInputPathsAndRemappings()) + return false; + + if (m_options.input.mode == InputMode::StandardJson) + return true; + + if (m_args.count(g_strLibraries)) + for (string const& library: m_args[g_strLibraries].as>()) + if (!parseLibraryOption(library)) + return false; + + if (m_args.count(g_strEVMVersion)) + { + string versionOptionStr = m_args[g_strEVMVersion].as(); + std::optional versionOption = langutil::EVMVersion::fromString(versionOptionStr); + if (!versionOption) + { + serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl; + return false; + } + m_options.output.evmVersion = *versionOption; + } + + if (m_options.input.mode == InputMode::Assembler) + { + vector const nonAssemblyModeOptions = { + // TODO: The list is not complete. Add more. + g_strOutputDir, + g_strGas, + g_strCombinedJson, + g_strOptimizeYul, + g_strNoOptimizeYul, + }; + if (countEnabledOptions(nonAssemblyModeOptions) >= 1) + { + auto optionEnabled = [&](string const& name){ return m_args.count(name) > 0; }; + auto enabledOptions = boost::copy_range>(nonAssemblyModeOptions | boost::adaptors::filtered(optionEnabled)); + + serr() << "The following options are invalid in assembly mode: "; + serr() << joinOptionNames(enabledOptions) << "."; + if (m_args.count(g_strOptimizeYul) || m_args.count(g_strNoOptimizeYul)) + serr() << " Optimization is disabled by default and can be enabled with --" << g_strOptimize << "." << endl; + serr() << endl; + return false; + } + + // switch to assembly mode + using Input = yul::AssemblyStack::Language; + using Machine = yul::AssemblyStack::Machine; + m_options.assembly.inputLanguage = m_args.count(g_strYul) ? Input::Yul : (m_args.count(g_strStrictAssembly) ? Input::StrictAssembly : Input::Assembly); + m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); + m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); + + if (!m_args[g_strOptimizeRuns].defaulted()) + m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); + + if (m_args.count(g_strYulOptimizations)) + { + if (!m_options.optimizer.enabled) + { + serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; + return false; + } + + try + { + yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); + } + catch (yul::OptimizerException const& _exception) + { + serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; + return false; + } + + m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); + } + + if (m_args.count(g_strMachine)) + { + string machine = m_args[g_strMachine].as(); + if (machine == g_strEVM) + m_options.assembly.targetMachine = Machine::EVM; + else if (machine == g_strEwasm) + m_options.assembly.targetMachine = Machine::Ewasm; + else + { + serr() << "Invalid option for --" << g_strMachine << ": " << machine << endl; + return false; + } + } + if (m_options.assembly.targetMachine == Machine::Ewasm && m_options.assembly.inputLanguage == Input::StrictAssembly) + m_options.assembly.inputLanguage = Input::Ewasm; + if (m_args.count(g_strYulDialect)) + { + string dialect = m_args[g_strYulDialect].as(); + if (dialect == g_strEVM) + m_options.assembly.inputLanguage = Input::StrictAssembly; + else if (dialect == g_strEwasm) + { + m_options.assembly.inputLanguage = Input::Ewasm; + if (m_options.assembly.targetMachine != Machine::Ewasm) + { + serr() << "If you select Ewasm as --" << g_strYulDialect << ", "; + serr() << "--" << g_strMachine << " has to be Ewasm as well." << endl; + return false; + } + } + else + { + serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl; + return false; + } + } + if (m_options.optimizer.enabled && (m_options.assembly.inputLanguage != Input::StrictAssembly && m_options.assembly.inputLanguage != Input::Ewasm)) + { + serr() << + "Optimizer can only be used for strict assembly. Use --" << + g_strStrictAssembly << + "." << + endl; + return false; + } + if (m_options.assembly.targetMachine == Machine::Ewasm && m_options.assembly.inputLanguage != Input::StrictAssembly && m_options.assembly.inputLanguage != Input::Ewasm) + { + serr() << "The selected input language is not directly supported when targeting the Ewasm machine "; + serr() << "and automatic translation is not available." << endl; + return false; + } + serr() << + "Warning: Yul is still experimental. Please use the output with care." << + endl; + + return true; + } + else if (countEnabledOptions({g_strYulDialect, g_strMachine}) >= 1) + { + serr() << "--" << g_strYulDialect << " and --" << g_strMachine << " "; + serr() << "are only valid in assembly mode." << endl; + return false; + } + + if (m_options.input.mode == InputMode::Linker) + return true; + + if (m_args.count(g_strMetadataHash)) + { + string hashStr = m_args[g_strMetadataHash].as(); + if (hashStr == g_strIPFS) + m_options.metadata.hash = CompilerStack::MetadataHash::IPFS; + else if (hashStr == g_strSwarm) + m_options.metadata.hash = CompilerStack::MetadataHash::Bzzr1; + else if (hashStr == g_strNone) + m_options.metadata.hash = CompilerStack::MetadataHash::None; + else + { + serr() << "Invalid option for --" << g_strMetadataHash << ": " << hashStr << endl; + return false; + } + } + + if (m_args.count(g_strModelCheckerContracts)) + { + string contractsStr = m_args[g_strModelCheckerContracts].as(); + optional contracts = ModelCheckerContracts::fromString(contractsStr); + if (!contracts) + { + serr() << "Invalid option for --" << g_strModelCheckerContracts << ": " << contractsStr << endl; + return false; + } + m_options.modelChecker.settings.contracts = move(*contracts); + } + + if (m_args.count(g_strModelCheckerEngine)) + { + string engineStr = m_args[g_strModelCheckerEngine].as(); + optional engine = ModelCheckerEngine::fromString(engineStr); + if (!engine) + { + serr() << "Invalid option for --" << g_strModelCheckerEngine << ": " << engineStr << endl; + return false; + } + m_options.modelChecker.settings.engine = *engine; + } + + if (m_args.count(g_strModelCheckerShowUnproved)) + { + bool showUnproved = m_args[g_strModelCheckerShowUnproved].as(); + m_options.modelChecker.settings.showUnproved = showUnproved; + } + + if (m_args.count(g_strModelCheckerSolvers)) + { + string solversStr = m_args[g_strModelCheckerSolvers].as(); + optional solvers = smtutil::SMTSolverChoice::fromString(solversStr); + if (!solvers) + { + serr() << "Invalid option for --" << g_strModelCheckerSolvers << ": " << solversStr << endl; + return false; + } + m_options.modelChecker.settings.solvers = *solvers; + } + + if (m_args.count(g_strModelCheckerTargets)) + { + string targetsStr = m_args[g_strModelCheckerTargets].as(); + optional targets = ModelCheckerTargets::fromString(targetsStr); + if (!targets) + { + serr() << "Invalid option for --" << g_strModelCheckerTargets << ": " << targetsStr << endl; + return false; + } + m_options.modelChecker.settings.targets = *targets; + } + + if (m_args.count(g_strModelCheckerTimeout)) + m_options.modelChecker.settings.timeout = m_args[g_strModelCheckerTimeout].as(); + + m_options.metadata.literalSources = (m_args.count(g_strMetadataLiteral) > 0); + m_options.modelChecker.initialize = + m_args.count(g_strModelCheckerContracts) || + m_args.count(g_strModelCheckerEngine) || + m_args.count(g_strModelCheckerShowUnproved) || + m_args.count(g_strModelCheckerSolvers) || + m_args.count(g_strModelCheckerTargets) || + m_args.count(g_strModelCheckerTimeout); + m_options.output.experimentalViaIR = (m_args.count(g_strExperimentalViaIR) > 0); + if (!m_args[g_strOptimizeRuns].defaulted()) + m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); + + m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); + m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); + + OptimiserSettings settings = m_options.optimizer.enabled ? OptimiserSettings::standard() : OptimiserSettings::minimal(); + if (m_options.optimizer.noOptimizeYul) + settings.runYulOptimiser = false; + if (m_args.count(g_strYulOptimizations)) + { + if (!settings.runYulOptimiser) + { + serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; + return false; + } + + try + { + yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); + } + catch (yul::OptimizerException const& _exception) + { + serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; + return false; + } + + m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); + } + + if (m_options.input.mode == InputMode::Compiler) + m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 0); + + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + return true; +} + +bool CommandLineParser::parseCombinedJsonOption() +{ + if (!m_args.count(g_strCombinedJson)) + return true; + + set requests; + for (string const& item: boost::split(requests, m_args[g_strCombinedJson].as(), boost::is_any_of(","))) + if (!g_combinedJsonArgs.count(item)) + { + serr() << "Invalid option to --" << g_strCombinedJson << ": " << item << endl; + return false; + } + + m_options.compiler.combinedJsonRequests = CombinedJsonRequests{}; + m_options.compiler.combinedJsonRequests->abi = (requests.count(g_strAbi) > 0); + m_options.compiler.combinedJsonRequests->metadata = (requests.count("metadata") > 0); + m_options.compiler.combinedJsonRequests->binary = (requests.count(g_strBinary) > 0); + m_options.compiler.combinedJsonRequests->binaryRuntime = (requests.count(g_strBinaryRuntime) > 0); + m_options.compiler.combinedJsonRequests->opcodes = (requests.count(g_strOpcodes) > 0); + m_options.compiler.combinedJsonRequests->asm_ = (requests.count(g_strAsm) > 0); + m_options.compiler.combinedJsonRequests->storageLayout = (requests.count(g_strStorageLayout) > 0); + m_options.compiler.combinedJsonRequests->generatedSources = (requests.count(g_strGeneratedSources) > 0); + m_options.compiler.combinedJsonRequests->generatedSourcesRuntime = (requests.count(g_strGeneratedSourcesRuntime) > 0); + m_options.compiler.combinedJsonRequests->srcMap = (requests.count(g_strSrcMap) > 0); + m_options.compiler.combinedJsonRequests->srcMapRuntime = (requests.count(g_strSrcMapRuntime) > 0); + m_options.compiler.combinedJsonRequests->funDebug = (requests.count(g_strFunDebug) > 0); + m_options.compiler.combinedJsonRequests->funDebugRuntime = (requests.count(g_strFunDebugRuntime) > 0); + m_options.compiler.combinedJsonRequests->signatureHashes = (requests.count(g_strSignatureHashes) > 0); + m_options.compiler.combinedJsonRequests->natspecDev = (requests.count(g_strNatspecDev) > 0); + m_options.compiler.combinedJsonRequests->natspecUser = (requests.count(g_strNatspecUser) > 0); + m_options.compiler.combinedJsonRequests->ast = (requests.count(g_strAst) > 0); + + return true; +} + +size_t CommandLineParser::countEnabledOptions(vector const& _optionNames) const +{ + size_t count = 0; + for (string const& _option: _optionNames) + count += m_args.count(_option); + + return count; +} + +string CommandLineParser::joinOptionNames(vector const& _optionNames, string _separator) +{ + return joinHumanReadable( + _optionNames | ranges::views::transform([](string const& _option){ return "--" + _option; }), + _separator + ); +} + +} // namespace solidity::frontend diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h new file mode 100644 index 000000000..423dacb5f --- /dev/null +++ b/solc/CommandLineParser.h @@ -0,0 +1,245 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Validates and parses command-line options into an internal representation. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace solidity::frontend +{ + +enum class InputMode +{ + Compiler, + CompilerWithASTImport, + StandardJson, + Linker, + Assembler, +}; + +struct CompilerOutputs +{ + bool operator!=(CompilerOutputs const& _other) const noexcept { return !(*this == _other); } + bool operator==(CompilerOutputs const& _other) const noexcept; + + bool astCompactJson = false; + bool asm_ = false; + bool asmJson = false; + bool opcodes = false; + bool binary = false; + bool binaryRuntime = false; + bool abi = false; + bool ir = false; + bool irOptimized = false; + bool ewasm = false; + bool signatureHashes = false; + bool natspecUser = false; + bool natspecDev = false; + bool metadata = false; + bool storageLayout = false; +}; + +struct CombinedJsonRequests +{ + bool operator!=(CombinedJsonRequests const& _other) const noexcept { return !(*this == _other); } + bool operator==(CombinedJsonRequests const& _other) const noexcept; + + bool abi = false; + bool metadata = false; + bool binary = false; + bool binaryRuntime = false; + bool opcodes = false; + bool asm_ = false; + bool storageLayout = false; + bool generatedSources = false; + bool generatedSourcesRuntime = false; + bool srcMap = false; + bool srcMapRuntime = false; + bool funDebug = false; + bool funDebugRuntime = false; + bool signatureHashes = false; + bool natspecDev = false; + bool natspecUser = false; + bool ast = false; +}; + +struct CommandLineOptions +{ + bool operator==(CommandLineOptions const& _other) const noexcept; + bool operator!=(CommandLineOptions const& _other) const noexcept { return !(*this == _other); } + + + struct + { + InputMode mode = InputMode::Compiler; + std::set paths; + std::vector remappings; + bool addStdin = false; + boost::filesystem::path basePath = ""; + FileReader::FileSystemPathSet allowedDirectories; + bool ignoreMissingFiles = false; + bool errorRecovery = false; + } input; + + struct + { + boost::filesystem::path dir; + bool overwriteFiles = false; + langutil::EVMVersion evmVersion; + bool experimentalViaIR = false; + RevertStrings revertStrings = RevertStrings::Default; + CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; + } output; + + struct + { + yul::AssemblyStack::Machine targetMachine = yul::AssemblyStack::Machine::EVM; + yul::AssemblyStack::Language inputLanguage = yul::AssemblyStack::Language::StrictAssembly; + } assembly; + + struct + { + std::map libraries; // library name -> address + } linker; + + struct + { + util::JsonFormat json; + std::optional coloredOutput; + bool withErrorIds = false; + } formatting; + + struct + { + CompilerOutputs outputs; + bool estimateGas = false; + std::optional combinedJsonRequests; + } compiler; + + struct + { + CompilerStack::MetadataHash hash = CompilerStack::MetadataHash::IPFS; + bool literalSources = false; + } metadata; + + struct + { + bool enabled = false; + std::optional expectedExecutionsPerDeployment; + bool noOptimizeYul = false; + std::optional yulSteps; + } optimizer; + + struct + { + bool initialize = false; + ModelCheckerSettings settings; + } modelChecker; + +}; + +/// Parses the command-line arguments and produces a filled-out CommandLineOptions structure. +/// Validates provided values and prints error messages in case of errors. +/// +/// The class is also responsible for handling options that only result in printing informational +/// text, without the need to invoke the compiler - printing usage banner, version or license. +class CommandLineParser +{ +public: + explicit CommandLineParser(std::ostream& _sout, std::ostream& _serr): + m_sout(_sout), + m_serr(_serr) + {} + + /// Parses the command-line arguments and fills out the internal CommandLineOptions structure. + /// Performs validation and prints error messages. If requested, prints usage banner, version + /// or license. + /// @param interactiveTerminal specifies whether the terminal is taking input from the user. + /// This is used to determine whether to provide more user-friendly output in some situations. + /// E.g. whether to print help text when no arguments are provided. + /// @return true if there were no validation errors when parsing options and the + /// CommandLineOptions structure has been fully initialized. false if there were errors - in + /// this case CommandLineOptions may be only partially filled out. May also return false if + /// there is not further processing necessary and the program should just exit. + bool parse(int _argc, char const* const* _argv, bool interactiveTerminal); + + CommandLineOptions const& options() const { return m_options; } + + /// Returns true if the parser has written anything to any of its output streams. + bool hasOutput() const { return m_hasOutput; } + +private: + /// Parses the value supplied to --combined-json. + /// @return false if there are any validation errors, true otherwise. + bool parseCombinedJsonOption(); + + /// Parses the names of the input files, remappings for all modes except for Standard JSON. + /// Does not check if files actually exist. + /// @return false if there are any validation errors, true otherwise. + bool parseInputPathsAndRemappings(); + + /// Tries to read from the file @a _input or interprets @a _input literally if that fails. + /// It then tries to parse the contents and appends to m_options.libraries. + /// @return false if there are any validation errors, true otherwise. + bool parseLibraryOption(std::string const& _input); + + bool checkMutuallyExclusive(std::vector const& _optionNames); + [[noreturn]] void printVersionAndExit(); + [[noreturn]] void printLicenseAndExit(); + size_t countEnabledOptions(std::vector const& _optionNames) const; + static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); + + /// Returns the stream that should receive normal output. Sets m_hasOutput to true if the + /// stream has ever been used. + std::ostream& sout(); + + /// Returns the stream that should receive error output. Sets m_hasOutput to true if the + /// stream has ever been used. + std::ostream& serr(); + + std::ostream& m_sout; + std::ostream& m_serr; + bool m_hasOutput = false; + + CommandLineOptions m_options; + + /// Map of command-line arguments produced by boost::program_options. + /// Serves as input for filling out m_options. + boost::program_options::variables_map m_args; +}; + +} diff --git a/solc/main.cpp b/solc/main.cpp index ae56066ed..9eefc1afd 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -54,9 +54,11 @@ static void setDefaultOrCLocale() int main(int argc, char** argv) { setDefaultOrCLocale(); - solidity::frontend::CommandLineInterface cli; + solidity::frontend::CommandLineInterface cli(cin, cout, cerr); if (!cli.parseArguments(argc, argv)) return 1; + if (!cli.readInputFiles()) + return 1; if (!cli.processInput()) return 1; bool success = false; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2dc7a50dd..a70a92639 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -126,6 +126,8 @@ set(libyul_sources libyul/Common.cpp libyul/Common.h libyul/CompilabilityChecker.cpp + libyul/ControlFlowGraphTest.cpp + libyul/ControlFlowGraphTest.h libyul/EVMCodeTransformTest.cpp libyul/EVMCodeTransformTest.h libyul/EwasmTranslationTest.cpp @@ -149,6 +151,14 @@ set(libyul_sources ) detect_stray_source_files("${libyul_sources}" "libyul/") +set(solcli_sources + solc/Common.cpp + solc/Common.h + solc/CommandLineInterface.cpp + solc/CommandLineParser.cpp +) +detect_stray_source_files("${solcli_sources}" "solc/") + set(yul_phaser_sources yulPhaser/TestHelpers.h yulPhaser/TestHelpers.cpp @@ -177,9 +187,10 @@ add_executable(soltest ${sources} ${libyul_sources} ${libsolidity_sources} ${libsolidity_util_sources} + ${solcli_sources} ${yul_phaser_sources} ) -target_link_libraries(soltest PRIVATE libsolc yul solidity smtutil solutil phaser Boost::boost yulInterpreter evmasm Boost::filesystem Boost::program_options Boost::unit_test_framework evmc) +target_link_libraries(soltest PRIVATE solcli libsolc yul solidity smtutil solutil phaser Boost::boost yulInterpreter evmasm Boost::filesystem Boost::program_options Boost::unit_test_framework evmc) # Special compilation flag for Visual Studio (version 2019 at least affected) diff --git a/test/Common.h b/test/Common.h index 4dfaeaed5..c8a863d8c 100644 --- a/test/Common.h +++ b/test/Common.h @@ -31,19 +31,19 @@ namespace solidity::test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-windows-amd64.zip"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-windows-amd64.zip"; static constexpr auto heraFilename = "hera.dll"; static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.2-evmc8.tar.gz"; #elif defined(__APPLE__) static constexpr auto evmoneFilename = "libevmone.dylib"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-darwin-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-darwin-x86_64.tar.gz"; static constexpr auto heraFilename = "libhera.dylib"; -static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-darwin-x86_64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-linux-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-linux-x86_64.tar.gz"; static constexpr auto heraFilename = "libhera.so"; -static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-linux-x86_64.tar.gz"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-linux-x86_64.tar.gz"; #endif struct ConfigException : public util::Exception {}; diff --git a/test/FilesystemUtils.cpp b/test/FilesystemUtils.cpp index 08d484504..cdad58e62 100644 --- a/test/FilesystemUtils.cpp +++ b/test/FilesystemUtils.cpp @@ -24,6 +24,21 @@ using namespace std; using namespace solidity; using namespace solidity::test; +void solidity::test::createFilesWithParentDirs(set const& _paths, string const& _content) +{ + for (boost::filesystem::path const& path: _paths) + { + if (!path.parent_path().empty()) + boost::filesystem::create_directories(path.parent_path()); + + ofstream newFile(path.string()); + newFile << _content; + + if (newFile.fail() || !boost::filesystem::exists(path)) + BOOST_THROW_EXCEPTION(runtime_error("Failed to create an empty file: \"" + path.string() + "\".")); + } +} + void solidity::test::createFileWithContent(boost::filesystem::path const& _path, string const& content) { if (boost::filesystem::is_regular_file(_path)) diff --git a/test/FilesystemUtils.h b/test/FilesystemUtils.h index f8ad32044..291188114 100644 --- a/test/FilesystemUtils.h +++ b/test/FilesystemUtils.h @@ -23,11 +23,16 @@ #include +#include #include namespace solidity::test { +/// Creates all the specified files and fills them with the specifiedcontent. Creates their parent +/// directories if they do not exist. Throws an exception if any part of the operation does not succeed. +void createFilesWithParentDirs(std::set const& _paths, std::string const& _content = ""); + /// Creates a file with the exact content specified in the second argument. /// Throws an exception if the file already exists or if the parent directory of the file does not. void createFileWithContent(boost::filesystem::path const& _path, std::string const& content); diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 68ca721a5..4ffdf5a74 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -55,20 +56,21 @@ struct Testsuite Testsuite const g_interactiveTestsuites[] = { /* Title Path Subpath SMT NeedsVM Creator function */ - {"Ewasm Translation", "libyul", "ewasmTranslationTests",false,false, &yul::test::EwasmTranslationTest::create}, - {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, - {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, - {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, - {"Function Side Effects","libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, - {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, - {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, - {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, - {"Error Recovery", "libsolidity", "errorRecoveryTests", false, false, &SyntaxTest::createErrorRecovery}, - {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, - {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, - {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, - {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, - {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create} + {"Ewasm Translation", "libyul", "ewasmTranslationTests", false, false, &yul::test::EwasmTranslationTest::create}, + {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, + {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, + {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, + {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create}, + {"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, + {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, + {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, + {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, + {"Error Recovery", "libsolidity", "errorRecoveryTests", false, false, &SyntaxTest::createErrorRecovery}, + {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, + {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, + {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, + {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, + {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create} }; } diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 4d99961ca..582a7acde 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -37,11 +37,8 @@ source "${REPO_ROOT}/scripts/common.sh" # shellcheck source=scripts/common_cmdline.sh source "${REPO_ROOT}/scripts/common_cmdline.sh" -(( $# <= 1 )) || { printError "Too many arguments"; exit 1; } -(( $# == 0 )) || [[ $1 == '--update' ]] || { printError "Invalid argument: '$1'"; exit 1; } - AUTOUPDATE=false -[[ $1 == --update ]] && AUTOUPDATE=true +[[ $1 == --update ]] && AUTOUPDATE=true && shift case "$OSTYPE" in msys) @@ -141,9 +138,16 @@ function test_solc_behaviour() if [[ " ${solc_args[*]} " == *" --standard-json "* ]] then - sed -i.bak -e 's/{[^{]*Warning: This is a pre-release compiler version[^}]*},\{0,1\}//' "$stdout_path" + python3 - < }] +json = re.sub(r"\"errors\":\s*\[\s*\],?\s*","",json) # Remove "errors" array if it's not empty +json = re.sub("\n\\s+\n", "\n\n", json) # Remove any leftover trailing whitespace +open("$stdout_path", "w").write(json) +EOF sed -i.bak -E -e 's/ Consider adding \\"pragma solidity \^[0-9.]*;\\"//g' "$stdout_path" - sed -i.bak -e 's/"errors":\[\],\{0,1\}//' "$stdout_path" sed -i.bak -E -e 's/\"opcodes\":\"[^"]+\"/\"opcodes\":\"\"/g' "$stdout_path" sed -i.bak -E -e 's/\"sourceMap\":\"[0-9:;-]+\"/\"sourceMap\":\"\"/g' "$stdout_path" @@ -161,6 +165,7 @@ function test_solc_behaviour() # Replace escaped newlines by actual newlines for readability # shellcheck disable=SC1003 sed -i.bak -E -e 's/\\n/\'$'\n/g' "$stdout_path" + sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1/' "$stdout_path" rm "$stdout_path.bak" else sed -i.bak -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" @@ -287,8 +292,21 @@ test_solc_behaviour "${0}" "ctx:=/some/remapping/target" "" "" 1 "" "Invalid rem printTask "Running general commandline tests..." ( cd "$REPO_ROOT"/test/cmdlineTests/ - for tdir in */ + for tdir in ${*:-*/} do + if ! [[ -d $tdir ]]; then + if [[ $tdir =~ ^--.*$ ]]; then + if [[ $tdir == "--update" ]]; then + printError "The --update option must be given before any positional arguments." + else + printError "Invalid option: $tdir." + fi + else + printError "Test directory not found: $tdir" + fi + exit 1 + fi + printTask " - ${tdir}" # Strip trailing slash from $tdir. @@ -351,7 +369,7 @@ printTask "Compiling various other contracts and libraries..." do echo " - $dir" cd "$dir" - compileFull -w ./*.sol ./*/*.sol + compileFull --expect-warnings ./*.sol ./*/*.sol cd .. done ) @@ -361,10 +379,10 @@ SOLTMPDIR=$(mktemp -d) ( set -e cd "$SOLTMPDIR" - "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ developmentVersion=$("$REPO_ROOT/scripts/get_version.sh") - for f in *.sol + for f in *.yul *.sol do # The contributors guide uses syntax tests, but we cannot # really handle them here. @@ -379,15 +397,15 @@ SOLTMPDIR=$(mktemp -d) # are used (in the style guide) if grep -E "This will not compile|import \"" "$f" >/dev/null then - opts=(-e) + opts=(--expect-errors) fi if grep "This will report a warning" "$f" >/dev/null then - opts+=(-w) + opts+=(--expect-warnings) fi if grep "This may report a warning" "$f" >/dev/null then - opts+=(-o) + opts+=(--ignore-warnings) fi # Disable the version pragma in code snippets that only work with the current development version. @@ -510,7 +528,7 @@ SOLTMPDIR=$(mktemp -d) set -e cd "$SOLTMPDIR" "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ - "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --quiet --input-files echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --without-optimizer --quiet --input-files diff --git a/test/cmdlineTests/combined_json_generated_sources/output b/test/cmdlineTests/combined_json_generated_sources/output index 9165832ca..3cf5cfb68 100644 --- a/test/cmdlineTests/combined_json_generated_sources/output +++ b/test/cmdlineTests/combined_json_generated_sources/output @@ -103,7 +103,7 @@ "src": "127:35:1" }, "nodeType": "YulIf", - "src": "124:2:1" + "src": "124:122:1" }, { "nodeType": "YulAssignment", @@ -188,7 +188,7 @@ "src": "297:30:1" }, "nodeType": "YulIf", - "src": "294:2:1" + "src": "294:117:1" }, { "nodeType": "YulAssignment", @@ -316,7 +316,7 @@ "src": "461:41:1" }, "nodeType": "YulIf", - "src": "458:2:1" + "src": "458:128:1" } ] }, @@ -431,7 +431,7 @@ "src": "712:32:1" }, "nodeType": "YulIf", - "src": "709:2:1" + "src": "709:119:1" }, { "nodeType": "YulBlock", @@ -542,7 +542,7 @@ "src": "914:30:1" }, "nodeType": "YulIf", - "src": "911:2:1" + "src": "911:117:1" }, { "nodeType": "YulAssignment", diff --git a/test/cmdlineTests/constant_optimizer_yul/output b/test/cmdlineTests/constant_optimizer_yul/output index 9ef151e3b..7fdb98670 100644 --- a/test/cmdlineTests/constant_optimizer_yul/output +++ b/test/cmdlineTests/constant_optimizer_yul/output @@ -6,12 +6,16 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"constant_optimizer_yul/input.sol", 1:"#utility.yul" object "C_12" { code { { + /// @src 0:61:418 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:103:238 sstore(0, shl(180, 1)) + /// @src 0:61:418 let _1 := datasize("C_12_deployed") codecopy(128, dataoffset("C_12_deployed"), _1) return(128, _1) @@ -20,9 +24,12 @@ object "C_12" { object "C_12_deployed" { code { { + /// @src 0:61:418 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:279:410 sstore(0, 0x1000000000000000000000000000000000000000000000) + /// @src 0:61:418 stop() } } diff --git a/test/cmdlineTests/exp_base_literal/output b/test/cmdlineTests/exp_base_literal/output index eaf776545..b7aaea4ba 100644 --- a/test/cmdlineTests/exp_base_literal/output +++ b/test/cmdlineTests/exp_base_literal/output @@ -7,9 +7,10 @@ IR: *=====================================================*/ +/// @use-src 0:"exp_base_literal/input.sol", 1:"#utility.yul" object "C_81" { code { - /// @src 0:82,370 + /// @src 0:82:370 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,11 +25,13 @@ object "C_81" { memPtr := mload(64) } + /// @src 0:82:370 function constructor_C_81() { - /// @src 0:82,370 + /// @src 0:82:370 } + /// @src 0:82:370 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -37,7 +40,7 @@ object "C_81" { } object "C_81_deployed" { code { - /// @src 0:82,370 + /// @src 0:82:370 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -211,123 +214,123 @@ object "C_81" { converted := cleanup_t_int256(value) } + /// @src 0:96:368 function fun_f_80(var_a_4, var_b_6, var_c_8, var_d_10) -> var__13, var__15, var__17, var__19 { - /// @src 0:96,368 - /// @src 0:160,164 + /// @src 0:160:164 let zero_t_uint256_1 := zero_value_for_split_t_uint256() var__13 := zero_t_uint256_1 - /// @src 0:166,169 + /// @src 0:166:169 let zero_t_int256_2 := zero_value_for_split_t_int256() var__15 := zero_t_int256_2 - /// @src 0:171,175 + /// @src 0:171:175 let zero_t_uint256_3 := zero_value_for_split_t_uint256() var__17 := zero_t_uint256_3 - /// @src 0:177,181 + /// @src 0:177:181 let zero_t_uint256_4 := zero_value_for_split_t_uint256() var__19 := zero_t_uint256_4 - /// @src 0:196,197 + /// @src 0:196:197 let expr_23 := 0x02 - /// @src 0:199,200 + /// @src 0:199:200 let _5 := var_a_4 let expr_24 := _5 - /// @src 0:196,200 + /// @src 0:196:200 let _6 := convert_t_rational_2_by_1_to_t_uint256(expr_23) let expr_25 := checked_exp_t_rational_2_by_1_t_uint256(expr_24) - /// @src 0:187,200 + /// @src 0:187:200 let var_w_22 := expr_25 - /// @src 0:213,215 + /// @src 0:213:215 let expr_30 := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe - /// @src 0:212,216 + /// @src 0:212:216 let expr_31 := expr_30 - /// @src 0:218,219 + /// @src 0:218:219 let _7 := var_b_6 let expr_32 := _7 - /// @src 0:212,219 + /// @src 0:212:219 let _8 := convert_t_rational_minus_2_by_1_to_t_int256(expr_31) let expr_33 := checked_exp_t_rational_minus_2_by_1_t_uint256(expr_32) - /// @src 0:204,219 + /// @src 0:204:219 let var_x_28 := expr_33 - /// @src 0:232,234 + /// @src 0:232:234 let expr_37 := 0x0a - /// @src 0:236,237 + /// @src 0:236:237 let _9 := var_c_8 let expr_38 := _9 - /// @src 0:232,237 + /// @src 0:232:237 let _10 := convert_t_rational_10_by_1_to_t_uint256(expr_37) let expr_39 := checked_exp_t_rational_10_by_1_t_uint256(expr_38) - /// @src 0:223,237 + /// @src 0:223:237 let var_y_36 := expr_39 - /// @src 0:251,260 + /// @src 0:251:260 let expr_47 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - /// @src 0:250,262 + /// @src 0:250:262 let expr_48 := expr_47 - /// @src 0:264,265 + /// @src 0:264:265 let _11 := var_d_10 let expr_49 := _11 - /// @src 0:250,265 + /// @src 0:250:265 let _12 := convert_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_to_t_uint256(expr_48) let expr_50 := checked_exp_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_t_uint256(expr_49) - /// @src 0:241,265 + /// @src 0:241:265 let var_z_42 := expr_50 - /// @src 0:308,309 + /// @src 0:308:309 let expr_53 := 0x00 - /// @src 0:307,310 + /// @src 0:307:310 let expr_54 := expr_53 - /// @src 0:312,313 + /// @src 0:312:313 let _13 := var_a_4 let expr_55 := _13 - /// @src 0:307,313 + /// @src 0:307:313 let _14 := convert_t_rational_0_by_1_to_t_uint256(expr_54) let expr_56 := checked_exp_t_rational_0_by_1_t_uint256(expr_55) - /// @src 0:303,313 + /// @src 0:303:313 var_w_22 := expr_56 let expr_57 := expr_56 - /// @src 0:322,324 + /// @src 0:322:324 let expr_61 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - /// @src 0:321,325 + /// @src 0:321:325 let expr_62 := expr_61 - /// @src 0:327,328 + /// @src 0:327:328 let _15 := var_b_6 let expr_63 := _15 - /// @src 0:321,328 + /// @src 0:321:328 let _16 := convert_t_rational_minus_1_by_1_to_t_int256(expr_62) let expr_64 := checked_exp_t_rational_minus_1_by_1_t_uint256(expr_63) - /// @src 0:317,328 + /// @src 0:317:328 var_x_28 := expr_64 let expr_65 := expr_64 - /// @src 0:336,337 + /// @src 0:336:337 let expr_68 := 0x01 - /// @src 0:339,340 + /// @src 0:339:340 let _17 := var_c_8 let expr_69 := _17 - /// @src 0:336,340 + /// @src 0:336:340 let _18 := convert_t_rational_1_by_1_to_t_uint256(expr_68) let expr_70 := checked_exp_t_rational_1_by_1_t_uint256(expr_69) - /// @src 0:332,340 + /// @src 0:332:340 var_y_36 := expr_70 let expr_71 := expr_70 - /// @src 0:353,354 + /// @src 0:353:354 let _19 := var_w_22 let expr_73 := _19 - /// @src 0:352,364 + /// @src 0:352:364 let expr_77_component_1 := expr_73 - /// @src 0:356,357 + /// @src 0:356:357 let _20 := var_x_28 let expr_74 := _20 - /// @src 0:352,364 + /// @src 0:352:364 let expr_77_component_2 := expr_74 - /// @src 0:359,360 + /// @src 0:359:360 let _21 := var_y_36 let expr_75 := _21 - /// @src 0:352,364 + /// @src 0:352:364 let expr_77_component_3 := expr_75 - /// @src 0:362,363 + /// @src 0:362:363 let _22 := var_z_42 let expr_76 := _22 - /// @src 0:352,364 + /// @src 0:352:364 let expr_77_component_4 := expr_76 - /// @src 0:345,364 + /// @src 0:345:364 var__13 := expr_77_component_1 var__15 := expr_77_component_2 var__17 := expr_77_component_3 @@ -335,6 +338,7 @@ object "C_81" { leave } + /// @src 0:82:370 function panic_error_0x11() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output index ad951774e..1af925406 100644 --- a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output +++ b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol", 1:"#utility.yul" object "C_7" { code { { + /// @src 0:82:117 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_7_deployed") @@ -19,6 +21,7 @@ object "C_7" { object "C_7_deployed" { code { { + /// @src 0:82:117 mstore(64, 128) revert(0, 0) } @@ -35,9 +38,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol", 1:"#utility.yul" object "D_10" { code { { + /// @src 0:118:137 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_10_deployed") @@ -48,6 +53,7 @@ object "D_10" { object "D_10_deployed" { code { { + /// @src 0:118:137 mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/ir_compiler_subobjects/output b/test/cmdlineTests/ir_compiler_subobjects/output index c10198bbb..ba93e6886 100644 --- a/test/cmdlineTests/ir_compiler_subobjects/output +++ b/test/cmdlineTests/ir_compiler_subobjects/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_compiler_subobjects/input.sol", 1:"#utility.yul" object "C_3" { code { { + /// @src 0:82:95 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -19,6 +21,7 @@ object "C_3" { object "C_3_deployed" { code { { + /// @src 0:82:95 mstore(64, 128) revert(0, 0) } @@ -35,9 +38,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_compiler_subobjects/input.sol", 1:"#utility.yul" object "D_16" { code { { + /// @src 0:96:165 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_16_deployed") @@ -48,6 +53,7 @@ object "D_16" { object "D_16_deployed" { code { { + /// @src 0:96:165 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -56,17 +62,22 @@ object "D_16" { { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + /// @src 0:149:156 let _2 := datasize("C_3") - let _3 := add(128, _2) - if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) + let _3 := add(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:96:165 */ 128)) + /// @src 0:149:156 { + /// @src 0:96:165 mstore(_1, shl(224, 0x4e487b71)) mstore(4, 0x41) revert(_1, 0x24) } - datacopy(128, dataoffset("C_3"), _2) - if iszero(create(_1, 128, _2)) + /// @src 0:149:156 + datacopy(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ dataoffset("C_3"), _2) + if iszero(create(/** @src 0:96:165 */ _1, 128, /** @src 0:149:156 */ _2)) { + /// @src 0:96:165 let pos := mload(64) returndatacopy(pos, _1, returndatasize()) revert(pos, returndatasize()) @@ -80,6 +91,7 @@ object "D_16" { object "C_3" { code { { + /// @src 0:82:95 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -90,6 +102,7 @@ object "D_16" { object "C_3_deployed" { code { { + /// @src 0:82:95 mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output index 4efea77ce..89ee1b5d9 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_with_assembly_no_memoryguard_creation/input.sol", 1:"#utility.yul" object "D_12" { code { { + /// @src 0:82:161 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_12_deployed") @@ -19,6 +21,7 @@ object "D_12" { object "D_12_deployed" { code { { + /// @src 0:82:161 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output index 9b39c93e6..1fcf2fcc8 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"ir_with_assembly_no_memoryguard_runtime/input.sol", 1:"#utility.yul" object "D_8" { code { { + /// @src 0:82:153 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_8_deployed") @@ -19,6 +21,7 @@ object "D_8" { object "D_8_deployed" { code { { + /// @src 0:82:153 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { diff --git a/test/cmdlineTests/keccak_optimization_deploy_code/output b/test/cmdlineTests/keccak_optimization_deploy_code/output index 907b08b70..83c7c3929 100644 --- a/test/cmdlineTests/keccak_optimization_deploy_code/output +++ b/test/cmdlineTests/keccak_optimization_deploy_code/output @@ -6,13 +6,17 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"keccak_optimization_deploy_code/input.sol", 1:"#utility.yul" object "C_12" { code { { + /// @src 0:62:463 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:103:275 mstore(0, 100) sstore(0, keccak256(0, 32)) + /// @src 0:62:463 let _1 := datasize("C_12_deployed") codecopy(128, dataoffset("C_12_deployed"), _1) return(128, _1) @@ -21,10 +25,13 @@ object "C_12" { object "C_12_deployed" { code { { + /// @src 0:62:463 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:317:454 mstore(0, 100) sstore(0, 17385872270140913825666367956517731270094621555228275961425792378517567244498) + /// @src 0:62:463 stop() } } diff --git a/test/cmdlineTests/keccak_optimization_low_runs/output b/test/cmdlineTests/keccak_optimization_low_runs/output index 99af8c821..203600cfa 100644 --- a/test/cmdlineTests/keccak_optimization_low_runs/output +++ b/test/cmdlineTests/keccak_optimization_low_runs/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"keccak_optimization_low_runs/input.sol", 1:"#utility.yul" object "C_7" { code { { + /// @src 0:62:285 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_7_deployed") @@ -19,10 +21,13 @@ object "C_7" { object "C_7_deployed" { code { { + /// @src 0:62:285 mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:109:277 mstore(0, 100) sstore(0, keccak256(0, 32)) + /// @src 0:62:285 stop() } } diff --git a/test/cmdlineTests/linking_strict_assembly/output b/test/cmdlineTests/linking_strict_assembly/output index 8a58b94e4..cb5e7bf96 100644 --- a/test/cmdlineTests/linking_strict_assembly/output +++ b/test/cmdlineTests/linking_strict_assembly/output @@ -14,6 +14,7 @@ Binary representation: 7312345678901234567890123456789012345678908060005550 Text representation: + /* "linking_strict_assembly/input.yul":44:79 */ linkerSymbol("f919ba91ac99f96129544b80b9516b27a80e376b9dc693819d0b18b7e0395612") /* "linking_strict_assembly/input.yul":98:102 */ dup1 diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output index 871bacf2e..5d5b87c67 100644 --- a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output @@ -14,6 +14,7 @@ Binary representation: 7312345678901234567890123456789012345678908060005550 Text representation: + /* "linking_strict_assembly_no_file_name_in_link_reference/input.yul":44:61 */ linkerSymbol("8aa64f937099b65a4febc243a5ae0f2d6416bb9e473c30dd29c1ee498fb7c5a8") /* "linking_strict_assembly_no_file_name_in_link_reference/input.yul":80:84 */ dup1 diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output index eb74885ee..521d26bfd 100644 --- a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output @@ -16,8 +16,9 @@ Binary representation: 73111111111111111111111111111111111111111173222222222222222222222222222222222222222281600055806001555050 Text representation: + /* "linking_strict_assembly_same_library_name_different_files/input.yul":45:75 */ linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec") - /* "linking_strict_assembly_same_library_name_different_files/input.yul":32:75 */ + /* "linking_strict_assembly_same_library_name_different_files/input.yul":97:127 */ linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f") /* "linking_strict_assembly_same_library_name_different_files/input.yul":146:151 */ dup2 diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output index b5a44af48..b161a93f8 100644 --- a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output @@ -16,8 +16,9 @@ Binary representation: 73123456789012345678901234567890123456789073__$c3523432985587641d17c68161d2f700c5$__81600055806001555050 Text representation: + /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":45:75 */ linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec") - /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":32:75 */ + /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":97:127 */ linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f") /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":146:151 */ dup2 diff --git a/test/cmdlineTests/linking_strict_assembly_unresolved_references/output b/test/cmdlineTests/linking_strict_assembly_unresolved_references/output index f927894ce..59acd6ae2 100644 --- a/test/cmdlineTests/linking_strict_assembly_unresolved_references/output +++ b/test/cmdlineTests/linking_strict_assembly_unresolved_references/output @@ -16,8 +16,9 @@ Binary representation: 73123456789012345678901234567890123456789073__$fb58009a6b1ecea3b9d99bedd645df4ec3$__81600055806001555050 Text representation: + /* "linking_strict_assembly_unresolved_references/input.yul":45:81 */ linkerSymbol("05b0326038374a21e0895480a58bda0768cdcc04c8d18f154362d1ca5223d245") - /* "linking_strict_assembly_unresolved_references/input.yul":32:81 */ + /* "linking_strict_assembly_unresolved_references/input.yul":103:139 */ linkerSymbol("fb58009a6b1ecea3b9d99bedd645df4ec308f17bc0087e5f39d078f77f809177") /* "linking_strict_assembly_unresolved_references/input.yul":158:163 */ dup2 diff --git a/test/cmdlineTests/model_checker_show_unproved_default_all_engines/args b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/args new file mode 100644 index 000000000..5aeb1490e --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/args @@ -0,0 +1 @@ +--model-checker-engine all diff --git a/test/cmdlineTests/model_checker_show_unproved_default_all_engines/err b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/err new file mode 100644 index 000000000..45c38ef35 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/err @@ -0,0 +1,3 @@ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_default_all_engines/input.sol b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_all_engines/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_default_bmc/args b/test/cmdlineTests/model_checker_show_unproved_default_bmc/args new file mode 100644 index 000000000..549f20236 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_bmc/args @@ -0,0 +1 @@ +--model-checker-engine bmc diff --git a/test/cmdlineTests/model_checker_show_unproved_default_bmc/err b/test/cmdlineTests/model_checker_show_unproved_default_bmc/err new file mode 100644 index 000000000..f8d0af2f4 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_bmc/err @@ -0,0 +1 @@ +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_default_bmc/input.sol b/test/cmdlineTests/model_checker_show_unproved_default_bmc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_bmc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_default_chc/args b/test/cmdlineTests/model_checker_show_unproved_default_chc/args new file mode 100644 index 000000000..7458a47d3 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc diff --git a/test/cmdlineTests/model_checker_show_unproved_default_chc/err b/test/cmdlineTests/model_checker_show_unproved_default_chc/err new file mode 100644 index 000000000..77083185f --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_chc/err @@ -0,0 +1 @@ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_default_chc/input.sol b/test/cmdlineTests/model_checker_show_unproved_default_chc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_default_chc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_false_all_engines/args b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/args new file mode 100644 index 000000000..5f6883c76 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-show-unproved false diff --git a/test/cmdlineTests/model_checker_show_unproved_false_all_engines/err b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/err new file mode 100644 index 000000000..45c38ef35 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/err @@ -0,0 +1,3 @@ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_false_all_engines/input.sol b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_all_engines/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_false_bmc/args b/test/cmdlineTests/model_checker_show_unproved_false_bmc/args new file mode 100644 index 000000000..40fcde62b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_bmc/args @@ -0,0 +1 @@ +--model-checker-engine bmc --model-checker-show-unproved false diff --git a/test/cmdlineTests/model_checker_show_unproved_false_bmc/err b/test/cmdlineTests/model_checker_show_unproved_false_bmc/err new file mode 100644 index 000000000..f8d0af2f4 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_bmc/err @@ -0,0 +1 @@ +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_false_bmc/input.sol b/test/cmdlineTests/model_checker_show_unproved_false_bmc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_bmc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_false_chc/args b/test/cmdlineTests/model_checker_show_unproved_false_chc/args new file mode 100644 index 000000000..c14333f44 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-show-unproved false diff --git a/test/cmdlineTests/model_checker_show_unproved_false_chc/err b/test/cmdlineTests/model_checker_show_unproved_false_chc/err new file mode 100644 index 000000000..77083185f --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_chc/err @@ -0,0 +1 @@ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_show_unproved_false_chc/input.sol b/test/cmdlineTests/model_checker_show_unproved_false_chc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_false_chc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_true_all_engines/args b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/args new file mode 100644 index 000000000..4ccfd2e96 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-show-unproved true diff --git a/test/cmdlineTests/model_checker_show_unproved_true_all_engines/err b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/err new file mode 100644 index 000000000..83864075d --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation might happen here. + --> model_checker_show_unproved_true_all_engines/input.sol:10:9: + | +10 | assert(s.x > 0); + | ^^^^^^^^^^^^^^^ + +Warning: BMC: Assertion violation might happen here. + --> model_checker_show_unproved_true_all_engines/input.sol:10:9: + | +10 | assert(s.x > 0); + | ^^^^^^^^^^^^^^^ +Note: diff --git a/test/cmdlineTests/model_checker_show_unproved_true_all_engines/input.sol b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_all_engines/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_true_bmc/args b/test/cmdlineTests/model_checker_show_unproved_true_bmc/args new file mode 100644 index 000000000..79cae9260 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_bmc/args @@ -0,0 +1 @@ +--model-checker-engine bmc --model-checker-show-unproved true diff --git a/test/cmdlineTests/model_checker_show_unproved_true_bmc/err b/test/cmdlineTests/model_checker_show_unproved_true_bmc/err new file mode 100644 index 000000000..84ada7873 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_bmc/err @@ -0,0 +1,6 @@ +Warning: BMC: Assertion violation might happen here. + --> model_checker_show_unproved_true_bmc/input.sol:10:9: + | +10 | assert(s.x > 0); + | ^^^^^^^^^^^^^^^ +Note: diff --git a/test/cmdlineTests/model_checker_show_unproved_true_bmc/input.sol b/test/cmdlineTests/model_checker_show_unproved_true_bmc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_bmc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_true_chc/args b/test/cmdlineTests/model_checker_show_unproved_true_chc/args new file mode 100644 index 000000000..f992f4d0c --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-show-unproved true diff --git a/test/cmdlineTests/model_checker_show_unproved_true_chc/err b/test/cmdlineTests/model_checker_show_unproved_true_chc/err new file mode 100644 index 000000000..8274f4b46 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_chc/err @@ -0,0 +1,5 @@ +Warning: CHC: Assertion violation might happen here. + --> model_checker_show_unproved_true_chc/input.sol:10:9: + | +10 | assert(s.x > 0); + | ^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_show_unproved_true_chc/input.sol b/test/cmdlineTests/model_checker_show_unproved_true_chc/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_true_chc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/args b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/args new file mode 100644 index 000000000..b20188349 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-show-unproved aaa diff --git a/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/err b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/err new file mode 100644 index 000000000..9ece92874 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/err @@ -0,0 +1 @@ +the argument ('aaa') for option '--model-checker-show-unproved' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false' diff --git a/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/exit b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/input.sol b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/input.sol new file mode 100644 index 000000000..567c9cd2b --- /dev/null +++ b/test/cmdlineTests/model_checker_show_unproved_wrong_all_engines/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} diff --git a/test/cmdlineTests/model_checker_solvers_all/args b/test/cmdlineTests/model_checker_solvers_all/args new file mode 100644 index 000000000..4f09ccbe9 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers all diff --git a/test/cmdlineTests/model_checker_solvers_all/err b/test/cmdlineTests/model_checker_solvers_all/err new file mode 100644 index 000000000..3db918e25 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +test.constructor() +test.f(0) + --> model_checker_solvers_all/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_solvers_all/input.sol b/test/cmdlineTests/model_checker_solvers_all/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_all_implicit/args b/test/cmdlineTests/model_checker_solvers_all_implicit/args new file mode 100644 index 000000000..5aeb1490e --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all_implicit/args @@ -0,0 +1 @@ +--model-checker-engine all diff --git a/test/cmdlineTests/model_checker_solvers_all_implicit/err b/test/cmdlineTests/model_checker_solvers_all_implicit/err new file mode 100644 index 000000000..7fffab4ef --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all_implicit/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +test.constructor() +test.f(0) + --> model_checker_solvers_all_implicit/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_solvers_all_implicit/input.sol b/test/cmdlineTests/model_checker_solvers_all_implicit/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_all_implicit/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_smtlib2/args b/test/cmdlineTests/model_checker_solvers_smtlib2/args new file mode 100644 index 000000000..3d90e9161 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_smtlib2/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers smtlib2 diff --git a/test/cmdlineTests/model_checker_solvers_smtlib2/err b/test/cmdlineTests/model_checker_solvers_smtlib2/err new file mode 100644 index 000000000..9a156ff90 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_smtlib2/err @@ -0,0 +1,3 @@ +Warning: CHC analysis was not possible. No Horn solver was available. None of the installed solvers was enabled. + +Warning: BMC analysis was not possible. No SMT solver (Z3 or CVC4) was available. None of the installed solvers was enabled. diff --git a/test/cmdlineTests/model_checker_solvers_smtlib2/input.sol b/test/cmdlineTests/model_checker_solvers_smtlib2/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_smtlib2/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_wrong/args b/test/cmdlineTests/model_checker_solvers_wrong/args new file mode 100644 index 000000000..542502ee3 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers ultraSolver diff --git a/test/cmdlineTests/model_checker_solvers_wrong/err b/test/cmdlineTests/model_checker_solvers_wrong/err new file mode 100644 index 000000000..bf2df0a70 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong/err @@ -0,0 +1 @@ +Invalid option for --model-checker-solvers: ultraSolver diff --git a/test/cmdlineTests/model_checker_solvers_wrong/exit b/test/cmdlineTests/model_checker_solvers_wrong/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/model_checker_solvers_wrong/input.sol b/test/cmdlineTests/model_checker_solvers_wrong/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_wrong2/args b/test/cmdlineTests/model_checker_solvers_wrong2/args new file mode 100644 index 000000000..a20c99800 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong2/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers z3, smtlib2 diff --git a/test/cmdlineTests/model_checker_solvers_wrong2/err b/test/cmdlineTests/model_checker_solvers_wrong2/err new file mode 100644 index 000000000..1bdd53068 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong2/err @@ -0,0 +1 @@ +"smtlib2" is not found. diff --git a/test/cmdlineTests/model_checker_solvers_wrong2/exit b/test/cmdlineTests/model_checker_solvers_wrong2/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong2/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/model_checker_solvers_wrong2/input.sol b/test/cmdlineTests/model_checker_solvers_wrong2/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_wrong2/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_z3/args b/test/cmdlineTests/model_checker_solvers_z3/args new file mode 100644 index 000000000..ff27293ea --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers z3 diff --git a/test/cmdlineTests/model_checker_solvers_z3/err b/test/cmdlineTests/model_checker_solvers_z3/err new file mode 100644 index 000000000..67349d8e1 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +test.constructor() +test.f(0) + --> model_checker_solvers_z3/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_solvers_z3/input.sol b/test/cmdlineTests/model_checker_solvers_z3/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_solvers_z3_smtlib2/args b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/args new file mode 100644 index 000000000..889888874 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/args @@ -0,0 +1 @@ +--model-checker-engine all --model-checker-solvers z3,smtlib2 diff --git a/test/cmdlineTests/model_checker_solvers_z3_smtlib2/err b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/err new file mode 100644 index 000000000..5699afcde --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +test.constructor() +test.f(0) + --> model_checker_solvers_z3_smtlib2/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_solvers_z3_smtlib2/input.sol b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/input.sol new file mode 100644 index 000000000..7d05ef6e5 --- /dev/null +++ b/test/cmdlineTests/model_checker_solvers_z3_smtlib2/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_timeout_all/err b/test/cmdlineTests/model_checker_timeout_all/err index 6b2d3392a..45c38ef35 100644 --- a/test/cmdlineTests/model_checker_timeout_all/err +++ b/test/cmdlineTests/model_checker_timeout_all/err @@ -1,12 +1,3 @@ -Warning: CHC: Assertion violation might happen here. - --> model_checker_timeout_all/input.sol:9:3: - | -9 | assert(r % k == 0); - | ^^^^^^^^^^^^^^^^^^ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -Warning: BMC: Assertion violation might happen here. - --> model_checker_timeout_all/input.sol:9:3: - | -9 | assert(r % k == 0); - | ^^^^^^^^^^^^^^^^^^ -Note: +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_timeout_bmc/err b/test/cmdlineTests/model_checker_timeout_bmc/err index 9cce6ba0c..f8d0af2f4 100644 --- a/test/cmdlineTests/model_checker_timeout_bmc/err +++ b/test/cmdlineTests/model_checker_timeout_bmc/err @@ -1,6 +1 @@ -Warning: BMC: Assertion violation might happen here. - --> model_checker_timeout_bmc/input.sol:9:3: - | -9 | assert(r % k == 0); - | ^^^^^^^^^^^^^^^^^^ -Note: +Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/model_checker_timeout_chc/err b/test/cmdlineTests/model_checker_timeout_chc/err index 187ef937c..77083185f 100644 --- a/test/cmdlineTests/model_checker_timeout_chc/err +++ b/test/cmdlineTests/model_checker_timeout_chc/err @@ -1,5 +1 @@ -Warning: CHC: Assertion violation might happen here. - --> model_checker_timeout_chc/input.sol:9:3: - | -9 | assert(r % k == 0); - | ^^^^^^^^^^^^^^^^^^ +Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/cmdlineTests/name_simplifier/output b/test/cmdlineTests/name_simplifier/output index d8e7d4f7c..7d0fba09f 100644 --- a/test/cmdlineTests/name_simplifier/output +++ b/test/cmdlineTests/name_simplifier/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"name_simplifier/input.sol", 1:"#utility.yul" object "C_59" { code { { + /// @src 0:346:625 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_59_deployed") @@ -19,6 +21,7 @@ object "C_59" { object "C_59_deployed" { code { { + /// @src 0:346:625 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -90,14 +93,18 @@ object "C_59" { if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } + /// @src 0:381:623 function fun_sumArray(var_s_mpos) -> var, var_mpos { + /// @src 0:346:625 if iszero(mload(var_s_mpos)) { panic_error_0x32() } - sstore(0x00, mload(mload(add(var_s_mpos, 32)))) + sstore(/** @src 0:472:473 */ 0x00, /** @src 0:346:625 */ mload(/** @src 0:469:474 */ mload(/** @src 0:346:625 */ add(var_s_mpos, 32)))) if iszero(lt(1, mload(var_s_mpos))) { panic_error_0x32() } - let _1 := mload(mload(add(var_s_mpos, 64))) + let _1 := mload(/** @src 0:489:494 */ mload(/** @src 0:346:625 */ add(var_s_mpos, 64))) sstore(0x02, _1) + /// @src 0:500:619 var := _1 + /// @src 0:346:625 let memPtr := mload(64) let newFreePtr := add(memPtr, 160) if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } @@ -107,8 +114,10 @@ object "C_59" { mstore(add(memPtr, 64), "ngstringlongstringlongstringlong") mstore(add(memPtr, 96), "stringlongstringlongstringlongst") mstore(add(memPtr, 128), "ring") + /// @src 0:500:619 var_mpos := memPtr } + /// @src 0:346:625 function panic_error_0x32() { mstore(0, shl(224, 0x4e487b71)) diff --git a/test/cmdlineTests/object_compiler/output b/test/cmdlineTests/object_compiler/output index 53ccbe424..eb8a3286e 100644 --- a/test/cmdlineTests/object_compiler/output +++ b/test/cmdlineTests/object_compiler/output @@ -32,9 +32,10 @@ Text representation: 0x00 /* "object_compiler/input.yul":118:137 */ sstore - dataSize(sub_0) /* "object_compiler/input.yul":240:259 */ + dataSize(sub_0) dup1 + /* "object_compiler/input.yul":217:238 */ dataOffset(sub_0) /* "object_compiler/input.yul":125:126 */ 0x00 diff --git a/test/cmdlineTests/optimizer_array_sload/output b/test/cmdlineTests/optimizer_array_sload/output index c80e9df87..36dc23663 100644 --- a/test/cmdlineTests/optimizer_array_sload/output +++ b/test/cmdlineTests/optimizer_array_sload/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"optimizer_array_sload/input.sol", 1:"#utility.yul" object "Arraysum_34" { code { { + /// @src 0:80:429 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("Arraysum_34_deployed") @@ -19,6 +21,7 @@ object "Arraysum_34" { object "Arraysum_34_deployed" { code { { + /// @src 0:80:429 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -28,19 +31,27 @@ object "Arraysum_34" { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } let var_sum := _1 - let var_i := _1 + /// @src 0:368:378 + let var_i := /** @src 0:80:429 */ _1 let _2 := sload(_1) + /// @src 0:364:423 for { } - lt(var_i, _2) + /** @src 0:380:397 */ lt(var_i, _2) + /// @src 0:368:378 { + /// @src 0:80:429 if eq(var_i, not(0)) { panic_error_0x11() } - var_i := add(var_i, 1) + /// @src 0:399:402 + var_i := /** @src 0:80:429 */ add(var_i, 1) } + /// @src 0:399:402 { + /// @src 0:80:429 mstore(_1, _1) let _3 := sload(add(18569430475105882587588266137607568536673111973893317399460219858819262702947, var_i)) if gt(var_sum, not(_3)) { panic_error_0x11() } - var_sum := add(var_sum, _3) + /// @src 0:407:423 + var_sum := /** @src 0:80:429 */ add(var_sum, _3) } let memPos := mload(64) return(memPos, sub(abi_encode_uint256(memPos, var_sum), memPos)) diff --git a/test/cmdlineTests/optimizer_inliner_dynamic_reference/output b/test/cmdlineTests/optimizer_inliner_dynamic_reference/output index 1f5597478..ba32c364b 100644 --- a/test/cmdlineTests/optimizer_inliner_dynamic_reference/output +++ b/test/cmdlineTests/optimizer_inliner_dynamic_reference/output @@ -162,7 +162,7 @@ sub_0: assembly { dup3 /* "#utility.yul":257:270 */ gt - /* "#utility.yul":254:256 */ + /* "#utility.yul":254:390 */ iszero tag_30 jumpi @@ -188,13 +188,13 @@ sub_0: assembly { 0x00 /* "#utility.yul":365:380 */ revert - /* "#utility.yul":254:256 */ + /* "#utility.yul":254:390 */ tag_30: pop /* "#utility.yul":406:415 */ add swap1 - /* "#utility.yul":244:421 */ + /* "#utility.yul":196:421 */ jump // out /* "#utility.yul":426:553 */ tag_26: diff --git a/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output b/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output index f17c19176..83f06edad 100644 --- a/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output +++ b/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output @@ -167,7 +167,7 @@ sub_0: assembly { dup3 /* "#utility.yul":257:270 */ gt - /* "#utility.yul":254:256 */ + /* "#utility.yul":254:390 */ iszero tag_26 jumpi @@ -193,13 +193,13 @@ sub_0: assembly { 0x00 /* "#utility.yul":365:380 */ revert - /* "#utility.yul":254:256 */ + /* "#utility.yul":254:390 */ tag_26: pop /* "#utility.yul":406:415 */ add swap1 - /* "#utility.yul":244:421 */ + /* "#utility.yul":196:421 */ jump // out /* "#utility.yul":426:553 */ tag_22: diff --git a/test/cmdlineTests/pretty_json_combined/args b/test/cmdlineTests/pretty_json_combined/args new file mode 100644 index 000000000..f2ed65c2e --- /dev/null +++ b/test/cmdlineTests/pretty_json_combined/args @@ -0,0 +1 @@ +--combined-json abi --pretty-json --json-indent 3 diff --git a/test/cmdlineTests/pretty_json_combined/input.sol b/test/cmdlineTests/pretty_json_combined/input.sol new file mode 100644 index 000000000..625af5682 --- /dev/null +++ b/test/cmdlineTests/pretty_json_combined/input.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C {} diff --git a/test/cmdlineTests/pretty_json_combined/output b/test/cmdlineTests/pretty_json_combined/output new file mode 100644 index 000000000..6bed3b94d --- /dev/null +++ b/test/cmdlineTests/pretty_json_combined/output @@ -0,0 +1,10 @@ +{ + "contracts": + { + "pretty_json_combined/input.sol:C": + { + "abi": [] + } + }, + "version": "" +} diff --git a/test/cmdlineTests/pretty_json_indent_only/args b/test/cmdlineTests/pretty_json_indent_only/args new file mode 100644 index 000000000..10c94a50c --- /dev/null +++ b/test/cmdlineTests/pretty_json_indent_only/args @@ -0,0 +1 @@ +--json-indent 7 diff --git a/test/cmdlineTests/pretty_json_indent_only/input.json b/test/cmdlineTests/pretty_json_indent_only/input.json new file mode 100644 index 000000000..e9dc4cf38 --- /dev/null +++ b/test/cmdlineTests/pretty_json_indent_only/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {}" + } + } +} diff --git a/test/cmdlineTests/pretty_json_indent_only/output b/test/cmdlineTests/pretty_json_indent_only/output new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/cmdlineTests/pretty_json_indent_only/output @@ -0,0 +1 @@ + diff --git a/test/cmdlineTests/pretty_json_indent_only/output.json b/test/cmdlineTests/pretty_json_indent_only/output.json new file mode 100644 index 000000000..80d51c4f1 --- /dev/null +++ b/test/cmdlineTests/pretty_json_indent_only/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard/args b/test/cmdlineTests/pretty_json_standard/args new file mode 100644 index 000000000..d13a8ac44 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard/args @@ -0,0 +1 @@ +--pretty-json diff --git a/test/cmdlineTests/pretty_json_standard/input.json b/test/cmdlineTests/pretty_json_standard/input.json new file mode 100644 index 000000000..e9dc4cf38 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {}" + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard/output.json b/test/cmdlineTests/pretty_json_standard/output.json new file mode 100644 index 000000000..29185e758 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard_indent/args b/test/cmdlineTests/pretty_json_standard_indent/args new file mode 100644 index 000000000..54a84919f --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard_indent/args @@ -0,0 +1 @@ +--pretty-json --json-indent 7 diff --git a/test/cmdlineTests/pretty_json_standard_indent/input.json b/test/cmdlineTests/pretty_json_standard_indent/input.json new file mode 100644 index 000000000..df5bb3dfc --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard_indent/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { }" + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard_indent/output b/test/cmdlineTests/pretty_json_standard_indent/output new file mode 100644 index 000000000..80d51c4f1 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard_indent/output @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/pretty_json_standard_indent/output.json b/test/cmdlineTests/pretty_json_standard_indent/output.json new file mode 100644 index 000000000..80d51c4f1 --- /dev/null +++ b/test/cmdlineTests/pretty_json_standard_indent/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/revert_strings/output b/test/cmdlineTests/revert_strings/output index 5ce644d53..6fa650eb1 100644 --- a/test/cmdlineTests/revert_strings/output +++ b/test/cmdlineTests/revert_strings/output @@ -7,9 +7,10 @@ IR: *=====================================================*/ +/// @use-src 0:"revert_strings/input.sol", 1:"#utility.yul" object "C_15" { code { - /// @src 0:59,147 + /// @src 0:59:147 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,11 +25,13 @@ object "C_15" { memPtr := mload(64) } + /// @src 0:59:147 function constructor_C_15() { - /// @src 0:59,147 + /// @src 0:59:147 } + /// @src 0:59:147 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { @@ -52,7 +55,7 @@ object "C_15" { } object "C_15_deployed" { code { - /// @src 0:59,147 + /// @src 0:59:147 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -216,10 +219,11 @@ object "C_15" { mstore(64, newFreePtr) } + /// @src 0:93:145 function fun_f_14(var__7_mpos, var_e_10) { - /// @src 0:93,145 } + /// @src 0:59:147 function panic_error_0x41() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/standard_empty_file_name/output.json b/test/cmdlineTests/standard_empty_file_name/output.json index 7a70b6aff..88cbe7667 100644 --- a/test/cmdlineTests/standard_empty_file_name/output.json +++ b/test/cmdlineTests/standard_empty_file_name/output.json @@ -1,3 +1,3 @@ {"errors":[{"component":"general","errorCode":"2904","formattedMessage":"DeclarationError: Declaration \"A\" not found in \"\" (referenced as \".\"). -","message":"Declaration \"A\" not found in \"\" (referenced as \".\").","severity":"error","type":"DeclarationError"}],"sources":{"":{"id":0}}} +","message":"Declaration \"A\" not found in \"\" (referenced as \".\").","severity":"error","sourceLocation":{"end":79,"file":"","start":59},"type":"DeclarationError"}],"sources":{"":{"id":0}}} diff --git a/test/cmdlineTests/standard_file_not_found/err b/test/cmdlineTests/standard_file_not_found/err index a97cc5b7a..db5590ed9 100644 --- a/test/cmdlineTests/standard_file_not_found/err +++ b/test/cmdlineTests/standard_file_not_found/err @@ -1 +1 @@ -File not found: input.sol +"input.sol" is not found. diff --git a/test/cmdlineTests/standard_generatedSources/output.json b/test/cmdlineTests/standard_generatedSources/output.json index 507bc25ed..b09f6750f 100644 --- a/test/cmdlineTests/standard_generatedSources/output.json +++ b/test/cmdlineTests/standard_generatedSources/output.json @@ -1,4 +1,4 @@ -{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:4001:1","statements":[{"body":{"nodeType":"YulBlock","src":"126:620:1","statements":[{"nodeType":"YulAssignment","src":"136:90:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"218:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"161:56:1"},"nodeType":"YulFunctionCall","src":"161:64:1"}],"functionName":{"name":"allocate_memory","nodeType":"YulIdentifier","src":"145:15:1"},"nodeType":"YulFunctionCall","src":"145:81:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"136:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"235:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"246:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"239:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"268:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"275:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"261:6:1"},"nodeType":"YulFunctionCall","src":"261:21:1"},"nodeType":"YulExpressionStatement","src":"261:21:1"},{"nodeType":"YulAssignment","src":"291:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"302:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"309:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"298:3:1"},"nodeType":"YulFunctionCall","src":"298:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"291:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"324:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"335:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"328:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"390:103:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulIdentifier","src":"404:77:1"},"nodeType":"YulFunctionCall","src":"404:79:1"},"nodeType":"YulExpressionStatement","src":"404:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"360:3:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"369:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"377:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"365:3:1"},"nodeType":"YulFunctionCall","src":"365:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"356:3:1"},"nodeType":"YulFunctionCall","src":"356:27:1"},{"name":"end","nodeType":"YulIdentifier","src":"385:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"353:2:1"},"nodeType":"YulFunctionCall","src":"353:36:1"},"nodeType":"YulIf","src":"350:2:1"},{"body":{"nodeType":"YulBlock","src":"562:178:1","statements":[{"nodeType":"YulVariableDeclaration","src":"577:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"595:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"581:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"619:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"645:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"657:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"624:20:1"},"nodeType":"YulFunctionCall","src":"624:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"612:6:1"},"nodeType":"YulFunctionCall","src":"612:50:1"},"nodeType":"YulExpressionStatement","src":"612:50:1"},{"nodeType":"YulAssignment","src":"675:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"686:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"691:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"682:3:1"},"nodeType":"YulFunctionCall","src":"682:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"675:3:1"}]},{"nodeType":"YulAssignment","src":"709:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"720:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"725:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"716:3:1"},"nodeType":"YulFunctionCall","src":"716:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"709:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"524:1:1"},{"name":"length","nodeType":"YulIdentifier","src":"527:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"521:2:1"},"nodeType":"YulFunctionCall","src":"521:13:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"535:18:1","statements":[{"nodeType":"YulAssignment","src":"537:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"546:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"549:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"542:3:1"},"nodeType":"YulFunctionCall","src":"542:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"537:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"506:14:1","statements":[{"nodeType":"YulVariableDeclaration","src":"508:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"517:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"512:1:1","type":""}]}]},"src":"502:238:1"}]},"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"96:6:1","type":""},{"name":"length","nodeType":"YulTypedName","src":"104:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"112:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"120:5:1","type":""}],"src":"24:722:1"},{"body":{"nodeType":"YulBlock","src":"846:293:1","statements":[{"body":{"nodeType":"YulBlock","src":"895:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulIdentifier","src":"897:77:1"},"nodeType":"YulFunctionCall","src":"897:79:1"},"nodeType":"YulExpressionStatement","src":"897:79:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"874:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"882:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"870:3:1"},"nodeType":"YulFunctionCall","src":"870:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"889:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"866:3:1"},"nodeType":"YulFunctionCall","src":"866:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"859:6:1"},"nodeType":"YulFunctionCall","src":"859:35:1"},"nodeType":"YulIf","src":"856:2:1"},{"nodeType":"YulVariableDeclaration","src":"987:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1014:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1001:12:1"},"nodeType":"YulFunctionCall","src":"1001:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"991:6:1","type":""}]},{"nodeType":"YulAssignment","src":"1030:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1106:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1114:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1102:3:1"},"nodeType":"YulFunctionCall","src":"1102:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"1121:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"1129:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1039:62:1"},"nodeType":"YulFunctionCall","src":"1039:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"1030:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"824:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"832:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"840:5:1","type":""}],"src":"769:370:1"},{"body":{"nodeType":"YulBlock","src":"1197:87:1","statements":[{"nodeType":"YulAssignment","src":"1207:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1229:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1216:12:1"},"nodeType":"YulFunctionCall","src":"1216:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1207:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1272:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1245:26:1"},"nodeType":"YulFunctionCall","src":"1245:33:1"},"nodeType":"YulExpressionStatement","src":"1245:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1175:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1183:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1191:5:1","type":""}],"src":"1145:139:1"},{"body":{"nodeType":"YulBlock","src":"1381:448:1","statements":[{"body":{"nodeType":"YulBlock","src":"1427:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulIdentifier","src":"1429:77:1"},"nodeType":"YulFunctionCall","src":"1429:79:1"},"nodeType":"YulExpressionStatement","src":"1429:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"1402:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"1411:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"1398:3:1"},"nodeType":"YulFunctionCall","src":"1398:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"1423:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"1394:3:1"},"nodeType":"YulFunctionCall","src":"1394:32:1"},"nodeType":"YulIf","src":"1391:2:1"},{"nodeType":"YulBlock","src":"1520:302:1","statements":[{"nodeType":"YulVariableDeclaration","src":"1535:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1566:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1577:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1562:3:1"},"nodeType":"YulFunctionCall","src":"1562:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1549:12:1"},"nodeType":"YulFunctionCall","src":"1549:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"1539:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1627:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulIdentifier","src":"1629:77:1"},"nodeType":"YulFunctionCall","src":"1629:79:1"},"nodeType":"YulExpressionStatement","src":"1629:79:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1599:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1607:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1596:2:1"},"nodeType":"YulFunctionCall","src":"1596:30:1"},"nodeType":"YulIf","src":"1593:2:1"},{"nodeType":"YulAssignment","src":"1724:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1784:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"1795:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1780:3:1"},"nodeType":"YulFunctionCall","src":"1780:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1804:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1734:45:1"},"nodeType":"YulFunctionCall","src":"1734:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1724:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1351:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"1362:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"1374:6:1","type":""}],"src":"1290:539:1"},{"body":{"nodeType":"YulBlock","src":"1900:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"1917:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1940:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1922:17:1"},"nodeType":"YulFunctionCall","src":"1922:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1910:6:1"},"nodeType":"YulFunctionCall","src":"1910:37:1"},"nodeType":"YulExpressionStatement","src":"1910:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1888:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"1895:3:1","type":""}],"src":"1835:118:1"},{"body":{"nodeType":"YulBlock","src":"2057:124:1","statements":[{"nodeType":"YulAssignment","src":"2067:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2079:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2090:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2075:3:1"},"nodeType":"YulFunctionCall","src":"2075:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"2067:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"2147:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2160:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2171:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2156:3:1"},"nodeType":"YulFunctionCall","src":"2156:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"2103:43:1"},"nodeType":"YulFunctionCall","src":"2103:71:1"},"nodeType":"YulExpressionStatement","src":"2103:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"2029:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"2041:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"2052:4:1","type":""}],"src":"1959:222:1"},{"body":{"nodeType":"YulBlock","src":"2228:88:1","statements":[{"nodeType":"YulAssignment","src":"2238:30:1","value":{"arguments":[],"functionName":{"name":"allocate_unbounded","nodeType":"YulIdentifier","src":"2248:18:1"},"nodeType":"YulFunctionCall","src":"2248:20:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2238:6:1"}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2297:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"2305:4:1"}],"functionName":{"name":"finalize_allocation","nodeType":"YulIdentifier","src":"2277:19:1"},"nodeType":"YulFunctionCall","src":"2277:33:1"},"nodeType":"YulExpressionStatement","src":"2277:33:1"}]},"name":"allocate_memory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"2212:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2221:6:1","type":""}],"src":"2187:129:1"},{"body":{"nodeType":"YulBlock","src":"2362:35:1","statements":[{"nodeType":"YulAssignment","src":"2372:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2388:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"2382:5:1"},"nodeType":"YulFunctionCall","src":"2382:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2372:6:1"}]}]},"name":"allocate_unbounded","nodeType":"YulFunctionDefinition","returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2355:6:1","type":""}],"src":"2322:75:1"},{"body":{"nodeType":"YulBlock","src":"2485:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"2590:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2592:16:1"},"nodeType":"YulFunctionCall","src":"2592:18:1"},"nodeType":"YulExpressionStatement","src":"2592:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2562:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2570:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2559:2:1"},"nodeType":"YulFunctionCall","src":"2559:30:1"},"nodeType":"YulIf","src":"2556:2:1"},{"nodeType":"YulAssignment","src":"2622:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2634:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2642:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2630:3:1"},"nodeType":"YulFunctionCall","src":"2630:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2622:4:1"}]},{"nodeType":"YulAssignment","src":"2684:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2696:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"2702:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2692:3:1"},"nodeType":"YulFunctionCall","src":"2692:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2684:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"2469:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"2480:4:1","type":""}],"src":"2403:311:1"},{"body":{"nodeType":"YulBlock","src":"2765:32:1","statements":[{"nodeType":"YulAssignment","src":"2775:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"2786:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"2775:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2747:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"2757:7:1","type":""}],"src":"2720:77:1"},{"body":{"nodeType":"YulBlock","src":"2846:238:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2856:58:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2878:6:1"},{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2908:4:1"}],"functionName":{"name":"round_up_to_mul_of_32","nodeType":"YulIdentifier","src":"2886:21:1"},"nodeType":"YulFunctionCall","src":"2886:27:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2874:3:1"},"nodeType":"YulFunctionCall","src":"2874:40:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"2860:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"3025:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"3027:16:1"},"nodeType":"YulFunctionCall","src":"3027:18:1"},"nodeType":"YulExpressionStatement","src":"3027:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2968:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"2980:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2965:2:1"},"nodeType":"YulFunctionCall","src":"2965:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3004:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"3016:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"3001:2:1"},"nodeType":"YulFunctionCall","src":"3001:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"2962:2:1"},"nodeType":"YulFunctionCall","src":"2962:62:1"},"nodeType":"YulIf","src":"2959:2:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3063:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3067:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3056:6:1"},"nodeType":"YulFunctionCall","src":"3056:22:1"},"nodeType":"YulExpressionStatement","src":"3056:22:1"}]},"name":"finalize_allocation","nodeType":"YulFunctionDefinition","parameters":[{"name":"memPtr","nodeType":"YulTypedName","src":"2832:6:1","type":""},{"name":"size","nodeType":"YulTypedName","src":"2840:4:1","type":""}],"src":"2803:281:1"},{"body":{"nodeType":"YulBlock","src":"3118:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3135:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3138:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3128:6:1"},"nodeType":"YulFunctionCall","src":"3128:88:1"},"nodeType":"YulExpressionStatement","src":"3128:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3232:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"3235:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3225:6:1"},"nodeType":"YulFunctionCall","src":"3225:15:1"},"nodeType":"YulExpressionStatement","src":"3225:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3256:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3259:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3249:6:1"},"nodeType":"YulFunctionCall","src":"3249:15:1"},"nodeType":"YulExpressionStatement","src":"3249:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"3090:180:1"},{"body":{"nodeType":"YulBlock","src":"3365:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3382:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3385:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3375:6:1"},"nodeType":"YulFunctionCall","src":"3375:12:1"},"nodeType":"YulExpressionStatement","src":"3375:12:1"}]},"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulFunctionDefinition","src":"3276:117:1"},{"body":{"nodeType":"YulBlock","src":"3488:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3505:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3508:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3498:6:1"},"nodeType":"YulFunctionCall","src":"3498:12:1"},"nodeType":"YulExpressionStatement","src":"3498:12:1"}]},"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulFunctionDefinition","src":"3399:117:1"},{"body":{"nodeType":"YulBlock","src":"3611:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3628:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3631:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3621:6:1"},"nodeType":"YulFunctionCall","src":"3621:12:1"},"nodeType":"YulExpressionStatement","src":"3621:12:1"}]},"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulFunctionDefinition","src":"3522:117:1"},{"body":{"nodeType":"YulBlock","src":"3734:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3751:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3754:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3744:6:1"},"nodeType":"YulFunctionCall","src":"3744:12:1"},"nodeType":"YulExpressionStatement","src":"3744:12:1"}]},"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulFunctionDefinition","src":"3645:117:1"},{"body":{"nodeType":"YulBlock","src":"3816:54:1","statements":[{"nodeType":"YulAssignment","src":"3826:38:1","value":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3844:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"3851:2:1","type":"","value":"31"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3840:3:1"},"nodeType":"YulFunctionCall","src":"3840:14:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3860:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"3856:3:1"},"nodeType":"YulFunctionCall","src":"3856:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"3836:3:1"},"nodeType":"YulFunctionCall","src":"3836:28:1"},"variableNames":[{"name":"result","nodeType":"YulIdentifier","src":"3826:6:1"}]}]},"name":"round_up_to_mul_of_32","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3799:5:1","type":""}],"returnVariables":[{"name":"result","nodeType":"YulTypedName","src":"3809:6:1","type":""}],"src":"3768:102:1"},{"body":{"nodeType":"YulBlock","src":"3919:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"3976:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3985:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3988:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3978:6:1"},"nodeType":"YulFunctionCall","src":"3978:12:1"},"nodeType":"YulExpressionStatement","src":"3978:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3942:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3967:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"3949:17:1"},"nodeType":"YulFunctionCall","src":"3949:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"3939:2:1"},"nodeType":"YulFunctionCall","src":"3939:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"3932:6:1"},"nodeType":"YulFunctionCall","src":"3932:43:1"},"nodeType":"YulIf","src":"3929:2:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3912:5:1","type":""}],"src":"3876:122:1"}]},"contents":"{ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:4001:1","statements":[{"body":{"nodeType":"YulBlock","src":"126:620:1","statements":[{"nodeType":"YulAssignment","src":"136:90:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"218:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"161:56:1"},"nodeType":"YulFunctionCall","src":"161:64:1"}],"functionName":{"name":"allocate_memory","nodeType":"YulIdentifier","src":"145:15:1"},"nodeType":"YulFunctionCall","src":"145:81:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"136:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"235:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"246:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"239:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"268:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"275:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"261:6:1"},"nodeType":"YulFunctionCall","src":"261:21:1"},"nodeType":"YulExpressionStatement","src":"261:21:1"},{"nodeType":"YulAssignment","src":"291:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"302:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"309:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"298:3:1"},"nodeType":"YulFunctionCall","src":"298:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"291:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"324:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"335:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"328:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"390:103:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulIdentifier","src":"404:77:1"},"nodeType":"YulFunctionCall","src":"404:79:1"},"nodeType":"YulExpressionStatement","src":"404:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"360:3:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"369:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"377:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"365:3:1"},"nodeType":"YulFunctionCall","src":"365:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"356:3:1"},"nodeType":"YulFunctionCall","src":"356:27:1"},{"name":"end","nodeType":"YulIdentifier","src":"385:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"353:2:1"},"nodeType":"YulFunctionCall","src":"353:36:1"},"nodeType":"YulIf","src":"350:143:1"},{"body":{"nodeType":"YulBlock","src":"562:178:1","statements":[{"nodeType":"YulVariableDeclaration","src":"577:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"595:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"581:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"619:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"645:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"657:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"624:20:1"},"nodeType":"YulFunctionCall","src":"624:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"612:6:1"},"nodeType":"YulFunctionCall","src":"612:50:1"},"nodeType":"YulExpressionStatement","src":"612:50:1"},{"nodeType":"YulAssignment","src":"675:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"686:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"691:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"682:3:1"},"nodeType":"YulFunctionCall","src":"682:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"675:3:1"}]},{"nodeType":"YulAssignment","src":"709:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"720:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"725:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"716:3:1"},"nodeType":"YulFunctionCall","src":"716:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"709:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"524:1:1"},{"name":"length","nodeType":"YulIdentifier","src":"527:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"521:2:1"},"nodeType":"YulFunctionCall","src":"521:13:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"535:18:1","statements":[{"nodeType":"YulAssignment","src":"537:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"546:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"549:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"542:3:1"},"nodeType":"YulFunctionCall","src":"542:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"537:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"506:14:1","statements":[{"nodeType":"YulVariableDeclaration","src":"508:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"517:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"512:1:1","type":""}]}]},"src":"502:238:1"}]},"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"96:6:1","type":""},{"name":"length","nodeType":"YulTypedName","src":"104:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"112:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"120:5:1","type":""}],"src":"24:722:1"},{"body":{"nodeType":"YulBlock","src":"846:293:1","statements":[{"body":{"nodeType":"YulBlock","src":"895:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulIdentifier","src":"897:77:1"},"nodeType":"YulFunctionCall","src":"897:79:1"},"nodeType":"YulExpressionStatement","src":"897:79:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"874:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"882:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"870:3:1"},"nodeType":"YulFunctionCall","src":"870:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"889:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"866:3:1"},"nodeType":"YulFunctionCall","src":"866:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"859:6:1"},"nodeType":"YulFunctionCall","src":"859:35:1"},"nodeType":"YulIf","src":"856:122:1"},{"nodeType":"YulVariableDeclaration","src":"987:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1014:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1001:12:1"},"nodeType":"YulFunctionCall","src":"1001:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"991:6:1","type":""}]},{"nodeType":"YulAssignment","src":"1030:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1106:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1114:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1102:3:1"},"nodeType":"YulFunctionCall","src":"1102:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"1121:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"1129:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1039:62:1"},"nodeType":"YulFunctionCall","src":"1039:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"1030:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"824:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"832:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"840:5:1","type":""}],"src":"769:370:1"},{"body":{"nodeType":"YulBlock","src":"1197:87:1","statements":[{"nodeType":"YulAssignment","src":"1207:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1229:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1216:12:1"},"nodeType":"YulFunctionCall","src":"1216:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1207:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1272:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1245:26:1"},"nodeType":"YulFunctionCall","src":"1245:33:1"},"nodeType":"YulExpressionStatement","src":"1245:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1175:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1183:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1191:5:1","type":""}],"src":"1145:139:1"},{"body":{"nodeType":"YulBlock","src":"1381:448:1","statements":[{"body":{"nodeType":"YulBlock","src":"1427:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulIdentifier","src":"1429:77:1"},"nodeType":"YulFunctionCall","src":"1429:79:1"},"nodeType":"YulExpressionStatement","src":"1429:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"1402:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"1411:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"1398:3:1"},"nodeType":"YulFunctionCall","src":"1398:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"1423:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"1394:3:1"},"nodeType":"YulFunctionCall","src":"1394:32:1"},"nodeType":"YulIf","src":"1391:119:1"},{"nodeType":"YulBlock","src":"1520:302:1","statements":[{"nodeType":"YulVariableDeclaration","src":"1535:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1566:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1577:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1562:3:1"},"nodeType":"YulFunctionCall","src":"1562:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1549:12:1"},"nodeType":"YulFunctionCall","src":"1549:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"1539:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1627:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulIdentifier","src":"1629:77:1"},"nodeType":"YulFunctionCall","src":"1629:79:1"},"nodeType":"YulExpressionStatement","src":"1629:79:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1599:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1607:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1596:2:1"},"nodeType":"YulFunctionCall","src":"1596:30:1"},"nodeType":"YulIf","src":"1593:117:1"},{"nodeType":"YulAssignment","src":"1724:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1784:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"1795:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1780:3:1"},"nodeType":"YulFunctionCall","src":"1780:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1804:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1734:45:1"},"nodeType":"YulFunctionCall","src":"1734:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1724:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1351:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"1362:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"1374:6:1","type":""}],"src":"1290:539:1"},{"body":{"nodeType":"YulBlock","src":"1900:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"1917:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1940:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1922:17:1"},"nodeType":"YulFunctionCall","src":"1922:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1910:6:1"},"nodeType":"YulFunctionCall","src":"1910:37:1"},"nodeType":"YulExpressionStatement","src":"1910:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1888:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"1895:3:1","type":""}],"src":"1835:118:1"},{"body":{"nodeType":"YulBlock","src":"2057:124:1","statements":[{"nodeType":"YulAssignment","src":"2067:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2079:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2090:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2075:3:1"},"nodeType":"YulFunctionCall","src":"2075:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"2067:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"2147:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2160:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2171:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2156:3:1"},"nodeType":"YulFunctionCall","src":"2156:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"2103:43:1"},"nodeType":"YulFunctionCall","src":"2103:71:1"},"nodeType":"YulExpressionStatement","src":"2103:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"2029:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"2041:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"2052:4:1","type":""}],"src":"1959:222:1"},{"body":{"nodeType":"YulBlock","src":"2228:88:1","statements":[{"nodeType":"YulAssignment","src":"2238:30:1","value":{"arguments":[],"functionName":{"name":"allocate_unbounded","nodeType":"YulIdentifier","src":"2248:18:1"},"nodeType":"YulFunctionCall","src":"2248:20:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2238:6:1"}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2297:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"2305:4:1"}],"functionName":{"name":"finalize_allocation","nodeType":"YulIdentifier","src":"2277:19:1"},"nodeType":"YulFunctionCall","src":"2277:33:1"},"nodeType":"YulExpressionStatement","src":"2277:33:1"}]},"name":"allocate_memory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"2212:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2221:6:1","type":""}],"src":"2187:129:1"},{"body":{"nodeType":"YulBlock","src":"2362:35:1","statements":[{"nodeType":"YulAssignment","src":"2372:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2388:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"2382:5:1"},"nodeType":"YulFunctionCall","src":"2382:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2372:6:1"}]}]},"name":"allocate_unbounded","nodeType":"YulFunctionDefinition","returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2355:6:1","type":""}],"src":"2322:75:1"},{"body":{"nodeType":"YulBlock","src":"2485:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"2590:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2592:16:1"},"nodeType":"YulFunctionCall","src":"2592:18:1"},"nodeType":"YulExpressionStatement","src":"2592:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2562:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2570:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2559:2:1"},"nodeType":"YulFunctionCall","src":"2559:30:1"},"nodeType":"YulIf","src":"2556:56:1"},{"nodeType":"YulAssignment","src":"2622:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2634:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2642:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2630:3:1"},"nodeType":"YulFunctionCall","src":"2630:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2622:4:1"}]},{"nodeType":"YulAssignment","src":"2684:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2696:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"2702:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2692:3:1"},"nodeType":"YulFunctionCall","src":"2692:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2684:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"2469:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"2480:4:1","type":""}],"src":"2403:311:1"},{"body":{"nodeType":"YulBlock","src":"2765:32:1","statements":[{"nodeType":"YulAssignment","src":"2775:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"2786:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"2775:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2747:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"2757:7:1","type":""}],"src":"2720:77:1"},{"body":{"nodeType":"YulBlock","src":"2846:238:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2856:58:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2878:6:1"},{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2908:4:1"}],"functionName":{"name":"round_up_to_mul_of_32","nodeType":"YulIdentifier","src":"2886:21:1"},"nodeType":"YulFunctionCall","src":"2886:27:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2874:3:1"},"nodeType":"YulFunctionCall","src":"2874:40:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"2860:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"3025:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"3027:16:1"},"nodeType":"YulFunctionCall","src":"3027:18:1"},"nodeType":"YulExpressionStatement","src":"3027:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2968:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"2980:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2965:2:1"},"nodeType":"YulFunctionCall","src":"2965:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3004:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"3016:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"3001:2:1"},"nodeType":"YulFunctionCall","src":"3001:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"2962:2:1"},"nodeType":"YulFunctionCall","src":"2962:62:1"},"nodeType":"YulIf","src":"2959:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3063:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3067:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3056:6:1"},"nodeType":"YulFunctionCall","src":"3056:22:1"},"nodeType":"YulExpressionStatement","src":"3056:22:1"}]},"name":"finalize_allocation","nodeType":"YulFunctionDefinition","parameters":[{"name":"memPtr","nodeType":"YulTypedName","src":"2832:6:1","type":""},{"name":"size","nodeType":"YulTypedName","src":"2840:4:1","type":""}],"src":"2803:281:1"},{"body":{"nodeType":"YulBlock","src":"3118:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3135:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3138:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3128:6:1"},"nodeType":"YulFunctionCall","src":"3128:88:1"},"nodeType":"YulExpressionStatement","src":"3128:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3232:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"3235:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3225:6:1"},"nodeType":"YulFunctionCall","src":"3225:15:1"},"nodeType":"YulExpressionStatement","src":"3225:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3256:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3259:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3249:6:1"},"nodeType":"YulFunctionCall","src":"3249:15:1"},"nodeType":"YulExpressionStatement","src":"3249:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"3090:180:1"},{"body":{"nodeType":"YulBlock","src":"3365:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3382:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3385:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3375:6:1"},"nodeType":"YulFunctionCall","src":"3375:12:1"},"nodeType":"YulExpressionStatement","src":"3375:12:1"}]},"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulFunctionDefinition","src":"3276:117:1"},{"body":{"nodeType":"YulBlock","src":"3488:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3505:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3508:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3498:6:1"},"nodeType":"YulFunctionCall","src":"3498:12:1"},"nodeType":"YulExpressionStatement","src":"3498:12:1"}]},"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulFunctionDefinition","src":"3399:117:1"},{"body":{"nodeType":"YulBlock","src":"3611:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3628:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3631:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3621:6:1"},"nodeType":"YulFunctionCall","src":"3621:12:1"},"nodeType":"YulExpressionStatement","src":"3621:12:1"}]},"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulFunctionDefinition","src":"3522:117:1"},{"body":{"nodeType":"YulBlock","src":"3734:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3751:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3754:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3744:6:1"},"nodeType":"YulFunctionCall","src":"3744:12:1"},"nodeType":"YulExpressionStatement","src":"3744:12:1"}]},"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulFunctionDefinition","src":"3645:117:1"},{"body":{"nodeType":"YulBlock","src":"3816:54:1","statements":[{"nodeType":"YulAssignment","src":"3826:38:1","value":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3844:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"3851:2:1","type":"","value":"31"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3840:3:1"},"nodeType":"YulFunctionCall","src":"3840:14:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3860:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"3856:3:1"},"nodeType":"YulFunctionCall","src":"3856:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"3836:3:1"},"nodeType":"YulFunctionCall","src":"3836:28:1"},"variableNames":[{"name":"result","nodeType":"YulIdentifier","src":"3826:6:1"}]}]},"name":"round_up_to_mul_of_32","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3799:5:1","type":""}],"returnVariables":[{"name":"result","nodeType":"YulTypedName","src":"3809:6:1","type":""}],"src":"3768:102:1"},{"body":{"nodeType":"YulBlock","src":"3919:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"3976:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3985:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3988:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3978:6:1"},"nodeType":"YulFunctionCall","src":"3978:12:1"},"nodeType":"YulExpressionStatement","src":"3978:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3942:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3967:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"3949:17:1"},"nodeType":"YulFunctionCall","src":"3949:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"3939:2:1"},"nodeType":"YulFunctionCall","src":"3939:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"3932:6:1"},"nodeType":"YulFunctionCall","src":"3932:43:1"},"nodeType":"YulIf","src":"3929:63:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3912:5:1","type":""}],"src":"3876:122:1"}]},"contents":"{ // uint256[] function abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(offset, length, end) -> array { diff --git a/test/cmdlineTests/standard_irOptimized_requested/output.json b/test/cmdlineTests/standard_irOptimized_requested/output.json index 73e9653d2..8b9ada71a 100644 --- a/test/cmdlineTests/standard_irOptimized_requested/output.json +++ b/test/cmdlineTests/standard_irOptimized_requested/output.json @@ -5,8 +5,10 @@ * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_7\" { code { + /// @src 0:79:121 mstore(64, 128) if callvalue() { @@ -25,6 +27,7 @@ object \"C_7\" { } object \"C_7_deployed\" { code { + /// @src 0:79:121 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -56,8 +59,10 @@ object \"C_7\" { { tail := add(headStart, 0) } function allocate_unbounded() -> memPtr { memPtr := mload(64) } + /// @src 0:92:119 function fun_f_6() { } + /// @src 0:79:121 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) } function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 0d61f8cca..2eb815d33 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -6,9 +6,10 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_7\" { code { - /// @src 0:79,121 + /// @src 0:79:121 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -23,11 +24,13 @@ object \"C_7\" { memPtr := mload(64) } + /// @src 0:79:121 function constructor_C_7() { - /// @src 0:79,121 + /// @src 0:79:121 } + /// @src 0:79:121 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -36,7 +39,7 @@ object \"C_7\" { } object \"C_7_deployed\" { code { - /// @src 0:79,121 + /// @src 0:79:121 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -75,10 +78,11 @@ object \"C_7\" { memPtr := mload(64) } + /// @src 0:92:119 function fun_f_6() { - /// @src 0:92,119 } + /// @src 0:79:121 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/input.json new file mode 100644 index 000000000..18a8b21fc --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/input.json @@ -0,0 +1,26 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/output.json new file mode 100644 index 000000000..cf4c00c61 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_default_all_engines/output.json @@ -0,0 +1,90 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/input.json new file mode 100644 index 000000000..d7396dd98 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "showUnproved": false + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/output.json new file mode 100644 index 000000000..cf4c00c61 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_all_engines/output.json @@ -0,0 +1,90 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/input.json new file mode 100644 index 000000000..c9ee3258e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "bmc", + "showUnproved": false + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/output.json new file mode 100644 index 000000000..6d9e16af8 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_bmc/output.json @@ -0,0 +1,88 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/input.json new file mode 100644 index 000000000..fe662e80c --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "showUnproved": false + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/output.json new file mode 100644 index 000000000..5e66cb899 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_false_chc/output.json @@ -0,0 +1,3 @@ +{"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. + +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/input.json new file mode 100644 index 000000000..3f9bb715e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "showUnproved": true + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/output.json new file mode 100644 index 000000000..2e384c009 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_all_engines/output.json @@ -0,0 +1,99 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. + --> A:11:7: + | +11 | \t\t\t\t\t\tassert(s.x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":201,"file":"A","start":186},"type":"Warning"},{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. + --> A:11:7: + | +11 | \t\t\t\t\t\tassert(s.x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^^^ +Note: + +","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":201,"file":"A","start":186},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/input.json new file mode 100644 index 000000000..2f367ae80 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "bmc", + "showUnproved": true + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/output.json new file mode 100644 index 000000000..d21389103 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_bmc/output.json @@ -0,0 +1,93 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x119e9d636624c5af8dc0d97ee8d2905551bfe9eea88d60c6d3793cfdc576a76b":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) +(declare-fun |expr_15_1| () Bool) +(declare-fun |expr_16_0| () Int) +(declare-fun |expr_17_0| () Int) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_12_0| () |struct test.S|) +(declare-fun |expr_14_1| () Int) +(declare-fun |expr_19_1| () Int) +(declare-fun |expr_12_1| () |struct test.S|) +(declare-fun |expr_12_2| () |struct test.S|) +(declare-fun |expr_14_2| () Int) +(declare-fun |s_7_2| () |struct test.S|) +(declare-fun |expr_12_3| () |struct test.S|) +(declare-fun |expr_22_0| () |struct test.S|) +(declare-fun |expr_23_1| () Int) +(declare-fun |expr_24_0| () Int) +(declare-fun |expr_25_1| () Bool) + +(assert (and (and (and true true) (and (= expr_25_1 (> expr_23_1 expr_24_0)) (and (=> (and true true) true) (and (= expr_24_0 0) (and (=> (and true true) (and (>= expr_23_1 0) (<= expr_23_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_23_1 (|struct test.S_accessor_x| expr_22_0)) (and (= expr_22_0 s_7_2) (and (= expr_12_3 s_7_2) (and (ite (and true true) (= s_7_2 expr_12_2) (= s_7_2 s_7_1)) (and (=> (and true true) (and (>= expr_14_2 0) (<= expr_14_2 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_2 (|struct test.S_accessor_x| expr_12_2)) (and (= (|struct test.S_accessor_x| expr_12_2) expr_19_1) (and (= expr_12_1 s_7_1) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (bv2nat (bvor (ite (>= expr_14_1 0) ((_ int2bv 256) expr_14_1) (bvneg ((_ int2bv 256) (- expr_14_1)))) (ite (>= expr_18_0 0) ((_ int2bv 256) expr_18_0) (bvneg ((_ int2bv 256) (- expr_18_0))))))) (and (=> (and true true) (and (>= expr_14_1 0) (<= expr_14_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_14_1 (|struct test.S_accessor_x| expr_12_0)) (and (= expr_12_0 s_7_1) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 255))) (and (= expr_18_0 (ite expr_15_1 expr_16_0 expr_17_0)) (and (=> (and (and true true) (not expr_15_1)) true) (and (= expr_17_0 2) (and (=> (and (and true true) expr_15_1) true) (and (= expr_16_0 1) (and (= expr_15_1 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))))))))))))))))))))))) (not expr_25_1))) +(declare-const |EVALEXPR_0| Bool) +(assert (= |EVALEXPR_0| b_9_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x4d368a1e0f051bee84d8e64e660d7d50d57486e66c037dcdb97b06447bbcfb8e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) (not expr_15_0))) +(check-sat) +","0xab73091601c574bdace0ae9a7fc088a8f13ff47d4b78323c2df81da0281c9df4":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-datatypes ((|struct test.S| 0)) (((|struct test.S| (|struct test.S_accessor_x| Int))))) +(declare-fun |s_7_0| () |struct test.S|) +(declare-fun |b_9_0| () Bool) +(declare-fun |s_7_1| () |struct test.S|) +(declare-fun |expr_15_0| () Bool) + +(assert (and (and (and true true) (and (= expr_15_0 b_9_0) (and true (and true (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 2562959041)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 152)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 195)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 166)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 193)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))) expr_15_0)) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. + --> A:11:7: + | +11 | \t\t\t\t\t\tassert(s.x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^^^ +Note: + +","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":201,"file":"A","start":186},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/input.json new file mode 100644 index 000000000..f81688b71 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "showUnproved": true + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/output.json new file mode 100644 index 000000000..352b76d27 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_true_chc/output.json @@ -0,0 +1,7 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. + --> A:11:7: + | +11 | \t\t\t\t\t\tassert(s.x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":201,"file":"A","start":186},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_wrong/input.json b/test/cmdlineTests/standard_model_checker_show_unproved_wrong/input.json new file mode 100644 index 000000000..bdaec9b3e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_wrong/input.json @@ -0,0 +1,27 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract test { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "showUnproved": "aaa" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_show_unproved_wrong/output.json b/test/cmdlineTests/standard_model_checker_show_unproved_wrong/output.json new file mode 100644 index 000000000..394dbf925 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_show_unproved_wrong/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"settings.modelChecker.showUnproved must be a Boolean value.","message":"settings.modelChecker.showUnproved must be a Boolean value.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_model_checker_solvers_all/input.json b/test/cmdlineTests/standard_model_checker_solvers_all/input.json new file mode 100644 index 000000000..db9f2232d --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_all/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_all/output.json b/test/cmdlineTests/standard_model_checker_solvers_all/output.json new file mode 100644 index 000000000..12dfe5871 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_all/output.json @@ -0,0 +1,21 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0) + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0)","severity":"warning","sourceLocation":{"end":119,"file":"A","start":106},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_solvers_none/input.json b/test/cmdlineTests/standard_model_checker_solvers_none/input.json new file mode 100644 index 000000000..07cbb88ac --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_none/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "solvers": [] + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_none/output.json b/test/cmdlineTests/standard_model_checker_solvers_none/output.json new file mode 100644 index 000000000..b237fadce --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_none/output.json @@ -0,0 +1,5 @@ +{"errors":[{"component":"general","errorCode":"7649","formattedMessage":"Warning: CHC analysis was not possible since no Horn solver was enabled. + +","message":"CHC analysis was not possible since no Horn solver was enabled.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"7710","formattedMessage":"Warning: BMC analysis was not possible since no SMT solver was found and enabled. + +","message":"BMC analysis was not possible since no SMT solver was found and enabled.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_solvers_smtlib2/input.json b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/input.json new file mode 100644 index 000000000..8903ec016 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "solvers": ["smtlib2"] + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json new file mode 100644 index 000000000..2ab2d6b5e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json @@ -0,0 +1,138 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x4ee925df3426fec84707cea517cc52ed216229bfde71458a69e7edeece2c071e":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) +(declare-fun |x_3_0| () Int) +(declare-fun |expr_7_0| () Int) +(declare-fun |expr_8_0| () Int) +(declare-fun |expr_9_1| () Bool) + +(assert (and (and (and true true) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> (and true true) true) (and (= expr_8_0 0) (and (=> (and true true) (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_0) (and (and (>= x_3_0 0) (<= x_3_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))) (not expr_9_1))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_3_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +","0x540d6953fff999f72ca33d8af8b900986dc012a365fc1b8572aa9d9978f1392b":"(set-logic HORN) + +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.chainid| Int) (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |interface_0_C_14_0| (Int |abi_type| |crypto_type| |state_type| ) Bool) +(declare-fun |nondet_interface_1_C_14_0| (Int Int |abi_type| |crypto_type| |state_type| |state_type| ) Bool) +(declare-fun |summary_constructor_2_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (state_0 |state_type|) (this_0 Int) (tx_0 |tx_type|)) +(=> (= error_0 0) (nondet_interface_1_C_14_0 error_0 this_0 abi_0 crypto_0 state_0 state_0)))) + + +(declare-fun |summary_3_function_f__13_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (nondet_interface_1_C_14_0 error_0 this_0 abi_0 crypto_0 state_0 state_1) true) (and (= error_0 0) (summary_3_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_1 x_3_0 state_2 x_3_1))) (nondet_interface_1_C_14_0 error_1 this_0 abi_0 crypto_0 state_0 state_2)))) + + +(declare-fun |block_4_function_f__13_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(declare-fun |block_5_f_12_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(block_4_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (block_4_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) (and (and (and (and (= state_1 state_0) (= error_0 0)) true) (and true (= x_3_1 x_3_0))) true)) true) (block_5_f_12_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(declare-fun |block_6_return_function_f__13_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(declare-fun |block_7_function_f__13_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| Int |state_type| Int ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (block_5_f_12_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> true true) (and (= expr_8_0 0) (and (=> true (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_1) (and (and (>= x_3_1 0) (<= x_3_1 115792089237316195423570985008687907853269984665640564039457584007913129639935)) true))))))) (and (not expr_9_1) (= error_1 1))) (block_7_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (block_7_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) (summary_3_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (block_5_f_12_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) (and (= error_1 error_0) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> true true) (and (= expr_8_0 0) (and (=> true (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_1) (and (and (>= x_3_1 0) (<= x_3_1 115792089237316195423570985008687907853269984665640564039457584007913129639935)) true)))))))) true) (block_6_return_function_f__13_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (block_6_return_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1) true) true) (summary_3_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (interface_0_C_14_0 this_0 abi_0 crypto_0 state_0) true) (and (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) (summary_3_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)) (= error_0 0))) (interface_0_C_14_0 this_0 abi_0 crypto_0 state_1)))) + + +(declare-fun |contract_initializer_8_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(declare-fun |contract_initializer_entry_9_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (= state_1 state_0) (= error_0 0)) true) (contract_initializer_entry_9_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) + + +(declare-fun |contract_initializer_after_init_10_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (contract_initializer_entry_9_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) true) true) (contract_initializer_after_init_10_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (contract_initializer_after_init_10_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) true) true) (contract_initializer_8_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) + + +(declare-fun |implicit_constructor_entry_11_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (and (= state_1 state_0) (= error_0 0)) true) true) (implicit_constructor_entry_11_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (implicit_constructor_entry_11_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) (and (contract_initializer_8_C_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_1 state_2) true)) (> error_1 0)) (summary_constructor_2_C_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 state_2)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (implicit_constructor_entry_11_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) (and (= error_1 0) (and (contract_initializer_8_C_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_1 state_2) true))) true) (summary_constructor_2_C_14_0 error_1 this_0 abi_0 crypto_0 tx_0 state_0 state_2)))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int)) +(=> (and (and (summary_constructor_2_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1) true) (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (= (|msg.value| tx_0) 0)) (= error_0 0))) (interface_0_C_14_0 this_0 abi_0 crypto_0 state_1)))) + + +(declare-fun |error_target_2_0| () Bool) +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int)) +(=> (and (and (interface_0_C_14_0 this_0 abi_0 crypto_0 state_0) true) (and (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) (summary_3_function_f__13_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 x_3_0 state_1 x_3_1)) (= error_0 1))) error_target_2_0))) + + +(assert +(forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int)) +(=> error_target_2_0 false))) +(check-sat)"}},"errors":[{"component":"general","errorCode":"3996","formattedMessage":"Warning: CHC analysis was not possible. No Horn solver was available. None of the installed solvers was enabled. + +","message":"CHC analysis was not possible. No Horn solver was available. None of the installed solvers was enabled.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"8084","formattedMessage":"Warning: BMC analysis was not possible. No SMT solver (Z3 or CVC4) was available. None of the installed solvers was enabled. + +","message":"BMC analysis was not possible. No SMT solver (Z3 or CVC4) was available. None of the installed solvers was enabled.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_solvers_z3/input.json b/test/cmdlineTests/standard_model_checker_solvers_z3/input.json new file mode 100644 index 000000000..91db9ad7f --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_z3/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "solvers": ["z3"] + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_z3/output.json b/test/cmdlineTests/standard_model_checker_solvers_z3/output.json new file mode 100644 index 000000000..12dfe5871 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_z3/output.json @@ -0,0 +1,21 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0) + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0)","severity":"warning","sourceLocation":{"end":119,"file":"A","start":106},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/input.json b/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/input.json new file mode 100644 index 000000000..19913a562 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\n\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all", + "solvers": ["z3", "smtlib2"] + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/output.json b/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/output.json new file mode 100644 index 000000000..12dfe5871 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_solvers_z3_smtlib2/output.json @@ -0,0 +1,21 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0) + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +C.constructor() +C.f(0)","severity":"warning","sourceLocation":{"end":119,"file":"A","start":106},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_all/output.json b/test/cmdlineTests/standard_model_checker_timeout_all/output.json index 15f7ad969..335a6f99e 100644 --- a/test/cmdlineTests/standard_model_checker_timeout_all/output.json +++ b/test/cmdlineTests/standard_model_checker_timeout_all/output.json @@ -254,17 +254,8 @@ (assert (= |EVALEXPR_3| r_33_1)) (check-sat) (get-value (|EVALEXPR_0| |EVALEXPR_1| |EVALEXPR_2| |EVALEXPR_3| )) -"}},"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. - --> A:6:85: - | -6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} - | ^^^^^^^^^^^^^^^^^^ +"}},"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":227,"file":"A","start":209},"type":"Warning"},{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. - --> A:6:85: - | -6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} - | ^^^^^^^^^^^^^^^^^^ -Note: +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"},{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":227,"file":"A","start":209},"type":"Warning"}],"sources":{"A":{"id":0}}} +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json b/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json index 0c3de2823..c1631811f 100644 --- a/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json @@ -518,11 +518,6 @@ (assert (and (and (and true true) (and (= expr_21_1 (= expr_19_1 expr_20_0)) (and (=> (and true true) true) (and (= expr_20_0 0) (and (=> (and true true) (and (>= expr_19_1 0) (<= expr_19_1 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_19_1 (ite (= expr_18_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_18_0 0) (< r_div_mod_0_0 expr_18_0))) (and (= (+ (* d_div_mod_0_0 expr_18_0) r_div_mod_0_0) expr_17_0) (and (=> (and true true) (and (>= expr_18_0 0) (<= expr_18_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_18_0 k_7_0) (and (=> (and true true) (and (>= expr_17_0 0) (<= expr_17_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_17_0 x_3_0) (and (=> (and true true) expr_13_1) (and (= expr_13_1 (> expr_11_0 expr_12_0)) (and (=> (and true true) true) (and (= expr_12_0 0) (and (=> (and true true) (and (>= expr_11_0 0) (<= expr_11_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_11_0 k_7_0) (and (and (>= k_7_0 0) (<= k_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_5_0 0) (<= y_5_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_3_0 0) (<= x_3_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_33_0 0) (and (and (and (and (and (and (and (and (and (and (and (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.difficulty| tx_0) 0) (<= (|block.difficulty| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3204897777)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 191)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 6)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 219)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 241)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true))))))))))))))))))))))) (not expr_21_1))) (check-sat) -"}},"errors":[{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. - --> A:6:85: - | -6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} - | ^^^^^^^^^^^^^^^^^^ -Note: +"}},"errors":[{"component":"general","errorCode":"2788","formattedMessage":"Warning: BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":227,"file":"A","start":209},"type":"Warning"}],"sources":{"A":{"id":0}}} +","message":"BMC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_chc/output.json b/test/cmdlineTests/standard_model_checker_timeout_chc/output.json index 34de85f8f..5e66cb899 100644 --- a/test/cmdlineTests/standard_model_checker_timeout_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_timeout_chc/output.json @@ -1,7 +1,3 @@ -{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. - --> A:6:85: - | -6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} - | ^^^^^^^^^^^^^^^^^^ +{"errors":[{"component":"general","errorCode":"5840","formattedMessage":"Warning: CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. -","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":227,"file":"A","start":209},"type":"Warning"}],"sources":{"A":{"id":0}}} +","message":"CHC: 1 verification condition(s) could not be proved. Enable the model checker option \"show unproved\" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_optimizer_generatedSources/output.json b/test/cmdlineTests/standard_optimizer_generatedSources/output.json index 9e8c34405..439101cc4 100644 --- a/test/cmdlineTests/standard_optimizer_generatedSources/output.json +++ b/test/cmdlineTests/standard_optimizer_generatedSources/output.json @@ -1,4 +1,4 @@ -{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:1456:1","statements":[{"nodeType":"YulBlock","src":"6:3:1","statements":[]},{"body":{"nodeType":"YulBlock","src":"109:1031:1","statements":[{"nodeType":"YulVariableDeclaration","src":"119:12:1","value":{"kind":"number","nodeType":"YulLiteral","src":"129:2:1","type":"","value":"32"},"variables":[{"name":"_1","nodeType":"YulTypedName","src":"123:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"176:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"185:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"188:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"178:6:1"},"nodeType":"YulFunctionCall","src":"178:12:1"},"nodeType":"YulExpressionStatement","src":"178:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"151:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"160:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"147:3:1"},"nodeType":"YulFunctionCall","src":"147:23:1"},{"name":"_1","nodeType":"YulIdentifier","src":"172:2:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"143:3:1"},"nodeType":"YulFunctionCall","src":"143:32:1"},"nodeType":"YulIf","src":"140:2:1"},{"nodeType":"YulVariableDeclaration","src":"201:37:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"228:9:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"215:12:1"},"nodeType":"YulFunctionCall","src":"215:23:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"205:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"247:28:1","value":{"kind":"number","nodeType":"YulLiteral","src":"257:18:1","type":"","value":"0xffffffffffffffff"},"variables":[{"name":"_2","nodeType":"YulTypedName","src":"251:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"302:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"311:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"314:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"304:6:1"},"nodeType":"YulFunctionCall","src":"304:12:1"},"nodeType":"YulExpressionStatement","src":"304:12:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"290:6:1"},{"name":"_2","nodeType":"YulIdentifier","src":"298:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"287:2:1"},"nodeType":"YulFunctionCall","src":"287:14:1"},"nodeType":"YulIf","src":"284:2:1"},{"nodeType":"YulVariableDeclaration","src":"327:32:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"341:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"352:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"337:3:1"},"nodeType":"YulFunctionCall","src":"337:22:1"},"variables":[{"name":"_3","nodeType":"YulTypedName","src":"331:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"407:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"416:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"419:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"409:6:1"},"nodeType":"YulFunctionCall","src":"409:12:1"},"nodeType":"YulExpressionStatement","src":"409:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"386:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"390:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"382:3:1"},"nodeType":"YulFunctionCall","src":"382:13:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"397:7:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"378:3:1"},"nodeType":"YulFunctionCall","src":"378:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"371:6:1"},"nodeType":"YulFunctionCall","src":"371:35:1"},"nodeType":"YulIf","src":"368:2:1"},{"nodeType":"YulVariableDeclaration","src":"432:26:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"455:2:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"442:12:1"},"nodeType":"YulFunctionCall","src":"442:16:1"},"variables":[{"name":"_4","nodeType":"YulTypedName","src":"436:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"481:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"483:16:1"},"nodeType":"YulFunctionCall","src":"483:18:1"},"nodeType":"YulExpressionStatement","src":"483:18:1"}]},"condition":{"arguments":[{"name":"_4","nodeType":"YulIdentifier","src":"473:2:1"},{"name":"_2","nodeType":"YulIdentifier","src":"477:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"470:2:1"},"nodeType":"YulFunctionCall","src":"470:10:1"},"nodeType":"YulIf","src":"467:2:1"},{"nodeType":"YulVariableDeclaration","src":"512:20:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"526:1:1","type":"","value":"5"},{"name":"_4","nodeType":"YulIdentifier","src":"529:2:1"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"522:3:1"},"nodeType":"YulFunctionCall","src":"522:10:1"},"variables":[{"name":"_5","nodeType":"YulTypedName","src":"516:2:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"541:23:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"561:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"555:5:1"},"nodeType":"YulFunctionCall","src":"555:9:1"},"variables":[{"name":"memPtr","nodeType":"YulTypedName","src":"545:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"573:56:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"595:6:1"},{"arguments":[{"arguments":[{"name":"_5","nodeType":"YulIdentifier","src":"611:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"615:2:1","type":"","value":"63"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"607:3:1"},"nodeType":"YulFunctionCall","src":"607:11:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"624:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"620:3:1"},"nodeType":"YulFunctionCall","src":"620:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"603:3:1"},"nodeType":"YulFunctionCall","src":"603:25:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"591:3:1"},"nodeType":"YulFunctionCall","src":"591:38:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"577:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"688:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"690:16:1"},"nodeType":"YulFunctionCall","src":"690:18:1"},"nodeType":"YulExpressionStatement","src":"690:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"647:10:1"},{"name":"_2","nodeType":"YulIdentifier","src":"659:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"644:2:1"},"nodeType":"YulFunctionCall","src":"644:18:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"667:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"679:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"664:2:1"},"nodeType":"YulFunctionCall","src":"664:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"641:2:1"},"nodeType":"YulFunctionCall","src":"641:46:1"},"nodeType":"YulIf","src":"638:2:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"726:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"730:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"719:6:1"},"nodeType":"YulFunctionCall","src":"719:22:1"},"nodeType":"YulExpressionStatement","src":"719:22:1"},{"nodeType":"YulVariableDeclaration","src":"750:17:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"761:6:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"754:3:1","type":""}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"783:6:1"},{"name":"_4","nodeType":"YulIdentifier","src":"791:2:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"776:6:1"},"nodeType":"YulFunctionCall","src":"776:18:1"},"nodeType":"YulExpressionStatement","src":"776:18:1"},{"nodeType":"YulAssignment","src":"803:22:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"814:6:1"},{"name":"_1","nodeType":"YulIdentifier","src":"822:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"810:3:1"},"nodeType":"YulFunctionCall","src":"810:15:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"803:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"834:22:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"849:2:1"},{"name":"_1","nodeType":"YulIdentifier","src":"853:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"845:3:1"},"nodeType":"YulFunctionCall","src":"845:11:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"838:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"902:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"911:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"914:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"904:6:1"},"nodeType":"YulFunctionCall","src":"904:12:1"},"nodeType":"YulExpressionStatement","src":"904:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"879:2:1"},{"name":"_5","nodeType":"YulIdentifier","src":"883:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"875:3:1"},"nodeType":"YulFunctionCall","src":"875:11:1"},{"name":"_1","nodeType":"YulIdentifier","src":"888:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"871:3:1"},"nodeType":"YulFunctionCall","src":"871:20:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"893:7:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"868:2:1"},"nodeType":"YulFunctionCall","src":"868:33:1"},"nodeType":"YulIf","src":"865:2:1"},{"nodeType":"YulVariableDeclaration","src":"927:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"936:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"931:1:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"991:118:1","statements":[{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1012:3:1"},{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1030:3:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1017:12:1"},"nodeType":"YulFunctionCall","src":"1017:17:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1005:6:1"},"nodeType":"YulFunctionCall","src":"1005:30:1"},"nodeType":"YulExpressionStatement","src":"1005:30:1"},{"nodeType":"YulAssignment","src":"1048:19:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1059:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1064:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1055:3:1"},"nodeType":"YulFunctionCall","src":"1055:12:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"1048:3:1"}]},{"nodeType":"YulAssignment","src":"1080:19:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1091:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1096:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1087:3:1"},"nodeType":"YulFunctionCall","src":"1087:12:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"1080:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"957:1:1"},{"name":"_4","nodeType":"YulIdentifier","src":"960:2:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"954:2:1"},"nodeType":"YulFunctionCall","src":"954:9:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"964:18:1","statements":[{"nodeType":"YulAssignment","src":"966:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"975:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"978:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"971:3:1"},"nodeType":"YulFunctionCall","src":"971:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"966:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"950:3:1","statements":[]},"src":"946:163:1"},{"nodeType":"YulAssignment","src":"1118:16:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"1128:6:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1118:6:1"}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"75:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"86:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"98:6:1","type":""}],"src":"14:1126:1"},{"body":{"nodeType":"YulBlock","src":"1246:76:1","statements":[{"nodeType":"YulAssignment","src":"1256:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1268:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1279:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1264:3:1"},"nodeType":"YulFunctionCall","src":"1264:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1256:4:1"}]},{"expression":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1298:9:1"},{"name":"value0","nodeType":"YulIdentifier","src":"1309:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1291:6:1"},"nodeType":"YulFunctionCall","src":"1291:25:1"},"nodeType":"YulExpressionStatement","src":"1291:25:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1215:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1226:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1237:4:1","type":""}],"src":"1145:177:1"},{"body":{"nodeType":"YulBlock","src":"1359:95:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1376:1:1","type":"","value":"0"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1383:3:1","type":"","value":"224"},{"kind":"number","nodeType":"YulLiteral","src":"1388:10:1","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"1379:3:1"},"nodeType":"YulFunctionCall","src":"1379:20:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1369:6:1"},"nodeType":"YulFunctionCall","src":"1369:31:1"},"nodeType":"YulExpressionStatement","src":"1369:31:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1416:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"1419:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1409:6:1"},"nodeType":"YulFunctionCall","src":"1409:15:1"},"nodeType":"YulExpressionStatement","src":"1409:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1440:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1443:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1433:6:1"},"nodeType":"YulFunctionCall","src":"1433:15:1"},"nodeType":"YulExpressionStatement","src":"1433:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"1327:127:1"}]},"contents":"{ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:1456:1","statements":[{"nodeType":"YulBlock","src":"6:3:1","statements":[]},{"body":{"nodeType":"YulBlock","src":"109:1031:1","statements":[{"nodeType":"YulVariableDeclaration","src":"119:12:1","value":{"kind":"number","nodeType":"YulLiteral","src":"129:2:1","type":"","value":"32"},"variables":[{"name":"_1","nodeType":"YulTypedName","src":"123:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"176:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"185:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"188:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"178:6:1"},"nodeType":"YulFunctionCall","src":"178:12:1"},"nodeType":"YulExpressionStatement","src":"178:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"151:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"160:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"147:3:1"},"nodeType":"YulFunctionCall","src":"147:23:1"},{"name":"_1","nodeType":"YulIdentifier","src":"172:2:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"143:3:1"},"nodeType":"YulFunctionCall","src":"143:32:1"},"nodeType":"YulIf","src":"140:52:1"},{"nodeType":"YulVariableDeclaration","src":"201:37:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"228:9:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"215:12:1"},"nodeType":"YulFunctionCall","src":"215:23:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"205:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"247:28:1","value":{"kind":"number","nodeType":"YulLiteral","src":"257:18:1","type":"","value":"0xffffffffffffffff"},"variables":[{"name":"_2","nodeType":"YulTypedName","src":"251:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"302:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"311:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"314:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"304:6:1"},"nodeType":"YulFunctionCall","src":"304:12:1"},"nodeType":"YulExpressionStatement","src":"304:12:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"290:6:1"},{"name":"_2","nodeType":"YulIdentifier","src":"298:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"287:2:1"},"nodeType":"YulFunctionCall","src":"287:14:1"},"nodeType":"YulIf","src":"284:34:1"},{"nodeType":"YulVariableDeclaration","src":"327:32:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"341:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"352:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"337:3:1"},"nodeType":"YulFunctionCall","src":"337:22:1"},"variables":[{"name":"_3","nodeType":"YulTypedName","src":"331:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"407:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"416:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"419:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"409:6:1"},"nodeType":"YulFunctionCall","src":"409:12:1"},"nodeType":"YulExpressionStatement","src":"409:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"386:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"390:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"382:3:1"},"nodeType":"YulFunctionCall","src":"382:13:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"397:7:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"378:3:1"},"nodeType":"YulFunctionCall","src":"378:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"371:6:1"},"nodeType":"YulFunctionCall","src":"371:35:1"},"nodeType":"YulIf","src":"368:55:1"},{"nodeType":"YulVariableDeclaration","src":"432:26:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"455:2:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"442:12:1"},"nodeType":"YulFunctionCall","src":"442:16:1"},"variables":[{"name":"_4","nodeType":"YulTypedName","src":"436:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"481:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"483:16:1"},"nodeType":"YulFunctionCall","src":"483:18:1"},"nodeType":"YulExpressionStatement","src":"483:18:1"}]},"condition":{"arguments":[{"name":"_4","nodeType":"YulIdentifier","src":"473:2:1"},{"name":"_2","nodeType":"YulIdentifier","src":"477:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"470:2:1"},"nodeType":"YulFunctionCall","src":"470:10:1"},"nodeType":"YulIf","src":"467:36:1"},{"nodeType":"YulVariableDeclaration","src":"512:20:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"526:1:1","type":"","value":"5"},{"name":"_4","nodeType":"YulIdentifier","src":"529:2:1"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"522:3:1"},"nodeType":"YulFunctionCall","src":"522:10:1"},"variables":[{"name":"_5","nodeType":"YulTypedName","src":"516:2:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"541:23:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"561:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"555:5:1"},"nodeType":"YulFunctionCall","src":"555:9:1"},"variables":[{"name":"memPtr","nodeType":"YulTypedName","src":"545:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"573:56:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"595:6:1"},{"arguments":[{"arguments":[{"name":"_5","nodeType":"YulIdentifier","src":"611:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"615:2:1","type":"","value":"63"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"607:3:1"},"nodeType":"YulFunctionCall","src":"607:11:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"624:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"620:3:1"},"nodeType":"YulFunctionCall","src":"620:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"603:3:1"},"nodeType":"YulFunctionCall","src":"603:25:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"591:3:1"},"nodeType":"YulFunctionCall","src":"591:38:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"577:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"688:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"690:16:1"},"nodeType":"YulFunctionCall","src":"690:18:1"},"nodeType":"YulExpressionStatement","src":"690:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"647:10:1"},{"name":"_2","nodeType":"YulIdentifier","src":"659:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"644:2:1"},"nodeType":"YulFunctionCall","src":"644:18:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"667:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"679:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"664:2:1"},"nodeType":"YulFunctionCall","src":"664:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"641:2:1"},"nodeType":"YulFunctionCall","src":"641:46:1"},"nodeType":"YulIf","src":"638:72:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"726:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"730:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"719:6:1"},"nodeType":"YulFunctionCall","src":"719:22:1"},"nodeType":"YulExpressionStatement","src":"719:22:1"},{"nodeType":"YulVariableDeclaration","src":"750:17:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"761:6:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"754:3:1","type":""}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"783:6:1"},{"name":"_4","nodeType":"YulIdentifier","src":"791:2:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"776:6:1"},"nodeType":"YulFunctionCall","src":"776:18:1"},"nodeType":"YulExpressionStatement","src":"776:18:1"},{"nodeType":"YulAssignment","src":"803:22:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"814:6:1"},{"name":"_1","nodeType":"YulIdentifier","src":"822:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"810:3:1"},"nodeType":"YulFunctionCall","src":"810:15:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"803:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"834:22:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"849:2:1"},{"name":"_1","nodeType":"YulIdentifier","src":"853:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"845:3:1"},"nodeType":"YulFunctionCall","src":"845:11:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"838:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"902:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"911:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"914:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"904:6:1"},"nodeType":"YulFunctionCall","src":"904:12:1"},"nodeType":"YulExpressionStatement","src":"904:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"879:2:1"},{"name":"_5","nodeType":"YulIdentifier","src":"883:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"875:3:1"},"nodeType":"YulFunctionCall","src":"875:11:1"},{"name":"_1","nodeType":"YulIdentifier","src":"888:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"871:3:1"},"nodeType":"YulFunctionCall","src":"871:20:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"893:7:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"868:2:1"},"nodeType":"YulFunctionCall","src":"868:33:1"},"nodeType":"YulIf","src":"865:53:1"},{"nodeType":"YulVariableDeclaration","src":"927:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"936:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"931:1:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"991:118:1","statements":[{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1012:3:1"},{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1030:3:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1017:12:1"},"nodeType":"YulFunctionCall","src":"1017:17:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1005:6:1"},"nodeType":"YulFunctionCall","src":"1005:30:1"},"nodeType":"YulExpressionStatement","src":"1005:30:1"},{"nodeType":"YulAssignment","src":"1048:19:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1059:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1064:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1055:3:1"},"nodeType":"YulFunctionCall","src":"1055:12:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"1048:3:1"}]},{"nodeType":"YulAssignment","src":"1080:19:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1091:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1096:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1087:3:1"},"nodeType":"YulFunctionCall","src":"1087:12:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"1080:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"957:1:1"},{"name":"_4","nodeType":"YulIdentifier","src":"960:2:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"954:2:1"},"nodeType":"YulFunctionCall","src":"954:9:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"964:18:1","statements":[{"nodeType":"YulAssignment","src":"966:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"975:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"978:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"971:3:1"},"nodeType":"YulFunctionCall","src":"971:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"966:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"950:3:1","statements":[]},"src":"946:163:1"},{"nodeType":"YulAssignment","src":"1118:16:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"1128:6:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1118:6:1"}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"75:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"86:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"98:6:1","type":""}],"src":"14:1126:1"},{"body":{"nodeType":"YulBlock","src":"1246:76:1","statements":[{"nodeType":"YulAssignment","src":"1256:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1268:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1279:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1264:3:1"},"nodeType":"YulFunctionCall","src":"1264:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1256:4:1"}]},{"expression":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1298:9:1"},{"name":"value0","nodeType":"YulIdentifier","src":"1309:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1291:6:1"},"nodeType":"YulFunctionCall","src":"1291:25:1"},"nodeType":"YulExpressionStatement","src":"1291:25:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1215:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1226:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1237:4:1","type":""}],"src":"1145:177:1"},{"body":{"nodeType":"YulBlock","src":"1359:95:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1376:1:1","type":"","value":"0"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1383:3:1","type":"","value":"224"},{"kind":"number","nodeType":"YulLiteral","src":"1388:10:1","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"1379:3:1"},"nodeType":"YulFunctionCall","src":"1379:20:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1369:6:1"},"nodeType":"YulFunctionCall","src":"1369:31:1"},"nodeType":"YulExpressionStatement","src":"1369:31:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1416:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"1419:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1409:6:1"},"nodeType":"YulFunctionCall","src":"1409:15:1"},"nodeType":"YulExpressionStatement","src":"1409:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1440:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1443:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1433:6:1"},"nodeType":"YulFunctionCall","src":"1433:15:1"},"nodeType":"YulExpressionStatement","src":"1433:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"1327:127:1"}]},"contents":"{ { } function abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr(headStart, dataEnd) -> value0 { diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json index 4f2399134..dca052187 100644 --- a/test/cmdlineTests/standard_viair_requested/output.json +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -6,9 +6,10 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_3\" { code { - /// @src 0:79,92 + /// @src 0:79:92 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -23,11 +24,13 @@ object \"C_3\" { memPtr := mload(64) } + /// @src 0:79:92 function constructor_C_3() { - /// @src 0:79,92 + /// @src 0:79:92 } + /// @src 0:79:92 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -36,7 +39,7 @@ object \"C_3\" { } object \"C_3_deployed\" { code { - /// @src 0:79,92 + /// @src 0:79:92 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -79,9 +82,10 @@ object \"C_3\" { *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"D_16\" { code { - /// @src 0:93,146 + /// @src 0:93:146 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -96,11 +100,13 @@ object \"D_16\" { memPtr := mload(64) } + /// @src 0:93:146 function constructor_D_16() { - /// @src 0:93,146 + /// @src 0:93:146 } + /// @src 0:93:146 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -109,7 +115,7 @@ object \"D_16\" { } object \"D_16_deployed\" { code { - /// @src 0:93,146 + /// @src 0:93:146 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -148,10 +154,10 @@ object \"D_16\" { memPtr := mload(64) } + /// @src 0:106:144 function fun_f_15() { - /// @src 0:106,144 - /// @src 0:134,141 + /// @src 0:134:141 let _1 := allocate_unbounded() let _2 := add(_1, datasize(\"C_3\")) if or(gt(_2, 0xffffffffffffffff), lt(_2, _1)) { panic_error_0x41() } @@ -162,10 +168,11 @@ object \"D_16\" { if iszero(expr_12_address) { revert_forward_1() } - /// @src 0:128,141 + /// @src 0:128:141 let var_c_8_address := expr_12_address } + /// @src 0:93:146 function panic_error_0x41() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) @@ -206,9 +213,10 @@ object \"D_16\" { * !USE AT YOUR OWN RISK! * *=====================================================*/ + /// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_3\" { code { - /// @src 0:79,92 + /// @src 0:79:92 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -223,11 +231,13 @@ object \"D_16\" { memPtr := mload(64) } + /// @src 0:79:92 function constructor_C_3() { - /// @src 0:79,92 + /// @src 0:79:92 } + /// @src 0:79:92 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -236,7 +246,7 @@ object \"D_16\" { } object \"C_3_deployed\" { code { - /// @src 0:79,92 + /// @src 0:79:92 mstore(64, 128) if iszero(lt(calldatasize(), 4)) diff --git a/test/cmdlineTests/standard_yul_object/output.json b/test/cmdlineTests/standard_yul_object/output.json index c0a3434a9..49aaa6a74 100644 --- a/test/cmdlineTests/standard_yul_object/output.json +++ b/test/cmdlineTests/standard_yul_object/output.json @@ -1,4 +1,5 @@ -{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 +{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" /* \"A\":39:61 */ + data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 /* \"A\":80:81 */ 0x00 /* \"A\":76:77 */ diff --git a/test/cmdlineTests/standard_yul_object_name/output.json b/test/cmdlineTests/standard_yul_object_name/output.json index 1a686914d..f2e85ed7c 100644 --- a/test/cmdlineTests/standard_yul_object_name/output.json +++ b/test/cmdlineTests/standard_yul_object_name/output.json @@ -1,4 +1,5 @@ -{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 +{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" /* \"A\":39:61 */ + data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 /* \"A\":80:81 */ 0x00 /* \"A\":76:77 */ diff --git a/test/cmdlineTests/strict_asm_optimizer_steps/output b/test/cmdlineTests/strict_asm_optimizer_steps/output index 595199408..bf9f34e7a 100644 --- a/test/cmdlineTests/strict_asm_optimizer_steps/output +++ b/test/cmdlineTests/strict_asm_optimizer_steps/output @@ -36,7 +36,7 @@ Text representation: mstore /* "strict_asm_optimizer_steps/input.yul":61:72 */ callvalue - /* "strict_asm_optimizer_steps/input.yul":58:60 */ + /* "strict_asm_optimizer_steps/input.yul":58:89 */ iszero tag_1 jumpi @@ -46,16 +46,17 @@ Text representation: dup1 /* "strict_asm_optimizer_steps/input.yul":75:87 */ revert - /* "strict_asm_optimizer_steps/input.yul":58:60 */ + /* "strict_asm_optimizer_steps/input.yul":58:89 */ tag_1: - /* "strict_asm_optimizer_steps/input.yul":98:163 */ + /* "strict_asm_optimizer_steps/input.yul":138:162 */ dataSize(sub_0) + /* "strict_asm_optimizer_steps/input.yul":110:136 */ dataOffset(sub_0) /* "strict_asm_optimizer_steps/input.yul":107:108 */ 0x00 /* "strict_asm_optimizer_steps/input.yul":98:163 */ codecopy - /* "strict_asm_optimizer_steps/input.yul":172:207 */ + /* "strict_asm_optimizer_steps/input.yul":182:206 */ dataSize(sub_0) /* "strict_asm_optimizer_steps/input.yul":179:180 */ 0x00 diff --git a/test/cmdlineTests/viair_abicoder_v1/output b/test/cmdlineTests/viair_abicoder_v1/output index d16d77f2d..d42a032f8 100644 --- a/test/cmdlineTests/viair_abicoder_v1/output +++ b/test/cmdlineTests/viair_abicoder_v1/output @@ -7,9 +7,10 @@ IR: *=====================================================*/ +/// @use-src 0:"viair_abicoder_v1/input.sol", 1:"#utility.yul" object "test_11" { code { - /// @src 0:79,169 + /// @src 0:79:169 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,11 +25,13 @@ object "test_11" { memPtr := mload(64) } + /// @src 0:79:169 function constructor_test_11() { - /// @src 0:79,169 + /// @src 0:79:169 } + /// @src 0:79:169 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -37,7 +40,7 @@ object "test_11" { } object "test_11_deployed" { code { - /// @src 0:79,169 + /// @src 0:79:169 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -86,19 +89,20 @@ object "test_11" { cleaned := iszero(iszero(value)) } + /// @src 0:99:167 function fun_f_10() -> var__5 { - /// @src 0:99,167 - /// @src 0:133,137 + /// @src 0:133:137 let zero_t_bool_1 := zero_value_for_split_t_bool() var__5 := zero_t_bool_1 - /// @src 0:156,160 + /// @src 0:156:160 let expr_7 := 0x01 - /// @src 0:149,160 + /// @src 0:149:160 var__5 := expr_7 leave } + /// @src 0:79:169 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/viair_subobjects/output b/test/cmdlineTests/viair_subobjects/output index cf52c5e97..9ca620d06 100644 --- a/test/cmdlineTests/viair_subobjects/output +++ b/test/cmdlineTests/viair_subobjects/output @@ -12,9 +12,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"viair_subobjects/input.sol", 1:"#utility.yul" object "C_3" { code { { + /// @src 0:82:95 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -25,6 +27,7 @@ object "C_3" { object "C_3_deployed" { code { { + /// @src 0:82:95 mstore(64, 128) revert(0, 0) } @@ -47,9 +50,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"viair_subobjects/input.sol", 1:"#utility.yul" object "D_16" { code { { + /// @src 0:96:165 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_16_deployed") @@ -60,6 +65,7 @@ object "D_16" { object "D_16_deployed" { code { { + /// @src 0:96:165 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -68,17 +74,22 @@ object "D_16" { { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + /// @src 0:149:156 let _2 := datasize("C_3") - let _3 := add(128, _2) - if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) + let _3 := add(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:96:165 */ 128)) + /// @src 0:149:156 { + /// @src 0:96:165 mstore(_1, shl(224, 0x4e487b71)) mstore(4, 0x41) revert(_1, 0x24) } - datacopy(128, dataoffset("C_3"), _2) - if iszero(create(_1, 128, _2)) + /// @src 0:149:156 + datacopy(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ dataoffset("C_3"), _2) + if iszero(create(/** @src 0:96:165 */ _1, 128, /** @src 0:149:156 */ _2)) { + /// @src 0:96:165 let pos := mload(64) returndatacopy(pos, _1, returndatasize()) revert(pos, returndatasize()) @@ -92,6 +103,7 @@ object "D_16" { object "C_3" { code { { + /// @src 0:82:95 mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -102,6 +114,7 @@ object "D_16" { object "C_3_deployed" { code { { + /// @src 0:82:95 mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/yul_optimize_runs/args b/test/cmdlineTests/yul_optimize_runs/args new file mode 100644 index 000000000..b48e7aeed --- /dev/null +++ b/test/cmdlineTests/yul_optimize_runs/args @@ -0,0 +1 @@ +--yul --yul-dialect evm --optimize --ir-optimized --optimize-runs 10000 diff --git a/test/cmdlineTests/yul_optimize_runs/err b/test/cmdlineTests/yul_optimize_runs/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/yul_optimize_runs/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/yul_optimize_runs/input.yul b/test/cmdlineTests/yul_optimize_runs/input.yul new file mode 100644 index 000000000..aeb7fd282 --- /dev/null +++ b/test/cmdlineTests/yul_optimize_runs/input.yul @@ -0,0 +1,13 @@ +object "RunsTest1" { + code { + // Deploy the contract + datacopy(0, dataoffset("Runtime"), datasize("Runtime")) + return(0, datasize("Runtime")) + } + object "Runtime" { + code { + let funcSel := shl(224, 0xabc12345) + mstore(0, funcSel) + } + } +} diff --git a/test/cmdlineTests/yul_optimize_runs/output b/test/cmdlineTests/yul_optimize_runs/output new file mode 100644 index 000000000..20196dcb5 --- /dev/null +++ b/test/cmdlineTests/yul_optimize_runs/output @@ -0,0 +1,52 @@ + +======= yul_optimize_runs/input.yul (EVM) ======= + +Pretty printed source: +object "RunsTest1" { + code { + { + let _1 := datasize("Runtime") + datacopy(0, dataoffset("Runtime"), _1) + return(0, _1) + } + } + object "Runtime" { + code { + { + mstore(0, 0xabc1234500000000000000000000000000000000000000000000000000000000) + } + } + } +} + + +Binary representation: +602480600d600039806000f3fe7fabc1234500000000000000000000000000000000000000000000000000000000600052 + +Text representation: + /* "yul_optimize_runs/input.yul":106:125 */ + dataSize(sub_0) + dup1 + /* "yul_optimize_runs/input.yul":83:104 */ + dataOffset(sub_0) + /* "yul_optimize_runs/input.yul":80:81 */ + 0x00 + /* "yul_optimize_runs/input.yul":71:126 */ + codecopy + /* "yul_optimize_runs/input.yul":145:164 */ + dup1 + /* "yul_optimize_runs/input.yul":80:81 */ + 0x00 + /* "yul_optimize_runs/input.yul":135:165 */ + return +stop + +sub_0: assembly { + /* "yul_optimize_runs/input.yul":237:257 */ + 0xabc1234500000000000000000000000000000000000000000000000000000000 + /* "yul_optimize_runs/input.yul":277:278 */ + 0x00 + /* "yul_optimize_runs/input.yul":270:288 */ + mstore +} + diff --git a/test/cmdlineTests/yul_optimizer_steps/output b/test/cmdlineTests/yul_optimizer_steps/output index 6f42b4761..af9ef0988 100644 --- a/test/cmdlineTests/yul_optimizer_steps/output +++ b/test/cmdlineTests/yul_optimizer_steps/output @@ -6,9 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ +/// @use-src 0:"yul_optimizer_steps/input.sol", 1:"#utility.yul" object "C_7" { code { { + /// @src 0:80:112 mstore(64, 128) if callvalue() { @@ -26,6 +28,7 @@ object "C_7" { object "C_7_deployed" { code { { + /// @src 0:80:112 mstore(64, 128) if iszero(lt(calldatasize(), 4)) { diff --git a/test/cmdlineTests/yul_source_locations/output.json b/test/cmdlineTests/yul_source_locations/output.json index 19f8b3714..f5f57a6d6 100644 --- a/test/cmdlineTests/yul_source_locations/output.json +++ b/test/cmdlineTests/yul_source_locations/output.json @@ -6,9 +6,10 @@ *=====================================================*/ +/// @use-src 0:\"C\", 1:\"D\", 2:\"#utility.yul\" object \"C_54\" { code { - /// @src 0:79,428 + /// @src 0:79:428 mstore(64, 160) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -52,23 +53,25 @@ object \"C_54\" { cleaned := value } + /// @src 0:175:223 function constructor_C_54(var__init_12) { - /// @src 0:175,223 + /// @src 0:175:223 - /// @src 0:147,149 + /// @src 0:147:149 let expr_7 := 0x2a let _3 := convert_t_rational_42_by_1_to_t_int256(expr_7) mstore(128, _3) - /// @src 0:214,219 + /// @src 0:214:219 let _4 := var__init_12 let expr_16 := _4 - /// @src 0:203,219 + /// @src 0:203:219 update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_16) let expr_17 := expr_16 } + /// @src 0:79:428 function convert_t_int256_to_t_int256(value) -> converted { converted := cleanup_t_int256(value) @@ -147,7 +150,7 @@ object \"C_54\" { } object \"C_54_deployed\" { code { - /// @src 0:79,428 + /// @src 0:79:428 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -262,9 +265,9 @@ object \"C_54\" { cleaned := and(value, 0xffffffffffffffffffffffffffffffffffffffff) } - /// @src 0:93,119 + /// @src 0:93:119 function constant_constVar_5() -> ret { - /// @src 0:117,119 + /// @src 0:117:119 let expr_4 := 0x29 let _2 := convert_t_rational_41_by_1_to_t_int256(expr_4) @@ -302,28 +305,29 @@ object \"C_54\" { mstore(64, newFreePtr) } + /// @src 0:343:426 function fun_f2_53() -> var__42 { - /// @src 0:343,426 - /// @src 0:375,378 + /// @src 0:375:378 let zero_t_int256_4 := zero_value_for_split_t_int256() var__42 := zero_t_int256_4 var__42 := modifier_m_40(var__42) } + /// @src 0:79:428 + /// @src 0:343:426 function fun_f2_53_inner(_8) -> var__42 { - /// @src 0:343,426 var__42 := _8 - /// @src 0:392,400 + /// @src 0:392:400 let _9 := read_from_storage_split_offset_0_t_int256(0x00) let expr_44 := _9 - /// @src 0:403,407 + /// @src 0:403:407 let expr_45_address := address() - /// @src 0:403,409 + /// @src 0:403:409 let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address) let expr_46_functionSelector := 0x26121ff0 - /// @src 0:403,411 + /// @src 0:403:411 if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() } // storage for arguments and returned data @@ -344,43 +348,45 @@ object \"C_54\" { // decode return parameters from external try-call into retVars expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, returndatasize())) } - /// @src 0:392,411 + /// @src 0:392:411 let expr_48 := checked_add_t_int256(expr_44, expr_47) - /// @src 0:414,422 + /// @src 0:414:422 let _13 := loadimmutable(\"8\") let expr_49 := _13 - /// @src 0:392,422 + /// @src 0:392:422 let expr_50 := checked_add_t_int256(expr_48, expr_49) - /// @src 0:385,422 + /// @src 0:385:422 var__42 := expr_50 leave } + /// @src 0:79:428 + /// @src 0:226:302 function fun_f_30() -> var__23 { - /// @src 0:226,302 - /// @src 0:262,265 + /// @src 0:262:265 let zero_t_int256_1 := zero_value_for_split_t_int256() var__23 := zero_t_int256_1 - /// @src 0:279,287 + /// @src 0:279:287 let expr_25 := constant_constVar_5() - /// @src 0:290,298 + /// @src 0:290:298 let _3 := loadimmutable(\"8\") let expr_26 := _3 - /// @src 0:279,298 + /// @src 0:279:298 let expr_27 := checked_add_t_int256(expr_25, expr_26) - /// @src 0:272,298 + /// @src 0:272:298 var__23 := expr_27 leave } + /// @src 0:79:428 + /// @src 0:152:171 function getter_fun_stateVar_10() -> ret { - /// @src 0:152,171 let slot := 0 let offset := 0 @@ -388,6 +394,7 @@ object \"C_54\" { ret := read_from_storage_split_dynamic_t_int256(slot, offset) } + /// @src 0:79:428 function increment_t_int256(value) -> ret { value := cleanup_t_int256(value) @@ -395,19 +402,20 @@ object \"C_54\" { ret := add(value, 1) } + /// @src 0:304:341 function modifier_m_40(var__42) -> _5 { - /// @src 0:304,341 _5 := var__42 - /// @src 0:322,332 + /// @src 0:322:332 let _7 := read_from_storage_split_offset_0_t_int256(0x00) let _6 := increment_t_int256(_7) update_storage_value_offset_0t_int256_to_t_int256(0x00, _6) let expr_33 := _7 - /// @src 0:336,337 + /// @src 0:336:337 _5 := fun_f2_53_inner(var__42) } + /// @src 0:79:428 function panic_error_0x11() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) @@ -535,9 +543,10 @@ object \"C_54\" { *=====================================================*/ +/// @use-src 0:\"C\", 1:\"D\", 2:\"#utility.yul\" object \"D_72\" { code { - /// @src 1:91,166 + /// @src 1:91:166 mstore(64, 160) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -597,42 +606,46 @@ object \"D_72\" { cleaned := value } + /// @src 0:175:223 function constructor_C_54(var__init_12) { - /// @src 0:175,223 + /// @src 0:175:223 - /// @src 0:147,149 + /// @src 0:147:149 let expr_7 := 0x2a let _6 := convert_t_rational_42_by_1_to_t_int256(expr_7) mstore(128, _6) - /// @src 0:214,219 + /// @src 0:214:219 let _7 := var__init_12 let expr_16 := _7 - /// @src 0:203,219 + /// @src 0:203:219 update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_16) let expr_17 := expr_16 } + /// @src 1:91:166 + /// @src 1:113:164 function constructor_D_72(var__init2_63) { - /// @src 1:107,108 + /// @src 1:107:108 let expr_60 := 0x03 let _3 := convert_t_rational_3_by_1_to_t_int256(expr_60) - /// @src 1:113,164 + /// @src 1:113:164 constructor_C_54(_3) - /// @src 1:154,160 + /// @src 1:154:160 let _4 := var__init2_63 let expr_67 := _4 - /// @src 1:142,160 + /// @src 1:142:160 let _5 := read_from_storage_split_offset_0_t_int256(0x00) let expr_68 := checked_add_t_int256(_5, expr_67) update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_68) } + /// @src 1:91:166 function convert_t_int256_to_t_int256(value) -> converted { converted := cleanup_t_int256(value) @@ -737,7 +750,7 @@ object \"D_72\" { } object \"D_72_deployed\" { code { - /// @src 1:91,166 + /// @src 1:91:166 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -852,9 +865,9 @@ object \"D_72\" { cleaned := and(value, 0xffffffffffffffffffffffffffffffffffffffff) } - /// @src 0:93,119 + /// @src 0:93:119 function constant_constVar_5() -> ret { - /// @src 0:117,119 + /// @src 0:117:119 let expr_4 := 0x29 let _2 := convert_t_rational_41_by_1_to_t_int256(expr_4) @@ -892,28 +905,29 @@ object \"D_72\" { mstore(64, newFreePtr) } + /// @src 0:343:426 function fun_f2_53() -> var__42 { - /// @src 0:343,426 - /// @src 0:375,378 + /// @src 0:375:378 let zero_t_int256_4 := zero_value_for_split_t_int256() var__42 := zero_t_int256_4 var__42 := modifier_m_40(var__42) } + /// @src 1:91:166 + /// @src 0:343:426 function fun_f2_53_inner(_8) -> var__42 { - /// @src 0:343,426 var__42 := _8 - /// @src 0:392,400 + /// @src 0:392:400 let _9 := read_from_storage_split_offset_0_t_int256(0x00) let expr_44 := _9 - /// @src 0:403,407 + /// @src 0:403:407 let expr_45_address := address() - /// @src 0:403,409 + /// @src 0:403:409 let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address) let expr_46_functionSelector := 0x26121ff0 - /// @src 0:403,411 + /// @src 0:403:411 if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() } // storage for arguments and returned data @@ -934,43 +948,45 @@ object \"D_72\" { // decode return parameters from external try-call into retVars expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, returndatasize())) } - /// @src 0:392,411 + /// @src 0:392:411 let expr_48 := checked_add_t_int256(expr_44, expr_47) - /// @src 0:414,422 + /// @src 0:414:422 let _13 := loadimmutable(\"8\") let expr_49 := _13 - /// @src 0:392,422 + /// @src 0:392:422 let expr_50 := checked_add_t_int256(expr_48, expr_49) - /// @src 0:385,422 + /// @src 0:385:422 var__42 := expr_50 leave } + /// @src 1:91:166 + /// @src 0:226:302 function fun_f_30() -> var__23 { - /// @src 0:226,302 - /// @src 0:262,265 + /// @src 0:262:265 let zero_t_int256_1 := zero_value_for_split_t_int256() var__23 := zero_t_int256_1 - /// @src 0:279,287 + /// @src 0:279:287 let expr_25 := constant_constVar_5() - /// @src 0:290,298 + /// @src 0:290:298 let _3 := loadimmutable(\"8\") let expr_26 := _3 - /// @src 0:279,298 + /// @src 0:279:298 let expr_27 := checked_add_t_int256(expr_25, expr_26) - /// @src 0:272,298 + /// @src 0:272:298 var__23 := expr_27 leave } + /// @src 1:91:166 + /// @src 0:152:171 function getter_fun_stateVar_10() -> ret { - /// @src 0:152,171 let slot := 0 let offset := 0 @@ -978,6 +994,7 @@ object \"D_72\" { ret := read_from_storage_split_dynamic_t_int256(slot, offset) } + /// @src 1:91:166 function increment_t_int256(value) -> ret { value := cleanup_t_int256(value) @@ -985,19 +1002,20 @@ object \"D_72\" { ret := add(value, 1) } + /// @src 0:304:341 function modifier_m_40(var__42) -> _5 { - /// @src 0:304,341 _5 := var__42 - /// @src 0:322,332 + /// @src 0:322:332 let _7 := read_from_storage_split_offset_0_t_int256(0x00) let _6 := increment_t_int256(_7) update_storage_value_offset_0t_int256_to_t_int256(0x00, _6) let expr_33 := _7 - /// @src 0:336,337 + /// @src 0:336:337 _5 := fun_f2_53_inner(var__42) } + /// @src 1:91:166 function panic_error_0x11() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/yul_source_locations_in_asm/input.json b/test/cmdlineTests/yul_source_locations_in_asm/input.json new file mode 100644 index 000000000..c1a7209b3 --- /dev/null +++ b/test/cmdlineTests/yul_source_locations_in_asm/input.json @@ -0,0 +1,24 @@ +{ + "language": "Solidity", + "sources": + { + "C": + { + "content": "//SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\npragma abicoder v2;\n\ncontract C\n{\n int constant constVar = 41;\n int immutable immutVar = 42;\n int public stateVar;\n\n constructor(int _init)\n {\n stateVar = _init;\n }\n\n function f() external pure returns (int)\n {\n return constVar + immutVar;\n }\n modifier m()\n {\n stateVar++;\n _;\n }\n function f2() m public returns (int)\n {\n return stateVar + this.f() + immutVar;\n }\n}\n" + }, + "D": + { + "content": "//SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\npragma abicoder v2;\nimport \"C\";\n\ncontract D is C(3)\n{\n constructor(int _init2)\n {\n stateVar += _init2;\n }\n}\n" + } + }, + "settings": + { + "viaIR": true, + "optimizer": { "enabled": true }, + + "outputSelection": + { + "*": { "*": ["evm.assembly"] } + } + } +} diff --git a/test/cmdlineTests/yul_source_locations_in_asm/output.json b/test/cmdlineTests/yul_source_locations_in_asm/output.json new file mode 100644 index 000000000..7c96a12db --- /dev/null +++ b/test/cmdlineTests/yul_source_locations_in_asm/output.json @@ -0,0 +1,888 @@ +{"contracts":{"C":{"C":{"evm":{"assembly":" /* \"C\":79:428 contract C... */ + mstore(0x40, 0xa0) + jumpi(tag_1, iszero(callvalue)) + 0x00 + dup1 + revert +tag_1: + bytecodeSize + codesize + dup2 + swap1 + sub + 0xa0 + 0x1f + dup3 + add + not(0x1f) + and + dup2 + add + swap1 + sub(shl(0x40, 0x01), 0x01) + dup3 + gt + swap1 + dup3 + lt + or + iszero + tag_2 + jumpi + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x41) + revert(0x00, 0x24) +tag_2: + 0x40 + mstore + dup1 + dup3 + 0xa0 + codecopy + 0x20 + dup2 + slt + iszero + tag_3 + jumpi + 0x00 + dup1 + revert +tag_3: + pop + pop + tag_4 + mload(0xa0) + /* \"C\":147:149 42 */ + mstore(0x80, 0x2a) + 0x00 + /* \"C\":79:428 contract C... */ + sstore + /* \"C\":175:223 constructor(int _init)... */ + jump + /* \"C\":79:428 contract C... */ +tag_4: + mload(0x40) + dataSize(sub_0) + dup1 + dataOffset(sub_0) + dup4 + codecopy + mload(0x80) + dup3 + assignImmutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + dup1 + dup3 + return +stop + +sub_0: assembly { + /* \"C\":79:428 contract C... */ + mstore(0x40, 0x80) + jumpi(tag_1, lt(calldatasize, 0x04)) + 0x00 + dup1 + calldataload + 0xe0 + shr + 0x26121ff0 + dup2 + eq + tag_3 + jumpi + 0x793816ec + dup2 + eq + tag_4 + jumpi + 0x9942ec6f + dup2 + eq + tag_5 + jumpi + jump(tag_2) + tag_3: + jumpi(tag_6, iszero(callvalue)) + dup2 + dup3 + revert + tag_6: + tag_7 + calldatasize + tag_8 + jump\t// in + tag_7: + /* \"C\":279:298 constVar + immutVar */ + tag_9 + /* \"C\":290:298 immutVar */ + immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + /* \"C\":279:298 constVar + immutVar */ + tag_10 + jump\t// in + tag_9: + /* \"C\":79:428 contract C... */ + mload(0x40) + dup2 + dup2 + mstore + 0x20 + dup2 + return + tag_4: + jumpi(tag_13, iszero(callvalue)) + dup2 + dup3 + revert + tag_13: + tag_14 + calldatasize + tag_8 + jump\t// in + tag_14: + dup2 + sload + mload(0x40) + dup2 + dup2 + mstore + 0x20 + dup2 + return + tag_5: + jumpi(tag_16, iszero(callvalue)) + dup2 + dup3 + revert + tag_16: + tag_17 + calldatasize + tag_8 + jump\t// in + tag_17: + /* \"C\":375:378 int */ + tag_9 + tag_19 + jump\t// in + /* \"C\":79:428 contract C... */ + tag_2: + pop + pop + tag_1: + 0x00 + dup1 + revert + tag_8: + 0x00 + not(0x03) + dup3 + add + slt + iszero + tag_23 + jumpi + 0x00 + dup1 + revert + tag_23: + pop + jump\t// out + tag_24: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_26 + jumpi + 0x00 + dup1 + revert + tag_26: + pop + mload + swap2 + swap1 + pop + jump\t// out + tag_10: + 0x00 + sub(shl(0xff, 0x01), 0x2a) + dup3 + sgt + 0x01 + and + iszero + tag_30 + jumpi + tag_30 + tag_31 + jump\t// in + tag_30: + pop + /* \"C\":117:119 41 */ + 0x29 + /* \"C\":79:428 contract C... */ + add + swap1 + jump\t// out + tag_32: + 0x00 + dup1 + dup3 + slt + dup1 + iszero + sub(shl(0xff, 0x01), 0x01) + dup5 + swap1 + sub + dup6 + sgt + and + iszero + tag_35 + jumpi + tag_35 + tag_31 + jump\t// in + tag_35: + shl(0xff, 0x01) + dup4 + swap1 + sub + dup5 + slt + dup2 + and + iszero + tag_37 + jumpi + tag_37 + tag_31 + jump\t// in + tag_37: + pop + pop + add + swap1 + jump\t// out + /* \"C\":304:341 modifier m()... */ + tag_19: + 0x00 + /* \"C\":79:428 contract C... */ + dup1 + sload + /* \"C\":304:341 modifier m()... */ + dup2 + swap1 + sub(shl(0xff, 0x01), 0x01) + /* \"C\":79:428 contract C... */ + dup2 + eq + iszero + tag_40 + jumpi + tag_40 + tag_31 + jump\t// in + tag_40: + 0x01 + add + dup1 + dup3 + sstore + /* \"C\":403:407 this */ + address + /* \"C\":403:411 this.f() */ + extcodesize + tag_41 + jumpi + /* \"C\":79:428 contract C... */ + dup2 + dup3 + revert + /* \"C\":403:411 this.f() */ + tag_41: + /* \"C\":79:428 contract C... */ + mload(0x40) + shl(0xe4, 0x026121ff) + /* \"C\":403:411 this.f() */ + dup2 + mstore + 0x20 + /* \"C\":79:428 contract C... */ + dup2 + /* \"C\":403:411 this.f() */ + 0x04 + /* \"C\":79:428 contract C... */ + dup2 + /* \"C\":403:407 this */ + address + /* \"C\":403:411 this.f() */ + gas + staticcall + dup1 + tag_42 + jumpi + /* \"C\":79:428 contract C... */ + mload(0x40) + returndatasize + dup6 + dup3 + returndatacopy + returndatasize + dup2 + revert + /* \"C\":403:411 this.f() */ + tag_42: + /* \"C\":79:428 contract C... */ + dup4 + /* \"C\":403:411 this.f() */ + dup2 + iszero + tag_43 + jumpi + returndatasize + /* \"C\":79:428 contract C... */ + 0x1f + add + not(0x1f) + and + /* \"C\":117:119 41 */ + dup4 + add + 0xffffffffffffffff + dup2 + gt + dup5 + dup3 + lt + or + iszero + tag_44 + jumpi + shl(0xe0, 0x4e487b71) + /* \"C\":79:428 contract C... */ + dup7 + mstore + 0x41 + /* \"C\":403:411 this.f() */ + 0x04 + /* \"C\":79:428 contract C... */ + mstore + 0x24 + dup7 + revert + /* \"C\":117:119 41 */ + tag_44: + /* \"C\":79:428 contract C... */ + 0x40 + /* \"C\":117:119 41 */ + mstore + /* \"C\":403:411 this.f() */ + tag_45 + returndatasize + dup5 + add + dup5 + tag_24 + jump\t// in + tag_45: + swap1 + pop + tag_43: + /* \"C\":392:411 stateVar + this.f() */ + tag_46 + dup2 + dup6 + tag_32 + jump\t// in + tag_46: + swap5 + pop + pop + pop + pop + pop + /* \"C\":392:422 stateVar + this.f() + immutVar */ + tag_47 + /* \"C\":414:422 immutVar */ + immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + /* \"C\":392:422 stateVar + this.f() + immutVar */ + dup3 + tag_32 + jump\t// in + tag_47: + /* \"C\":336:337 _ */ + swap2 + pop + pop + /* \"C\":304:341 modifier m()... */ + swap1 + jump\t// out + /* \"C\":79:428 contract C... */ + tag_31: + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x11) + revert(0x00, 0x24) + + auxdata: +} +"}}},"D":{"D":{"evm":{"assembly":" /* \"D\":91:166 contract D is C(3)... */ + mstore(0x40, 0xa0) + jumpi(tag_1, iszero(callvalue)) + 0x00 + dup1 + revert +tag_1: + bytecodeSize + codesize + dup2 + swap1 + sub + 0xa0 + 0x1f + dup3 + add + not(0x1f) + and + dup2 + add + swap1 + sub(shl(0x40, 0x01), 0x01) + dup3 + gt + swap1 + dup3 + lt + or + iszero + tag_2 + jumpi + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x41) + revert(0x00, 0x24) +tag_2: + 0x40 + mstore + dup1 + dup3 + 0xa0 + codecopy + 0x20 + dup2 + slt + iszero + tag_3 + jumpi + 0x00 + dup1 + revert +tag_3: + pop + pop + tag_4 + mload(0xa0) + tag_5 + jump\t// in +tag_4: + mload(0x40) + dataSize(sub_0) + dup1 + dataOffset(sub_0) + dup4 + codecopy + mload(0x80) + dup3 + assignImmutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + dup1 + dup3 + return + /* \"D\":113:164 constructor(int _init2)... */ +tag_5: + /* \"C\":147:149 42 */ + mstore(0x80, 0x2a) + /* \"D\":107:108 3 */ + 0x03 + 0x00 + /* \"D\":91:166 contract D is C(3)... */ + sstore + sub(shl(0xff, 0x01), 0x04) + dup2 + sgt + 0x01 + and + iszero + tag_8 + jumpi + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x11) + revert(0x00, 0x24) +tag_8: + /* \"D\":107:108 3 */ + 0x03 + /* \"D\":91:166 contract D is C(3)... */ + add + 0x00 + sstore + /* \"D\":113:164 constructor(int _init2)... */ + jump\t// out +stop + +sub_0: assembly { + /* \"D\":91:166 contract D is C(3)... */ + mstore(0x40, 0x80) + jumpi(tag_1, lt(calldatasize, 0x04)) + 0x00 + dup1 + calldataload + 0xe0 + shr + 0x26121ff0 + dup2 + eq + tag_3 + jumpi + 0x793816ec + dup2 + eq + tag_4 + jumpi + 0x9942ec6f + dup2 + eq + tag_5 + jumpi + jump(tag_2) + tag_3: + jumpi(tag_6, iszero(callvalue)) + dup2 + dup3 + revert + tag_6: + tag_7 + calldatasize + tag_8 + jump\t// in + tag_7: + /* \"C\":279:298 constVar + immutVar */ + tag_9 + /* \"C\":290:298 immutVar */ + immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + /* \"C\":279:298 constVar + immutVar */ + tag_10 + jump\t// in + tag_9: + /* \"D\":91:166 contract D is C(3)... */ + mload(0x40) + dup2 + dup2 + mstore + 0x20 + dup2 + return + tag_4: + jumpi(tag_13, iszero(callvalue)) + dup2 + dup3 + revert + tag_13: + tag_14 + calldatasize + tag_8 + jump\t// in + tag_14: + dup2 + sload + mload(0x40) + dup2 + dup2 + mstore + 0x20 + dup2 + return + tag_5: + jumpi(tag_16, iszero(callvalue)) + dup2 + dup3 + revert + tag_16: + tag_17 + calldatasize + tag_8 + jump\t// in + tag_17: + /* \"C\":375:378 int */ + tag_9 + tag_19 + jump\t// in + /* \"D\":91:166 contract D is C(3)... */ + tag_2: + pop + pop + tag_1: + 0x00 + dup1 + revert + tag_8: + 0x00 + not(0x03) + dup3 + add + slt + iszero + tag_23 + jumpi + 0x00 + dup1 + revert + tag_23: + pop + jump\t// out + tag_24: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_26 + jumpi + 0x00 + dup1 + revert + tag_26: + pop + mload + swap2 + swap1 + pop + jump\t// out + tag_10: + 0x00 + sub(shl(0xff, 0x01), 0x2a) + dup3 + sgt + 0x01 + and + iszero + tag_30 + jumpi + tag_30 + tag_31 + jump\t// in + tag_30: + pop + /* \"C\":117:119 41 */ + 0x29 + /* \"D\":91:166 contract D is C(3)... */ + add + swap1 + jump\t// out + tag_32: + 0x00 + dup1 + dup3 + slt + dup1 + iszero + sub(shl(0xff, 0x01), 0x01) + dup5 + swap1 + sub + dup6 + sgt + and + iszero + tag_35 + jumpi + tag_35 + tag_31 + jump\t// in + tag_35: + shl(0xff, 0x01) + dup4 + swap1 + sub + dup5 + slt + dup2 + and + iszero + tag_37 + jumpi + tag_37 + tag_31 + jump\t// in + tag_37: + pop + pop + add + swap1 + jump\t// out + /* \"C\":304:341 modifier m()... */ + tag_19: + 0x00 + /* \"D\":91:166 contract D is C(3)... */ + dup1 + sload + /* \"C\":304:341 modifier m()... */ + dup2 + swap1 + sub(shl(0xff, 0x01), 0x01) + /* \"D\":91:166 contract D is C(3)... */ + dup2 + eq + iszero + tag_40 + jumpi + tag_40 + tag_31 + jump\t// in + tag_40: + 0x01 + add + dup1 + dup3 + sstore + /* \"C\":403:407 this */ + address + /* \"C\":403:411 this.f() */ + extcodesize + tag_41 + jumpi + /* \"D\":91:166 contract D is C(3)... */ + dup2 + dup3 + revert + /* \"C\":403:411 this.f() */ + tag_41: + /* \"D\":91:166 contract D is C(3)... */ + mload(0x40) + shl(0xe4, 0x026121ff) + /* \"C\":403:411 this.f() */ + dup2 + mstore + 0x20 + /* \"D\":91:166 contract D is C(3)... */ + dup2 + /* \"C\":403:411 this.f() */ + 0x04 + /* \"D\":91:166 contract D is C(3)... */ + dup2 + /* \"C\":403:407 this */ + address + /* \"C\":403:411 this.f() */ + gas + staticcall + dup1 + tag_42 + jumpi + /* \"D\":91:166 contract D is C(3)... */ + mload(0x40) + returndatasize + dup6 + dup3 + returndatacopy + returndatasize + dup2 + revert + /* \"C\":403:411 this.f() */ + tag_42: + /* \"D\":91:166 contract D is C(3)... */ + dup4 + /* \"C\":403:411 this.f() */ + dup2 + iszero + tag_43 + jumpi + returndatasize + /* \"D\":91:166 contract D is C(3)... */ + 0x1f + add + not(0x1f) + and + /* \"C\":117:119 41 */ + dup4 + add + 0xffffffffffffffff + dup2 + gt + dup5 + dup3 + lt + or + iszero + tag_44 + jumpi + shl(0xe0, 0x4e487b71) + /* \"D\":91:166 contract D is C(3)... */ + dup7 + mstore + 0x41 + /* \"C\":403:411 this.f() */ + 0x04 + /* \"D\":91:166 contract D is C(3)... */ + mstore + 0x24 + dup7 + revert + /* \"C\":117:119 41 */ + tag_44: + /* \"D\":91:166 contract D is C(3)... */ + 0x40 + /* \"C\":117:119 41 */ + mstore + /* \"C\":403:411 this.f() */ + tag_45 + returndatasize + dup5 + add + dup5 + tag_24 + jump\t// in + tag_45: + swap1 + pop + tag_43: + /* \"C\":392:411 stateVar + this.f() */ + tag_46 + dup2 + dup6 + tag_32 + jump\t// in + tag_46: + swap5 + pop + pop + pop + pop + pop + /* \"C\":392:422 stateVar + this.f() + immutVar */ + tag_47 + /* \"C\":414:422 immutVar */ + immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") + /* \"C\":392:422 stateVar + this.f() + immutVar */ + dup3 + tag_32 + jump\t// in + tag_47: + /* \"C\":336:337 _ */ + swap2 + pop + pop + /* \"C\":304:341 modifier m()... */ + swap1 + jump\t// out + /* \"D\":91:166 contract D is C(3)... */ + tag_31: + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x11) + revert(0x00, 0x24) + + auxdata: +} +"}}}},"sources":{"C":{"id":0},"D":{"id":1}}} diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index df96ed791..a48cc0d68 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -6,9 +6,10 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { - /// @src 0:78,164 + /// @src 0:78:164 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -23,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:164 function constructor_C_11() { - /// @src 0:78,164 + /// @src 0:78:164 } + /// @src 0:78:164 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -36,7 +39,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,164 + /// @src 0:78:164 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -149,17 +152,18 @@ object \"C_11\" { mstore(64, newFreePtr) } + /// @src 0:91:162 function fun_f_10() -> var__5_mpos { - /// @src 0:91,162 - /// @src 0:127,140 + /// @src 0:127:140 let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() var__5_mpos := zero_t_string_memory_ptr_1_mpos - /// @src 0:144,159 + /// @src 0:144:159 var__5_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave } + /// @src 0:78:164 function panic_error_0x41() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 9e47d6e1e..6dabd7fdd 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -6,9 +6,10 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { - /// @src 0:78,158 + /// @src 0:78:158 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -23,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:158 function constructor_C_11() { - /// @src 0:78,158 + /// @src 0:78:158 } + /// @src 0:78:158 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -36,7 +39,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,158 + /// @src 0:78:158 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -89,17 +92,18 @@ object \"C_11\" { converted := 0x6162636162630000000000000000000000000000000000000000000000000000 } + /// @src 0:91:156 function fun_f_10() -> var__5 { - /// @src 0:91,156 - /// @src 0:127,134 + /// @src 0:127:134 let zero_t_bytes32_1 := zero_value_for_split_t_bytes32() var__5 := zero_t_bytes32_1 - /// @src 0:138,153 + /// @src 0:138:153 var__5 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave } + /// @src 0:78:158 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 8c5df7044..09e81312b 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -6,9 +6,10 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { - /// @src 0:78,159 + /// @src 0:78:159 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -23,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:159 function constructor_C_11() { - /// @src 0:78,159 + /// @src 0:78:159 } + /// @src 0:78:159 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -36,7 +39,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,159 + /// @src 0:78:159 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -93,19 +96,20 @@ object \"C_11\" { converted := shift_left_224(cleanup_t_rational_1633837924_by_1(value)) } + /// @src 0:91:157 function fun_f_10() -> var__5 { - /// @src 0:91,157 - /// @src 0:127,133 + /// @src 0:127:133 let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() var__5 := zero_t_bytes4_1 - /// @src 0:144,154 + /// @src 0:144:154 let expr_7 := 0x61626364 - /// @src 0:137,154 + /// @src 0:137:154 var__5 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_7) leave } + /// @src 0:78:159 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index ad2197252..f9110a134 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -6,9 +6,10 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { - /// @src 0:78,243 + /// @src 0:78:243 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -23,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:243 function constructor_C_11() { - /// @src 0:78,243 + /// @src 0:78:243 } + /// @src 0:78:243 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -36,7 +39,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,243 + /// @src 0:78:243 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -149,17 +152,18 @@ object \"C_11\" { mstore(64, newFreePtr) } + /// @src 0:91:241 function fun_f_10() -> var__5_mpos { - /// @src 0:91,241 - /// @src 0:127,140 + /// @src 0:127:140 let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() var__5_mpos := zero_t_string_memory_ptr_1_mpos - /// @src 0:144,238 + /// @src 0:144:238 var__5_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave } + /// @src 0:78:243 function panic_error_0x41() { mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 86eb7ed62..e944cf5e1 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -6,9 +6,10 @@ *=====================================================*/ +/// @use-src 0:\"A\", 1:\"#utility.yul\" object \"C_11\" { code { - /// @src 0:78,159 + /// @src 0:78:159 mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -23,11 +24,13 @@ object \"C_11\" { memPtr := mload(64) } + /// @src 0:78:159 function constructor_C_11() { - /// @src 0:78,159 + /// @src 0:78:159 } + /// @src 0:78:159 function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) @@ -36,7 +39,7 @@ object \"C_11\" { } object \"C_11_deployed\" { code { - /// @src 0:78,159 + /// @src 0:78:159 mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -93,19 +96,20 @@ object \"C_11\" { converted := shift_left_224(cleanup_t_rational_2864434397_by_1(value)) } + /// @src 0:91:157 function fun_f_10() -> var__5 { - /// @src 0:91,157 - /// @src 0:127,133 + /// @src 0:127:133 let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() var__5 := zero_t_bytes4_1 - /// @src 0:144,154 + /// @src 0:144:154 let expr_7 := 0xaabbccdd - /// @src 0:137,154 + /// @src 0:137:154 var__5 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_7) leave } + /// @src 0:78:159 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) diff --git a/test/cmdlineTests/yul_verbatim/output b/test/cmdlineTests/yul_verbatim/output index 821e7461a..60fc7e6d1 100644 --- a/test/cmdlineTests/yul_verbatim/output +++ b/test/cmdlineTests/yul_verbatim/output @@ -32,6 +32,7 @@ Text representation: dup1 /* "yul_verbatim/input.yul":75:76 */ dup3 + /* "yul_verbatim/input.yul":53:80 */ verbatimbytecode_616263 /* "yul_verbatim/input.yul":95:96 */ dup3 @@ -39,6 +40,7 @@ Text representation: dup2 /* "yul_verbatim/input.yul":85:97 */ sstore + /* "yul_verbatim/input.yul":111:132 */ verbatimbytecode_646566 /* "yul_verbatim/input.yul":137:158 */ verbatimbytecode_78797a diff --git a/test/docsCodeStyle.sh b/test/docsCodeStyle.sh index 9e082b7fb..33b787e25 100755 --- a/test/docsCodeStyle.sh +++ b/test/docsCodeStyle.sh @@ -22,7 +22,7 @@ SOLTMPDIR=$(mktemp -d) ( set -e cd "$SOLTMPDIR" - "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ if npm -v >/dev/null 2>&1; then if npm list -g | grep solhint >/dev/null 2>&1; then diff --git a/test/evmc/README.md b/test/evmc/README.md index 68bcdd92b..cae41f170 100644 --- a/test/evmc/README.md +++ b/test/evmc/README.md @@ -1,5 +1,5 @@ # EVMC -This is an import of [EVMC](https://github.com/ethereum/evmc) version [8.0.0](https://github.com/ethereum/evmc/releases/tag/v8.0.0). +This is an import of [EVMC](https://github.com/ethereum/evmc) version [9.0.0](https://github.com/ethereum/evmc/releases/tag/v9.0.0). Important: The `MockedAccount.storage` is changed to a map from unordered_map as ordering is important for fuzzing. diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index 5de5f5dae..c0d79d4e7 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -44,7 +44,7 @@ enum * * @see @ref versioning */ - EVMC_ABI_VERSION = 8 + EVMC_ABI_VERSION = 9 }; @@ -154,6 +154,7 @@ struct evmc_tx_context int64_t block_gas_limit; /**< The block gas limit. */ evmc_uint256be block_difficulty; /**< The block difficulty. */ evmc_uint256be chain_id; /**< The blockchain's ChainID. */ + evmc_uint256be block_base_fee; /**< The block base fee per gas (EIP-1559, EIP-3198). */ }; /** @@ -813,19 +814,40 @@ enum evmc_revision /** * The Istanbul revision. * - * The spec draft: https://eips.ethereum.org/EIPS/eip-1679. + * https://eips.ethereum.org/EIPS/eip-1679 */ EVMC_ISTANBUL = 7, /** * The Berlin revision. * - * The spec draft: https://eips.ethereum.org/EIPS/eip-2070. + * https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md */ EVMC_BERLIN = 8, + /** + * The London revision. + * + * https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md + */ + EVMC_LONDON = 9, + + /** + * The Shanghai revision. + * + * https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md + */ + EVMC_SHANGHAI = 10, + /** The maximum EVM revision supported. */ - EVMC_MAX_REVISION = EVMC_BERLIN + EVMC_MAX_REVISION = EVMC_SHANGHAI, + + /** + * The latest known EVM revision with finalized specification. + * + * This is handy for EVM tools to always use the latest revision available. + */ + EVMC_LATEST_STABLE_REVISION = EVMC_LONDON }; diff --git a/test/externalTests/solc-js/solc-js.sh b/test/externalTests/solc-js/solc-js.sh index 9cc753959..3762ba7a2 100755 --- a/test/externalTests/solc-js/solc-js.sh +++ b/test/externalTests/solc-js/solc-js.sh @@ -47,8 +47,10 @@ function solcjs_test cp -Rf "$SOLCJS_INPUT_DIR/DAO" test/ printLog "Copying SMTChecker tests..." - cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests test/ - rm -rf test/smtCheckerTests/imports + # We do not copy all tests because that takes too long. + cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests/external_calls test/smtCheckerTests/ + cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests/loops test/smtCheckerTests/ + cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests/invariants test/smtCheckerTests/ # Update version (needed for some tests) echo "Updating package.json to version $VERSION" diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 5e260b14d..0c1e4dbad 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -57,11 +57,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) { "sub.asm", 1 } }; Assembly _assembly; - auto root_asm = make_shared("lorem ipsum", "root.asm"); + auto root_asm = make_shared("root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); Assembly _subAsm; - auto sub_asm = make_shared("lorem ipsum", "sub.asm"); + auto sub_asm = make_shared("sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); // PushImmutable _subAsm.appendImmutable("someImmutable"); @@ -172,11 +172,11 @@ BOOST_AUTO_TEST_CASE(immutable) { "sub.asm", 1 } }; Assembly _assembly; - auto root_asm = make_shared("lorem ipsum", "root.asm"); + auto root_asm = make_shared("root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); Assembly _subAsm; - auto sub_asm = make_shared("lorem ipsum", "sub.asm"); + auto sub_asm = make_shared("sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.appendImmutable("someImmutable"); _subAsm.appendImmutable("someOtherImmutable"); diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index b8d51aca5..51d051236 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -34,13 +34,15 @@ BOOST_AUTO_TEST_SUITE(ScannerTest) BOOST_AUTO_TEST_CASE(test_empty) { - Scanner scanner(CharStream{}); + CharStream stream{}; + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); } BOOST_AUTO_TEST_CASE(smoke_test) { - Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1", "")); + CharStream stream("function break;765 \t \"string1\",'string2'\nidentifier1", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Break); BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); @@ -58,7 +60,8 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_AUTO_TEST_CASE(assembly_assign) { - Scanner scanner(CharStream("let a := 1", "")); + CharStream stream("let a := 1", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Let); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); @@ -69,7 +72,8 @@ BOOST_AUTO_TEST_CASE(assembly_assign) BOOST_AUTO_TEST_CASE(assembly_multiple_assign) { - Scanner scanner(CharStream("let a, b, c := 1", "")); + CharStream stream("let a, b, c := 1", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Let); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); @@ -89,14 +93,16 @@ BOOST_AUTO_TEST_CASE(string_printable) // Escape \ and " (since we are quoting with ") if (v == '\\' || v == '"') lit = string{'\\'} + lit; - Scanner scanner(CharStream(" { \"" + lit + "\"", "")); + CharStream stream(" { \"" + lit + "\"", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), string{static_cast(v)}); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } // Special case of unescaped " for strings quoted with ' - Scanner scanner(CharStream(" { '\"'", "")); + CharStream stream(" { '\"'", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "\""); @@ -110,7 +116,8 @@ BOOST_AUTO_TEST_CASE(string_nonprintable) if (v >= 0x20 && v <= 0x7e) continue; string lit{static_cast(v)}; - Scanner scanner(CharStream(" { \"" + lit + "\"", "")); + CharStream stream(" { \"" + lit + "\"", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); if (v == '\n' || v == '\v' || v == '\f' || v == '\r') @@ -123,7 +130,8 @@ BOOST_AUTO_TEST_CASE(string_nonprintable) BOOST_AUTO_TEST_CASE(string_escapes) { - Scanner scanner(CharStream(" { \"a\\x61\"", "")); + CharStream stream(" { \"a\\x61\"", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa"); @@ -131,25 +139,46 @@ BOOST_AUTO_TEST_CASE(string_escapes) BOOST_AUTO_TEST_CASE(string_escapes_all) { - Scanner scanner(CharStream(" { \"a\\x61\\n\\r\\t\"", "")); + CharStream stream(" { \"a\\x61\\n\\r\\t\"", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa\n\r\t"); } +struct TestScanner +{ + unique_ptr stream; + unique_ptr scanner; + explicit TestScanner(string _text) { reset(move(_text)); } + + void reset(std::string _text) + { + stream = make_unique(move(_text), ""); + scanner = make_unique(*stream); + } + + decltype(auto) currentToken() { return scanner->currentToken(); } + decltype(auto) next() { return scanner->next(); } + decltype(auto) currentError() { return scanner->currentError(); } + decltype(auto) currentLiteral() { return scanner->currentLiteral(); } + decltype(auto) currentCommentLiteral() { return scanner->currentCommentLiteral(); } + decltype(auto) currentLocation() { return scanner->currentLocation(); } +}; + BOOST_AUTO_TEST_CASE(string_escapes_legal_before_080) { - Scanner scanner(CharStream(" { \"a\\b", "")); + TestScanner scanner(" { \"a\\b"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); BOOST_CHECK_EQUAL(scanner.currentLiteral(), ""); - scanner.reset(CharStream(" { \"a\\f", "")); + scanner.reset(" { \"a\\f"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); BOOST_CHECK_EQUAL(scanner.currentLiteral(), ""); - scanner.reset(CharStream(" { \"a\\v", "")); + scanner.reset(" { \"a\\v"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); @@ -158,7 +187,7 @@ BOOST_AUTO_TEST_CASE(string_escapes_legal_before_080) BOOST_AUTO_TEST_CASE(string_escapes_with_zero) { - Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"", "")); + TestScanner scanner(" { \"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)); @@ -166,7 +195,8 @@ BOOST_AUTO_TEST_CASE(string_escapes_with_zero) BOOST_AUTO_TEST_CASE(string_escape_illegal) { - Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)", "")); + CharStream stream(" bla \"\\x6rf\" (illegalescape)", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); @@ -180,7 +210,7 @@ BOOST_AUTO_TEST_CASE(string_escape_illegal) BOOST_AUTO_TEST_CASE(hex_numbers) { - Scanner scanner(CharStream("var x = 0x765432536763762734623472346;", "")); + TestScanner scanner("var x = 0x765432536763762734623472346;"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); @@ -188,34 +218,35 @@ 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("0x1234"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x1234"); - scanner.reset(CharStream("0X1234", "")); + scanner.reset("0X1234"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); } BOOST_AUTO_TEST_CASE(octal_numbers) { - Scanner scanner(CharStream("07", "")); + TestScanner scanner("07"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); - scanner.reset(CharStream("007", "")); + scanner.reset("007"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); - scanner.reset(CharStream("-07", "")); + scanner.reset("-07"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Sub); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); - scanner.reset(CharStream("-.07", "")); + scanner.reset("-.07"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Sub); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); - scanner.reset(CharStream("0", "")); + scanner.reset("0"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); - scanner.reset(CharStream("0.1", "")); + scanner.reset("0.1"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); } BOOST_AUTO_TEST_CASE(scientific_notation) { - Scanner scanner(CharStream("var x = 2e10;", "")); + CharStream stream("var x = 2e10;", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); @@ -227,14 +258,14 @@ BOOST_AUTO_TEST_CASE(scientific_notation) BOOST_AUTO_TEST_CASE(leading_dot_in_identifier) { - Scanner scanner(CharStream("function .a(", "")); + TestScanner scanner("function .a("); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("function .a(", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("function .a("); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -244,7 +275,7 @@ BOOST_AUTO_TEST_CASE(leading_dot_in_identifier) BOOST_AUTO_TEST_CASE(middle_dot_in_identifier) { - Scanner scanner(CharStream("function a..a(", "")); + TestScanner scanner("function a..a("); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); @@ -252,8 +283,8 @@ BOOST_AUTO_TEST_CASE(middle_dot_in_identifier) BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("function a...a(", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("function a...a("); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -262,14 +293,14 @@ BOOST_AUTO_TEST_CASE(middle_dot_in_identifier) BOOST_AUTO_TEST_CASE(trailing_dot_in_identifier) { - Scanner scanner(CharStream("function a.(", "")); + TestScanner scanner("function a.("); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("function a.(", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("function a.("); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -278,19 +309,19 @@ BOOST_AUTO_TEST_CASE(trailing_dot_in_identifier) BOOST_AUTO_TEST_CASE(trailing_dot_in_numbers) { - Scanner scanner(CharStream("2.5", "")); + TestScanner scanner("2.5"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("2.5e10", "")); + scanner.reset("2.5e10"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(".5", "")); + scanner.reset(".5"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(".5e10", "")); + scanner.reset(".5e10"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("2.", "")); + scanner.reset("2."); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -299,7 +330,8 @@ BOOST_AUTO_TEST_CASE(trailing_dot_in_numbers) BOOST_AUTO_TEST_CASE(leading_underscore_decimal_is_identifier) { // Actual error is cought by SyntaxChecker. - Scanner scanner(CharStream("_1.2", "")); + CharStream stream("_1.2", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -308,11 +340,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", "")); + TestScanner scanner("1._2"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("1._", "")); + scanner.reset("1._"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -320,7 +352,8 @@ 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", "")); + CharStream stream("_1e2", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -328,7 +361,8 @@ 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", "")); + CharStream stream("1e_2", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1e_2"); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -336,7 +370,8 @@ BOOST_AUTO_TEST_CASE(leading_underscore_exp_after_e_illegal) BOOST_AUTO_TEST_CASE(leading_underscore_hex_illegal) { - Scanner scanner(CharStream("0x_abc", "")); + CharStream stream("0x_abc", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -345,7 +380,8 @@ 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", "")); + CharStream stream("12._1234_1234", ""); + Scanner scanner(stream); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -353,22 +389,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_", "")); + TestScanner scanner("0x123_"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("123_", "")); + scanner.reset("123_"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("12.34_", "")); + scanner.reset("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;", "")); + TestScanner scanner("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); @@ -395,7 +431,7 @@ BOOST_AUTO_TEST_CASE(negative_numbers) BOOST_AUTO_TEST_CASE(locations) { - Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment", "")); + TestScanner scanner("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); @@ -418,7 +454,7 @@ BOOST_AUTO_TEST_CASE(locations) BOOST_AUTO_TEST_CASE(ambiguities) { // test scanning of some operators which need look-ahead - Scanner scanner(CharStream("<=" "<" "+ +=a++ =>" "<<" ">>" " >>=" ">>>" ">>>=" " >>>>>=><<=", "")); + TestScanner scanner("<=" "<" "+ +=a++ =>" "<<" ">>" " >>=" ">>>" ">>>=" " >>>>>=><<="); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LessThanOrEqual); BOOST_CHECK_EQUAL(scanner.next(), Token::LessThan); BOOST_CHECK_EQUAL(scanner.next(), Token::Add); @@ -440,21 +476,21 @@ BOOST_AUTO_TEST_CASE(ambiguities) BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin) { - Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user", "")); + TestScanner scanner("/// 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*/", "")); + TestScanner scanner("/** 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", "")); + TestScanner scanner("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); @@ -464,9 +500,9 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed) BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) { - Scanner scanner(CharStream("some other tokens /**\n" - "* Send $(value / 1000) chocolates to the user\n" - "*/", "")); + TestScanner scanner("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); @@ -476,9 +512,9 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars) { - Scanner scanner(CharStream("some other tokens /**\n" - " Send $(value / 1000) chocolates to the user\n" - "*/", "")); + TestScanner scanner("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); @@ -488,9 +524,9 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars) 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" - "*/", "")); + TestScanner scanner("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); @@ -500,37 +536,37 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell) BOOST_AUTO_TEST_CASE(comment_before_eos) { - Scanner scanner(CharStream("//", "")); + TestScanner scanner("//"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(documentation_comment_before_eos) { - Scanner scanner(CharStream("///", "")); + TestScanner scanner("///"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(empty_multiline_comment) { - Scanner scanner(CharStream("/**/", "")); + TestScanner scanner("/**/"); 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("/***/", "")); + TestScanner scanner("/***/"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); } BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) { - Scanner scanner(CharStream("hello_world ///documentation comment \n" - "//simple comment \n" - "<<", "")); + TestScanner scanner("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 "); @@ -538,7 +574,7 @@ BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) BOOST_AUTO_TEST_CASE(ether_subdenominations) { - Scanner scanner(CharStream("wei gwei ether", "")); + TestScanner scanner("wei gwei ether"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubWei); BOOST_CHECK_EQUAL(scanner.next(), Token::SubGwei); BOOST_CHECK_EQUAL(scanner.next(), Token::SubEther); @@ -546,7 +582,7 @@ BOOST_AUTO_TEST_CASE(ether_subdenominations) BOOST_AUTO_TEST_CASE(time_subdenominations) { - Scanner scanner(CharStream("seconds minutes hours days weeks years", "")); + TestScanner scanner("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); @@ -557,7 +593,7 @@ BOOST_AUTO_TEST_CASE(time_subdenominations) BOOST_AUTO_TEST_CASE(empty_comment) { - Scanner scanner(CharStream("//\ncontract{}", "")); + TestScanner scanner("//\ncontract{}"); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Contract); BOOST_CHECK_EQUAL(scanner.next(), Token::LBrace); @@ -569,7 +605,7 @@ BOOST_AUTO_TEST_CASE(empty_comment) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape) { - Scanner scanner(CharStream("{ \"\\u00DAnicode\"", "")); + TestScanner scanner("{ \"\\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)); @@ -577,7 +613,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7f) { - Scanner scanner(CharStream("{ \"\\u007Fnicode\"", "")); + TestScanner scanner("{ \"\\u007Fnicode\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x7Fnicode", 7)); @@ -585,7 +621,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7f) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7ff) { - Scanner scanner(CharStream("{ \"\\u07FFnicode\"", "")); + TestScanner scanner("{ \"\\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)); @@ -593,7 +629,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7ff) BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_ffff) { - Scanner scanner(CharStream("{ \"\\uFFFFnicode\"", "")); + TestScanner scanner("{ \"\\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)); @@ -601,7 +637,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_ffff) BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) { - Scanner scanner(CharStream("{ \"\\uFFnicode\"", "")); + TestScanner scanner("{ \"\\uFFnicode\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } @@ -610,12 +646,12 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) BOOST_AUTO_TEST_CASE(unicode_prefix_only) { - Scanner scanner(CharStream("{ unicode", "")); + TestScanner scanner("{ unicode"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); - scanner.reset(CharStream("{ unicode", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("{ unicode"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); @@ -623,7 +659,7 @@ BOOST_AUTO_TEST_CASE(unicode_prefix_only) BOOST_AUTO_TEST_CASE(unicode_invalid_space) { - Scanner scanner(CharStream("{ unicode ", "")); + TestScanner scanner("{ unicode "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); @@ -631,12 +667,12 @@ BOOST_AUTO_TEST_CASE(unicode_invalid_space) BOOST_AUTO_TEST_CASE(unicode_invalid_token) { - Scanner scanner(CharStream("{ unicode test", "")); + TestScanner scanner("{ unicode test"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); - scanner.reset(CharStream("{ unicode test", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("{ unicode test"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); @@ -646,7 +682,7 @@ BOOST_AUTO_TEST_CASE(unicode_invalid_token) BOOST_AUTO_TEST_CASE(valid_unicode_literal) { - Scanner scanner(CharStream("{ unicode\"Hello 😃\"", "")); + TestScanner scanner("{ unicode\"Hello 😃\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::UnicodeStringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \xf0\x9f\x98\x83", 10)); @@ -655,7 +691,7 @@ BOOST_AUTO_TEST_CASE(valid_unicode_literal) BOOST_AUTO_TEST_CASE(valid_nonprintable_in_unicode_literal) { // Non-printable characters are allowed in unicode strings... - Scanner scanner(CharStream("{ unicode\"Hello \007😃\"", "")); + TestScanner scanner("{ unicode\"Hello \007😃\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::UnicodeStringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \x07\xf0\x9f\x98\x83", 11)); @@ -665,19 +701,19 @@ BOOST_AUTO_TEST_CASE(valid_nonprintable_in_unicode_literal) BOOST_AUTO_TEST_CASE(hex_prefix_only) { - Scanner scanner(CharStream("{ hex", "")); + TestScanner scanner("{ hex"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); - scanner.reset(CharStream("{ hex", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("{ hex"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); } BOOST_AUTO_TEST_CASE(hex_invalid_space) { - Scanner scanner(CharStream("{ hex ", "")); + TestScanner scanner("{ hex "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); @@ -685,12 +721,12 @@ BOOST_AUTO_TEST_CASE(hex_invalid_space) BOOST_AUTO_TEST_CASE(hex_invalid_token) { - Scanner scanner(CharStream("{ hex test", "")); + TestScanner scanner("{ hex test"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); - scanner.reset(CharStream("{ hex test", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("{ hex test"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); @@ -698,7 +734,7 @@ BOOST_AUTO_TEST_CASE(hex_invalid_token) BOOST_AUTO_TEST_CASE(valid_hex_literal) { - Scanner scanner(CharStream("{ hex\"00112233FF\"", "")); + TestScanner scanner("{ hex\"00112233FF\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::HexStringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); @@ -706,7 +742,7 @@ BOOST_AUTO_TEST_CASE(valid_hex_literal) BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) { - Scanner scanner(CharStream("{ hex\"00112233F\"", "")); + TestScanner scanner("{ hex\"00112233F\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); @@ -714,7 +750,7 @@ BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) { - Scanner scanner(CharStream("{ hex\"00112233FF \"", "")); + TestScanner scanner("{ hex\"00112233FF \""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); @@ -722,7 +758,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) { - Scanner scanner(CharStream("{ hex\"00112233FF'", "")); + TestScanner scanner("{ hex\"00112233FF'"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); @@ -730,7 +766,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) { - Scanner scanner(CharStream("{ hex\"hello\"", "")); + TestScanner scanner("{ hex\"hello\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); @@ -741,7 +777,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", "")); + TestScanner scanner("/** / x"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -749,14 +785,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("/**", "")); + TestScanner scanner("/**"); 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("/*", "")); + TestScanner scanner("/*"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -765,7 +801,7 @@ BOOST_AUTO_TEST_CASE(regular_line_break_in_single_line_comment) { for (auto const& nl: {"\r", "\n", "\r\n"}) { - Scanner scanner(CharStream("// abc " + string(nl) + " def ", "")); + TestScanner scanner("// abc " + string(nl) + " def "); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -777,7 +813,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 ", "")); + TestScanner scanner("// 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++) @@ -792,7 +828,7 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_single_line_doc_comment) { for (auto const& nl: {"\r", "\n", "\r\n"}) { - Scanner scanner(CharStream("/// abc " + string(nl) + " def ", "")); + TestScanner scanner("/// abc " + string(nl) + " def "); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -806,7 +842,7 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_multiline_doc_comment) // Any accepted non-LF is being canonicalized to LF. for (auto const& nl : {"\r"s, "\n"s, "\r\n"s}) { - Scanner scanner{CharStream{"/// Hello" + nl + "/// World" + nl + "ident", ""}}; + TestScanner scanner{"/// Hello" + nl + "/// World" + nl + "ident"}; auto const& lit = scanner.currentCommentLiteral(); BOOST_CHECK_EQUAL(lit, "Hello\n World"); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Hello\n World"); @@ -820,7 +856,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 ", "")); + TestScanner scanner("/// 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++) @@ -835,7 +871,7 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_strings) { for (auto const& nl: {"\r"s, "\n"s, "\r\n"s}) { - Scanner scanner(CharStream("\"abc " + nl + " def\"", "")); + TestScanner scanner("\"abc " + nl + " def\""); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); @@ -848,7 +884,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\"", "")); + TestScanner scanner("\"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); @@ -863,7 +899,7 @@ BOOST_AUTO_TEST_CASE(solidity_keywords) { // These are tokens which have a different meaning in Yul. string keywords = "return byte bool address var in true false leave switch case default"; - Scanner scanner(CharStream(keywords, "")); + TestScanner scanner(keywords); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Return); BOOST_CHECK_EQUAL(scanner.next(), Token::Byte); BOOST_CHECK_EQUAL(scanner.next(), Token::Bool); @@ -877,8 +913,8 @@ BOOST_AUTO_TEST_CASE(solidity_keywords) BOOST_CHECK_EQUAL(scanner.next(), Token::Case); BOOST_CHECK_EQUAL(scanner.next(), Token::Default); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(keywords, "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset(keywords); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -896,28 +932,28 @@ BOOST_AUTO_TEST_CASE(solidity_keywords) BOOST_AUTO_TEST_CASE(yul_keyword_like) { - Scanner scanner(CharStream("leave.function", "")); + TestScanner scanner("leave.function"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("leave.function", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("leave.function"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(yul_identifier_with_dots) { - Scanner scanner(CharStream("mystorage.slot := 1", "")); + TestScanner scanner("mystorage.slot := 1"); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream("mystorage.slot := 1", "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset("mystorage.slot := 1"); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); BOOST_CHECK_EQUAL(scanner.next(), Token::Number); @@ -927,7 +963,7 @@ BOOST_AUTO_TEST_CASE(yul_identifier_with_dots) BOOST_AUTO_TEST_CASE(yul_function) { string sig = "function f(a, b) -> x, y"; - Scanner scanner(CharStream(sig, "")); + TestScanner scanner(sig); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -940,8 +976,8 @@ BOOST_AUTO_TEST_CASE(yul_function) BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(sig, "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset(sig); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -959,7 +995,7 @@ BOOST_AUTO_TEST_CASE(yul_function) BOOST_AUTO_TEST_CASE(yul_function_with_whitespace) { string sig = "function f (a, b) - > x, y"; - Scanner scanner(CharStream(sig, "")); + TestScanner scanner(sig); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -973,8 +1009,8 @@ BOOST_AUTO_TEST_CASE(yul_function_with_whitespace) BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - scanner.reset(CharStream(sig, "")); - scanner.setScannerMode(ScannerKind::Yul); + scanner.reset(sig); + scanner.scanner->setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index ff1664dfe..c8457dd06 100644 --- a/test/liblangutil/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -34,9 +34,9 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest) BOOST_AUTO_TEST_CASE(test_fail) { - auto const source = std::make_shared("lorem ipsum", "source"); - auto const sourceA = std::make_shared("lorem ipsum", "sourceA"); - auto const sourceB = std::make_shared("lorem ipsum", "sourceB"); + auto const source = std::make_shared("source"); + auto const sourceA = std::make_shared("sourceA"); + auto const sourceB = std::make_shared("sourceB"); BOOST_CHECK(SourceLocation{} == SourceLocation{}); BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB})); diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index a571d282b..8579e9ea4 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -141,9 +141,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi if (!c.compile(CompilerStack::State::Parsed)) { - SourceReferenceFormatter formatter(_stream, _formatted, false); - for (auto const& error: c.errors()) - formatter.printErrorInformation(*error); + SourceReferenceFormatter formatter(_stream, c, _formatted, false); + formatter.printErrorInformation(c.errors()); return TestResult::FatalError; } @@ -167,9 +166,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi if (m_expectation.empty()) return resultsMatch ? TestResult::Success : TestResult::Failure; - SourceReferenceFormatter formatter(_stream, _formatted, false); - for (auto const& error: c.errors()) - formatter.printErrorInformation(*error); + SourceReferenceFormatter{_stream, c, _formatted, false} + .printErrorInformation(c.errors()); return TestResult::FatalError; } diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 36172562d..bc1a5372d 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -151,7 +151,7 @@ string AnalysisFramework::formatErrors() const string AnalysisFramework::formatError(Error const& _error) const { - return SourceReferenceFormatter::formatErrorInformation(_error); + return SourceReferenceFormatter::formatErrorInformation(_error, *m_compiler); } ContractDefinition const* AnalysisFramework::retrieveContractByName(SourceUnit const& _source, string const& _name) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 75238c9cf..76b1ba2a6 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -26,7 +26,8 @@ #include #include -#include +#include + #include #include #include @@ -58,7 +59,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) ErrorReporter errorReporter(errors); Parser parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()); ASTPointer sourceUnit; - BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(_sourceCode))); + BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(*_sourceCode)); BOOST_CHECK(!!sourceUnit); Scoper::assignScopes(*sourceUnit); @@ -109,7 +110,7 @@ void printAssemblyLocations(AssemblyItems const& _items) ", " << _loc.end << ", make_shared(\"" << - _loc.source->name() << + *_loc.sourceName << "\")}) +" << endl; }; @@ -157,15 +158,16 @@ BOOST_AUTO_TEST_SUITE(Assembly) BOOST_AUTO_TEST_CASE(location_test) { - auto sourceCode = make_shared(R"( + string sourceCode = R"( pragma abicoder v1; contract test { function f() public returns (uint256 a) { return 16; } } - )", ""); - AssemblyItems items = compileContract(sourceCode); + )"; + AssemblyItems items = compileContract(make_shared(sourceCode, "")); + shared_ptr sourceName = make_shared(); bool hasShifts = solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting(); auto codegenCharStream = make_shared("", "--CODEGEN--"); @@ -173,18 +175,18 @@ BOOST_AUTO_TEST_CASE(location_test) vector locations; if (solidity::test::CommonOptions::get().optimize) locations = - vector(31, SourceLocation{23, 103, sourceCode}) + - vector(1, SourceLocation{41, 100, sourceCode}) + - vector(1, SourceLocation{93, 95, sourceCode}) + - vector(15, SourceLocation{41, 100, sourceCode}); + vector(31, SourceLocation{23, 103, sourceName}) + + vector(1, SourceLocation{41, 100, sourceName}) + + vector(1, SourceLocation{93, 95, sourceName}) + + vector(15, SourceLocation{41, 100, sourceName}); else locations = - vector(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceCode}) + - vector(24, SourceLocation{41, 100, sourceCode}) + - vector(1, SourceLocation{70, 79, sourceCode}) + - vector(1, SourceLocation{93, 95, sourceCode}) + - vector(2, SourceLocation{86, 95, sourceCode}) + - vector(2, SourceLocation{41, 100, sourceCode}); + vector(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceName}) + + vector(24, SourceLocation{41, 100, sourceName}) + + vector(1, SourceLocation{70, 79, sourceName}) + + vector(1, SourceLocation{93, 95, sourceName}) + + vector(2, SourceLocation{86, 95, sourceName}) + + vector(2, SourceLocation{41, 100, sourceName}); checkAssemblyLocations(items, locations); } diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index 36910647c..fd215cb94 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -118,9 +118,8 @@ TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, b if (!compiler().parseAndAnalyze() || !compiler().compile()) { - SourceReferenceFormatter formatter(_stream, _formatted, false); - for (auto const& error: compiler().errors()) - formatter.printErrorInformation(*error); + SourceReferenceFormatter{_stream, compiler(), _formatted, false} + .printErrorInformation(compiler().errors()); return TestResult::FatalError; } diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 32ed71e17..19b2da3b3 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -81,7 +81,7 @@ std::optional parseAndReturnFirstError( { string errors; for (auto const& err: stack.errors()) - errors += SourceReferenceFormatter::formatErrorInformation(*err); + errors += SourceReferenceFormatter::formatErrorInformation(*err, stack); BOOST_FAIL("Found more than one error:\n" + errors); } error = e; diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index bdc408b9f..166aa2982 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -27,23 +27,23 @@ using namespace solidity::frontend::test; SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, EVMVersion{}) { + auto const& showUnproved = m_reader.stringSetting("SMTShowUnproved", "yes"); + if (showUnproved == "no") + m_modelCheckerSettings.showUnproved = false; + else if (showUnproved == "yes") + m_modelCheckerSettings.showUnproved = true; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT \"show unproved\" choice.")); + auto const& choice = m_reader.stringSetting("SMTSolvers", "any"); if (choice == "any") - m_enabledSolvers = smtutil::SMTSolverChoice::All(); - else if (choice == "z3") - m_enabledSolvers = smtutil::SMTSolverChoice::Z3(); - else if (choice == "cvc4") - m_enabledSolvers = smtutil::SMTSolverChoice::CVC4(); + m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::All(); else if (choice == "none") - m_enabledSolvers = smtutil::SMTSolverChoice::None(); - else + m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::None(); + else if (!m_modelCheckerSettings.solvers.setSolver(choice)) BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice.")); - auto available = ModelChecker::availableSolvers(); - if (!available.z3) - m_enabledSolvers.z3 = false; - if (!available.cvc4) - m_enabledSolvers.cvc4 = false; + m_modelCheckerSettings.solvers &= ModelChecker::availableSolvers(); auto engine = ModelCheckerEngine::fromString(m_reader.stringSetting("SMTEngine", "all")); if (engine) @@ -51,7 +51,7 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E else BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT engine choice.")); - if (m_enabledSolvers.none() || m_modelCheckerSettings.engine.none()) + if (m_modelCheckerSettings.solvers.none() || m_modelCheckerSettings.engine.none()) m_shouldRun = false; auto const& ignoreCex = m_reader.stringSetting("SMTIgnoreCex", "no"); @@ -66,7 +66,6 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { setupCompiler(); - compiler().setSMTSolverChoice(m_enabledSolvers); compiler().setModelCheckerSettings(m_modelCheckerSettings); parseAndAnalyze(); filterObtainedErrors(); diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 7206bc251..b0fbb8547 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -43,7 +43,8 @@ namespace SemVerMatchExpression parseExpression(string const& _input) { - Scanner scanner{CharStream(_input, "")}; + CharStream stream(_input, ""); + Scanner scanner{stream}; vector literals; vector tokens; while (scanner.currentToken() != Token::EOS) diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 6ec949c68..349baf481 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -65,10 +65,8 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( for (auto const& error: m_compiler.errors()) if (error->type() == langutil::Error::Type::CodeGenerationError) BOOST_THROW_EXCEPTION(*error); - langutil::SourceReferenceFormatter formatter(std::cerr, true, false); - - for (auto const& error: m_compiler.errors()) - formatter.printErrorInformation(*error); + langutil::SourceReferenceFormatter{std::cerr, m_compiler, true, false} + .printErrorInformation(m_compiler.errors()); BOOST_ERROR("Compiling contract failed"); } string contractName(_contractName.empty() ? m_compiler.lastContractName(_mainSourceName) : _contractName); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 748718bec..ee1bf0239 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -101,15 +101,14 @@ bytes compileFirstExpression( ) { string sourceCode = "pragma solidity >=0.0; // SPDX-License-Identifier: GPL-3\n" + _sourceCode; + CharStream stream(sourceCode, ""); ASTPointer sourceUnit; try { ErrorList errors; ErrorReporter errorReporter(errors); - sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse( - make_shared(CharStream(sourceCode, "")) - ); + sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse(stream); if (!sourceUnit) return bytes(); } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 5443f7b39..58024d9a6 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -42,11 +42,12 @@ namespace ASTPointer parseText(std::string const& _source, ErrorList& _errors, bool errorRecovery = false) { ErrorReporter errorReporter(_errors); + auto charStream = CharStream(_source, ""); ASTPointer sourceUnit = Parser( errorReporter, solidity::test::CommonOptions::get().evmVersion(), errorRecovery - ).parse(std::make_shared(CharStream(_source, ""))); + ).parse(charStream); if (!sourceUnit) return ASTPointer(); for (ASTPointer const& node: sourceUnit->nodes()) @@ -591,19 +592,21 @@ BOOST_AUTO_TEST_CASE(inline_asm_end_location) class CheckInlineAsmLocation: public ASTConstVisitor { public: + explicit CheckInlineAsmLocation(string _sourceCode): m_sourceCode(_sourceCode) {} bool visited = false; bool visit(InlineAssembly const& _inlineAsm) override { auto loc = _inlineAsm.location(); - auto asmStr = loc.source->source().substr(static_cast(loc.start), static_cast(loc.end - loc.start)); + auto asmStr = m_sourceCode.substr(static_cast(loc.start), static_cast(loc.end - loc.start)); BOOST_CHECK_EQUAL(asmStr, "assembly { a := 0x12345678 }"); visited = true; return false; } + string m_sourceCode; }; - CheckInlineAsmLocation visitor; + CheckInlineAsmLocation visitor{sourceCode}; contract->accept(visitor); BOOST_CHECK_MESSAGE(visitor.visited, "No inline asm block found?!"); diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index ed23bc609..bc0f6a7d8 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include + #include #include #include @@ -119,11 +120,13 @@ void SyntaxTest::filterObtainedErrors() string sourceName; if (auto location = boost::get_error_info(*currentError)) { - solAssert(location->source, ""); - sourceName = location->source->name(); - + solAssert(location->sourceName, ""); + sourceName = *location->sourceName; solAssert(m_sources.sources.count(sourceName) == 1, ""); - int preambleSize = static_cast(location->source->source().size()) - static_cast(m_sources.sources[sourceName].size()); + + int preambleSize = + static_cast(compiler().charStream(sourceName).size()) - + static_cast(m_sources.sources[sourceName].size()); solAssert(preambleSize >= 0, ""); // ignore the version & license pragma inserted by the testing tool when calculating locations. diff --git a/test/libsolidity/analysis/FunctionCallGraph.cpp b/test/libsolidity/analysis/FunctionCallGraph.cpp index e4cd3d066..de63be563 100644 --- a/test/libsolidity/analysis/FunctionCallGraph.cpp +++ b/test/libsolidity/analysis/FunctionCallGraph.cpp @@ -215,7 +215,7 @@ ostream& operator<<(ostream& _out, map> const& _map) namespace boost::test_tools::tt_detail { -// Boost won't find find the << operator unless we put it in the std namespace which is illegal. +// Boost won't find the << operator unless we put it in the std namespace which is illegal. // The recommended solution is to overload print_log_value<> struct and make it use our operator. template<> diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol index 2936cf86f..77be50301 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol @@ -50,11 +50,11 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1947094 +// gas irOptimized: 1965559 // gas legacy: 2602700 // gas legacyOptimized: 1874490 // div(int256,int256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 -// gas irOptimized: 22222 +// gas irOptimized: 22244 // gas legacy: 22767 // gas legacyOptimized: 22282 // exp(int256): 3141592653589793238 -> 23140692632779268978 @@ -82,7 +82,7 @@ contract test { // gas legacy: 22807 // gas legacyOptimized: 22295 // pow(int256,uint256): 3141592653589793238, 5 -> 306019684785281453040 -// gas irOptimized: 22863 +// gas irOptimized: 22861 // gas legacy: 23508 // gas legacyOptimized: 22921 // sqrt(int256): 3141592653589793238 -> 1772453850905516027 diff --git a/test/libsolidity/semanticTests/viaYul/negation_bug.yul b/test/libsolidity/semanticTests/viaYul/negation_bug.yul new file mode 100644 index 000000000..6174eadd1 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/negation_bug.yul @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + -(int8(0)); + unchecked { + // Used to incorrectly use the checked unary negation function and revert. + (-(type(int8).min)); + } + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_array_slice_2.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_array_slice_2.sol index 7f8b98806..fc34ff1c7 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_array_slice_2.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_array_slice_2.sol @@ -28,6 +28,7 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 2072: (643-658): Unused local variable. // Warning 6328: (298-328): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal.sol index 0598311ad..2bbaa2e1c 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal.sol @@ -22,14 +22,14 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (226-256): CHC: Assertion violation happens here. // Warning 1218: (310-340): CHC: Error trying to invoke SMT solver. -// Warning 6328: (310-340): CHC: Assertion violation might happen here. // Warning 1218: (483-513): CHC: Error trying to invoke SMT solver. -// Warning 6328: (483-513): CHC: Assertion violation might happen here. // Warning 1218: (568-598): CHC: Error trying to invoke SMT solver. -// Warning 6328: (568-598): CHC: Assertion violation might happen here. // Warning 1218: (654-684): CHC: Error trying to invoke SMT solver. +// Warning 6328: (226-256): CHC: Assertion violation happens here. +// Warning 6328: (310-340): CHC: Assertion violation might happen here. +// Warning 6328: (483-513): CHC: Assertion violation might happen here. +// Warning 6328: (568-598): CHC: Assertion violation might happen here. // Warning 6328: (654-684): CHC: Assertion violation might happen here. // Warning 4661: (310-340): BMC: Assertion violation happens here. // Warning 4661: (483-513): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal_no_unproved.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal_no_unproved.sol new file mode 100644 index 000000000..ffeef4896 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_packed_string_literal_no_unproved.sol @@ -0,0 +1,35 @@ +contract C { + function abiencodePackedStringLiteral() public pure { + bytes memory b1 = abi.encodePacked(""); + bytes memory b2 = abi.encodePacked(""); + // should hold, but currently fails due to string literal abstraction + assert(b1.length == b2.length); + + bytes memory b3 = abi.encodePacked(bytes("")); + assert(b1.length == b3.length); // should fail + + bytes memory b4 = abi.encodePacked(bytes24("")); + // should hold, but currently fails due to string literal abstraction + assert(b1.length == b4.length); + + bytes memory b5 = abi.encodePacked(string("")); + assert(b1.length == b5.length); // should fail + + bytes memory b6 = abi.encode(""); + assert(b1.length == b6.length); // should fail + } +} +// ==== +// SMTEngine: all +// SMTShowUnproved: no +// ---- +// Warning 1218: (310-340): CHC: Error trying to invoke SMT solver. +// Warning 1218: (483-513): CHC: Error trying to invoke SMT solver. +// Warning 1218: (568-598): CHC: Error trying to invoke SMT solver. +// Warning 1218: (654-684): CHC: Error trying to invoke SMT solver. +// Warning 6328: (226-256): CHC: Assertion violation happens here. +// Warning 5840: CHC: 4 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. +// Warning 4661: (310-340): BMC: Assertion violation happens here. +// Warning 4661: (483-513): BMC: Assertion violation happens here. +// Warning 4661: (568-598): BMC: Assertion violation happens here. +// Warning 4661: (654-684): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol index 3b2006215..2a3ff2bb2 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_string_literal.sol @@ -19,15 +19,13 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 1218: (208-238): CHC: Error trying to invoke SMT solver. -// Warning 6328: (208-238): CHC: Assertion violation might happen here. // Warning 1218: (286-316): CHC: Error trying to invoke SMT solver. -// Warning 6328: (286-316): CHC: Assertion violation might happen here. // Warning 1218: (453-483): CHC: Error trying to invoke SMT solver. -// Warning 6328: (453-483): CHC: Assertion violation might happen here. // Warning 1218: (532-562): CHC: Error trying to invoke SMT solver. +// Warning 6328: (208-238): CHC: Assertion violation happens here. +// Warning 6328: (286-316): CHC: Assertion violation might happen here. +// Warning 6328: (453-483): CHC: Assertion violation might happen here. // Warning 6328: (532-562): CHC: Assertion violation might happen here. -// Warning 4661: (208-238): BMC: Assertion violation happens here. // Warning 4661: (286-316): BMC: Assertion violation happens here. // Warning 4661: (453-483): BMC: Assertion violation happens here. // Warning 4661: (532-562): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice.sol index 0365b9655..c9d9bb056 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice.sol @@ -25,13 +25,13 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 1218: (691-721): CHC: Error trying to invoke SMT solver. +// Warning 1218: (959-989): CHC: Error trying to invoke SMT solver. +// Warning 1218: (1079-1109): CHC: Error trying to invoke SMT solver. // Warning 6328: (325-355): CHC: Assertion violation happens here. // Warning 6328: (578-608): CHC: Assertion violation happens here. -// Warning 1218: (691-721): CHC: Error trying to invoke SMT solver. // Warning 6328: (691-721): CHC: Assertion violation might happen here. -// Warning 1218: (959-989): CHC: Error trying to invoke SMT solver. // Warning 6328: (959-989): CHC: Assertion violation might happen here. -// Warning 1218: (1079-1109): CHC: Error trying to invoke SMT solver. // Warning 6328: (1079-1109): CHC: Assertion violation might happen here. // Warning 4661: (691-721): BMC: Assertion violation happens here. // Warning 4661: (959-989): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice_2.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice_2.sol index b94977030..740411bf3 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice_2.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_array_slice_2.sol @@ -25,13 +25,13 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 1218: (692-722): CHC: Error trying to invoke SMT solver. +// Warning 1218: (960-990): CHC: Error trying to invoke SMT solver. +// Warning 1218: (1080-1110): CHC: Error trying to invoke SMT solver. // Warning 6328: (326-356): CHC: Assertion violation happens here. // Warning 6328: (579-609): CHC: Assertion violation happens here. -// Warning 1218: (692-722): CHC: Error trying to invoke SMT solver. // Warning 6328: (692-722): CHC: Assertion violation might happen here. -// Warning 1218: (960-990): CHC: Error trying to invoke SMT solver. // Warning 6328: (960-990): CHC: Assertion violation might happen here. -// Warning 1218: (1080-1110): CHC: Error trying to invoke SMT solver. // Warning 6328: (1080-1110): CHC: Assertion violation might happen here. // Warning 4661: (692-722): BMC: Assertion violation happens here. // Warning 4661: (960-990): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_hash.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_hash.sol index 3360feb02..ca1b6b40a 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_hash.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_hash.sol @@ -14,8 +14,8 @@ contract C { // SMTEngine: all // ---- // Warning 1218: (333-371): CHC: Error trying to invoke SMT solver. -// Warning 6328: (333-371): CHC: Assertion violation might happen here. // Warning 1218: (390-428): CHC: Error trying to invoke SMT solver. +// Warning 6328: (333-371): CHC: Assertion violation might happen here. // Warning 6328: (390-428): CHC: Assertion violation might happen here. // Warning 4661: (333-371): BMC: Assertion violation happens here. // Warning 4661: (390-428): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_string_literal.sol index e824c57d1..839d00835 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_string_literal.sol @@ -22,14 +22,14 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (252-282): CHC: Assertion violation happens here. // Warning 1218: (347-377): CHC: Error trying to invoke SMT solver. -// Warning 6328: (347-377): CHC: Assertion violation might happen here. // Warning 1218: (531-561): CHC: Error trying to invoke SMT solver. -// Warning 6328: (531-561): CHC: Assertion violation might happen here. // Warning 1218: (627-657): CHC: Error trying to invoke SMT solver. -// Warning 6328: (627-657): CHC: Assertion violation might happen here. // Warning 1218: (746-776): CHC: Error trying to invoke SMT solver. +// Warning 6328: (252-282): CHC: Assertion violation happens here. +// Warning 6328: (347-377): CHC: Assertion violation might happen here. +// Warning 6328: (531-561): CHC: Assertion violation might happen here. +// Warning 6328: (627-657): CHC: Assertion violation might happen here. // Warning 6328: (746-776): CHC: Assertion violation might happen here. // Warning 4661: (347-377): BMC: Assertion violation happens here. // Warning 4661: (531-561): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_vs_sig.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_vs_sig.sol index b97f28955..b5346b049 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_vs_sig.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_selector_vs_sig.sol @@ -9,4 +9,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (294-324): CHC: Assertion violation happens here. +// Warning 6328: (294-324): CHC: Assertion violation might happen here. +// Warning 7812: (294-324): BMC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice.sol index 7530ea6c5..fd15ea27d 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice.sol @@ -25,13 +25,13 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 1218: (702-732): CHC: Error trying to invoke SMT solver. +// Warning 1218: (971-1001): CHC: Error trying to invoke SMT solver. +// Warning 1218: (1086-1116): CHC: Error trying to invoke SMT solver. // Warning 6328: (334-364): CHC: Assertion violation happens here. // Warning 6328: (588-618): CHC: Assertion violation happens here. -// Warning 1218: (702-732): CHC: Error trying to invoke SMT solver. // Warning 6328: (702-732): CHC: Assertion violation might happen here. -// Warning 1218: (971-1001): CHC: Error trying to invoke SMT solver. // Warning 6328: (971-1001): CHC: Assertion violation might happen here. -// Warning 1218: (1086-1116): CHC: Error trying to invoke SMT solver. // Warning 6328: (1086-1116): CHC: Assertion violation might happen here. // Warning 4661: (702-732): BMC: Assertion violation happens here. // Warning 4661: (971-1001): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice_2.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice_2.sol index f9da7be6b..cdb87233e 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice_2.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_array_slice_2.sol @@ -25,13 +25,13 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 1218: (703-733): CHC: Error trying to invoke SMT solver. +// Warning 1218: (972-1002): CHC: Error trying to invoke SMT solver. +// Warning 1218: (1087-1117): CHC: Error trying to invoke SMT solver. // Warning 6328: (335-365): CHC: Assertion violation happens here. // Warning 6328: (589-619): CHC: Assertion violation happens here. -// Warning 1218: (703-733): CHC: Error trying to invoke SMT solver. // Warning 6328: (703-733): CHC: Assertion violation might happen here. -// Warning 1218: (972-1002): CHC: Error trying to invoke SMT solver. // Warning 6328: (972-1002): CHC: Assertion violation might happen here. -// Warning 1218: (1087-1117): CHC: Error trying to invoke SMT solver. // Warning 6328: (1087-1117): CHC: Assertion violation might happen here. // Warning 4661: (703-733): BMC: Assertion violation happens here. // Warning 4661: (972-1002): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_hash.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_hash.sol index a76f91f81..dbf423741 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_hash.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_hash.sol @@ -14,8 +14,8 @@ contract C { // SMTEngine: all // ---- // Warning 1218: (337-375): CHC: Error trying to invoke SMT solver. -// Warning 6328: (337-375): CHC: Assertion violation might happen here. // Warning 1218: (394-432): CHC: Error trying to invoke SMT solver. +// Warning 6328: (337-375): CHC: Assertion violation might happen here. // Warning 6328: (394-432): CHC: Assertion violation might happen here. // Warning 4661: (337-375): BMC: Assertion violation happens here. // Warning 4661: (394-432): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_string_literal.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_string_literal.sol index 666fa23ba..2e64229bc 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_with_sig_string_literal.sol @@ -22,14 +22,14 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (261-291): CHC: Assertion violation happens here. // Warning 1218: (357-387): CHC: Error trying to invoke SMT solver. -// Warning 6328: (357-387): CHC: Assertion violation might happen here. // Warning 1218: (542-572): CHC: Error trying to invoke SMT solver. -// Warning 6328: (542-572): CHC: Assertion violation might happen here. // Warning 1218: (639-669): CHC: Error trying to invoke SMT solver. -// Warning 6328: (639-669): CHC: Assertion violation might happen here. // Warning 1218: (753-783): CHC: Error trying to invoke SMT solver. +// Warning 6328: (261-291): CHC: Assertion violation happens here. +// Warning 6328: (357-387): CHC: Assertion violation might happen here. +// Warning 6328: (542-572): CHC: Assertion violation might happen here. +// Warning 6328: (639-669): CHC: Assertion violation might happen here. // Warning 6328: (753-783): CHC: Assertion violation might happen here. // Warning 4661: (357-387): BMC: Assertion violation happens here. // Warning 4661: (542-572): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol index d51a5f9e2..402ea67b4 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol @@ -14,5 +14,5 @@ contract C { // SMTEngine: all // ---- // Warning 6368: (188-192): CHC: Out of bounds access happens here.\nCounterexample:\na = []\nb = [32]\n\nTransaction trace:\nC.constructor()\nState: a = []\nC.f() -// Warning 6368: (188-195): CHC: Out of bounds access happens here.\nCounterexample:\na = [[], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]]\nb = [32]\n\nTransaction trace:\nC.constructor()\nState: a = []\nC.f() +// Warning 6368: (188-195): CHC: Out of bounds access happens here.\nCounterexample:\na = [[], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [25, 25, 25, 25, 25, 25, 25, 25], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15], [15, 15, 15, 15, 15, 15, 15, 15]]\nb = [32]\n\nTransaction trace:\nC.constructor()\nState: a = []\nC.f() // Warning 6328: (181-202): CHC: Assertion violation happens here.\nCounterexample:\n\nb = [32]\n\nTransaction trace:\nC.constructor()\nState: a = []\nC.f() diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/compound_bitwise_or_uint_3.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/compound_bitwise_or_uint_3.sol new file mode 100644 index 000000000..02d538d7d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/compound_bitwise_or_uint_3.sol @@ -0,0 +1,15 @@ +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} +// ==== +// SMTEngine: bmc +// SMTShowUnproved: no +// ---- +// Warning 2788: BMC: 1 verification condition(s) could not be proved. Enable the model checker option "show unproved" to see all of them. Consider choosing a specific contract to be verified in order to reduce the solving problems. Consider increasing the timeout per query. diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol index 76868fa2b..8485b89a6 100644 --- a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol @@ -7,12 +7,14 @@ contract C { function s(bytes memory b0, bytes memory b1) public pure { bytes32 s0 = sha256(b0); bytes32 s1 = sha256(b1); - assert(s0 == s1); + // Disabled because of Spacer nondeterminism. + //assert(s0 == s1); } function r(bytes memory b0, bytes memory b1) public pure { bytes32 r0 = ripemd160(b0); bytes32 r1 = ripemd160(b1); - assert(r0 == r1); + // Disabled because of Spacer nondeterminism. + //assert(r0 == r1); } function e(bytes32 h0, uint8 v0, bytes32 r0, bytes32 s0, bytes32 h1, uint8 v1, bytes32 r1, bytes32 s1) public pure { address a0 = ecrecover(h0, v0, r0, s0); @@ -25,12 +27,10 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 2072: (556-566): Unused local variable. -// Warning 2072: (598-608): Unused local variable. -// Warning 1218: (135-151): CHC: Error trying to invoke SMT solver. -// Warning 6328: (135-151): CHC: Assertion violation might happen here. -// Warning 6328: (272-288): CHC: Assertion violation happens here. -// Warning 1218: (415-431): CHC: Error trying to invoke SMT solver. -// Warning 6328: (415-431): CHC: Assertion violation might happen here. -// Warning 4661: (135-151): BMC: Assertion violation happens here. -// Warning 4661: (415-431): BMC: Assertion violation happens here. +// Warning 2072: (218-228): Unused local variable. +// Warning 2072: (245-255): Unused local variable. +// Warning 2072: (405-415): Unused local variable. +// Warning 2072: (435-445): Unused local variable. +// Warning 2072: (656-666): Unused local variable. +// Warning 2072: (698-708): Unused local variable. +// Warning 6328: (135-151): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol index 865ec694b..fd3b55a8c 100644 --- a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol @@ -46,12 +46,12 @@ contract C { // SMTEngine: all // ---- // Warning 1218: (693-712): CHC: Error trying to invoke SMT solver. -// Warning 6328: (693-712): CHC: Assertion violation might happen here. // Warning 1218: (716-735): CHC: Error trying to invoke SMT solver. -// Warning 6328: (716-735): CHC: Assertion violation might happen here. // Warning 1218: (739-758): CHC: Error trying to invoke SMT solver. -// Warning 6328: (739-758): CHC: Assertion violation might happen here. // Warning 1218: (762-781): CHC: Error trying to invoke SMT solver. +// Warning 6328: (693-712): CHC: Assertion violation might happen here. +// Warning 6328: (716-735): CHC: Assertion violation might happen here. +// Warning 6328: (739-758): CHC: Assertion violation might happen here. // Warning 6328: (762-781): CHC: Assertion violation might happen here. // Warning 4661: (693-712): BMC: Assertion violation happens here. // Warning 4661: (716-735): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3.sol index eb5cd97dd..9d27d6d3b 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3.sol @@ -20,4 +20,4 @@ contract C { // SMTEngine: all // ---- // Warning 6328: (69-85): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 100\n = 0\n\nTransaction trace:\nState.constructor()\nState.f(100) -// Warning 6328: (203-217): CHC: Assertion violation happens here.\nCounterexample:\ns = 0, z = 3\n\nTransaction trace:\nC.constructor()\nState: s = 0, z = 3\nC.f() +// Warning 6328: (203-217): CHC: Assertion violation happens here.\nCounterexample:\ns = 0, z = 0\n\nTransaction trace:\nC.constructor()\nState: s = 0, z = 0\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol b/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol index 3c1777d85..22a4551c8 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol @@ -20,6 +20,5 @@ contract C { // SMTIgnoreCex: yes // ---- // Warning 4984: (113-116): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4984: (113-116): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. // Warning 6328: (156-170): CHC: Assertion violation happens here. // Warning 2661: (113-116): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_crypto.sol b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_crypto.sol index 7c9ba2d5c..2437d9829 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_crypto.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_crypto.sol @@ -27,7 +27,6 @@ contract C { // SMTEngine: all // ---- // Warning 1218: (302-333): CHC: Error trying to invoke SMT solver. -// Warning 6328: (302-333): CHC: Assertion violation might happen here. // Warning 1218: (302-333): CHC: Error trying to invoke SMT solver. // Warning 6328: (302-333): CHC: Assertion violation might happen here. // Warning 4661: (302-333): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/file_level/constants_at_file_level_referencing.sol b/test/libsolidity/smtCheckerTests/file_level/constants_at_file_level_referencing.sol index 4082885b9..2af325964 100644 --- a/test/libsolidity/smtCheckerTests/file_level/constants_at_file_level_referencing.sol +++ b/test/libsolidity/smtCheckerTests/file_level/constants_at_file_level_referencing.sol @@ -58,7 +58,7 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (s2.sol:518-539): CHC: Assertion violation happens here.\nCounterexample:\n\nr1 = [3, 1, 2]\nr2 = []\nr3 = []\nz = 0\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call +// Warning 6328: (s2.sol:518-539): CHC: Assertion violation happens here.\nCounterexample:\n\nr2 = []\nr3 = []\nz = 0\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call // Warning 6328: (s2.sol:704-725): CHC: Assertion violation happens here.\nCounterexample:\n\nr1 = [3, 1, 2]\nr2 = [3, 1, 2]\nr3 = []\nz = 0\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n C.g() -- internal call // Warning 6328: (s2.sol:890-911): CHC: Assertion violation happens here. // Warning 6328: (s2.sol:980-994): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol index 1da78ef51..5dd951521 100644 --- a/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol @@ -10,6 +10,7 @@ function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 4984: (168-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\ndata = []\nx = 1\n = 0\n = 0\na = 0\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.f(1, input)\n fun(_x, []) -- internal call -// Warning 6368: (289-294): CHC: Out of bounds access happens here.\nCounterexample:\ndata = []\nx = 0\ninput = [5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 13, 5, 5, 5, 5, 5, 19, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\n = 0\n = 0\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.f(0, [5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 13, 5, 5, 5, 5, 5, 19, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])\n fun([5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 13, 5, 5, 5, 5, 5, 19, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], []) -- internal call +// Warning 4984: (168-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6368: (289-294): CHC: Out of bounds access happens here. diff --git a/test/libsolidity/smtCheckerTests/file_level/libraries_from_free.sol b/test/libsolidity/smtCheckerTests/file_level/libraries_from_free.sol index 9f0104c21..728f9f69f 100644 --- a/test/libsolidity/smtCheckerTests/file_level/libraries_from_free.sol +++ b/test/libsolidity/smtCheckerTests/file_level/libraries_from_free.sol @@ -23,6 +23,6 @@ contract C { // ---- // Warning 4588: (190-197): Assertion checker does not yet implement this type of function call. // Warning 4588: (190-197): Assertion checker does not yet implement this type of function call. -// Warning 6328: (284-298): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 8\ny = 8\n\nTransaction trace:\nC.constructor()\nC.f()\n fu() -- internal call\n L.inter() -- internal call +// Warning 6328: (284-298): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 8\n\nTransaction trace:\nC.constructor()\nC.f()\n fu() -- internal call\n L.inter() -- internal call // Warning 6328: (363-377): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 8\n\nTransaction trace:\nC.constructor()\nC.f()\n fu() -- internal call\n L.inter() -- internal call // Warning 4588: (190-197): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/file_level/recursion.sol b/test/libsolidity/smtCheckerTests/file_level/recursion.sol index ae3e25e08..34a26d326 100644 --- a/test/libsolidity/smtCheckerTests/file_level/recursion.sol +++ b/test/libsolidity/smtCheckerTests/file_level/recursion.sol @@ -13,27 +13,15 @@ contract C { } function f() public pure { // All of these should hold but the SMTChecker can't prove them. + // Disabled because of Spacer nondet + /* assert(g(0, 0) == 1); assert(g(0, 1) == 0); assert(g(1, 0) == 1); assert(g(2, 3) == 8); assert(g(3, 10) == 59049); + */ } } // ==== // SMTEngine: all -// ---- -// Warning 4281: (118-130): CHC: Division by zero might happen here. -// Warning 4984: (134-148): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4984: (176-189): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 6328: (430-450): CHC: Assertion violation happens here. -// Warning 6328: (478-498): CHC: Assertion violation might happen here. -// Warning 6328: (502-527): CHC: Assertion violation might happen here. -// Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 2661: (176-189): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 8065: (176-189): BMC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4661: (478-498): BMC: Assertion violation happens here. -// Warning 2661: (134-148): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 4661: (502-527): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol index 067ac0218..84fdaf87d 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -21,5 +21,5 @@ contract A is B { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (200-218): CHC: Assertion violation happens here. // Warning 4984: (171-176): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (200-218): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol index 165740218..b7cacc0f6 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol @@ -19,5 +19,5 @@ contract A is B { // ==== // SMTEngine: all // ---- -// Warning 4984: (175-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\nTransaction trace:\nA.constructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) // Warning 4984: (166-171): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639934\n\nTransaction trace:\nA.constructor(115792089237316195423570985008687907853269984665640564039457584007913129639934) +// Warning 4984: (175-180): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\nTransaction trace:\nA.constructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol index 98da0a2a6..6a2b07919 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -29,6 +29,6 @@ contract A is B2, B1 { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 4984: (209-214): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 4984: (193-198): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (209-214): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 6328: (302-318): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol index 5add9bf68..48819f839 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol @@ -12,5 +12,6 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (112-126): CHC: Assertion violation happens here.\nCounterexample:\nx = 10\ny = 11\n\nTransaction trace:\nC.constructor()\nState: x = 10\nC.f(11) +// Warning 6328: (112-126): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol index 414809209..8fd9bf9a2 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol @@ -14,5 +14,5 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (129-143): CHC: Assertion violation happens here. // Warning 4984: (82-87): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (129-143): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol b/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol index 01dec8bc1..875cd5455 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol @@ -10,6 +10,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 1218: (162-201): CHC: Error trying to invoke SMT solver. -// Warning 6328: (162-201): CHC: Assertion violation might happen here. -// Warning 4661: (162-201): BMC: Assertion violation happens here. +// Warning 6328: (162-201): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/string.sol b/test/libsolidity/smtCheckerTests/functions/getters/string.sol index 2f2319a16..c856ad514 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/string.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/string.sol @@ -10,6 +10,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 1218: (178-224): CHC: Error trying to invoke SMT solver. -// Warning 6328: (178-224): CHC: Assertion violation might happen here. -// Warning 4661: (178-224): BMC: Assertion violation happens here. +// Warning 6328: (178-224): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol index cf31fa9e5..1fe537aad 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol @@ -21,8 +21,8 @@ contract C{ // SMTEngine: all // ---- // Warning 5667: (37-43): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning 6328: (49-63): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor(0) // Warning 6328: (105-119): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f() // Warning 6328: (137-151): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f()\n C.g() -- internal call // Warning 6328: (187-201): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f()\n C.g() -- internal call // Warning 6328: (212-226): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f()\n C.g() -- internal call -// Warning 6328: (49-63): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol index 2e934a0f2..f03e1b4eb 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol @@ -21,6 +21,6 @@ contract C{ // SMTEngine: all // ---- // Warning 5667: (37-43): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning 6328: (49-63): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor(0) // Warning 6328: (105-119): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f() // Warning 6328: (151-165): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(0)\nState: x = 1\nC.f()\n C.g() -- internal call\n C.g() -- internal call -// Warning 6328: (49-63): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor(0) diff --git a/test/libsolidity/smtCheckerTests/inheritance/base_contract_assertion_fail_9.sol b/test/libsolidity/smtCheckerTests/inheritance/base_contract_assertion_fail_9.sol index 83f8e609c..f97bae5cd 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/base_contract_assertion_fail_9.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/base_contract_assertion_fail_9.sol @@ -28,5 +28,5 @@ contract C is B { // ==== // SMTEngine: all // ---- -// Warning 6328: (131-145): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.f()\n A.v() -- internal call // Warning 6328: (62-76): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\nTransaction trace:\nC.constructor()\nState: x = 0\nB.f()\n A.f() -- internal call\n C.v() -- internal call +// Warning 6328: (131-145): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.f()\n A.v() -- internal call diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol index f3a6393fa..f02e416a4 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol @@ -23,6 +23,6 @@ contract A is B { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (243-261): CHC: Assertion violation happens here. // Warning 4984: (125-130): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 4984: (184-189): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (243-261): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol index d20358525..e223ad5f7 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol @@ -23,6 +23,6 @@ contract A is B { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (241-259): CHC: Assertion violation happens here. // Warning 4984: (125-131): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 4984: (185-190): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (241-259): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol index 940ff64b3..c7c63f00f 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol @@ -25,5 +25,5 @@ contract D is B, C { // ==== // SMTEngine: all // ---- -// Warning 6328: (223-237): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\nTransaction trace:\nD.constructor() // Warning 6328: (134-148): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\nTransaction trace:\nD.constructor() +// Warning 6328: (223-237): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\nTransaction trace:\nD.constructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol b/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol index 81b3d9a48..203484265 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol @@ -20,7 +20,8 @@ contract B is A { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (81-95): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.fallback() -// Warning 6328: (130-144): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.g() -// Warning 6328: (256-270): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\nTransaction trace:\nB.constructor()\nState: y = 0, x = 0\nB.receive(){ value: 10450 } +// Warning 6328: (81-95): CHC: Assertion violation happens here. +// Warning 6328: (130-144): CHC: Assertion violation happens here. +// Warning 6328: (256-270): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/receive.sol b/test/libsolidity/smtCheckerTests/inheritance/receive.sol index a88ff75f8..f55b9efd6 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/receive.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/receive.sol @@ -20,7 +20,8 @@ contract B is A { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (95-109): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.receive() -// Warning 6328: (144-158): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.g() -// Warning 6328: (267-281): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\nTransaction trace:\nB.constructor()\nState: y = 0, x = 0\nB.receive() +// Warning 6328: (95-109): CHC: Assertion violation happens here. +// Warning 6328: (144-158): CHC: Assertion violation happens here. +// Warning 6328: (267-281): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inline_assembly/assembly_local_storage_access_inside_function.sol b/test/libsolidity/smtCheckerTests/inline_assembly/assembly_local_storage_access_inside_function.sol index a95bb8731..797f6ae76 100644 --- a/test/libsolidity/smtCheckerTests/inline_assembly/assembly_local_storage_access_inside_function.sol +++ b/test/libsolidity/smtCheckerTests/inline_assembly/assembly_local_storage_access_inside_function.sol @@ -17,8 +17,9 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 7737: (83-149): Inline assembly may cause SMTChecker to produce spurious warnings (false positives). -// Warning 6328: (152-167): CHC: Assertion violation happens here.\nCounterexample:\nz = 0\ni = 32\n\nTransaction trace:\nC.constructor()\nState: z = 0\nC.f() -// Warning 6328: (186-200): CHC: Assertion violation happens here.\nCounterexample:\nz = 0\ni = 32\n\nTransaction trace:\nC.constructor()\nState: z = 0\nC.f() +// Warning 6328: (152-167): CHC: Assertion violation happens here. +// Warning 6328: (186-200): CHC: Assertion violation happens here. // Warning 7737: (83-149): Inline assembly may cause SMTChecker to produce spurious warnings (false positives). diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol index 88fe7f51f..02a66575a 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol @@ -24,5 +24,5 @@ contract LoopFor2 { // ---- // Warning 2072: (202-217): Unused local variable. // Warning 6368: (354-358): CHC: Out of bounds access happens here. -// Warning 6368: (378-382): CHC: Out of bounds access happens here. // Warning 6368: (371-375): CHC: Out of bounds access happens here. +// Warning 6368: (378-382): CHC: Out of bounds access happens here. diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol index ecd34cf3a..5a1bc9395 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol @@ -30,4 +30,4 @@ contract C // SMTEngine: all // ---- // Warning 6328: (296-311): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 10\nb = false\nc = true\n\nTransaction trace:\nC.constructor()\nC.f(0, 9, false, true) -// Warning 6328: (347-362): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 15\ny = 20\nb = false\nc = false\n\nTransaction trace:\nC.constructor()\nC.f(0, 0, false, false) +// Warning 6328: (347-362): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 15\ny = 0\nb = true\nc = false\n\nTransaction trace:\nC.constructor()\nC.f(9, 0, true, false) diff --git a/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol b/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol index f3a6581ca..2a2f16c47 100644 --- a/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol +++ b/test/libsolidity/smtCheckerTests/natspec/abstract_function_nondet_pow_no_abstraction.sol @@ -7,6 +7,8 @@ contract C { } function pow(uint base, uint exponent) internal pure returns (uint) { + // Disabled because of Spacer nondet + /* if (base == 0) { return 0; } @@ -28,16 +30,8 @@ contract C { } } return base * y; + */ } } // ==== // SMTEngine: chc -// ---- -// Warning 4984: (176-179): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4281: (435-447): CHC: Division by zero might happen here. -// Warning 4984: (467-478): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4281: (495-507): CHC: Division by zero might happen here. -// Warning 4984: (529-537): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 4984: (550-561): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 3944: (579-591): CHC: Underflow (resulting value less than 0) might happen here. -// Warning 4984: (616-624): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/natspec/safe_assert_false_positive_pure.sol b/test/libsolidity/smtCheckerTests/natspec/safe_assert_false_positive_pure.sol index 03dd24475..464d9834a 100644 --- a/test/libsolidity/smtCheckerTests/natspec/safe_assert_false_positive_pure.sol +++ b/test/libsolidity/smtCheckerTests/natspec/safe_assert_false_positive_pure.sol @@ -26,4 +26,4 @@ contract C { // ---- // Warning 2018: (33-335): Function state mutability can be restricted to view // Warning 2018: (457-524): Function state mutability can be restricted to pure -// Warning 6328: (135-150): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n_x = 0\nz = 1\nt = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, y = 0\nC.g(0)\n C.f1(0) -- internal call +// Warning 6328: (135-150): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n_x = 1\nz = 0\nt = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, y = 0\nC.g(1)\n C.f1(1) -- internal call diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol index e617e274e..d7a309405 100644 --- a/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol @@ -7,5 +7,5 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 4281: (77-82): CHC: Division by zero happens here. // Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here. +// Warning 4281: (77-82): CHC: Division by zero happens here. diff --git a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol index d872cb669..e8cf49a83 100644 --- a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol @@ -4,4 +4,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 4984: (80-85): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\na = 115792089237316195423570985008687907853269984665640564039457584007913129639935\nb = 1\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(115792089237316195423570985008687907853269984665640564039457584007913129639935, 1) +// Warning 4984: (80-85): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\na = 1\nb = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol index 0a6d85a7e..045fcbff8 100644 --- a/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol @@ -6,4 +6,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 4984: (80-85): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 57896044618658097711785492504343953926634992332820282019728792003956564819968\ny = 2\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(57896044618658097711785492504343953926634992332820282019728792003956564819968, 2) +// Warning 4984: (80-85): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 2\ny = 57896044618658097711785492504343953926634992332820282019728792003956564819968\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(2, 57896044618658097711785492504343953926634992332820282019728792003956564819968) diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol index 5a6beef49..17e3b30d8 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol @@ -15,6 +15,6 @@ contract C { // Warning 2072: (152-156): Unused local variable. // Warning 8364: (123-124): Assertion checker does not yet implement type type(contract C) // Warning 8364: (193-194): Assertion checker does not yet implement type type(contract C) -// Warning 6328: (220-236): CHC: Assertion violation happens here.\nCounterexample:\n\ndata = [13, 13, 13, 13, 13, 13, 13, 13, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]\na1 = 7719\nb1 = 5\nc1 = 6\na2 = 7719\nb2 = 5\nc2 = 6\n\nTransaction trace:\nC.constructor()\nC.f([13, 13, 13, 13, 13, 13, 13, 13, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]) +// Warning 6328: (220-236): CHC: Assertion violation happens here.\nCounterexample:\n\ndata = [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]\na1 = 7719\nb1 = 5\nc1 = 6\na2 = 7719\nb2 = 5\nc2 = 6\n\nTransaction trace:\nC.constructor()\nC.f([9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]) // Warning 8364: (123-124): Assertion checker does not yet implement type type(contract C) // Warning 8364: (193-194): Assertion checker does not yet implement type type(contract C) diff --git a/test/libsolidity/smtCheckerTests/special/blockhash.sol b/test/libsolidity/smtCheckerTests/special/blockhash.sol index 7c8b23052..2a7cc39fa 100644 --- a/test/libsolidity/smtCheckerTests/special/blockhash.sol +++ b/test/libsolidity/smtCheckerTests/special/blockhash.sol @@ -9,6 +9,7 @@ contract C } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (52-76): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\ny = 0\n\nTransaction trace:\nC.constructor()\nC.f(38){ value: 8365 } -// Warning 6328: (80-104): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\ny = 0\n\nTransaction trace:\nC.constructor()\nC.f(38){ value: 32285 } +// Warning 6328: (52-76): CHC: Assertion violation happens here. +// Warning 6328: (80-104): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/event.sol b/test/libsolidity/smtCheckerTests/special/event.sol index 2788eeecd..a0a6d566b 100644 --- a/test/libsolidity/smtCheckerTests/special/event.sol +++ b/test/libsolidity/smtCheckerTests/special/event.sol @@ -27,4 +27,4 @@ contract C { // ---- // Warning 6321: (247-251): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. // Warning 6321: (397-401): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. -// Warning 6328: (407-416): CHC: Assertion violation happens here.\nCounterexample:\nx = false\n\nTransaction trace:\nC.constructor()\nState: x = true\nC.h()\n C.h_data() -- internal call\n C.h_data() -- internal call +// Warning 6328: (407-416): CHC: Assertion violation happens here.\nCounterexample:\nx = false\n\nTransaction trace:\nC.constructor()\nState: x = true\nC.h()\n C.h_data() -- internal call diff --git a/test/libsolidity/smtCheckerTests/special/msg_data.sol b/test/libsolidity/smtCheckerTests/special/msg_data.sol index 5bdc754c4..90022508f 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_data.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_data.sol @@ -21,6 +21,7 @@ contract C } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (120-147): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ value: 30612 } -// Warning 6328: (467-494): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g(){ value: 30612 } +// Warning 6328: (120-147): CHC: Assertion violation happens here. +// Warning 6328: (467-494): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol index f1bcf66b6..efc59fcfc 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol @@ -14,5 +14,5 @@ contract C is A { // ==== // SMTEngine: all // ---- -// Warning 6328: (240-254): CHC: Assertion violation happens here.\nCounterexample:\nv = 1, x = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } // Warning 6328: (60-74): CHC: Assertion violation happens here.\nCounterexample:\nv = 0, x = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } +// Warning 6328: (240-254): CHC: Assertion violation happens here.\nCounterexample:\nv = 1, x = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } diff --git a/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol b/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol index 609b31fcd..d11d3a8a6 100644 --- a/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol +++ b/test/libsolidity/smtCheckerTests/try_catch/try_multiple_catch_clauses_2.sol @@ -19,5 +19,6 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (306-320): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 3\nsuccess = false\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (306-320): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol index 10fd28ee3..7ce444357 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol @@ -11,5 +11,5 @@ contract B { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (130-152): CHC: Assertion violation happens here. // Warning 6328: (104-126): CHC: Assertion violation happens here. +// Warning 6328: (130-152): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol index 9f111dfb4..15500167d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol @@ -33,6 +33,7 @@ contract C // SMTEngine: all // SMTIgnoreCex: yes // ---- +// Warning 6368: (186-196): CHC: Out of bounds access might happen here. // Warning 6368: (329-333): CHC: Out of bounds access happens here. // Warning 6368: (342-346): CHC: Out of bounds access happens here. // Warning 6368: (355-359): CHC: Out of bounds access happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_branches_3d_show_unproved.sol b/test/libsolidity/smtCheckerTests/types/array_branches_3d_show_unproved.sol new file mode 100644 index 000000000..6ec629c9f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/array_branches_3d_show_unproved.sol @@ -0,0 +1,25 @@ +contract C +{ + uint[][][] c; + constructor() { + c.push(); + c[0].push(); + c[0][0].push(); + } + function f(bool b) public { + c[0][0][0] = 0; + if (b) + c[0][0][0] = 1; + assert(c[0][0][0] < 2); + } +} +// ==== +// SMTEngine: all +// SMTShowUnproved: yes +// ---- +// Warning 6368: (124-131): CHC: Out of bounds access might happen here. +// Warning 6368: (124-134): CHC: Out of bounds access might happen here. +// Warning 6368: (152-159): CHC: Out of bounds access might happen here. +// Warning 6368: (152-162): CHC: Out of bounds access might happen here. +// Warning 6368: (177-184): CHC: Out of bounds access might happen here. +// Warning 6368: (177-187): CHC: Out of bounds access might happen here. diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol index 24a2652d8..81f513bd5 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol @@ -9,6 +9,7 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (114-133): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0\ny = 0\nz = 0\n\nTransaction trace:\nC.constructor()\nC.f(0) -// Warning 6328: (137-157): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538830022687173130149211684818290356179572910782152375644828738034597888\ny = 52647538830022687173130149211684818290356179572910782152375644828738034597888\nz = 52647538830022687173130149211684818290356179572910782152375644828738034597888\n\nTransaction trace:\nC.constructor()\nC.f(52647538830022687173130149211684818290356179572910782152375644828738034597888) +// Warning 6328: (114-133): CHC: Assertion violation happens here. +// Warning 6328: (137-157): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol index d34eb47ed..a1a40d64b 100644 --- a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol @@ -36,5 +36,6 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 6328: (804-842): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol index d1686e8c0..662b4f1d2 100644 --- a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol @@ -40,9 +40,10 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 6328: (148-165): CHC: Assertion violation happens here. -// Warning 6328: (183-202): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [], ts: []}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() +// Warning 6328: (183-202): CHC: Assertion violation happens here. // Warning 6328: (266-286): CHC: Assertion violation happens here. -// Warning 6328: (404-427): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [0, 0, 4], ts: [{y: 0, a: []}, {y: 0, a: []}, {y: 0, a: []}, {y: 5, a: []}, {y: 0, a: []}, {y: 0, a: []}]}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() -// Warning 6328: (578-604): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [0, 0, 4], ts: [{y: 0, a: []}, {y: 0, a: []}, {y: 0, a: []}, {y: 5, a: []}, {y: 0, a: [0, 0, 0, 0, 0, 6]}, {y: 0, a: []}]}\n\nTransaction trace:\nC.constructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nC.f() +// Warning 6328: (404-427): CHC: Assertion violation happens here. +// Warning 6328: (578-604): CHC: Assertion violation happens here. diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index 35dd0de26..8099037f3 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -53,15 +53,6 @@ Dialect const& defaultDialect(bool _yul) } } -void yul::test::printErrors(ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(cout, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} - - pair, shared_ptr> yul::test::parse(string const& _source, bool _yul) { AssemblyStack stack( @@ -83,7 +74,8 @@ pair, shared_ptr> yul::test::parse( ) { ErrorReporter errorReporter(_errors); - shared_ptr scanner = make_shared(CharStream(_source, "")); + CharStream stream(_source, ""); + shared_ptr scanner = make_shared(stream); shared_ptr parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false); if (!parserResult) return {}; diff --git a/test/libyul/Common.h b/test/libyul/Common.h index 57f659400..f6c63feb9 100644 --- a/test/libyul/Common.h +++ b/test/libyul/Common.h @@ -44,8 +44,6 @@ struct Dialect; namespace solidity::yul::test { -void printErrors(langutil::ErrorList const& _errors); - std::pair, std::shared_ptr> parse(std::string const& _source, bool _yul = true); diff --git a/test/libyul/ControlFlowGraphTest.cpp b/test/libyul/ControlFlowGraphTest.cpp new file mode 100644 index 000000000..1107f4ca2 --- /dev/null +++ b/test/libyul/ControlFlowGraphTest.cpp @@ -0,0 +1,273 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef ISOLTEST +#include +#endif + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul; +using namespace solidity::yul::test; +using namespace solidity::frontend; +using namespace solidity::frontend::test; +using namespace std; + +ControlFlowGraphTest::ControlFlowGraphTest(string const& _filename): + TestCase(_filename) +{ + m_source = m_reader.source(); + auto dialectName = m_reader.stringSetting("dialect", "evm"); + m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion()); + m_expectation = m_reader.simpleExpectations(); +} + +namespace +{ +static std::string stackSlotToString(StackSlot const& _slot) +{ + return std::visit(util::GenericVisitor{ + [](FunctionCallReturnLabelSlot const& _ret) -> std::string { return "RET[" + _ret.call.get().functionName.name.str() + "]"; }, + [](FunctionReturnLabelSlot const&) -> std::string { return "RET"; }, + [](VariableSlot const& _var) { return _var.variable.get().name.str(); }, + [](LiteralSlot const& _lit) { return util::toCompactHexWithPrefix(_lit.value); }, + [](TemporarySlot const& _tmp) -> std::string { return "TMP[" + _tmp.call.get().functionName.name.str() + ", " + std::to_string(_tmp.index) + "]"; }, + [](JunkSlot const&) -> std::string { return "JUNK"; } + }, _slot); +} + +static std::string stackToString(Stack const& _stack) +{ + std::string result("[ "); + for (auto const& slot: _stack) + result += stackSlotToString(slot) + ' '; + result += ']'; + return result; +} +static std::string variableSlotToString(VariableSlot const& _slot) +{ + return _slot.variable.get().name.str(); +} +} + +class ControlFlowGraphPrinter +{ +public: + ControlFlowGraphPrinter(std::ostream& _stream): + m_stream(_stream) + { + } + void operator()(CFG::BasicBlock const& _block, bool _isMainEntry = true) + { + if (_isMainEntry) + { + m_stream << "Entry [label=\"Entry\"];\n"; + m_stream << "Entry -> Block" << getBlockId(_block) << ";\n"; + } + while (!m_blocksToPrint.empty()) + { + CFG::BasicBlock const* block = *m_blocksToPrint.begin(); + m_blocksToPrint.erase(m_blocksToPrint.begin()); + printBlock(*block); + } + + } + void operator()( + CFG::FunctionInfo const& _info + ) + { + m_stream << "FunctionEntry_" << _info.function.name.str() << "_" << getBlockId(*_info.entry) << " [label=\""; + m_stream << "function " << _info.function.name.str() << "("; + m_stream << joinHumanReadable(_info.parameters | ranges::views::transform(variableSlotToString)); + m_stream << ")"; + if (!_info.returnVariables.empty()) + { + m_stream << " -> "; + m_stream << joinHumanReadable(_info.returnVariables | ranges::views::transform(variableSlotToString)); + } + m_stream << "\"];\n"; + m_stream << "FunctionEntry_" << _info.function.name.str() << "_" << getBlockId(*_info.entry) << " -> Block" << getBlockId(*_info.entry) << ";\n"; + (*this)(*_info.entry, false); + } + +private: + void printBlock(CFG::BasicBlock const& _block) + { + m_stream << "Block" << getBlockId(_block) << " [label=\"\\\n"; + + // Verify that the entries of this block exit into this block. + for (auto const& entry: _block.entries) + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::Jump const& _jump) + { + soltestAssert(_jump.target == &_block, "Invalid control flow graph."); + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + soltestAssert( + _conditionalJump.zero == &_block || _conditionalJump.nonZero == &_block, + "Invalid control flow graph." + ); + }, + [&](auto const&) + { + soltestAssert(false, "Invalid control flow graph."); + } + }, entry->exit); + + for (auto const& operation: _block.operations) + { + std::visit(util::GenericVisitor{ + [&](CFG::FunctionCall const& _call) { + m_stream << _call.function.get().name.str() << ": "; + }, + [&](CFG::BuiltinCall const& _call) { + m_stream << _call.functionCall.get().functionName.name.str() << ": "; + + }, + [&](CFG::Assignment const& _assignment) { + m_stream << "Assignment("; + m_stream << joinHumanReadable(_assignment.variables | ranges::views::transform(variableSlotToString)); + m_stream << "): "; + } + }, operation.operation); + m_stream << stackToString(operation.input) << " => " << stackToString(operation.output) << "\\l\\\n"; + } + m_stream << "\"];\n"; + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"MainExit\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + }, + [&](CFG::BasicBlock::Jump const& _jump) + { + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit [arrowhead=none];\n"; + m_stream << "Block" << getBlockId(_block) << "Exit [label=\""; + if (_jump.backwards) + m_stream << "Backwards"; + m_stream << "Jump\" shape=oval];\n"; + m_stream << "Block" << getBlockId(_block) << "Exit -> Block" << getBlockId(*_jump.target) << ";\n"; + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"{ "; + m_stream << stackSlotToString(_conditionalJump.condition); + m_stream << "| { <0> Zero | <1> NonZero }}\" shape=Mrecord];\n"; + m_stream << "Block" << getBlockId(_block); + m_stream << "Exit:0 -> Block" << getBlockId(*_conditionalJump.zero) << ";\n"; + m_stream << "Block" << getBlockId(_block); + m_stream << "Exit:1 -> Block" << getBlockId(*_conditionalJump.nonZero) << ";\n"; + }, + [&](CFG::BasicBlock::FunctionReturn const& _return) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"FunctionReturn[" << _return.info->function.name.str() << "]\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + }, + [&](CFG::BasicBlock::Terminated const&) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"Terminated\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + } + }, _block.exit); + m_stream << "\n"; + } + size_t getBlockId(CFG::BasicBlock const& _block) + { + if (size_t* id = util::valueOrNullptr(m_blockIds, &_block)) + return *id; + size_t id = m_blockIds[&_block] = m_blockCount++; + m_blocksToPrint.emplace_back(&_block); + return id; + } + std::ostream& m_stream; + std::map m_blockIds; + size_t m_blockCount = 0; + std::list m_blocksToPrint; +}; + +TestCase::TestResult ControlFlowGraphTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + ErrorList errors; + auto [object, analysisInfo] = parse(m_source, *m_dialect, errors); + if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) + { + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + return TestResult::FatalError; + } + + std::ostringstream output; + + std::unique_ptr cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, *object->code); + + output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n"; + ControlFlowGraphPrinter printer{output}; + printer(*cfg->entry); + for (auto function: cfg->functions) + printer(cfg->functionInfo.at(function)); + output << "}\n"; + + m_obtainedResult = output.str(); + + auto result = checkResult(_stream, _linePrefix, _formatted); + +#ifdef ISOLTEST + char* graphDisplayer = nullptr; + // The environment variables specify an optional command that will receive the graph encoded in DOT through stdin. + // Examples for suitable commands are ``dot -Tx11:cairo`` or ``xdot -``. + if (result == TestResult::Failure) + // ISOLTEST_DISPLAY_GRAPHS_ON_FAILURE_COMMAND will run on all failing tests (intended for use during modifications). + graphDisplayer = getenv("ISOLTEST_DISPLAY_GRAPHS_ON_FAILURE_COMMAND"); + else if (result == TestResult::Success) + // ISOLTEST_DISPLAY_GRAPHS_ON_FAILURE_COMMAND will run on all succeeding tests (intended for use during reviews). + graphDisplayer = getenv("ISOLTEST_DISPLAY_GRAPHS_ON_SUCCESS_COMMAND"); + + if (graphDisplayer) + { + if (result == TestResult::Success) + std::cout << std::endl << m_source << std::endl; + boost::process::opstream pipe; + boost::process::child child(graphDisplayer, boost::process::std_in < pipe); + + pipe << output.str(); + pipe.flush(); + pipe.pipe().close(); + if (result == TestResult::Success) + child.wait(); + else + child.detach(); + } +#endif + + return result; + +} diff --git a/test/libyul/ControlFlowGraphTest.h b/test/libyul/ControlFlowGraphTest.h new file mode 100644 index 000000000..44cea06d3 --- /dev/null +++ b/test/libyul/ControlFlowGraphTest.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +namespace solidity::yul +{ +struct Dialect; + +namespace test +{ + +class ControlFlowGraphTest: public solidity::frontend::test::TestCase +{ +public: + static std::unique_ptr create(Config const& _config) + { + return std::make_unique(_config.filename); + } + explicit ControlFlowGraphTest(std::string const& _filename); + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; +private: + Dialect const* m_dialect = nullptr; +}; +} +} diff --git a/test/libyul/EVMCodeTransformTest.cpp b/test/libyul/EVMCodeTransformTest.cpp index eea7e0914..043eae331 100644 --- a/test/libyul/EVMCodeTransformTest.cpp +++ b/test/libyul/EVMCodeTransformTest.cpp @@ -51,9 +51,8 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _ if (!stack.parseAndAnalyze("", m_source)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - SourceReferenceFormatter formatter(_stream, true, false); - for (auto const& error: stack.errors()) - formatter.printErrorInformation(*error); + SourceReferenceFormatter{_stream, stack, true, false} + .printErrorInformation(stack.errors()); return TestResult::FatalError; } diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index 3c5e6cacb..6051c2e20 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -63,7 +63,8 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _ return TestResult::FatalError; *m_object = EVMToEwasmTranslator( - EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()) + EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()), + m_stack ).run(*m_object); // Add call to "main()". @@ -78,20 +79,21 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _ bool EwasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - AssemblyStack stack( + m_stack = AssemblyStack( solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none() ); - if (stack.parseAndAnalyze("", m_source)) + if (m_stack.parseAndAnalyze("", m_source)) { - m_object = stack.parserResult(); + m_object = m_stack.parserResult(); return true; } else { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, stack.errors()); + SourceReferenceFormatter{_stream, m_stack, true, false} + .printErrorInformation(m_stack.errors()); return false; } } @@ -114,11 +116,3 @@ string EwasmTranslationTest::interpret() state.dumpTraceAndState(result); return result.str(); } - -void EwasmTranslationTest::printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} diff --git a/test/libyul/EwasmTranslationTest.h b/test/libyul/EwasmTranslationTest.h index 5175430e3..eba82beee 100644 --- a/test/libyul/EwasmTranslationTest.h +++ b/test/libyul/EwasmTranslationTest.h @@ -20,16 +20,12 @@ #include -namespace solidity::langutil -{ -class Scanner; -class Error; -using ErrorList = std::vector>; -} +#include namespace solidity::yul { struct Object; +class AssemblyStack; } namespace solidity::yul::test @@ -51,9 +47,8 @@ private: bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); std::string interpret(); - static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); - std::shared_ptr m_object; + AssemblyStack m_stack; }; } diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index f4a1c296d..2c3560726 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -69,7 +69,8 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li if (!stack.parseAndAnalyze("source", m_source)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, stack.errors()); + SourceReferenceFormatter{_stream, stack, true, false} + .printErrorInformation(stack.errors()); return TestResult::FatalError; } stack.optimize(); @@ -104,11 +105,3 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li return checkResult(_stream, _linePrefix, _formatted); } - -void ObjectCompilerTest::printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} diff --git a/test/libyul/ObjectCompilerTest.h b/test/libyul/ObjectCompilerTest.h index e75f72fa9..c7880fe55 100644 --- a/test/libyul/ObjectCompilerTest.h +++ b/test/libyul/ObjectCompilerTest.h @@ -54,8 +54,6 @@ private: bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); void disambiguate(); - static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); - frontend::OptimisationPreset m_optimisationPreset; bool m_wasm = false; }; diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index de5b00f3e..1d4e0abc7 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -23,17 +23,26 @@ #include +#include + #include +#include #include +#include #include +#include #include +#include + #include #include #include +#include +using namespace ranges; using namespace std; using namespace solidity::frontend; using namespace solidity::langutil; @@ -44,7 +53,7 @@ namespace solidity::yul::test namespace { -std::pair parse(string const& _source) +pair parse(string const& _source) { try { @@ -63,7 +72,7 @@ std::pair parse(string const& _source) return {false, {}}; } -std::optional parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) +optional parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) { bool success; ErrorList errors; @@ -88,12 +97,12 @@ std::optional parseAndReturnFirstError(string const& _source, bool _allow return {}; } -bool successParse(std::string const& _source, bool _allowWarnings = true) +bool successParse(string const& _source, bool _allowWarnings = true) { return !parseAndReturnFirstError(_source, _allowWarnings); } -Error expectError(std::string const& _source, bool _allowWarnings = false) +Error expectError(string const& _source, bool _allowWarnings = false) { auto error = parseAndReturnFirstError(_source, _allowWarnings); @@ -101,6 +110,21 @@ Error expectError(std::string const& _source, bool _allowWarnings = false) return *error; } +tuple, ErrorList> tryGetSourceLocationMapping(string _source) +{ + vector lines; + boost::split(lines, _source, boost::is_any_of("\n")); + string source = util::joinHumanReadablePrefixed(lines, "\n///") + "\n{}\n"; + + ErrorList errors; + ErrorReporter reporter(errors); + Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(EVMVersion::berlin()); + ObjectParser objectParser{reporter, dialect}; + CharStream stream(move(source), ""); + objectParser.parse(make_shared(stream), false); + return {objectParser.sourceNameMapping(), std::move(errors)}; +} + } #define CHECK_ERROR(text, typ, substring) \ @@ -162,6 +186,98 @@ BOOST_AUTO_TEST_CASE(to_string) BOOST_CHECK_EQUAL(asmStack.print(), expectation); } +BOOST_AUTO_TEST_CASE(use_src_empty) +{ + auto const [mapping, _] = tryGetSourceLocationMapping(""); + BOOST_REQUIRE(!mapping); +} + +BOOST_AUTO_TEST_CASE(use_src_simple) +{ + auto const [mapping, _] = tryGetSourceLocationMapping(R"(@use-src 0:"contract.sol")"); + BOOST_REQUIRE(mapping.has_value()); + BOOST_REQUIRE_EQUAL(mapping->size(), 1); + BOOST_REQUIRE_EQUAL(*mapping->at(0), "contract.sol"); +} + +BOOST_AUTO_TEST_CASE(use_src_multiple) +{ + auto const [mapping, _] = tryGetSourceLocationMapping(R"(@use-src 0:"contract.sol", 1:"misc.yul")"); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 2); + BOOST_REQUIRE_EQUAL(*mapping->at(0), "contract.sol"); + BOOST_REQUIRE_EQUAL(*mapping->at(1), "misc.yul"); +} + +BOOST_AUTO_TEST_CASE(use_src_escaped_filenames) +{ + auto const [mapping, _] = tryGetSourceLocationMapping( + R"(@use-src 42:"con\"tract@\".sol")" + ); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 1); + BOOST_REQUIRE(mapping->count(42)); + BOOST_REQUIRE_EQUAL(*mapping->at(42), "con\"tract@\".sol"); +} + +BOOST_AUTO_TEST_CASE(use_src_invalid_syntax_malformed_param_1) +{ + // open quote arg, missing closing quote + auto const [mapping, errors] = tryGetSourceLocationMapping(R"(@use-src 42_"con")"); + + BOOST_REQUIRE_EQUAL(errors.size(), 1); + BOOST_CHECK_EQUAL(errors.front()->errorId().error, 9804); +} + +BOOST_AUTO_TEST_CASE(use_src_invalid_syntax_malformed_param_2) +{ + // open quote arg, missing closing quote + auto const [mapping, errors] = tryGetSourceLocationMapping(R"(@use-src 42:"con)"); + + BOOST_REQUIRE_EQUAL(errors.size(), 1); + BOOST_CHECK_EQUAL(errors.front()->errorId().error, 9804); +} + +BOOST_AUTO_TEST_CASE(use_src_error_unexpected_trailing_tokens) +{ + auto const [mapping, errors] = tryGetSourceLocationMapping( + R"(@use-src 1:"file.sol" @use-src 2:"foo.sol")" + ); + + BOOST_REQUIRE_EQUAL(errors.size(), 1); + BOOST_CHECK_EQUAL(errors.front()->errorId().error, 9804); +} + +BOOST_AUTO_TEST_CASE(use_src_multiline) +{ + auto const [mapping, _] = tryGetSourceLocationMapping( + " @use-src \n 0:\"contract.sol\" \n , \n 1:\"misc.yul\"" + ); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 2); + BOOST_REQUIRE_EQUAL(*mapping->at(0), "contract.sol"); + BOOST_REQUIRE_EQUAL(*mapping->at(1), "misc.yul"); +} + +BOOST_AUTO_TEST_CASE(use_src_empty_body) +{ + auto const [mapping, _] = tryGetSourceLocationMapping("@use-src"); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 0); +} + +BOOST_AUTO_TEST_CASE(use_src_leading_text) +{ + auto const [mapping, _] = tryGetSourceLocationMapping( + "@something else @use-src 0:\"contract.sol\", 1:\"misc.sol\""s + ); + BOOST_REQUIRE(mapping); + BOOST_REQUIRE_EQUAL(mapping->size(), 2); + BOOST_REQUIRE(mapping->find(0) != mapping->end()); + BOOST_REQUIRE_EQUAL(*mapping->at(0), "contract.sol"); + BOOST_REQUIRE_EQUAL(*mapping->at(1), "misc.sol"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 6bb34064e..4a95f625e 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -55,8 +54,16 @@ shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorRep { try { - auto scanner = make_shared(CharStream(_source, "")); - auto parserResult = yul::Parser(errorReporter, _dialect).parse(scanner, false); + auto stream = CharStream(_source, ""); + map> indicesToSourceNames; + indicesToSourceNames[0] = make_shared("source0"); + indicesToSourceNames[1] = make_shared("source1"); + + auto parserResult = yul::Parser( + errorReporter, + _dialect, + move(indicesToSourceNames) + ).parse(stream); if (parserResult) { yul::AsmAnalysisInfo analysisInfo; @@ -187,7 +194,382 @@ BOOST_AUTO_TEST_CASE(default_types_set) ); } +#define CHECK_LOCATION(_actual, _sourceName, _start, _end) \ + do { \ + BOOST_CHECK_EQUAL((_sourceName), ((_actual).sourceName ? *(_actual).sourceName : "")); \ + BOOST_CHECK_EQUAL((_start), (_actual).start); \ + BOOST_CHECK_EQUAL((_end), (_actual).end); \ + } while (0) +BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{\n" + "let x:bool := true:bool\n" + "/// @src 0:123:432\n" + "let z:bool := true\n" + "let y := add(1, 2)\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + BOOST_REQUIRE_EQUAL(3, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432); + // [2] is inherited source location + CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{\n" + "let x:bool := true:bool\n" + "/// @src 1:123:432\n" + "let z:bool := true\n" + "let y := add(1, 2)\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + BOOST_REQUIRE_EQUAL(3, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 123, 432); + // [2] is inherited source location + CHECK_LOCATION(locationOf(result->statements.at(2)), "source1", 123, 432); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{\n" + "let y := add(1, 2)\n" + "/// @src 0:343:434\n" + "switch y case 0 {} default {}\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + BOOST_REQUIRE_EQUAL(2, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 343, 434); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:234:543\n" + "{\n" + "let y := add(1, 2)\n" + "/// @src 0:343:434\n" + "switch y\n" + "/// @src 0:3141:59265\n" + "case 0 {\n" + " /// @src 0:271:828\n" + " let z := add(3, 4)\n" + "}\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + + BOOST_REQUIRE_EQUAL(2, result->statements.size()); + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + auto const& switchStmt = get(result->statements.at(1)); + + CHECK_LOCATION(switchStmt.debugData->location, "source0", 343, 434); + BOOST_REQUIRE_EQUAL(1, switchStmt.cases.size()); + CHECK_LOCATION(switchStmt.cases.at(0).debugData->location, "source0", 3141, 59265); + + auto const& caseBody = switchStmt.cases.at(0).body; + BOOST_REQUIRE_EQUAL(1, caseBody.statements.size()); + CHECK_LOCATION(locationOf(caseBody.statements.at(0)), "source0", 271, 828); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "/// @src 0:1:100\n" + "{\n" + "{\n" + "/// @src 0:123:432\n" + "let x:bool := true:bool\n" + "}\n" + "let z:bool := true\n" + "let y := add(1, 2)\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + + CHECK_LOCATION(result->debugData->location, "source0", 1, 100); + + BOOST_REQUIRE_EQUAL(3, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 1, 100); + + // First child element must be a block itself with one statement. + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + BOOST_REQUIRE_EQUAL(get(result->statements.at(0)).statements.size(), 1); + CHECK_LOCATION(locationOf(get(result->statements.at(0)).statements.at(0)), "source0", 123, 432); + + // The next two elements have an inherited source location from the prior inner scope. + CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty) +{ + // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child. + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "{\n" + "/// @src 0:123:432\n" + "let a:bool\n" + "/// @src 1:1:10\n" + "a := true:bool\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); // should still parse + BOOST_REQUIRE_EQUAL(2, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432); + CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 1, 10); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index) +{ + // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child. + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "{\n" + "/// @src 1:123:432\n" + "let a:bool := true:bool\n" + "/// @src 2345:0:8\n" + "let b:bool := true:bool\n" + "\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); // should still parse +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1) +{ + // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child. + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "{\n" + "/// @src 0:123:432\n" + "let x:bool \n" + "/// @src 0:234:2026\n" + ":= true:bool\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + + BOOST_REQUIRE_EQUAL(1, result->statements.size()); + CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432); + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + CHECK_LOCATION(locationOf(*varDecl.value), "source0", 234, 2026); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:0:5 + { + let x := /// @src 1:2:3 + add(1, /// @src 0:4:8 + 2) + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE_EQUAL(1, result->statements.size()); + CHECK_LOCATION(result->debugData->location, "source0", 0, 5); + + // `let x := add(1, ` + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + CHECK_LOCATION(varDecl.debugData->location, "source0", 0, 5); + BOOST_REQUIRE(!!varDecl.value); + BOOST_REQUIRE(holds_alternative(*varDecl.value)); + FunctionCall const& call = get(*varDecl.value); + CHECK_LOCATION(call.debugData->location, "source1", 2, 3); + + // `2` + BOOST_REQUIRE_EQUAL(2, call.arguments.size()); + CHECK_LOCATION(locationOf(call.arguments.at(1)), "source0", 4, 8); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 1:23:45 + { // Block + { // Block + sstore(0, 1) // FunctionCall + /// @src 0:420:680 + } + mstore(1, 2) // FunctionCall + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE_EQUAL(2, result->statements.size()); + CHECK_LOCATION(result->debugData->location, "source1", 23, 45); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + Block const& innerBlock = get(result->statements.at(0)); + CHECK_LOCATION(innerBlock.debugData->location, "source1", 23, 45); + + BOOST_REQUIRE_EQUAL(1, innerBlock.statements.size()); + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + ExpressionStatement const& sstoreStmt = get(innerBlock.statements.at(0)); + BOOST_REQUIRE(holds_alternative(sstoreStmt.expression)); + FunctionCall const& sstoreCall = get(sstoreStmt.expression); + CHECK_LOCATION(sstoreCall.debugData->location, "source1", 23, 45); + + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + ExpressionStatement mstoreStmt = get(result->statements.at(1)); + BOOST_REQUIRE(holds_alternative(mstoreStmt.expression)); + FunctionCall const& mstoreCall = get(mstoreStmt.expression); + CHECK_LOCATION(mstoreCall.debugData->location, "source0", 420, 680); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 1:23:45 + { + /// @src 0:420:680 + /// @invalid + let a:bool := true + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE_EQUAL(1, result->statements.size()); + CHECK_LOCATION(result->debugData->location, "source1", 23, 45); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + CHECK_LOCATION(varDecl.debugData->location, "source0", 420, 680); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:420:680foo + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_unspecified) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src -1:-1:-1 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + CHECK_LOCATION(result->debugData->location, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:123:432 + { + /// @src 1:10:20 + /// @src 0:30:40 + let x:bool := true + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + + // Ensure the latest @src per documentation-comment is used (0:30:40). + CHECK_LOCATION(varDecl.debugData->location, "source0", 30, 40); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 1:2:3 + { + /// @src -1:10:20 + let x:bool := true + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = get(result->statements.at(0)); + + // -1 points to original source code, which in this case is `"source0"` (which is also + CHECK_LOCATION(varDecl.debugData->location, "", 10, 20); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index f275e38a4..6f7da9f98 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -78,7 +78,8 @@ bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool else { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, stack.errors()); + SourceReferenceFormatter{_stream, stack, true, false} + .printErrorInformation(stack.errors()); return false; } } @@ -101,11 +102,3 @@ string YulInterpreterTest::interpret() state.dumpTraceAndState(result); return result.str(); } - -void YulInterpreterTest::printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} diff --git a/test/libyul/YulInterpreterTest.h b/test/libyul/YulInterpreterTest.h index 88e535fb0..fc362ecf6 100644 --- a/test/libyul/YulInterpreterTest.h +++ b/test/libyul/YulInterpreterTest.h @@ -20,13 +20,6 @@ #include -namespace solidity::langutil -{ -class Scanner; -class Error; -using ErrorList = std::vector>; -} - namespace solidity::yul { struct AsmAnalysisInfo; @@ -52,8 +45,6 @@ private: bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); std::string interpret(); - static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); - std::shared_ptr m_ast; std::shared_ptr m_analysisInfo; }; diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 36d9cefc2..ada942152 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -27,7 +27,9 @@ #include #include +#include #include +#include #include @@ -115,16 +117,10 @@ std::pair, std::shared_ptr> YulOptimize if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, errors); + CharStream charStream(_source, ""); + SourceReferenceFormatter{_stream, SingletonCharStreamProvider(charStream), true, false} + .printErrorInformation(errors); return {}; } return {std::move(object), std::move(analysisInfo)}; } - -void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); -} diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index b4ba4ca7a..832b5dfb8 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -51,7 +51,6 @@ private: std::pair, std::shared_ptr> parse( std::ostream& _stream, std::string const& _linePrefix, bool const _formatted, std::string const& _source ); - static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); std::string m_optimizerStep; diff --git a/test/libyul/objectCompiler/datacopy.yul b/test/libyul/objectCompiler/datacopy.yul index 4499a3771..b6ec642f0 100644 --- a/test/libyul/objectCompiler/datacopy.yul +++ b/test/libyul/objectCompiler/datacopy.yul @@ -13,14 +13,15 @@ object "a" { } // ---- // Assembly: -// /* "source":26:73 */ +// /* "source":57:72 */ // dataSize(sub_0) +// /* "source":38:55 */ // dataOffset(sub_0) // /* "source":35:36 */ // 0x00 // /* "source":26:73 */ // codecopy -// /* "source":78:104 */ +// /* "source":88:103 */ // dataSize(sub_0) // /* "source":85:86 */ // 0x00 @@ -29,13 +30,13 @@ object "a" { // stop // // sub_0: assembly { -// /* "source":143:171 */ +// /* "source":153:170 */ // 0x00 // /* "source":150:151 */ // dup1 // /* "source":143:171 */ // sstore -// /* "source":178:206 */ +// /* "source":188:205 */ // 0x0d // /* "source":185:186 */ // 0x00 @@ -46,4 +47,4 @@ object "a" { // } // Bytecode: 600a600d600039600a6000f3fe60008055600d600052fe // Opcodes: PUSH1 0xA PUSH1 0xD PUSH1 0x0 CODECOPY PUSH1 0xA PUSH1 0x0 RETURN INVALID PUSH1 0x0 DUP1 SSTORE PUSH1 0xD PUSH1 0x0 MSTORE INVALID -// SourceMappings: 26:47:0:-:0;;35:1;26:47;78:26;85:1;78:26 +// SourceMappings: 57:15:0:-:0;38:17;35:1;26:47;88:15;85:1;78:26 diff --git a/test/libyul/objectCompiler/dataoffset_code.yul b/test/libyul/objectCompiler/dataoffset_code.yul index ed6242090..00184feab 100644 --- a/test/libyul/objectCompiler/dataoffset_code.yul +++ b/test/libyul/objectCompiler/dataoffset_code.yul @@ -7,7 +7,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:50 */ +// /* "source":32:49 */ // dataOffset(sub_0) // /* "source":29:30 */ // 0x00 @@ -27,4 +27,4 @@ object "a" { // } // Bytecode: 6006600055fe6008600055fe // Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID PUSH1 0x8 PUSH1 0x0 SSTORE INVALID -// SourceMappings: 22:28:0:-:0;29:1;22:28 +// SourceMappings: 32:17:0:-:0;29:1;22:28 diff --git a/test/libyul/objectCompiler/dataoffset_data.yul b/test/libyul/objectCompiler/dataoffset_data.yul index 6848ce9b9..2a029785b 100644 --- a/test/libyul/objectCompiler/dataoffset_data.yul +++ b/test/libyul/objectCompiler/dataoffset_data.yul @@ -4,7 +4,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:52 */ +// /* "source":32:51 */ // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f // /* "source":29:30 */ // 0x00 @@ -14,4 +14,4 @@ object "a" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // Bytecode: 6006600055fe48656c6c6f2c20576f726c6421 // Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID 0x48 PUSH6 0x6C6C6F2C2057 PUSH16 0x726C6421000000000000000000000000 -// SourceMappings: 22:30:0:-:0;29:1;22:30 +// SourceMappings: 32:19:0:-:0;29:1;22:30 diff --git a/test/libyul/objectCompiler/dataoffset_self.yul b/test/libyul/objectCompiler/dataoffset_self.yul index 4ee790d94..163cd7574 100644 --- a/test/libyul/objectCompiler/dataoffset_self.yul +++ b/test/libyul/objectCompiler/dataoffset_self.yul @@ -4,7 +4,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:48 */ +// /* "source":32:47 */ // 0x00 // /* "source":29:30 */ // dup1 @@ -14,4 +14,4 @@ object "a" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // Bytecode: 60008055fe // Opcodes: PUSH1 0x0 DUP1 SSTORE INVALID -// SourceMappings: 22:26:0:-:0;29:1;22:26 +// SourceMappings: 32:15:0:-:0;29:1;22:26 diff --git a/test/libyul/objectCompiler/datasize_code.yul b/test/libyul/objectCompiler/datasize_code.yul index c48b1eba9..fc26e4eb9 100644 --- a/test/libyul/objectCompiler/datasize_code.yul +++ b/test/libyul/objectCompiler/datasize_code.yul @@ -7,7 +7,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:48 */ +// /* "source":32:47 */ // dataSize(sub_0) // /* "source":29:30 */ // 0x00 @@ -27,4 +27,4 @@ object "a" { // } // Bytecode: 6006600055fe // Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID -// SourceMappings: 22:26:0:-:0;29:1;22:26 +// SourceMappings: 32:15:0:-:0;29:1;22:26 diff --git a/test/libyul/objectCompiler/datasize_data.yul b/test/libyul/objectCompiler/datasize_data.yul index 191d162df..a9ba7b027 100644 --- a/test/libyul/objectCompiler/datasize_data.yul +++ b/test/libyul/objectCompiler/datasize_data.yul @@ -4,7 +4,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:50 */ +// /* "source":32:49 */ // 0x0d // /* "source":29:30 */ // 0x00 @@ -14,4 +14,4 @@ object "a" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // Bytecode: 600d600055fe // Opcodes: PUSH1 0xD PUSH1 0x0 SSTORE INVALID -// SourceMappings: 22:28:0:-:0;29:1;22:28 +// SourceMappings: 32:17:0:-:0;29:1;22:28 diff --git a/test/libyul/objectCompiler/datasize_self.yul b/test/libyul/objectCompiler/datasize_self.yul index cfd96555a..d4a3c8d06 100644 --- a/test/libyul/objectCompiler/datasize_self.yul +++ b/test/libyul/objectCompiler/datasize_self.yul @@ -4,7 +4,7 @@ object "a" { } // ---- // Assembly: -// /* "source":22:46 */ +// /* "source":32:45 */ // bytecodeSize // /* "source":29:30 */ // 0x00 @@ -14,4 +14,4 @@ object "a" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // Bytecode: 6006600055fe // Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID -// SourceMappings: 22:24:0:-:0;29:1;22:24 +// SourceMappings: 32:13:0:-:0;29:1;22:24 diff --git a/test/libyul/objectCompiler/function_series.yul b/test/libyul/objectCompiler/function_series.yul index 9efcf77af..cf4911997 100644 --- a/test/libyul/objectCompiler/function_series.yul +++ b/test/libyul/objectCompiler/function_series.yul @@ -13,12 +13,10 @@ object "Contract" { // /* "source":33:48 */ // jump(tag_1) // tag_2: -// /* "source":46:48 */ // tag_3: // jump // out // /* "source":53:68 */ // tag_4: -// /* "source":66:68 */ // tag_5: // jump // out // tag_1: @@ -30,4 +28,4 @@ object "Contract" { // sstore // Bytecode: 6009565b5b565b5b565b6001600055 // Opcodes: PUSH1 0x9 JUMP JUMPDEST JUMPDEST JUMP JUMPDEST JUMPDEST JUMP JUMPDEST PUSH1 0x1 PUSH1 0x0 SSTORE -// SourceMappings: 33:15:0:-:0;;;46:2;:::o;53:15::-;66:2;:::o;:::-;83:1;80;73:12 +// SourceMappings: 33:15:0:-:0;;;;:::o;53:::-;;:::o;:::-;83:1;80;73:12 diff --git a/test/libyul/objectCompiler/jump_tags.yul b/test/libyul/objectCompiler/jump_tags.yul index bd3920900..b78173d18 100644 --- a/test/libyul/objectCompiler/jump_tags.yul +++ b/test/libyul/objectCompiler/jump_tags.yul @@ -21,21 +21,21 @@ object "Contract" { // tag_5 // jump // in // tag_4: -// /* "source":46:54 */ +// /* "source":33:54 */ // tag_3: // jump // out // /* "source":59:104 */ // tag_5: // /* "source":78:79 */ // dup1 -// /* "source":75:77 */ +// /* "source":75:89 */ // iszero // tag_7 // jumpi // /* "source":82:87 */ // pop // jump(tag_6) -// /* "source":75:77 */ +// /* "source":75:89 */ // tag_7: // /* "source":90:102 */ // tag_8 @@ -49,7 +49,7 @@ object "Contract" { // tag_5 // jump // in // tag_8: -// /* "source":73:104 */ +// /* "source":59:104 */ // pop // tag_6: // jump // out @@ -64,4 +64,4 @@ object "Contract" { // tag_9: // Bytecode: 6026565b600b6001600e565b5b565b8015601857506024565b602260028201600e565b505b565b602e6001600e565b // Opcodes: PUSH1 0x26 JUMP JUMPDEST PUSH1 0xB PUSH1 0x1 PUSH1 0xE JUMP JUMPDEST JUMPDEST JUMP JUMPDEST DUP1 ISZERO PUSH1 0x18 JUMPI POP PUSH1 0x24 JUMP JUMPDEST PUSH1 0x22 PUSH1 0x2 DUP3 ADD PUSH1 0xE JUMP JUMPDEST POP JUMPDEST JUMP JUMPDEST PUSH1 0x2E PUSH1 0x1 PUSH1 0xE JUMP JUMPDEST -// SourceMappings: 33:21:0:-:0;;;48:4;50:1;48:4;:::i;:::-;46:8;:::o;59:45::-;78:1;75:2;;;82:5;;;75:2;90:12;99:1;96;92:9;90:12;:::i;:::-;73:31;;:::o;:::-;109:4;111:1;109:4;:::i;:::- +// SourceMappings: 33:21:0:-:0;;;48:4;50:1;48:4;:::i;:::-;33:21;:::o;59:45::-;78:1;75:14;;;82:5;;;75:14;90:12;99:1;96;92:9;90:12;:::i;:::-;59:45;;:::o;:::-;109:4;111:1;109:4;:::i;:::- diff --git a/test/libyul/objectCompiler/linkersymbol.yul b/test/libyul/objectCompiler/linkersymbol.yul index 65bf004ef..9dac37448 100644 --- a/test/libyul/objectCompiler/linkersymbol.yul +++ b/test/libyul/objectCompiler/linkersymbol.yul @@ -7,6 +7,7 @@ object "a" { } // ---- // Assembly: +// /* "source":44:79 */ // linkerSymbol("f919ba91ac99f96129544b80b9516b27a80e376b9dc693819d0b18b7e0395612") // /* "source":109:119 */ // 0x18530aaf @@ -39,4 +40,4 @@ object "a" { // pop // Bytecode: 7300000000000000000000000000000000000000006318530aaf60e31b60805260006080600460806000855af15050 // Opcodes: PUSH20 0x0 PUSH4 0x18530AAF PUSH1 0xE3 SHL PUSH1 0x80 MSTORE PUSH1 0x0 PUSH1 0x80 PUSH1 0x4 PUSH1 0x80 PUSH1 0x0 DUP6 GAS CALL POP POP -// SourceMappings: :::-:0;109:10:0;104:3;100:20;95:3;88:33;179:1;174:3;171:1;166:3;163:1;157:4;150:5;145:36;22:165; +// SourceMappings: 44:35:0:-:0;109:10;104:3;100:20;95:3;88:33;179:1;174:3;171:1;166:3;163:1;157:4;150:5;145:36;22:165; diff --git a/test/libyul/objectCompiler/long_object_name.yul b/test/libyul/objectCompiler/long_object_name.yul index 0aa8c4405..ad8266d1e 100644 --- a/test/libyul/objectCompiler/long_object_name.yul +++ b/test/libyul/objectCompiler/long_object_name.yul @@ -10,7 +10,7 @@ object "t" { // optimizationPreset: full // ---- // Assembly: -// /* "source":23:147 */ +// /* "source":33:146 */ // dataSize(sub_0) // /* "source":30:31 */ // 0x00 @@ -22,4 +22,4 @@ object "t" { // } // Bytecode: 6000600055fe // Opcodes: PUSH1 0x0 PUSH1 0x0 SSTORE INVALID -// SourceMappings: 23:124:0:-:0;30:1;23:124 +// SourceMappings: 33:113:0:-:0;30:1;23:124 diff --git a/test/libyul/objectCompiler/metadata.yul b/test/libyul/objectCompiler/metadata.yul index 9facd54e4..ac66e4bda 100644 --- a/test/libyul/objectCompiler/metadata.yul +++ b/test/libyul/objectCompiler/metadata.yul @@ -21,8 +21,9 @@ object "A" { } // ---- // Assembly: +// /* "source":35:48 */ // 0x0e -// /* "source":26:48 */ +// /* "source":62:75 */ // 0x03 // /* "source":90:91 */ // dup2 @@ -44,6 +45,7 @@ object "A" { // data_e1629b9dda060bb30c7908346f6af189c16773fa148d3366701fbaa35d54f3c8 414243 // // sub_0: assembly { +// /* "source":157:176 */ // data_211450822d7f8c345093893187e7e1fbebc4ec67af72601920194be14104e336 // /* "source":193:194 */ // dup1 @@ -62,4 +64,4 @@ object "A" { // auxdata: 0x4d32 // Bytecode: 600e600381600055806020555050fe4d32 // Opcodes: PUSH1 0xE PUSH1 0x3 DUP2 PUSH1 0x0 SSTORE DUP1 PUSH1 0x20 SSTORE POP POP INVALID 0x4D ORIGIN -// SourceMappings: :::-:0;26:22:0;90:1;87;80:12;108:1;104:2;97:13;20:94; +// SourceMappings: 35:13:0:-:0;62;90:1;87;80:12;108:1;104:2;97:13;20:94; diff --git a/test/libyul/objectCompiler/sourceLocations.yul b/test/libyul/objectCompiler/sourceLocations.yul new file mode 100644 index 000000000..f98f74f77 --- /dev/null +++ b/test/libyul/objectCompiler/sourceLocations.yul @@ -0,0 +1,53 @@ +// something else +/*-- another unrelated comment --*/ +/// @use-src 3: "abc.sol" , 2: "def.sol" +object "a" { + code { + /// @src 3:0:2 + datacopy(0, dataoffset("sub"), datasize("sub")) + return(0, + /** @src 2:5:6 */ + datasize("sub") + ) + } + object "sub" { + code { + /// @src 2:70:72 + sstore(0, dataoffset("sub")) + /** + * @something else + * @src 3:2:5 + */ + mstore( + 0, + datasize("data1") + /// @src 3:90:2 + ) + } + data "data1" "Hello, World!" + } +} +// ---- +// Assembly: +// /* "abc.sol":0:2 */ +// codecopy(0x00, dataOffset(sub_0), dataSize(sub_0)) +// /* "def.sol":5:6 */ +// dataSize(sub_0) +// /* "abc.sol":0:2 */ +// 0x00 +// return +// stop +// +// sub_0: assembly { +// /* "def.sol":70:72 */ +// 0x00 +// dup1 +// sstore +// /* "abc.sol":2:5 */ +// mstore(0x00, 0x0d) +// stop +// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 +// } +// Bytecode: 600a600d600039600a6000f3fe60008055600d600052fe +// Opcodes: PUSH1 0xA PUSH1 0xD PUSH1 0x0 CODECOPY PUSH1 0xA PUSH1 0x0 RETURN INVALID PUSH1 0x0 DUP1 SSTORE PUSH1 0xD PUSH1 0x0 MSTORE INVALID +// SourceMappings: 0:2::-:0;;;;5:1;0:2; diff --git a/test/libyul/objectCompiler/subObjectAccess.yul b/test/libyul/objectCompiler/subObjectAccess.yul index cadb6cc41..1e28ed5d5 100644 --- a/test/libyul/objectCompiler/subObjectAccess.yul +++ b/test/libyul/objectCompiler/subObjectAccess.yul @@ -67,24 +67,25 @@ object "A" { } // ---- // Assembly: +// /* "source":37:52 */ // 0x00 -// /* "source":26:52 */ +// /* "source":68:81 */ // bytecodeSize -// /* "source":57:81 */ +// /* "source":97:112 */ // dataOffset(sub_0) -// /* "source":86:112 */ +// /* "source":128:141 */ // dataSize(sub_0) -// /* "source":117:141 */ +// /* "source":158:175 */ // dataOffset(sub_0.sub_0) -// /* "source":146:175 */ +// /* "source":192:207 */ // dataSize(sub_0.sub_0) -// /* "source":180:207 */ +// /* "source":224:241 */ // dataOffset(sub_0.sub_1) -// /* "source":212:241 */ +// /* "source":258:273 */ // dataSize(sub_0.sub_1) -// /* "source":246:273 */ +// /* "source":291:310 */ // dataOffset(sub_0.sub_0.sub_0) -// /* "source":278:310 */ +// /* "source":328:345 */ // dataSize(sub_0.sub_0.sub_0) // /* "source":361:364 */ // dup10 @@ -156,16 +157,17 @@ object "A" { // data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 // // sub_0: assembly { +// /* "source":659:674 */ // dataOffset(sub_0) -// /* "source":648:674 */ +// /* "source":692:705 */ // dataSize(sub_0) -// /* "source":681:705 */ +// /* "source":723:738 */ // dataOffset(sub_1) -// /* "source":712:738 */ +// /* "source":756:769 */ // dataSize(sub_1) -// /* "source":745:769 */ +// /* "source":788:805 */ // dataOffset(sub_0.sub_0) -// /* "source":776:805 */ +// /* "source":824:839 */ // dataSize(sub_0.sub_0) // /* "source":857:860 */ // dup6 @@ -212,8 +214,9 @@ object "A" { // stop // // sub_0: assembly { +// /* "source":1052:1067 */ // dataOffset(sub_0) -// /* "source":1041:1067 */ +// /* "source":1087:1100 */ // dataSize(sub_0) // /* "source":1120:1123 */ // dup2 @@ -248,4 +251,4 @@ object "A" { // } // Bytecode: 600060996045603f60866013608560016084600189600055886020558760405586606055856080558460a0558360c0558260e055816101005580610120556101406000f3fe602a6013603d6001603e600185600055846020558360405582606055816080558060a05560c06000f3fe60126001816000558060205560406000f3fefefefefefe60126001816000558060205560406000f3fefe // Opcodes: PUSH1 0x0 PUSH1 0x99 PUSH1 0x45 PUSH1 0x3F PUSH1 0x86 PUSH1 0x13 PUSH1 0x85 PUSH1 0x1 PUSH1 0x84 PUSH1 0x1 DUP10 PUSH1 0x0 SSTORE DUP9 PUSH1 0x20 SSTORE DUP8 PUSH1 0x40 SSTORE DUP7 PUSH1 0x60 SSTORE DUP6 PUSH1 0x80 SSTORE DUP5 PUSH1 0xA0 SSTORE DUP4 PUSH1 0xC0 SSTORE DUP3 PUSH1 0xE0 SSTORE DUP2 PUSH2 0x100 SSTORE DUP1 PUSH2 0x120 SSTORE PUSH2 0x140 PUSH1 0x0 RETURN INVALID PUSH1 0x2A PUSH1 0x13 PUSH1 0x3D PUSH1 0x1 PUSH1 0x3E PUSH1 0x1 DUP6 PUSH1 0x0 SSTORE DUP5 PUSH1 0x20 SSTORE DUP4 PUSH1 0x40 SSTORE DUP3 PUSH1 0x60 SSTORE DUP2 PUSH1 0x80 SSTORE DUP1 PUSH1 0xA0 SSTORE PUSH1 0xC0 PUSH1 0x0 RETURN INVALID PUSH1 0x12 PUSH1 0x1 DUP2 PUSH1 0x0 SSTORE DUP1 PUSH1 0x20 SSTORE PUSH1 0x40 PUSH1 0x0 RETURN INVALID INVALID INVALID INVALID INVALID INVALID PUSH1 0x12 PUSH1 0x1 DUP2 PUSH1 0x0 SSTORE DUP1 PUSH1 0x20 SSTORE PUSH1 0x40 PUSH1 0x0 RETURN INVALID INVALID -// SourceMappings: :::-:0;26:26:0;57:24;86:26;117:24;146:29;180:27;212:29;246:27;278:32;361:3;358:1;351:14;381:3;377:2;370:15;401:3;397:2;390:15;421:3;417:2;410:15;442:4;437:3;430:17;464:4;459:3;452:17;486:4;481:3;474:17;508:4;503:3;496:17;530:5;525:3;518:18;553:5;548:3;541:18;574:3;571:1;564:14 +// SourceMappings: 37:15:0:-:0;68:13;97:15;128:13;158:17;192:15;224:17;258:15;291:19;328:17;361:3;358:1;351:14;381:3;377:2;370:15;401:3;397:2;390:15;421:3;417:2;410:15;442:4;437:3;430:17;464:4;459:3;452:17;486:4;481:3;474:17;508:4;503:3;496:17;530:5;525:3;518:18;553:5;548:3;541:18;574:3;571:1;564:14 diff --git a/test/libyul/yulControlFlowGraph/ambiguous_names.yul b/test/libyul/yulControlFlowGraph/ambiguous_names.yul new file mode 100644 index 000000000..fabeda45b --- /dev/null +++ b/test/libyul/yulControlFlowGraph/ambiguous_names.yul @@ -0,0 +1,65 @@ +{ + a() + c() + function a() { + let x := 42 + sstore(x,x) + b() + function b() {} + } + function c() { + let x := 21 + mstore(x,x) + b() + function b() {} + } +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// a: [ RET[a] ] => [ ]\l\ +// c: [ RET[c] ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_a_1 [label="function a()"]; +// FunctionEntry_a_1 -> Block1; +// Block1 [label="\ +// Assignment(x): [ 0x2a ] => [ x ]\l\ +// sstore: [ x x ] => [ ]\l\ +// b: [ RET[b] ] => [ ]\l\ +// "]; +// Block1Exit [label="FunctionReturn[a]"]; +// Block1 -> Block1Exit; +// +// FunctionEntry_b_2 [label="function b()"]; +// FunctionEntry_b_2 -> Block2; +// Block2 [label="\ +// "]; +// Block2Exit [label="FunctionReturn[b]"]; +// Block2 -> Block2Exit; +// +// FunctionEntry_c_3 [label="function c()"]; +// FunctionEntry_c_3 -> Block3; +// Block3 [label="\ +// Assignment(x): [ 0x15 ] => [ x ]\l\ +// mstore: [ x x ] => [ ]\l\ +// b: [ RET[b] ] => [ ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[c]"]; +// Block3 -> Block3Exit; +// +// FunctionEntry_b_4 [label="function b()"]; +// FunctionEntry_b_4 -> Block4; +// Block4 [label="\ +// "]; +// Block4Exit [label="FunctionReturn[b]"]; +// Block4 -> Block4Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/break.yul b/test/libyul/yulControlFlowGraph/break.yul new file mode 100644 index 000000000..ceca7cce0 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/break.yul @@ -0,0 +1,87 @@ +{ + sstore(0x01, 0x0101) + for { sstore(0x02, 0x0202) } sload(0x03) { sstore(0x04, 0x0404) } { + sstore(0x05, 0x0505) + if sload(0x06) { sstore(0x07,0x0707) break } + sstore(0x08, 0x0808) + if sload(0x09) { sstore(0x0A,0x0A0A) continue } + sstore(0x0B, 0x0B0B) + } + sstore(0x0C, 0x0C0C) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block0 -> Block0Exit [arrowhead=none]; +// Block0Exit [label="Jump" shape=oval]; +// Block0Exit -> Block1; +// +// Block1 [label="\ +// sload: [ 0x03 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// sstore: [ 0x0c0c 0x0c ] => [ ]\l\ +// "]; +// Block2Exit [label="MainExit"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// sstore: [ 0x0505 0x05 ] => [ ]\l\ +// sload: [ 0x06 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block3 -> Block3Exit; +// Block3Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block3Exit:0 -> Block4; +// Block3Exit:1 -> Block5; +// +// Block4 [label="\ +// sstore: [ 0x0808 0x08 ] => [ ]\l\ +// sload: [ 0x09 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block4 -> Block4Exit; +// Block4Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block4Exit:0 -> Block6; +// Block4Exit:1 -> Block7; +// +// Block5 [label="\ +// sstore: [ 0x0707 0x07 ] => [ ]\l\ +// "]; +// Block5 -> Block5Exit [arrowhead=none]; +// Block5Exit [label="Jump" shape=oval]; +// Block5Exit -> Block2; +// +// Block6 [label="\ +// sstore: [ 0x0b0b 0x0b ] => [ ]\l\ +// "]; +// Block6 -> Block6Exit [arrowhead=none]; +// Block6Exit [label="Jump" shape=oval]; +// Block6Exit -> Block8; +// +// Block7 [label="\ +// sstore: [ 0x0a0a 0x0a ] => [ ]\l\ +// "]; +// Block7 -> Block7Exit [arrowhead=none]; +// Block7Exit [label="Jump" shape=oval]; +// Block7Exit -> Block8; +// +// Block8 [label="\ +// sstore: [ 0x0404 0x04 ] => [ ]\l\ +// "]; +// Block8 -> Block8Exit [arrowhead=none]; +// Block8Exit [label="BackwardsJump" shape=oval]; +// Block8Exit -> Block1; +// +// } diff --git a/test/libyul/yulControlFlowGraph/complex.yul b/test/libyul/yulControlFlowGraph/complex.yul new file mode 100644 index 000000000..4001a9309 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/complex.yul @@ -0,0 +1,195 @@ +{ + function f(a, b) -> c { + for { let x := 42 } lt(x, a) { + x := add(x, 1) + if calldataload(x) + { + sstore(0, x) + leave + sstore(0x01, 0x0101) + } + sstore(0xFF, 0xFFFF) + } + { + switch mload(x) + case 0 { + sstore(0x02, 0x0202) + break + sstore(0x03, 0x0303) + } + case 1 { + sstore(0x04, 0x0404) + leave + sstore(0x05, 0x0505) + } + case 2 { + sstore(0x06, 0x0606) + revert(0, 0) + sstore(0x07, 0x0707) + } + case 3 { + sstore(0x08, 0x0808) + } + default { + if mload(b) { + return(0, 0) + sstore(0x09, 0x0909) + } + sstore(0x0A, 0x0A0A) + } + sstore(0x0B, 0x0B0B) + } + sstore(0x0C, 0x0C0C) + } + pop(f(1,2)) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// f: [ RET[f] 0x02 0x01 ] => [ TMP[f, 0] ]\l\ +// pop: [ TMP[f, 0] ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_f_1 [label="function f(a, b) -> c"]; +// FunctionEntry_f_1 -> Block1; +// Block1 [label="\ +// Assignment(x): [ 0x2a ] => [ x ]\l\ +// "]; +// Block1 -> Block1Exit [arrowhead=none]; +// Block1Exit [label="Jump" shape=oval]; +// Block1Exit -> Block2; +// +// Block2 [label="\ +// lt: [ a x ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block2 -> Block2Exit; +// Block2Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block2Exit:0 -> Block3; +// Block2Exit:1 -> Block4; +// +// Block3 [label="\ +// sstore: [ 0x0c0c 0x0c ] => [ ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[f]"]; +// Block3 -> Block3Exit; +// +// Block4 [label="\ +// mload: [ x ] => [ TMP[mload, 0] ]\l\ +// Assignment(GHOST[0]): [ TMP[mload, 0] ] => [ GHOST[0] ]\l\ +// eq: [ GHOST[0] 0x00 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block4 -> Block4Exit; +// Block4Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block4Exit:0 -> Block5; +// Block4Exit:1 -> Block6; +// +// Block5 [label="\ +// eq: [ GHOST[0] 0x01 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block5 -> Block5Exit; +// Block5Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block5Exit:0 -> Block7; +// Block5Exit:1 -> Block8; +// +// Block6 [label="\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block6 -> Block6Exit [arrowhead=none]; +// Block6Exit [label="Jump" shape=oval]; +// Block6Exit -> Block3; +// +// Block7 [label="\ +// eq: [ GHOST[0] 0x02 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block7 -> Block7Exit; +// Block7Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block7Exit:0 -> Block9; +// Block7Exit:1 -> Block10; +// +// Block8 [label="\ +// sstore: [ 0x0404 0x04 ] => [ ]\l\ +// "]; +// Block8Exit [label="FunctionReturn[f]"]; +// Block8 -> Block8Exit; +// +// Block9 [label="\ +// eq: [ GHOST[0] 0x03 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block9 -> Block9Exit; +// Block9Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block9Exit:0 -> Block11; +// Block9Exit:1 -> Block12; +// +// Block10 [label="\ +// sstore: [ 0x0606 0x06 ] => [ ]\l\ +// revert: [ 0x00 0x00 ] => [ ]\l\ +// "]; +// Block10Exit [label="Terminated"]; +// Block10 -> Block10Exit; +// +// Block11 [label="\ +// mload: [ b ] => [ TMP[mload, 0] ]\l\ +// "]; +// Block11 -> Block11Exit; +// Block11Exit [label="{ TMP[mload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block11Exit:0 -> Block13; +// Block11Exit:1 -> Block14; +// +// Block12 [label="\ +// sstore: [ 0x0808 0x08 ] => [ ]\l\ +// "]; +// Block12 -> Block12Exit [arrowhead=none]; +// Block12Exit [label="Jump" shape=oval]; +// Block12Exit -> Block15; +// +// Block13 [label="\ +// sstore: [ 0x0a0a 0x0a ] => [ ]\l\ +// "]; +// Block13 -> Block13Exit [arrowhead=none]; +// Block13Exit [label="Jump" shape=oval]; +// Block13Exit -> Block15; +// +// Block14 [label="\ +// return: [ 0x00 0x00 ] => [ ]\l\ +// "]; +// Block14Exit [label="Terminated"]; +// Block14 -> Block14Exit; +// +// Block15 [label="\ +// sstore: [ 0x0b0b 0x0b ] => [ ]\l\ +// "]; +// Block15 -> Block15Exit [arrowhead=none]; +// Block15Exit [label="Jump" shape=oval]; +// Block15Exit -> Block16; +// +// Block16 [label="\ +// add: [ 0x01 x ] => [ TMP[add, 0] ]\l\ +// Assignment(x): [ TMP[add, 0] ] => [ x ]\l\ +// calldataload: [ x ] => [ TMP[calldataload, 0] ]\l\ +// "]; +// Block16 -> Block16Exit; +// Block16Exit [label="{ TMP[calldataload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block16Exit:0 -> Block17; +// Block16Exit:1 -> Block18; +// +// Block17 [label="\ +// sstore: [ 0xffff 0xff ] => [ ]\l\ +// "]; +// Block17 -> Block17Exit [arrowhead=none]; +// Block17Exit [label="BackwardsJump" shape=oval]; +// Block17Exit -> Block2; +// +// Block18 [label="\ +// sstore: [ x 0x00 ] => [ ]\l\ +// "]; +// Block18Exit [label="FunctionReturn[f]"]; +// Block18 -> Block18Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/for.yul b/test/libyul/yulControlFlowGraph/for.yul new file mode 100644 index 000000000..52658a128 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/for.yul @@ -0,0 +1,51 @@ +{ + sstore(0x01, 0x0101) + for { sstore(0x02, 0x0202) } sload(0x03) { sstore(0x04, 0x0404) } { + sstore(0x05, 0x0505) + } + sstore(0x06, 0x0506) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block0 -> Block0Exit [arrowhead=none]; +// Block0Exit [label="Jump" shape=oval]; +// Block0Exit -> Block1; +// +// Block1 [label="\ +// sload: [ 0x03 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// sstore: [ 0x0506 0x06 ] => [ ]\l\ +// "]; +// Block2Exit [label="MainExit"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// sstore: [ 0x0505 0x05 ] => [ ]\l\ +// "]; +// Block3 -> Block3Exit [arrowhead=none]; +// Block3Exit [label="Jump" shape=oval]; +// Block3Exit -> Block4; +// +// Block4 [label="\ +// sstore: [ 0x0404 0x04 ] => [ ]\l\ +// "]; +// Block4 -> Block4Exit [arrowhead=none]; +// Block4Exit [label="BackwardsJump" shape=oval]; +// Block4Exit -> Block1; +// +// } diff --git a/test/libyul/yulControlFlowGraph/function.yul b/test/libyul/yulControlFlowGraph/function.yul new file mode 100644 index 000000000..862b1b9e6 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/function.yul @@ -0,0 +1,75 @@ +{ + function f(a, b) -> r { + let x := add(a,b) + r := sub(x,a) + } + function g() { + sstore(0x01, 0x0101) + } + function h(x) { + h(f(x, 0)) + g() + } + function i() -> v, w { + v := 0x0202 + w := 0x0303 + } + let x, y := i() + h(x) + h(y) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// i: [ RET[i] ] => [ TMP[i, 0] TMP[i, 1] ]\l\ +// Assignment(x, y): [ TMP[i, 0] TMP[i, 1] ] => [ x y ]\l\ +// h: [ RET[h] x ] => [ ]\l\ +// h: [ RET[h] y ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_f_1 [label="function f(a, b) -> r"]; +// FunctionEntry_f_1 -> Block1; +// Block1 [label="\ +// add: [ b a ] => [ TMP[add, 0] ]\l\ +// Assignment(x): [ TMP[add, 0] ] => [ x ]\l\ +// sub: [ a x ] => [ TMP[sub, 0] ]\l\ +// Assignment(r): [ TMP[sub, 0] ] => [ r ]\l\ +// "]; +// Block1Exit [label="FunctionReturn[f]"]; +// Block1 -> Block1Exit; +// +// FunctionEntry_g_2 [label="function g()"]; +// FunctionEntry_g_2 -> Block2; +// Block2 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// "]; +// Block2Exit [label="FunctionReturn[g]"]; +// Block2 -> Block2Exit; +// +// FunctionEntry_h_3 [label="function h(x)"]; +// FunctionEntry_h_3 -> Block3; +// Block3 [label="\ +// f: [ RET[f] 0x00 x ] => [ TMP[f, 0] ]\l\ +// h: [ RET[h] TMP[f, 0] ] => [ ]\l\ +// g: [ RET[g] ] => [ ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[h]"]; +// Block3 -> Block3Exit; +// +// FunctionEntry_i_4 [label="function i() -> v, w"]; +// FunctionEntry_i_4 -> Block4; +// Block4 [label="\ +// Assignment(v): [ 0x0202 ] => [ v ]\l\ +// Assignment(w): [ 0x0303 ] => [ w ]\l\ +// "]; +// Block4Exit [label="FunctionReturn[i]"]; +// Block4 -> Block4Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/if.yul b/test/libyul/yulControlFlowGraph/if.yul new file mode 100644 index 000000000..a313c53ee --- /dev/null +++ b/test/libyul/yulControlFlowGraph/if.yul @@ -0,0 +1,37 @@ +{ + sstore(0x01, 0x0101) + if calldataload(0) { + sstore(0x02, 0x0202) + } + sstore(0x03, 0x003) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// calldataload: [ 0x00 ] => [ TMP[calldataload, 0] ]\l\ +// "]; +// Block0 -> Block0Exit; +// Block0Exit [label="{ TMP[calldataload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0Exit:0 -> Block1; +// Block0Exit:1 -> Block2; +// +// Block1 [label="\ +// sstore: [ 0x03 0x03 ] => [ ]\l\ +// "]; +// Block1Exit [label="MainExit"]; +// Block1 -> Block1Exit; +// +// Block2 [label="\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block2 -> Block2Exit [arrowhead=none]; +// Block2Exit [label="Jump" shape=oval]; +// Block2Exit -> Block1; +// +// } diff --git a/test/libyul/yulControlFlowGraph/leave.yul b/test/libyul/yulControlFlowGraph/leave.yul new file mode 100644 index 000000000..4d0a63af4 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/leave.yul @@ -0,0 +1,51 @@ +{ + function f(a, b) -> c { + sstore(0x01, 0x0101) + if lt(a,b) { + sstore(0x02, 0x0202) + leave + sstore(0x03, 0x0303) + } + sstore(0x04, 0x0404) + } + + pop(f(0,1)) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// f: [ RET[f] 0x01 0x00 ] => [ TMP[f, 0] ]\l\ +// pop: [ TMP[f, 0] ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_f_1 [label="function f(a, b) -> c"]; +// FunctionEntry_f_1 -> Block1; +// Block1 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// lt: [ b a ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// sstore: [ 0x0404 0x04 ] => [ ]\l\ +// "]; +// Block2Exit [label="FunctionReturn[f]"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// sstore: [ 0x0202 0x02 ] => [ ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[f]"]; +// Block3 -> Block3Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/nested_loop_complex.yul b/test/libyul/yulControlFlowGraph/nested_loop_complex.yul new file mode 100644 index 000000000..ea9aedf76 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/nested_loop_complex.yul @@ -0,0 +1,193 @@ +{ + for { let x := 0 } lt(x, 0x0101) { + sstore(x, 0x0202) + for { let y := 0 } lt(y, 0x0303) { y := add(y, 0x0404) } { + sstore(y, 0x0505) + } + x := add(x, 0x0202) + } + { + sstore(0x0606, 0x0606) + if sload(0x0707) { continue } + sstore(0x0808, 0x0808) + if sload(0x0909) { break } + sstore(0x0A0A, 0x0B0B) + for { let z := 0 } lt(z, 0x0C0C) { z := add(z, 1) } { + sstore(0x0D0D, 0x0D0D) + if sload(0x0E0E) { + continue + } + sstore(0x0F0F, 0x0F0F) + if sload(0x1010) { + break + } + sstore(0x1111, 0x1111) + } + sstore(0x1212, 0x1212) + } +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// Assignment(x): [ 0x00 ] => [ x ]\l\ +// "]; +// Block0 -> Block0Exit [arrowhead=none]; +// Block0Exit [label="Jump" shape=oval]; +// Block0Exit -> Block1; +// +// Block1 [label="\ +// lt: [ 0x0101 x ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// "]; +// Block2Exit [label="MainExit"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// sstore: [ 0x0606 0x0606 ] => [ ]\l\ +// sload: [ 0x0707 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block3 -> Block3Exit; +// Block3Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block3Exit:0 -> Block4; +// Block3Exit:1 -> Block5; +// +// Block4 [label="\ +// sstore: [ 0x0808 0x0808 ] => [ ]\l\ +// sload: [ 0x0909 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block4 -> Block4Exit; +// Block4Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block4Exit:0 -> Block6; +// Block4Exit:1 -> Block7; +// +// Block5 [label="\ +// "]; +// Block5 -> Block5Exit [arrowhead=none]; +// Block5Exit [label="Jump" shape=oval]; +// Block5Exit -> Block8; +// +// Block6 [label="\ +// sstore: [ 0x0b0b 0x0a0a ] => [ ]\l\ +// Assignment(z): [ 0x00 ] => [ z ]\l\ +// "]; +// Block6 -> Block6Exit [arrowhead=none]; +// Block6Exit [label="Jump" shape=oval]; +// Block6Exit -> Block9; +// +// Block7 [label="\ +// "]; +// Block7 -> Block7Exit [arrowhead=none]; +// Block7Exit [label="Jump" shape=oval]; +// Block7Exit -> Block2; +// +// Block8 [label="\ +// sstore: [ 0x0202 x ] => [ ]\l\ +// Assignment(y): [ 0x00 ] => [ y ]\l\ +// "]; +// Block8 -> Block8Exit [arrowhead=none]; +// Block8Exit [label="Jump" shape=oval]; +// Block8Exit -> Block10; +// +// Block9 [label="\ +// lt: [ 0x0c0c z ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block9 -> Block9Exit; +// Block9Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block9Exit:0 -> Block11; +// Block9Exit:1 -> Block12; +// +// Block10 [label="\ +// lt: [ 0x0303 y ] => [ TMP[lt, 0] ]\l\ +// "]; +// Block10 -> Block10Exit; +// Block10Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block10Exit:0 -> Block13; +// Block10Exit:1 -> Block14; +// +// Block11 [label="\ +// sstore: [ 0x1212 0x1212 ] => [ ]\l\ +// "]; +// Block11 -> Block11Exit [arrowhead=none]; +// Block11Exit [label="Jump" shape=oval]; +// Block11Exit -> Block8; +// +// Block12 [label="\ +// sstore: [ 0x0d0d 0x0d0d ] => [ ]\l\ +// sload: [ 0x0e0e ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block12 -> Block12Exit; +// Block12Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block12Exit:0 -> Block15; +// Block12Exit:1 -> Block16; +// +// Block13 [label="\ +// add: [ 0x0202 x ] => [ TMP[add, 0] ]\l\ +// Assignment(x): [ TMP[add, 0] ] => [ x ]\l\ +// "]; +// Block13 -> Block13Exit [arrowhead=none]; +// Block13Exit [label="BackwardsJump" shape=oval]; +// Block13Exit -> Block1; +// +// Block14 [label="\ +// sstore: [ 0x0505 y ] => [ ]\l\ +// "]; +// Block14 -> Block14Exit [arrowhead=none]; +// Block14Exit [label="Jump" shape=oval]; +// Block14Exit -> Block17; +// +// Block15 [label="\ +// sstore: [ 0x0f0f 0x0f0f ] => [ ]\l\ +// sload: [ 0x1010 ] => [ TMP[sload, 0] ]\l\ +// "]; +// Block15 -> Block15Exit; +// Block15Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block15Exit:0 -> Block18; +// Block15Exit:1 -> Block19; +// +// Block16 [label="\ +// "]; +// Block16 -> Block16Exit [arrowhead=none]; +// Block16Exit [label="Jump" shape=oval]; +// Block16Exit -> Block20; +// +// Block17 [label="\ +// add: [ 0x0404 y ] => [ TMP[add, 0] ]\l\ +// Assignment(y): [ TMP[add, 0] ] => [ y ]\l\ +// "]; +// Block17 -> Block17Exit [arrowhead=none]; +// Block17Exit [label="BackwardsJump" shape=oval]; +// Block17Exit -> Block10; +// +// Block18 [label="\ +// sstore: [ 0x1111 0x1111 ] => [ ]\l\ +// "]; +// Block18 -> Block18Exit [arrowhead=none]; +// Block18Exit [label="Jump" shape=oval]; +// Block18Exit -> Block20; +// +// Block19 [label="\ +// "]; +// Block19 -> Block19Exit [arrowhead=none]; +// Block19Exit [label="Jump" shape=oval]; +// Block19Exit -> Block11; +// +// Block20 [label="\ +// add: [ 0x01 z ] => [ TMP[add, 0] ]\l\ +// Assignment(z): [ TMP[add, 0] ] => [ z ]\l\ +// "]; +// Block20 -> Block20Exit [arrowhead=none]; +// Block20Exit [label="BackwardsJump" shape=oval]; +// Block20Exit -> Block9; +// +// } diff --git a/test/libyul/yulControlFlowGraph/stub.yul b/test/libyul/yulControlFlowGraph/stub.yul new file mode 100644 index 000000000..4ec901619 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/stub.yul @@ -0,0 +1,15 @@ +{ +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/switch.yul b/test/libyul/yulControlFlowGraph/switch.yul new file mode 100644 index 000000000..2d6751d8a --- /dev/null +++ b/test/libyul/yulControlFlowGraph/switch.yul @@ -0,0 +1,68 @@ +{ + sstore(0, 0) + switch sload(0) + case 0 { + sstore(0x01, 0x0101) + } + case 1 { + sstore(0x02, 0x0101) + } + default { + sstore(0x03, 0x0101) + } + sstore(0x04, 0x0101) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// sstore: [ 0x00 0x00 ] => [ ]\l\ +// sload: [ 0x00 ] => [ TMP[sload, 0] ]\l\ +// Assignment(GHOST[0]): [ TMP[sload, 0] ] => [ GHOST[0] ]\l\ +// eq: [ GHOST[0] 0x00 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block0 -> Block0Exit; +// Block0Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0Exit:0 -> Block1; +// Block0Exit:1 -> Block2; +// +// Block1 [label="\ +// eq: [ GHOST[0] 0x01 ] => [ TMP[eq, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block3; +// Block1Exit:1 -> Block4; +// +// Block2 [label="\ +// sstore: [ 0x0101 0x01 ] => [ ]\l\ +// "]; +// Block2 -> Block2Exit [arrowhead=none]; +// Block2Exit [label="Jump" shape=oval]; +// Block2Exit -> Block5; +// +// Block3 [label="\ +// sstore: [ 0x0101 0x03 ] => [ ]\l\ +// "]; +// Block3 -> Block3Exit [arrowhead=none]; +// Block3Exit [label="Jump" shape=oval]; +// Block3Exit -> Block5; +// +// Block4 [label="\ +// sstore: [ 0x0101 0x02 ] => [ ]\l\ +// "]; +// Block4 -> Block4Exit [arrowhead=none]; +// Block4Exit [label="Jump" shape=oval]; +// Block4Exit -> Block5; +// +// Block5 [label="\ +// sstore: [ 0x0101 0x04 ] => [ ]\l\ +// "]; +// Block5Exit [label="MainExit"]; +// Block5 -> Block5Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/variables.yul b/test/libyul/yulControlFlowGraph/variables.yul new file mode 100644 index 000000000..ecd274054 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/variables.yul @@ -0,0 +1,31 @@ +{ + let x := calldataload(0) + let y := calldataload(2) + + x := calldataload(3) + y := calldataload(4) + + sstore(x,y) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// calldataload: [ 0x00 ] => [ TMP[calldataload, 0] ]\l\ +// Assignment(x): [ TMP[calldataload, 0] ] => [ x ]\l\ +// calldataload: [ 0x02 ] => [ TMP[calldataload, 0] ]\l\ +// Assignment(y): [ TMP[calldataload, 0] ] => [ y ]\l\ +// calldataload: [ 0x03 ] => [ TMP[calldataload, 0] ]\l\ +// Assignment(x): [ TMP[calldataload, 0] ] => [ x ]\l\ +// calldataload: [ 0x04 ] => [ TMP[calldataload, 0] ]\l\ +// Assignment(y): [ TMP[calldataload, 0] ] => [ y ]\l\ +// sstore: [ y x ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// } diff --git a/test/libyul/yulControlFlowGraph/verbatim.yul b/test/libyul/yulControlFlowGraph/verbatim.yul new file mode 100644 index 000000000..05a4cc034 --- /dev/null +++ b/test/libyul/yulControlFlowGraph/verbatim.yul @@ -0,0 +1,26 @@ +{ + let a_1 := 42 + let a_2 := 23 + let a_3 := 1 + let b := verbatim_10i_1o("test", a_1, a_2, a_3, 2, 3, 4, 5, 6, 7, 8) + sstore(b,b) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// Assignment(a_1): [ 0x2a ] => [ a_1 ]\l\ +// Assignment(a_2): [ 0x17 ] => [ a_2 ]\l\ +// Assignment(a_3): [ 0x01 ] => [ a_3 ]\l\ +// verbatim_10i_1o: [ 0x08 0x07 0x06 0x05 0x04 0x03 0x02 a_3 a_2 a_1 ] => [ TMP[verbatim_10i_1o, 0] ]\l\ +// Assignment(b): [ TMP[verbatim_10i_1o, 0] ] => [ b ]\l\ +// sstore: [ b b ] => [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// } diff --git a/test/scripts/fixtures/code_block.rst b/test/scripts/fixtures/code_block.rst index f97967b93..a0bb7a91e 100644 --- a/test/scripts/fixtures/code_block.rst +++ b/test/scripts/fixtures/code_block.rst @@ -20,3 +20,25 @@ Some text contract C {} More text. + +.. code-block:: yul + + let x := add(1, 5) + +.. code-block:: yul + + // Yul code wrapped in object + { + { + let y := mul(3, 5) + } + } + +.. code-block:: yul + // Yul code wrapped in named object + object "Test" { + { + let y := mul(6, 9) + } + } + diff --git a/test/scripts/fixtures/code_block_with_directives.rst b/test/scripts/fixtures/code_block_with_directives.rst index ff52d5b0e..fba8ed7dd 100644 --- a/test/scripts/fixtures/code_block_with_directives.rst +++ b/test/scripts/fixtures/code_block_with_directives.rst @@ -46,3 +46,26 @@ Sphinx does not complain about these. contract E {} More text. + +.. code-block:: yul + + :force: + let x := add(1, 5) + +.. code-block:: yul + + :linenos: + :language: Yul + // Yul code wrapped in object + { + let y := mul(3, 5) + } + +.. code-block:: yul + + // Yul code wrapped in named object + object "Test" { + let y := mul(3, 5) + :linenos: + } + diff --git a/test/scripts/test_isolate_tests.py b/test/scripts/test_isolate_tests.py index 15c375a82..16874b747 100644 --- a/test/scripts/test_isolate_tests.py +++ b/test/scripts/test_isolate_tests.py @@ -2,61 +2,119 @@ import unittest +from textwrap import dedent, indent + from unittest_helpers import FIXTURE_DIR, load_fixture # NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports # pragma pylint: disable=import-error -from isolate_tests import extract_docs_cases +from isolate_tests import extract_solidity_docs_cases, extract_yul_docs_cases # pragma pylint: enable=import-error - CODE_BLOCK_RST_PATH = FIXTURE_DIR / 'code_block.rst' CODE_BLOCK_RST_CONTENT = load_fixture(CODE_BLOCK_RST_PATH) CODE_BLOCK_WITH_DIRECTIVES_RST_PATH = FIXTURE_DIR / 'code_block_with_directives.rst' CODE_BLOCK_WITH_DIRECTIVES_RST_CONTENT = load_fixture(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH) +def formatCase(text): + """Formats code to contain only one indentation and terminate with a \n""" + return indent(dedent(text.lstrip("\n")), " ") + "\n" class TestExtractDocsCases(unittest.TestCase): def setUp(self): self.maxDiff = 10000 + def test_solidity_block(self): - expected_cases = [ - " // SPDX-License-Identifier: GPL-3.0\n" - " pragma solidity >=0.7.0 <0.9.0;\n" - "\n" - " contract C {\n" - " function foo() public view {}\n" - " }\n" - "\n" - "\n", + expected_cases = [formatCase(case) for case in [ + """ + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; - " contract C {}\n" - "\n", - ] + contract C { + function foo() public view {} + } - self.assertEqual(extract_docs_cases(CODE_BLOCK_RST_PATH), expected_cases) + """, + """ + contract C {} + """, + ]] + + self.assertEqual(extract_solidity_docs_cases(CODE_BLOCK_RST_PATH), expected_cases) def test_solidity_block_with_directives(self): - expected_cases = [ - " // SPDX-License-Identifier: GPL-3.0\n" - " pragma solidity >=0.7.0 <0.9.0;\n" - "\n" - " contract C {\n" - " function foo() public view {}\n" - " }\n" - "\n" - "\n", + expected_cases = [formatCase(case) for case in [ + """ + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; - " contract C {}\n" - "\n", + contract C { + function foo() public view {} + } - " contract D {}\n" - " :linenos:\n" - "\n", + """, + """ + contract C {} + """, + """ + contract D {} + :linenos: + """, + """ + contract E {} + """, + ]] - " contract E {}\n" - "\n", - ] + self.assertEqual(extract_solidity_docs_cases(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH), expected_cases) - self.assertEqual(extract_docs_cases(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH), expected_cases) + def test_yul_block(self): + expected_cases = [formatCase(case) for case in [ + """ + { + let x := add(1, 5) + } + """, + """ + // Yul code wrapped in object + { + { + let y := mul(3, 5) + } + } + """, + """ + // Yul code wrapped in named object + object "Test" { + { + let y := mul(6, 9) + } + } + """, + ]] + + self.assertEqual(extract_yul_docs_cases(CODE_BLOCK_RST_PATH), expected_cases) + + def test_yul_block_with_directives(self): + expected_cases = [formatCase(case) for case in [ + """ + { + let x := add(1, 5) + } + """, + """ + // Yul code wrapped in object + { + let y := mul(3, 5) + } + """, + """ + // Yul code wrapped in named object + object "Test" { + let y := mul(3, 5) + :linenos: + } + """, + ]] + + self.assertEqual(extract_yul_docs_cases(CODE_BLOCK_WITH_DIRECTIVES_RST_PATH), expected_cases) diff --git a/test/solc/CommandLineInterface.cpp b/test/solc/CommandLineInterface.cpp new file mode 100644 index 000000000..9c73c66ea --- /dev/null +++ b/test/solc/CommandLineInterface.cpp @@ -0,0 +1,346 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +/// Unit tests for solc/CommandLineInterface.h + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity::frontend; +using namespace solidity::test; + +using PathSet = set; + +#define TEST_CASE_NAME (boost::unit_test::framework::current_test_case().p_name) + +namespace +{ + +ostream& operator<<(ostream& _out, vector const& _remappings) +{ + static auto remappingToString = [](auto const& _remapping) + { + return _remapping.context + ":" + _remapping.prefix + "=" + _remapping.target; + }; + + _out << "[" << joinHumanReadable(_remappings | ranges::views::transform(remappingToString)) << "]"; + return _out; +} + +ostream& operator<<(ostream& _out, map const& _map) +{ + _out << "{" << endl; + for (auto const& [key, value]: _map) + _out << "" << key << ": " << value << "," << endl; + _out << "}"; + + return _out; +} + +ostream& operator<<(ostream& _out, PathSet const& _paths) +{ + static auto pathString = [](auto const& _path) { return _path.string(); }; + + _out << "{" << joinHumanReadable(_paths | ranges::views::transform(pathString)) << "}"; + return _out; +} + +} // namespace + +namespace boost::test_tools::tt_detail +{ + +// Boost won't find the << operator unless we put it in the std namespace which is illegal. +// The recommended solution is to overload print_log_value<> struct and make it use our operator. + +template<> +struct print_log_value> +{ + void operator()(std::ostream& _out, vector const& _value) { ::operator<<(_out, _value); } +}; + +template<> +struct print_log_value> +{ + void operator()(std::ostream& _out, map const& _value) { ::operator<<(_out, _value); } +}; + +template<> +struct print_log_value +{ + void operator()(std::ostream& _out, PathSet const& _value) { ::operator<<(_out, _value); } +}; + +} // namespace boost::test_tools::tt_detail + +namespace solidity::frontend::test +{ + +BOOST_AUTO_TEST_SUITE(CommandLineInterfaceTest) + +BOOST_AUTO_TEST_CASE(multiple_input_modes) +{ + array inputModeOptions = { + "--standard-json", + "--link", + "--assemble", + "--strict-assembly", + "--yul", + "--import-ast", + }; + string expectedMessage = + "The following options are mutually exclusive: " + "--standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast. " + "Select at most one.\n"; + + for (string const& mode1: inputModeOptions) + for (string const& mode2: inputModeOptions) + if (mode1 != mode2) + { + vector commandLine = {"solc", mode1, mode2}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); + } +} + +BOOST_AUTO_TEST_CASE(cli_input) +{ + TemporaryDirectory tempDir1(TEST_CASE_NAME); + TemporaryDirectory tempDir2(TEST_CASE_NAME); + createFilesWithParentDirs({tempDir1.path() / "input1.sol"}); + createFilesWithParentDirs({tempDir2.path() / "input2.sol"}); + + vector expectedRemappings = { + {"", "a", "b/c/d"}, + {"a", "b", "c/d/e/"}, + }; + map expectedSources = { + {"", "\n"}, + {(tempDir1.path() / "input1.sol").generic_string(), ""}, + {(tempDir2.path() / "input2.sol").generic_string(), ""}, + }; + PathSet expectedAllowedPaths = { + boost::filesystem::canonical(tempDir1.path()), + boost::filesystem::canonical(tempDir2.path()), + "b/c", + "c/d/e", + }; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + "a=b/c/d", + (tempDir1.path() / "input1.sol").string(), + (tempDir2.path() / "input2.sol").string(), + "a:b=c/d/e/", + "-", + }); + + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::Compiler); + BOOST_TEST(result.options.input.addStdin); + BOOST_CHECK_EQUAL(result.options.input.remappings, expectedRemappings); + BOOST_CHECK_EQUAL(result.reader.sourceCodes(), expectedSources); + BOOST_CHECK_EQUAL(result.reader.allowedDirectories(), expectedAllowedPaths); +} + +BOOST_AUTO_TEST_CASE(cli_ignore_missing_some_files_exist) +{ + TemporaryDirectory tempDir1(TEST_CASE_NAME); + TemporaryDirectory tempDir2(TEST_CASE_NAME); + createFilesWithParentDirs({tempDir1.path() / "input1.sol"}); + + // NOTE: Allowed paths should not be added for skipped files. + map expectedSources = {{(tempDir1.path() / "input1.sol").generic_string(), ""}}; + PathSet expectedAllowedPaths = {boost::filesystem::canonical(tempDir1.path())}; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + (tempDir1.path() / "input1.sol").string(), + (tempDir2.path() / "input2.sol").string(), + "--ignore-missing", + }); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == "\"" + (tempDir2.path() / "input2.sol").string() + "\" is not found. Skipping.\n"); + BOOST_TEST(result.options.input.mode == InputMode::Compiler); + BOOST_TEST(!result.options.input.addStdin); + BOOST_CHECK_EQUAL(result.reader.sourceCodes(), expectedSources); + BOOST_CHECK_EQUAL(result.reader.allowedDirectories(), expectedAllowedPaths); +} + +BOOST_AUTO_TEST_CASE(cli_ignore_missing_no_files_exist) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + + string expectedMessage = + "\"" + (tempDir.path() / "input1.sol").string() + "\" is not found. Skipping.\n" + "\"" + (tempDir.path() / "input2.sol").string() + "\" is not found. Skipping.\n" + "All specified input files either do not exist or are not regular files.\n"; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + (tempDir.path() / "input1.sol").string(), + (tempDir.path() / "input2.sol").string(), + "--ignore-missing", + }); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(cli_not_a_file) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + + string expectedMessage = "\"" + tempDir.path().string() + "\" is not a valid file.\n"; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", tempDir.path().string()}); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(standard_json_base_path) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + "--standard-json", + "--base-path=" + tempDir.path().string(), + }); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::StandardJson); + BOOST_TEST(result.options.input.addStdin); + BOOST_TEST(result.options.input.paths.empty()); + BOOST_TEST(result.reader.sourceCodes().empty()); + BOOST_TEST(result.reader.allowedDirectories().empty()); + BOOST_TEST(result.reader.basePath() == tempDir.path()); +} + +BOOST_AUTO_TEST_CASE(standard_json_no_input_file) +{ + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", "--standard-json"}); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::StandardJson); + BOOST_TEST(result.options.input.addStdin); + BOOST_TEST(result.options.input.paths.empty()); + BOOST_TEST(result.reader.sourceCodes().empty()); + BOOST_TEST(result.reader.allowedDirectories().empty()); +} + +BOOST_AUTO_TEST_CASE(standard_json_dash) +{ + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", "--standard-json", "-"}); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::StandardJson); + BOOST_TEST(result.options.input.addStdin); + BOOST_TEST(result.reader.sourceCodes().empty()); + BOOST_TEST(result.reader.allowedDirectories().empty()); +} + +BOOST_AUTO_TEST_CASE(standard_json_one_input_file) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + createFilesWithParentDirs({tempDir.path() / "input.json"}); + + vector commandLine = {"solc", "--standard-json", (tempDir.path() / "input.json").string()}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(result.success); + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.options.input.mode == InputMode::StandardJson); + BOOST_TEST(!result.options.input.addStdin); + BOOST_TEST(result.options.input.paths == PathSet{tempDir.path() / "input.json"}); + BOOST_TEST(result.reader.allowedDirectories().empty()); +} + +BOOST_AUTO_TEST_CASE(standard_json_two_input_files) +{ + string expectedMessage = + "Too many input files for --standard-json.\n" + "Please either specify a single file name or provide its content on standard input.\n"; + + vector commandLine = {"solc", "--standard-json", "input1.json", "input2.json"}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(standard_json_one_input_file_and_stdin) +{ + string expectedMessage = + "Too many input files for --standard-json.\n" + "Please either specify a single file name or provide its content on standard input.\n"; + + vector commandLine = {"solc", "--standard-json", "input1.json", "-"}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(standard_json_ignore_missing) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + + // This option is pretty much useless Standard JSON mode. + string expectedMessage = + "\"" + (tempDir.path() / "input.json").string() + "\" is not found. Skipping.\n" + "All specified input files either do not exist or are not regular files.\n"; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ + "solc", + "--standard-json", + (tempDir.path() / "input.json").string(), + "--ignore-missing", + }); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(standard_json_remapping) +{ + string expectedMessage = + "Import remappings are not accepted on the command line in Standard JSON mode.\n" + "Please put them under 'settings.remappings' in the JSON input.\n"; + + vector commandLine = {"solc", "--standard-json", "a=b"}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace solidity::frontend::test diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp new file mode 100644 index 000000000..bb427232c --- /dev/null +++ b/test/solc/CommandLineParser.cpp @@ -0,0 +1,430 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +/// Unit tests for solc/CommandLineParser.h + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity::frontend; +using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::yul; + +namespace +{ + +optional parseCommandLine(vector const& _commandLine, ostream& _stdout, ostream& _stderr) +{ + vector argv = test::makeArgv(_commandLine); + + CommandLineParser cliParser(_stdout, _stderr); + bool success = cliParser.parse( + static_cast(_commandLine.size()), + argv.data(), + false // interactiveTerminal + ); + + if (!success) + return nullopt; + else + return cliParser.options(); +} + +} // namespace + +namespace solidity::frontend::test +{ + +BOOST_AUTO_TEST_SUITE(CommandLineParserTest) + +BOOST_AUTO_TEST_CASE(no_options) +{ + vector commandLine = {"solc", "contract.sol"}; + + CommandLineOptions expectedOptions; + expectedOptions.input.paths = {"contract.sol"}; + expectedOptions.modelChecker.initialize = true; + expectedOptions.modelChecker.settings = { + ModelCheckerContracts::Default(), + ModelCheckerEngine::None(), + false, + smtutil::SMTSolverChoice::All(), + ModelCheckerTargets::Default(), + nullopt, + }; + + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); + BOOST_REQUIRE(parsedOptions.has_value()); + BOOST_TEST(parsedOptions.value() == expectedOptions); +} + +BOOST_AUTO_TEST_CASE(help) +{ + stringstream sout, serr; + optional parsedOptions = parseCommandLine({"solc", "--help"}, sout, serr); + + BOOST_TEST(serr.str() == ""); + BOOST_TEST(boost::starts_with(sout.str(), "solc, the Solidity commandline compiler.")); + BOOST_TEST(sout.str().find("Usage: solc [options] [input_file...]") != string::npos); + BOOST_TEST(!parsedOptions.has_value()); +} + +BOOST_AUTO_TEST_CASE(cli_mode_options) +{ + for (InputMode inputMode: {InputMode::Compiler, InputMode::CompilerWithASTImport}) + { + vector commandLine = { + "solc", + "contract.sol", // Both modes do not care about file names, just about + "/tmp/projects/token.sol", // their content. They also both support stdin. + "/home/user/lib/dex.sol", + "file", + "input.json", + "-", + "/tmp=/usr/lib/", + "a:b=c/d", + ":contract.sol=", + "--base-path=/home/user/", + "--allow-paths=/tmp,/home,project,../contracts", + "--ignore-missing", + "--error-recovery", + "--output-dir=/tmp/out", + "--overwrite", + "--evm-version=spuriousDragon", + "--experimental-via-ir", + "--revert-strings=strip", + "--pretty-json", + "--json-indent=7", + "--no-color", + "--error-codes", + "--libraries=" + "dir1/file1.sol:L=0x1234567890123456789012345678901234567890," + "dir2/file2.sol:L=0x1111122222333334444455555666667777788888", + "--ast-compact-json", "--asm", "--asm-json", "--opcodes", "--bin", "--bin-runtime", "--abi", + "--ir", "--ir-optimized", "--ewasm", "--hashes", "--userdoc", "--devdoc", "--metadata", "--storage-layout", + "--gas", + "--combined-json=" + "abi,metadata,bin,bin-runtime,opcodes,asm,storage-layout,generated-sources,generated-sources-runtime," + "srcmap,srcmap-runtime,function-debug,function-debug-runtime,hashes,devdoc,userdoc,ast", + "--metadata-hash=swarm", + "--metadata-literal", + "--optimize", + "--optimize-runs=1000", + "--yul-optimizations=agf", + "--model-checker-contracts=contract1.yul:A,contract2.yul:B", + "--model-checker-engine=bmc", + "--model-checker-show-unproved=true", + "--model-checker-solvers=z3,smtlib2", + "--model-checker-targets=underflow,divByZero", + "--model-checker-timeout=5", + }; + + if (inputMode == InputMode::CompilerWithASTImport) + commandLine += vector{ + "--import-ast", + }; + + CommandLineOptions expectedOptions; + expectedOptions.input.mode = inputMode; + expectedOptions.input.paths = {"contract.sol", "/tmp/projects/token.sol", "/home/user/lib/dex.sol", "file", "input.json"}; + expectedOptions.input.remappings = { + {"", "/tmp", "/usr/lib/"}, + {"a", "b", "c/d"}, + {"", "contract.sol", ""}, + }; + + expectedOptions.input.addStdin = true; + expectedOptions.input.basePath = "/home/user/"; + expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "", "c", "/usr/lib"}; + expectedOptions.input.ignoreMissingFiles = true; + expectedOptions.input.errorRecovery = (inputMode == InputMode::Compiler); + expectedOptions.output.dir = "/tmp/out"; + expectedOptions.output.overwriteFiles = true; + expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); + expectedOptions.output.experimentalViaIR = true; + expectedOptions.output.revertStrings = RevertStrings::Strip; + expectedOptions.formatting.json = JsonFormat{JsonFormat::Pretty, 7}; + expectedOptions.linker.libraries = { + {"dir1/file1.sol:L", h160("1234567890123456789012345678901234567890")}, + {"dir2/file2.sol:L", h160("1111122222333334444455555666667777788888")}, + }; + expectedOptions.formatting.coloredOutput = false; + expectedOptions.formatting.withErrorIds = true; + expectedOptions.compiler.outputs = { + true, true, true, true, true, + true, true, true, true, true, + true, true, true, true, true, + }; + expectedOptions.compiler.estimateGas = true; + expectedOptions.compiler.combinedJsonRequests = { + true, true, true, true, true, + true, true, true, true, true, + true, true, true, true, true, + true, true, + }; + expectedOptions.metadata.hash = CompilerStack::MetadataHash::Bzzr1; + expectedOptions.metadata.literalSources = true; + expectedOptions.optimizer.enabled = true; + expectedOptions.optimizer.expectedExecutionsPerDeployment = 1000; + expectedOptions.optimizer.yulSteps = "agf"; + + expectedOptions.modelChecker.initialize = true; + expectedOptions.modelChecker.settings = { + {{{"contract1.yul", {"A"}}, {"contract2.yul", {"B"}}}}, + {true, false}, + true, + {false, true, true}, + {{VerificationTargetType::Underflow, VerificationTargetType::DivByZero}}, + 5, + }; + + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); + BOOST_REQUIRE(parsedOptions.has_value()); + BOOST_TEST(parsedOptions.value() == expectedOptions); + } +} + +BOOST_AUTO_TEST_CASE(assembly_mode_options) +{ + static vector, AssemblyStack::Machine, AssemblyStack::Language>> const allowedCombinations = { + {{"--machine=ewasm", "--yul-dialect=ewasm", "--assemble"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=ewasm", "--yul"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=ewasm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=evm", "--assemble"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--yul-dialect=evm", "--yul"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--yul-dialect=evm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, + {{"--machine=evm", "--yul-dialect=evm", "--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + {{"--machine=evm", "--yul-dialect=evm", "--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + {{"--machine=evm", "--yul-dialect=evm", "--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + {{"--machine=evm", "--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Assembly}, + {{"--machine=evm", "--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Yul}, + {{"--machine=evm", "--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + {{"--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Assembly}, + {{"--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Yul}, + {{"--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + }; + + for (auto const& [assemblyOptions, expectedMachine, expectedLanguage]: allowedCombinations) + { + vector commandLine = { + "solc", + "contract.yul", + "/tmp/projects/token.yul", + "/home/user/lib/dex.yul", + "file", + "input.json", + "-", + "/tmp=/usr/lib/", + "a:b=c/d", + ":contract.yul=", + "--base-path=/home/user/", + "--allow-paths=/tmp,/home,project,../contracts", + "--ignore-missing", + "--error-recovery", // Ignored in assembly mode + "--overwrite", + "--evm-version=spuriousDragon", + "--experimental-via-ir", // Ignored in assembly mode + "--revert-strings=strip", // Accepted but has no effect in assembly mode + "--pretty-json", + "--json-indent=1", + "--no-color", + "--error-codes", + "--libraries=" + "dir1/file1.sol:L=0x1234567890123456789012345678901234567890," + "dir2/file2.sol:L=0x1111122222333334444455555666667777788888", + "--metadata-hash=swarm", // Ignored in assembly mode + "--metadata-literal", // Ignored in assembly mode + "--model-checker-contracts=" // Ignored in assembly mode + "contract1.yul:A," + "contract2.yul:B", + "--model-checker-engine=bmc", // Ignored in assembly mode + "--model-checker-show-unproved=true", // Ignored in assembly mode + "--model-checker-solvers=z3,smtlib2", // Ignored in assembly mode + "--model-checker-targets=" // Ignored in assembly mode + "underflow," + "divByZero", + "--model-checker-timeout=5", // Ignored in assembly mode + + // Accepted but has no effect in assembly mode + "--ast-compact-json", "--asm", "--asm-json", "--opcodes", "--bin", "--bin-runtime", "--abi", + "--ir", "--ir-optimized", "--ewasm", "--hashes", "--userdoc", "--devdoc", "--metadata", "--storage-layout", + }; + commandLine += assemblyOptions; + if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) + commandLine += vector{ + "--optimize", + "--optimize-runs=1000", + "--yul-optimizations=agf", + }; + + CommandLineOptions expectedOptions; + expectedOptions.input.mode = InputMode::Assembler; + + expectedOptions.input.paths = {"contract.yul", "/tmp/projects/token.yul", "/home/user/lib/dex.yul", "file", "input.json"}; + expectedOptions.input.remappings = { + {"", "/tmp", "/usr/lib/"}, + {"a", "b", "c/d"}, + {"", "contract.yul", ""}, + }; + expectedOptions.input.addStdin = true; + expectedOptions.input.basePath = "/home/user/"; + expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "", "c", "/usr/lib"}; + expectedOptions.input.ignoreMissingFiles = true; + expectedOptions.output.overwriteFiles = true; + expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); + expectedOptions.output.revertStrings = RevertStrings::Strip; + expectedOptions.formatting.json = JsonFormat {JsonFormat::Pretty, 1}; + expectedOptions.assembly.targetMachine = expectedMachine; + expectedOptions.assembly.inputLanguage = expectedLanguage; + expectedOptions.linker.libraries = { + {"dir1/file1.sol:L", h160("1234567890123456789012345678901234567890")}, + {"dir2/file2.sol:L", h160("1111122222333334444455555666667777788888")}, + }; + expectedOptions.formatting.coloredOutput = false; + expectedOptions.formatting.withErrorIds = true; + expectedOptions.compiler.outputs = { + true, true, true, true, true, + true, true, true, true, true, + true, true, true, true, true, + }; + if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) + { + expectedOptions.optimizer.enabled = true; + expectedOptions.optimizer.yulSteps = "agf"; + expectedOptions.optimizer.expectedExecutionsPerDeployment = 1000; + } + + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == "Warning: Yul is still experimental. Please use the output with care.\n"); + BOOST_REQUIRE(parsedOptions.has_value()); + BOOST_TEST(parsedOptions.value() == expectedOptions); + } +} + +BOOST_AUTO_TEST_CASE(standard_json_mode_options) +{ + vector commandLine = { + "solc", + "input.json", + "--standard-json", + "--base-path=/home/user/", + "--allow-paths=/tmp,/home,project,../contracts", + "--ignore-missing", + "--error-recovery", // Ignored in Standard JSON mode + "--output-dir=/tmp/out", // Accepted but has no effect in Standard JSON mode + "--overwrite", // Accepted but has no effect in Standard JSON mode + "--evm-version=spuriousDragon", // Ignored in Standard JSON mode + "--experimental-via-ir", // Ignored in Standard JSON mode + "--revert-strings=strip", // Accepted but has no effect in Standard JSON mode + "--pretty-json", + "--json-indent=1", + "--no-color", // Accepted but has no effect in Standard JSON mode + "--error-codes", // Accepted but has no effect in Standard JSON mode + "--libraries=" // Ignored in Standard JSON mode + "dir1/file1.sol:L=0x1234567890123456789012345678901234567890," + "dir2/file2.sol:L=0x1111122222333334444455555666667777788888", + "--gas", // Accepted but has no effect in Standard JSON mode + "--combined-json=abi,bin", // Accepted but has no effect in Standard JSON mode + "--metadata-hash=swarm", // Ignored in Standard JSON mode + "--metadata-literal", // Ignored in Standard JSON mode + "--optimize", // Ignored in Standard JSON mode + "--optimize-runs=1000", // Ignored in Standard JSON mode + "--yul-optimizations=agf", + "--model-checker-contracts=" // Ignored in Standard JSON mode + "contract1.yul:A," + "contract2.yul:B", + "--model-checker-engine=bmc", // Ignored in Standard JSON mode + "--model-checker-show-unproved=true", // Ignored in Standard JSON mode + "--model-checker-solvers=z3,smtlib2", // Ignored in Standard JSON mode + "--model-checker-targets=" // Ignored in Standard JSON mode + "underflow," + "divByZero", + "--model-checker-timeout=5", // Ignored in Standard JSON mode + + // Accepted but has no effect in Standard JSON mode + "--ast-compact-json", "--asm", "--asm-json", "--opcodes", "--bin", "--bin-runtime", "--abi", + "--ir", "--ir-optimized", "--ewasm", "--hashes", "--userdoc", "--devdoc", "--metadata", "--storage-layout", + }; + + CommandLineOptions expectedOptions; + + expectedOptions.input.mode = InputMode::StandardJson; + expectedOptions.input.paths = {"input.json"}; + expectedOptions.input.basePath = "/home/user/"; + expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts"}; + expectedOptions.input.ignoreMissingFiles = true; + expectedOptions.output.dir = "/tmp/out"; + expectedOptions.output.overwriteFiles = true; + expectedOptions.output.revertStrings = RevertStrings::Strip; + expectedOptions.formatting.json = JsonFormat {JsonFormat::Pretty, 1}; + expectedOptions.formatting.coloredOutput = false; + expectedOptions.formatting.withErrorIds = true; + expectedOptions.compiler.outputs = { + true, true, true, true, true, + true, true, true, true, true, + true, true, true, true, true, + }; + expectedOptions.compiler.estimateGas = true; + expectedOptions.compiler.combinedJsonRequests = CombinedJsonRequests{}; + expectedOptions.compiler.combinedJsonRequests->abi = true; + expectedOptions.compiler.combinedJsonRequests->binary = true; + + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); + BOOST_REQUIRE(parsedOptions.has_value()); + BOOST_TEST(parsedOptions.value() == expectedOptions); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace solidity::frontend::test diff --git a/test/solc/Common.cpp b/test/solc/Common.cpp new file mode 100644 index 000000000..1bd209067 --- /dev/null +++ b/test/solc/Common.cpp @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +using namespace std; +using namespace solidity::frontend; + +vector test::makeArgv(vector const& _commandLine) +{ + size_t argc = _commandLine.size(); + vector argv(_commandLine.size() + 1); + + // C++ standard mandates argv[argc] to be NULL + argv[argc] = nullptr; + + for (size_t i = 0; i < argc; ++i) + argv[i] = _commandLine[i].c_str(); + + return argv; +} + +test::OptionsReaderAndMessages test::parseCommandLineAndReadInputFiles( + vector const& _commandLine, + string const& _standardInputContent, + bool _processInput +) +{ + vector argv = makeArgv(_commandLine); + stringstream sin(_standardInputContent), sout, serr; + CommandLineInterface cli(sin, sout, serr); + bool success = cli.parseArguments(static_cast(_commandLine.size()), argv.data()); + if (success) + success = cli.readInputFiles(); + if (success && _processInput) + success = cli.processInput(); + + return {success, cli.options(), cli.fileReader(), cli.standardJsonInput(), sout.str(), serr.str()}; +} diff --git a/test/solc/Common.h b/test/solc/Common.h new file mode 100644 index 000000000..6d5e3875d --- /dev/null +++ b/test/solc/Common.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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +/// Utilities shared by multiple tests for code in solc/. + +#include + +#include + +#include +#include +#include + +BOOST_TEST_DONT_PRINT_LOG_VALUE(solidity::frontend::CommandLineOptions) +BOOST_TEST_DONT_PRINT_LOG_VALUE(solidity::frontend::InputMode) + +namespace solidity::frontend::test +{ + +struct OptionsReaderAndMessages +{ + bool success; + CommandLineOptions options; + FileReader reader; + std::optional standardJsonInput; + std::string stdoutContent; + std::string stderrContent; +}; + +std::vector makeArgv(std::vector const& _commandLine); + +OptionsReaderAndMessages parseCommandLineAndReadInputFiles( + std::vector const& _commandLine, + std::string const& _standardInputContent = "", + bool _processInput = false +); + +} // namespace solidity::frontend::test diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 95eb3a636..1f62b57fc 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable(isoltest ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerTest.cpp ../libyul/Common.cpp + ../libyul/ControlFlowGraphTest.cpp ../libyul/EVMCodeTransformTest.cpp ../libyul/EwasmTranslationTest.cpp ../libyul/FunctionSideEffects.cpp @@ -41,4 +42,5 @@ add_executable(isoltest ../libyul/YulOptimizerTestCommon.cpp ../libyul/YulInterpreterTest.cpp ) -target_link_libraries(isoltest PRIVATE evmc libsolc solidity yulInterpreter evmasm Boost::boost Boost::program_options Boost::unit_test_framework) +target_compile_definitions(isoltest PRIVATE ISOLTEST) +target_link_libraries(isoltest PRIVATE evmc libsolc solidity yulInterpreter evmasm Boost::boost Boost::program_options Boost::unit_test_framework Threads::Threads) diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index 99a3775e1..65749a009 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -16,11 +16,11 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include "libsolidity/formal/ModelCheckerSettings.h" #include #include #include +#include #include @@ -105,6 +105,8 @@ void FuzzerUtil::testCompiler( compiler.setModelCheckerSettings({ frontend::ModelCheckerContracts::Default(), frontend::ModelCheckerEngine::All(), + /*showUnproved=*/false, + smtutil::SMTSolverChoice::All(), frontend::ModelCheckerTargets::Default(), /*timeout=*/1 }); diff --git a/test/tools/ossfuzz/SolidityEvmoneInterface.cpp b/test/tools/ossfuzz/SolidityEvmoneInterface.cpp index 954ae2bf0..4d536183c 100644 --- a/test/tools/ossfuzz/SolidityEvmoneInterface.cpp +++ b/test/tools/ossfuzz/SolidityEvmoneInterface.cpp @@ -41,13 +41,11 @@ optional SolidityCompilationFramework::compileContract() { if (m_compilerInput.debugFailure) { - SourceReferenceFormatter formatter(cerr, false, false); - cerr << "Compiling contract failed" << endl; for (auto const& error: m_compiler.errors()) - formatter.printExceptionInformation( + cerr << SourceReferenceFormatter::formatErrorInformation( *error, - formatter.formatErrorInformation(*error) + m_compiler ); } return {}; diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index d405c2bc8..1fe4e6eaa 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -43,20 +43,6 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::yul::test::yul_fuzzer; -namespace -{ -void printErrors(ostream& _stream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_stream, false, false); - - for (auto const& error: _errors) - formatter.printExceptionInformation( - *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error" - ); -} -} - DEFINE_PROTO_FUZZER(Program const& _input) { ProtoConverter converter; @@ -88,7 +74,13 @@ DEFINE_PROTO_FUZZER(Program const& _input) !Error::containsOnlyWarnings(stack.errors()) ) { - printErrors(std::cout, stack.errors()); + SourceReferenceFormatter formatter(std::cout, stack, false, false); + + for (auto const& error: stack.errors()) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); yulAssert(false, "Proto fuzzer generated malformed program"); } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 1d3ca3868..357311595 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -44,6 +43,7 @@ #include #include +#include #include #include @@ -78,17 +78,19 @@ class YulOpti public: void printErrors() { - SourceReferenceFormatter formatter(cerr, true, false); - - for (auto const& error: m_errors) - formatter.printErrorInformation(*error); + SourceReferenceFormatter{ + cerr, + SingletonCharStreamProvider(*m_charStream), + true, + false + }.printErrorInformation(m_errors); } bool parse(string const& _input) { ErrorReporter errorReporter(m_errors); - shared_ptr scanner = make_shared(CharStream(_input, "")); - m_ast = yul::Parser(errorReporter, m_dialect).parse(scanner, false); + m_charStream = make_shared(_input, ""); + m_ast = yul::Parser(errorReporter, m_dialect).parse(*m_charStream); if (!m_ast || !errorReporter.errors().empty()) { cerr << "Error parsing source." << endl; @@ -230,6 +232,7 @@ public: private: ErrorList m_errors; + shared_ptr m_charStream; shared_ptr m_ast; Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; shared_ptr m_analysisInfo; diff --git a/test/tools/yulrun.cpp b/test/tools/yulrun.cpp index b5f8d60dd..7906be744 100644 --- a/test/tools/yulrun.cpp +++ b/test/tools/yulrun.cpp @@ -53,12 +53,6 @@ namespace po = boost::program_options; namespace { -void printErrors(ErrorList const& _errors) -{ - for (auto const& error: _errors) - SourceReferenceFormatter(cout, true, false).printErrorInformation(*error); -} - pair, shared_ptr> parse(string const& _source) { AssemblyStack stack( @@ -73,7 +67,7 @@ pair, shared_ptr> parse(string const& _source } else { - printErrors(stack.errors()); + SourceReferenceFormatter(cout, stack, true, false).printErrorInformation(stack.errors()); return {}; } } diff --git a/test/yulPhaser/TestHelpers.h b/test/yulPhaser/TestHelpers.h index 31a5907f3..f9d0afa4c 100644 --- a/test/yulPhaser/TestHelpers.h +++ b/test/yulPhaser/TestHelpers.h @@ -57,7 +57,7 @@ std::ostream& operator<<(std::ostream& _output, std::tuple const& _tuple namespace boost::test_tools::tt_detail { -// Boost won't find find the << operator unless we put it in the std namespace which is illegal. +// Boost won't find the << operator unless we put it in the std namespace which is illegal. // The recommended solution is to overload print_log_value<> struct and make it use our global operator. template struct print_log_value> diff --git a/tools/solidityUpgrade/SourceTransform.h b/tools/solidityUpgrade/SourceTransform.h index 65226a442..04d128298 100644 --- a/tools/solidityUpgrade/SourceTransform.h +++ b/tools/solidityUpgrade/SourceTransform.h @@ -22,6 +22,9 @@ #include +#include +#include + #include #include @@ -47,42 +50,57 @@ public: operator std::string() { return m_stream.str(); } }; +class SourceTextRetriever +{ +public: + explicit SourceTextRetriever(langutil::CharStreamProvider const& _charStreamProvider): + m_charStreamProvider(_charStreamProvider) {} + std::string text(langutil::SourceLocation const& _location) const + { + solAssert(_location.hasText(), ""); + return std::string{m_charStreamProvider.charStream(*_location.sourceName).text(_location)}; + } +protected: + langutil::CharStreamProvider const& m_charStreamProvider; +}; /** * Helper that provides functions which analyze certain source locations * on a textual base. They utilize regular expression to search for * keywords or to determine formatting. */ -class SourceAnalysis +class SourceAnalysis: public SourceTextRetriever { public: - static bool isMultilineKeyword( + using SourceTextRetriever::SourceTextRetriever; + + bool isMultilineKeyword( langutil::SourceLocation const& _location, std::string const& _keyword - ) + ) const { return regex_search( - _location.text(), + text(_location), std::regex{"(\\b" + _keyword + "\\b\\n|\\r|\\r\\n)"} ); } - static bool hasMutabilityKeyword(langutil::SourceLocation const& _location) + bool hasMutabilityKeyword(langutil::SourceLocation const& _location) const { return regex_search( - _location.text(), + text(_location), std::regex{"(\\b(pure|view|nonpayable|payable)\\b)"} ); } - static bool hasVirtualKeyword(langutil::SourceLocation const& _location) + bool hasVirtualKeyword(langutil::SourceLocation const& _location) const { - return regex_search(_location.text(), std::regex{"(\\b(virtual)\\b)"}); + return regex_search(text(_location), std::regex{"(\\b(virtual)\\b)"}); } - static bool hasVisibilityKeyword(langutil::SourceLocation const& _location) + bool hasVisibilityKeyword(langutil::SourceLocation const& _location) const { - return regex_search(_location.text(), std::regex{"\\bpublic\\b"}); + return regex_search(text(_location), std::regex{"\\bpublic\\b"}); } }; @@ -117,21 +135,23 @@ public: * on a textual base. In general, these utilize regular expressions applied * to the given source location. */ -class SourceTransform +class SourceTransform: public SourceTextRetriever { public: + using SourceTextRetriever::SourceTextRetriever; + /// Searches for the keyword given and prepends the expression. /// E.g. `function f() view;` -> `function f() public view;` - static std::string insertBeforeKeyword( + std::string insertBeforeKeyword( langutil::SourceLocation const& _location, std::string const& _keyword, std::string const& _expression - ) + ) const { auto _regex = std::regex{"(\\b" + _keyword + "\\b)"}; - if (regex_search(_location.text(), _regex)) + if (regex_search(text(_location), _regex)) return regex_replace( - _location.text(), + text(_location), _regex, _expression + " " + _keyword, std::regex_constants::format_first_only @@ -139,7 +159,7 @@ public: else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); @@ -148,22 +168,22 @@ public: /// Searches for the keyword given and appends the expression. /// E.g. `function f() public {}` -> `function f() public override {}` - static std::string insertAfterKeyword( + std::string insertAfterKeyword( langutil::SourceLocation const& _location, std::string const& _keyword, std::string const& _expression - ) + ) const { - bool isMultiline = SourceAnalysis::isMultilineKeyword(_location, _keyword); + bool isMultiline = SourceAnalysis{m_charStreamProvider}.isMultilineKeyword(_location, _keyword); std::string toAppend = isMultiline ? ("\n " + _expression) : (" " + _expression); std::regex keyword{"(\\b" + _keyword + "\\b)"}; - if (regex_search(_location.text(), keyword)) - return regex_replace(_location.text(), keyword, _keyword + toAppend); + if (regex_search(text(_location), keyword)) + return regex_replace(text(_location), keyword, _keyword + toAppend); else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); @@ -174,22 +194,22 @@ public: /// Searches for the first right parenthesis and appends the expression /// given. /// E.g. `function f() {}` -> `function f() public {}` - static std::string insertAfterRightParenthesis( + std::string insertAfterRightParenthesis( langutil::SourceLocation const& _location, std::string const& _expression - ) + ) const { auto _regex = std::regex{"(\\))"}; - if (regex_search(_location.text(), _regex)) + if (regex_search(text(_location), _regex)) return regex_replace( - _location.text(), + text(_location), std::regex{"(\\))"}, ") " + _expression ); else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); @@ -199,38 +219,38 @@ public: /// Searches for the `function` keyword and its identifier and replaces /// both by the expression given. /// E.g. `function Storage() {}` -> `constructor() {}` - static std::string replaceFunctionName( + std::string replaceFunctionName( langutil::SourceLocation const& _location, std::string const& _name, std::string const& _expression - ) + ) const { auto _regex = std::regex{ "(\\bfunction\\s*" + _name + "\\b)"}; - if (regex_search(_location.text(), _regex)) + if (regex_search(text(_location), _regex)) return regex_replace( - _location.text(), + text(_location), _regex, _expression ); else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); return ""; } - static std::string gasUpdate(langutil::SourceLocation const& _location) + std::string gasUpdate(langutil::SourceLocation const& _location) const { // dot, "gas", any number of whitespaces, left bracket std::regex gasReg{"\\.gas\\s*\\("}; - if (regex_search(_location.text(), gasReg)) + if (regex_search(text(_location), gasReg)) { std::string out = regex_replace( - _location.text(), + text(_location), gasReg, "{gas: ", std::regex_constants::format_first_only @@ -240,22 +260,22 @@ public: else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); return ""; } - static std::string valueUpdate(langutil::SourceLocation const& _location) + std::string valueUpdate(langutil::SourceLocation const& _location) const { // dot, "value", any number of whitespaces, left bracket std::regex valueReg{"\\.value\\s*\\("}; - if (regex_search(_location.text(), valueReg)) + if (regex_search(text(_location), valueReg)) { std::string out = regex_replace( - _location.text(), + text(_location), valueReg, "{value: ", std::regex_constants::format_first_only @@ -265,21 +285,21 @@ public: else solAssert( false, - LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << + LocationHelper() << "Could not fix: " << text(_location) << " at " << _location << "\nNeeds to be fixed manually." ); return ""; } - static std::string nowUpdate(langutil::SourceLocation const& _location) + std::string nowUpdate(langutil::SourceLocation const& _location) const { - return regex_replace(_location.text(), std::regex{"now"}, "block.timestamp"); + return regex_replace(text(_location), std::regex{"now"}, "block.timestamp"); } - static std::string removeVisibility(langutil::SourceLocation const& _location) + std::string removeVisibility(langutil::SourceLocation const& _location) const { - std::string replacement = _location.text(); + std::string replacement = text(_location); for (auto const& replace: {"public ", "public", "internal ", "internal", "external ", "external"}) replacement = regex_replace(replacement, std::regex{replace}, ""); return replacement; diff --git a/tools/solidityUpgrade/SourceUpgrade.cpp b/tools/solidityUpgrade/SourceUpgrade.cpp index 540a042a7..6b9bcd1d0 100644 --- a/tools/solidityUpgrade/SourceUpgrade.cpp +++ b/tools/solidityUpgrade/SourceUpgrade.cpp @@ -347,14 +347,14 @@ bool SourceUpgrade::analyzeAndUpgrade(pair const& _sourceCode) log() << "Analyzing and upgrading " << _sourceCode.first << "." << endl; if (m_compiler->state() >= CompilerStack::State::AnalysisPerformed) - m_suite.analyze(m_compiler->ast(_sourceCode.first)); + m_suite.analyze(*m_compiler, m_compiler->ast(_sourceCode.first)); if (!m_suite.changes().empty()) { auto& change = m_suite.changes().front(); if (verbose) - change.log(true); + change.log(*m_compiler, true); if (change.level() == UpgradeChange::Level::Safe) { @@ -388,20 +388,19 @@ void SourceUpgrade::applyChange( log() << _change.patch(); } - _change.apply(); - m_sourceCodes[_sourceCode.first] = _change.source(); + m_sourceCodes[_sourceCode.first] = _change.apply(_sourceCode.second); if (!dryRun) - writeInputFile(_sourceCode.first, _change.source()); + writeInputFile(_sourceCode.first, m_sourceCodes[_sourceCode.first]); } void SourceUpgrade::printErrors() const { - auto formatter = make_unique(cout, true, false); + langutil::SourceReferenceFormatter formatter{cout, *m_compiler, true, false}; for (auto const& error: m_compiler->errors()) if (error->type() != langutil::Error::Type::Warning) - formatter->printErrorInformation(*error); + formatter.printErrorInformation(*error); } void SourceUpgrade::printStatistics() const diff --git a/tools/solidityUpgrade/SourceUpgrade.h b/tools/solidityUpgrade/SourceUpgrade.h index f70d24080..11e7e12a9 100644 --- a/tools/solidityUpgrade/SourceUpgrade.h +++ b/tools/solidityUpgrade/SourceUpgrade.h @@ -68,29 +68,29 @@ private: class Suite: public UpgradeSuite { public: - void analyze(frontend::SourceUnit const& _sourceUnit) + void analyze(langutil::CharStreamProvider const& _charStreamProvider, frontend::SourceUnit const& _sourceUnit) { /// Solidity 0.5.0 if (isActivated(Module::ConstructorKeyword)) - ConstructorKeyword{m_changes}.analyze(_sourceUnit); + ConstructorKeyword{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::VisibilitySpecifier)) - VisibilitySpecifier{m_changes}.analyze(_sourceUnit); + VisibilitySpecifier{_charStreamProvider, m_changes}.analyze(_sourceUnit); /// Solidity 0.6.0 if (isActivated(Module::AbstractContract)) - AbstractContract{m_changes}.analyze(_sourceUnit); + AbstractContract{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::OverridingFunction)) - OverridingFunction{m_changes}.analyze(_sourceUnit); + OverridingFunction{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::VirtualFunction)) - VirtualFunction{m_changes}.analyze(_sourceUnit); + VirtualFunction{_charStreamProvider, m_changes}.analyze(_sourceUnit); /// Solidity 0.7.0 if (isActivated(Module::DotSyntax)) - DotSyntax{m_changes}.analyze(_sourceUnit); + DotSyntax{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::NowKeyword)) - NowKeyword{m_changes}.analyze(_sourceUnit); + NowKeyword{_charStreamProvider, m_changes}.analyze(_sourceUnit); if (isActivated(Module::ConstrutorVisibility)) - ConstructorVisibility{m_changes}.analyze(_sourceUnit); + ConstructorVisibility{_charStreamProvider, m_changes}.analyze(_sourceUnit); } void activateModule(Module _module) { m_modules.insert(_module); } diff --git a/tools/solidityUpgrade/Upgrade050.cpp b/tools/solidityUpgrade/Upgrade050.cpp index 9c0ecd656..c8b4de9e8 100644 --- a/tools/solidityUpgrade/Upgrade050.cpp +++ b/tools/solidityUpgrade/Upgrade050.cpp @@ -36,7 +36,7 @@ void ConstructorKeyword::endVisit(ContractDefinition const& _contract) m_changes.emplace_back( UpgradeChange::Level::Safe, function->location(), - SourceTransform::replaceFunctionName( + SourceTransform{m_charStreamProvider}.replaceFunctionName( function->location(), function->name(), "constructor" @@ -50,6 +50,6 @@ void VisibilitySpecifier::endVisit(FunctionDefinition const& _function) m_changes.emplace_back( UpgradeChange::Level::Safe, _function.location(), - SourceTransform::insertAfterRightParenthesis(_function.location(), "public") + SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis(_function.location(), "public") ); } diff --git a/tools/solidityUpgrade/Upgrade060.cpp b/tools/solidityUpgrade/Upgrade060.cpp index c614f89d0..52010db39 100644 --- a/tools/solidityUpgrade/Upgrade060.cpp +++ b/tools/solidityUpgrade/Upgrade060.cpp @@ -29,75 +29,6 @@ using namespace solidity; using namespace solidity::frontend; using namespace solidity::tools; -using Contracts = set; - -namespace -{ - -inline string appendOverride( - FunctionDefinition const& _function, - Contracts const& _expectedContracts -) -{ - auto location = _function.location(); - string upgradedCode; - string overrideExpression = SourceGeneration::functionOverride(_expectedContracts); - - if (SourceAnalysis::hasVirtualKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - "virtual", - overrideExpression - ); - else if (SourceAnalysis::hasMutabilityKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - stateMutabilityToString(_function.stateMutability()), - overrideExpression - ); - else if (SourceAnalysis::hasVisibilityKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - Declaration::visibilityToString(_function.visibility()), - overrideExpression - ); - else - upgradedCode = SourceTransform::insertAfterRightParenthesis( - location, - overrideExpression - ); - - return upgradedCode; -} - -inline string appendVirtual(FunctionDefinition const& _function) -{ - auto location = _function.location(); - string upgradedCode; - - if (SourceAnalysis::hasMutabilityKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - stateMutabilityToString(_function.stateMutability()), - "virtual" - ); - else if (SourceAnalysis::hasVisibilityKeyword(location)) - upgradedCode = SourceTransform::insertAfterKeyword( - location, - Declaration::visibilityToString(_function.visibility()), - "virtual" - ); - else - upgradedCode = SourceTransform::insertAfterRightParenthesis( - _function.location(), - "virtual" - ); - - return upgradedCode; -} - -} - void AbstractContract::endVisit(ContractDefinition const& _contract) { bool isFullyImplemented = _contract.annotation().unimplementedDeclarations->empty(); @@ -110,7 +41,7 @@ void AbstractContract::endVisit(ContractDefinition const& _contract) m_changes.emplace_back( UpgradeChange::Level::Safe, _contract.location(), - SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract") + SourceTransform{m_charStreamProvider}.insertBeforeKeyword(_contract.location(), "contract", "abstract") ); } @@ -159,6 +90,42 @@ void OverridingFunction::endVisit(ContractDefinition const& _contract) } } +string OverridingFunction::appendOverride( + FunctionDefinition const& _function, + Contracts const& _expectedContracts +) +{ + auto location = _function.location(); + string upgradedCode; + string overrideExpression = SourceGeneration::functionOverride(_expectedContracts); + + if (SourceAnalysis{m_charStreamProvider}.hasVirtualKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + "virtual", + overrideExpression + ); + else if (SourceAnalysis{m_charStreamProvider}.hasMutabilityKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + stateMutabilityToString(_function.stateMutability()), + overrideExpression + ); + else if (SourceAnalysis{m_charStreamProvider}.hasVisibilityKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + Declaration::visibilityToString(_function.visibility()), + overrideExpression + ); + else + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis( + location, + overrideExpression + ); + + return upgradedCode; +} + void VirtualFunction::endVisit(ContractDefinition const& _contract) { auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract); @@ -192,12 +159,39 @@ void VirtualFunction::endVisit(ContractDefinition const& _contract) ) { m_changes.emplace_back( - UpgradeChange::Level::Safe, - function->location(), - appendVirtual(*function) + UpgradeChange::Level::Safe, + function->location(), + appendVirtual(*function) ); } } } } } + +string VirtualFunction::appendVirtual(FunctionDefinition const& _function) const +{ + auto location = _function.location(); + string upgradedCode; + + if (SourceAnalysis{m_charStreamProvider}.hasMutabilityKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + stateMutabilityToString(_function.stateMutability()), + "virtual" + ); + else if (SourceAnalysis{m_charStreamProvider}.hasVisibilityKeyword(location)) + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword( + location, + Declaration::visibilityToString(_function.visibility()), + "virtual" + ); + else + upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis( + _function.location(), + "virtual" + ); + + return upgradedCode; +} + diff --git a/tools/solidityUpgrade/Upgrade060.h b/tools/solidityUpgrade/Upgrade060.h index 9a8880b96..46b10a3a0 100644 --- a/tools/solidityUpgrade/Upgrade060.h +++ b/tools/solidityUpgrade/Upgrade060.h @@ -50,7 +50,14 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: + using Contracts = std::set; + void endVisit(frontend::ContractDefinition const& _contract) override; + + std::string appendOverride( + frontend::FunctionDefinition const& _function, + Contracts const& _expectedContracts + ); }; /** @@ -65,6 +72,8 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: void endVisit(frontend::ContractDefinition const& _function) override; + + std::string appendVirtual(frontend::FunctionDefinition const& _function) const; }; } diff --git a/tools/solidityUpgrade/Upgrade070.cpp b/tools/solidityUpgrade/Upgrade070.cpp index 04b88f7ca..6a26b1400 100644 --- a/tools/solidityUpgrade/Upgrade070.cpp +++ b/tools/solidityUpgrade/Upgrade070.cpp @@ -29,14 +29,14 @@ void DotSyntax::endVisit(FunctionCall const& _functionCall) m_changes.emplace_back( UpgradeChange::Level::Safe, _functionCall.location(), - SourceTransform::valueUpdate(_functionCall.location()) + SourceTransform{m_charStreamProvider}.valueUpdate(_functionCall.location()) ); if (funcType->gasSet()) m_changes.emplace_back( UpgradeChange::Level::Safe, _functionCall.location(), - SourceTransform::gasUpdate(_functionCall.location()) + SourceTransform{m_charStreamProvider}.gasUpdate(_functionCall.location()) ); } } @@ -55,7 +55,7 @@ void NowKeyword::endVisit(Identifier const& _identifier) m_changes.emplace_back( UpgradeChange::Level::Safe, _identifier.location(), - SourceTransform::nowUpdate(_identifier.location()) + SourceTransform{m_charStreamProvider}.nowUpdate(_identifier.location()) ); } } @@ -72,7 +72,7 @@ void ConstructorVisibility::endVisit(ContractDefinition const& _contract) m_changes.emplace_back( UpgradeChange::Level::Safe, _contract.location(), - SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract") + SourceTransform{m_charStreamProvider}.insertBeforeKeyword(_contract.location(), "contract", "abstract") ); for (FunctionDefinition const* function: _contract.definedFunctions()) @@ -80,6 +80,6 @@ void ConstructorVisibility::endVisit(ContractDefinition const& _contract) m_changes.emplace_back( UpgradeChange::Level::Safe, function->location(), - SourceTransform::removeVisibility(function->location()) + SourceTransform{m_charStreamProvider}.removeVisibility(function->location()) ); } diff --git a/tools/solidityUpgrade/UpgradeChange.cpp b/tools/solidityUpgrade/UpgradeChange.cpp index 51475ffd3..2976efe5d 100644 --- a/tools/solidityUpgrade/UpgradeChange.cpp +++ b/tools/solidityUpgrade/UpgradeChange.cpp @@ -26,18 +26,20 @@ using namespace solidity::langutil; using namespace solidity::util; using namespace solidity::tools; -void UpgradeChange::apply() +string UpgradeChange::apply(string _source) const { - m_source.replace( + _source.replace( static_cast(m_location.start), - static_cast(m_location.end - m_location.start), m_patch + static_cast(m_location.end - m_location.start), + m_patch ); + return _source; } -void UpgradeChange::log(bool const _shorten) const +void UpgradeChange::log(CharStreamProvider const& _charStreamProvider, bool const _shorten) const { stringstream os; - SourceReferenceFormatter formatter{os, true, false}; + SourceReferenceFormatter formatter{os, _charStreamProvider, true, false}; string start = to_string(m_location.start); string end = to_string(m_location.end); @@ -48,10 +50,10 @@ void UpgradeChange::log(bool const _shorten) const os << endl; AnsiColorized(os, true, {formatting::BOLD, color}) << "Upgrade change (" << level << ")" << endl; os << "=======================" << endl; - formatter.printSourceLocation(SourceReferenceExtractor::extract(&m_location)); + formatter.printSourceLocation(SourceReferenceExtractor::extract(_charStreamProvider, &m_location)); os << endl; - LineColumn lineEnd = m_location.source->translatePositionToLineColumn(m_location.end); + LineColumn lineEnd = _charStreamProvider.charStream(*m_location.sourceName).translatePositionToLineColumn(m_location.end); int const leftpad = static_cast(log10(max(lineEnd.line, 1))) + 2; stringstream output; diff --git a/tools/solidityUpgrade/UpgradeChange.h b/tools/solidityUpgrade/UpgradeChange.h index 19f08c6a0..6cccc154b 100644 --- a/tools/solidityUpgrade/UpgradeChange.h +++ b/tools/solidityUpgrade/UpgradeChange.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -47,32 +48,27 @@ public: Level _level, langutil::SourceLocation _location, std::string _patch - ) - : + ): m_location(_location), - m_source(_location.source->source()), m_patch(std::move(_patch)), - m_level(_level) {} + m_level(_level) + {} ~UpgradeChange() {} - langutil::SourceLocation const& location() { return m_location; } - std::string source() const { return m_source; } - std::string patch() { return m_patch; } + langutil::SourceLocation const& location() const { return m_location; } + std::string patch() const { return m_patch; } Level level() const { return m_level; } - /// Does the actual replacement of code under at current source location. - /// The change is applied on the upgrade-specific copy of source code. - /// The altered code is then requested by the upgrade routine later on. - void apply(); - /// Does a pretty-print of this upgrade change. It uses a source formatter - /// provided by the compiler in order to print affected code. Since the patch + /// Performs the actual replacement on the provided original source code + /// and returns the modified source code. + std::string apply(std::string _source) const; + /// Does a pretty-print of this upgrade change. Since the patch /// can contain a lot of code lines, it can be shortened, which is signaled /// by setting the flag. - void log(bool const _shorten = true) const; + void log(langutil::CharStreamProvider const& _charStreamProvider, bool const _shorten = true) const; private: langutil::SourceLocation m_location; - std::string m_source; std::string m_patch; Level m_level; diff --git a/tools/solidityUpgrade/UpgradeSuite.h b/tools/solidityUpgrade/UpgradeSuite.h index 33aeb40eb..2bde6ec0e 100644 --- a/tools/solidityUpgrade/UpgradeSuite.h +++ b/tools/solidityUpgrade/UpgradeSuite.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -37,13 +38,17 @@ namespace solidity::tools class Upgrade { public: - Upgrade(std::vector& _changes): m_changes(_changes) {} + Upgrade( + langutil::CharStreamProvider const& _charStreamProvider, + std::vector& _changes + ): m_changes(_changes), m_charStreamProvider(_charStreamProvider) {} protected: /// A reference to a suite-specific set of changes. /// It is passed to all upgrade modules and meant to collect /// reported changes. std::vector& m_changes; + langutil::CharStreamProvider const& m_charStreamProvider; }; /** @@ -53,8 +58,11 @@ protected: class AnalysisUpgrade: public Upgrade, public frontend::ASTConstVisitor { public: - AnalysisUpgrade(std::vector& _changes): - Upgrade(_changes), + AnalysisUpgrade( + langutil::CharStreamProvider const& _charStreamProvider, + std::vector& _changes + ): + Upgrade(_charStreamProvider, _changes), m_errorReporter(m_errors), m_overrideChecker(m_errorReporter) {} diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index ca15f3ccd..30d3b8876 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -27,6 +27,9 @@ #include #include +#include +#include +#include #include #include @@ -389,7 +392,9 @@ vector ProgramFactory::build(Options const& _options) variant programOrErrors = Program::load(sourceCode); if (holds_alternative(programOrErrors)) { - cerr << get(programOrErrors) << endl; + SourceReferenceFormatter{cerr, SingletonCharStreamProvider(sourceCode), true, false} + .printErrorInformation(get(programOrErrors)); + cerr << endl; assertThrow(false, InvalidProgram, "Failed to load program " + path); } diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index e01581d6a..5c61701d8 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -60,16 +60,6 @@ ostream& operator<<(ostream& _stream, Program const& _program); } -ostream& std::operator<<(ostream& _outputStream, ErrorList const& _errors) -{ - SourceReferenceFormatter formatter(_outputStream, true, false); - - for (auto const& error: _errors) - formatter.printErrorInformation(*error); - - return _outputStream; -} - Program::Program(Program const& program): m_ast(make_unique(get(ASTCopier{}(*program.m_ast)))), m_dialect{program.m_dialect}, @@ -130,7 +120,7 @@ variant, ErrorList> Program::parseObject(Dialect const& _diale { ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared(move(_source)); + auto scanner = make_shared(_source); ObjectParser parser(errorReporter, _dialect); shared_ptr object = parser.parse(scanner, false); diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index d0eb8e197..e42471d77 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -35,6 +35,7 @@ namespace solidity::langutil { class CharStream; +class Scanner; } @@ -47,13 +48,6 @@ struct CodeWeights; } -namespace std -{ - -std::ostream& operator<<(std::ostream& _outputStream, solidity::langutil::ErrorList const& _errors); - -} - namespace solidity::phaser {