Merge pull request #6188 from ethereum/develop

Release 0.5.5
This commit is contained in:
chriseth 2019-03-05 16:22:00 +01:00 committed by GitHub
commit 47a71e8f1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
394 changed files with 8798 additions and 2530 deletions

View File

@ -56,10 +56,10 @@ jobs:
paths: paths:
- boost_1_68_0 - boost_1_68_0
- store_artifacts: - store_artifacts:
path: build/libsolc/soljson.js path: emscripten_build/libsolc/soljson.js
destination: soljson.js destination: soljson.js
- run: mkdir -p workspace - 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 - run: scripts/get_version.sh > workspace/version.txt
- persist_to_workspace: - persist_to_workspace:
root: workspace root: workspace
@ -202,8 +202,6 @@ jobs:
- run: - run:
name: Install build dependencies name: Install build dependencies
command: | command: |
brew update
brew upgrade
brew unlink python brew unlink python
brew install z3 brew install z3
brew install boost brew install boost
@ -353,8 +351,6 @@ jobs:
- run: - run:
name: Install dependencies name: Install dependencies
command: | command: |
brew update
brew upgrade
brew unlink python brew unlink python
brew install z3 brew install z3
- run: mkdir -p test_results - run: mkdir -p test_results

1
.gitignore vendored
View File

