diff --git a/.circleci/config.yml b/.circleci/config.yml index a73770798..48b9a537f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -56,10 +56,10 @@ jobs: paths: - boost_1_68_0 - store_artifacts: - path: build/libsolc/soljson.js + path: emscripten_build/libsolc/soljson.js destination: soljson.js - run: mkdir -p workspace - - run: cp build/libsolc/soljson.js workspace/soljson.js + - run: cp emscripten_build/libsolc/soljson.js workspace/soljson.js - run: scripts/get_version.sh > workspace/version.txt - persist_to_workspace: root: workspace @@ -202,8 +202,6 @@ jobs: - run: name: Install build dependencies command: | - brew update - brew upgrade brew unlink python brew install z3 brew install boost @@ -353,8 +351,6 @@ jobs: - run: name: Install dependencies command: | - brew update - brew upgrade brew unlink python brew install z3 - run: mkdir -p test_results diff --git a/.gitignore b/.gitignore index e0f010d34..62ddd1af0 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ prerelease.txt # Build directory build/ +emscripten_build/ docs/_build docs/utils/__pycache__ docs/utils/*.pyc diff --git a/CMakeLists.txt b/CMakeLists.txt index 05af35045..ebb4a615a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.5.4") +set(PROJECT_VERSION "0.5.5") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES CXX) option(LLL "Build LLL" OFF) diff --git a/Changelog.md b/Changelog.md index 8f533ecc0..b7ef05e25 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,43 @@ +### 0.5.5 (2019-03-05) + +Language Features: + * Add support for getters of mappings with ``string`` or ``bytes`` key types. + * Meta programming: Provide access to the name of contracts via ``type(C).name``. + + +Compiler Features: + * Support ``petersburg`` as ``evmVersion`` and set as default. + * Inline Assembly: Consider ``extcodehash`` as part of Constantinople. + * Inline Assembly: Instructions unavailable to the currently configured EVM are errors now. + * SMTChecker: Do not report underflow/overflow if they always revert. This removes false positives when using ``SafeMath``. + * Standard JSON Interface: Allow retrieving metadata without triggering bytecode generation. + * Static Analyzer: Warn about expressions with custom types when they have no effect. + * Optimizer: Add new rules with constants including ``LT``, ``GT``, ``AND`` and ``BYTE``. + * Optimizer: Add rule for shifts with constants for Constantinople. + * Optimizer: Combine multiple shifts with constant shift-by values into one. + * Optimizer: Do not mask with 160-bits after ``CREATE`` and ``CREATE2`` as they are guaranteed to return an address or 0. + * Optimizer: Support shifts in the constant optimiser for Constantinople. + * Yul Optimizer: Add rule to replace switch statements with literals by matching case body. + + +Bugfixes: + * ABIEncoderV2: Fix internal error related to bare delegatecall. + * ABIEncoderV2: Fix internal error related to ecrecover. + * ABIEncoderV2: Fix internal error related to mappings as library parameters. + * ABIEncoderV2: Fix invalid signature for events containing structs emitted in libraries. + * Inline Assembly: Proper error message for missing variables. + * Optimizer: Fix internal error related to unused tag removal across assemblies. This never generated any invalid code. + * SMTChecker: Fix crash related to statically-sized arrays. + * TypeChecker: Fix internal error and disallow index access on contracts and libraries. + * Yul: Properly detect name clashes with functions before their declaration. + * Yul: Take built-in functions into account in the compilability checker. + * Yul Optimizer: Properly take reassignments to variables in sub-expressions into account when replacing in the ExpressionSimplifier. + + +Build System: + * Soltest: Add support for left-aligned, padded hex literals. + * Soltest: Add support for right-aligned, padded boolean literals. + ### 0.5.4 (2019-02-12) Language Features: diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 2610daa28..39dc30ad7 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -1,24 +1,51 @@ -Checklist for making a release: +## Checklist for making a release: +### Requirements + - [ ] Lauchpad (Ubuntu One) account + - [ ] gnupg key (has to be version 1, gpg2 won't work) for `your-name@ethereum.org` created and uploaded + - [ ] Readthedocs account, access to the Solidity project + - [ ] Write access to https://github.com/ethereum/homebrew-ethereum + +### Pre-release - [ ] Ensure that a Github project exists for the release. - [ ] Check that all issues and pull requests from the Github project to be released are merged to ``develop``. - - [ ] Create a commit in ``develop`` that updates the ``Changelog`` to include a release date (run ``./scripts/tests.sh`` to update the bug list). Sort the changelog entries alphabetically and correct any errors you notice. + +### Changelog + - [ ] Sort the changelog entries alphabetically and correct any errors you notice. + - [ ] Create a commit on a new branch that updates the ``Changelog`` to include a release date. + - [ ] Run ``./scripts/tests.sh`` to update the bug list. - [ ] Create a pull request and wait for the tests, merge it. + +### Create the Release + - [ ] Create Github release page: https://github.com/ethereum/solidity/releases/new + - [ ] On the release page, select the ``release`` branch as new target and set tag to the new version (e.g. `v0.5.4`) (make sure you only `SAVE DRAFT` instead of `PUBLISH RELEASE` before the actual release) - [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``). - [ ] Create a pull request from ``develop`` to ``release``, wait for the tests, then merge it. - [ ] Make a final check that there are no platform-dependency issues in the ``solidity-test-bytecode`` repository. - [ ] Wait for the tests for the commit on ``release``, create a release in Github, creating the tag. - - [ ] Wait for the CI runs on the tag itself (they should push artifacts onto the Github release page). - - [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. - - [ ] Upload the source tarball (in the upload directory) to the release page. + - [ ] Wait for the CI runs on the tag itself (travis and appveyor should push artifacts onto the Github release page). + - [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. Make sure to create ``prerelease.txt`` before: (``echo -n > prerelease.txt``). This will create the tarball in a directory called ``upload``. + - [ ] Take the tarball from the upload directory (its name should be ``solidity_x.x.x.tar.gz``, otherwise ``prerelease.txt`` was missing in the step before) and upload the source tarball to the release page. + - [ ] Click the `PUBLISH RELEASE` button on the release page. + +### PPA + - [ ] Change ``scripts/release_ppa.sh`` to match your key's email and key id. - [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key). - - [ ] Once the ``~ethereum/ubuntu/ethereum-static`` PPA build is finished and published for all platforms (make sure not to do this earlier), copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``. - - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``). - - [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash) + - [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``. + - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``). + +### Homebrew + - [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb + +### Documentation - [ ] Update the default version on readthedocs. - - [ ] Make a release of ``solc-js``: Increment the version number, create a pull request for that, merge it after tests succeeded. + +### Release solc-js + - [ ] Increment the version number, create a pull request for that, merge it after tests succeeded. - [ ] Run ``npm publish`` in the updated ``solc-js`` repository. - [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry. - [ ] Merge ``release`` back into ``develop``. + +### Post-release - [ ] Announce on Twitter and Reddit. - [ ] Lean back, wait for bug reports and repeat from step 1 :) diff --git a/codecov.yml b/codecov.yml index f20980ef5..c0b72761e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -3,6 +3,10 @@ codecov: coverage: range: 70...100 status: + patch: + default: + target: "50%" + paths: "!test/" project: default: target: auto diff --git a/docs/assembly.rst b/docs/assembly.rst index b2f150ca8..953ebf48d 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -162,7 +162,6 @@ Note that the order of arguments can be seen to be reversed in non-functional st Opcodes marked with ``-`` do not push an item onto the stack (do not return a result), those marked with ``*`` are special and all others push exactly one item onto the stack (their "return value"). Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively. -Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to but not including position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 531dcd462..da2b1dc1e 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -628,5 +628,9 @@ "0.5.4": { "bugs": [], "released": "2019-02-12" + }, + "0.5.5": { + "bugs": [], + "released": "2019-03-05" } } \ No newline at end of file diff --git a/docs/contributing.rst b/docs/contributing.rst index 5768d9446..5e9308b50 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -90,7 +90,7 @@ The option ``--no-smt`` disables the tests that require ``libz3`` and ``--no-ipc`` disables those that require ``aleth``. If you want to run the ipc tests (that test the semantics of the generated code), -you need to install `aleth `_ and run it in testing mode: ``aleth --db memorydb --test -d /tmp/testeth``. +you need to install `aleth `_ and run it in testing mode: ``aleth --db memorydb --test -d /tmp/testeth``. To run the actual tests, use: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``. @@ -326,3 +326,82 @@ escaping and without iterated replacements. An area can be delimited by ``<#name by as many concatenations of its contents as there were sets of variables supplied to the template system, each time replacing any ```` items by their respective value. Top-level variables can also be used inside such areas. + +.. _documentation-style: + +Documentation Style Guide +========================= + +The following are style recommendations specifically for documentation +contributions to Solidity. + +English Language +---------------- + +Use English, with British English spelling preferred, unless using project or brand names. Try to reduce the usage of +local slang and references, making your language as clear to all readers as possible. Below are some references to help: + +* `Simplified technical English `_ +* `International English `_ +* `British English spelling `_ + + +.. note:: + + While the official Solidity documentation is written in English, there are community contributed :ref:`translations` + in other languages available. + +Title Case for Headings +----------------------- + +Use `title case `_ for headings. This means capitalise all principal words in +titles, but not articles, conjunctions, and prepositions unless they start the +title. + +For example, the following are all correct: + +* Title Case for Headings +* For Headings Use Title Case +* Local and State Variable Names +* Order of Layout + +Expand Contractions +------------------- + +Use expanded contractions for words, for example: + +* "Do not" instead of "Don't". +* "Can not" instead of "Can't". + +Active and Passive Voice +------------------------ + +Active voice is typically recommended for tutorial style documentation as it +helps the reader understand who or what is performing a task. However, as the +Solidity documentation is a mixture of tutorials and reference content, passive +voice is sometimes more applicable. + +As a summary: + +* Use passive voice for technical reference, for example language definition and internals of the Ethereum VM. +* Use active voice when describing recommendations on how to apply an aspect of Solidity. + +For example, the below is in passive voice as it specifies an aspect of Solidity: + + Functions can be declared ``pure`` in which case they promise not to read + from or modify the state. + +For example, the below is in active voice as it discusses an application of Solidity: + + When invoking the compiler, you can specify how to discover the first element + of a path, and also path prefix remappings. + +Common Terms +------------ + +* "Function parameters" and "return variables", not input and output parameters. + +Code Examples +------------- + +* Ensure that all code examples begin with a ``pragma`` version that spans the largest where the contract code is valid. For example ``pragma solidity >=0.4.0 <0.6.0;``. \ No newline at end of file diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 45ba190ec..6e3c64d50 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -91,7 +91,7 @@ You need to use the modifier ``payable`` with the ``info`` function because otherwise, the ``.value()`` option would not be available. .. warning:: - Be careful that ``feed.info.value(10).gas(800)`` only locally sets the ``value`` and amount of ``gas`` sent with the function call, and the parentheses at the end perform the actual call. So in this case, the function is not called. + Be careful that ``feed.info.value(10).gas(800)`` only locally sets the ``value`` and amount of ``gas`` sent with the function call, and the parentheses at the end perform the actual call. So in this case, the function is not called and the ``value`` and ``gas`` settings are lost. Function calls cause exceptions if the called contract does not exist (in the sense that the account does not contain code) or if the called contract itself diff --git a/docs/examples/modular.rst b/docs/examples/modular.rst new file mode 100644 index 000000000..b9bd92d9c --- /dev/null +++ b/docs/examples/modular.rst @@ -0,0 +1,57 @@ +.. index:: contract;modular, modular contract + +***************** +Modular Contracts +***************** + +A modular approach to building your contracts helps you prevent overflow risks +and unexpected tokens. In the example below, the contract uses the ``move`` method +of the ``Balances`` :ref:`library ` to check that balances sent between +addresses match what you expect. + +:: + + pragma solidity >=0.4.22 <0.6.0; + + library Balances { + function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal { + require(balances[from] >= amount); + require(balances[to] + amount >= balances[to]); + balances[from] -= amount; + balances[to] += amount; + } + } + + contract Token { + mapping(address => uint256) balances; + using Balances for *; + mapping(address => mapping (address => uint256)) allowed; + + event Transfer(address from, address to, uint amount); + event Approval(address owner, address spender, uint amount); + + function balanceOf(address tokenOwner) public view returns (uint balance) { + return balances[tokenOwner]; + } + function transfer(address to, uint amount) public returns (bool success) { + balances.move(msg.sender, to, amount); + emit Transfer(msg.sender, to, amount); + return true; + + } + + function transferFrom(address from, address to, uint amount) public returns (bool success) { + require(allowed[from][msg.sender] >= amount); + allowed[from][msg.sender] -= amount; + balances.move(from, to, amount); + emit Transfer(from, to, amount); + return true; + } + + function approve(address spender, uint tokens) public returns (bool success) { + require(allowed[msg.sender][spender] == 0, ""); + allowed[msg.sender][spender] = tokens; + emit Approval(msg.sender, spender, tokens); + return true; + } + } diff --git a/docs/index.rst b/docs/index.rst index bdd466704..0ec500e7f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,7 +19,7 @@ user-defined types among other features. With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions, and multi-signature wallets. -When deploying contracts, you should use the latest released version of Solidity. This is because breaking changes as well as new features and bug fixes are introduced regularly. We currently use a 0.x version number [to indicate this fast pace of change](https://semver.org/#spec-item-4). +When deploying contracts, you should use the latest released version of Solidity. This is because breaking changes as well as new features and bug fixes are introduced regularly. We currently use a 0.x version number `to indicate this fast pace of change `_. Language Documentation ---------------------- @@ -52,6 +52,8 @@ If you have any questions, you can try searching for answers or asking on the Ideas for improving Solidity or this documentation are always welcome, read our :doc:`contributors guide ` for more details. +.. _translations: + Translations ------------ diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index c475d4276..375130b88 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -154,23 +154,35 @@ Gentoo Linux also provides a solidity package that can be installed using ``emer Building from Source ==================== -Prerequisites - Linux ---------------------- +Prerequisites - All Operating Systems +------------------------------------- -You need to install the following dependencies for Linux builds of Solidity: +The following are dependencies for all builds of Solidity: +-----------------------------------+-------------------------------------------------------+ | Software | Notes | +===================================+=======================================================+ -| `Git for Linux`_ | Command-line tool for retrieving source from Github. | +| `CMake`_ | Cross-platform build file generator. | ++-----------------------------------+-------------------------------------------------------+ +| `Boost`_ (version 1.65+) | C++ libraries. | ++-----------------------------------+-------------------------------------------------------+ +| `Git`_ | Command-line tool for retrieving source code. | ++-----------------------------------+-------------------------------------------------------+ +| `z3`_ (version 5.6+, Optional) | For use with SMT checker. | ++-----------------------------------+-------------------------------------------------------+ +| `cvc4`_ (Optional) | For use with SMT checker. | +-----------------------------------+-------------------------------------------------------+ -.. _Git for Linux: https://git-scm.com/download/linux +.. _cvc4: http://cvc4.cs.stanford.edu/web/ +.. _Git: https://git-scm.com/download +.. _Boost: https://www.boost.org +.. _CMake: https://cmake.org/download/ +.. _z3: https://github.com/Z3Prover/z3 Prerequisites - macOS --------------------- -For macOS, ensure that you have the latest version of +For macOS builds, ensure that you have the latest version of `Xcode installed `_. This contains the `Clang C++ compiler `_, the `Xcode IDE `_ and other Apple development @@ -183,13 +195,12 @@ command-line builds: sudo xcodebuild -license accept -Our OS X builds require you to `install the Homebrew `_ +Our OS X build script uses `the Homebrew `_ package manager for installing external dependencies. Here's how to `uninstall Homebrew `_, if you ever want to start again from scratch. - Prerequisites - Windows ----------------------- @@ -198,21 +209,17 @@ You need to install the following dependencies for Windows builds of Solidity: +-----------------------------------+-------------------------------------------------------+ | Software | Notes | +===================================+=======================================================+ -| `Git for Windows`_ | Command-line tool for retrieving source from Github. | -+-----------------------------------+-------------------------------------------------------+ -| `CMake`_ | Cross-platform build file generator. | -+-----------------------------------+-------------------------------------------------------+ | `Visual Studio 2017 Build Tools`_ | C++ compiler | +-----------------------------------+-------------------------------------------------------+ | `Visual Studio 2017`_ (Optional) | C++ compiler and dev environment. | +-----------------------------------+-------------------------------------------------------+ -If you've already had one IDE and only need compiler and libraries, +If you already have one IDE and only need the compiler and libraries, you could install Visual Studio 2017 Build Tools. Visual Studio 2017 provides both IDE and necessary compiler and libraries. So if you have not got an IDE and prefer to develop solidity, Visual Studio 2017 -may be an choice for you to get everything setup easily. +may be a choice for you to get everything setup easily. Here is the list of components that should be installed in Visual Studio 2017 Build Tools or Visual Studio 2017: @@ -223,11 +230,25 @@ in Visual Studio 2017 Build Tools or Visual Studio 2017: * Windows 8.1 SDK * C++/CLI support -.. _Git for Windows: https://git-scm.com/download/win -.. _CMake: https://cmake.org/download/ .. _Visual Studio 2017: https://www.visualstudio.com/vs/ .. _Visual Studio 2017 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017 +Dependencies Helper Script +-------------------------- + +We have a helper script which you can use to install all required external dependencies +on macOS, Windows and on numerous Linux distros. + +.. code-block:: bash + + ./scripts/install_deps.sh + +Or, on Windows: + +.. code-block:: bat + + scripts\install_deps.bat + Clone the Repository -------------------- @@ -245,23 +266,6 @@ you should fork Solidity and add your personal fork as a second remote: git remote add personal git@github.com:[username]/solidity.git -External Dependencies ---------------------- - -We have a helper script which installs all required external dependencies -on macOS, Windows and on numerous Linux distros. - -.. code-block:: bash - - ./scripts/install_deps.sh - -Or, on Windows: - -.. code-block:: bat - - scripts\install_deps.bat - - Command-Line Build ------------------ @@ -278,7 +282,11 @@ Building Solidity is quite similar on Linux, macOS and other Unices: cd build cmake .. && make -or even easier: +.. warning:: + + BSD builds should work, but are untested by the Solidity team. + +or even easier on Linux and macOS, you can run: .. code-block:: bash diff --git a/docs/metadata.rst b/docs/metadata.rst index c0613809a..0e6282ddb 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -66,10 +66,23 @@ explanatory purposes. { // Required for Solidity: Sorted list of remappings remappings: [ ":g/dir" ], - // Optional: Optimizer settings (enabled defaults to false) + // Optional: Optimizer settings. The fields "enabled" and "runs" are deprecated + // and are only given for backwards-compatibility. optimizer: { enabled: true, - runs: 500 + runs: 500, + details: { + // peephole defaults to "true" + peephole: true, + // jumpdestRemover defaults to "true" + jumpdestRemover: true, + orderLiterals: false, + deduplicate: false, + cse: false, + constantOptimizer: false, + yul: false, + yulDetails: {} + } }, // Required for Solidity: File and name of the contract or library this // metadata is created for. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 55409678d..a57aa2884 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -385,6 +385,7 @@ Global Variables - ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +- ``type(C).name`` (``string``): the name of the contract - ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information`. - ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information`. diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 3e54ec28b..4b0f3e13f 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -3,8 +3,11 @@ Solidity by Example ################### .. include:: examples/voting.rst + .. include:: examples/blind-auction.rst .. include:: examples/safe-remote.rst -.. include:: examples/micropayment.rst \ No newline at end of file +.. include:: examples/micropayment.rst + +.. include:: examples/modular.rst \ No newline at end of file diff --git a/docs/style-guide.rst b/docs/style-guide.rst index bf1be93ef..dcbfc486a 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -1088,12 +1088,6 @@ Avoiding Naming Collisions This convention is suggested when the desired name collides with that of a built-in or otherwise reserved name. - -General Recommendations -======================= - -TODO - .. _natspec: ******* diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index c4eeb433d..039e605e2 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -305,8 +305,7 @@ Contract Types Every :ref:`contract` defines its own type. You can implicitly convert contracts to contracts they inherit from. -Contracts can be explicitly converted to and from all other contract types -and the ``address`` type. +Contracts can be explicitly converted to and from the ``address`` type. Explicit conversion to and from the ``address payable`` type is only possible if the contract type has a payable fallback function. @@ -614,15 +613,23 @@ just use ``f``, if you want to use its external form, use ``this.f``. Members: -Public (or external) functions also have a special member called ``selector``, -which returns the :ref:`ABI function selector `:: +Public (or external) functions have the following members: + +* ``.selector`` returns the :ref:`ABI function selector ` +* ``.gas(uint)`` returns a callable function object which, when called, will send the specified amount of gas to the target function. See :ref:`External Function Calls ` for more information. +* ``.value(uint)`` returns a callable function object which, when called, will send the specified amount of wei to the target function. See :ref:`External Function Calls ` for more information. + +Example that shows how to use the members:: pragma solidity >=0.4.16 <0.6.0; - contract Selector { - function f() public pure returns (bytes4) { + contract Example { + function f() public payable returns (bytes4) { return this.f.selector; } + function g() public { + this.f.gas(10).value(800)(); + } } Example that shows how to use internal function types:: diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 53b9fb5ae..6c80fb0df 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -156,29 +156,41 @@ Mathematical and Cryptographic Functions ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. + ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. + ``keccak256(bytes memory) returns (bytes32)``: compute the Keccak-256 hash of the input + ``sha256(bytes memory) returns (bytes32)``: compute the SHA-256 hash of the input + ``ripemd160(bytes memory) returns (bytes20)``: compute RIPEMD-160 hash of the input + ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: - recover the address associated with the public key from elliptic curve signature or return zero on error - (`example usage `_) + recover the address associated with the public key from elliptic curve signature or return zero on error. + The function parameters correspond to ECDSA values of the signature: + + ``r`` = first 32 bytes of signature + ``s`` = second 32 bytes of signature + ``v`` = final 1 byte of signature + + ``ecrecover`` returns an ``address``, and not an ``address payable``. See :ref:`address payable
` for + conversion, in case you need to transfer funds to the recovered address. + + For further details, read `example usage `_. .. note:: - Function ``ecrecover`` returns an ``address``, and not an ``address - payable``. See :ref:`address payable
` for conversion, in case you need - to transfer funds to the recovered address. -It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net. + When running ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*, you might encounter Out-of-Gas. This is because these functions are implemented as "precompiled contracts" and only really exist after they receive the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution might run into an Out-of-Gas error. A workaround for this problem is to first send Wei (1 for example) to each of the contracts before you use them in your actual contracts. This is not an issue on the main or test net. .. note:: There used to be an alias for ``keccak256`` called ``sha3``, which was removed in version 0.5.0. .. index:: balance, send, transfer, call, callcode, delegatecall, staticcall + .. _address_related: Members of Address Types @@ -257,6 +269,9 @@ type ``X``. Currently, there is limited support for this feature, but it might be expanded in the future. The following properties are available for a contract type ``C``: +``type(C).name``: + The name of the contract. + ``type(C).creationCode``: Memory byte array that contains the creation bytecode of the contract. This can be used in inline assembly to build custom creation routines, diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index b5f79b66b..f4b5aceec 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -17,10 +17,15 @@ Using ``solc --help`` provides you with an explanation of all options. The compi If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``. Before you deploy your contract, activate the optimizer when compiling using ``solc --optimize --bin sourceFile.sol``. -By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime. +By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime +(more specifically, it assumes each opcode is executed around 200 times). If you want the initial contract deployment to be cheaper and the later function executions to be more expensive, set it to ``--optimize-runs=1``. If you expect many transactions and do not care for higher deployment cost and 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 commandline compiler will automatically read imported files from the filesystem, but it is also possible to provide path redirects using ``prefix=path`` in the following way: @@ -103,18 +108,21 @@ at each version. Backward compatibility is not guaranteed between each version. - ``homestead`` (oldest version) - ``tangerineWhistle`` - - gas cost for access to other accounts increased, relevant for gas estimation and the optimizer. - - all gas sent by default for external calls, previously a certain amount had to be retained. + - Gas cost for access to other accounts increased, relevant for gas estimation and the optimizer. + - All gas sent by default for external calls, previously a certain amount had to be retained. - ``spuriousDragon`` - - gas cost for the ``exp`` opcode increased, relevant for gas estimation and the optimizer. -- ``byzantium`` (**default**) - - opcodes ``returndatacopy``, ``returndatasize`` and ``staticcall`` are available in assembly. - - the ``staticcall`` opcode is used when calling non-library view or pure functions, which prevents the functions from modifying state at the EVM level, i.e., even applies when you use invalid type conversions. - - it is possible to access dynamic data returned from function calls. + - Gas cost for the ``exp`` opcode increased, relevant for gas estimation and the optimizer. +- ``byzantium`` + - Opcodes ``returndatacopy``, ``returndatasize`` and ``staticcall`` are available in assembly. + - The ``staticcall`` opcode is used when calling non-library view or pure functions, which prevents the functions from modifying state at the EVM level, i.e., even applies when you use invalid type conversions. + - It is possible to access dynamic data returned from function calls. - ``revert`` opcode introduced, which means that ``revert()`` will not waste gas. -- ``constantinople`` (still in progress) - - opcodes ``shl``, ``shr`` and ``sar`` are available in assembly. - - shifting operators use shifting opcodes and thus need less gas. +- ``constantinople`` + - Opcodes ``create2`, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly. + - Shifting operators use shifting opcodes and thus need less gas. +- ``petersburg`` (**default**) + - The compiler behaves the same way as with constantinople. + .. _compiler-api: @@ -186,9 +194,33 @@ Input Description "enabled": true, // Optimize for how many times you intend to run the code. // Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage. - "runs": 200 + "runs": 200, + // Switch optimizer components on or off in detail. + // The "enabled" switch above provides two defaults which can be + // tweaked here. If "details" is given, "enabled" can be omitted. + "details": { + // The peephole optimizer is always on if no details are given, use details to switch it off. + "peephole": true, + // The unused jumpdest remover is always on if no details are given, use details to switch it off. + "jumpdestRemover": true, + // Sometimes re-orders literals in commutative operations. + "orderLiterals": false, + // Removes duplicate code blocks + "deduplicate": false, + // Common subexpression elimination, this is the most complicated step but + // can also provide the largest gain. + "cse": false, + // Optimize representation of literal numbers and strings in code. + "constantOptimizer": false, + // The new Yul optimizer. Mostly operates on the code of ABIEncoderV2. + // It can only be activated through the details here. + // This feature is still considered experimental. + "yul": false, + // Future tuning options, currently unused. + "yulDetails": {} + } }, - "evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople + "evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg // Metadata settings (optional) "metadata": { // Use only literal content and not URLs (false by default) diff --git a/libdevcore/Common.h b/libdevcore/Common.h index e02cf7fa8..35574909c 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -74,7 +74,7 @@ using strings = std::vector; /// Interprets @a _u as a two's complement signed number and returns the resulting s256. inline s256 u2s(u256 _u) { - static const bigint c_end = bigint(1) << 256; + static bigint const c_end = bigint(1) << 256; if (boost::multiprecision::bit_test(_u, 255)) return s256(-(c_end - _u)); else @@ -84,10 +84,10 @@ inline s256 u2s(u256 _u) /// @returns the two's complement signed representation of the signed number _u. inline u256 s2u(s256 _u) { - static const bigint c_end = bigint(1) << 256; - if (_u >= 0) + static bigint const c_end = bigint(1) << 256; + if (_u >= 0) return u256(_u); - else + else return u256(c_end + _u); } diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index adf9d70ce..c157583a2 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -140,6 +140,12 @@ inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0) return (_min || _val) ? bytes{ _val } : bytes{}; } +/// Workarounds shift left bug in boost <1.65.1. +template S bigintShiftLeftWorkaround(S const& _a, unsigned _b) +{ + return (S)(bigint(_a) << _b); +} + /// Convenience function for conversion of a u256 to hex inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) { @@ -175,9 +181,6 @@ inline std::string toCompactHexWithPrefix(u256 val) // Algorithms for string and string-like collections. -/// Escapes a string into the C-string representation. -/// @p _all if true will escape all characters, not just the unprintable ones. -std::string escaped(std::string const& _s, bool _all = true); /// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero. template inline unsigned bytesRequired(T _i) @@ -240,7 +243,7 @@ bool contains(T const& _t, V const& _v) /// place at the end, but already visited elements might be invalidated. /// If nothing is replaced, no copy is performed. template -void iterateReplacing(std::vector& _vector, const F& _f) +void iterateReplacing(std::vector& _vector, F const& _f) { // Concept: _f must be Callable, must accept param T&, must return optional> bool useModified = false; diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index cfe72fbf6..dc4cadd36 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -31,7 +31,7 @@ namespace dev /// Base class for all exceptions. struct Exception: virtual std::exception, virtual boost::exception { - const char* what() const noexcept override; + char const* what() const noexcept override; /// @returns "FileName:LineNumber" referring to the point where the exception was thrown. std::string lineInfo() const; diff --git a/libdevcore/Keccak256.cpp b/libdevcore/Keccak256.cpp index 7933fc7e0..752973822 100644 --- a/libdevcore/Keccak256.cpp +++ b/libdevcore/Keccak256.cpp @@ -47,17 +47,17 @@ namespace /******** The Keccak-f[1600] permutation ********/ /*** Constants. ***/ -static const uint8_t rho[24] = \ +static uint8_t const rho[24] = \ { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44}; -static const uint8_t pi[24] = \ +static uint8_t const pi[24] = \ {10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1}; -static const uint64_t RC[24] = \ +static uint64_t const RC[24] = \ {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, @@ -71,8 +71,8 @@ static const uint64_t RC[24] = \ #define REPEAT24(e) REPEAT6(e e e e) #define REPEAT5(e) e e e e e #define FOR5(v, s, e) \ - v = 0; \ - REPEAT5(e; v += s;) + v = 0; \ + REPEAT5(e; v += s;) /*** Keccak-f[1600] ***/ static inline void keccakf(void* state) { @@ -84,25 +84,25 @@ static inline void keccakf(void* state) { uint8_t x, y; // Theta FOR5(x, 1, - b[x] = 0; - FOR5(y, 5, - b[x] ^= a[x + y]; )) + b[x] = 0; + FOR5(y, 5, + b[x] ^= a[x + y]; )) FOR5(x, 1, - FOR5(y, 5, - a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) + FOR5(y, 5, + a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) // Rho and pi uint64_t t = a[1]; x = 0; REPEAT24(b[0] = a[pi[x]]; - a[pi[x]] = rol(t, rho[x]); - t = b[0]; - x++; ) + a[pi[x]] = rol(t, rho[x]); + t = b[0]; + x++; ) // Chi FOR5(y, - 5, - FOR5(x, 1, + 5, + FOR5(x, 1, b[x] = a[y + x];) - FOR5(x, 1, + FOR5(x, 1, a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) // Iota a[0] ^= RC[i]; @@ -115,19 +115,19 @@ static inline void keccakf(void* state) { #define _(S) do { S } while (0) #define FOR(i, ST, L, S) \ - _(for (size_t i = 0; i < L; i += ST) { S; }) + _(for (size_t i = 0; i < L; i += ST) { S; }) #define mkapply_ds(NAME, S) \ - static inline void NAME(uint8_t* dst, \ - const uint8_t* src, \ - size_t len) { \ - FOR(i, 1, len, S); \ - } + static inline void NAME(uint8_t* dst, \ + uint8_t const* src, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } #define mkapply_sd(NAME, S) \ - static inline void NAME(const uint8_t* src, \ - uint8_t* dst, \ - size_t len) { \ - FOR(i, 1, len, S); \ - } + static inline void NAME(uint8_t const* src, \ + uint8_t* dst, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } mkapply_ds(xorin, dst[i] ^= src[i]) // xorin mkapply_sd(setout, dst[i] = src[i]) // setout @@ -137,18 +137,18 @@ mkapply_sd(setout, dst[i] = src[i]) // setout // Fold P*F over the full blocks of an input. #define foldP(I, L, F) \ - while (L >= rate) { \ - F(a, I, rate); \ - P(a); \ - I += rate; \ - L -= rate; \ - } + while (L >= rate) { \ + F(a, I, rate); \ + P(a); \ + I += rate; \ + L -= rate; \ + } /** The sponge-based hash construction. **/ inline void hash( uint8_t* out, size_t outlen, - const uint8_t* in, + uint8_t const* in, size_t inlen, size_t rate, uint8_t delim diff --git a/libdevcore/StringUtils.h b/libdevcore/StringUtils.h index 64044cb2b..16c2fb158 100644 --- a/libdevcore/StringUtils.h +++ b/libdevcore/StringUtils.h @@ -139,8 +139,8 @@ inline std::string formatNumberReadable( if (len < 24) return str; - const int initialChars = (prefix == HexPrefix::Add) ? 6 : 4; - const int finalChars = 4; + int const initialChars = (prefix == HexPrefix::Add) ? 6 : 4; + int const finalChars = 4; int numSkipped = len - initialChars - finalChars; return str.substr(0, initialChars) + diff --git a/libdevcore/UTF8.cpp b/libdevcore/UTF8.cpp index 2ae720ec4..c61fea8f7 100644 --- a/libdevcore/UTF8.cpp +++ b/libdevcore/UTF8.cpp @@ -77,7 +77,7 @@ bool isWellFormed(unsigned char byte1, unsigned char byte2) return false; } -bool validateUTF8(const unsigned char *_input, size_t _length, size_t& _invalidPosition) +bool validateUTF8(unsigned char const* _input, size_t _length, size_t& _invalidPosition) { bool valid = true; size_t i = 0; diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 7cf7137d2..6b9ece01e 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -390,7 +390,7 @@ Assembly& Assembly::optimise(OptimiserSettings const& _settings) map Assembly::optimiseInternal( OptimiserSettings const& _settings, - std::set const& _tagsReferencedFromOutside + std::set _tagsReferencedFromOutside ) { // Run optimisation for sub-assemblies. @@ -436,7 +436,22 @@ map Assembly::optimiseInternal( BlockDeduplicator dedup{m_items}; if (dedup.deduplicate()) { - tagReplacements.insert(dedup.replacedTags().begin(), dedup.replacedTags().end()); + for (auto const& replacement: dedup.replacedTags()) + { + assertThrow( + replacement.first <= size_t(-1) && replacement.second <= size_t(-1), + OptimizerException, + "Invalid tag replacement." + ); + assertThrow( + !tagReplacements.count(replacement.first), + OptimizerException, + "Replacement already known." + ); + tagReplacements[replacement.first] = replacement.second; + if (_tagsReferencedFromOutside.erase(size_t(replacement.first))) + _tagsReferencedFromOutside.insert(size_t(replacement.second)); + } count++; } } diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 1718fbaef..a53c92420 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -107,13 +107,14 @@ public: bool runDeduplicate = false; bool runCSE = false; bool runConstantOptimiser = false; - solidity::EVMVersion evmVersion; + langutil::EVMVersion evmVersion; /// This specifies an estimate on how often each opcode in this assembly will be executed, /// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage. size_t expectedExecutionsPerDeployment = 200; }; - /// Execute optimisation passes as defined by @a _settings and return the optimised assembly. + /// Modify and return the current assembly such that creation and execution gas usage + /// is optimised according to the settings in @a _settings. Assembly& optimise(OptimiserSettings const& _settings); /// Modify (if @a _enable is set) and return the current assembly such that creation and @@ -121,7 +122,7 @@ public: /// @a _runs specifes an estimate on how often each opcode in this assembly will be executed, /// i.e. use a small value to optimise for size and a large value to optimise for runtime. /// If @a _enable is not set, will perform some simple peephole optimizations. - Assembly& optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation = true, size_t _runs = 200); + Assembly& optimise(bool _enable, langutil::EVMVersion _evmVersion, bool _isCreation, size_t _runs); /// Create a text representation of the assembly. std::string assemblyString( @@ -154,7 +155,7 @@ protected: /// Does the same operations as @a optimise, but should only be applied to a sub and /// returns the replaced tags. Also takes an argument containing the tags of this assembly /// that are referenced in a super-assembly. - std::map optimiseInternal(OptimiserSettings const& _settings, std::set const& _tagsReferencedFromOutside); + std::map optimiseInternal(OptimiserSettings const& _settings, std::set _tagsReferencedFromOutside); unsigned bytesRequired(unsigned subTagSize) const; diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index eba25db08..cddc928aa 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -149,7 +149,7 @@ private: /// Appends the given assembly item. void appendItem(AssemblyItem const& _item); - static const int c_invalidPosition = -0x7fffffff; + static int const c_invalidPosition = -0x7fffffff; AssemblyItems m_generatedItems; /// Current height of the stack relative to the start. @@ -161,7 +161,7 @@ private: /// Current positions of equivalence classes, equal to the empty set if already deleted. std::map> m_classPositions; - /// The actual eqivalence class items and how to compute them. + /// The actual equivalence class items and how to compute them. ExpressionClasses& m_expressionClasses; /// Keeps information about which storage or memory slots were written to by which operations. /// The operations are sorted ascendingly by sequence number. diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 7e9616321..267daa5e5 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -29,7 +29,7 @@ using namespace dev::eth; unsigned ConstantOptimisationMethod::optimiseConstants( bool _isCreation, size_t _runs, - solidity::EVMVersion _evmVersion, + langutil::EVMVersion _evmVersion, Assembly& _assembly ) { @@ -210,7 +210,10 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value) AssemblyItems newRoutine; if (lowerPart != 0) newRoutine += findRepresentation(u256(abs(lowerPart))); - newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP}; + if (m_params.evmVersion.hasBitwiseShifting()) + newRoutine += AssemblyItems{u256(1), u256(bits), Instruction::SHL}; + else + newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP}; if (upperPart != 1) newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL}; if (lowerPart > 0) @@ -231,7 +234,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value) } } -bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& _routine) +bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& _routine) const { // This is a tiny EVM that can only evaluate some instructions. vector stack; @@ -263,6 +266,24 @@ bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& case Instruction::NOT: sp[0] = ~sp[0]; break; + case Instruction::SHL: + assertThrow( + m_params.evmVersion.hasBitwiseShifting(), + OptimizerException, + "Shift generated for invalid EVM version." + ); + assertThrow(sp[0] <= u256(255), OptimizerException, "Invalid shift generated."); + sp[-1] = u256(bigint(sp[-1]) << unsigned(sp[0])); + break; + case Instruction::SHR: + assertThrow( + m_params.evmVersion.hasBitwiseShifting(), + OptimizerException, + "Shift generated for invalid EVM version." + ); + assertThrow(sp[0] <= u256(255), OptimizerException, "Invalid shift generated."); + sp[-1] = sp[-1] >> unsigned(sp[0]); + break; default: return false; } diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index bd87c4dce..be1628c71 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -52,7 +52,7 @@ public: static unsigned optimiseConstants( bool _isCreation, size_t _runs, - solidity::EVMVersion _evmVersion, + langutil::EVMVersion _evmVersion, Assembly& _assembly ); @@ -64,7 +64,7 @@ protected: bool isCreation; ///< Whether this is called during contract creation or runtime. size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract. size_t multiplicity; ///< Number of times the constant appears in the code. - solidity::EVMVersion evmVersion; ///< Version of the EVM + langutil::EVMVersion evmVersion; ///< Version of the EVM }; explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value): @@ -155,7 +155,7 @@ protected: /// Tries to recursively find a way to compute @a _value. AssemblyItems findRepresentation(u256 const& _value); /// Recomputes the value from the calculated representation and checks for correctness. - static bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine); + bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine) const; bigint gasNeeded(AssemblyItems const& _routine) const; /// Counter for the complexity of optimization, will stop when it reaches zero. diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index e3909e86c..f988138c0 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -47,24 +47,24 @@ namespace GasCosts static unsigned const tier5Gas = 10; static unsigned const tier6Gas = 20; static unsigned const tier7Gas = 0; - inline unsigned extCodeGas(EVMVersion _evmVersion) + inline unsigned extCodeGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 20; + return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 700 : 20; } - inline unsigned balanceGas(EVMVersion _evmVersion) + inline unsigned balanceGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= EVMVersion::tangerineWhistle() ? 400 : 20; + return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 400 : 20; } static unsigned const expGas = 10; - inline unsigned expByteGas(EVMVersion _evmVersion) + inline unsigned expByteGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= EVMVersion::spuriousDragon() ? 50 : 10; + return _evmVersion >= langutil::EVMVersion::spuriousDragon() ? 50 : 10; } static unsigned const keccak256Gas = 30; static unsigned const keccak256WordGas = 6; - inline unsigned sloadGas(EVMVersion _evmVersion) + inline unsigned sloadGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= EVMVersion::tangerineWhistle() ? 200 : 50; + return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 200 : 50; } static unsigned const sstoreSetGas = 20000; static unsigned const sstoreResetGas = 5000; @@ -74,16 +74,16 @@ namespace GasCosts static unsigned const logDataGas = 8; static unsigned const logTopicGas = 375; static unsigned const createGas = 32000; - inline unsigned callGas(EVMVersion _evmVersion) + inline unsigned callGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 40; + return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 700 : 40; } static unsigned const callStipend = 2300; static unsigned const callValueTransferGas = 9000; static unsigned const callNewAccountGas = 25000; - inline unsigned selfdestructGas(EVMVersion _evmVersion) + inline unsigned selfdestructGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= EVMVersion::tangerineWhistle() ? 5000 : 0; + return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 5000 : 0; } static unsigned const selfdestructRefundGas = 24000; static unsigned const memoryGas = 3; @@ -122,7 +122,7 @@ public: }; /// Constructs a new gas meter given the current state. - GasMeter(std::shared_ptr const& _state, solidity::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0): + GasMeter(std::shared_ptr const& _state, langutil::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0): m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {} /// @returns an upper bound on the gas consumed by the given instruction and updates @@ -152,7 +152,7 @@ private: GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize); std::shared_ptr m_state; - EVMVersion m_evmVersion; + langutil::EVMVersion m_evmVersion; /// Largest point where memory was accessed since the creation of this object. u256 m_largestMemoryAccess; }; diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index cf98c9384..b3ee0fe50 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -29,7 +29,7 @@ using namespace std; using namespace dev; using namespace dev::solidity; -const std::map dev::solidity::c_instructions = +std::map const dev::solidity::c_instructions = { { "STOP", Instruction::STOP }, { "ADD", Instruction::ADD }, @@ -173,7 +173,7 @@ const std::map dev::solidity::c_instructions = { "SELFDESTRUCT", Instruction::SELFDESTRUCT } }; -static const std::map c_instructionInfo = +static std::map const c_instructionInfo = { // Add, Args, Ret, SideEffects, GasPriceTier { Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::Zero } }, { Instruction::ADD, { "ADD", 0, 2, 1, false, Tier::VeryLow } }, diff --git a/libevmasm/PathGasMeter.cpp b/libevmasm/PathGasMeter.cpp index cdadba768..0b199806c 100644 --- a/libevmasm/PathGasMeter.cpp +++ b/libevmasm/PathGasMeter.cpp @@ -27,7 +27,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -PathGasMeter::PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion): +PathGasMeter::PathGasMeter(AssemblyItems const& _items, langutil::EVMVersion _evmVersion): m_items(_items), m_evmVersion(_evmVersion) { for (size_t i = 0; i < m_items.size(); ++i) diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h index 772df4844..67b607c42 100644 --- a/libevmasm/PathGasMeter.h +++ b/libevmasm/PathGasMeter.h @@ -53,13 +53,13 @@ struct GasPath class PathGasMeter { public: - explicit PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion); + explicit PathGasMeter(AssemblyItems const& _items, langutil::EVMVersion _evmVersion); GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr const& _state); static GasMeter::GasConsumption estimateMax( AssemblyItems const& _items, - solidity::EVMVersion _evmVersion, + langutil::EVMVersion _evmVersion, size_t _startIndex, std::shared_ptr const& _state ) @@ -81,7 +81,7 @@ private: std::map m_highestGasUsagePerJumpdest; std::map m_tagPositions; AssemblyItems const& m_items; - solidity::EVMVersion m_evmVersion; + langutil::EVMVersion m_evmVersion; }; } diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 01e9b984e..046633d17 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -24,6 +24,8 @@ #include #include +#include + #include #include @@ -85,12 +87,12 @@ std::vector> simplificationRuleListPart1( return B.d(); unsigned testBit = unsigned(A.d()) * 8 + 7; u256 mask = (u256(1) << testBit) - 1; - return u256(boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask); + return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask; }, false}, {{Instruction::SHL, {A, B}}, [=]{ if (A.d() > 255) return u256(0); - return u256(bigint(B.d()) << unsigned(A.d())); + return bigintShiftLeftWorkaround(B.d(), unsigned(A.d())); }, false}, {{Instruction::SHR, {A, B}}, [=]{ if (A.d() > 255) @@ -106,7 +108,7 @@ std::vector> simplificationRuleListPart2( Pattern, Pattern, Pattern X, - Pattern + Pattern Y ) { return std::vector> { @@ -140,6 +142,16 @@ std::vector> simplificationRuleListPart2( {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true}, {{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, {{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, + {{Instruction::SHL, {0, X}}, [=]{ return X; }, false}, + {{Instruction::SHR, {0, X}}, [=]{ return X; }, false}, + {{Instruction::SHL, {X, 0}}, [=]{ return u256(0); }, true}, + {{Instruction::SHR, {X, 0}}, [=]{ return u256(0); }, true}, + {{Instruction::LT, {X, 0}}, [=]{ return u256(0); }, true}, + {{Instruction::GT, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false}, + {{Instruction::GT, {X, ~u256(0)}}, [=]{ return u256(0); }, true}, + {{Instruction::GT, {0, X}}, [=]{ return u256(0); }, true}, + {{Instruction::AND, {{Instruction::BYTE, {X, Y}}, {u256(0xff)}}}, [=]() -> Pattern { return {Instruction::BYTE, {X, Y}}; }, false}, + {{Instruction::BYTE, {X, 31}}, [=]() -> Pattern { return {Instruction::AND, {X, u256(0xff)}}; }, false} }; } @@ -225,7 +237,9 @@ std::vector> simplificationRuleListPart5( Instruction::ADDRESS, Instruction::CALLER, Instruction::ORIGIN, - Instruction::COINBASE + Instruction::COINBASE, + Instruction::CREATE, + Instruction::CREATE2 }) { u256 const mask = (u256(1) << 160) - 1; @@ -332,6 +346,20 @@ std::vector> simplificationRuleListPart7( } } + rules.push_back({ + // SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X) + {Instruction::SHL, {{B}, {Instruction::SHL, {{A}, {X}}}}}, + [=]() -> Pattern { return {Instruction::SHL, {std::min(A.d() + B.d(), u256(256)), X}}; }, + false + }); + + rules.push_back({ + // SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X) + {Instruction::SHR, {{B}, {Instruction::SHR, {{A}, {X}}}}}, + [=]() -> Pattern { return {Instruction::SHR, {std::min(A.d() + B.d(), u256(256)), X}}; }, + false + }); + return rules; } diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt index 4539376f0..1c3d5a05e 100644 --- a/liblangutil/CMakeLists.txt +++ b/liblangutil/CMakeLists.txt @@ -1,5 +1,6 @@ # Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul) set(sources + Common.h CharStream.cpp CharStream.h ErrorReporter.cpp diff --git a/liblangutil/CharStream.cpp b/liblangutil/CharStream.cpp index aee7cb3e0..2c10bd608 100644 --- a/liblangutil/CharStream.cpp +++ b/liblangutil/CharStream.cpp @@ -1,48 +1,48 @@ /* - 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 . - - This file is derived from the file "scanner.cc", which was part of the - V8 project. The original copyright header follows: - - Copyright 2006-2012, the V8 project authors. All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 . + * + * This file is derived from the file "scanner.cc", which was part of the + * V8 project. The original copyright header follows: + * + * Copyright 2006-2012, the V8 project authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @author Christian @@ -85,8 +85,10 @@ string CharStream::lineAtPosition(int _position) const lineStart = 0; else lineStart++; - return m_source.substr(lineStart, min(m_source.find('\n', lineStart), - m_source.size()) - lineStart); + return m_source.substr( + lineStart, + min(m_source.find('\n', lineStart), m_source.size()) - lineStart + ); } tuple CharStream::translatePositionToLineColumn(int _position) const diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h index 0c998b2aa..c11340b21 100644 --- a/liblangutil/CharStream.h +++ b/liblangutil/CharStream.h @@ -1,48 +1,48 @@ /* - 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 . - - This file is derived from the file "scanner.h", which was part of the - V8 project. The original copyright header follows: - - Copyright 2006-2012, the V8 project authors. All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 . + * + * This file is derived from the file "scanner.h", which was part of the + * V8 project. The original copyright header follows: + * + * Copyright 2006-2012, the V8 project authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @author Christian diff --git a/liblangutil/Common.h b/liblangutil/Common.h new file mode 100644 index 000000000..ed6aa2cbc --- /dev/null +++ b/liblangutil/Common.h @@ -0,0 +1,63 @@ +/* + This file is part of solidity. + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +namespace langutil +{ + +inline bool isDecimalDigit(char c) +{ + return '0' <= c && c <= '9'; +} + +inline bool isHexDigit(char c) +{ + return + isDecimalDigit(c) || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F'); +} + +inline bool isLineTerminator(char c) +{ + return c == '\n'; +} + +inline bool isWhiteSpace(char c) +{ + return c == ' ' || c == '\n' || c == '\t' || c == '\r'; +} + +inline bool isIdentifierStart(char c) +{ + return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); +} + +inline bool isIdentifierPart(char c) +{ + return isIdentifierStart(c) || isDecimalDigit(c); +} + +inline int hexValue(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else return -1; +} +} diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 21889bd9c..21a29cb82 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -25,14 +25,12 @@ #include #include -namespace dev -{ -namespace solidity +namespace langutil { /** * A version specifier of the EVM we want to compile to. - * Defaults to the latest version. + * Defaults to the latest version deployed on Ethereum mainnet at the time of compiler release. */ class EVMVersion: boost::less_than_comparable, @@ -46,10 +44,11 @@ public: static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; } static EVMVersion byzantium() { return {Version::Byzantium}; } static EVMVersion constantinople() { return {Version::Constantinople}; } + static EVMVersion petersburg() { return {Version::Petersburg}; } static boost::optional fromString(std::string const& _version) { - for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople()}) + for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg()}) if (_version == v.name()) return v; return {}; @@ -67,6 +66,7 @@ public: case Version::SpuriousDragon: return "spuriousDragon"; case Version::Byzantium: return "byzantium"; case Version::Constantinople: return "constantinople"; + case Version::Petersburg: return "petersburg"; } return "INVALID"; } @@ -76,19 +76,19 @@ public: bool hasStaticCall() const { return *this >= byzantium(); } bool hasBitwiseShifting() const { return *this >= constantinople(); } bool hasCreate2() const { return *this >= constantinople(); } + bool hasExtCodeHash() const { return *this >= constantinople(); } /// Whether we have to retain the costs for the call opcode itself (false), /// or whether we can just forward easily all remaining gas (true). bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); } private: - enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople }; + enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople, Petersburg }; EVMVersion(Version _version): m_version(_version) {} - Version m_version = Version::Byzantium; + Version m_version = Version::Petersburg; }; } -} diff --git a/liblangutil/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index 5b6e0072f..fb01847d5 100644 --- a/liblangutil/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -134,7 +134,7 @@ void ErrorReporter::clear() m_errorList.clear(); } -void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const&_secondaryLocation, string const& _description) +void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { error( Error::Type::DeclarationError, diff --git a/liblangutil/ErrorReporter.h b/liblangutil/ErrorReporter.h index fe4321ce5..d7cea4cda 100644 --- a/liblangutil/ErrorReporter.h +++ b/liblangutil/ErrorReporter.h @@ -26,6 +26,9 @@ #include #include +#include + +#include namespace langutil { @@ -87,6 +90,19 @@ public: void typeError(SourceLocation const& _location, std::string const& _description); + template + void typeErrorConcatenateDescriptions(SourceLocation const& _location, Strings const&... _descriptions) + { + std::initializer_list const descs = {_descriptions...}; + solAssert(descs.size() > 0, "Need error descriptions!"); + + auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); }); + + std::string errorStr = dev::joinHumanReadable(descs | filterEmpty); + + error(Error::Type::TypeError, _location, errorStr); + } + void fatalTypeError(SourceLocation const& _location, std::string const& _description); void docstringParsingError(std::string const& _description); @@ -119,8 +135,8 @@ private: unsigned m_errorCount = 0; unsigned m_warningCount = 0; - const unsigned c_maxWarningsAllowed = 256; - const unsigned c_maxErrorsAllowed = 256; + unsigned const c_maxWarningsAllowed = 256; + unsigned const c_maxErrorsAllowed = 256; }; } diff --git a/liblangutil/Exceptions.cpp b/liblangutil/Exceptions.cpp index 346313d5e..4e68dfa5b 100644 --- a/liblangutil/Exceptions.cpp +++ b/liblangutil/Exceptions.cpp @@ -57,7 +57,7 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip *this << errinfo_comment(_description); } -Error::Error(Error::Type _type, const std::string& _description, const SourceLocation& _location): +Error::Error(Error::Type _type, std::string const& _description, SourceLocation const& _location): Error(_type) { if (!_location.isEmpty()) diff --git a/liblangutil/Exceptions.h b/liblangutil/Exceptions.h index 22deb058e..0bd6d681f 100644 --- a/liblangutil/Exceptions.h +++ b/liblangutil/Exceptions.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian @@ -42,13 +42,13 @@ struct UnimplementedFeatureError: virtual dev::Exception {}; /// Assertion that throws an InternalCompilerError containing the given description if it is not met. #define solAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::langutil::InternalCompilerError, DESCRIPTION) + assertThrow(CONDITION, ::langutil::InternalCompilerError, DESCRIPTION) #define solUnimplementedAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::langutil::UnimplementedFeatureError, DESCRIPTION) + assertThrow(CONDITION, ::langutil::UnimplementedFeatureError, DESCRIPTION) #define solUnimplemented(DESCRIPTION) \ - solUnimplementedAssert(false, DESCRIPTION) + solUnimplementedAssert(false, DESCRIPTION) class Error: virtual public dev::Exception { diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp index c0770e045..edd23fbaf 100644 --- a/liblangutil/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/liblangutil/ParserBase.h b/liblangutil/ParserBase.h index 855201e22..1c6f298c1 100644 --- a/liblangutil/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index ac298bd5d..014191b24 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -1,48 +1,48 @@ /* - 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 . - - This file is derived from the file "scanner.cc", which was part of the - V8 project. The original copyright header follows: - - Copyright 2006-2012, the V8 project authors. All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 . + * + * This file is derived from the file "scanner.cc", which was part of the + * V8 project. The original copyright header follows: + * + * Copyright 2006-2012, the V8 project authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @author Christian @@ -50,6 +50,7 @@ * Solidity scanner. */ +#include #include #include #include @@ -61,46 +62,6 @@ using namespace std; namespace langutil { -namespace -{ -bool isDecimalDigit(char c) -{ - return '0' <= c && c <= '9'; -} -bool isHexDigit(char c) -{ - return isDecimalDigit(c) - || ('a' <= c && c <= 'f') - || ('A' <= c && c <= 'F'); -} -bool isLineTerminator(char c) -{ - return c == '\n'; -} -bool isWhiteSpace(char c) -{ - return c == ' ' || c == '\n' || c == '\t' || c == '\r'; -} -bool isIdentifierStart(char c) -{ - return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); -} -bool isIdentifierPart(char c) -{ - return isIdentifierStart(c) || isDecimalDigit(c); -} -int hexValue(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - else if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - else return -1; -} -} // end anonymous namespace - std::string to_string(ScannerError _errorCode) { switch (_errorCode) @@ -595,7 +556,12 @@ void Scanner::scanToken() token = Token::Period; break; case ':': - token = selectToken(Token::Colon); + // : := + advance(); + if (m_char == '=') + token = selectToken(Token::AssemblyAssign); + else + token = Token::Colon; break; case ';': token = selectToken(Token::Semicolon); diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 72d0072f5..965328cbb 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -1,48 +1,48 @@ /* - 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 . - - This file is derived from the file "scanner.h", which was part of the - V8 project. The original copyright header follows: - - Copyright 2006-2012, the V8 project authors. All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 . + * + * This file is derived from the file "scanner.h", which was part of the + * V8 project. The original copyright header follows: + * + * Copyright 2006-2012, the V8 project authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @author Christian diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index c461909f2..cf5308e02 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Lefteris Karapetsas diff --git a/liblangutil/Token.cpp b/liblangutil/Token.cpp index cbfd4a8ca..b03fa3185 100644 --- a/liblangutil/Token.cpp +++ b/liblangutil/Token.cpp @@ -143,7 +143,7 @@ static Token keywordByName(string const& _name) // and keywords to be put inside the keywords variable. #define KEYWORD(name, string, precedence) {string, Token::name}, #define TOKEN(name, string, precedence) - static const map keywords({TOKEN_LIST(TOKEN, KEYWORD)}); + static map const keywords({TOKEN_LIST(TOKEN, KEYWORD)}); #undef KEYWORD #undef TOKEN auto it = keywords.find(_name); diff --git a/liblangutil/Token.h b/liblangutil/Token.h index b3a1acb18..35d4f76f3 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -140,6 +140,8 @@ namespace langutil T(Dec, "--", 0) \ K(Delete, "delete", 0) \ \ + /* Inline Assembly Operators */ \ + T(AssemblyAssign, ":=", 2) \ /* Keywords */ \ K(Anonymous, "anonymous", 0) \ K(As, "as", 0) \ @@ -287,7 +289,7 @@ namespace TokenTraits constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; } constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd || - op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; } + op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; } constexpr bool isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; } constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; } diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 63d8da3d5..b9cb96151 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -649,22 +649,22 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) auto end = m_asm.newTag(); m_asm.append(Instruction::MSIZE); // Result will be original top of memory - m_asm.append(code[0].m_asm, 1); // The alloc argument N + m_asm.append(code[0].m_asm, 1); // The alloc argument N m_asm.append(Instruction::DUP1); m_asm.append(Instruction::ISZERO);// (alloc 0) does not change MSIZE m_asm.appendJumpI(end); m_asm.append(u256(1)); m_asm.append(Instruction::DUP2); // Copy N - m_asm.append(Instruction::SUB); // N-1 + m_asm.append(Instruction::SUB); // N-1 m_asm.append(u256(0x1f)); // Bit mask - m_asm.append(Instruction::NOT); // Invert - m_asm.append(Instruction::AND); // Align N-1 on 32 byte boundary + m_asm.append(Instruction::NOT); // Invert + m_asm.append(Instruction::AND); // Align N-1 on 32 byte boundary m_asm.append(Instruction::MSIZE); // MSIZE is cheap m_asm.append(Instruction::ADD); m_asm.append(Instruction::MLOAD); // Updates MSIZE - m_asm.append(Instruction::POP); // Discard the result of the MLOAD + m_asm.append(Instruction::POP); // Discard the result of the MLOAD m_asm.append(end); - m_asm.append(Instruction::POP); // Discard duplicate N + m_asm.append(Instruction::POP); // Discard duplicate N _s.usedAlloc = true; } diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h index 5c2f49a69..f59a5d6fd 100644 --- a/liblll/CodeFragment.h +++ b/liblll/CodeFragment.h @@ -65,7 +65,7 @@ private: ReadCallback m_readFile; }; -static const CodeFragment NullCodeFragment; +static CodeFragment const NullCodeFragment; } } diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index 6296cbcff..03190347a 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -28,7 +28,7 @@ using namespace std; using namespace dev; using namespace dev::lll; -bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVersion, bool _opt, std::vector* _errors, ReadCallback const& _readFile) +bytes dev::lll::compileLLL(string const& _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector* _errors, ReadCallback const& _readFile) { try { @@ -36,7 +36,7 @@ bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer cs.populateStandard(); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) - assembly = assembly.optimise(true, _evmVersion); + assembly = assembly.optimise(true, _evmVersion, true, 200); bytes ret = assembly.assemble().bytecode; for (auto i: cs.treesToKill) killBigints(i); @@ -66,7 +66,7 @@ bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer return bytes(); } -std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVersion, bool _opt, std::vector* _errors, ReadCallback const& _readFile) +std::string dev::lll::compileLLLToAsm(std::string const& _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector* _errors, ReadCallback const& _readFile) { try { @@ -74,7 +74,7 @@ std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVe cs.populateStandard(); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) - assembly = assembly.optimise(true, _evmVersion); + assembly = assembly.optimise(true, _evmVersion, true, 200); string ret = assembly.assemblyString(); for (auto i: cs.treesToKill) killBigints(i); diff --git a/liblll/Compiler.h b/liblll/Compiler.h index 93235cdd2..a8a7b9988 100644 --- a/liblll/Compiler.h +++ b/liblll/Compiler.h @@ -36,8 +36,8 @@ namespace lll using ReadCallback = std::function; std::string parseLLL(std::string const& _src); -std::string compileLLLToAsm(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); -bytes compileLLL(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); +std::string compileLLLToAsm(std::string const& _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); +bytes compileLLL(std::string const& _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); } } diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index 019582d4c..dbd6e91cd 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -44,7 +44,7 @@ CodeFragment const& CompilerState::getDef(std::string const& _s) const void CompilerState::populateStandard() { - static const string s = "{" + static string const s = "{" "(def 'panic () (asm INVALID))" // Alternative macro version of alloc, which is currently implemented in the parser // "(def 'alloc (n) (raw (msize) (when n (pop (mload (+ (msize) (& (- n 1) (~ 0x1f))))))))" diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 6cc77c4e3..36125c2ef 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -51,8 +51,6 @@ set(sources codegen/ABIFunctions.h codegen/ArrayUtils.cpp codegen/ArrayUtils.h - codegen/AsmCodeGen.cpp - codegen/AsmCodeGen.h codegen/Compiler.cpp codegen/Compiler.h codegen/CompilerContext.cpp @@ -82,14 +80,13 @@ set(sources formal/VariableUsage.h interface/ABI.cpp interface/ABI.h - interface/AssemblyStack.cpp - interface/AssemblyStack.h interface/CompilerStack.cpp interface/CompilerStack.h interface/GasEstimator.cpp interface/GasEstimator.h interface/Natspec.cpp interface/Natspec.h + interface/OptimiserSettings.h interface/ReadFile.h interface/StandardCompiler.cpp interface/StandardCompiler.h diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index b801547f6..ccc2e8a3c 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -120,7 +120,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod vector uninitializedAccessesOrdered( exitInfo.uninitializedVariableAccesses.begin(), exitInfo.uninitializedVariableAccesses.end() - ); + ); boost::range::sort( uninitializedAccessesOrdered, [](VariableOccurrence const* lhs, VariableOccurrence const* rhs) -> bool diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h index a55b96e82..506cce03f 100644 --- a/libsolidity/analysis/ControlFlowGraph.h +++ b/libsolidity/analysis/ControlFlowGraph.h @@ -32,11 +32,12 @@ namespace dev namespace solidity { -/** Occurrence of a variable in a block of control flow. - * Stores the declaration of the referenced variable, the - * kind of the occurrence and possibly the node at which - * it occurred. - */ +/** + * Occurrence of a variable in a block of control flow. + * Stores the declaration of the referenced variable, the + * kind of the occurrence and possibly the node at which + * it occurred. + */ class VariableOccurrence { public: @@ -85,11 +86,12 @@ private: ASTNode const* m_occurrence = nullptr; }; -/** Node of the Control Flow Graph. - * The control flow is a directed graph connecting control flow blocks. - * An arc between two nodes indicates that the control flow can possibly - * move from its start node to its end node during execution. - */ +/** + * Node of the Control Flow Graph. + * The control flow is a directed graph connecting control flow blocks. + * An arc between two nodes indicates that the control flow can possibly + * move from its start node to its end node during execution. + */ struct CFGNode { /// Entry nodes. All CFG nodes from which control flow may move into this node. diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index d0657898c..463510436 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h index e26f58912..9ef5e1c43 100644 --- a/libsolidity/analysis/DeclarationContainer.h +++ b/libsolidity/analysis/DeclarationContainer.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 0878b5502..108a18564 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian @@ -42,7 +42,7 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit) bool DocStringAnalyser::visit(ContractDefinition const& _contract) { - static const set validTags = set{"author", "title", "dev", "notice"}; + static set const validTags = set{"author", "title", "dev", "notice"}; parseDocStrings(_contract, _contract.annotation(), validTags, "contracts"); return true; @@ -99,7 +99,7 @@ void DocStringAnalyser::handleConstructor( DocumentedAnnotation& _annotation ) { - static const set validTags = set{"author", "dev", "notice", "param"}; + static set const validTags = set{"author", "dev", "notice", "param"}; parseDocStrings(_node, _annotation, validTags, "constructor"); checkParameters(_callable, _annotation); } @@ -110,7 +110,7 @@ void DocStringAnalyser::handleCallable( DocumentedAnnotation& _annotation ) { - static const set validTags = set{"author", "dev", "notice", "return", "param"}; + static set const validTags = set{"author", "dev", "notice", "return", "param"}; parseDocStrings(_node, _annotation, validTags, "functions"); checkParameters(_callable, _annotation); } diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index f6b236db6..8d3658ac8 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 95bc69fe5..d5f972801 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 89c539321..a3b78f0e4 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 4e6d7f596..cce1a3031 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian @@ -246,7 +246,7 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array."); if (Expression const* length = _typeName.length()) { - TypePointer lengthTypeGeneric = length->annotation().type; + TypePointer& lengthTypeGeneric = length->annotation().type; if (!lengthTypeGeneric) lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length); RationalNumberType const* lengthType = dynamic_cast(lengthTypeGeneric.get()); @@ -298,11 +298,13 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) } declarations = m_resolver.nameFromCurrentScope(realName); } - if (declarations.size() != 1) + if (declarations.size() > 1) { declarationError(_identifier.location, "Multiple matching identifiers. Resolving overloaded identifiers is not supported."); return size_t(-1); } + else if (declarations.size() == 0) + return size_t(-1); if (auto var = dynamic_cast(declarations.front())) if (var->isLocalVariable() && _crossesFunctionBoundary) { @@ -322,9 +324,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) yul::AsmAnalyzer( analysisInfo, errorsIgnored, - EVMVersion(), errorTypeForLoose, - yul::EVMDialect::looseAssemblyForEVM(), + yul::EVMDialect::looseAssemblyForEVM(EVMVersion{}), resolver ).analyze(_inlineAssembly.operations()); return false; diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index b3de9458f..5631fb801 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c716d6730..5378f15fb 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -139,14 +139,21 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c toString(arguments.size()) + " were provided." ); - if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType::bytesMemory())) - m_errorReporter.typeError( - arguments.front()->location(), - "Invalid type for argument in function call. " - "Invalid implicit conversion from " + - type(*arguments.front())->toString() + - " to bytes memory requested." - ); + + if (arguments.size() >= 1) + { + BoolResult result = type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType::bytesMemory()); + + if (!result) + m_errorReporter.typeErrorConcatenateDescriptions( + arguments.front()->location(), + "Invalid type for argument in function call. " + "Invalid implicit conversion from " + + type(*arguments.front())->toString() + + " to bytes memory requested.", + result.message() + ); + } if (arguments.size() < 2) return {}; @@ -262,16 +269,20 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) ); } for (size_t i = 0; i < std::min(arguments->size(), parameterTypes.size()); ++i) - if (!type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) - m_errorReporter.typeError( + { + BoolResult result = type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i]); + if (!result) + m_errorReporter.typeErrorConcatenateDescriptions( (*arguments)[i]->location(), "Invalid type for argument in constructor call. " "Invalid implicit conversion from " + type(*(*arguments)[i])->toString() + " to " + parameterTypes[i]->toString() + - " requested." + " requested.", + result.message() ); + } } } @@ -448,6 +459,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) ) m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); + if (_variable.typeName()) + _variable.typeName()->accept(*this); + // type is filled either by ReferencesResolver directly from the type name or by // TypeChecker at the VariableDeclarationStatement level. TypePointer varType = _variable.annotation().type; @@ -511,14 +525,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) ) m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded."); break; - case Type::Category::Mapping: - if (auto mappingType = dynamic_cast(varType.get())) - if ( - mappingType->keyType()->isDynamicallySized() && - _variable.visibility() == Declaration::Visibility::Public - ) - m_errorReporter.typeError(_variable.location(), "Dynamically-sized keys for public mappings are not supported."); - break; default: break; } @@ -571,16 +577,20 @@ void TypeChecker::visitManually( return; } for (size_t i = 0; i < arguments.size(); ++i) - if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i]))) - m_errorReporter.typeError( + { + BoolResult result = type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i])); + if (!result) + m_errorReporter.typeErrorConcatenateDescriptions( arguments[i]->location(), "Invalid type for argument in modifier invocation. " "Invalid implicit conversion from " + type(*arguments[i])->toString() + " to " + type(*(*parameters)[i])->toString() + - " requested." + " requested.", + result.message() ); + } } bool TypeChecker::visit(EventDefinition const& _eventDef) @@ -637,6 +647,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) bool requiresStorage = ref->second.isSlot || ref->second.isOffset; if (auto var = dynamic_cast(declaration)) { + solAssert(var->type(), "Expected variable type!"); if (var->isConstant()) { m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly."); @@ -713,9 +724,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) yul::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, - m_evmVersion, Error::Type::SyntaxError, - yul::EVMDialect::looseAssemblyForEVM(), + yul::EVMDialect::looseAssemblyForEVM(m_evmVersion), identifierAccess ); if (!analyzer.analyze(_inlineAssembly.operations())) @@ -772,29 +782,34 @@ void TypeChecker::endVisit(Return const& _return) { if (tupleType->components().size() != params->parameters().size()) m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); - else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes))) - m_errorReporter.typeError( - _return.expression()->location(), - "Return argument type " + - type(*_return.expression())->toString() + - " is not implicitly convertible to expected type " + - TupleType(returnTypes).toString(false) + - "." - ); + else + { + BoolResult result = tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes)); + if (!result) + m_errorReporter.typeErrorConcatenateDescriptions( + _return.expression()->location(), + "Return argument type " + + type(*_return.expression())->toString() + + " is not implicitly convertible to expected type " + + TupleType(returnTypes).toString(false) + ".", + result.message() + ); + } } else if (params->parameters().size() != 1) m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); else { TypePointer const& expected = type(*params->parameters().front()); - if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected)) - m_errorReporter.typeError( + BoolResult result = type(*_return.expression())->isImplicitlyConvertibleTo(*expected); + if (!result) + m_errorReporter.typeErrorConcatenateDescriptions( _return.expression()->location(), "Return argument type " + type(*_return.expression())->toString() + " is not implicitly convertible to expected type (type of first return variable) " + - expected->toString() + - "." + expected->toString() + ".", + result.message() ); } } @@ -986,7 +1001,8 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) else { var.accept(*this); - if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type)) + BoolResult result = valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type); + if (!result) { auto errorMsg = "Type " + valueComponentType->toString() + @@ -1013,11 +1029,23 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) ); } else - m_errorReporter.typeError(_statement.location(), errorMsg + "."); + m_errorReporter.typeErrorConcatenateDescriptions( + _statement.location(), + errorMsg + ".", + result.message() + ); } } } + if (valueTypes.size() != variables.size()) + { + solAssert(m_errorReporter.hasErrors(), "Should have errors!"); + for (auto const& var: variables) + if (var && !var->annotation().type) + BOOST_THROW_EXCEPTION(FatalError()); + } + if (autoTypeDeductionNeeded) { if (!typeCanBeExpressed(variables)) @@ -2046,25 +2074,27 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) } string errorMsg = "Member \"" + memberName + "\" not found or not visible " "after argument-dependent lookup in " + exprType->toString() + "."; - if (memberName == "value") + + if (auto const& funType = dynamic_pointer_cast(exprType)) { - errorMsg.pop_back(); - errorMsg += " - did you forget the \"payable\" modifier?"; - } - else if (exprType->category() == Type::Category::Function) - { - if (auto const& funType = dynamic_pointer_cast(exprType)) + auto const& t = funType->returnParameterTypes(); + + if (memberName == "value") { - auto const& t = funType->returnParameterTypes(); - if (t.size() == 1) - if ( - t.front()->category() == Type::Category::Contract || - t.front()->category() == Type::Category::Struct - ) - errorMsg += " Did you intend to call the function?"; + if (funType->kind() == FunctionType::Kind::Creation) + errorMsg = "Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available."; + else + errorMsg = "Member \"value\" is only available for payable functions."; } + else if ( + t.size() == 1 && + (t.front()->category() == Type::Category::Struct || + t.front()->category() == Type::Category::Contract) + ) + errorMsg += " Did you intend to call the function?"; } - if (exprType->category() == Type::Category::Contract) + else if (exprType->category() == Type::Category::Contract) + { for (auto const& addressMember: AddressType::addressPayable().nativeMembers(nullptr)) if (addressMember.name == memberName) { @@ -2073,6 +2103,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member."; break; } + } + else if (auto addressType = dynamic_cast(exprType.get())) + { + // Trigger error when using send or transfer with a non-payable fallback function. + if (memberName == "send" || memberName == "transfer") + { + solAssert( + addressType->stateMutability() != StateMutability::Payable, + "Expected address not-payable as members were not found" + ); + + errorMsg = "\"send\" and \"transfer\" are only available for objects of type \"address payable\", not \"" + exprType->toString() + "\"."; + } + } + m_errorReporter.fatalTypeError( _memberAccess.location(), errorMsg @@ -2090,12 +2135,11 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) annotation.type = possibleMembers.front().type; if (auto funType = dynamic_cast(annotation.type.get())) - if (funType->bound() && !exprType->isImplicitlyConvertibleTo(*funType->selfType())) - m_errorReporter.typeError( - _memberAccess.location(), - "Function \"" + memberName + "\" cannot be called on an object of type " + - exprType->toString() + " (expected " + funType->selfType()->toString() + ")." - ); + solAssert( + !funType->bound() || exprType->isImplicitlyConvertibleTo(*funType->selfType()), + "Function \"" + memberName + "\" cannot be called on an object of type " + + exprType->toString() + " (expected " + funType->selfType()->toString() + ")." + ); if (auto const* structType = dynamic_cast(exprType.get())) annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData); @@ -2116,26 +2160,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) annotation.isLValue = annotation.referencedDeclaration->isLValue(); } - if (exprType->category() == Type::Category::Contract) - { - // Warn about using send or transfer with a non-payable fallback function. - if (auto callType = dynamic_cast(type(_memberAccess).get())) - { - auto kind = callType->kind(); - auto contractType = dynamic_cast(exprType.get()); - solAssert(!!contractType, "Should be contract type."); - - if ( - (kind == FunctionType::Kind::Send || kind == FunctionType::Kind::Transfer) && - !contractType->isPayable() - ) - m_errorReporter.typeError( - _memberAccess.location(), - "Value transfer to a contract without a payable fallback function." - ); - } - } - // TODO some members might be pure, but for example `address(0x123).balance` is not pure // although every subexpression is, so leaving this limited for now. if (auto tt = dynamic_cast(exprType.get())) @@ -2159,6 +2183,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) "Circular reference for contract code access." ); } + else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name") + annotation.isPure = true; } return false; @@ -2213,6 +2239,8 @@ bool TypeChecker::visit(IndexAccess const& _access) case Type::Category::TypeType: { TypeType const& typeType = dynamic_cast(*baseType); + if (dynamic_cast(typeType.actualType().get())) + m_errorReporter.typeError(_access.location(), "Index access for contracts or libraries is not possible."); if (!index) resultType = make_shared(make_shared(DataLocation::Memory, typeType.actualType())); else @@ -2324,8 +2352,13 @@ bool TypeChecker::visit(Identifier const& _identifier) if (auto variableDeclaration = dynamic_cast(annotation.referencedDeclaration)) annotation.isPure = annotation.isConstant = variableDeclaration->isConstant(); else if (dynamic_cast(annotation.referencedDeclaration)) + { if (dynamic_cast(annotation.type.get())) annotation.isPure = true; + } + else if (dynamic_cast(annotation.type.get())) + annotation.isPure = true; + // Check for deprecated function names. // The check is done here for the case without an actual function call. diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index d5f3645c5..09879322e 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -48,7 +48,7 @@ class TypeChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - TypeChecker(EVMVersion _evmVersion, langutil::ErrorReporter& _errorReporter): + TypeChecker(langutil::EVMVersion _evmVersion, langutil::ErrorReporter& _errorReporter): m_evmVersion(_evmVersion), m_errorReporter(_errorReporter) {} @@ -156,7 +156,7 @@ private: ContractDefinition const* m_scope = nullptr; - EVMVersion m_evmVersion; + langutil::EVMVersion m_evmVersion; /// Flag indicating whether we are currently inside an EmitStatement. bool m_insideEmitStatement = false; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 7df7ac17c..88ca727ce 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -340,7 +340,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::Message, "data"}, {MagicType::Kind::Message, "sig"}, {MagicType::Kind::MetaType, "creationCode"}, - {MagicType::Kind::MetaType, "runtimeCode"} + {MagicType::Kind::MetaType, "runtimeCode"}, + {MagicType::Kind::MetaType, "name"}, }; set static const payableMembers{ {MagicType::Kind::Message, "value"} diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 4cf8b1e8e..5be036f4e 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index f6fdc4416..60cbc7dce 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian @@ -1209,7 +1209,7 @@ private: class Continue: public Statement { public: - explicit Continue(SourceLocation const& _location, ASTPointer const& _docString): + explicit Continue(SourceLocation const& _location, ASTPointer const& _docString): Statement(_location, _docString) {} void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; @@ -1576,7 +1576,7 @@ private: }; /** - * Index access to an array. Example: a[2] + * Index access to an array or mapping. Example: a[2] */ class IndexAccess: public Expression { diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h index d47a5f05b..f44c34353 100644 --- a/libsolidity/ast/ASTEnums.h +++ b/libsolidity/ast/ASTEnums.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @date 2017 diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index f61acad95..23f0b471f 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index f8222598b..726ef9808 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @date 2017 diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 770e3d9d4..da3c8605a 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Lefteris diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index d8bafa2c1..7d77ff7b9 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian @@ -113,9 +113,13 @@ bool ASTPrinter::visit(ParameterList const& _node) bool ASTPrinter::visit(FunctionDefinition const& _node) { - writeLine("FunctionDefinition \"" + _node.name() + "\"" + - (_node.isPublic() ? " - public" : "") + - (_node.stateMutability() == StateMutability::View ? " - const" : "")); + writeLine( + "FunctionDefinition \"" + + _node.name() + + "\"" + + (_node.isPublic() ? " - public" : "") + + (_node.stateMutability() == StateMutability::View ? " - const" : "") + ); printSourcePart(_node); return goDeeper(); } @@ -305,8 +309,12 @@ bool ASTPrinter::visit(TupleExpression const& _node) bool ASTPrinter::visit(UnaryOperation const& _node) { - writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + - ") " + TokenTraits::toString(_node.getOperator())); + writeLine( + string("UnaryOperation (") + + (_node.isPrefixOperation() ? "prefix" : "postfix") + + ") " + + TokenTraits::toString(_node.getOperator()) + ); printType(_node); printSourcePart(_node); return goDeeper(); diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index d762af473..2e9cb4fc6 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 8cb94e05c..5b97bb06a 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index aeff6e4a9..5538180d9 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 54aad573c..1d94ae67f 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -35,13 +35,13 @@ enum class ExperimentalFeature TestOnlyAnalysis }; -static const std::map ExperimentalFeatureOnlyAnalysis = +static std::map const ExperimentalFeatureOnlyAnalysis = { { ExperimentalFeature::SMTChecker, true }, { ExperimentalFeature::TestOnlyAnalysis, true }, }; -static const std::map ExperimentalFeatureNames = +static std::map const ExperimentalFeatureNames = { { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 }, { "SMTChecker", ExperimentalFeature::SMTChecker }, diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index aeb59dfca..4748b8217 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian @@ -496,8 +496,8 @@ BoolResult AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const if (auto const* contractType = dynamic_cast(&_convertTo)) return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable(); return isImplicitlyConvertibleTo(_convertTo) || - _convertTo.category() == Category::Integer || - (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast(_convertTo).numBytes() * 8); + _convertTo.category() == Category::Integer || + (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast(_convertTo).numBytes() * 8); } string AddressType::toString(bool) const @@ -1380,7 +1380,7 @@ string StringLiteralType::richIdentifier() const return "t_stringliteral_" + toHex(keccak256(m_value).asBytes()); } -bool StringLiteralType::operator==(const Type& _other) const +bool StringLiteralType::operator==(Type const& _other) const { if (_other.category() != category()) return false; @@ -1463,7 +1463,7 @@ TypeResult FixedBytesType::binaryOperatorResult(Token _operator, TypePointer con return TypePointer(); } -MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) const +MemberList::MemberMap FixedBytesType::nativeMembers(ContractDefinition const*) const { return MemberList::MemberMap{MemberList::Member{"length", make_shared(8)}}; } @@ -1611,7 +1611,7 @@ string ReferenceType::identifierLocationSuffix() const return id; } -BoolResult ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const +BoolResult ArrayType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() != category()) return false; @@ -1651,7 +1651,7 @@ BoolResult ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const } } -BoolResult ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const +BoolResult ArrayType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (isImplicitlyConvertibleTo(_convertTo)) return true; @@ -1707,8 +1707,8 @@ bool ArrayType::operator==(Type const& _other) const bool ArrayType::validForCalldata() const { if (auto arrayBaseType = dynamic_cast(baseType().get())) - if (!arrayBaseType->validForCalldata()) - return false; + if (!arrayBaseType->validForCalldata()) + return false; return unlimitedCalldataEncodedSize(true) <= numeric_limits::max(); } @@ -2010,7 +2010,7 @@ vector> ContractType::stateVar return variablesAndOffsets; } -BoolResult StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const +BoolResult StructType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() != category()) return false; @@ -3062,7 +3062,8 @@ string FunctionType::externalSignature() const solAssert(false, "Invalid function type for requesting external signature."); } - bool const inLibrary = dynamic_cast(*m_declaration->scope()).isLibrary(); + // "inLibrary" is only relevant if this is not an event. + bool const inLibrary = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); FunctionTypePointer external = interfaceFunctionType(); solAssert(!!external, "External function type requested."); auto parameterTypes = external->parameterTypes(); @@ -3298,7 +3299,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current return members; } -ModifierType::ModifierType(const ModifierDefinition& _modifier) +ModifierType::ModifierType(ModifierDefinition const& _modifier) { TypePointers params; params.reserve(_modifier.parameters().size()); @@ -3327,8 +3328,12 @@ bool ModifierType::operator==(Type const& _other) const return false; auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }; - if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), - other.m_parameterTypes.cbegin(), typeCompare)) + if (!equal( + m_parameterTypes.cbegin(), + m_parameterTypes.cend(), + other.m_parameterTypes.cbegin(), + typeCompare + )) return false; return true; } @@ -3485,8 +3490,9 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const ContractDefinition const& contract = dynamic_cast(*m_typeArgument).contractDefinition(); if (contract.canBeDeployed()) return MemberList::MemberMap({ - {"creationCode", std::make_shared(DataLocation::Memory)}, - {"runtimeCode", std::make_shared(DataLocation::Memory)} + {"creationCode", make_shared(DataLocation::Memory)}, + {"runtimeCode", make_shared(DataLocation::Memory)}, + {"name", make_shared(DataLocation::Memory, true)}, }); else return {}; diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index ea6792398..f88232c9b 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian @@ -216,7 +216,6 @@ public: /// @returns number of bytes used by this type when encoded for CALL. If it is a dynamic type, /// returns the size of the pointer (usually 32). Returns 0 if the type cannot be encoded /// in calldata. - /// @note: This should actually not be called on types, where isDynamicallyEncoded returns true. /// If @a _padded then it is assumed that each element is padded to a multiple of 32 bytes. virtual unsigned calldataEncodedSize(bool _padded) const { (void)_padded; return 0; } /// @returns the size of this data type in bytes when stored in memory. For memory-reference @@ -332,8 +331,8 @@ protected: class AddressType: public Type { public: - static AddressType& address() { static std::shared_ptr addr(std::make_shared(StateMutability::NonPayable)); return *addr; } - static AddressType& addressPayable() { static std::shared_ptr addr(std::make_shared(StateMutability::Payable)); return *addr; } + static AddressType& address() { static std::shared_ptr addr(std::make_shared(StateMutability::NonPayable)); return *addr; } + static AddressType& addressPayable() { static std::shared_ptr addr(std::make_shared(StateMutability::Payable)); return *addr; } Category category() const override { return Category::Address; } @@ -707,7 +706,7 @@ public: BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; std::string richIdentifier() const override; - bool operator==(const Type& _other) const override; + bool operator==(Type const& _other) const override; unsigned calldataEncodedSize(bool _padded) const override; bool isDynamicallySized() const override { return m_hasDynamicLength; } bool isDynamicallyEncoded() const override; @@ -736,6 +735,13 @@ public: TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override; + /// The offset to advance in calldata to move from one array element to the next. + unsigned calldataStride() const { return isByteArray() ? 1 : m_baseType->calldataEncodedSize(); } + /// The offset to advance in memory to move from one array element to the next. + unsigned memoryStride() const { return isByteArray() ? 1 : m_baseType->memoryHeadSize(); } + /// The offset to advance in storage to move from one array element to the next. + unsigned storageStride() const { return isByteArray() ? 1 : m_baseType->storageBytes(); } + private: /// String is interpreted as a subtype of Bytes. enum class ArrayKind { Ordinary, Bytes, String }; @@ -823,7 +829,7 @@ public: Category category() const override { return Category::Struct; } explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): ReferenceType(_location), m_struct(_struct) {} - BoolResult isImplicitlyConvertibleTo(const Type& _convertTo) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; unsigned calldataEncodedSize(bool _padded) const override; diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 0c7a43c37..f4c743174 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -308,6 +308,7 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) break; case Type::Category::Array: case Type::Category::Struct: + case Type::Category::Mapping: solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type."); templ("body", "cleaned := value"); break; @@ -1089,7 +1090,7 @@ string ABIFunctions::abiEncodingFunctionStruct( if (_options.dynamicInplace) encode = Whiskers{"pos := (memberValue, pos)"} ("encode", abiEncodeAndReturnUpdatedPosFunction(*memberTypeFrom, *memberTypeTo, subOptions)) - .render(); + .render(); else { Whiskers encodeTempl( @@ -1262,7 +1263,7 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo return abiDecodingFunctionValueType(_type, _fromMemory); } -string ABIFunctions::abiDecodingFunctionValueType(const Type& _type, bool _fromMemory) +string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromMemory) { TypePointer decodingType = _type.decodingType(); solAssert(decodingType, ""); diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 8d8be9d71..654afc034 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -50,7 +50,7 @@ using TypePointers = std::vector; class ABIFunctions { public: - explicit ABIFunctions(EVMVersion _evmVersion = EVMVersion{}) : m_evmVersion(_evmVersion) {} + explicit ABIFunctions(langutil::EVMVersion _evmVersion = langutil::EVMVersion{}) : m_evmVersion(_evmVersion) {} /// @returns name of an assembly function to ABI-encode values of @a _givenTypes /// into memory, converting the types to @a _targetTypes on the fly. @@ -286,7 +286,7 @@ private: /// Map from function name to code for a multi-use function. std::map m_requestedFunctions; std::set m_externallyUsedFunctions; - EVMVersion m_evmVersion; + langutil::EVMVersion m_evmVersion; }; } diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index f9678f7ce..39189046f 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -623,7 +623,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; // stack: data_pos_end data_pos - if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) + if (_type.storageStride() < 32) clearStorageLoop(make_shared(256)); else clearStorageLoop(_type.baseType()); @@ -769,7 +769,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const // stack: ref new_length data_pos new_size delete_end _context << Instruction::SWAP2 << Instruction::ADD; // stack: ref new_length delete_end delete_start - if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) + if (_type.storageStride() < 32) ArrayUtils(_context).clearStorageLoop(make_shared(256)); else ArrayUtils(_context).clearStorageLoop(_type.baseType()); @@ -934,8 +934,11 @@ void ArrayUtils::clearStorageLoop(TypePointer const& _type) const eth::AssemblyItem loopStart = _context.appendJumpToNew(); _context << loopStart; // check for loop condition - _context << Instruction::DUP1 << Instruction::DUP3 - << Instruction::GT << Instruction::ISZERO; + _context << + Instruction::DUP1 << + Instruction::DUP3 << + Instruction::GT << + Instruction::ISZERO; eth::AssemblyItem zeroLoopEnd = _context.newTag(); _context.appendConditionalJumpTo(zeroLoopEnd); // delete diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index 72efed33a..55db6290d 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -35,16 +35,20 @@ void Compiler::compileContract( bytes const& _metadata ) { - ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns); + ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimiserSettings); runtimeCompiler.compileContract(_contract, _otherCompilers); m_runtimeContext.appendAuxiliaryData(_metadata); // This might modify m_runtimeContext because it can access runtime functions at // creation time. - ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1); + OptimiserSettings creationSettings{m_optimiserSettings}; + // The creation code will be executed at most once, so we modify the optimizer + // settings accordingly. + creationSettings.expectedExecutionsPerDeployment = 1; + ContractCompiler creationCompiler(&runtimeCompiler, m_context, creationSettings); m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers); - m_context.optimise(m_optimize, m_optimizeRuns); + m_context.optimise(m_optimiserSettings); } std::shared_ptr Compiler::runtimeAssemblyPtr() const diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index c21de96d8..34bae7a27 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include #include @@ -34,9 +35,8 @@ namespace solidity { class Compiler { public: - explicit Compiler(EVMVersion _evmVersion = EVMVersion{}, bool _optimize = false, unsigned _runs = 200): - m_optimize(_optimize), - m_optimizeRuns(_runs), + explicit Compiler(langutil::EVMVersion _evmVersion, OptimiserSettings _optimiserSettings): + m_optimiserSettings(std::move(_optimiserSettings)), m_runtimeContext(_evmVersion), m_context(_evmVersion, &m_runtimeContext) { } @@ -78,8 +78,7 @@ public: eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const; private: - bool const m_optimize; - unsigned const m_optimizeRuns; + OptimiserSettings const m_optimiserSettings; CompilerContext m_runtimeContext; size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present. CompilerContext m_context; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index d7839a0a4..3053e3f7c 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -31,7 +30,9 @@ #include #include #include +#include #include +#include #include #include @@ -129,8 +130,10 @@ void CompilerContext::appendMissingLowLevelFunctions() } } -void CompilerContext::addVariable(VariableDeclaration const& _declaration, - unsigned _offsetToCurrent) +void CompilerContext::addVariable( + VariableDeclaration const& _declaration, + unsigned _offsetToCurrent +) { solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, ""); unsigned sizeOnStack = _declaration.annotation().type->sizeOnStack(); @@ -265,7 +268,7 @@ unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const return m_asm->deposit() - _offset - 1; } -pair CompilerContext::storageLocationOfVariable(const Declaration& _declaration) const +pair CompilerContext::storageLocationOfVariable(Declaration const& _declaration) const { auto it = m_stateVariables.find(&_declaration); solAssert(it != m_stateVariables.end(), "Variable not found in storage."); @@ -326,12 +329,19 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node) void CompilerContext::appendInlineAssembly( string const& _assembly, vector const& _localVariables, - set const&, - bool _system + set const& _externallyUsedFunctions, + bool _system, + bool _optimise ) { int startStackHeight = stackHeight(); + set externallyUsedIdentifiers; + for (auto const& fun: _externallyUsedFunctions) + externallyUsedIdentifiers.insert(yul::YulString(fun)); + for (auto const& var: _localVariables) + externallyUsedIdentifiers.insert(yul::YulString(var)); + yul::ExternalIdentifierAccess identifierAccess; identifierAccess.resolve = [&]( yul::Identifier const& _identifier, @@ -374,25 +384,16 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(langutil::CharStream(_assembly, "--CODEGEN--")); - auto parserResult = yul::Parser(errorReporter, yul::EVMDialect::strictAssemblyForEVM()).parse(scanner, false); + auto parserResult = yul::Parser(errorReporter, yul::EVMDialect::strictAssemblyForEVM(m_evmVersion)).parse(scanner, false); #ifdef SOL_OUTPUT_ASM cout << yul::AsmPrinter()(*parserResult) << endl; #endif - yul::AsmAnalysisInfo analysisInfo; - bool analyzerResult = false; - if (parserResult) - analyzerResult = yul::AsmAnalyzer( - analysisInfo, - errorReporter, - m_evmVersion, - boost::none, - yul::EVMDialect::strictAssemblyForEVM(), - identifierAccess.resolve - ).analyze(*parserResult); - if (!parserResult || !errorReporter.errors().empty() || !analyzerResult) + + auto reportError = [&](string const& _context) { string message = - "Error parsing/analyzing inline assembly block:\n" + "Error parsing/analyzing inline assembly block:\n" + + _context + "\n" "------------------ Input: -----------------\n" + _assembly + "\n" "------------------ Errors: ----------------\n"; @@ -404,10 +405,47 @@ void CompilerContext::appendInlineAssembly( message += "-------------------------------------------\n"; solAssert(false, message); + }; + + yul::AsmAnalysisInfo analysisInfo; + bool analyzerResult = false; + if (parserResult) + analyzerResult = yul::AsmAnalyzer( + analysisInfo, + errorReporter, + boost::none, + yul::EVMDialect::strictAssemblyForEVM(m_evmVersion), + identifierAccess.resolve + ).analyze(*parserResult); + if (!parserResult || !errorReporter.errors().empty() || !analyzerResult) + reportError("Invalid assembly generated by code generator."); + + // Several optimizer steps cannot handle externally supplied stack variables, + // so we essentially only optimize the ABI functions. + if (_optimise && _localVariables.empty()) + { + yul::OptimiserSuite::run( + yul::EVMDialect::strictAssemblyForEVM(m_evmVersion), + *parserResult, + analysisInfo, + externallyUsedIdentifiers + ); + analysisInfo = yul::AsmAnalysisInfo{}; + if (!yul::AsmAnalyzer( + analysisInfo, + errorReporter, + boost::none, + yul::EVMDialect::strictAssemblyForEVM(m_evmVersion), + identifierAccess.resolve + ).analyze(*parserResult)) + reportError("Optimizer introduced error into inline assembly."); } + if (!errorReporter.errors().empty()) + reportError("Failed to analyze inline assembly block."); + solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block."); - CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); + yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, m_evmVersion, identifierAccess, _system, _optimise); // Reset the source location to the one of the node (instead of the CODEGEN source location) updateSourceLocation(); @@ -446,6 +484,21 @@ void CompilerContext::updateSourceLocation() m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location()); } +eth::Assembly::OptimiserSettings CompilerContext::translateOptimiserSettings(OptimiserSettings const& _settings) +{ + // Constructing it this way so that we notice changes in the fields. + eth::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, m_evmVersion, 0}; + asmSettings.isCreation = true; + asmSettings.runJumpdestRemover = _settings.runJumpdestRemover; + asmSettings.runPeephole = _settings.runPeephole; + asmSettings.runDeduplicate = _settings.runDeduplicate; + asmSettings.runCSE = _settings.runCSE; + asmSettings.runConstantOptimiser = _settings.runConstantOptimiser; + asmSettings.expectedExecutionsPerDeployment = _settings.expectedExecutionsPerDeployment; + asmSettings.evmVersion = m_evmVersion; + return asmSettings; +} + eth::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel( Declaration const& _declaration, CompilerContext& _context diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index e5ddfbc5b..44f96f5dd 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -50,7 +52,7 @@ class Compiler; class CompilerContext { public: - explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr): + explicit CompilerContext(langutil::EVMVersion _evmVersion, CompilerContext* _runtimeContext = nullptr): m_asm(std::make_shared()), m_evmVersion(_evmVersion), m_runtimeContext(_runtimeContext), @@ -60,7 +62,7 @@ public: m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); } - EVMVersion const& evmVersion() const { return m_evmVersion; } + langutil::EVMVersion const& evmVersion() const { return m_evmVersion; } /// Update currently enabled set of experimental features. void setExperimentalFeatures(std::set const& _features) { m_experimentalFeatures = _features; } @@ -214,14 +216,15 @@ public: std::string const& _assembly, std::vector const& _localVariables = std::vector(), std::set const& _externallyUsedFunctions = std::set(), - bool _system = false + bool _system = false, + bool _optimise = false ); /// Appends arbitrary data to the end of the bytecode. void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } /// Run optimisation step. - void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); } + void optimise(OptimiserSettings const& _settings) { m_asm->optimise(translateOptimiserSettings(_settings)); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. CompilerContext* runtimeContext() const { return m_runtimeContext; } @@ -267,10 +270,12 @@ private: std::vector::const_iterator _searchStart ); /// @returns an iterator to the contract directly above the given contract. - std::vector::const_iterator superContract(const ContractDefinition &_contract) const; + std::vector::const_iterator superContract(ContractDefinition const& _contract) const; /// Updates source location set in the assembly. void updateSourceLocation(); + eth::Assembly::OptimiserSettings translateOptimiserSettings(OptimiserSettings const& _settings); + /** * Helper class that manages function labels and ensures that referenced functions are * compiled in a specific order. @@ -291,7 +296,7 @@ private: Declaration const* nextFunctionToCompile() const; /// Informs the queue that we are about to compile the given function, i.e. removes /// the function from the queue of functions to compile. - void startFunction(const Declaration &_function); + void startFunction(Declaration const& _function); /// Labels pointing to the entry points of functions. std::map m_entryLabels; @@ -305,7 +310,7 @@ private: eth::AssemblyPointer m_asm; /// Version of the EVM to compile against. - EVMVersion m_evmVersion; + langutil::EVMVersion m_evmVersion; /// Activated experimental features. std::set m_experimentalFeatures; /// Other already compiled contracts to be used in contract creation calls. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index f8c8b3a88..b32e744b1 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -37,11 +37,11 @@ namespace dev namespace solidity { -const unsigned CompilerUtils::dataStartOffset = 4; -const size_t CompilerUtils::freeMemoryPointer = 64; -const size_t CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32; -const size_t CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32; -const unsigned CompilerUtils::identityContractAddress = 4; +unsigned const CompilerUtils::dataStartOffset = 4; +size_t const CompilerUtils::freeMemoryPointer = 64; +size_t const CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32; +size_t const CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32; +unsigned const CompilerUtils::identityContractAddress = 4; static_assert(CompilerUtils::freeMemoryPointer >= 64, "Free memory pointer must not overlap with scratch area."); static_assert(CompilerUtils::zeroPointer >= CompilerUtils::freeMemoryPointer + 32, "Zero pointer must not overlap with free memory pointer."); @@ -70,6 +70,13 @@ void CompilerUtils::allocateMemory() storeFreeMemoryPointer(); } +void CompilerUtils::allocateMemory(u256 const& size) +{ + fetchFreeMemoryPointer(); + m_context << Instruction::DUP1 << size << Instruction::ADD; + storeFreeMemoryPointer(); +} + void CompilerUtils::toSizeAfterFreeMemoryPointer() { fetchFreeMemoryPointer(); @@ -243,7 +250,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem gt(add(array_data_start, mul(array_length, )), input_end) ) { revert(0, 0) } })"); - templ("item_size", to_string(arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true))); + templ("item_size", to_string(arrayType.calldataStride())); m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr"}); // stack: v1 v2 ... v(k-1) input_end base_offset current_offset v(k) moveIntoStack(3); @@ -279,11 +286,10 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem // stack: input_end base_offset next_pointer array_length data_pointer m_context << Instruction::SWAP2; // stack: input_end base_offset data_pointer array_length next_pointer - unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true); m_context.appendInlineAssembly(R"({ if or( gt(array_length, 0x100000000), - gt(add(data_ptr, mul(array_length, )" + to_string(itemSize) + R"()), input_end) + gt(add(data_ptr, mul(array_length, )" + to_string(arrayType.calldataStride()) + R"()), input_end) ) { revert(0, 0) } })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); } @@ -517,7 +523,7 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) codecopy(memptr, codesize(), size) memptr := add(memptr, size) })"); - templ("element_size", to_string(_type.isByteArray() ? 1 : _type.baseType()->memoryHeadSize())); + templ("element_size", to_string(_type.memoryStride())); m_context.appendInlineAssembly(templ.render(), {"length", "memptr"}); } else @@ -653,10 +659,13 @@ void CompilerUtils::convertType( bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer; if (chopSignBitsPending) { - const IntegerType& targetIntegerType = dynamic_cast(_targetType); + IntegerType const& targetIntegerType = dynamic_cast(_targetType); chopSignBitsPending = targetIntegerType.isSigned(); } + if (targetTypeCategory == Type::Category::FixedPoint) + solUnimplemented("Not yet implemented - FixedPointType."); + switch (stackTypeCategory) { case Type::Category::FixedBytes: @@ -811,9 +820,8 @@ void CompilerUtils::convertType( { auto const& arrayType = dynamic_cast(_targetType); solAssert(arrayType.isByteArray(), ""); - u256 storageSize(32 + ((data.size() + 31) / 32) * 32); - m_context << storageSize; - allocateMemory(); + unsigned storageSize = 32 + ((data.size() + 31) / 32) * 32; + allocateMemory(storageSize); // stack: mempos m_context << Instruction::DUP1 << u256(data.size()); storeInMemoryDynamic(IntegerType::uint256()); @@ -936,8 +944,7 @@ void CompilerUtils::convertType( { case DataLocation::Storage: // stack: - m_context << typeOnStack.memorySize(); - allocateMemory(); + allocateMemory(typeOnStack.memorySize()); m_context << Instruction::SWAP1 << Instruction::DUP2; // stack: for (auto const& member: typeOnStack.members(nullptr)) @@ -1101,8 +1108,7 @@ void CompilerUtils::pushZeroValue(Type const& _type) 1, [type](CompilerContext& _context) { CompilerUtils utils(_context); - _context << u256(max(32u, type->calldataEncodedSize())); - utils.allocateMemory(); + utils.allocateMemory(max(32u, type->calldataEncodedSize())); _context << Instruction::DUP1; if (auto structType = dynamic_cast(type.get())) diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index d095e05f9..5e07e7bb4 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -49,6 +49,10 @@ public: /// Stack pre: /// Stack post: void allocateMemory(); + /// Allocates a number of bytes in memory as given on the stack. + /// Stack pre: + /// Stack post: + void allocateMemory(u256 const& size); /// Appends code that transforms memptr to (memptr - free_memptr) memptr /// Stack pre: /// Stack post: @@ -84,7 +88,6 @@ public: ); /// Stores a 256 bit integer from stack in memory. /// @param _offset offset in memory - /// @param _type type of the data on the stack void storeInMemory(unsigned _offset); /// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack /// and also updates that. For reference types, only copies the data pointer. Fails for @@ -187,6 +190,11 @@ public: /// Stack post: void memoryCopy(); + /// Stores the given string in memory. + /// Stack pre: mempos + /// Stack post: + void storeStringData(bytesConstRef _data); + /// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true) /// external function type
into two stack slots: /// address (right aligned), function identifier (right aligned) @@ -273,23 +281,18 @@ public: /// Bytes we need to the start of call data. /// - The size in bytes of the function (hash) identifier. - static const unsigned dataStartOffset; + static unsigned const dataStartOffset; /// Position of the free-memory-pointer in memory; - static const size_t freeMemoryPointer; + static size_t const freeMemoryPointer; /// Position of the memory slot that is always zero. - static const size_t zeroPointer; + static size_t const zeroPointer; /// Starting offset for memory available to the user (aka the contract). - static const size_t generalPurposeMemoryStart; + static size_t const generalPurposeMemoryStart; private: /// Address of the precompiled identity contract. - static const unsigned identityContractAddress; - - /// Stores the given string in memory. - /// Stack pre: mempos - /// Stack post: - void storeStringData(bytesConstRef _data); + static unsigned const identityContractAddress; /// Appends code that cleans higher-order bits for integer types. void cleanHigherOrderBits(IntegerType const& _typeOnStack); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index f843e07a2..b10882749 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -21,14 +21,16 @@ */ #include -#include #include #include #include +#include + #include #include #include + #include #include @@ -175,6 +177,7 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract) solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); m_context.pushSubroutineSize(m_context.runtimeSub()); m_context.pushSubroutineOffset(m_context.runtimeSub()); + // This code replaces the address added by appendDeployTimeAddress(). m_context.appendInlineAssembly(R"( { // If code starts at 11, an mstore(0) writes to the full PUSH20 plus data @@ -182,8 +185,7 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract) let codepos := 11 codecopy(codepos, subOffset, subSize) // Check that the first opcode is a PUSH20 - switch eq(0x73, byte(0, mload(codepos))) - case 0 { invalid() } + if iszero(eq(0x73, byte(0, mload(codepos)))) { invalid() } mstore(0, address()) mstore8(codepos, 0x73) return(codepos, subSize) @@ -353,7 +355,7 @@ bool hasPayableFunctions(ContractDefinition const& _contract) void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract) { map, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions(); - map, const eth::AssemblyItem> callDataUnpackerEntryPoints; + map, eth::AssemblyItem const> callDataUnpackerEntryPoints; if (_contract.isLibrary()) { @@ -389,7 +391,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac sortedIDs.emplace_back(it.first); } std::sort(sortedIDs.begin(), sortedIDs.end()); - appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimise_runs); + appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimiserSettings.expectedExecutionsPerDeployment); } m_context << notFound; @@ -482,7 +484,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); for (VariableDeclaration const* variable: _contract.stateVariables()) if (variable->value() && !variable->isConstant()) - ExpressionCompiler(m_context, m_optimise).appendStateVariableInitialization(*variable); + ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable); } bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration) @@ -495,9 +497,9 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration) m_continueTags.clear(); if (_variableDeclaration.isConstant()) - ExpressionCompiler(m_context, m_optimise).appendConstStateVariableAccessor(_variableDeclaration); + ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendConstStateVariableAccessor(_variableDeclaration); else - ExpressionCompiler(m_context, m_optimise).appendStateVariableAccessor(_variableDeclaration); + ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableAccessor(_variableDeclaration); return false; } @@ -713,10 +715,11 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } }; solAssert(_inlineAssembly.annotation().analysisInfo, ""); - CodeGenerator::assemble( + yul::CodeGenerator::assemble( _inlineAssembly.operations(), *_inlineAssembly.annotation().analysisInfo, *m_context.assemblyPtr(), + m_context.evmVersion(), identifierAccess ); m_context.setStackOffset(startStackHeight); @@ -975,7 +978,13 @@ void ContractCompiler::appendMissingFunctions() m_context.appendMissingLowLevelFunctions(); auto abiFunctions = m_context.abiFunctions().requestedFunctions(); if (!abiFunctions.first.empty()) - m_context.appendInlineAssembly("{" + move(abiFunctions.first) + "}", {}, abiFunctions.second, true); + m_context.appendInlineAssembly( + "{" + move(abiFunctions.first) + "}", + {}, + abiFunctions.second, + true, + m_optimiserSettings.runYulOptimiser + ); } void ContractCompiler::appendModifierOrFunctionCode() @@ -1050,7 +1059,7 @@ void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration con void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType) { - ExpressionCompiler expressionCompiler(m_context, m_optimise); + ExpressionCompiler expressionCompiler(m_context, m_optimiserSettings.runOrderLiterals); expressionCompiler.compile(_expression); if (_targetType) CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 9ab006f6e..86013eb87 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -28,8 +28,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ /** * Code generator at the contract level. Can be used to generate code for exactly one contract @@ -38,13 +40,15 @@ namespace solidity { class ContractCompiler: private ASTConstVisitor { public: - explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise, size_t _optimise_runs = 200): - m_optimise(_optimise), - m_optimise_runs(_optimise_runs), + explicit ContractCompiler( + ContractCompiler* _runtimeCompiler, + CompilerContext& _context, + OptimiserSettings _optimiserSettings + ): + m_optimiserSettings(std::move(_optimiserSettings)), m_runtimeCompiler(_runtimeCompiler), m_context(_context) { - m_context = CompilerContext(_context.evmVersion(), _runtimeCompiler ? &_runtimeCompiler->m_context : nullptr); } void compileContract( @@ -130,8 +134,7 @@ private: /// Sets the stack height for the visited loop. void storeStackHeight(ASTNode const* _node); - bool const m_optimise; - size_t const m_optimise_runs = 200; + OptimiserSettings const m_optimiserSettings; /// Pointer to the runtime compiler in case this is a creation compiler. ContractCompiler* m_runtimeCompiler = nullptr; CompilerContext& m_context; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 1e7a84e65..a72b31dfe 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -106,18 +106,49 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& if (auto mappingType = dynamic_cast(returnType.get())) { solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); - solUnimplementedAssert( - !paramTypes[i]->isDynamicallySized(), - "Accessors for mapping with dynamically-sized keys not yet implemented." - ); + // pop offset m_context << Instruction::POP; - // move storage offset to memory. - utils().storeInMemory(32); - // move key to memory. - utils().copyToStackTop(paramTypes.size() - i, 1); - utils().storeInMemory(0); - m_context << u256(64) << u256(0) << Instruction::KECCAK256; + if (paramTypes[i]->isDynamicallySized()) + { + solAssert( + dynamic_cast(*paramTypes[i]).isByteArray(), + "Expected string or byte array for mapping key type" + ); + + // stack: + + // copy key[i] to top. + utils().copyToStackTop(paramTypes.size() - i + 1, 1); + + m_context.appendInlineAssembly(R"({ + let key_len := mload(key_ptr) + // Temp. use the memory after the array data for the slot + // position + let post_data_ptr := add(key_ptr, add(key_len, 0x20)) + let orig_data := mload(post_data_ptr) + mstore(post_data_ptr, slot_pos) + let hash := keccak256(add(key_ptr, 0x20), add(key_len, 0x20)) + mstore(post_data_ptr, orig_data) + slot_pos := hash + })", {"slot_pos", "key_ptr"}); + + m_context << Instruction::POP; + } + else + { + solAssert(paramTypes[i]->isValueType(), "Expected value type for mapping key"); + + // move storage offset to memory. + utils().storeInMemory(32); + + // move key to memory. + utils().copyToStackTop(paramTypes.size() - i, 1); + utils().storeInMemory(0); + m_context << u256(64) << u256(0); + m_context << Instruction::KECCAK256; + } + // push offset m_context << u256(0); returnType = mappingType->valueType(); @@ -286,8 +317,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) ArrayType const& arrayType = dynamic_cast(*_tuple.annotation().type); solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array."); - m_context << max(u256(32u), arrayType.memorySize()); - utils().allocateMemory(); + utils().allocateMemory(max(u256(32u), arrayType.memorySize())); m_context << Instruction::DUP1; for (auto const& component: _tuple.components()) @@ -418,7 +448,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) { return dynamic_cast(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber; }; - bool swap = m_optimize && TokenTraits::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); + bool swap = m_optimiseOrderLiterals && TokenTraits::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) { leftExpression.accept(*this); @@ -496,8 +526,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) TypeType const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); - m_context << max(u256(32u), structType.memorySize()); - utils().allocateMemory(); + utils().allocateMemory(max(u256(32u), structType.memorySize())); m_context << Instruction::DUP1; for (unsigned i = 0; i < arguments.size(); ++i) @@ -1310,8 +1339,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) utils().leftShiftNumberOnStack(224); } else - solAssert(!!_memberAccess.expression().annotation().type->memberType(member), - "Invalid member access to function."); + solAssert( + !!_memberAccess.expression().annotation().type->memberType(member), + "Invalid member access to function." + ); break; case Type::Category::Magic: // we can ignore the kind of magic and only look at the name of the member @@ -1359,6 +1390,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) ); m_context << Instruction::POP; } + else if (member == "name") + { + TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); + ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + utils().allocateMemory(contract.name().length() + 32); + // store string length + m_context << u256(contract.name().length()) << Instruction::DUP2 << Instruction::MSTORE; + // adjust pointer + m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; + utils().storeStringData(contract.name()); + } else solAssert(false, "Unknown magic member."); break; @@ -1965,12 +2007,18 @@ void ExpressionCompiler::appendExternalFunctionCall( // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place. // Move arguments to memory, will not update the free memory pointer (but will update the memory // pointer on the stack). + bool encodeInPlace = _functionType.takesArbitraryParameters() || _functionType.isBareCall(); + if (_functionType.kind() == FunctionType::Kind::ECRecover) + // This would be the only combination of padding and in-place encoding, + // but all parameters of ecrecover are value types anyway. + encodeInPlace = false; + bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; utils().encodeToMemory( argumentTypes, parameterTypes, _functionType.padArguments(), - _functionType.takesArbitraryParameters() || _functionType.isBareCall(), - isDelegateCall + encodeInPlace, + encodeForLibraryCall ); // Stack now: @@ -2083,7 +2131,7 @@ void ExpressionCompiler::appendExternalFunctionCall( mstore(v, returndatasize()) returndatacopy(add(v, 0x20), 0, returndatasize()) } - })", {"v"}); + })", {"v"}); } else utils().pushZeroPointer(); diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 4a6e43ff9..ca4e05b5f 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -55,11 +55,8 @@ class ArrayType; class ExpressionCompiler: private ASTConstVisitor { public: - /// Appends code for a State Variable accessor function - static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false); - - explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false): - m_optimize(_optimize), m_context(_compilerContext) {} + explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimiseOrderLiterals): + m_optimiseOrderLiterals(_optimiseOrderLiterals), m_context(_compilerContext) {} /// Compile the given @a _expression and leave its value on the stack. void compile(Expression const& _expression); @@ -71,7 +68,7 @@ public: void appendStateVariableAccessor(VariableDeclaration const& _varDecl); /// Appends code for a Constant State Variable accessor function - void appendConstStateVariableAccessor(const VariableDeclaration& _varDecl); + void appendConstStateVariableAccessor(VariableDeclaration const& _varDecl); private: bool visit(Conditional const& _condition) override; @@ -127,7 +124,7 @@ private: /// @returns the CompilerUtils object containing the current context. CompilerUtils utils(); - bool m_optimize; + bool m_optimiseOrderLiterals; CompilerContext& m_context; std::unique_ptr m_currentLValue; diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 70dbee812..d3fe4e439 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -473,7 +473,7 @@ void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeRefer m_context << Instruction::SWAP1 << Instruction::SSTORE; } -StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const ArrayType& _arrayType): +StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, ArrayType const& _arrayType): LValue(_compilerContext, _arrayType.memberType("length").get()), m_arrayType(_arrayType) { diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 912e2a69b..1f6e85356 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -109,6 +108,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function) m_expressions.clear(); m_globalContext.clear(); m_uninterpretedTerms.clear(); + m_overflowTargets.clear(); resetStateVariables(); initializeLocalVariables(_function); m_loopExecutionHappened = false; @@ -126,7 +126,10 @@ void SMTChecker::endVisit(FunctionDefinition const&) // Otherwise we remove any local variables from the context and // keep the state variables. if (isRootFunction()) + { + checkUnderOverflow(); removeLocalVariables(); + } m_functionPath.pop_back(); } @@ -316,21 +319,56 @@ void SMTChecker::endVisit(TupleExpression const& _tuple) defineExpr(_tuple, expr(*_tuple.components()[0])); } -void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location) +void SMTChecker::addOverflowTarget( + OverflowTarget::Type _type, + TypePointer _intType, + smt::Expression _value, + SourceLocation const& _location +) { - checkCondition( - _value < minValue(_type), - _location, - "Underflow (resulting value less than " + formatNumberReadable(_type.minValue()) + ")", - "", - &_value + m_overflowTargets.emplace_back( + _type, + std::move(_intType), + std::move(_value), + currentPathConditions(), + _location ); +} + +void SMTChecker::checkUnderOverflow() +{ + for (auto& target: m_overflowTargets) + { + if (target.type != OverflowTarget::Type::Overflow) + checkUnderflow(target); + if (target.type != OverflowTarget::Type::Underflow) + checkOverflow(target); + } +} + +void SMTChecker::checkUnderflow(OverflowTarget& _target) +{ + solAssert(_target.type != OverflowTarget::Type::Overflow, ""); + auto intType = dynamic_cast(_target.intType.get()); checkCondition( - _value > maxValue(_type), - _location, - "Overflow (resulting value larger than " + formatNumberReadable(_type.maxValue()) + ")", + _target.path && _target.value < minValue(*intType), + _target.location, + "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")", "", - &_value + &_target.value + ); +} + +void SMTChecker::checkOverflow(OverflowTarget& _target) +{ + solAssert(_target.type != OverflowTarget::Type::Underflow, ""); + auto intType = dynamic_cast(_target.intType.get()); + checkCondition( + _target.path && _target.value > maxValue(*intType), + _target.location, + "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")", + "", + &_target.value ); } @@ -376,8 +414,13 @@ void SMTChecker::endVisit(UnaryOperation const& _op) case Token::Sub: // - { defineExpr(_op, 0 - expr(_op.subExpression())); - if (auto intType = dynamic_cast(_op.annotation().type.get())) - checkUnderOverflow(expr(_op), *intType, _op.location()); + if (_op.annotation().type->category() == Type::Category::Integer) + addOverflowTarget( + OverflowTarget::Type::All, + _op.annotation().type, + expr(_op), + _op.location() + ); break; } default: @@ -582,10 +625,8 @@ void SMTChecker::endVisit(Identifier const& _identifier) { // Will be translated as part of the node that requested the lvalue. } - else if (dynamic_cast(_identifier.annotation().type.get())) - { + else if (_identifier.annotation().type->category() == Type::Category::Function) visitFunctionIdentifier(_identifier); - } else if (isSupportedType(_identifier.annotation().type->category())) { if (VariableDeclaration const* decl = dynamic_cast(_identifier.annotation().referencedDeclaration)) @@ -654,6 +695,7 @@ void SMTChecker::visitFunctionIdentifier(Identifier const& _identifier) void SMTChecker::endVisit(Literal const& _literal) { + solAssert(_literal.annotation().type, "Expected type for AST node"); Type const& type = *_literal.annotation().type; if (isNumber(type.category())) @@ -853,9 +895,28 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) m_interface->addAssertion(right != 0); } - checkUnderOverflow(value, intType, _op.location()); + addOverflowTarget( + OverflowTarget::Type::All, + _op.annotation().commonType, + value, + _op.location() + ); + + smt::Expression intValueRange = (0 - minValue(intType)) + maxValue(intType) + 1; + defineExpr(_op, smt::Expression::ite( + value > maxValue(intType) || value < minValue(intType), + value % intValueRange, + value + )); + if (intType.isSigned()) + { + defineExpr(_op, smt::Expression::ite( + expr(_op) > maxValue(intType), + expr(_op) - intValueRange, + expr(_op) + )); + } - defineExpr(_op, value); break; } default: @@ -944,11 +1005,11 @@ void SMTChecker::assignment(VariableDeclaration const& _variable, Expression con void SMTChecker::assignment(VariableDeclaration const& _variable, smt::Expression const& _value, SourceLocation const& _location) { TypePointer type = _variable.type(); - if (auto const* intType = dynamic_cast(type.get())) - checkUnderOverflow(_value, *intType, _location); - else if (dynamic_cast(type.get())) - checkUnderOverflow(_value, IntegerType(160), _location); - else if (dynamic_cast(type.get())) + if (type->category() == Type::Category::Integer) + addOverflowTarget(OverflowTarget::Type::All, type, _value, _location); + else if (type->category() == Type::Category::Address) + addOverflowTarget(OverflowTarget::Type::All, make_shared(160), _value, _location); + else if (type->category() == Type::Category::Mapping) arrayAssignment(); m_interface->addAssertion(newValue(_variable) == _value); } @@ -1371,7 +1432,7 @@ void SMTChecker::createExpr(Expression const& _e) void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value) { createExpr(_e); - solAssert(isSupportedType(*_e.annotation().type), "Equality operator applied to type that is not fully supported"); + solAssert(smtKind(_e.annotation().type->category()) != smt::Kind::Function, "Equality operator applied to type that is not fully supported"); m_interface->addAssertion(expr(_e) == _value); } diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 1521c613b..ccb7e1e4d 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -137,9 +137,33 @@ private: Expression const& _condition, std::string const& _description ); - /// Checks that the value is in the range given by the type. - void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, langutil::SourceLocation const& _location); + struct OverflowTarget + { + enum class Type { Underflow, Overflow, All } type; + TypePointer intType; + smt::Expression value; + smt::Expression path; + langutil::SourceLocation const& location; + + OverflowTarget(Type _type, TypePointer _intType, smt::Expression _value, smt::Expression _path, langutil::SourceLocation const& _location): + type(_type), + intType(_intType), + value(_value), + path(_path), + location(_location) + { + solAssert(dynamic_cast(intType.get()), ""); + } + }; + + /// Checks that the value is in the range given by the type. + void checkUnderflow(OverflowTarget& _target); + void checkOverflow(OverflowTarget& _target); + /// Calls the functions above for all elements in m_overflowTargets accordingly. + void checkUnderOverflow(); + /// Adds an overflow target for lazy check at the end of the function. + void addOverflowTarget(OverflowTarget::Type _type, TypePointer _intType, smt::Expression _value, langutil::SourceLocation const& _location); std::pair> checkSatisfiableAndGenerateModel(std::vector const& _expressionsToEvaluate); @@ -244,6 +268,8 @@ private: bool isRootFunction(); /// Returns true if _funDef was already visited. bool visitedFunction(FunctionDefinition const* _funDef); + + std::vector m_overflowTargets; }; } diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index c4fc81da8..57921558c 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -65,7 +65,7 @@ smt::Expression SymbolicVariable::increaseIndex() SymbolicBoolVariable::SymbolicBoolVariable( TypePointer _type, string const& _uniqueName, - smt::SolverInterface&_interface + smt::SolverInterface& _interface ): SymbolicVariable(move(_type), _uniqueName, _interface) { @@ -102,7 +102,7 @@ SymbolicFixedBytesVariable::SymbolicFixedBytesVariable( SymbolicFunctionVariable::SymbolicFunctionVariable( TypePointer _type, string const& _uniqueName, - smt::SolverInterface&_interface + smt::SolverInterface& _interface ): SymbolicVariable(move(_type), _uniqueName, _interface), m_declaration(m_interface.newVariable(currentName(), smtSort(*m_type))) diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 0d27109e5..3c33eae8e 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index b97e064e8..c0dc2ffd7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -86,17 +86,55 @@ boost::optional CompilerStack::parseRemapping(string c void CompilerStack::setRemappings(vector const& _remappings) { + if (m_stackState >= ParsingSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set remappings before parsing.")); for (auto const& remapping: _remappings) solAssert(!remapping.prefix.empty(), ""); m_remappings = _remappings; } -void CompilerStack::setEVMVersion(EVMVersion _version) +void CompilerStack::setEVMVersion(langutil::EVMVersion _version) { - solAssert(m_stackState < State::ParsingSuccessful, "Set EVM version after parsing."); + if (m_stackState >= ParsingSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set EVM version before parsing.")); m_evmVersion = _version; } +void CompilerStack::setLibraries(std::map const& _libraries) +{ + if (m_stackState >= ParsingSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set libraries before parsing.")); + m_libraries = _libraries; +} + +void CompilerStack::setOptimiserSettings(bool _optimize, unsigned _runs) +{ + OptimiserSettings settings = _optimize ? OptimiserSettings::enabled() : OptimiserSettings::minimal(); + settings.expectedExecutionsPerDeployment = _runs; + setOptimiserSettings(std::move(settings)); +} + +void CompilerStack::setOptimiserSettings(OptimiserSettings _settings) +{ + if (m_stackState >= ParsingSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing.")); + m_optimiserSettings = std::move(_settings); +} + +void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources) +{ + if (m_stackState >= ParsingSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set use literal sources before parsing.")); + m_metadataLiteralSources = _metadataLiteralSources; +} + +void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response) +{ + if (m_stackState >= ParsingSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must add SMTLib2 responses before parsing.")); + m_smtlib2Responses[_hash] = _response; +} + void CompilerStack::reset(bool _keepSources) { if (_keepSources) @@ -113,9 +151,8 @@ void CompilerStack::reset(bool _keepSources) m_smtlib2Responses.clear(); m_unhandledSMTLib2Queries.clear(); m_libraries.clear(); - m_evmVersion = EVMVersion(); - m_optimize = false; - m_optimizeRuns = 200; + m_evmVersion = langutil::EVMVersion(); + m_optimiserSettings = OptimiserSettings::minimal(); m_globalContext.reset(); m_scopes.clear(); m_sourceOrder.clear(); @@ -135,9 +172,8 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool bool CompilerStack::parse() { - //reset if (m_stackState != SourcesSet) - return false; + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call parse only after the SourcesSet state.")); m_errorReporter.clear(); ASTNode::resetID(); @@ -178,8 +214,8 @@ bool CompilerStack::parse() bool CompilerStack::analyze() { - if (m_stackState != ParsingSuccessful) - return false; + if (m_stackState != ParsingSuccessful || m_stackState >= AnalysisSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was successful.")); resolveImports(); bool noErrors = true; @@ -595,10 +631,24 @@ Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const string const& CompilerStack::metadata(string const& _contractName) const { - if (m_stackState != CompilationSuccessful) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); + if (m_stackState < AnalysisSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); - return contract(_contractName).metadata; + return metadata(contract(_contractName)); +} + +string const& CompilerStack::metadata(Contract const& _contract) const +{ + if (m_stackState < AnalysisSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); + + solAssert(_contract.contract, ""); + + // cache the result + if (!_contract.metadata) + _contract.metadata.reset(new string(createMetadata(_contract))); + + return *_contract.metadata; } Scanner const& CompilerStack::scanner(string const& _sourceName) const @@ -809,14 +859,11 @@ void CompilerStack::compileContract( Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); - shared_ptr compiler = make_shared(m_evmVersion, m_optimize, m_optimizeRuns); + shared_ptr compiler = make_shared(m_evmVersion, m_optimiserSettings); compiledContract.compiler = compiler; - string metadata = createMetadata(compiledContract); - compiledContract.metadata = metadata; - bytes cborEncodedMetadata = createCBORMetadata( - metadata, + metadata(compiledContract), !onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures) ); @@ -922,8 +969,35 @@ string CompilerStack::createMetadata(Contract const& _contract) const meta["sources"][s.first]["urls"].append("bzzr://" + toHex(s.second.swarmHash().asBytes())); } } - meta["settings"]["optimizer"]["enabled"] = m_optimize; - meta["settings"]["optimizer"]["runs"] = m_optimizeRuns; + + static_assert(sizeof(m_optimiserSettings.expectedExecutionsPerDeployment) <= sizeof(Json::LargestUInt), "Invalid word size."); + solAssert(static_cast(m_optimiserSettings.expectedExecutionsPerDeployment) < std::numeric_limits::max(), ""); + meta["settings"]["optimizer"]["runs"] = Json::Value(Json::LargestUInt(m_optimiserSettings.expectedExecutionsPerDeployment)); + + /// Backwards compatibility: If set to one of the default settings, do not provide details. + OptimiserSettings settingsWithoutRuns = m_optimiserSettings; + // reset to default + settingsWithoutRuns.expectedExecutionsPerDeployment = OptimiserSettings::minimal().expectedExecutionsPerDeployment; + if (settingsWithoutRuns == OptimiserSettings::minimal()) + meta["settings"]["optimizer"]["enabled"] = false; + else if (settingsWithoutRuns == OptimiserSettings::enabled()) + meta["settings"]["optimizer"]["enabled"] = true; + else + { + Json::Value details{Json::objectValue}; + + details["orderLiterals"] = m_optimiserSettings.runOrderLiterals; + details["jumpdestRemover"] = m_optimiserSettings.runJumpdestRemover; + details["peephole"] = m_optimiserSettings.runPeephole; + details["deduplicate"] = m_optimiserSettings.runDeduplicate; + details["cse"] = m_optimiserSettings.runCSE; + details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser; + details["yul"] = m_optimiserSettings.runYulOptimiser; + details["yulDetails"] = Json::objectValue; + + meta["settings"]["optimizer"]["details"] = std::move(details); + } + meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = _contract.contract->annotation().canonicalName; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 799278503..e8eb585c6 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include @@ -117,43 +118,43 @@ public: static boost::optional parseRemapping(std::string const& _remapping); /// Sets path remappings. + /// Must be set before parsing. void setRemappings(std::vector const& _remappings); /// Sets library addresses. Addresses are cleared iff @a _libraries is missing. - /// Will not take effect before running compile. - void setLibraries(std::map const& _libraries = std::map{}) - { - m_libraries = _libraries; - } + /// Must be set before parsing. + void setLibraries(std::map const& _libraries = std::map{}); /// Changes the optimiser settings. - /// Will not take effect before running compile. - void setOptimiserSettings(bool _optimize, unsigned _runs = 200) - { - m_optimize = _optimize; - m_optimizeRuns = _runs; - } + /// Must be set before parsing. + void setOptimiserSettings(bool _optimize, unsigned _runs = 200); + + /// Changes the optimiser settings. + /// Must be set before parsing. + void setOptimiserSettings(OptimiserSettings _settings); /// Set the EVM version used before running compile. /// When called without an argument it will revert to the default version. - void setEVMVersion(EVMVersion _version = EVMVersion{}); + /// Must be set before parsing. + void setEVMVersion(langutil::EVMVersion _version = langutil::EVMVersion{}); /// Sets the list of requested contract names. If empty, no filtering is performed and every contract /// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing. - void setRequestedContractNames(std::set const& _contractNames = std::set{}) - { + void setRequestedContractNames(std::set const& _contractNames = std::set{}) { m_requestedContractNames = _contractNames; } /// @arg _metadataLiteralSources When true, store sources as literals in the contract metadata. - void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } + /// Must be set before parsing. + void useMetadataLiteralSources(bool _metadataLiteralSources); /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// @returns true if a source object by the name already existed and was replaced. bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false); /// Adds a response to an SMTLib2 query (identified by the hash of the query input). - void addSMTLib2Response(h256 const& _hash, std::string const& _response) { m_smtlib2Responses[_hash] = _response; } + /// Must be set before parsing. + void addSMTLib2Response(h256 const& _hash, std::string const& _response); /// Parses all source units that were added /// @returns false on error. @@ -275,7 +276,7 @@ private: std::shared_ptr compiler; eth::LinkerObject object; ///< Deployment object (includes the runtime sub-object). eth::LinkerObject runtimeObject; ///< Runtime object. - std::string metadata; ///< The metadata json that will be hashed into the chain. + mutable std::unique_ptr metadata; ///< The metadata json that will be hashed into the chain. mutable std::unique_ptr abi; mutable std::unique_ptr userDocumentation; mutable std::unique_ptr devDocumentation; @@ -338,6 +339,10 @@ private: /// This will generate the JSON object and store it in the Contract object if it is not present yet. Json::Value const& natspecDev(Contract const&) const; + /// @returns the Contract Metadata + /// This will generate the metadata and store it in the Contract object if it is not present yet. + std::string const& metadata(Contract const&) const; + /// @returns the offset of the entry point of the given function into the list of assembly items /// or zero if it is not found or does not exist. size_t functionEntryPoint( @@ -346,9 +351,8 @@ private: ) const; ReadCallback::Callback m_readFile; - bool m_optimize = false; - unsigned m_optimizeRuns = 200; - EVMVersion m_evmVersion; + OptimiserSettings m_optimiserSettings; + langutil::EVMVersion m_evmVersion; std::set m_requestedContractNames; std::map m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h index f40cffebd..784fada2f 100644 --- a/libsolidity/interface/GasEstimator.h +++ b/libsolidity/interface/GasEstimator.h @@ -47,7 +47,7 @@ public: using ASTGasConsumptionSelfAccumulated = std::map>; - explicit GasEstimator(EVMVersion _evmVersion): m_evmVersion(_evmVersion) {} + explicit GasEstimator(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) {} /// Estimates the gas consumption for every assembly item in the given assembly and stores /// it by source location. @@ -84,7 +84,7 @@ public: private: /// @returns the set of AST nodes which are the finest nodes at their location. static std::set finestNodesAtLocation(std::vector const& _roots); - EVMVersion m_evmVersion; + langutil::EVMVersion m_evmVersion; }; } diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index 7a89abaed..e28d8703e 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Lefteris @@ -118,7 +118,7 @@ string Natspec::extractDoc(multimap const& _tags, string const& return value; } -Json::Value Natspec::devDocumentation(std::multimap const &_tags) +Json::Value Natspec::devDocumentation(std::multimap const& _tags) { Json::Value json(Json::objectValue); auto dev = extractDoc(_tags, "dev"); diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h index fbaa6d4da..7a0c40a1a 100644 --- a/libsolidity/interface/Natspec.h +++ b/libsolidity/interface/Natspec.h @@ -59,7 +59,7 @@ private: /// @param _tags docTags that are used. /// @return A JSON representation /// of the contract's developer documentation - static Json::Value devDocumentation(std::multimap const &_tags); + static Json::Value devDocumentation(std::multimap const& _tags); }; } //solidity NS diff --git a/libsolidity/interface/OptimiserSettings.h b/libsolidity/interface/OptimiserSettings.h new file mode 100644 index 000000000..aae5fa1ce --- /dev/null +++ b/libsolidity/interface/OptimiserSettings.h @@ -0,0 +1,105 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @author Alex Beregszaszi + * @date 2017 + * Helper class for optimiser settings. + */ + +#pragma once + +#include + +namespace dev +{ +namespace solidity +{ + +struct OptimiserSettings +{ + /// No optimisations at all - not recommended. + static OptimiserSettings none() + { + return {}; + } + /// Minimal optimisations: Peephole and jumpdest remover + static OptimiserSettings minimal() + { + OptimiserSettings s = none(); + s.runJumpdestRemover = true; + s.runPeephole = true; + return s; + } + /// Standard optimisations. + static OptimiserSettings enabled() + { + OptimiserSettings s; + s.runOrderLiterals = true; + s.runJumpdestRemover = true; + s.runPeephole = true; + s.runDeduplicate = true; + s.runCSE = true; + s.runConstantOptimiser = true; + // The only disabled one + s.runYulOptimiser = false; + s.expectedExecutionsPerDeployment = 200; + return s; + } + /// Standard optimisations plus yul optimiser. + static OptimiserSettings full() + { + OptimiserSettings s = enabled(); + s.runYulOptimiser = true; + return s; + } + + bool operator==(OptimiserSettings const& _other) const + { + return + runOrderLiterals == _other.runOrderLiterals && + runJumpdestRemover == _other.runJumpdestRemover && + runPeephole == _other.runPeephole && + runDeduplicate == _other.runDeduplicate && + runCSE == _other.runCSE && + runConstantOptimiser == _other.runConstantOptimiser && + runYulOptimiser == _other.runYulOptimiser && + expectedExecutionsPerDeployment == _other.expectedExecutionsPerDeployment; + } + + /// Move literals to the right of commutative binary operators during code generation. + /// This helps exploiting associativity. + bool runOrderLiterals = false; + /// Non-referenced jump destination remover. + bool runJumpdestRemover = false; + /// Peephole optimizer + bool runPeephole = false; + /// Assembly block deduplicator + bool runDeduplicate = false; + /// Common subexpression eliminator based on assembly items. + bool runCSE = false; + /// Constant optimizer, which tries to find better representations that satisfy the given + /// size/cost-trade-off. + bool runConstantOptimiser = false; + /// Yul optimiser with default settings. Will only run on certain parts of the code for now. + bool runYulOptimiser = false; + /// This specifies an estimate on how often each opcode in this assembly will be executed, + /// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage. + size_t expectedExecutionsPerDeployment = 200; +}; + +} +} diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 606e858c6..88080a70b 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -201,7 +201,6 @@ bool isBinaryRequested(Json::Value const& _outputSelection) // This does not inculde "evm.methodIdentifiers" on purpose! static vector const outputsThatRequireBinaries{ "*", - "metadata", // This is only generated at the end of compilation, but could be generated earlier. "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", @@ -292,10 +291,27 @@ boost::optional checkSettingsKeys(Json::Value const& _input) boost::optional checkOptimizerKeys(Json::Value const& _input) { - static set keys{"enabled", "runs"}; + static set keys{"details", "enabled", "runs"}; return checkKeys(_input, keys, "settings.optimizer"); } +boost::optional checkOptimizerDetailsKeys(Json::Value const& _input) +{ + static set keys{"peephole", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails"}; + return checkKeys(_input, keys, "settings.optimizer.details"); +} + +boost::optional checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting) +{ + if (_details.isMember(_name)) + { + if (!_details[_name].isBool()) + return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be Boolean"); + _setting = _details[_name].asBool(); + } + return {}; +} + boost::optional checkMetadataKeys(Json::Value const& _input) { if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool()) @@ -351,6 +367,61 @@ boost::optional checkOutputSelection(Json::Value const& _outputSele } +boost::optional StandardCompiler::parseOptimizerSettings(Json::Value const& _jsonInput) +{ + if (auto result = checkOptimizerKeys(_jsonInput)) + return *result; + + OptimiserSettings settings = OptimiserSettings::none(); + + if (_jsonInput.isMember("enabled")) + { + if (!_jsonInput["enabled"].isBool()) + return formatFatalError("JSONError", "The \"enabled\" setting must be a Boolean."); + + settings = _jsonInput["enabled"].asBool() ? OptimiserSettings::enabled() : OptimiserSettings::minimal(); + } + + if (_jsonInput.isMember("runs")) + { + if (!_jsonInput["runs"].isUInt()) + return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number."); + settings.expectedExecutionsPerDeployment = _jsonInput["runs"].asUInt(); + } + + if (_jsonInput.isMember("details")) + { + Json::Value const& details = _jsonInput["details"]; + if (auto result = checkOptimizerDetailsKeys(details)) + return *result; + + if (auto error = checkOptimizerDetail(details, "peephole", settings.runPeephole)) + return *error; + if (auto error = checkOptimizerDetail(details, "jumpdestRemover", settings.runJumpdestRemover)) + return *error; + if (auto error = checkOptimizerDetail(details, "orderLiterals", settings.runOrderLiterals)) + return *error; + if (auto error = checkOptimizerDetail(details, "deduplicate", settings.runDeduplicate)) + return *error; + if (auto error = checkOptimizerDetail(details, "cse", settings.runCSE)) + return *error; + if (auto error = checkOptimizerDetail(details, "constantOptimizer", settings.runConstantOptimiser)) + return *error; + if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser)) + return *error; + if (details.isMember("yulDetails")) + { + if (!_jsonInput["yulDetails"].isObject()) + return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting has to be a JSON object."); + if (!_jsonInput["yulDetails"].getMemberNames().empty()) + return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting cannot have any settings yet."); + } + } + m_compilerStack.setOptimiserSettings(std::move(settings)); + return {}; +} + + Json::Value StandardCompiler::compileInternal(Json::Value const& _input) { m_compilerStack.reset(false); @@ -490,7 +561,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) { if (!settings["evmVersion"].isString()) return formatFatalError("JSONError", "evmVersion must be a string."); - boost::optional version = EVMVersion::fromString(settings["evmVersion"].asString()); + boost::optional version = langutil::EVMVersion::fromString(settings["evmVersion"].asString()); if (!version) return formatFatalError("JSONError", "Invalid EVM version requested."); m_compilerStack.setEVMVersion(*version); @@ -512,29 +583,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.setRemappings(remappings); if (settings.isMember("optimizer")) - { - Json::Value optimizerSettings = settings["optimizer"]; - - if (auto result = checkOptimizerKeys(optimizerSettings)) + if (auto result = parseOptimizerSettings(settings["optimizer"])) return *result; - if (optimizerSettings.isMember("enabled")) - { - if (!optimizerSettings["enabled"].isBool()) - return formatFatalError("JSONError", "The \"enabled\" setting must be a boolean."); - - bool const optimize = optimizerSettings["enabled"].asBool(); - unsigned optimizeRuns = 200; - if (optimizerSettings.isMember("runs")) - { - if (!optimizerSettings["runs"].isUInt()) - return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number."); - optimizeRuns = optimizerSettings["runs"].asUInt(); - } - m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); - } - } - map libraries; Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); if (!jsonLibraries.isObject()) @@ -725,7 +776,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value contractData(Json::objectValue); if (isArtifactRequested(outputSelection, file, name, "abi")) contractData["abi"] = m_compilerStack.contractABI(contractName); - if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "metadata")) + if (isArtifactRequested(outputSelection, file, name, "metadata")) contractData["metadata"] = m_compilerStack.metadata(contractName); if (isArtifactRequested(outputSelection, file, name, "userdoc")) contractData["userdoc"] = m_compilerStack.natspecUser(contractName); diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index fc9c3a594..7cc567317 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -24,6 +24,8 @@ #include +#include + namespace dev { @@ -53,6 +55,10 @@ public: std::string compile(std::string const& _input) noexcept; private: + /// Validaes and applies the optimizer settings. + /// On error returns the json-formatted error message. + boost::optional parseOptimizerSettings(Json::Value const& _settings); + Json::Value compileInternal(Json::Value const& _input); CompilerStack m_compilerStack; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8cbac5a29..79ff447bc 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian @@ -1041,8 +1041,11 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con m_scanner->next(); } - yul::Parser asmParser(m_errorReporter, yul::EVMDialect::looseAssemblyForEVM()); + // Using latest EVM Version for now, it will be run again later. + yul::Parser asmParser(m_errorReporter, yul::EVMDialect::looseAssemblyForEVM(EVMVersion{})); shared_ptr block = asmParser.parse(m_scanner, true); + if (block == nullptr) + BOOST_THROW_EXCEPTION(FatalError()); nodeFactory.markEndPosition(); return nodeFactory.createNode(_docString, block); } diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index b8d0e9a8c..00e3f7519 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * @author Christian diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index d61aefb61..abb717f2d 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -1,18 +1,18 @@ /* - This file is part of solidity. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with solidity. If not, see . */ /** * Solidity and Yul both share the same Token (and Scanner) API. diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 62beb3d3f..abf2e3d1d 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -52,12 +52,14 @@ bool AsmAnalyzer::analyze(Block const& _block) if (!(ScopeFiller(m_info, m_errorReporter))(_block)) return false; - return (*this)(_block); + bool success = (*this)(_block); + if (!success) + solAssert(m_errorReporter.hasErrors(), "No success but no error."); + return success && !m_errorReporter.hasErrors(); } AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect( shared_ptr _dialect, - EVMVersion _evmVersion, Block const& _ast ) { @@ -67,7 +69,6 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect( bool success = yul::AsmAnalyzer( analysisInfo, errors, - _evmVersion, Error::Type::SyntaxError, _dialect ).analyze(_ast); @@ -638,59 +639,67 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); // Similarly we assume bitwise shifting and create2 go together. solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); + solAssert(m_dialect->flavour != AsmFlavour::Yul, ""); - if (_instr == solidity::Instruction::EXTCODEHASH) - m_errorReporter.warning( + auto errorForVM = [=](string const& vmKindMessage) { + m_errorReporter.typeError( _location, "The \"" + boost::to_lower_copy(instructionInfo(_instr).name) - + "\" instruction is not supported by the VM version \"" + - "" + m_evmVersion.name() + - "\" you are currently compiling for. " + - "It will be interpreted as an invalid instruction on this VM." - ); - else if (( - _instr == solidity::Instruction::RETURNDATACOPY || - _instr == solidity::Instruction::RETURNDATASIZE || - _instr == solidity::Instruction::STATICCALL - ) && !m_evmVersion.supportsReturndata()) - m_errorReporter.warning( - _location, - "The \"" + - boost::to_lower_copy(instructionInfo(_instr).name) - + "\" instruction is only available for Byzantium-compatible VMs. " + - "You are currently compiling for \"" + + + "\" instruction is " + + vmKindMessage + + " VMs " + + " (you are currently compiling for \"" + m_evmVersion.name() + - "\", where it will be interpreted as an invalid instruction." + "\")." ); + }; + + if (( + _instr == solidity::Instruction::RETURNDATACOPY || + _instr == solidity::Instruction::RETURNDATASIZE + ) && !m_evmVersion.supportsReturndata()) + { + errorForVM("only available for Byzantium-compatible"); + } + else if (_instr == solidity::Instruction::STATICCALL && !m_evmVersion.hasStaticCall()) + { + errorForVM("only available for Byzantium-compatible"); + } else if (( _instr == solidity::Instruction::SHL || _instr == solidity::Instruction::SHR || - _instr == solidity::Instruction::SAR || - _instr == solidity::Instruction::CREATE2 + _instr == solidity::Instruction::SAR ) && !m_evmVersion.hasBitwiseShifting()) - m_errorReporter.warning( - _location, - "The \"" + - boost::to_lower_copy(instructionInfo(_instr).name) - + "\" instruction is only available for Constantinople-compatible VMs. " + - "You are currently compiling for \"" + - m_evmVersion.name() + - "\", where it will be interpreted as an invalid instruction." - ); - - if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) { - if (m_dialect->flavour != AsmFlavour::Loose) - solAssert(m_errorTypeForLoose && *m_errorTypeForLoose != Error::Type::Warning, ""); - - m_errorReporter.error( - m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning, - _location, - "Jump instructions and labels are low-level EVM features that can lead to " - "incorrect stack access. Because of that they are discouraged. " - "Please consider using \"switch\", \"if\" or \"for\" statements instead." - ); + errorForVM("only available for Constantinople-compatible"); + } + else if (_instr == solidity::Instruction::CREATE2 && !m_evmVersion.hasCreate2()) + { + errorForVM("only available for Constantinople-compatible"); + } + else if (_instr == solidity::Instruction::EXTCODEHASH && !m_evmVersion.hasExtCodeHash()) + { + errorForVM("only available for Constantinople-compatible"); + } + else if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) + { + if (m_dialect->flavour == AsmFlavour::Loose) + m_errorReporter.error( + m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning, + _location, + "Jump instructions and labels are low-level EVM features that can lead to " + "incorrect stack access. Because of that they are discouraged. " + "Please consider using \"switch\", \"if\" or \"for\" statements instead." + ); + else + m_errorReporter.error( + Error::Type::SyntaxError, + _location, + "Jump instructions and labels are low-level EVM features that can lead to " + "incorrect stack access. Because of that they are disallowed in strict assembly. " + "Use functions, \"switch\", \"if\" or \"for\" statements instead." + ); } } diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index 19b0558cc..7d11391f9 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -57,7 +58,6 @@ public: explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, langutil::ErrorReporter& _errorReporter, - dev::solidity::EVMVersion _evmVersion, boost::optional _errorTypeForLoose, std::shared_ptr _dialect, ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver() @@ -65,16 +65,17 @@ public: m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), - m_evmVersion(_evmVersion), m_dialect(std::move(_dialect)), m_errorTypeForLoose(_errorTypeForLoose) - {} + { + if (EVMDialect const* evmDialect = dynamic_cast(m_dialect.get())) + m_evmVersion = evmDialect->evmVersion(); + } bool analyze(Block const& _block); static AsmAnalysisInfo analyzeStrictAssertCorrect( std::shared_ptr _dialect, - dev::solidity::EVMVersion _evmVersion, Block const& _ast ); @@ -120,7 +121,7 @@ private: std::set m_activeVariables; AsmAnalysisInfo& m_info; langutil::ErrorReporter& m_errorReporter; - dev::solidity::EVMVersion m_evmVersion; + langutil::EVMVersion m_evmVersion; std::shared_ptr m_dialect; boost::optional m_errorTypeForLoose; }; diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 217c838a7..0adab10b6 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -48,8 +48,7 @@ shared_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ } catch (FatalError const&) { - if (m_errorReporter.errors().empty()) - throw; // Something is weird here, rather throw again. + solAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); } return nullptr; } @@ -80,7 +79,7 @@ Statement Parser::parseStatement() case Token::If: { If _if = createWithLocation(); - m_scanner->next(); + advance(); _if.condition = make_unique(parseExpression()); _if.body = parseBlock(); return Statement{move(_if)}; @@ -88,15 +87,15 @@ Statement Parser::parseStatement() case Token::Switch: { Switch _switch = createWithLocation(); - m_scanner->next(); + advance(); _switch.expression = make_unique(parseExpression()); - while (m_scanner->currentToken() == Token::Case) + while (currentToken() == Token::Case) _switch.cases.emplace_back(parseCase()); - if (m_scanner->currentToken() == Token::Default) + if (currentToken() == Token::Default) _switch.cases.emplace_back(parseCase()); - if (m_scanner->currentToken() == Token::Default) + if (currentToken() == Token::Default) fatalParserError("Only one default case allowed."); - else if (m_scanner->currentToken() == Token::Case) + else if (currentToken() == Token::Case) fatalParserError("Case not allowed after default case."); if (_switch.cases.empty()) fatalParserError("Switch statement without any cases."); @@ -138,63 +137,66 @@ Statement Parser::parseStatement() return ExpressionStatement{locationOf(expr), expr}; } case Token::Comma: + case Token::AssemblyAssign: { - // if a comma follows, a multiple assignment is assumed + std::vector variableNames; - if (elementary.type() != typeid(Identifier)) - fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); - Identifier const& identifier = boost::get(elementary); - - Assignment assignment = createWithLocation(identifier.location); - assignment.variableNames.emplace_back(identifier); - - do + while (true) { - expectToken(Token::Comma); - elementary = parseElementaryOperation(); if (elementary.type() != typeid(Identifier)) - fatalParserError("Variable name expected in multiple assignment."); - assignment.variableNames.emplace_back(boost::get(elementary)); - } - while (currentToken() == Token::Comma); + { + auto const token = currentToken() == Token::Comma ? "," : ":="; - expectToken(Token::Colon); - expectToken(Token::Assign); + fatalParserError( + std::string("Variable name must precede \"") + + token + + "\"" + + (currentToken() == Token::Comma ? " in multiple assignment." : " in assignment.") + ); + } + + auto const& identifier = boost::get(elementary); + + if (m_dialect->builtin(identifier.name)) + fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\"."); + + variableNames.emplace_back(identifier); + + if (currentToken() != Token::Comma) + break; + + expectToken(Token::Comma); + + elementary = parseElementaryOperation(); + } + + Assignment assignment = + createWithLocation(boost::get(elementary).location); + assignment.variableNames = std::move(variableNames); + + expectToken(Token::AssemblyAssign); assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; + return Statement{std::move(assignment)}; } case Token::Colon: { if (elementary.type() != typeid(Identifier)) - fatalParserError("Label name / variable name must precede \":\"."); + fatalParserError("Label name must precede \":\"."); + Identifier const& identifier = boost::get(elementary); + advance(); - // identifier:=: should be parsed as identifier: =: (i.e. a label), - // while identifier:= (being followed by a non-colon) as identifier := (assignment). - if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) - { - Assignment assignment = createWithLocation(identifier.location); - if (m_dialect->builtin(identifier.name)) - fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\"."); - else if (m_dialect->flavour != AsmFlavour::Yul && instructions().count(identifier.name.str())) - fatalParserError("Cannot use instruction names for identifier names."); - advance(); - assignment.variableNames.emplace_back(identifier); - assignment.value.reset(new Expression(parseExpression())); - assignment.location.end = locationOf(*assignment.value).end; - return Statement{std::move(assignment)}; - } - else - { - // label - if (m_dialect->flavour != AsmFlavour::Loose) - fatalParserError("Labels are not supported."); - Label label = createWithLocation