@ -32,6 +32,7 @@ prerelease.txt
# Build directory # Build directory
build/ build/
emscripten_build/
docs/_build docs/_build
docs/utils/__pycache__ docs/utils/__pycache__
docs/utils/*.pyc docs/utils/*.pyc

View File

@ -10,7 +10,7 @@ include(EthPolicy)
eth_policy() eth_policy()
# project name and version should be set after cmake_policy CMP0048 # 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) project(solidity VERSION ${PROJECT_VERSION} LANGUAGES CXX)
option(LLL "Build LLL" OFF) option(LLL "Build LLL" OFF)

View File

@ -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) ### 0.5.4 (2019-02-12)
Language Features: Language Features:

View File

@ -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. - [ ] 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``. - [ ] 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 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``). - [ ] 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. - [ ] 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. - [ ] 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 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). - [ ] 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. - [ ] 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``.
- [ ] Upload the source tarball (in the upload directory) to the release page. - [ ] 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). - [ ] 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``. - [ ] 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 release``). - [ ] 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``).
- [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash)
### 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. - [ ] 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. - [ ] 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. - [ ] 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``. - [ ] Merge ``release`` back into ``develop``.
### Post-release
- [ ] Announce on Twitter and Reddit. - [ ] Announce on Twitter and Reddit.
- [ ] Lean back, wait for bug reports and repeat from step 1 :) - [ ] Lean back, wait for bug reports and repeat from step 1 :)

View File

@ -3,6 +3,10 @@ codecov:
coverage: coverage:
range: 70...100 range: 70...100
status: status:
patch:
default:
target: "50%"
paths: "!test/"
project: project:
default: default:
target: auto target: auto

View File

@ -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), 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"). 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. 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 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``. but not including position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.

View File

@ -628,5 +628,9 @@
"0.5.4": { "0.5.4": {
"bugs": [], "bugs": [],
"released": "2019-02-12" "released": "2019-02-12"
},
"0.5.5": {
"bugs": [],
"released": "2019-03-05"
} }
} }

View File

@ -90,7 +90,7 @@ The option ``--no-smt`` disables the tests that require ``libz3`` and
``--no-ipc`` disables those that require ``aleth``. ``--no-ipc`` disables those that require ``aleth``.
If you want to run the ipc tests (that test the semantics of the generated code), If you want to run the ipc tests (that test the semantics of the generated code),
you need to install `aleth <https://github.com/ethereum/aleth/releases/download/v1.5.0-alpha.7/aleth-1.5.0-alpha.7-linux-x86_64.tar.gz>`_ and run it in testing mode: ``aleth --db memorydb --test -d /tmp/testeth``. you need to install `aleth <https://github.com/ethereum/aleth/releases/download/v1.5.2/aleth-1.5.2-linux-x86_64.tar.gz>`_ 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``. 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, by as many concatenations of its contents as there were sets of variables supplied to the template system,
each time replacing any ``<inner>`` items by their respective value. Top-level variables can also be used each time replacing any ``<inner>`` items by their respective value. Top-level variables can also be used
inside such areas. 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 <https://en.wikipedia.org/wiki/Simplified_Technical_English>`_
* `International English <https://en.wikipedia.org/wiki/International_English>`_
* `British English spelling <https://en.oxforddictionaries.com/spelling/british-and-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 <http://titlecase.com>`_ 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;``.

View File

@ -91,7 +91,7 @@ You need to use the modifier ``payable`` with the ``info`` function because
otherwise, the ``.value()`` option would not be available. otherwise, the ``.value()`` option would not be available.
.. warning:: .. 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 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 sense that the account does not contain code) or if the called contract itself

57
docs/examples/modular.rst Normal file
View File

@ -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 <libraries>` 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;
}
}

View File

@ -19,7 +19,7 @@ user-defined types among other features.
With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions, With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions,
and multi-signature wallets. 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 <https://semver.org/#spec-item-4>`_.
Language Documentation 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 <contributing>` for more details. Ideas for improving Solidity or this documentation are always welcome, read our :doc:`contributors guide <contributing>` for more details.
.. _translations:
Translations Translations
------------ ------------

View File

@ -154,23 +154,35 @@ Gentoo Linux also provides a solidity package that can be installed using ``emer
Building from Source 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 | | 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 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 <https://developer.apple.com/xcode/download/>`_. `Xcode installed <https://developer.apple.com/xcode/download/>`_.
This contains the `Clang C++ compiler <https://en.wikipedia.org/wiki/Clang>`_, the This contains the `Clang C++ compiler <https://en.wikipedia.org/wiki/Clang>`_, the
`Xcode IDE <https://en.wikipedia.org/wiki/Xcode>`_ and other Apple development `Xcode IDE <https://en.wikipedia.org/wiki/Xcode>`_ and other Apple development
@ -183,13 +195,12 @@ command-line builds:
sudo xcodebuild -license accept sudo xcodebuild -license accept
Our OS X builds require you to `install the Homebrew <http://brew.sh>`_ Our OS X build script uses `the Homebrew <http://brew.sh>`_
package manager for installing external dependencies. package manager for installing external dependencies.
Here's how to `uninstall Homebrew Here's how to `uninstall Homebrew
<https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/FAQ.md#how-do-i-uninstall-homebrew>`_, <https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/FAQ.md#how-do-i-uninstall-homebrew>`_,
if you ever want to start again from scratch. if you ever want to start again from scratch.
Prerequisites - Windows Prerequisites - Windows
----------------------- -----------------------
@ -198,21 +209,17 @@ You need to install the following dependencies for Windows builds of Solidity:
+-----------------------------------+-------------------------------------------------------+ +-----------------------------------+-------------------------------------------------------+
| Software | Notes | | 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 Build Tools`_ | C++ compiler |
+-----------------------------------+-------------------------------------------------------+ +-----------------------------------+-------------------------------------------------------+
| `Visual Studio 2017`_ (Optional) | C++ compiler and dev environment. | | `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. you could install Visual Studio 2017 Build Tools.
Visual Studio 2017 provides both IDE and necessary compiler and libraries. 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 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 Here is the list of components that should be installed
in Visual Studio 2017 Build Tools or Visual Studio 2017: 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 * Windows 8.1 SDK
* C++/CLI support * 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: https://www.visualstudio.com/vs/
.. _Visual Studio 2017 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017 .. _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 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 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 Command-Line Build
------------------ ------------------
@ -278,7 +282,11 @@ Building Solidity is quite similar on Linux, macOS and other Unices:
cd build cd build
cmake .. && make 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 .. code-block:: bash

View File

@ -66,10 +66,23 @@ explanatory purposes.
{ {
// Required for Solidity: Sorted list of remappings // Required for Solidity: Sorted list of remappings
remappings: [ ":g/dir" ], 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: { optimizer: {
enabled: true, 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 // Required for Solidity: File and name of the contract or library this
// metadata is created for. // metadata is created for.

View File

@ -385,6 +385,7 @@ Global Variables
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure - ``<address payable>.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<meta-type>`. - ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`. - ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.

View File

@ -3,8 +3,11 @@ Solidity by Example
################### ###################
.. include:: examples/voting.rst .. include:: examples/voting.rst
.. include:: examples/blind-auction.rst .. include:: examples/blind-auction.rst
.. include:: examples/safe-remote.rst .. include:: examples/safe-remote.rst
.. include:: examples/micropayment.rst .. include:: examples/micropayment.rst
.. include:: examples/modular.rst

View File

@ -1088,12 +1088,6 @@ Avoiding Naming Collisions
This convention is suggested when the desired name collides with that of a This convention is suggested when the desired name collides with that of a
built-in or otherwise reserved name. built-in or otherwise reserved name.
General Recommendations
=======================
TODO
.. _natspec: .. _natspec:
******* *******

View File

@ -305,8 +305,7 @@ Contract Types
Every :ref:`contract<contracts>` defines its own type. Every :ref:`contract<contracts>` defines its own type.
You can implicitly convert contracts to contracts they inherit from. You can implicitly convert contracts to contracts they inherit from.
Contracts can be explicitly converted to and from all other contract types Contracts can be explicitly converted to and from the ``address`` type.
and the ``address`` type.
Explicit conversion to and from the ``address payable`` type Explicit conversion to and from the ``address payable`` type
is only possible if the contract type has a payable fallback function. 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: Members:
Public (or external) functions also have a special member called ``selector``, Public (or external) functions have the following members:
which returns the :ref:`ABI function selector <abi_function_selector>`::
* ``.selector`` returns the :ref:`ABI function selector <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 <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 <external-function-calls>` for more information.
Example that shows how to use the members::
pragma solidity >=0.4.16 <0.6.0; pragma solidity >=0.4.16 <0.6.0;
contract Selector { contract Example {
function f() public pure returns (bytes4) { function f() public payable returns (bytes4) {
return this.f.selector; return this.f.selector;
} }
function g() public {
this.f.gas(10).value(800)();
}
} }
Example that shows how to use internal function types:: Example that shows how to use internal function types::

View File

@ -156,29 +156,41 @@ Mathematical and Cryptographic Functions
``addmod(uint x, uint y, uint k) returns (uint)``: ``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. 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)``: ``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. 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)``: ``keccak256(bytes memory) returns (bytes32)``:
compute the Keccak-256 hash of the input compute the Keccak-256 hash of the input
``sha256(bytes memory) returns (bytes32)``: ``sha256(bytes memory) returns (bytes32)``:
compute the SHA-256 hash of the input compute the SHA-256 hash of the input
``ripemd160(bytes memory) returns (bytes20)``: ``ripemd160(bytes memory) returns (bytes20)``:
compute RIPEMD-160 hash of the input compute RIPEMD-160 hash of the input
``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: ``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 recover the address associated with the public key from elliptic curve signature or return zero on error.
(`example usage <https://ethereum.stackexchange.com/q/1777/222>`_) 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<address>` for
conversion, in case you need to transfer funds to the recovered address.
For further details, read `example usage <https://ethereum.stackexchange.com/q/1777/222>`_.
.. note:: .. note::
Function ``ecrecover`` returns an ``address``, and not an ``address
payable``. See :ref:`address payable<address>` 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:: .. note::
There used to be an alias for ``keccak256`` called ``sha3``, which was removed in version 0.5.0. 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 .. index:: balance, send, transfer, call, callcode, delegatecall, staticcall
.. _address_related: .. _address_related:
Members of Address Types 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 it might be expanded in the future. The following properties are
available for a contract type ``C``: available for a contract type ``C``:
``type(C).name``:
The name of the contract.
``type(C).creationCode``: ``type(C).creationCode``:
Memory byte array that contains the creation bytecode of the contract. Memory byte array that contains the creation bytecode of the contract.
This can be used in inline assembly to build custom creation routines, This can be used in inline assembly to build custom creation routines,

View File

@ -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``. 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``. 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, 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 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. 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 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: 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) - ``homestead`` (oldest version)
- ``tangerineWhistle`` - ``tangerineWhistle``
- gas cost for access to other accounts increased, relevant for gas estimation and the optimizer. - 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. - All gas sent by default for external calls, previously a certain amount had to be retained.
- ``spuriousDragon`` - ``spuriousDragon``
- gas cost for the ``exp`` opcode increased, relevant for gas estimation and the optimizer. - Gas cost for the ``exp`` opcode increased, relevant for gas estimation and the optimizer.
- ``byzantium`` (**default**) - ``byzantium``
- opcodes ``returndatacopy``, ``returndatasize`` and ``staticcall`` are available in assembly. - 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. - 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. - It is possible to access dynamic data returned from function calls.
- ``revert`` opcode introduced, which means that ``revert()`` will not waste gas. - ``revert`` opcode introduced, which means that ``revert()`` will not waste gas.
- ``constantinople`` (still in progress) - ``constantinople``
- opcodes ``shl``, ``shr`` and ``sar`` are available in assembly. - Opcodes ``create2`, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly.
- shifting operators use shifting opcodes and thus need less gas. - Shifting operators use shifting opcodes and thus need less gas.
- ``petersburg`` (**default**)
- The compiler behaves the same way as with constantinople.
.. _compiler-api: .. _compiler-api:
@ -186,9 +194,33 @@ Input Description
"enabled": true, "enabled": true,
// Optimize for how many times you intend to run the code. // 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. // 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 settings (optional)
"metadata": { "metadata": {
// Use only literal content and not URLs (false by default) // Use only literal content and not URLs (false by default)

View File

@ -74,7 +74,7 @@ using strings = std::vector<std::string>;
/// Interprets @a _u as a two's complement signed number and returns the resulting s256. /// Interprets @a _u as a two's complement signed number and returns the resulting s256.
inline s256 u2s(u256 _u) 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)) if (boost::multiprecision::bit_test(_u, 255))
return s256(-(c_end - _u)); return s256(-(c_end - _u));
else else
@ -84,10 +84,10 @@ inline s256 u2s(u256 _u)
/// @returns the two's complement signed representation of the signed number _u. /// @returns the two's complement signed representation of the signed number _u.
inline u256 s2u(s256 _u) inline u256 s2u(s256 _u)
{ {
static const bigint c_end = bigint(1) << 256; static bigint const c_end = bigint(1) << 256;
if (_u >= 0) if (_u >= 0)
return u256(_u); return u256(_u);
else else
return u256(c_end + _u); return u256(c_end + _u);
} }

View File

@ -140,6 +140,12 @@ inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0)
return (_min || _val) ? bytes{ _val } : bytes{}; return (_min || _val) ? bytes{ _val } : bytes{};
} }
/// Workarounds shift left bug in boost <1.65.1.
template <class S> S bigintShiftLeftWorkaround(S const& _a, unsigned _b)
{
return (S)(bigint(_a) << _b);
}
/// Convenience function for conversion of a u256 to hex /// Convenience function for conversion of a u256 to hex
inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) 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. // 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. /// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero.
template <class T> template <class T>
inline unsigned bytesRequired(T _i) 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. /// place at the end, but already visited elements might be invalidated.
/// If nothing is replaced, no copy is performed. /// If nothing is replaced, no copy is performed.
template <typename T, typename F> template <typename T, typename F>
void iterateReplacing(std::vector<T>& _vector, const F& _f) void iterateReplacing(std::vector<T>& _vector, F const& _f)
{ {
// Concept: _f must be Callable, must accept param T&, must return optional<vector<T>> // Concept: _f must be Callable, must accept param T&, must return optional<vector<T>>
bool useModified = false; bool useModified = false;

View File

@ -31,7 +31,7 @@ namespace dev
/// Base class for all exceptions. /// Base class for all exceptions.
struct Exception: virtual std::exception, virtual boost::exception 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. /// @returns "FileName:LineNumber" referring to the point where the exception was thrown.
std::string lineInfo() const; std::string lineInfo() const;

View File

@ -47,17 +47,17 @@ namespace
/******** The Keccak-f[1600] permutation ********/ /******** The Keccak-f[1600] permutation ********/
/*** Constants. ***/ /*** Constants. ***/
static const uint8_t rho[24] = \ static uint8_t const rho[24] = \
{ 1, 3, 6, 10, 15, 21, { 1, 3, 6, 10, 15, 21,
28, 36, 45, 55, 2, 14, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 27, 41, 56, 8, 25, 43,
62, 18, 39, 61, 20, 44}; 62, 18, 39, 61, 20, 44};
static const uint8_t pi[24] = \ static uint8_t const pi[24] = \
{10, 7, 11, 17, 18, 3, {10, 7, 11, 17, 18, 3,
5, 16, 8, 21, 24, 4, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 15, 23, 19, 13, 12, 2,
20, 14, 22, 9, 6, 1}; 20, 14, 22, 9, 6, 1};
static const uint64_t RC[24] = \ static uint64_t const RC[24] = \
{1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
@ -71,8 +71,8 @@ static const uint64_t RC[24] = \
#define REPEAT24(e) REPEAT6(e e e e) #define REPEAT24(e) REPEAT6(e e e e)
#define REPEAT5(e) e e e e e #define REPEAT5(e) e e e e e
#define FOR5(v, s, e) \ #define FOR5(v, s, e) \
v = 0; \ v = 0; \
REPEAT5(e; v += s;) REPEAT5(e; v += s;)
/*** Keccak-f[1600] ***/ /*** Keccak-f[1600] ***/
static inline void keccakf(void* state) { static inline void keccakf(void* state) {
@ -84,25 +84,25 @@ static inline void keccakf(void* state) {
uint8_t x, y; uint8_t x, y;
// Theta // Theta
FOR5(x, 1, FOR5(x, 1,
b[x] = 0; b[x] = 0;
FOR5(y, 5, FOR5(y, 5,
b[x] ^= a[x + y]; )) b[x] ^= a[x + y]; ))
FOR5(x, 1, FOR5(x, 1,
FOR5(y, 5, FOR5(y, 5,
a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
// Rho and pi // Rho and pi
uint64_t t = a[1]; uint64_t t = a[1];
x = 0; x = 0;
REPEAT24(b[0] = a[pi[x]]; REPEAT24(b[0] = a[pi[x]];
a[pi[x]] = rol(t, rho[x]); a[pi[x]] = rol(t, rho[x]);
t = b[0]; t = b[0];
x++; ) x++; )
// Chi // Chi
FOR5(y, FOR5(y,
5, 5,
FOR5(x, 1, FOR5(x, 1,
b[x] = a[y + x];) b[x] = a[y + x];)
FOR5(x, 1, FOR5(x, 1,
a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
// Iota // Iota
a[0] ^= RC[i]; a[0] ^= RC[i];
@ -115,19 +115,19 @@ static inline void keccakf(void* state) {
#define _(S) do { S } while (0) #define _(S) do { S } while (0)
#define FOR(i, ST, L, S) \ #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) \ #define mkapply_ds(NAME, S) \
static inline void NAME(uint8_t* dst, \ static inline void NAME(uint8_t* dst, \
const uint8_t* src, \ uint8_t const* src, \
size_t len) { \ size_t len) { \
FOR(i, 1, len, S); \ FOR(i, 1, len, S); \
} }
#define mkapply_sd(NAME, S) \ #define mkapply_sd(NAME, S) \
static inline void NAME(const uint8_t* src, \ static inline void NAME(uint8_t const* src, \
uint8_t* dst, \ uint8_t* dst, \
size_t len) { \ size_t len) { \
FOR(i, 1, len, S); \ FOR(i, 1, len, S); \
} }
mkapply_ds(xorin, dst[i] ^= src[i]) // xorin mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
mkapply_sd(setout, dst[i] = src[i]) // setout 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. // Fold P*F over the full blocks of an input.
#define foldP(I, L, F) \ #define foldP(I, L, F) \
while (L >= rate) { \ while (L >= rate) { \
F(a, I, rate); \ F(a, I, rate); \
P(a); \ P(a); \
I += rate; \ I += rate; \
L -= rate; \ L -= rate; \
} }
/** The sponge-based hash construction. **/ /** The sponge-based hash construction. **/
inline void hash( inline void hash(
uint8_t* out, uint8_t* out,
size_t outlen, size_t outlen,
const uint8_t* in, uint8_t const* in,
size_t inlen, size_t inlen,
size_t rate, size_t rate,
uint8_t delim uint8_t delim

View File

@ -139,8 +139,8 @@ inline std::string formatNumberReadable(
if (len < 24) if (len < 24)
return str; return str;
const int initialChars = (prefix == HexPrefix::Add) ? 6 : 4; int const initialChars = (prefix == HexPrefix::Add) ? 6 : 4;
const int finalChars = 4; int const finalChars = 4;
int numSkipped = len - initialChars - finalChars; int numSkipped = len - initialChars - finalChars;
return str.substr(0, initialChars) + return str.substr(0, initialChars) +

View File

@ -77,7 +77,7 @@ bool isWellFormed(unsigned char byte1, unsigned char byte2)
return false; 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; bool valid = true;
size_t i = 0; size_t i = 0;

View File

@ -390,7 +390,7 @@ Assembly& Assembly::optimise(OptimiserSettings const& _settings)
map<u256, u256> Assembly::optimiseInternal( map<u256, u256> Assembly::optimiseInternal(
OptimiserSettings const& _settings, OptimiserSettings const& _settings,
std::set<size_t> const& _tagsReferencedFromOutside std::set<size_t> _tagsReferencedFromOutside
) )
{ {
// Run optimisation for sub-assemblies. // Run optimisation for sub-assemblies.
@ -436,7 +436,22 @@ map<u256, u256> Assembly::optimiseInternal(
BlockDeduplicator dedup{m_items}; BlockDeduplicator dedup{m_items};
if (dedup.deduplicate()) 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++; count++;
} }
} }

View File

@ -107,13 +107,14 @@ public:
bool runDeduplicate = false; bool runDeduplicate = false;
bool runCSE = false; bool runCSE = false;
bool runConstantOptimiser = 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, /// 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. /// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
size_t expectedExecutionsPerDeployment = 200; 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); Assembly& optimise(OptimiserSettings const& _settings);
/// Modify (if @a _enable is set) and return the current assembly such that creation and /// 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, /// @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. /// 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. /// 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. /// Create a text representation of the assembly.
std::string assemblyString( std::string assemblyString(
@ -154,7 +155,7 @@ protected:
/// Does the same operations as @a optimise, but should only be applied to a sub and /// 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 /// returns the replaced tags. Also takes an argument containing the tags of this assembly
/// that are referenced in a super-assembly. /// that are referenced in a super-assembly.
std::map<u256, u256> optimiseInternal(OptimiserSettings const& _settings, std::set<size_t> const& _tagsReferencedFromOutside); std::map<u256, u256> optimiseInternal(OptimiserSettings const& _settings, std::set<size_t> _tagsReferencedFromOutside);
unsigned bytesRequired(unsigned subTagSize) const; unsigned bytesRequired(unsigned subTagSize) const;

View File

@ -149,7 +149,7 @@ private:
/// Appends the given assembly item. /// Appends the given assembly item.
void appendItem(AssemblyItem const& _item); void appendItem(AssemblyItem const& _item);
static const int c_invalidPosition = -0x7fffffff; static int const c_invalidPosition = -0x7fffffff;
AssemblyItems m_generatedItems; AssemblyItems m_generatedItems;
/// Current height of the stack relative to the start. /// 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. /// Current positions of equivalence classes, equal to the empty set if already deleted.
std::map<Id, std::set<int>> m_classPositions; std::map<Id, std::set<int>> 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; ExpressionClasses& m_expressionClasses;
/// Keeps information about which storage or memory slots were written to by which operations. /// Keeps information about which storage or memory slots were written to by which operations.
/// The operations are sorted ascendingly by sequence number. /// The operations are sorted ascendingly by sequence number.

View File

@ -29,7 +29,7 @@ using namespace dev::eth;
unsigned ConstantOptimisationMethod::optimiseConstants( unsigned ConstantOptimisationMethod::optimiseConstants(
bool _isCreation, bool _isCreation,
size_t _runs, size_t _runs,
solidity::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
Assembly& _assembly Assembly& _assembly
) )
{ {
@ -210,7 +210,10 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
AssemblyItems newRoutine; AssemblyItems newRoutine;
if (lowerPart != 0) if (lowerPart != 0)
newRoutine += findRepresentation(u256(abs(lowerPart))); 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) if (upperPart != 1)
newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL}; newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL};
if (lowerPart > 0) 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. // This is a tiny EVM that can only evaluate some instructions.
vector<u256> stack; vector<u256> stack;
@ -263,6 +266,24 @@ bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const&
case Instruction::NOT: case Instruction::NOT:
sp[0] = ~sp[0]; sp[0] = ~sp[0];
break; 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: default:
return false; return false;
} }

View File

@ -52,7 +52,7 @@ public:
static unsigned optimiseConstants( static unsigned optimiseConstants(
bool _isCreation, bool _isCreation,
size_t _runs, size_t _runs,
solidity::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
Assembly& _assembly Assembly& _assembly
); );
@ -64,7 +64,7 @@ protected:
bool isCreation; ///< Whether this is called during contract creation or runtime. 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 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. 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): explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
@ -155,7 +155,7 @@ protected:
/// Tries to recursively find a way to compute @a _value. /// Tries to recursively find a way to compute @a _value.
AssemblyItems findRepresentation(u256 const& _value); AssemblyItems findRepresentation(u256 const& _value);
/// Recomputes the value from the calculated representation and checks for correctness. /// 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; bigint gasNeeded(AssemblyItems const& _routine) const;
/// Counter for the complexity of optimization, will stop when it reaches zero. /// Counter for the complexity of optimization, will stop when it reaches zero.

View File

@ -47,24 +47,24 @@ namespace GasCosts
static unsigned const tier5Gas = 10; static unsigned const tier5Gas = 10;
static unsigned const tier6Gas = 20; static unsigned const tier6Gas = 20;
static unsigned const tier7Gas = 0; 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; 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 keccak256Gas = 30;
static unsigned const keccak256WordGas = 6; 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 sstoreSetGas = 20000;
static unsigned const sstoreResetGas = 5000; static unsigned const sstoreResetGas = 5000;
@ -74,16 +74,16 @@ namespace GasCosts
static unsigned const logDataGas = 8; static unsigned const logDataGas = 8;
static unsigned const logTopicGas = 375; static unsigned const logTopicGas = 375;
static unsigned const createGas = 32000; 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 callStipend = 2300;
static unsigned const callValueTransferGas = 9000; static unsigned const callValueTransferGas = 9000;
static unsigned const callNewAccountGas = 25000; 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 selfdestructRefundGas = 24000;
static unsigned const memoryGas = 3; static unsigned const memoryGas = 3;
@ -122,7 +122,7 @@ public:
}; };
/// Constructs a new gas meter given the current state. /// Constructs a new gas meter given the current state.
GasMeter(std::shared_ptr<KnownState> const& _state, solidity::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0): GasMeter(std::shared_ptr<KnownState> const& _state, langutil::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0):
m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {} m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {}
/// @returns an upper bound on the gas consumed by the given instruction and updates /// @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); GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize);
std::shared_ptr<KnownState> m_state; std::shared_ptr<KnownState> m_state;
EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
/// Largest point where memory was accessed since the creation of this object. /// Largest point where memory was accessed since the creation of this object.
u256 m_largestMemoryAccess; u256 m_largestMemoryAccess;
}; };

View File

@ -29,7 +29,7 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::solidity; using namespace dev::solidity;
const std::map<std::string, Instruction> dev::solidity::c_instructions = std::map<std::string, Instruction> const dev::solidity::c_instructions =
{ {
{ "STOP", Instruction::STOP }, { "STOP", Instruction::STOP },
{ "ADD", Instruction::ADD }, { "ADD", Instruction::ADD },
@ -173,7 +173,7 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
{ "SELFDESTRUCT", Instruction::SELFDESTRUCT } { "SELFDESTRUCT", Instruction::SELFDESTRUCT }
}; };
static const std::map<Instruction, InstructionInfo> c_instructionInfo = static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ // Add, Args, Ret, SideEffects, GasPriceTier { // Add, Args, Ret, SideEffects, GasPriceTier
{ Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::Zero } }, { Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::Zero } },
{ Instruction::ADD, { "ADD", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::ADD, { "ADD", 0, 2, 1, false, Tier::VeryLow } },

View File

@ -27,7 +27,7 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; 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) m_items(_items), m_evmVersion(_evmVersion)
{ {
for (size_t i = 0; i < m_items.size(); ++i) for (size_t i = 0; i < m_items.size(); ++i)

View File

@ -53,13 +53,13 @@ struct GasPath
class PathGasMeter class PathGasMeter
{ {
public: 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<KnownState> const& _state); GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state);
static GasMeter::GasConsumption estimateMax( static GasMeter::GasConsumption estimateMax(
AssemblyItems const& _items, AssemblyItems const& _items,
solidity::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
size_t _startIndex, size_t _startIndex,
std::shared_ptr<KnownState> const& _state std::shared_ptr<KnownState> const& _state
) )
@ -81,7 +81,7 @@ private:
std::map<size_t, GasMeter::GasConsumption> m_highestGasUsagePerJumpdest; std::map<size_t, GasMeter::GasConsumption> m_highestGasUsagePerJumpdest;
std::map<u256, size_t> m_tagPositions; std::map<u256, size_t> m_tagPositions;
AssemblyItems const& m_items; AssemblyItems const& m_items;
solidity::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
}; };
} }

View File

@ -24,6 +24,8 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <boost/multiprecision/detail/min_max.hpp>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <libevmasm/SimplificationRule.h> #include <libevmasm/SimplificationRule.h>
@ -85,12 +87,12 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
return B.d(); return B.d();
unsigned testBit = unsigned(A.d()) * 8 + 7; unsigned testBit = unsigned(A.d()) * 8 + 7;
u256 mask = (u256(1) << testBit) - 1; 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}, }, false},
{{Instruction::SHL, {A, B}}, [=]{ {{Instruction::SHL, {A, B}}, [=]{
if (A.d() > 255) if (A.d() > 255)
return u256(0); return u256(0);
return u256(bigint(B.d()) << unsigned(A.d())); return bigintShiftLeftWorkaround(B.d(), unsigned(A.d()));
}, false}, }, false},
{{Instruction::SHR, {A, B}}, [=]{ {{Instruction::SHR, {A, B}}, [=]{
if (A.d() > 255) if (A.d() > 255)
@ -106,7 +108,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
Pattern, Pattern,
Pattern, Pattern,
Pattern X, Pattern X,
Pattern Pattern Y
) )
{ {
return std::vector<SimplificationRule<Pattern>> { return std::vector<SimplificationRule<Pattern>> {
@ -140,6 +142,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
{{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true}, {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true},
{{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, {{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
{{Instruction::EQ, {0, X}}, [=]() -> 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<SimplificationRule<Pattern>> simplificationRuleListPart5(
Instruction::ADDRESS, Instruction::ADDRESS,
Instruction::CALLER, Instruction::CALLER,
Instruction::ORIGIN, Instruction::ORIGIN,
Instruction::COINBASE Instruction::COINBASE,
Instruction::CREATE,
Instruction::CREATE2
}) })
{ {
u256 const mask = (u256(1) << 160) - 1; u256 const mask = (u256(1) << 160) - 1;
@ -332,6 +346,20 @@ std::vector<SimplificationRule<Pattern>> 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; return rules;
} }

View File

@ -1,5 +1,6 @@
# Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul) # Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul)
set(sources set(sources
Common.h
CharStream.cpp CharStream.cpp
CharStream.h CharStream.h
ErrorReporter.cpp ErrorReporter.cpp

View File

@ -1,48 +1,48 @@
/* /*
This file is part of solidity. * This file is part of solidity.
*
solidity is free software: you can redistribute it and/or modify * solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. * (at your option) any later version.
*
solidity is distributed in the hope that it will be useful, * solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. * GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. * along with solidity. If not, see <http://www.gnu.org/licenses/>.
*
This file is derived from the file "scanner.cc", which was part of the * This file is derived from the file "scanner.cc", which was part of the
V8 project. The original copyright header follows: * V8 project. The original copyright header follows:
*
Copyright 2006-2012, the V8 project authors. All rights reserved. * Copyright 2006-2012, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
met: * met:
*
* Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above * * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following * copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided * disclaimer in the documentation and/or other materials provided
with the distribution. * with the distribution.
* Neither the name of Google Inc. nor the names of its * * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived * contributors may be used to endorse or promote products derived
from this software without specific prior written permission. * from this software without specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -85,8 +85,10 @@ string CharStream::lineAtPosition(int _position) const
lineStart = 0; lineStart = 0;
else else
lineStart++; lineStart++;
return m_source.substr(lineStart, min(m_source.find('\n', lineStart), return m_source.substr(
m_source.size()) - lineStart); lineStart,
min(m_source.find('\n', lineStart), m_source.size()) - lineStart
);
} }
tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const

View File

@ -1,48 +1,48 @@
/* /*
This file is part of solidity. * This file is part of solidity.
*
solidity is free software: you can redistribute it and/or modify * solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. * (at your option) any later version.
*
solidity is distributed in the hope that it will be useful, * solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. * GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. * along with solidity. If not, see <http://www.gnu.org/licenses/>.
*
This file is derived from the file "scanner.h", which was part of the * This file is derived from the file "scanner.h", which was part of the
V8 project. The original copyright header follows: * V8 project. The original copyright header follows:
*
Copyright 2006-2012, the V8 project authors. All rights reserved. * Copyright 2006-2012, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
met: * met:
*
* Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above * * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following * copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided * disclaimer in the documentation and/or other materials provided
with the distribution. * with the distribution.
* Neither the name of Google Inc. nor the names of its * * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived * contributors may be used to endorse or promote products derived
from this software without specific prior written permission. * from this software without specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

63
liblangutil/Common.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}
}

View File

@ -25,14 +25,12 @@
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/operators.hpp> #include <boost/operators.hpp>
namespace dev namespace langutil
{
namespace solidity
{ {
/** /**
* A version specifier of the EVM we want to compile to. * 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: class EVMVersion:
boost::less_than_comparable<EVMVersion>, boost::less_than_comparable<EVMVersion>,
@ -46,10 +44,11 @@ public:
static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; } static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; }
static EVMVersion byzantium() { return {Version::Byzantium}; } static EVMVersion byzantium() { return {Version::Byzantium}; }
static EVMVersion constantinople() { return {Version::Constantinople}; } static EVMVersion constantinople() { return {Version::Constantinople}; }
static EVMVersion petersburg() { return {Version::Petersburg}; }
static boost::optional<EVMVersion> fromString(std::string const& _version) static boost::optional<EVMVersion> 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()) if (_version == v.name())
return v; return v;
return {}; return {};
@ -67,6 +66,7 @@ public:
case Version::SpuriousDragon: return "spuriousDragon"; case Version::SpuriousDragon: return "spuriousDragon";
case Version::Byzantium: return "byzantium"; case Version::Byzantium: return "byzantium";
case Version::Constantinople: return "constantinople"; case Version::Constantinople: return "constantinople";
case Version::Petersburg: return "petersburg";
} }
return "INVALID"; return "INVALID";
} }
@ -76,19 +76,19 @@ public:
bool hasStaticCall() const { return *this >= byzantium(); } bool hasStaticCall() const { return *this >= byzantium(); }
bool hasBitwiseShifting() const { return *this >= constantinople(); } bool hasBitwiseShifting() const { return *this >= constantinople(); }
bool hasCreate2() 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), /// Whether we have to retain the costs for the call opcode itself (false),
/// or whether we can just forward easily all remaining gas (true). /// or whether we can just forward easily all remaining gas (true).
bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); } bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); }
private: private:
enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople }; enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople, Petersburg };
EVMVersion(Version _version): m_version(_version) {} EVMVersion(Version _version): m_version(_version) {}
Version m_version = Version::Byzantium; Version m_version = Version::Petersburg;
}; };
} }
}

View File

@ -134,7 +134,7 @@ void ErrorReporter::clear()
m_errorList.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(
Error::Type::DeclarationError, Error::Type::DeclarationError,

View File

@ -26,6 +26,9 @@
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <liblangutil/SourceLocation.h> #include <liblangutil/SourceLocation.h>
#include <libdevcore/StringUtils.h>
#include <boost/range/adaptor/filtered.hpp>
namespace langutil namespace langutil
{ {
@ -87,6 +90,19 @@ public:
void typeError(SourceLocation const& _location, std::string const& _description); void typeError(SourceLocation const& _location, std::string const& _description);
template <typename... Strings>
void typeErrorConcatenateDescriptions(SourceLocation const& _location, Strings const&... _descriptions)
{
std::initializer_list<std::string> 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 fatalTypeError(SourceLocation const& _location, std::string const& _description);
void docstringParsingError(std::string const& _description); void docstringParsingError(std::string const& _description);
@ -119,8 +135,8 @@ private:
unsigned m_errorCount = 0; unsigned m_errorCount = 0;
unsigned m_warningCount = 0; unsigned m_warningCount = 0;
const unsigned c_maxWarningsAllowed = 256; unsigned const c_maxWarningsAllowed = 256;
const unsigned c_maxErrorsAllowed = 256; unsigned const c_maxErrorsAllowed = 256;
}; };
} }

View File

@ -57,7 +57,7 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip
*this << errinfo_comment(_description); *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) Error(_type)
{ {
if (!_location.isEmpty()) if (!_location.isEmpty())

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -42,13 +42,13 @@ struct UnimplementedFeatureError: virtual dev::Exception {};
/// Assertion that throws an InternalCompilerError containing the given description if it is not met. /// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \ #define solAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::langutil::InternalCompilerError, DESCRIPTION) assertThrow(CONDITION, ::langutil::InternalCompilerError, DESCRIPTION)
#define solUnimplementedAssert(CONDITION, DESCRIPTION) \ #define solUnimplementedAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::langutil::UnimplementedFeatureError, DESCRIPTION) assertThrow(CONDITION, ::langutil::UnimplementedFeatureError, DESCRIPTION)
#define solUnimplemented(DESCRIPTION) \ #define solUnimplemented(DESCRIPTION) \
solUnimplementedAssert(false, DESCRIPTION) solUnimplementedAssert(false, DESCRIPTION)
class Error: virtual public dev::Exception class Error: virtual public dev::Exception
{ {

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -1,48 +1,48 @@
/* /*
This file is part of solidity. * This file is part of solidity.
*
solidity is free software: you can redistribute it and/or modify * solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. * (at your option) any later version.
*
solidity is distributed in the hope that it will be useful, * solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. * GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. * along with solidity. If not, see <http://www.gnu.org/licenses/>.
*
This file is derived from the file "scanner.cc", which was part of the * This file is derived from the file "scanner.cc", which was part of the
V8 project. The original copyright header follows: * V8 project. The original copyright header follows:
*
Copyright 2006-2012, the V8 project authors. All rights reserved. * Copyright 2006-2012, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
met: * met:
*
* Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above * * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following * copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided * disclaimer in the documentation and/or other materials provided
with the distribution. * with the distribution.
* Neither the name of Google Inc. nor the names of its * * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived * contributors may be used to endorse or promote products derived
from this software without specific prior written permission. * from this software without specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -50,6 +50,7 @@
* Solidity scanner. * Solidity scanner.
*/ */
#include <liblangutil/Common.h>
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <liblangutil/Scanner.h> #include <liblangutil/Scanner.h>
#include <algorithm> #include <algorithm>
@ -61,46 +62,6 @@ using namespace std;
namespace langutil 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) std::string to_string(ScannerError _errorCode)
{ {
switch (_errorCode) switch (_errorCode)
@ -595,7 +556,12 @@ void Scanner::scanToken()
token = Token::Period; token = Token::Period;
break; break;
case ':': case ':':
token = selectToken(Token::Colon); // : :=
advance();
if (m_char == '=')
token = selectToken(Token::AssemblyAssign);
else
token = Token::Colon;
break; break;
case ';': case ';':
token = selectToken(Token::Semicolon); token = selectToken(Token::Semicolon);

View File

@ -1,48 +1,48 @@
/* /*
This file is part of solidity. * This file is part of solidity.
*
solidity is free software: you can redistribute it and/or modify * solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. * (at your option) any later version.
*
solidity is distributed in the hope that it will be useful, * solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. * GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. * along with solidity. If not, see <http://www.gnu.org/licenses/>.
*
This file is derived from the file "scanner.h", which was part of the * This file is derived from the file "scanner.h", which was part of the
V8 project. The original copyright header follows: * V8 project. The original copyright header follows:
*
Copyright 2006-2012, the V8 project authors. All rights reserved. * Copyright 2006-2012, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
met: * met:
*
* Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above * * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following * copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided * disclaimer in the documentation and/or other materials provided
with the distribution. * with the distribution.
* Neither the name of Google Inc. nor the names of its * * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived * contributors may be used to endorse or promote products derived
from this software without specific prior written permission. * from this software without specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Lefteris Karapetsas <lefteris@ethdev.com> * @author Lefteris Karapetsas <lefteris@ethdev.com>

View File

@ -143,7 +143,7 @@ static Token keywordByName(string const& _name)
// and keywords to be put inside the keywords variable. // and keywords to be put inside the keywords variable.
#define KEYWORD(name, string, precedence) {string, Token::name}, #define KEYWORD(name, string, precedence) {string, Token::name},
#define TOKEN(name, string, precedence) #define TOKEN(name, string, precedence)
static const map<string, Token> keywords({TOKEN_LIST(TOKEN, KEYWORD)}); static map<string, Token> const keywords({TOKEN_LIST(TOKEN, KEYWORD)});
#undef KEYWORD #undef KEYWORD
#undef TOKEN #undef TOKEN
auto it = keywords.find(_name); auto it = keywords.find(_name);

View File

@ -140,6 +140,8 @@ namespace langutil
T(Dec, "--", 0) \ T(Dec, "--", 0) \
K(Delete, "delete", 0) \ K(Delete, "delete", 0) \
\ \
/* Inline Assembly Operators */ \
T(AssemblyAssign, ":=", 2) \
/* Keywords */ \ /* Keywords */ \
K(Anonymous, "anonymous", 0) \ K(Anonymous, "anonymous", 0) \
K(As, "as", 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 isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } 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 || 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 isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; }
constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; } constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; }

View File

@ -649,22 +649,22 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
auto end = m_asm.newTag(); auto end = m_asm.newTag();
m_asm.append(Instruction::MSIZE); // Result will be original top of memory 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::DUP1);
m_asm.append(Instruction::ISZERO);// (alloc 0) does not change MSIZE m_asm.append(Instruction::ISZERO);// (alloc 0) does not change MSIZE
m_asm.appendJumpI(end); m_asm.appendJumpI(end);
m_asm.append(u256(1)); m_asm.append(u256(1));
m_asm.append(Instruction::DUP2); // Copy N 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(u256(0x1f)); // Bit mask
m_asm.append(Instruction::NOT); // Invert m_asm.append(Instruction::NOT); // Invert
m_asm.append(Instruction::AND); // Align N-1 on 32 byte boundary m_asm.append(Instruction::AND); // Align N-1 on 32 byte boundary
m_asm.append(Instruction::MSIZE); // MSIZE is cheap m_asm.append(Instruction::MSIZE); // MSIZE is cheap
m_asm.append(Instruction::ADD); m_asm.append(Instruction::ADD);
m_asm.append(Instruction::MLOAD); // Updates MSIZE 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(end);
m_asm.append(Instruction::POP); // Discard duplicate N m_asm.append(Instruction::POP); // Discard duplicate N
_s.usedAlloc = true; _s.usedAlloc = true;
} }

View File

@ -65,7 +65,7 @@ private:
ReadCallback m_readFile; ReadCallback m_readFile;
}; };
static const CodeFragment NullCodeFragment; static CodeFragment const NullCodeFragment;
} }
} }

View File

@ -28,7 +28,7 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::lll; using namespace dev::lll;
bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile) bytes dev::lll::compileLLL(string const& _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
{ {
try try
{ {
@ -36,7 +36,7 @@ bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer
cs.populateStandard(); cs.populateStandard();
auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs);
if (_opt) if (_opt)
assembly = assembly.optimise(true, _evmVersion); assembly = assembly.optimise(true, _evmVersion, true, 200);
bytes ret = assembly.assemble().bytecode; bytes ret = assembly.assemble().bytecode;
for (auto i: cs.treesToKill) for (auto i: cs.treesToKill)
killBigints(i); killBigints(i);
@ -66,7 +66,7 @@ bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer
return bytes(); return bytes();
} }
std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile) std::string dev::lll::compileLLLToAsm(std::string const& _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
{ {
try try
{ {
@ -74,7 +74,7 @@ std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVe
cs.populateStandard(); cs.populateStandard();
auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs);
if (_opt) if (_opt)
assembly = assembly.optimise(true, _evmVersion); assembly = assembly.optimise(true, _evmVersion, true, 200);
string ret = assembly.assemblyString(); string ret = assembly.assemblyString();
for (auto i: cs.treesToKill) for (auto i: cs.treesToKill)
killBigints(i); killBigints(i);

View File

@ -36,8 +36,8 @@ namespace lll
using ReadCallback = std::function<std::string(std::string const&)>; using ReadCallback = std::function<std::string(std::string const&)>;
std::string parseLLL(std::string const& _src); std::string parseLLL(std::string const& _src);
std::string compileLLLToAsm(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); std::string compileLLLToAsm(std::string const& _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
bytes compileLLL(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); bytes compileLLL(std::string const& _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
} }
} }

View File

@ -44,7 +44,7 @@ CodeFragment const& CompilerState::getDef(std::string const& _s) const
void CompilerState::populateStandard() void CompilerState::populateStandard()
{ {
static const string s = "{" static string const s = "{"
"(def 'panic () (asm INVALID))" "(def 'panic () (asm INVALID))"
// Alternative macro version of alloc, which is currently implemented in the parser // 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))))))))" // "(def 'alloc (n) (raw (msize) (when n (pop (mload (+ (msize) (& (- n 1) (~ 0x1f))))))))"

View File

@ -51,8 +51,6 @@ set(sources
codegen/ABIFunctions.h codegen/ABIFunctions.h
codegen/ArrayUtils.cpp codegen/ArrayUtils.cpp
codegen/ArrayUtils.h codegen/ArrayUtils.h
codegen/AsmCodeGen.cpp
codegen/AsmCodeGen.h
codegen/Compiler.cpp codegen/Compiler.cpp
codegen/Compiler.h codegen/Compiler.h
codegen/CompilerContext.cpp codegen/CompilerContext.cpp
@ -82,14 +80,13 @@ set(sources
formal/VariableUsage.h formal/VariableUsage.h
interface/ABI.cpp interface/ABI.cpp
interface/ABI.h interface/ABI.h
interface/AssemblyStack.cpp
interface/AssemblyStack.h
interface/CompilerStack.cpp interface/CompilerStack.cpp
interface/CompilerStack.h interface/CompilerStack.h
interface/GasEstimator.cpp interface/GasEstimator.cpp
interface/GasEstimator.h interface/GasEstimator.h
interface/Natspec.cpp interface/Natspec.cpp
interface/Natspec.h interface/Natspec.h
interface/OptimiserSettings.h
interface/ReadFile.h interface/ReadFile.h
interface/StandardCompiler.cpp interface/StandardCompiler.cpp
interface/StandardCompiler.h interface/StandardCompiler.h

View File

@ -120,7 +120,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
vector<VariableOccurrence const*> uninitializedAccessesOrdered( vector<VariableOccurrence const*> uninitializedAccessesOrdered(
exitInfo.uninitializedVariableAccesses.begin(), exitInfo.uninitializedVariableAccesses.begin(),
exitInfo.uninitializedVariableAccesses.end() exitInfo.uninitializedVariableAccesses.end()
); );
boost::range::sort( boost::range::sort(
uninitializedAccessesOrdered, uninitializedAccessesOrdered,
[](VariableOccurrence const* lhs, VariableOccurrence const* rhs) -> bool [](VariableOccurrence const* lhs, VariableOccurrence const* rhs) -> bool

View File

@ -32,11 +32,12 @@ namespace dev
namespace solidity namespace solidity
{ {
/** Occurrence of a variable in a block of control flow. /**
* Stores the declaration of the referenced variable, the * Occurrence of a variable in a block of control flow.
* kind of the occurrence and possibly the node at which * Stores the declaration of the referenced variable, the
* it occurred. * kind of the occurrence and possibly the node at which
*/ * it occurred.
*/
class VariableOccurrence class VariableOccurrence
{ {
public: public:
@ -85,11 +86,12 @@ private:
ASTNode const* m_occurrence = nullptr; ASTNode const* m_occurrence = nullptr;
}; };
/** Node of the Control Flow Graph. /**
* The control flow is a directed graph connecting control flow blocks. * Node of the Control Flow Graph.
* An arc between two nodes indicates that the control flow can possibly * The control flow is a directed graph connecting control flow blocks.
* move from its start node to its end node during execution. * 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 struct CFGNode
{ {
/// Entry nodes. All CFG nodes from which control flow may move into this node. /// Entry nodes. All CFG nodes from which control flow may move into this node.

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -42,7 +42,7 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
bool DocStringAnalyser::visit(ContractDefinition const& _contract) bool DocStringAnalyser::visit(ContractDefinition const& _contract)
{ {
static const set<string> validTags = set<string>{"author", "title", "dev", "notice"}; static set<string> const validTags = set<string>{"author", "title", "dev", "notice"};
parseDocStrings(_contract, _contract.annotation(), validTags, "contracts"); parseDocStrings(_contract, _contract.annotation(), validTags, "contracts");
return true; return true;
@ -99,7 +99,7 @@ void DocStringAnalyser::handleConstructor(
DocumentedAnnotation& _annotation DocumentedAnnotation& _annotation
) )
{ {
static const set<string> validTags = set<string>{"author", "dev", "notice", "param"}; static set<string> const validTags = set<string>{"author", "dev", "notice", "param"};
parseDocStrings(_node, _annotation, validTags, "constructor"); parseDocStrings(_node, _annotation, validTags, "constructor");
checkParameters(_callable, _annotation); checkParameters(_callable, _annotation);
} }
@ -110,7 +110,7 @@ void DocStringAnalyser::handleCallable(
DocumentedAnnotation& _annotation DocumentedAnnotation& _annotation
) )
{ {
static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"}; static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param"};
parseDocStrings(_node, _annotation, validTags, "functions"); parseDocStrings(_node, _annotation, validTags, "functions");
checkParameters(_callable, _annotation); checkParameters(_callable, _annotation);
} }

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -246,7 +246,7 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array."); fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
if (Expression const* length = _typeName.length()) if (Expression const* length = _typeName.length())
{ {
TypePointer lengthTypeGeneric = length->annotation().type; TypePointer& lengthTypeGeneric = length->annotation().type;
if (!lengthTypeGeneric) if (!lengthTypeGeneric)
lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length); lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get()); RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get());
@ -298,11 +298,13 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
} }
declarations = m_resolver.nameFromCurrentScope(realName); 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."); declarationError(_identifier.location, "Multiple matching identifiers. Resolving overloaded identifiers is not supported.");
return size_t(-1); return size_t(-1);
} }
else if (declarations.size() == 0)
return size_t(-1);
if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front())) if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
if (var->isLocalVariable() && _crossesFunctionBoundary) if (var->isLocalVariable() && _crossesFunctionBoundary)
{ {
@ -322,9 +324,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
yul::AsmAnalyzer( yul::AsmAnalyzer(
analysisInfo, analysisInfo,
errorsIgnored, errorsIgnored,
EVMVersion(),
errorTypeForLoose, errorTypeForLoose,
yul::EVMDialect::looseAssemblyForEVM(), yul::EVMDialect::looseAssemblyForEVM(EVMVersion{}),
resolver resolver
).analyze(_inlineAssembly.operations()); ).analyze(_inlineAssembly.operations());
return false; return false;

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -139,14 +139,21 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
toString(arguments.size()) + toString(arguments.size()) +
" were provided." " were provided."
); );
if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType::bytesMemory()))
m_errorReporter.typeError( if (arguments.size() >= 1)
arguments.front()->location(), {
"Invalid type for argument in function call. " BoolResult result = type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType::bytesMemory());
"Invalid implicit conversion from " +
type(*arguments.front())->toString() + if (!result)
" to bytes memory requested." 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) if (arguments.size() < 2)
return {}; return {};
@ -262,16 +269,20 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
); );
} }
for (size_t i = 0; i < std::min(arguments->size(), parameterTypes.size()); ++i) 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(), (*arguments)[i]->location(),
"Invalid type for argument in constructor call. " "Invalid type for argument in constructor call. "
"Invalid implicit conversion from " + "Invalid implicit conversion from " +
type(*(*arguments)[i])->toString() + type(*(*arguments)[i])->toString() +
" to " + " to " +
parameterTypes[i]->toString() + 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."); 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 // type is filled either by ReferencesResolver directly from the type name or by
// TypeChecker at the VariableDeclarationStatement level. // TypeChecker at the VariableDeclarationStatement level.
TypePointer varType = _variable.annotation().type; 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."); m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded.");
break; break;
case Type::Category::Mapping:
if (auto mappingType = dynamic_cast<MappingType const*>(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: default:
break; break;
} }
@ -571,16 +577,20 @@ void TypeChecker::visitManually(
return; return;
} }
for (size_t i = 0; i < arguments.size(); ++i) 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(), arguments[i]->location(),
"Invalid type for argument in modifier invocation. " "Invalid type for argument in modifier invocation. "
"Invalid implicit conversion from " + "Invalid implicit conversion from " +
type(*arguments[i])->toString() + type(*arguments[i])->toString() +
" to " + " to " +
type(*(*parameters)[i])->toString() + type(*(*parameters)[i])->toString() +
" requested." " requested.",
result.message()
); );
}
} }
bool TypeChecker::visit(EventDefinition const& _eventDef) bool TypeChecker::visit(EventDefinition const& _eventDef)
@ -637,6 +647,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
bool requiresStorage = ref->second.isSlot || ref->second.isOffset; bool requiresStorage = ref->second.isSlot || ref->second.isOffset;
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration)) if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{ {
solAssert(var->type(), "Expected variable type!");
if (var->isConstant()) if (var->isConstant())
{ {
m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly."); 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( yul::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo, *_inlineAssembly.annotation().analysisInfo,
m_errorReporter, m_errorReporter,
m_evmVersion,
Error::Type::SyntaxError, Error::Type::SyntaxError,
yul::EVMDialect::looseAssemblyForEVM(), yul::EVMDialect::looseAssemblyForEVM(m_evmVersion),
identifierAccess identifierAccess
); );
if (!analyzer.analyze(_inlineAssembly.operations())) if (!analyzer.analyze(_inlineAssembly.operations()))
@ -772,29 +782,34 @@ void TypeChecker::endVisit(Return const& _return)
{ {
if (tupleType->components().size() != params->parameters().size()) if (tupleType->components().size() != params->parameters().size())
m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes))) else
m_errorReporter.typeError( {
_return.expression()->location(), BoolResult result = tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes));
"Return argument type " + if (!result)
type(*_return.expression())->toString() + m_errorReporter.typeErrorConcatenateDescriptions(
" is not implicitly convertible to expected type " + _return.expression()->location(),
TupleType(returnTypes).toString(false) + "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) else if (params->parameters().size() != 1)
m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
else else
{ {
TypePointer const& expected = type(*params->parameters().front()); TypePointer const& expected = type(*params->parameters().front());
if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected)) BoolResult result = type(*_return.expression())->isImplicitlyConvertibleTo(*expected);
m_errorReporter.typeError( if (!result)
m_errorReporter.typeErrorConcatenateDescriptions(
_return.expression()->location(), _return.expression()->location(),
"Return argument type " + "Return argument type " +
type(*_return.expression())->toString() + type(*_return.expression())->toString() +
" is not implicitly convertible to expected type (type of first return variable) " + " 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 else
{ {
var.accept(*this); var.accept(*this);
if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type)) BoolResult result = valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type);
if (!result)
{ {
auto errorMsg = "Type " + auto errorMsg = "Type " +
valueComponentType->toString() + valueComponentType->toString() +
@ -1013,11 +1029,23 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
); );
} }
else 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 (autoTypeDeductionNeeded)
{ {
if (!typeCanBeExpressed(variables)) if (!typeCanBeExpressed(variables))
@ -2046,25 +2074,27 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
} }
string errorMsg = "Member \"" + memberName + "\" not found or not visible " string errorMsg = "Member \"" + memberName + "\" not found or not visible "
"after argument-dependent lookup in " + exprType->toString() + "."; "after argument-dependent lookup in " + exprType->toString() + ".";
if (memberName == "value")
if (auto const& funType = dynamic_pointer_cast<FunctionType const>(exprType))
{ {
errorMsg.pop_back(); auto const& t = funType->returnParameterTypes();
errorMsg += " - did you forget the \"payable\" modifier?";
} if (memberName == "value")
else if (exprType->category() == Type::Category::Function)
{
if (auto const& funType = dynamic_pointer_cast<FunctionType const>(exprType))
{ {
auto const& t = funType->returnParameterTypes(); if (funType->kind() == FunctionType::Kind::Creation)
if (t.size() == 1) errorMsg = "Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available.";
if ( else
t.front()->category() == Type::Category::Contract || errorMsg = "Member \"value\" is only available for payable functions.";
t.front()->category() == Type::Category::Struct
)
errorMsg += " Did you intend to call the function?";
} }
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)) for (auto const& addressMember: AddressType::addressPayable().nativeMembers(nullptr))
if (addressMember.name == memberName) if (addressMember.name == memberName)
{ {
@ -2073,6 +2103,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member."; errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member.";
break; break;
} }
}
else if (auto addressType = dynamic_cast<AddressType const*>(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( m_errorReporter.fatalTypeError(
_memberAccess.location(), _memberAccess.location(),
errorMsg errorMsg
@ -2090,12 +2135,11 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.type = possibleMembers.front().type; annotation.type = possibleMembers.front().type;
if (auto funType = dynamic_cast<FunctionType const*>(annotation.type.get())) if (auto funType = dynamic_cast<FunctionType const*>(annotation.type.get()))
if (funType->bound() && !exprType->isImplicitlyConvertibleTo(*funType->selfType())) solAssert(
m_errorReporter.typeError( !funType->bound() || exprType->isImplicitlyConvertibleTo(*funType->selfType()),
_memberAccess.location(), "Function \"" + memberName + "\" cannot be called on an object of type " +
"Function \"" + memberName + "\" cannot be called on an object of type " + exprType->toString() + " (expected " + funType->selfType()->toString() + ")."
exprType->toString() + " (expected " + funType->selfType()->toString() + ")." );
);
if (auto const* structType = dynamic_cast<StructType const*>(exprType.get())) if (auto const* structType = dynamic_cast<StructType const*>(exprType.get()))
annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData); annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData);
@ -2116,26 +2160,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.isLValue = annotation.referencedDeclaration->isLValue(); 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<FunctionType const*>(type(_memberAccess).get()))
{
auto kind = callType->kind();
auto contractType = dynamic_cast<ContractType const*>(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 // 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. // although every subexpression is, so leaving this limited for now.
if (auto tt = dynamic_cast<TypeType const*>(exprType.get())) if (auto tt = dynamic_cast<TypeType const*>(exprType.get()))
@ -2159,6 +2183,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"Circular reference for contract code access." "Circular reference for contract code access."
); );
} }
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name")
annotation.isPure = true;
} }
return false; return false;
@ -2213,6 +2239,8 @@ bool TypeChecker::visit(IndexAccess const& _access)
case Type::Category::TypeType: case Type::Category::TypeType:
{ {
TypeType const& typeType = dynamic_cast<TypeType const&>(*baseType); TypeType const& typeType = dynamic_cast<TypeType const&>(*baseType);
if (dynamic_cast<ContractType const*>(typeType.actualType().get()))
m_errorReporter.typeError(_access.location(), "Index access for contracts or libraries is not possible.");
if (!index) if (!index)
resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType())); resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType()));
else else
@ -2324,8 +2352,13 @@ bool TypeChecker::visit(Identifier const& _identifier)
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration)) if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration))
annotation.isPure = annotation.isConstant = variableDeclaration->isConstant(); annotation.isPure = annotation.isConstant = variableDeclaration->isConstant();
else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration)) else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration))
{
if (dynamic_cast<FunctionType const*>(annotation.type.get())) if (dynamic_cast<FunctionType const*>(annotation.type.get()))
annotation.isPure = true; annotation.isPure = true;
}
else if (dynamic_cast<TypeType const*>(annotation.type.get()))
annotation.isPure = true;
// Check for deprecated function names. // Check for deprecated function names.
// The check is done here for the case without an actual function call. // The check is done here for the case without an actual function call.

View File

@ -48,7 +48,7 @@ class TypeChecker: private ASTConstVisitor
{ {
public: public:
/// @param _errorReporter provides the error logging functionality. /// @param _errorReporter provides the error logging functionality.
TypeChecker(EVMVersion _evmVersion, langutil::ErrorReporter& _errorReporter): TypeChecker(langutil::EVMVersion _evmVersion, langutil::ErrorReporter& _errorReporter):
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_errorReporter(_errorReporter) m_errorReporter(_errorReporter)
{} {}
@ -156,7 +156,7 @@ private:
ContractDefinition const* m_scope = nullptr; ContractDefinition const* m_scope = nullptr;
EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
/// Flag indicating whether we are currently inside an EmitStatement. /// Flag indicating whether we are currently inside an EmitStatement.
bool m_insideEmitStatement = false; bool m_insideEmitStatement = false;

View File

@ -340,7 +340,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{MagicType::Kind::Message, "data"}, {MagicType::Kind::Message, "data"},
{MagicType::Kind::Message, "sig"}, {MagicType::Kind::Message, "sig"},
{MagicType::Kind::MetaType, "creationCode"}, {MagicType::Kind::MetaType, "creationCode"},
{MagicType::Kind::MetaType, "runtimeCode"} {MagicType::Kind::MetaType, "runtimeCode"},
{MagicType::Kind::MetaType, "name"},
}; };
set<MagicMember> static const payableMembers{ set<MagicMember> static const payableMembers{
{MagicType::Kind::Message, "value"} {MagicType::Kind::Message, "value"}

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -1209,7 +1209,7 @@ private:
class Continue: public Statement class Continue: public Statement
{ {
public: public:
explicit Continue(SourceLocation const& _location, ASTPointer<ASTString> const& _docString): explicit Continue(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_location, _docString) {} Statement(_location, _docString) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const 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 class IndexAccess: public Expression
{ {

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @date 2017 * @date 2017

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @date 2017 * @date 2017

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Lefteris <lefteris@ethdev.com> * @author Lefteris <lefteris@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -113,9 +113,13 @@ bool ASTPrinter::visit(ParameterList const& _node)
bool ASTPrinter::visit(FunctionDefinition const& _node) bool ASTPrinter::visit(FunctionDefinition const& _node)
{ {
writeLine("FunctionDefinition \"" + _node.name() + "\"" + writeLine(
(_node.isPublic() ? " - public" : "") + "FunctionDefinition \"" +
(_node.stateMutability() == StateMutability::View ? " - const" : "")); _node.name() +
"\"" +
(_node.isPublic() ? " - public" : "") +
(_node.stateMutability() == StateMutability::View ? " - const" : "")
);
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
@ -305,8 +309,12 @@ bool ASTPrinter::visit(TupleExpression const& _node)
bool ASTPrinter::visit(UnaryOperation const& _node) bool ASTPrinter::visit(UnaryOperation const& _node)
{ {
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + writeLine(
") " + TokenTraits::toString(_node.getOperator())); string("UnaryOperation (") +
(_node.isPrefixOperation() ? "prefix" : "postfix") +
") " +
TokenTraits::toString(_node.getOperator())
);
printType(_node); printType(_node);
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>

View File

@ -35,13 +35,13 @@ enum class ExperimentalFeature
TestOnlyAnalysis TestOnlyAnalysis
}; };
static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis = static std::map<ExperimentalFeature, bool> const ExperimentalFeatureOnlyAnalysis =
{ {
{ ExperimentalFeature::SMTChecker, true }, { ExperimentalFeature::SMTChecker, true },
{ ExperimentalFeature::TestOnlyAnalysis, true }, { ExperimentalFeature::TestOnlyAnalysis, true },
}; };
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames = static std::map<std::string, ExperimentalFeature> const ExperimentalFeatureNames =
{ {
{ "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 }, { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
{ "SMTChecker", ExperimentalFeature::SMTChecker }, { "SMTChecker", ExperimentalFeature::SMTChecker },

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -496,8 +496,8 @@ BoolResult AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo)) if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo))
return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable(); return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable();
return isImplicitlyConvertibleTo(_convertTo) || return isImplicitlyConvertibleTo(_convertTo) ||
_convertTo.category() == Category::Integer || _convertTo.category() == Category::Integer ||
(_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8); (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8);
} }
string AddressType::toString(bool) const string AddressType::toString(bool) const
@ -1380,7 +1380,7 @@ string StringLiteralType::richIdentifier() const
return "t_stringliteral_" + toHex(keccak256(m_value).asBytes()); 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()) if (_other.category() != category())
return false; return false;
@ -1463,7 +1463,7 @@ TypeResult FixedBytesType::binaryOperatorResult(Token _operator, TypePointer con
return TypePointer(); return TypePointer();
} }
MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) const MemberList::MemberMap FixedBytesType::nativeMembers(ContractDefinition const*) const
{ {
return MemberList::MemberMap{MemberList::Member{"length", make_shared<IntegerType>(8)}}; return MemberList::MemberMap{MemberList::Member{"length", make_shared<IntegerType>(8)}};
} }
@ -1611,7 +1611,7 @@ string ReferenceType::identifierLocationSuffix() const
return id; return id;
} }
BoolResult ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const BoolResult ArrayType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{ {
if (_convertTo.category() != category()) if (_convertTo.category() != category())
return false; 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)) if (isImplicitlyConvertibleTo(_convertTo))
return true; return true;
@ -1707,8 +1707,8 @@ bool ArrayType::operator==(Type const& _other) const
bool ArrayType::validForCalldata() const bool ArrayType::validForCalldata() const
{ {
if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType().get())) if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType().get()))
if (!arrayBaseType->validForCalldata()) if (!arrayBaseType->validForCalldata())
return false; return false;
return unlimitedCalldataEncodedSize(true) <= numeric_limits<unsigned>::max(); return unlimitedCalldataEncodedSize(true) <= numeric_limits<unsigned>::max();
} }
@ -2010,7 +2010,7 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
return variablesAndOffsets; return variablesAndOffsets;
} }
BoolResult StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const BoolResult StructType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{ {
if (_convertTo.category() != category()) if (_convertTo.category() != category())
return false; return false;
@ -3062,7 +3062,8 @@ string FunctionType::externalSignature() const
solAssert(false, "Invalid function type for requesting external signature."); solAssert(false, "Invalid function type for requesting external signature.");
} }
bool const inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary(); // "inLibrary" is only relevant if this is not an event.
bool const inLibrary = kind() != Kind::Event && dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
FunctionTypePointer external = interfaceFunctionType(); FunctionTypePointer external = interfaceFunctionType();
solAssert(!!external, "External function type requested."); solAssert(!!external, "External function type requested.");
auto parameterTypes = external->parameterTypes(); auto parameterTypes = external->parameterTypes();
@ -3298,7 +3299,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
return members; return members;
} }
ModifierType::ModifierType(const ModifierDefinition& _modifier) ModifierType::ModifierType(ModifierDefinition const& _modifier)
{ {
TypePointers params; TypePointers params;
params.reserve(_modifier.parameters().size()); params.reserve(_modifier.parameters().size());
@ -3327,8 +3328,12 @@ bool ModifierType::operator==(Type const& _other) const
return false; return false;
auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }; auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), if (!equal(
other.m_parameterTypes.cbegin(), typeCompare)) m_parameterTypes.cbegin(),
m_parameterTypes.cend(),
other.m_parameterTypes.cbegin(),
typeCompare
))
return false; return false;
return true; return true;
} }
@ -3485,8 +3490,9 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition(); ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
if (contract.canBeDeployed()) if (contract.canBeDeployed())
return MemberList::MemberMap({ return MemberList::MemberMap({
{"creationCode", std::make_shared<ArrayType>(DataLocation::Memory)}, {"creationCode", make_shared<ArrayType>(DataLocation::Memory)},
{"runtimeCode", std::make_shared<ArrayType>(DataLocation::Memory)} {"runtimeCode", make_shared<ArrayType>(DataLocation::Memory)},
{"name", make_shared<ArrayType>(DataLocation::Memory, true)},
}); });
else else
return {}; return {};

View File

@ -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 solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
solidity is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
@ -216,7 +216,6 @@ public:
/// @returns number of bytes used by this type when encoded for CALL. If it is a dynamic type, /// @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 /// returns the size of the pointer (usually 32). Returns 0 if the type cannot be encoded
/// in calldata. /// 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. /// 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; } 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 /// @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 class AddressType: public Type
{ {
public: public:
static AddressType& address() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::NonPayable)); return *addr; } static AddressType& address() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::NonPayable)); return *addr; }
static AddressType& addressPayable() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::Payable)); return *addr; } static AddressType& addressPayable() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::Payable)); return *addr; }
Category category() const override { return Category::Address; } Category category() const override { return Category::Address; }
@ -707,7 +706,7 @@ public:
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
std::string richIdentifier() 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; unsigned calldataEncodedSize(bool _padded) const override;
bool isDynamicallySized() const override { return m_hasDynamicLength; } bool isDynamicallySized() const override { return m_hasDynamicLength; }
bool isDynamicallyEncoded() const override; bool isDynamicallyEncoded() const override;
@ -736,6 +735,13 @@ public:
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override; 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: private:
/// String is interpreted as a subtype of Bytes. /// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String }; enum class ArrayKind { Ordinary, Bytes, String };
@ -823,7 +829,7 @@ public:
Category category() const override { return Category::Struct; } Category category() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
ReferenceType(_location), m_struct(_struct) {} ReferenceType(_location), m_struct(_struct) {}
BoolResult isImplicitlyConvertibleTo(const Type& _convertTo) const override; BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override; unsigned calldataEncodedSize(bool _padded) const override;

View File

@ -308,6 +308,7 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
break; break;
case Type::Category::Array: case Type::Category::Array:
case Type::Category::Struct: case Type::Category::Struct:
case Type::Category::Mapping:
solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type."); solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type.");
templ("body", "cleaned := value"); templ("body", "cleaned := value");
break; break;
@ -1089,7 +1090,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
if (_options.dynamicInplace) if (_options.dynamicInplace)
encode = Whiskers{"pos := <encode>(memberValue, pos)"} encode = Whiskers{"pos := <encode>(memberValue, pos)"}
("encode", abiEncodeAndReturnUpdatedPosFunction(*memberTypeFrom, *memberTypeTo, subOptions)) ("encode", abiEncodeAndReturnUpdatedPosFunction(*memberTypeFrom, *memberTypeTo, subOptions))
.render(); .render();
else else
{ {
Whiskers encodeTempl( Whiskers encodeTempl(
@ -1262,7 +1263,7 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo
return abiDecodingFunctionValueType(_type, _fromMemory); return abiDecodingFunctionValueType(_type, _fromMemory);
} }
string ABIFunctions::abiDecodingFunctionValueType(const Type& _type, bool _fromMemory) string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromMemory)
{ {
TypePointer decodingType = _type.decodingType(); TypePointer decodingType = _type.decodingType();
solAssert(decodingType, ""); solAssert(decodingType, "");

View File

@ -50,7 +50,7 @@ using TypePointers = std::vector<TypePointer>;
class ABIFunctions class ABIFunctions
{ {
public: 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 /// @returns name of an assembly function to ABI-encode values of @a _givenTypes
/// into memory, converting the types to @a _targetTypes on the fly. /// 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. /// Map from function name to code for a multi-use function.
std::map<std::string, std::string> m_requestedFunctions; std::map<std::string, std::string> m_requestedFunctions;
std::set<std::string> m_externallyUsedFunctions; std::set<std::string> m_externallyUsedFunctions;
EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
}; };
} }

View File

@ -623,7 +623,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD
<< Instruction::SWAP1; << Instruction::SWAP1;
// stack: data_pos_end data_pos // stack: data_pos_end data_pos
if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) if (_type.storageStride() < 32)
clearStorageLoop(make_shared<IntegerType>(256)); clearStorageLoop(make_shared<IntegerType>(256));
else else
clearStorageLoop(_type.baseType()); clearStorageLoop(_type.baseType());
@ -769,7 +769,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
// stack: ref new_length data_pos new_size delete_end // stack: ref new_length data_pos new_size delete_end
_context << Instruction::SWAP2 << Instruction::ADD; _context << Instruction::SWAP2 << Instruction::ADD;
// stack: ref new_length delete_end delete_start // stack: ref new_length delete_end delete_start
if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) if (_type.storageStride() < 32)
ArrayUtils(_context).clearStorageLoop(make_shared<IntegerType>(256)); ArrayUtils(_context).clearStorageLoop(make_shared<IntegerType>(256));
else else
ArrayUtils(_context).clearStorageLoop(_type.baseType()); ArrayUtils(_context).clearStorageLoop(_type.baseType());
@ -934,8 +934,11 @@ void ArrayUtils::clearStorageLoop(TypePointer const& _type) const
eth::AssemblyItem loopStart = _context.appendJumpToNew(); eth::AssemblyItem loopStart = _context.appendJumpToNew();
_context << loopStart; _context << loopStart;
// check for loop condition // check for loop condition
_context << Instruction::DUP1 << Instruction::DUP3 _context <<
<< Instruction::GT << Instruction::ISZERO; Instruction::DUP1 <<
Instruction::DUP3 <<
Instruction::GT <<
Instruction::ISZERO;
eth::AssemblyItem zeroLoopEnd = _context.newTag(); eth::AssemblyItem zeroLoopEnd = _context.newTag();
_context.appendConditionalJumpTo(zeroLoopEnd); _context.appendConditionalJumpTo(zeroLoopEnd);
// delete // delete

View File

@ -35,16 +35,20 @@ void Compiler::compileContract(
bytes const& _metadata bytes const& _metadata
) )
{ {
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns); ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimiserSettings);
runtimeCompiler.compileContract(_contract, _otherCompilers); runtimeCompiler.compileContract(_contract, _otherCompilers);
m_runtimeContext.appendAuxiliaryData(_metadata); m_runtimeContext.appendAuxiliaryData(_metadata);
// This might modify m_runtimeContext because it can access runtime functions at // This might modify m_runtimeContext because it can access runtime functions at
// creation time. // 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_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
m_context.optimise(m_optimize, m_optimizeRuns); m_context.optimise(m_optimiserSettings);
} }
std::shared_ptr<eth::Assembly> Compiler::runtimeAssemblyPtr() const std::shared_ptr<eth::Assembly> Compiler::runtimeAssemblyPtr() const

View File

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <libsolidity/codegen/CompilerContext.h> #include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <functional> #include <functional>
@ -34,9 +35,8 @@ namespace solidity {
class Compiler class Compiler
{ {
public: public:
explicit Compiler(EVMVersion _evmVersion = EVMVersion{}, bool _optimize = false, unsigned _runs = 200): explicit Compiler(langutil::EVMVersion _evmVersion, OptimiserSettings _optimiserSettings):
m_optimize(_optimize), m_optimiserSettings(std::move(_optimiserSettings)),
m_optimizeRuns(_runs),
m_runtimeContext(_evmVersion), m_runtimeContext(_evmVersion),
m_context(_evmVersion, &m_runtimeContext) m_context(_evmVersion, &m_runtimeContext)
{ } { }
@ -78,8 +78,7 @@ public:
eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const; eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const;
private: private:
bool const m_optimize; OptimiserSettings const m_optimiserSettings;
unsigned const m_optimizeRuns;
CompilerContext m_runtimeContext; CompilerContext m_runtimeContext;
size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present. size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
CompilerContext m_context; CompilerContext m_context;

View File

@ -23,7 +23,6 @@
#include <libsolidity/codegen/CompilerContext.h> #include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/AsmCodeGen.h>
#include <libsolidity/codegen/Compiler.h> #include <libsolidity/codegen/Compiler.h>
#include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/interface/Version.h> #include <libsolidity/interface/Version.h>
@ -31,7 +30,9 @@
#include <libyul/AsmParser.h> #include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h> #include <libyul/AsmAnalysisInfo.h>
#include <libyul/backends/evm/AsmCodeGen.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <libyul/optimiser/Suite.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
@ -129,8 +130,10 @@ void CompilerContext::appendMissingLowLevelFunctions()
} }
} }
void CompilerContext::addVariable(VariableDeclaration const& _declaration, void CompilerContext::addVariable(
unsigned _offsetToCurrent) VariableDeclaration const& _declaration,
unsigned _offsetToCurrent
)
{ {
solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, ""); solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, "");
unsigned sizeOnStack = _declaration.annotation().type->sizeOnStack(); unsigned sizeOnStack = _declaration.annotation().type->sizeOnStack();
@ -265,7 +268,7 @@ unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
return m_asm->deposit() - _offset - 1; return m_asm->deposit() - _offset - 1;
} }
pair<u256, unsigned> CompilerContext::storageLocationOfVariable(const Declaration& _declaration) const pair<u256, unsigned> CompilerContext::storageLocationOfVariable(Declaration const& _declaration) const
{ {
auto it = m_stateVariables.find(&_declaration); auto it = m_stateVariables.find(&_declaration);
solAssert(it != m_stateVariables.end(), "Variable not found in storage."); solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
@ -326,12 +329,19 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
void CompilerContext::appendInlineAssembly( void CompilerContext::appendInlineAssembly(
string const& _assembly, string const& _assembly,
vector<string> const& _localVariables, vector<string> const& _localVariables,
set<string> const&, set<string> const& _externallyUsedFunctions,
bool _system bool _system,
bool _optimise
) )
{ {
int startStackHeight = stackHeight(); int startStackHeight = stackHeight();
set<yul::YulString> externallyUsedIdentifiers;
for (auto const& fun: _externallyUsedFunctions)
externallyUsedIdentifiers.insert(yul::YulString(fun));
for (auto const& var: _localVariables)
externallyUsedIdentifiers.insert(yul::YulString(var));
yul::ExternalIdentifierAccess identifierAccess; yul::ExternalIdentifierAccess identifierAccess;
identifierAccess.resolve = [&]( identifierAccess.resolve = [&](
yul::Identifier const& _identifier, yul::Identifier const& _identifier,
@ -374,25 +384,16 @@ void CompilerContext::appendInlineAssembly(
ErrorList errors; ErrorList errors;
ErrorReporter errorReporter(errors); ErrorReporter errorReporter(errors);
auto scanner = make_shared<langutil::Scanner>(langutil::CharStream(_assembly, "--CODEGEN--")); auto scanner = make_shared<langutil::Scanner>(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 #ifdef SOL_OUTPUT_ASM
cout << yul::AsmPrinter()(*parserResult) << endl; cout << yul::AsmPrinter()(*parserResult) << endl;
#endif #endif
yul::AsmAnalysisInfo analysisInfo;
bool analyzerResult = false; auto reportError = [&](string const& _context)
if (parserResult)
analyzerResult = yul::AsmAnalyzer(
analysisInfo,
errorReporter,
m_evmVersion,
boost::none,
yul::EVMDialect::strictAssemblyForEVM(),
identifierAccess.resolve
).analyze(*parserResult);
if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
{ {
string message = string message =
"Error parsing/analyzing inline assembly block:\n" "Error parsing/analyzing inline assembly block:\n" +
_context + "\n"
"------------------ Input: -----------------\n" + "------------------ Input: -----------------\n" +
_assembly + "\n" _assembly + "\n"
"------------------ Errors: ----------------\n"; "------------------ Errors: ----------------\n";
@ -404,10 +405,47 @@ void CompilerContext::appendInlineAssembly(
message += "-------------------------------------------\n"; message += "-------------------------------------------\n";
solAssert(false, message); 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."); 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) // Reset the source location to the one of the node (instead of the CODEGEN source location)
updateSourceLocation(); updateSourceLocation();
@ -446,6 +484,21 @@ void CompilerContext::updateSourceLocation()
m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location()); 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( eth::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel(
Declaration const& _declaration, Declaration const& _declaration,
CompilerContext& _context CompilerContext& _context

View File

@ -27,6 +27,8 @@
#include <libsolidity/ast/Types.h> #include <libsolidity/ast/Types.h>
#include <libsolidity/codegen/ABIFunctions.h> #include <libsolidity/codegen/ABIFunctions.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
@ -50,7 +52,7 @@ class Compiler;
class CompilerContext class CompilerContext
{ {
public: public:
explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr): explicit CompilerContext(langutil::EVMVersion _evmVersion, CompilerContext* _runtimeContext = nullptr):
m_asm(std::make_shared<eth::Assembly>()), m_asm(std::make_shared<eth::Assembly>()),
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_runtimeContext(_runtimeContext), m_runtimeContext(_runtimeContext),
@ -60,7 +62,7 @@ public:
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); 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. /// Update currently enabled set of experimental features.
void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; } void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
@ -214,14 +216,15 @@ public:
std::string const& _assembly, std::string const& _assembly,
std::vector<std::string> const& _localVariables = std::vector<std::string>(), std::vector<std::string> const& _localVariables = std::vector<std::string>(),
std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(), std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(),
bool _system = false bool _system = false,
bool _optimise = false
); );
/// Appends arbitrary data to the end of the bytecode. /// Appends arbitrary data to the end of the bytecode.
void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); }
/// Run optimisation step. /// 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. /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
CompilerContext* runtimeContext() const { return m_runtimeContext; } CompilerContext* runtimeContext() const { return m_runtimeContext; }
@ -267,10 +270,12 @@ private:
std::vector<ContractDefinition const*>::const_iterator _searchStart std::vector<ContractDefinition const*>::const_iterator _searchStart
); );
/// @returns an iterator to the contract directly above the given contract. /// @returns an iterator to the contract directly above the given contract.
std::vector<ContractDefinition const*>::const_iterator superContract(const ContractDefinition &_contract) const; std::vector<ContractDefinition const*>::const_iterator superContract(ContractDefinition const& _contract) const;
/// Updates source location set in the assembly. /// Updates source location set in the assembly.
void updateSourceLocation(); void updateSourceLocation();
eth::Assembly::OptimiserSettings translateOptimiserSettings(OptimiserSettings const& _settings);
/** /**
* Helper class that manages function labels and ensures that referenced functions are * Helper class that manages function labels and ensures that referenced functions are
* compiled in a specific order. * compiled in a specific order.
@ -291,7 +296,7 @@ private:
Declaration const* nextFunctionToCompile() const; Declaration const* nextFunctionToCompile() const;
/// Informs the queue that we are about to compile the given function, i.e. removes /// Informs the queue that we are about to compile the given function, i.e. removes
/// the function from the queue of functions to compile. /// 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. /// Labels pointing to the entry points of functions.
std::map<Declaration const*, eth::AssemblyItem> m_entryLabels; std::map<Declaration const*, eth::AssemblyItem> m_entryLabels;
@ -305,7 +310,7 @@ private:
eth::AssemblyPointer m_asm; eth::AssemblyPointer m_asm;
/// Version of the EVM to compile against. /// Version of the EVM to compile against.
EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
/// Activated experimental features. /// Activated experimental features.
std::set<ExperimentalFeature> m_experimentalFeatures; std::set<ExperimentalFeature> m_experimentalFeatures;
/// Other already compiled contracts to be used in contract creation calls. /// Other already compiled contracts to be used in contract creation calls.

View File

@ -37,11 +37,11 @@ namespace dev
namespace solidity namespace solidity
{ {
const unsigned CompilerUtils::dataStartOffset = 4; unsigned const CompilerUtils::dataStartOffset = 4;
const size_t CompilerUtils::freeMemoryPointer = 64; size_t const CompilerUtils::freeMemoryPointer = 64;
const size_t CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32; size_t const CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32;
const size_t CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32; size_t const CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32;
const unsigned CompilerUtils::identityContractAddress = 4; unsigned const CompilerUtils::identityContractAddress = 4;
static_assert(CompilerUtils::freeMemoryPointer >= 64, "Free memory pointer must not overlap with scratch area."); 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."); static_assert(CompilerUtils::zeroPointer >= CompilerUtils::freeMemoryPointer + 32, "Zero pointer must not overlap with free memory pointer.");
@ -70,6 +70,13 @@ void CompilerUtils::allocateMemory()
storeFreeMemoryPointer(); storeFreeMemoryPointer();
} }
void CompilerUtils::allocateMemory(u256 const& size)
{
fetchFreeMemoryPointer();
m_context << Instruction::DUP1 << size << Instruction::ADD;
storeFreeMemoryPointer();
}
void CompilerUtils::toSizeAfterFreeMemoryPointer() void CompilerUtils::toSizeAfterFreeMemoryPointer()
{ {
fetchFreeMemoryPointer(); fetchFreeMemoryPointer();
@ -243,7 +250,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem
gt(add(array_data_start, mul(array_length, <item_size>)), input_end) gt(add(array_data_start, mul(array_length, <item_size>)), input_end)
) { revert(0, 0) } ) { 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"}); 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) // stack: v1 v2 ... v(k-1) input_end base_offset current_offset v(k)
moveIntoStack(3); 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 // stack: input_end base_offset next_pointer array_length data_pointer
m_context << Instruction::SWAP2; m_context << Instruction::SWAP2;
// stack: input_end base_offset data_pointer array_length next_pointer // stack: input_end base_offset data_pointer array_length next_pointer
unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true);
m_context.appendInlineAssembly(R"({ m_context.appendInlineAssembly(R"({
if or( if or(
gt(array_length, 0x100000000), 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) } ) { revert(0, 0) }
})", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"});
} }
@ -517,7 +523,7 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
codecopy(memptr, codesize(), size) codecopy(memptr, codesize(), size)
memptr := add(memptr, 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"}); m_context.appendInlineAssembly(templ.render(), {"length", "memptr"});
} }
else else
@ -653,10 +659,13 @@ void CompilerUtils::convertType(
bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer; bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer;
if (chopSignBitsPending) if (chopSignBitsPending)
{ {
const IntegerType& targetIntegerType = dynamic_cast<const IntegerType &>(_targetType); IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
chopSignBitsPending = targetIntegerType.isSigned(); chopSignBitsPending = targetIntegerType.isSigned();
} }
if (targetTypeCategory == Type::Category::FixedPoint)
solUnimplemented("Not yet implemented - FixedPointType.");
switch (stackTypeCategory) switch (stackTypeCategory)
{ {
case Type::Category::FixedBytes: case Type::Category::FixedBytes:
@ -811,9 +820,8 @@ void CompilerUtils::convertType(
{ {
auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType); auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
solAssert(arrayType.isByteArray(), ""); solAssert(arrayType.isByteArray(), "");
u256 storageSize(32 + ((data.size() + 31) / 32) * 32); unsigned storageSize = 32 + ((data.size() + 31) / 32) * 32;
m_context << storageSize; allocateMemory(storageSize);
allocateMemory();
// stack: mempos // stack: mempos
m_context << Instruction::DUP1 << u256(data.size()); m_context << Instruction::DUP1 << u256(data.size());
storeInMemoryDynamic(IntegerType::uint256()); storeInMemoryDynamic(IntegerType::uint256());
@ -936,8 +944,7 @@ void CompilerUtils::convertType(
{ {
case DataLocation::Storage: case DataLocation::Storage:
// stack: <source ref> // stack: <source ref>
m_context << typeOnStack.memorySize(); allocateMemory(typeOnStack.memorySize());
allocateMemory();
m_context << Instruction::SWAP1 << Instruction::DUP2; m_context << Instruction::SWAP1 << Instruction::DUP2;
// stack: <memory ptr> <source ref> <memory ptr> // stack: <memory ptr> <source ref> <memory ptr>
for (auto const& member: typeOnStack.members(nullptr)) for (auto const& member: typeOnStack.members(nullptr))
@ -1101,8 +1108,7 @@ void CompilerUtils::pushZeroValue(Type const& _type)
1, 1,
[type](CompilerContext& _context) { [type](CompilerContext& _context) {
CompilerUtils utils(_context); CompilerUtils utils(_context);
_context << u256(max(32u, type->calldataEncodedSize())); utils.allocateMemory(max(32u, type->calldataEncodedSize()));
utils.allocateMemory();
_context << Instruction::DUP1; _context << Instruction::DUP1;
if (auto structType = dynamic_cast<StructType const*>(type.get())) if (auto structType = dynamic_cast<StructType const*>(type.get()))

View File

@ -49,6 +49,10 @@ public:
/// Stack pre: <size> /// Stack pre: <size>
/// Stack post: <mem_start> /// Stack post: <mem_start>
void allocateMemory(); void allocateMemory();
/// Allocates a number of bytes in memory as given on the stack.
/// Stack pre:
/// Stack post: <mem_start>
void allocateMemory(u256 const& size);
/// Appends code that transforms memptr to (memptr - free_memptr) memptr /// Appends code that transforms memptr to (memptr - free_memptr) memptr
/// Stack pre: <mem_end> /// Stack pre: <mem_end>
/// Stack post: <size> <mem_start> /// Stack post: <size> <mem_start>
@ -84,7 +88,6 @@ public:
); );
/// Stores a 256 bit integer from stack in memory. /// Stores a 256 bit integer from stack in memory.
/// @param _offset offset in memory /// @param _offset offset in memory
/// @param _type type of the data on the stack
void storeInMemory(unsigned _offset); void storeInMemory(unsigned _offset);
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack /// 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 /// and also updates that. For reference types, only copies the data pointer. Fails for
@ -187,6 +190,11 @@ public:
/// Stack post: /// Stack post:
void memoryCopy(); 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) /// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true)
/// external function type <address><function identifier> into two stack slots: /// external function type <address><function identifier> into two stack slots:
/// address (right aligned), function identifier (right aligned) /// address (right aligned), function identifier (right aligned)
@ -273,23 +281,18 @@ public:
/// Bytes we need to the start of call data. /// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier. /// - 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; /// 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. /// 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). /// Starting offset for memory available to the user (aka the contract).
static const size_t generalPurposeMemoryStart; static size_t const generalPurposeMemoryStart;
private: private:
/// Address of the precompiled identity contract. /// Address of the precompiled identity contract.
static const unsigned identityContractAddress; static unsigned const identityContractAddress;
/// Stores the given string in memory.
/// Stack pre: mempos
/// Stack post:
void storeStringData(bytesConstRef _data);
/// Appends code that cleans higher-order bits for integer types. /// Appends code that cleans higher-order bits for integer types.
void cleanHigherOrderBits(IntegerType const& _typeOnStack); void cleanHigherOrderBits(IntegerType const& _typeOnStack);

View File

@ -21,14 +21,16 @@
*/ */
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/AsmCodeGen.h>
#include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/ContractCompiler.h> #include <libsolidity/codegen/ContractCompiler.h>
#include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/codegen/ExpressionCompiler.h>
#include <libyul/backends/evm/AsmCodeGen.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmasm/GasMeter.h> #include <libevmasm/GasMeter.h>
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
@ -175,6 +177,7 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
m_context.pushSubroutineSize(m_context.runtimeSub()); m_context.pushSubroutineSize(m_context.runtimeSub());
m_context.pushSubroutineOffset(m_context.runtimeSub()); m_context.pushSubroutineOffset(m_context.runtimeSub());
// This code replaces the address added by appendDeployTimeAddress().
m_context.appendInlineAssembly(R"( m_context.appendInlineAssembly(R"(
{ {
// If code starts at 11, an mstore(0) writes to the full PUSH20 plus data // 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 let codepos := 11
codecopy(codepos, subOffset, subSize) codecopy(codepos, subOffset, subSize)
// Check that the first opcode is a PUSH20 // Check that the first opcode is a PUSH20
switch eq(0x73, byte(0, mload(codepos))) if iszero(eq(0x73, byte(0, mload(codepos)))) { invalid() }
case 0 { invalid() }
mstore(0, address()) mstore(0, address())
mstore8(codepos, 0x73) mstore8(codepos, 0x73)
return(codepos, subSize) return(codepos, subSize)
@ -353,7 +355,7 @@ bool hasPayableFunctions(ContractDefinition const& _contract)
void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract) void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract)
{ {
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions(); map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints; map<FixedHash<4>, eth::AssemblyItem const> callDataUnpackerEntryPoints;
if (_contract.isLibrary()) if (_contract.isLibrary())
{ {
@ -389,7 +391,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
sortedIDs.emplace_back(it.first); sortedIDs.emplace_back(it.first);
} }
std::sort(sortedIDs.begin(), sortedIDs.end()); std::sort(sortedIDs.begin(), sortedIDs.end());
appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimise_runs); appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimiserSettings.expectedExecutionsPerDeployment);
} }
m_context << notFound; m_context << notFound;
@ -482,7 +484,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr
solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
for (VariableDeclaration const* variable: _contract.stateVariables()) for (VariableDeclaration const* variable: _contract.stateVariables())
if (variable->value() && !variable->isConstant()) 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) bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
@ -495,9 +497,9 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
m_continueTags.clear(); m_continueTags.clear();
if (_variableDeclaration.isConstant()) if (_variableDeclaration.isConstant())
ExpressionCompiler(m_context, m_optimise).appendConstStateVariableAccessor(_variableDeclaration); ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendConstStateVariableAccessor(_variableDeclaration);
else else
ExpressionCompiler(m_context, m_optimise).appendStateVariableAccessor(_variableDeclaration); ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableAccessor(_variableDeclaration);
return false; return false;
} }
@ -713,10 +715,11 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
} }
}; };
solAssert(_inlineAssembly.annotation().analysisInfo, ""); solAssert(_inlineAssembly.annotation().analysisInfo, "");
CodeGenerator::assemble( yul::CodeGenerator::assemble(
_inlineAssembly.operations(), _inlineAssembly.operations(),
*_inlineAssembly.annotation().analysisInfo, *_inlineAssembly.annotation().analysisInfo,
*m_context.assemblyPtr(), *m_context.assemblyPtr(),
m_context.evmVersion(),
identifierAccess identifierAccess
); );
m_context.setStackOffset(startStackHeight); m_context.setStackOffset(startStackHeight);
@ -975,7 +978,13 @@ void ContractCompiler::appendMissingFunctions()
m_context.appendMissingLowLevelFunctions(); m_context.appendMissingLowLevelFunctions();
auto abiFunctions = m_context.abiFunctions().requestedFunctions(); auto abiFunctions = m_context.abiFunctions().requestedFunctions();
if (!abiFunctions.first.empty()) 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() void ContractCompiler::appendModifierOrFunctionCode()
@ -1050,7 +1059,7 @@ void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration con
void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType) 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); expressionCompiler.compile(_expression);
if (_targetType) if (_targetType)
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);

View File

@ -28,8 +28,10 @@
#include <functional> #include <functional>
#include <ostream> #include <ostream>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
/** /**
* Code generator at the contract level. Can be used to generate code for exactly one contract * 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 class ContractCompiler: private ASTConstVisitor
{ {
public: public:
explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise, size_t _optimise_runs = 200): explicit ContractCompiler(
m_optimise(_optimise), ContractCompiler* _runtimeCompiler,
m_optimise_runs(_optimise_runs), CompilerContext& _context,
OptimiserSettings _optimiserSettings
):
m_optimiserSettings(std::move(_optimiserSettings)),
m_runtimeCompiler(_runtimeCompiler), m_runtimeCompiler(_runtimeCompiler),
m_context(_context) m_context(_context)
{ {
m_context = CompilerContext(_context.evmVersion(), _runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
} }
void compileContract( void compileContract(
@ -130,8 +134,7 @@ private:
/// Sets the stack height for the visited loop. /// Sets the stack height for the visited loop.
void storeStackHeight(ASTNode const* _node); void storeStackHeight(ASTNode const* _node);
bool const m_optimise; OptimiserSettings const m_optimiserSettings;
size_t const m_optimise_runs = 200;
/// Pointer to the runtime compiler in case this is a creation compiler. /// Pointer to the runtime compiler in case this is a creation compiler.
ContractCompiler* m_runtimeCompiler = nullptr; ContractCompiler* m_runtimeCompiler = nullptr;
CompilerContext& m_context; CompilerContext& m_context;

View File

@ -106,18 +106,49 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get())) if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
{ {
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
solUnimplementedAssert(
!paramTypes[i]->isDynamicallySized(),
"Accessors for mapping with dynamically-sized keys not yet implemented."
);
// pop offset // pop offset
m_context << Instruction::POP; m_context << Instruction::POP;
// move storage offset to memory. if (paramTypes[i]->isDynamicallySized())
utils().storeInMemory(32); {
// move key to memory. solAssert(
utils().copyToStackTop(paramTypes.size() - i, 1); dynamic_cast<ArrayType const&>(*paramTypes[i]).isByteArray(),
utils().storeInMemory(0); "Expected string or byte array for mapping key type"
m_context << u256(64) << u256(0) << Instruction::KECCAK256; );
// stack: <keys..> <slot position>
// 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 // push offset
m_context << u256(0); m_context << u256(0);
returnType = mappingType->valueType(); returnType = mappingType->valueType();
@ -286,8 +317,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type); ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array."); solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
m_context << max(u256(32u), arrayType.memorySize()); utils().allocateMemory(max(u256(32u), arrayType.memorySize()));
utils().allocateMemory();
m_context << Instruction::DUP1; m_context << Instruction::DUP1;
for (auto const& component: _tuple.components()) for (auto const& component: _tuple.components())
@ -418,7 +448,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
{ {
return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber; return dynamic_cast<Literal const*>(&_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) if (swap)
{ {
leftExpression.accept(*this); leftExpression.accept(*this);
@ -496,8 +526,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
m_context << max(u256(32u), structType.memorySize()); utils().allocateMemory(max(u256(32u), structType.memorySize()));
utils().allocateMemory();
m_context << Instruction::DUP1; m_context << Instruction::DUP1;
for (unsigned i = 0; i < arguments.size(); ++i) for (unsigned i = 0; i < arguments.size(); ++i)
@ -1310,8 +1339,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
utils().leftShiftNumberOnStack(224); utils().leftShiftNumberOnStack(224);
} }
else else
solAssert(!!_memberAccess.expression().annotation().type->memberType(member), solAssert(
"Invalid member access to function."); !!_memberAccess.expression().annotation().type->memberType(member),
"Invalid member access to function."
);
break; break;
case Type::Category::Magic: case Type::Category::Magic:
// we can ignore the kind of magic and only look at the name of the member // 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; m_context << Instruction::POP;
} }
else if (member == "name")
{
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*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 else
solAssert(false, "Unknown magic member."); solAssert(false, "Unknown magic member.");
break; 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. // 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 // Move arguments to memory, will not update the free memory pointer (but will update the memory
// pointer on the stack). // 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( utils().encodeToMemory(
argumentTypes, argumentTypes,
parameterTypes, parameterTypes,
_functionType.padArguments(), _functionType.padArguments(),
_functionType.takesArbitraryParameters() || _functionType.isBareCall(), encodeInPlace,
isDelegateCall encodeForLibraryCall
); );
// Stack now: // Stack now:
@ -2083,7 +2131,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
mstore(v, returndatasize()) mstore(v, returndatasize())
returndatacopy(add(v, 0x20), 0, returndatasize()) returndatacopy(add(v, 0x20), 0, returndatasize())
} }
})", {"v"}); })", {"v"});
} }
else else
utils().pushZeroPointer(); utils().pushZeroPointer();

View File

@ -55,11 +55,8 @@ class ArrayType;
class ExpressionCompiler: private ASTConstVisitor class ExpressionCompiler: private ASTConstVisitor
{ {
public: public:
/// Appends code for a State Variable accessor function explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimiseOrderLiterals):
static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false); m_optimiseOrderLiterals(_optimiseOrderLiterals), m_context(_compilerContext) {}
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
m_optimize(_optimize), m_context(_compilerContext) {}
/// Compile the given @a _expression and leave its value on the stack. /// Compile the given @a _expression and leave its value on the stack.
void compile(Expression const& _expression); void compile(Expression const& _expression);
@ -71,7 +68,7 @@ public:
void appendStateVariableAccessor(VariableDeclaration const& _varDecl); void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
/// Appends code for a Constant State Variable accessor function /// Appends code for a Constant State Variable accessor function
void appendConstStateVariableAccessor(const VariableDeclaration& _varDecl); void appendConstStateVariableAccessor(VariableDeclaration const& _varDecl);
private: private:
bool visit(Conditional const& _condition) override; bool visit(Conditional const& _condition) override;
@ -127,7 +124,7 @@ private:
/// @returns the CompilerUtils object containing the current context. /// @returns the CompilerUtils object containing the current context.
CompilerUtils utils(); CompilerUtils utils();
bool m_optimize; bool m_optimiseOrderLiterals;
CompilerContext& m_context; CompilerContext& m_context;
std::unique_ptr<LValue> m_currentLValue; std::unique_ptr<LValue> m_currentLValue;

View File

@ -473,7 +473,7 @@ void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeRefer
m_context << Instruction::SWAP1 << Instruction::SSTORE; 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()), LValue(_compilerContext, _arrayType.memberType("length").get()),
m_arrayType(_arrayType) m_arrayType(_arrayType)
{ {

View File

@ -21,7 +21,6 @@
#include <libsolidity/formal/VariableUsage.h> #include <libsolidity/formal/VariableUsage.h>
#include <libsolidity/formal/SymbolicTypes.h> #include <libsolidity/formal/SymbolicTypes.h>
#include <liblangutil/ErrorReporter.h>
#include <libdevcore/StringUtils.h> #include <libdevcore/StringUtils.h>
#include <boost/range/adaptor/map.hpp> #include <boost/range/adaptor/map.hpp>
@ -109,6 +108,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function)
m_expressions.clear(); m_expressions.clear();
m_globalContext.clear(); m_globalContext.clear();
m_uninterpretedTerms.clear(); m_uninterpretedTerms.clear();
m_overflowTargets.clear();
resetStateVariables(); resetStateVariables();
initializeLocalVariables(_function); initializeLocalVariables(_function);
m_loopExecutionHappened = false; m_loopExecutionHappened = false;
@ -126,7 +126,10 @@ void SMTChecker::endVisit(FunctionDefinition const&)
// Otherwise we remove any local variables from the context and // Otherwise we remove any local variables from the context and
// keep the state variables. // keep the state variables.
if (isRootFunction()) if (isRootFunction())
{
checkUnderOverflow();
removeLocalVariables(); removeLocalVariables();
}
m_functionPath.pop_back(); m_functionPath.pop_back();
} }
@ -316,21 +319,56 @@ void SMTChecker::endVisit(TupleExpression const& _tuple)
defineExpr(_tuple, expr(*_tuple.components()[0])); 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( m_overflowTargets.emplace_back(
_value < minValue(_type), _type,
_location, std::move(_intType),
"Underflow (resulting value less than " + formatNumberReadable(_type.minValue()) + ")", std::move(_value),
"<result>", currentPathConditions(),
&_value _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<IntegerType const*>(_target.intType.get());
checkCondition( checkCondition(
_value > maxValue(_type), _target.path && _target.value < minValue(*intType),
_location, _target.location,
"Overflow (resulting value larger than " + formatNumberReadable(_type.maxValue()) + ")", "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")",
"<result>", "<result>",
&_value &_target.value
);
}
void SMTChecker::checkOverflow(OverflowTarget& _target)
{
solAssert(_target.type != OverflowTarget::Type::Underflow, "");
auto intType = dynamic_cast<IntegerType const*>(_target.intType.get());
checkCondition(
_target.path && _target.value > maxValue(*intType),
_target.location,
"Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")",
"<result>",
&_target.value
); );
} }
@ -376,8 +414,13 @@ void SMTChecker::endVisit(UnaryOperation const& _op)
case Token::Sub: // - case Token::Sub: // -
{ {
defineExpr(_op, 0 - expr(_op.subExpression())); defineExpr(_op, 0 - expr(_op.subExpression()));
if (auto intType = dynamic_cast<IntegerType const*>(_op.annotation().type.get())) if (_op.annotation().type->category() == Type::Category::Integer)
checkUnderOverflow(expr(_op), *intType, _op.location()); addOverflowTarget(
OverflowTarget::Type::All,
_op.annotation().type,
expr(_op),
_op.location()
);
break; break;
} }
default: default:
@ -582,10 +625,8 @@ void SMTChecker::endVisit(Identifier const& _identifier)
{ {
// Will be translated as part of the node that requested the lvalue. // Will be translated as part of the node that requested the lvalue.
} }
else if (dynamic_cast<FunctionType const*>(_identifier.annotation().type.get())) else if (_identifier.annotation().type->category() == Type::Category::Function)
{
visitFunctionIdentifier(_identifier); visitFunctionIdentifier(_identifier);
}
else if (isSupportedType(_identifier.annotation().type->category())) else if (isSupportedType(_identifier.annotation().type->category()))
{ {
if (VariableDeclaration const* decl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) if (VariableDeclaration const* decl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
@ -654,6 +695,7 @@ void SMTChecker::visitFunctionIdentifier(Identifier const& _identifier)
void SMTChecker::endVisit(Literal const& _literal) void SMTChecker::endVisit(Literal const& _literal)
{ {
solAssert(_literal.annotation().type, "Expected type for AST node");
Type const& type = *_literal.annotation().type; Type const& type = *_literal.annotation().type;
if (isNumber(type.category())) if (isNumber(type.category()))
@ -853,9 +895,28 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op)
m_interface->addAssertion(right != 0); 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; break;
} }
default: 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) void SMTChecker::assignment(VariableDeclaration const& _variable, smt::Expression const& _value, SourceLocation const& _location)
{ {
TypePointer type = _variable.type(); TypePointer type = _variable.type();
if (auto const* intType = dynamic_cast<IntegerType const*>(type.get())) if (type->category() == Type::Category::Integer)
checkUnderOverflow(_value, *intType, _location); addOverflowTarget(OverflowTarget::Type::All, type, _value, _location);
else if (dynamic_cast<AddressType const*>(type.get())) else if (type->category() == Type::Category::Address)
checkUnderOverflow(_value, IntegerType(160), _location); addOverflowTarget(OverflowTarget::Type::All, make_shared<IntegerType>(160), _value, _location);
else if (dynamic_cast<MappingType const*>(type.get())) else if (type->category() == Type::Category::Mapping)
arrayAssignment(); arrayAssignment();
m_interface->addAssertion(newValue(_variable) == _value); 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) void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value)
{ {
createExpr(_e); 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); m_interface->addAssertion(expr(_e) == _value);
} }

View File

@ -137,9 +137,33 @@ private:
Expression const& _condition, Expression const& _condition,
std::string const& _description 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<IntegerType const*>(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<smt::CheckResult, std::vector<std::string>> std::pair<smt::CheckResult, std::vector<std::string>>
checkSatisfiableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate); checkSatisfiableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);
@ -244,6 +268,8 @@ private:
bool isRootFunction(); bool isRootFunction();
/// Returns true if _funDef was already visited. /// Returns true if _funDef was already visited.
bool visitedFunction(FunctionDefinition const* _funDef); bool visitedFunction(FunctionDefinition const* _funDef);
std::vector<OverflowTarget> m_overflowTargets;
}; };
} }

Some files were not shown because too many files have changed in this diff Show More