mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #3678 from ethereum/develop
Merge develop into release.
This commit is contained in:
commit
dfe3193c73
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.sol linguist-language=Solidity
|
14
.travis.yml
14
.travis.yml
@ -39,7 +39,6 @@ env:
|
|||||||
global:
|
global:
|
||||||
- ENCRYPTION_LABEL="6d4541b72666"
|
- ENCRYPTION_LABEL="6d4541b72666"
|
||||||
- SOLC_BUILD_TYPE=RelWithDebInfo
|
- SOLC_BUILD_TYPE=RelWithDebInfo
|
||||||
- SOLC_DOCS=Off
|
|
||||||
- SOLC_EMSCRIPTEN=Off
|
- SOLC_EMSCRIPTEN=Off
|
||||||
- SOLC_INSTALL_DEPS_TRAVIS=On
|
- SOLC_INSTALL_DEPS_TRAVIS=On
|
||||||
- SOLC_RELEASE=On
|
- SOLC_RELEASE=On
|
||||||
@ -65,18 +64,6 @@ matrix:
|
|||||||
- ZIP_SUFFIX=ubuntu-trusty-clang
|
- ZIP_SUFFIX=ubuntu-trusty-clang
|
||||||
- SOLC_STOREBYTECODE=On
|
- SOLC_STOREBYTECODE=On
|
||||||
|
|
||||||
# Documentation target, which generates documentation using Phoenix / ReadTheDocs.
|
|
||||||
- os: linux
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
compiler: gcc
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get -y install python-sphinx
|
|
||||||
env:
|
|
||||||
- SOLC_DOCS=On
|
|
||||||
- SOLC_RELEASE=Off
|
|
||||||
- SOLC_TESTS=Off
|
|
||||||
|
|
||||||
# Docker target, which generates a statically linked alpine image
|
# Docker target, which generates a statically linked alpine image
|
||||||
- os: linux
|
- os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
@ -184,7 +171,6 @@ before_script:
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
- test $SOLC_EMSCRIPTEN != On || (scripts/test_emscripten.sh)
|
- test $SOLC_EMSCRIPTEN != On || (scripts/test_emscripten.sh)
|
||||||
- test $SOLC_DOCS != On || (scripts/docs.sh)
|
|
||||||
- test $SOLC_TESTS != On || (cd $TRAVIS_BUILD_DIR && scripts/tests.sh)
|
- test $SOLC_TESTS != On || (cd $TRAVIS_BUILD_DIR && scripts/tests.sh)
|
||||||
- test $SOLC_STOREBYTECODE != On || (cd $TRAVIS_BUILD_DIR && scripts/bytecodecompare/storebytecode.sh)
|
- test $SOLC_STOREBYTECODE != On || (cd $TRAVIS_BUILD_DIR && scripts/bytecodecompare/storebytecode.sh)
|
||||||
|
|
||||||
|
@ -8,7 +8,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.4.20")
|
set(PROJECT_VERSION "0.4.21")
|
||||||
project(solidity VERSION ${PROJECT_VERSION})
|
project(solidity VERSION ${PROJECT_VERSION})
|
||||||
|
|
||||||
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
|
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
|
||||||
|
31
Changelog.md
31
Changelog.md
@ -1,3 +1,34 @@
|
|||||||
|
### 0.4.21 (2018-03-07)
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
|
||||||
|
* Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead).
|
||||||
|
* Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature.
|
||||||
|
* General: C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
|
||||||
|
* General: Improved messaging when error spans multiple lines of a sourcefile
|
||||||
|
* General: Support and recommend using ``emit EventName();`` to call events explicitly.
|
||||||
|
* Inline Assembly: Enforce strict mode as experimental 0.5.0 feature.
|
||||||
|
* Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default).
|
||||||
|
* Standard JSON: Reject badly formatted invalid JSON inputs.
|
||||||
|
* Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature.
|
||||||
|
* Syntax Analyser: Do not warn about experimental features if they do not concern code generation.
|
||||||
|
* Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` and do not set the experimental flag in the bytecode for this.
|
||||||
|
* Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature.
|
||||||
|
* Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature.
|
||||||
|
* Syntax Checker: Issue warning when using overloads of ``address`` on contract instances.
|
||||||
|
* Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Assembly: Raise error on oversized number literals in assembly.
|
||||||
|
* JSON-AST: Add "documentation" property to function, event and modifier definition.
|
||||||
|
* Resolver: Properly determine shadowing for imports with aliases.
|
||||||
|
* Standalone Assembly: Do not ignore input after closing brace of top level block.
|
||||||
|
* Standard JSON: Catch errors properly when invalid "sources" are passed.
|
||||||
|
* Standard JSON: Ensure that library addresses supplied are of correct length and hex prefixed.
|
||||||
|
* Type Checker: Properly detect which array and struct types are unsupported by the old ABI encoder.
|
||||||
|
* Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly.
|
||||||
|
* Commandline interface: throw error if option is unknown
|
||||||
|
|
||||||
### 0.4.20 (2018-02-14)
|
### 0.4.20 (2018-02-14)
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
97
circle.yml
97
circle.yml
@ -5,10 +5,6 @@ jobs:
|
|||||||
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
|
||||||
name: Init submodules
|
|
||||||
command: |
|
|
||||||
git submodule update --init
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
name: Restore Boost build
|
name: Restore Boost build
|
||||||
key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }}
|
key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }}
|
||||||
@ -83,7 +79,7 @@ jobs:
|
|||||||
name: External tests
|
name: External tests
|
||||||
command: |
|
command: |
|
||||||
. /usr/local/nvm/nvm.sh
|
. /usr/local/nvm/nvm.sh
|
||||||
test/externalTests.sh /tmp/workspace/soljson.js
|
test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js
|
||||||
build_x86:
|
build_x86:
|
||||||
docker:
|
docker:
|
||||||
- image: buildpack-deps:artful
|
- image: buildpack-deps:artful
|
||||||
@ -94,41 +90,84 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
apt-get -qq update
|
apt-get -qq update
|
||||||
apt-get -qy install ccache cmake libboost-all-dev libz3-dev
|
apt-get -qy install ccache cmake libboost-all-dev libz3-dev
|
||||||
- run:
|
|
||||||
name: Init submodules
|
|
||||||
command: |
|
|
||||||
git submodule update --init
|
|
||||||
- run:
|
- run:
|
||||||
name: Store commit hash and prerelease
|
name: Store commit hash and prerelease
|
||||||
command: |
|
command: |
|
||||||
date -u +"nightly.%Y.%-m.%-d" > prerelease.txt
|
if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
|
||||||
echo -n "$CIRCLE_SHA1" > commit_hash.txt
|
echo -n "$CIRCLE_SHA1" > commit_hash.txt
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: ccache-{{ arch }}-{{ .Branch }}
|
key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
key: ccache-{{ arch }}
|
key: ccache-{{ arch }}-{{ .Branch }}-
|
||||||
key: ccache
|
key: ccache-{{ arch }}-develop-
|
||||||
|
key: ccache-{{ arch }}-
|
||||||
|
- run:
|
||||||
|
name: Configure ccache
|
||||||
|
command: ccache -M 200M && ccache -c && ccache -s && ccache -z
|
||||||
- run:
|
- run:
|
||||||
name: Build
|
name: Build
|
||||||
command: ./scripts/build.sh RelWithDebInfo
|
command: |
|
||||||
|
mkdir -p build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
|
make -j4
|
||||||
|
- run:
|
||||||
|
name: CCache statistics
|
||||||
|
command: ccache -s
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: ccache-{{ arch }}-{{ .Branch }}
|
key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
when: always
|
||||||
paths:
|
paths:
|
||||||
- ~/.ccache
|
- ~/.ccache
|
||||||
- run:
|
|
||||||
name: Commandline tests
|
|
||||||
command: test/cmdlineTests.sh
|
|
||||||
- run: mkdir -p test_results
|
|
||||||
- run:
|
|
||||||
name: Test without optimizer (exclude IPC tests)
|
|
||||||
command: build/test/soltest --logger=JUNIT,test_suite,test_results/no_opt.xml -- --no-ipc
|
|
||||||
- run:
|
|
||||||
name: Test with optimizer (exclude IPC tests)
|
|
||||||
command: build/test/soltest --logger=JUNIT,test_suite,test_results/opt.xml -- --optimize --no-ipc
|
|
||||||
- store_test_results:
|
|
||||||
path: test_results/
|
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: build/solc/solc
|
path: build/solc/solc
|
||||||
destination: solc
|
destination: solc
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: build
|
||||||
|
paths:
|
||||||
|
- solc/solc
|
||||||
|
- test/soltest
|
||||||
|
- test/solfuzzer
|
||||||
|
|
||||||
|
test_x86:
|
||||||
|
docker:
|
||||||
|
- image: buildpack-deps:artful
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: build
|
||||||
|
- run:
|
||||||
|
name: Install dependencies
|
||||||
|
command: |
|
||||||
|
apt-get -qq update
|
||||||
|
apt-get -qy install libz3-dev libleveldb1v5
|
||||||
|
- run: mkdir -p test_results
|
||||||
|
- run:
|
||||||
|
name: Tests
|
||||||
|
command: scripts/tests.sh --junit_report test_results
|
||||||
|
- store_test_results:
|
||||||
|
path: test_results/
|
||||||
|
|
||||||
|
docs:
|
||||||
|
docker:
|
||||||
|
- image: buildpack-deps:artful
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Install build dependencies
|
||||||
|
command: |
|
||||||
|
apt-get -qq update
|
||||||
|
apt-get -qy install python-sphinx
|
||||||
|
- run:
|
||||||
|
name: Store commit hash and prerelease
|
||||||
|
command: |
|
||||||
|
if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
|
||||||
|
echo -n "$CIRCLE_SHA1" > commit_hash.txt
|
||||||
|
- run:
|
||||||
|
name: Build documentation
|
||||||
|
command: ./scripts/docs.sh
|
||||||
|
- store_artifacts:
|
||||||
|
path: docs/_build/html/
|
||||||
|
destination: docs-html
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
@ -142,3 +181,7 @@ workflows:
|
|||||||
requires:
|
requires:
|
||||||
- build_emscripten
|
- build_emscripten
|
||||||
- build_x86
|
- build_x86
|
||||||
|
- test_x86:
|
||||||
|
requires:
|
||||||
|
- build_x86
|
||||||
|
- docs
|
||||||
|
@ -140,6 +140,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1")
|
||||||
# Disable greedy exception catcher
|
# Disable greedy exception catcher
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
|
||||||
|
# Abort if linking results in any undefined symbols
|
||||||
|
# Note: this is on by default in the CMake Emscripten module which we aren't using
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
|
||||||
add_definitions(-DETH_EMSCRIPTEN=1)
|
add_definitions(-DETH_EMSCRIPTEN=1)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
@ -26,6 +26,10 @@ The first four bytes of the call data for a function call specifies the function
|
|||||||
first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype, i.e.
|
first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype, i.e.
|
||||||
the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used.
|
the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The return type of a function is not part of this signature. In :ref:`Solidity's function overloading <overload-function>` return types are not considered. The reason is to keep function call resolution context-independent.
|
||||||
|
The JSON description of the ABI however contains both inputs and outputs. See (the :ref:`JSON ABI <abi_json>`)
|
||||||
|
|
||||||
Argument Encoding
|
Argument Encoding
|
||||||
=================
|
=================
|
||||||
|
|
||||||
@ -155,15 +159,15 @@ on the type of ``X`` being
|
|||||||
|
|
||||||
``enc(X) = enc(enc_utf8(X))``, i.e. ``X`` is utf-8 encoded and this value is interpreted as of ``bytes`` type and encoded further. Note that the length used in this subsequent encoding is the number of bytes of the utf-8 encoded string, not its number of characters.
|
``enc(X) = enc(enc_utf8(X))``, i.e. ``X`` is utf-8 encoded and this value is interpreted as of ``bytes`` type and encoded further. Note that the length used in this subsequent encoding is the number of bytes of the utf-8 encoded string, not its number of characters.
|
||||||
|
|
||||||
- ``uint<M>``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is a multiple of 32 bytes.
|
- ``uint<M>``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is 32 bytes.
|
||||||
- ``address``: as in the ``uint160`` case
|
- ``address``: as in the ``uint160`` case
|
||||||
- ``int<M>``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-oder (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is a multiple of 32 bytes.
|
- ``int<M>``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is 32 bytes.
|
||||||
- ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false``
|
- ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false``
|
||||||
- ``fixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``.
|
- ``fixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``.
|
||||||
- ``fixed``: as in the ``fixed128x19`` case
|
- ``fixed``: as in the ``fixed128x19`` case
|
||||||
- ``ufixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``.
|
- ``ufixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``.
|
||||||
- ``ufixed``: as in the ``ufixed128x19`` case
|
- ``ufixed``: as in the ``ufixed128x19`` case
|
||||||
- ``bytes<M>``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes to a length of 32.
|
- ``bytes<M>``: ``enc(X)`` is the sequence of bytes in ``X`` padded with trailing zero-bytes to a length of 32 bytes.
|
||||||
|
|
||||||
Note that for any ``X``, ``len(enc(X))`` is a multiple of 32.
|
Note that for any ``X``, ``len(enc(X))`` is a multiple of 32.
|
||||||
|
|
||||||
@ -290,6 +294,8 @@ In effect, a log entry using this ABI is described as:
|
|||||||
|
|
||||||
For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value.
|
For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value.
|
||||||
|
|
||||||
|
.. _abi_json:
|
||||||
|
|
||||||
JSON
|
JSON
|
||||||
====
|
====
|
||||||
|
|
||||||
|
@ -153,6 +153,8 @@ If an opcode takes arguments (always from the top of the stack), they are given
|
|||||||
Note that the order of arguments can be seen to be reversed in non-functional style (explained below).
|
Note that the order of arguments can be seen to be reversed in non-functional style (explained below).
|
||||||
Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are
|
Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are
|
||||||
special and all others push exactly one item onto the stack.
|
special and all others push exactly one item onto the stack.
|
||||||
|
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
|
||||||
(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
|
(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
|
||||||
@ -161,165 +163,173 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly.
|
|||||||
|
|
||||||
In the grammar, opcodes are represented as pre-defined identifiers.
|
In the grammar, opcodes are represented as pre-defined identifiers.
|
||||||
|
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| stop + `-` | stop execution, identical to return(0,0) |
|
| Instruction | | | Explanation |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+=========================+=====+===+=================================================================+
|
||||||
| add(x, y) | | x + y |
|
| stop + `-` | F | stop execution, identical to return(0,0) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| sub(x, y) | | x - y |
|
| add(x, y) | | F | x + y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| mul(x, y) | | x * y |
|
| sub(x, y) | | F | x - y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| div(x, y) | | x / y |
|
| mul(x, y) | | F | x * y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| sdiv(x, y) | | x / y, for signed numbers in two's complement |
|
| div(x, y) | | F | x / y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| mod(x, y) | | x % y |
|
| sdiv(x, y) | | F | x / y, for signed numbers in two's complement |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| smod(x, y) | | x % y, for signed numbers in two's complement |
|
| mod(x, y) | | F | x % y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| exp(x, y) | | x to the power of y |
|
| smod(x, y) | | F | x % y, for signed numbers in two's complement |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| not(x) | | ~x, every bit of x is negated |
|
| exp(x, y) | | F | x to the power of y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| lt(x, y) | | 1 if x < y, 0 otherwise |
|
| not(x) | | F | ~x, every bit of x is negated |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| gt(x, y) | | 1 if x > y, 0 otherwise |
|
| lt(x, y) | | F | 1 if x < y, 0 otherwise |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| slt(x, y) | | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
|
| gt(x, y) | | F | 1 if x > y, 0 otherwise |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| sgt(x, y) | | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
|
| slt(x, y) | | F | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| eq(x, y) | | 1 if x == y, 0 otherwise |
|
| sgt(x, y) | | F | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| iszero(x) | | 1 if x == 0, 0 otherwise |
|
| eq(x, y) | | F | 1 if x == y, 0 otherwise |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| and(x, y) | | bitwise and of x and y |
|
| iszero(x) | | F | 1 if x == 0, 0 otherwise |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| or(x, y) | | bitwise or of x and y |
|
| and(x, y) | | F | bitwise and of x and y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| xor(x, y) | | bitwise xor of x and y |
|
| or(x, y) | | F | bitwise or of x and y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| byte(n, x) | | nth byte of x, where the most significant byte is the 0th byte |
|
| xor(x, y) | | F | bitwise xor of x and y |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| addmod(x, y, m) | | (x + y) % m with arbitrary precision arithmetics |
|
| byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| mulmod(x, y, m) | | (x * y) % m with arbitrary precision arithmetics |
|
| shl(x, y) | | C | logical shift left y by x bits |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| signextend(i, x) | | sign extend from (i*8+7)th bit counting from least significant |
|
| shr(x, y) | | C | logical shift right y by x bits |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| keccak256(p, n) | | keccak(mem[p...(p+n))) |
|
| sar(x, y) | | C | arithmetic shift right y by x bits |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| sha3(p, n) | | keccak(mem[p...(p+n))) |
|
| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| jump(label) | `-` | jump to label / code position |
|
| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| jumpi(label, cond) | `-` | jump to label if cond is nonzero |
|
| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| pc | | current position in code |
|
| keccak256(p, n) | | F | keccak(mem[p...(p+n))) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| pop(x) | `-` | remove the element pushed by x |
|
| sha3(p, n) | | F | keccak(mem[p...(p+n))) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) |
|
| jump(label) | `-` | F | jump to label / code position |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| swap1 ... swap16 | `*` | swap topmost and ith stack slot below it |
|
| jumpi(label, cond) | `-` | F | jump to label if cond is nonzero |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| mload(p) | | mem[p..(p+32)) |
|
| pc | | F | current position in code |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| mstore(p, v) | `-` | mem[p..(p+32)) := v |
|
| pop(x) | `-` | F | remove the element pushed by x |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| mstore8(p, v) | `-` | mem[p] := v & 0xff - only modifies a single byte |
|
| dup1 ... dup16 | | F | copy ith stack slot to the top (counting from top) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| sload(p) | | storage[p] |
|
| swap1 ... swap16 | `*` | F | swap topmost and ith stack slot below it |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| sstore(p, v) | `-` | storage[p] := v |
|
| mload(p) | | F | mem[p..(p+32)) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| msize | | size of memory, i.e. largest accessed memory index |
|
| mstore(p, v) | `-` | F | mem[p..(p+32)) := v |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| gas | | gas still available to execution |
|
| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| address | | address of the current contract / execution context |
|
| sload(p) | | F | storage[p] |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| balance(a) | | wei balance at address a |
|
| sstore(p, v) | `-` | F | storage[p] := v |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| caller | | call sender (excluding delegatecall) |
|
| msize | | F | size of memory, i.e. largest accessed memory index |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| callvalue | | wei sent together with the current call |
|
| gas | | F | gas still available to execution |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| calldataload(p) | | call data starting from position p (32 bytes) |
|
| address | | F | address of the current contract / execution context |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| calldatasize | | size of call data in bytes |
|
| balance(a) | | F | wei balance at address a |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t |
|
| caller | | F | call sender (excluding ``delegatecall``) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| codesize | | size of the code of the current contract / execution context |
|
| callvalue | | F | wei sent together with the current call |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| codecopy(t, f, s) | `-` | copy s bytes from code at position f to mem at position t |
|
| calldataload(p) | | F | call data starting from position p (32 bytes) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| extcodesize(a) | | size of the code at address a |
|
| calldatasize | | F | size of call data in bytes |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a |
|
| calldatacopy(t, f, s) | `-` | F | copy s bytes from calldata at position f to mem at position t |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| returndatasize | | size of the last returndata |
|
| codesize | | F | size of the code of the current contract / execution context |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| returndatacopy(t, f, s) | `-` | copy s bytes from returndata at position f to mem at position t |
|
| codecopy(t, f, s) | `-` | F | copy s bytes from code at position f to mem at position t |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei |
|
| extcodesize(a) | | F | size of the code at address a |
|
||||||
| | | and return the new address |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| extcodecopy(a, t, f, s) | `-` | F | like codecopy(t, f, s) but take code at address a |
|
||||||
| create2(v, n, p, s) | | create new contract with code mem[p..(p+s)) at address |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v |
|
| returndatasize | | B | size of the last returndata |
|
||||||
| | | wei and return the new address |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t |
|
||||||
| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| insize, out, outsize) | | providing g gas and v wei and output area |
|
| create(v, p, s) | | F | create new contract with code mem[p..(p+s)) and send v wei |
|
||||||
| | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
|
| | | | and return the new address |
|
||||||
| | | and 1 on success |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| create2(v, n, p, s) | | C | create new contract with code mem[p..(p+s)) at address |
|
||||||
| callcode(g, a, v, in, | | identical to `call` but only use the code from a and stay |
|
| | | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v |
|
||||||
| insize, out, outsize) | | in the context of the current contract otherwise |
|
| | | | wei and return the new address |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` |
|
| call(g, a, v, in, | | F | call contract at address a with input mem[in..(in+insize)) |
|
||||||
| insize, out, outsize) | | and ``callvalue`` |
|
| insize, out, outsize) | | | providing g gas and v wei and output area |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
|
||||||
| staticcall(g, a, in, | | identical to `call(g, a, 0, in, insize, out, outsize)` but do |
|
| | | | and 1 on success |
|
||||||
| insize, out, outsize) | | not allow state modifications |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay |
|
||||||
| return(p, s) | `-` | end execution, return data mem[p..(p+s)) |
|
| insize, out, outsize) | | | in the context of the current contract otherwise |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| revert(p, s) | `-` | end execution, revert state changes, return data mem[p..(p+s)) |
|
| delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| insize, out, outsize) | | | and ``callvalue`` |
|
||||||
| selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do |
|
||||||
| invalid | `-` | end execution with invalid instruction |
|
| insize, out, outsize) | | | not allow state modifications |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) |
|
| return(p, s) | `-` | F | end execution, return data mem[p..(p+s)) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) |
|
| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p..(p+s)) |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| log2(p, s, t1, t2) | `-` | log with topics t1, t2 and data mem[p..(p+s)) |
|
| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| log3(p, s, t1, t2, t3) | `-` | log with topics t1, t2, t3 and data mem[p..(p+s)) |
|
| invalid | `-` | F | end execution with invalid instruction |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| log4(p, s, t1, t2, t3, | `-` | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
|
| log0(p, s) | `-` | F | log without topics and data mem[p..(p+s)) |
|
||||||
| t4) | | |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p..(p+s)) |
|
||||||
| origin | | transaction sender |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p..(p+s)) |
|
||||||
| gasprice | | gas price of the transaction |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p..(p+s)) |
|
||||||
| blockhash(b) | | hash of block nr b - only for last 256 blocks excluding current |
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
|
||||||
| coinbase | | current mining beneficiary |
|
| t4) | | | |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| timestamp | | timestamp of the current block in seconds since the epoch |
|
| origin | | F | transaction sender |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| number | | current block number |
|
| gasprice | | F | gas price of the transaction |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| difficulty | | difficulty of the current block |
|
| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| gaslimit | | block gas limit of the current block |
|
| coinbase | | F | current mining beneficiary |
|
||||||
+-------------------------+------+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
|
| timestamp | | F | timestamp of the current block in seconds since the epoch |
|
||||||
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
|
| number | | F | current block number |
|
||||||
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
|
| difficulty | | F | difficulty of the current block |
|
||||||
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
|
| gaslimit | | F | block gas limit of the current block |
|
||||||
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
|
|
||||||
Literals
|
Literals
|
||||||
--------
|
--------
|
||||||
@ -376,8 +386,8 @@ Functions external to inline assembly can also be accessed: The assembly will
|
|||||||
push their entry label (with virtual function resolution applied). The calling semantics
|
push their entry label (with virtual function resolution applied). The calling semantics
|
||||||
in solidity are:
|
in solidity are:
|
||||||
|
|
||||||
- the caller pushes return label, arg1, arg2, ..., argn
|
- the caller pushes ``return label``, ``arg1``, ``arg2``, ..., ``argn``
|
||||||
- the call returns with ret1, ret2, ..., retm
|
- the call returns with ``ret1``, ``ret2``, ..., ``retm``
|
||||||
|
|
||||||
This feature is still a bit cumbersome to use, because the stack offset essentially
|
This feature is still a bit cumbersome to use, because the stack offset essentially
|
||||||
changes during the call, and thus references to local variables will be wrong.
|
changes during the call, and thus references to local variables will be wrong.
|
||||||
@ -727,7 +737,7 @@ The following assembly will be generated::
|
|||||||
// function dispatcher
|
// function dispatcher
|
||||||
switch div(calldataload(0), exp(2, 226))
|
switch div(calldataload(0), exp(2, 226))
|
||||||
case 0xb3de648b {
|
case 0xb3de648b {
|
||||||
let (r) = f(calldataload(4))
|
let r := f(calldataload(4))
|
||||||
let ret := $allocate(0x20)
|
let ret := $allocate(0x20)
|
||||||
mstore(ret, r)
|
mstore(ret, r)
|
||||||
return(ret, 0x20)
|
return(ret, 0x20)
|
||||||
@ -861,38 +871,37 @@ Grammar::
|
|||||||
AssemblyItem =
|
AssemblyItem =
|
||||||
Identifier |
|
Identifier |
|
||||||
AssemblyBlock |
|
AssemblyBlock |
|
||||||
FunctionalAssemblyExpression |
|
AssemblyExpression |
|
||||||
AssemblyLocalDefinition |
|
AssemblyLocalDefinition |
|
||||||
FunctionalAssemblyAssignment |
|
|
||||||
AssemblyAssignment |
|
AssemblyAssignment |
|
||||||
|
AssemblyStackAssignment |
|
||||||
LabelDefinition |
|
LabelDefinition |
|
||||||
AssemblyIf |
|
AssemblyIf |
|
||||||
AssemblySwitch |
|
AssemblySwitch |
|
||||||
AssemblyFunctionDefinition |
|
AssemblyFunctionDefinition |
|
||||||
AssemblyFor |
|
AssemblyFor |
|
||||||
'break' | 'continue' |
|
'break' |
|
||||||
SubAssembly | 'dataSize' '(' Identifier ')' |
|
'continue' |
|
||||||
LinkerSymbol |
|
SubAssembly
|
||||||
'errorLabel' | 'bytecodeSize' |
|
AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral
|
||||||
NumberLiteral | StringLiteral | HexLiteral
|
AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral
|
||||||
Identifier = [a-zA-Z_$] [a-zA-Z_0-9]*
|
Identifier = [a-zA-Z_$] [a-zA-Z_0-9]*
|
||||||
FunctionalAssemblyExpression = Identifier '(' ( AssemblyItem ( ',' AssemblyItem )* )? ')'
|
AssemblyCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')'
|
||||||
AssemblyLocalDefinition = 'let' IdentifierOrList ':=' FunctionalAssemblyExpression
|
AssemblyLocalDefinition = 'let' IdentifierOrList ( ':=' AssemblyExpression )?
|
||||||
FunctionalAssemblyAssignment = IdentifierOrList ':=' FunctionalAssemblyExpression
|
AssemblyAssignment = IdentifierOrList ':=' AssemblyExpression
|
||||||
IdentifierOrList = Identifier | '(' IdentifierList ')'
|
IdentifierOrList = Identifier | '(' IdentifierList ')'
|
||||||
IdentifierList = Identifier ( ',' Identifier)*
|
IdentifierList = Identifier ( ',' Identifier)*
|
||||||
AssemblyAssignment = '=:' Identifier
|
AssemblyStackAssignment = '=:' Identifier
|
||||||
LabelDefinition = Identifier ':'
|
LabelDefinition = Identifier ':'
|
||||||
AssemblyIf = 'if' FunctionalAssemblyExpression AssemblyBlock
|
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
|
||||||
AssemblySwitch = 'switch' FunctionalAssemblyExpression AssemblyCase*
|
AssemblySwitch = 'switch' AssemblyExpression AssemblyCase*
|
||||||
( 'default' AssemblyBlock )?
|
( 'default' AssemblyBlock )?
|
||||||
AssemblyCase = 'case' FunctionalAssemblyExpression AssemblyBlock
|
AssemblyCase = 'case' AssemblyExpression AssemblyBlock
|
||||||
AssemblyFunctionDefinition = 'function' Identifier '(' IdentifierList? ')'
|
AssemblyFunctionDefinition = 'function' Identifier '(' IdentifierList? ')'
|
||||||
( '->' '(' IdentifierList ')' )? AssemblyBlock
|
( '->' '(' IdentifierList ')' )? AssemblyBlock
|
||||||
AssemblyFor = 'for' ( AssemblyBlock | FunctionalAssemblyExpression)
|
AssemblyFor = 'for' ( AssemblyBlock | AssemblyExpression )
|
||||||
FunctionalAssemblyExpression ( AssemblyBlock | FunctionalAssemblyExpression) AssemblyBlock
|
AssemblyExpression ( AssemblyBlock | AssemblyExpression ) AssemblyBlock
|
||||||
SubAssembly = 'assembly' Identifier AssemblyBlock
|
SubAssembly = 'assembly' Identifier AssemblyBlock
|
||||||
LinkerSymbol = 'linkerSymbol' '(' StringLiteral ')'
|
|
||||||
NumberLiteral = HexNumber | DecimalNumber
|
NumberLiteral = HexNumber | DecimalNumber
|
||||||
HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
|
HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
|
||||||
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
|
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
|
||||||
|
@ -418,6 +418,10 @@
|
|||||||
"bugs": [],
|
"bugs": [],
|
||||||
"released": "2018-02-14"
|
"released": "2018-02-14"
|
||||||
},
|
},
|
||||||
|
"0.4.21": {
|
||||||
|
"bugs": [],
|
||||||
|
"released": "2018-03-07"
|
||||||
|
},
|
||||||
"0.4.3": {
|
"0.4.3": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
"ZeroFunctionSelector",
|
"ZeroFunctionSelector",
|
||||||
|
@ -402,7 +402,7 @@ State variables can be declared as ``constant``. In this case, they have to be
|
|||||||
assigned from an expression which is a constant at compile time. Any expression
|
assigned from an expression which is a constant at compile time. Any expression
|
||||||
that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or
|
that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or
|
||||||
``block.number``) or
|
``block.number``) or
|
||||||
execution data (``msg.gas``) or make calls to external contracts are disallowed. Expressions
|
execution data (``msg.value`` or ``gasleft()``) or make calls to external contracts are disallowed. Expressions
|
||||||
that might have a side-effect on memory allocation are allowed, but those that
|
that might have a side-effect on memory allocation are allowed, but those that
|
||||||
might have a side-effect on other memory objects are not. The built-in functions
|
might have a side-effect on other memory objects are not. The built-in functions
|
||||||
``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod``
|
``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod``
|
||||||
@ -467,13 +467,20 @@ The following statements are considered modifying the state:
|
|||||||
}
|
}
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
``constant`` is an alias to ``view``.
|
``constant`` on functions is an alias to ``view``, but this is deprecated and is planned to be dropped in version 0.5.0.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Getter methods are marked ``view``.
|
Getter methods are marked ``view``.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If invalid explicit type conversions are used, state modifications are possible
|
||||||
|
even though a ``view`` function was called.
|
||||||
|
You can switch the compiler to use ``STATICCALL`` when calling such functions and thus
|
||||||
|
prevent modifications to the state on the level of the EVM by adding
|
||||||
|
``pragma experimental "v0.5.0";``
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
The compiler does not enforce yet that a ``view`` method is not modifying state.
|
The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though.
|
||||||
|
|
||||||
.. index:: ! pure function, function;pure
|
.. index:: ! pure function, function;pure
|
||||||
|
|
||||||
@ -502,8 +509,20 @@ In addition to the list of state modifying statements explained above, the follo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If invalid explicit type conversions are used, state modifications are possible
|
||||||
|
even though a ``pure`` function was called.
|
||||||
|
You can switch the compiler to use ``STATICCALL`` when calling such functions and thus
|
||||||
|
prevent modifications to the state on the level of the EVM by adding
|
||||||
|
``pragma experimental "v0.5.0";``
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
The compiler does not enforce yet that a ``pure`` method is not reading from the state.
|
It is not possible to prevent functions from reading the state at the level
|
||||||
|
of the EVM, it is only possible to prevent them from writing to the state
|
||||||
|
(i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not).
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state.
|
||||||
|
|
||||||
.. index:: ! fallback function, function;fallback
|
.. index:: ! fallback function, function;fallback
|
||||||
|
|
||||||
@ -523,16 +542,14 @@ Ether (without data). Additionally, in order to receive Ether, the fallback func
|
|||||||
must be marked ``payable``. If no such function exists, the contract cannot receive
|
must be marked ``payable``. If no such function exists, the contract cannot receive
|
||||||
Ether through regular transactions.
|
Ether through regular transactions.
|
||||||
|
|
||||||
In such a context, there is usually very little gas available to the function call (to be precise, 2300 gas), so it is important to make fallback functions as cheap as possible. Note that the gas required by a transaction (as opposed to an internal call) that invokes the fallback function is much higher, because each transaction charges an additional amount of 21000 gas or more for things like signature checking.
|
In the worst case, the fallback function can only rely on 2300 gas being available (for example when send or transfer is used), leaving not much room to perform other operations except basic logging. The following operations will consume more gas than the 2300 gas stipend:
|
||||||
|
|
||||||
In particular, the following operations will consume more gas than the stipend provided to a fallback function:
|
|
||||||
|
|
||||||
- Writing to storage
|
- Writing to storage
|
||||||
- Creating a contract
|
- Creating a contract
|
||||||
- Calling an external function which consumes a large amount of gas
|
- Calling an external function which consumes a large amount of gas
|
||||||
- Sending Ether
|
- Sending Ether
|
||||||
|
|
||||||
Please ensure you test your fallback function thoroughly to ensure the execution cost is less than 2300 gas before deploying a contract.
|
Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve
|
Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve
|
||||||
@ -724,10 +741,12 @@ All non-indexed arguments will be stored in the data part of the log.
|
|||||||
);
|
);
|
||||||
|
|
||||||
function deposit(bytes32 _id) public payable {
|
function deposit(bytes32 _id) public payable {
|
||||||
// Any call to this function (even deeply nested) can
|
// Events are emitted using `emit`, followed by
|
||||||
// be detected from the JavaScript API by filtering
|
// the name of the event and the arguments
|
||||||
// for `Deposit` to be called.
|
// (if any) in parentheses. Any such invocation
|
||||||
Deposit(msg.sender, _id, msg.value);
|
// (even deeply nested) can be detected from
|
||||||
|
// the JavaScript API by filtering for `Deposit`.
|
||||||
|
emit Deposit(msg.sender, _id, msg.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -953,6 +972,31 @@ not known in the context of the class where it is used,
|
|||||||
although its type is known. This is similar for ordinary
|
although its type is known. This is similar for ordinary
|
||||||
virtual method lookup.
|
virtual method lookup.
|
||||||
|
|
||||||
|
.. index:: ! constructor
|
||||||
|
|
||||||
|
Constructors
|
||||||
|
============
|
||||||
|
A constructor is an optional function with the same name as the contract which is executed upon contract creation.
|
||||||
|
Constructor functions can be either ``public`` or ``internal``.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pragma solidity ^0.4.11;
|
||||||
|
|
||||||
|
contract A {
|
||||||
|
uint public a;
|
||||||
|
|
||||||
|
function A(uint _a) internal {
|
||||||
|
a = _a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract B is A(1) {
|
||||||
|
function B() public {}
|
||||||
|
}
|
||||||
|
|
||||||
|
A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`.
|
||||||
|
|
||||||
.. index:: ! base;constructor
|
.. index:: ! base;constructor
|
||||||
|
|
||||||
Arguments for Base Constructors
|
Arguments for Base Constructors
|
||||||
@ -1025,11 +1069,13 @@ As an exception, a state variable getter can override a public function.
|
|||||||
|
|
||||||
.. index:: ! contract;abstract, ! abstract contract
|
.. index:: ! contract;abstract, ! abstract contract
|
||||||
|
|
||||||
|
.. _abstract-contract:
|
||||||
|
|
||||||
******************
|
******************
|
||||||
Abstract Contracts
|
Abstract Contracts
|
||||||
******************
|
******************
|
||||||
|
|
||||||
Contract functions can lack an implementation as in the following example (note that the function declaration header is terminated by ``;``)::
|
Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``)::
|
||||||
|
|
||||||
pragma solidity ^0.4.0;
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
@ -1037,9 +1083,7 @@ Contract functions can lack an implementation as in the following example (note
|
|||||||
function utterance() public returns (bytes32);
|
function utterance() public returns (bytes32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Such contracts cannot be compiled (even if they contain
|
Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts::
|
||||||
implemented functions alongside non-implemented functions),
|
|
||||||
but they can be used as base contracts::
|
|
||||||
|
|
||||||
pragma solidity ^0.4.0;
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
@ -1053,6 +1097,19 @@ but they can be used as base contracts::
|
|||||||
|
|
||||||
If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract.
|
If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract.
|
||||||
|
|
||||||
|
Note that a function without implementation is different from a :ref:`Function Type <function_types>` even though their syntax looks very similar.
|
||||||
|
|
||||||
|
Example of function without implementation (a function declaration)::
|
||||||
|
|
||||||
|
function foo(address) external returns (address);
|
||||||
|
|
||||||
|
Example of a Function Type (a variable declaration, where the variable is of type ``function``)::
|
||||||
|
|
||||||
|
function(address) external returns (address) foo;
|
||||||
|
|
||||||
|
Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and
|
||||||
|
facilitating patterns like the `Template method <https://en.wikipedia.org/wiki/Template_method_pattern>`_ and removing code duplication.
|
||||||
|
|
||||||
.. index:: ! contract;interface, ! interface contract
|
.. index:: ! contract;interface, ! interface contract
|
||||||
|
|
||||||
**********
|
**********
|
||||||
|
@ -324,7 +324,8 @@ is ``false``. The default value for the ``uint`` or ``int`` types is ``0``. For
|
|||||||
element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes``
|
element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes``
|
||||||
and ``string``, the default value is an empty array or string.
|
and ``string``, the default value is an empty array or string.
|
||||||
|
|
||||||
A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared.
|
A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared
|
||||||
|
(this will change soon, see below).
|
||||||
This happens because Solidity inherits its scoping rules from JavaScript.
|
This happens because Solidity inherits its scoping rules from JavaScript.
|
||||||
This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block.
|
This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block.
|
||||||
As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``::
|
As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``::
|
||||||
@ -366,7 +367,9 @@ As a result, the following code is illegal and cause the compiler to throw an er
|
|||||||
}
|
}
|
||||||
|
|
||||||
In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value.
|
In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value.
|
||||||
As a result, the following code is legal, despite being poorly written::
|
As a result, the following code is legal, despite being poorly written:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
pragma solidity ^0.4.0;
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
@ -383,6 +386,60 @@ As a result, the following code is legal, despite being poorly written::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Scoping starting from Version 0.5.0
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
Starting from version 0.5.0, Solidity will change to the more widespread scoping rules of C99
|
||||||
|
(and many other languages): Variables are visible from the point right after their declaration
|
||||||
|
until the end of a ``{ }``-block. As an exception to this rule, variables declared in the
|
||||||
|
initialization part of a for-loop are only visible until the end of the for-loop.
|
||||||
|
|
||||||
|
Variables and other items declared outside of a code block, for example functions, contracts,
|
||||||
|
user-defined types, etc., do not change their scoping behaviour. This means you can
|
||||||
|
use state variables before they are declared and call functions recursively.
|
||||||
|
|
||||||
|
These rules are already introduced now as an experimental feature.
|
||||||
|
|
||||||
|
As a consequence, the following examples will compile without warnings, since
|
||||||
|
the two variables have the same name but disjoint scopes. In non-0.5.0-mode,
|
||||||
|
they have the same scope (the function ``minimalScoping``) and thus it does
|
||||||
|
not compile there.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pragma solidity ^0.4.0;
|
||||||
|
pragma experimental "v0.5.0";
|
||||||
|
contract C {
|
||||||
|
function minimalScoping() pure public {
|
||||||
|
{
|
||||||
|
uint same2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint same2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
As a special example of the C99 scoping rules, note that in the following,
|
||||||
|
the first assignment to ``x`` will actually assign the outer and not the inner variable.
|
||||||
|
In any case, you will get a warning about the outer variable being shadowed.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pragma solidity ^0.4.0;
|
||||||
|
pragma experimental "v0.5.0";
|
||||||
|
contract C {
|
||||||
|
function f() pure public returns (uint) {
|
||||||
|
uint x = 1;
|
||||||
|
{
|
||||||
|
x = 2; // this will assign to the outer variable
|
||||||
|
uint x;
|
||||||
|
}
|
||||||
|
return x; // x has value 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.. index:: ! exception, ! throw, ! assert, ! require, ! revert
|
.. index:: ! exception, ! throw, ! assert, ! require, ! revert
|
||||||
|
|
||||||
Error handling: Assert, Require, Revert and Exceptions
|
Error handling: Assert, Require, Revert and Exceptions
|
||||||
@ -407,7 +464,7 @@ and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those
|
|||||||
of an exception instead of "bubbling up".
|
of an exception instead of "bubbling up".
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the calling account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
|
The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
|
||||||
|
|
||||||
Catching exceptions is not yet possible.
|
Catching exceptions is not yet possible.
|
||||||
|
|
||||||
|
@ -9,17 +9,6 @@ This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_.
|
|||||||
Basic Questions
|
Basic Questions
|
||||||
***************
|
***************
|
||||||
|
|
||||||
Example contracts
|
|
||||||
=================
|
|
||||||
|
|
||||||
There are some `contract examples <https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts/>`_ by fivedogit and
|
|
||||||
there should be a `test contract <https://github.com/ethereum/solidity/blob/develop/test/libsolidity/SolidityEndToEndTest.cpp>`_ for every single feature of Solidity.
|
|
||||||
|
|
||||||
Create and publish the most basic contract possible
|
|
||||||
===================================================
|
|
||||||
|
|
||||||
A quite simple contract is the `greeter <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol>`_
|
|
||||||
|
|
||||||
Is it possible to do something on a specific block number? (e.g. publish a contract or execute a transaction)
|
Is it possible to do something on a specific block number? (e.g. publish a contract or execute a transaction)
|
||||||
=============================================================================================================
|
=============================================================================================================
|
||||||
|
|
||||||
@ -40,9 +29,9 @@ Is there a decompiler available?
|
|||||||
================================
|
================================
|
||||||
|
|
||||||
There is no exact decompiler to Solidity, but
|
There is no exact decompiler to Solidity, but
|
||||||
`Porosity <https://github.com/comaeio/porosity>`_ is close.
|
`Porosity <https://github.com/comaeio/porosity>`_ is close.
|
||||||
Because some information like variable names, comments, and
|
Because some information like variable names, comments, and
|
||||||
source code formatting is lost in the compilation process,
|
source code formatting is lost in the compilation process,
|
||||||
it is not possible to completely recover the original source code.
|
it is not possible to completely recover the original source code.
|
||||||
|
|
||||||
Bytecode can be disassembled to opcodes, a service that is provided by
|
Bytecode can be disassembled to opcodes, a service that is provided by
|
||||||
@ -74,25 +63,6 @@ has it (which includes `Remix <https://remix.ethereum.org/>`_), then
|
|||||||
``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my
|
``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my
|
||||||
examples.
|
examples.
|
||||||
|
|
||||||
Store Ether in a contract
|
|
||||||
=========================
|
|
||||||
|
|
||||||
The trick is to create the contract with ``{from:someaddress, value: web3.toWei(3,"ether")...}``
|
|
||||||
|
|
||||||
See `endowment_retriever.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_.
|
|
||||||
|
|
||||||
Use a non-constant function (req ``sendTransaction``) to increment a variable in a contract
|
|
||||||
===========================================================================================
|
|
||||||
|
|
||||||
See `value_incrementer.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/20_value_incrementer.sol>`_.
|
|
||||||
|
|
||||||
Get a contract to return its funds to you (not using ``selfdestruct(...)``).
|
|
||||||
============================================================================
|
|
||||||
|
|
||||||
This example demonstrates how to send funds from a contract to an address.
|
|
||||||
|
|
||||||
See `endowment_retriever <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_.
|
|
||||||
|
|
||||||
Can you return an array or a ``string`` from a solidity function call?
|
Can you return an array or a ``string`` from a solidity function call?
|
||||||
======================================================================
|
======================================================================
|
||||||
|
|
||||||
@ -542,12 +512,27 @@ contract level) with ``arrayname.length = <some new length>;``. If you get the
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
int8[] memory memArr; // Case 1
|
// This will not compile
|
||||||
memArr.length++; // illegal
|
|
||||||
int8[5] storageArr; // Case 2
|
pragma solidity ^0.4.18;
|
||||||
somearray.length++; // legal
|
|
||||||
int8[5] storage storageArr2; // Explicit case 2
|
contract C {
|
||||||
somearray2.length++; // legal
|
int8[] dynamicStorageArray;
|
||||||
|
int8[5] fixedStorageArray;
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
int8[] memory memArr; // Case 1
|
||||||
|
memArr.length++; // illegal
|
||||||
|
|
||||||
|
int8[5] storage storageArr = fixedStorageArray; // Case 2
|
||||||
|
storageArr.length++; // illegal
|
||||||
|
|
||||||
|
int8[] storage storageArr2 = dynamicStorageArray;
|
||||||
|
storageArr2.length++; // legal
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
**Important note:** In Solidity, array dimensions are declared backwards from the way you
|
**Important note:** In Solidity, array dimensions are declared backwards from the way you
|
||||||
might be used to declaring them in C or Java, but they are access as in
|
might be used to declaring them in C or Java, but they are access as in
|
||||||
|
@ -63,7 +63,7 @@ StateMutability = 'pure' | 'constant' | 'view' | 'payable'
|
|||||||
Block = '{' Statement* '}'
|
Block = '{' Statement* '}'
|
||||||
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
|
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
|
||||||
( DoWhileStatement | PlaceholderStatement | Continue | Break | Return |
|
( DoWhileStatement | PlaceholderStatement | Continue | Break | Return |
|
||||||
Throw | SimpleStatement ) ';'
|
Throw | EmitStatement | SimpleStatement ) ';'
|
||||||
|
|
||||||
ExpressionStatement = Expression
|
ExpressionStatement = Expression
|
||||||
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
|
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
|
||||||
@ -77,6 +77,7 @@ Continue = 'continue'
|
|||||||
Break = 'break'
|
Break = 'break'
|
||||||
Return = 'return' Expression?
|
Return = 'return' Expression?
|
||||||
Throw = 'throw'
|
Throw = 'throw'
|
||||||
|
EmitStatement = 'emit' FunctionCall
|
||||||
VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )?
|
VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )?
|
||||||
IdentifierList = '(' ( Identifier? ',' )* Identifier? ')'
|
IdentifierList = '(' ( Identifier? ',' )* Identifier? ')'
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@ crowdfunding, blind auctions, multi-signature wallets and more.
|
|||||||
.. note::
|
.. note::
|
||||||
The best way to try out Solidity right now is using
|
The best way to try out Solidity right now is using
|
||||||
`Remix <https://remix.ethereum.org/>`_
|
`Remix <https://remix.ethereum.org/>`_
|
||||||
(it can take a while to load, please be patient).
|
(it can take a while to load, please be patient). Remix is a web browser
|
||||||
|
based IDE that allows you to write Solidity smart contracts, then deploy
|
||||||
|
and run the smart contracts.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Since software is written by humans, it can have bugs. Thus, also
|
Since software is written by humans, it can have bugs. Thus, also
|
||||||
@ -34,6 +36,7 @@ Translations
|
|||||||
|
|
||||||
This documentation is translated into several languages by community volunteers, but the English version stands as a reference.
|
This documentation is translated into several languages by community volunteers, but the English version stands as a reference.
|
||||||
|
|
||||||
|
* `Simplified Chinese <http://solidity-cn.readthedocs.io>`_ (in progress)
|
||||||
* `Spanish <https://solidity-es.readthedocs.io>`_
|
* `Spanish <https://solidity-es.readthedocs.io>`_
|
||||||
* `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated)
|
* `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated)
|
||||||
|
|
||||||
|
@ -29,18 +29,20 @@ Further options on this page detail installing commandline Solidity compiler sof
|
|||||||
on your computer. Choose a commandline compiler if you are working on a larger contract
|
on your computer. Choose a commandline compiler if you are working on a larger contract
|
||||||
or if you require more compilation options.
|
or if you require more compilation options.
|
||||||
|
|
||||||
|
.. _solcjs:
|
||||||
|
|
||||||
npm / Node.js
|
npm / Node.js
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The
|
Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The
|
||||||
`solcjs` program has less features than all options further down this page. Our
|
`solcjs` program has less features than all options further down this page. Our
|
||||||
`Using the compiler <using-the-compiler.html>` documentation assumes you are using
|
:ref:`commandline-compiler` documentation assumes you are using
|
||||||
the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will
|
the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will
|
||||||
stop reading the documentation here and then continue to <https://github.com/ethereum/solc-js>,
|
stop reading the documentation here and then continue to `solc-js <https://github.com/ethereum/solc-js>`_.
|
||||||
|
|
||||||
Note: The `solc-js <https://github.com/ethereum/solc-js>` project is derived from the C++
|
Note: The solc-js project is derived from the C++
|
||||||
`solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix).
|
`solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix).
|
||||||
Please refer to the `solc-js <https://github.com/ethereum/solc-js>`_ repository for instructions.
|
Please refer to the solc-js repository for instructions.
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ registering with username and password - all you need is an Ethereum keypair.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pragma solidity ^0.4.0;
|
pragma solidity ^0.4.21;
|
||||||
|
|
||||||
contract Coin {
|
contract Coin {
|
||||||
// The keyword "public" makes those variables
|
// The keyword "public" makes those variables
|
||||||
@ -107,7 +107,7 @@ registering with username and password - all you need is an Ethereum keypair.
|
|||||||
if (balances[msg.sender] < amount) return;
|
if (balances[msg.sender] < amount) return;
|
||||||
balances[msg.sender] -= amount;
|
balances[msg.sender] -= amount;
|
||||||
balances[receiver] += amount;
|
balances[receiver] += amount;
|
||||||
Sent(msg.sender, receiver, amount);
|
emit Sent(msg.sender, receiver, amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,9 +118,11 @@ that is publicly accessible. The ``address`` type is a 160-bit value
|
|||||||
that does not allow any arithmetic operations. It is suitable for
|
that does not allow any arithmetic operations. It is suitable for
|
||||||
storing addresses of contracts or keypairs belonging to external
|
storing addresses of contracts or keypairs belonging to external
|
||||||
persons. The keyword ``public`` automatically generates a function that
|
persons. The keyword ``public`` automatically generates a function that
|
||||||
allows you to access the current value of the state variable.
|
allows you to access the current value of the state variable
|
||||||
|
from outside of the contract.
|
||||||
Without this keyword, other contracts have no way to access the variable.
|
Without this keyword, other contracts have no way to access the variable.
|
||||||
The function will look something like this::
|
The code of the function generated by the compiler is roughly equivalent
|
||||||
|
to the following::
|
||||||
|
|
||||||
function minter() returns (address) { return minter; }
|
function minter() returns (address) { return minter; }
|
||||||
|
|
||||||
@ -155,10 +157,10 @@ single account.
|
|||||||
.. index:: event
|
.. index:: event
|
||||||
|
|
||||||
The line ``event Sent(address from, address to, uint amount);`` declares
|
The line ``event Sent(address from, address to, uint amount);`` declares
|
||||||
a so-called "event" which is fired in the last line of the function
|
a so-called "event" which is emitted in the last line of the function
|
||||||
``send``. User interfaces (as well as server applications of course) can
|
``send``. User interfaces (as well as server applications of course) can
|
||||||
listen for those events being fired on the blockchain without much
|
listen for those events being emitted on the blockchain without much
|
||||||
cost. As soon as it is fired, the listener will also receive the
|
cost. As soon as it is emitted, the listener will also receive the
|
||||||
arguments ``from``, ``to`` and ``amount``, which makes it easy to track
|
arguments ``from``, ``to`` and ``amount``, which makes it easy to track
|
||||||
transactions. In order to listen for this event, you would use ::
|
transactions. In order to listen for this event, you would use ::
|
||||||
|
|
||||||
|
153
docs/julia.rst
153
docs/julia.rst
@ -320,168 +320,169 @@ The following functions must be available:
|
|||||||
|
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------------------------------------------------------------------------+
|
||||||
| *Arithmetics* |
|
| *Arithmetics* |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| addu256(x:u256, y:u256) -> z:u256 | x + y |
|
| addu256(x:u256, y:u256) -> z:u256 | x + y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| subu256(x:u256, y:u256) -> z:u256 | x - y |
|
| subu256(x:u256, y:u256) -> z:u256 | x - y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| mulu256(x:u256, y:u256) -> z:u256 | x * y |
|
| mulu256(x:u256, y:u256) -> z:u256 | x * y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| divu256(x:u256, y:u256) -> z:u256 | x / y |
|
| divu256(x:u256, y:u256) -> z:u256 | x / y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| divs256(x:s256, y:s256) -> z:s256 | x / y, for signed numbers in two's complement |
|
| divs256(x:s256, y:s256) -> z:s256 | x / y, for signed numbers in two's complement |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| modu256(x:u256, y:u256) -> z:u256 | x % y |
|
| modu256(x:u256, y:u256) -> z:u256 | x % y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| mods256(x:s256, y:s256) -> z:s256 | x % y, for signed numbers in two's complement |
|
| mods256(x:s256, y:s256) -> z:s256 | x % y, for signed numbers in two's complement |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| signextendu256(i:u256, x:u256) -> z:u256 | sign extend from (i*8+7)th bit counting from least significant |
|
| signextendu256(i:u256, x:u256) -> z:u256 | sign extend from (i*8+7)th bit counting from least significant |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| expu256(x:u256, y:u256) -> z:u256 | x to the power of y |
|
| expu256(x:u256, y:u256) -> z:u256 | x to the power of y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetics |
|
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetics |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics |
|
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| ltu256(x:u256, y:u256) -> z:bool | 1 if x < y, 0 otherwise |
|
| ltu256(x:u256, y:u256) -> z:bool | 1 if x < y, 0 otherwise |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| gtu256(x:u256, y:u256) -> z:bool | 1 if x > y, 0 otherwise |
|
| gtu256(x:u256, y:u256) -> z:bool | 1 if x > y, 0 otherwise |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| sltu256(x:s256, y:s256) -> z:bool | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
|
| sltu256(x:s256, y:s256) -> z:bool | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| sgtu256(x:s256, y:s256) -> z:bool | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
|
| sgtu256(x:s256, y:s256) -> z:bool | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| equ256(x:u256, y:u256) -> z:bool | 1 if x == y, 0 otherwise |
|
| equ256(x:u256, y:u256) -> z:bool | 1 if x == y, 0 otherwise |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| notu256(x:u256) -> z:u256 | ~x, every bit of x is negated |
|
| notu256(x:u256) -> z:u256 | ~x, every bit of x is negated |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| andu256(x:u256, y:u256) -> z:u256 | bitwise and of x and y |
|
| andu256(x:u256, y:u256) -> z:u256 | bitwise and of x and y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| oru256(x:u256, y:u256) -> z:u256 | bitwise or of x and y |
|
| oru256(x:u256, y:u256) -> z:u256 | bitwise or of x and y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| xoru256(x:u256, y:u256) -> z:u256 | bitwise xor of x and y |
|
| xoru256(x:u256, y:u256) -> z:u256 | bitwise xor of x and y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| shlu256(x:u256, y:u256) -> z:u256 | logical left shift of x by y |
|
| shlu256(x:u256, y:u256) -> z:u256 | logical left shift of x by y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y |
|
| shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y |
|
| saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte |
|
| byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte |
|
||||||
| Cannot this be just replaced by and256(shr256(n, x), 0xff) and let it be optimised out by the EVM backend? |
|
| | Cannot this be just replaced by and256(shr256(n, x), 0xff) and |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
| | let it be optimised out by the EVM backend? |
|
||||||
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| *Memory and storage* |
|
| *Memory and storage* |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| mload(p:u256) -> v:u256 | mem[p..(p+32)) |
|
| mload(p:u256) -> v:u256 | mem[p..(p+32)) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| mstore(p:u256, v:u256) | mem[p..(p+32)) := v |
|
| mstore(p:u256, v:u256) | mem[p..(p+32)) := v |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| mstore8(p:u256, v:u256) | mem[p] := v & 0xff - only modifies a single byte |
|
| mstore8(p:u256, v:u256) | mem[p] := v & 0xff - only modifies a single byte |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| sload(p:u256) -> v:u256 | storage[p] |
|
| sload(p:u256) -> v:u256 | storage[p] |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| sstore(p:u256, v:u256) | storage[p] := v |
|
| sstore(p:u256, v:u256) | storage[p] := v |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| msize() -> size:u256 | size of memory, i.e. largest accessed memory index, albeit due |
|
| msize() -> size:u256 | size of memory, i.e. largest accessed memory index, albeit due |
|
||||||
| | due to the memory extension function, which extends by words, |
|
| | due to the memory extension function, which extends by words, |
|
||||||
| | this will always be a multiple of 32 bytes |
|
| | this will always be a multiple of 32 bytes |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| *Execution control* |
|
| *Execution control* |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei |
|
| create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei |
|
||||||
| | and return the new address |
|
| | and return the new address |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) |
|
| call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) |
|
||||||
| insize:u256, out:u256, | providing g gas and v wei and output area |
|
| insize:u256, out:u256, | providing g gas and v wei and output area |
|
||||||
| outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
|
| outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
|
||||||
| -> r:u256 | and 1 on success |
|
| -> r:u256 | and 1 on success |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| callcode(g:u256, a:u256, v:u256, in:u256, | identical to ``call`` but only use the code from a |
|
| callcode(g:u256, a:u256, v:u256, in:u256, | identical to ``call`` but only use the code from a |
|
||||||
| insize:u256, out:u256, | and stay in the context of the |
|
| insize:u256, out:u256, | and stay in the context of the |
|
||||||
| outsize:u256) -> r:u256 | current contract otherwise |
|
| outsize:u256) -> r:u256 | current contract otherwise |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| delegatecall(g:u256, a:u256, in:u256, | identical to ``callcode``, |
|
| delegatecall(g:u256, a:u256, in:u256, | identical to ``callcode``, |
|
||||||
| insize:u256, out:u256, | but also keep ``caller`` |
|
| insize:u256, out:u256, | but also keep ``caller`` |
|
||||||
| outsize:u256) -> r:u256 | and ``callvalue`` |
|
| outsize:u256) -> r:u256 | and ``callvalue`` |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| stop() | stop execution, identical to return(0,0) |
|
| stop() | stop execution, identical to return(0,0) |
|
||||||
| Perhaps it would make sense retiring this as it equals to return(0,0). It can be an optimisation by the EVM |
|
| | Perhaps it would make sense retiring this as it equals to |
|
||||||
| backend. |
|
| | return(0,0). It can be an optimisation by the EVM backend. |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| abort() | abort (equals to invalid instruction on EVM) |
|
| abort() | abort (equals to invalid instruction on EVM) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) |
|
| return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| revert(p:u256, s:u256) | end execution, revert state changes, return data mem[p..(p+s)) |
|
| revert(p:u256, s:u256) | end execution, revert state changes, return data mem[p..(p+s)) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| selfdestruct(a:u256) | end execution, destroy current contract and send funds to a |
|
| selfdestruct(a:u256) | end execution, destroy current contract and send funds to a |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| log0(p:u256, s:u256) | log without topics and data mem[p..(p+s)) |
|
| log0(p:u256, s:u256) | log without topics and data mem[p..(p+s)) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| log1(p:u256, s:u256, t1:u256) | log with topic t1 and data mem[p..(p+s)) |
|
| log1(p:u256, s:u256, t1:u256) | log with topic t1 and data mem[p..(p+s)) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| log2(p:u256, s:u256, t1:u256, t2:u256) | log with topics t1, t2 and data mem[p..(p+s)) |
|
| log2(p:u256, s:u256, t1:u256, t2:u256) | log with topics t1, t2 and data mem[p..(p+s)) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| log3(p:u256, s:u256, t1:u256, t2:u256, | log with topics t, t2, t3 and data mem[p..(p+s)) |
|
| log3(p:u256, s:u256, t1:u256, t2:u256, | log with topics t, t2, t3 and data mem[p..(p+s)) |
|
||||||
| t3:u256) | |
|
| t3:u256) | |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| log4(p:u256, s:u256, t1:u256, t2:u256, | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
|
| log4(p:u256, s:u256, t1:u256, t2:u256, | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
|
||||||
| t3:u256, t4:u256) | |
|
| t3:u256, t4:u256) | |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| *State queries* |
|
| *State queries* |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| blockcoinbase() -> address:u256 | current mining beneficiary |
|
| blockcoinbase() -> address:u256 | current mining beneficiary |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| blockdifficulty() -> difficulty:u256 | difficulty of the current block |
|
| blockdifficulty() -> difficulty:u256 | difficulty of the current block |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| blockgaslimit() -> limit:u256 | block gas limit of the current block |
|
| blockgaslimit() -> limit:u256 | block gas limit of the current block |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| blockhash(b:u256) -> hash:u256 | hash of block nr b - only for last 256 blocks excluding current |
|
| blockhash(b:u256) -> hash:u256 | hash of block nr b - only for last 256 blocks excluding current |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| blocknumber() -> block:u256 | current block number |
|
| blocknumber() -> block:u256 | current block number |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| blocktimestamp() -> timestamp:u256 | timestamp of the current block in seconds since the epoch |
|
| blocktimestamp() -> timestamp:u256 | timestamp of the current block in seconds since the epoch |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| txorigin() -> address:u256 | transaction sender |
|
| txorigin() -> address:u256 | transaction sender |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| txgasprice() -> price:u256 | gas price of the transaction |
|
| txgasprice() -> price:u256 | gas price of the transaction |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| gasleft() -> gas:u256 | gas still available to execution |
|
| gasleft() -> gas:u256 | gas still available to execution |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| balance(a:u256) -> v:u256 | wei balance at address a |
|
| balance(a:u256) -> v:u256 | wei balance at address a |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| this() -> address:u256 | address of the current contract / execution context |
|
| this() -> address:u256 | address of the current contract / execution context |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| caller() -> address:u256 | call sender (excluding delegatecall) |
|
| caller() -> address:u256 | call sender (excluding delegatecall) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| callvalue() -> v:u256 | wei sent together with the current call |
|
| callvalue() -> v:u256 | wei sent together with the current call |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| calldataload(p:u256) -> v:u256 | call data starting from position p (32 bytes) |
|
| calldataload(p:u256) -> v:u256 | call data starting from position p (32 bytes) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| calldatasize() -> v:u256 | size of call data in bytes |
|
| calldatasize() -> v:u256 | size of call data in bytes |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| calldatacopy(t:u256, f:u256, s:u256) | copy s bytes from calldata at position f to mem at position t |
|
| calldatacopy(t:u256, f:u256, s:u256) | copy s bytes from calldata at position f to mem at position t |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| codesize() -> size:u256 | size of the code of the current contract / execution context |
|
| codesize() -> size:u256 | size of the code of the current contract / execution context |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| codecopy(t:u256, f:u256, s:u256) | copy s bytes from code at position f to mem at position t |
|
| codecopy(t:u256, f:u256, s:u256) | copy s bytes from code at position f to mem at position t |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| extcodesize(a:u256) -> size:u256 | size of the code at address a |
|
| extcodesize(a:u256) -> size:u256 | size of the code at address a |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a |
|
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| *Others* |
|
| *Others* |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| discardu256(unused:u256) | discard value |
|
| discardu256(unused:u256) | discard value |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's |
|
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's |
|
||||||
| x3:u64, x4:u64) | |
|
| x3:u64, x4:u64) | |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 |
|
| combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 |
|
||||||
| x4:u64) -> (x:u256) | |
|
| x4:u64) -> (x:u256) | |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
| sha3(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) |
|
| sha3(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) |
|
||||||
+---------------------------------------------------------------------------------------------------------------+
|
+---------------------------------------------+-----------------------------------------------------------------+
|
||||||
|
|
||||||
Backends
|
Backends
|
||||||
--------
|
--------
|
||||||
|
@ -192,6 +192,11 @@ These are regular array indices into a list of source files usually called
|
|||||||
``"sourceList"``, which is part of the combined-json and the output of
|
``"sourceList"``, which is part of the combined-json and the output of
|
||||||
the json / npm compiler.
|
the json / npm compiler.
|
||||||
|
|
||||||
|
.. note ::
|
||||||
|
In the case of instructions that are not associated with any particular source file,
|
||||||
|
the source mapping assigns an integer identifier of ``-1``. This may happen for
|
||||||
|
bytecode sections stemming from compiler-generated inline assembly statements.
|
||||||
|
|
||||||
The source mappings inside the AST use the following
|
The source mappings inside the AST use the following
|
||||||
notation:
|
notation:
|
||||||
|
|
||||||
@ -219,7 +224,7 @@ This means the following source mappings represent the same information:
|
|||||||
|
|
||||||
``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2``
|
``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2``
|
||||||
|
|
||||||
``1:2:1;:9;2::2;;``
|
``1:2:1;:9;2:1:2;;``
|
||||||
|
|
||||||
***************
|
***************
|
||||||
Tips and Tricks
|
Tips and Tricks
|
||||||
@ -230,7 +235,10 @@ Tips and Tricks
|
|||||||
* Make your state variables public - the compiler will create :ref:`getters <visibility-and-getters>` for you automatically.
|
* Make your state variables public - the compiler will create :ref:`getters <visibility-and-getters>` for you automatically.
|
||||||
* If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`.
|
* If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`.
|
||||||
* If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``.
|
* If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``.
|
||||||
* Initialise storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});``
|
* Initialize storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});``
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If the storage struct has tightly packed properties, initialize it with separate assignments: ``x.a = 1; x.b = 2;``. In this way it will be easier for the optimizer to update storage in one go, thus making assignment cheaper.
|
||||||
|
|
||||||
**********
|
**********
|
||||||
Cheatsheet
|
Cheatsheet
|
||||||
@ -312,8 +320,9 @@ Global Variables
|
|||||||
- ``block.gaslimit`` (``uint``): current block gaslimit
|
- ``block.gaslimit`` (``uint``): current block gaslimit
|
||||||
- ``block.number`` (``uint``): current block number
|
- ``block.number`` (``uint``): current block number
|
||||||
- ``block.timestamp`` (``uint``): current block timestamp
|
- ``block.timestamp`` (``uint``): current block timestamp
|
||||||
|
- ``gasleft() returns (uint256)``: remaining gas
|
||||||
- ``msg.data`` (``bytes``): complete calldata
|
- ``msg.data`` (``bytes``): complete calldata
|
||||||
- ``msg.gas`` (``uint``): remaining gas
|
- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()``
|
||||||
- ``msg.sender`` (``address``): sender of the message (current call)
|
- ``msg.sender`` (``address``): sender of the message (current call)
|
||||||
- ``msg.value`` (``uint``): number of wei sent with the message
|
- ``msg.value`` (``uint``): number of wei sent with the message
|
||||||
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
|
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
|
||||||
@ -327,8 +336,8 @@ Global Variables
|
|||||||
- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||||
- ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
- ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||||
- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error
|
- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error
|
||||||
- ``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``
|
- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||||
- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``
|
- ``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.
|
||||||
- ``this`` (current contract's type): the current contract, explicitly convertible to ``address``
|
- ``this`` (current contract's type): the current contract, explicitly convertible to ``address``
|
||||||
- ``super``: the contract one level higher in the inheritance hierarchy
|
- ``super``: the contract one level higher in the inheritance hierarchy
|
||||||
- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address
|
- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address
|
||||||
|
@ -94,7 +94,11 @@ of votes.
|
|||||||
// called incorrectly. But watch out, this
|
// called incorrectly. But watch out, this
|
||||||
// will currently also consume all provided gas
|
// will currently also consume all provided gas
|
||||||
// (this is planned to change in the future).
|
// (this is planned to change in the future).
|
||||||
require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0));
|
require(
|
||||||
|
(msg.sender == chairperson) &&
|
||||||
|
!voters[voter].voted &&
|
||||||
|
(voters[voter].weight == 0)
|
||||||
|
);
|
||||||
voters[voter].weight = 1;
|
voters[voter].weight = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,15 +130,15 @@ of votes.
|
|||||||
// modifies `voters[msg.sender].voted`
|
// modifies `voters[msg.sender].voted`
|
||||||
sender.voted = true;
|
sender.voted = true;
|
||||||
sender.delegate = to;
|
sender.delegate = to;
|
||||||
Voter storage delegate = voters[to];
|
Voter storage delegate_ = voters[to];
|
||||||
if (delegate.voted) {
|
if (delegate_.voted) {
|
||||||
// If the delegate already voted,
|
// If the delegate already voted,
|
||||||
// directly add to the number of votes
|
// directly add to the number of votes
|
||||||
proposals[delegate.vote].voteCount += sender.weight;
|
proposals[delegate_.vote].voteCount += sender.weight;
|
||||||
} else {
|
} else {
|
||||||
// If the delegate did not vote yet,
|
// If the delegate did not vote yet,
|
||||||
// add to her weight.
|
// add to her weight.
|
||||||
delegate.weight += sender.weight;
|
delegate_.weight += sender.weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,13 +159,13 @@ of votes.
|
|||||||
/// @dev Computes the winning proposal taking all
|
/// @dev Computes the winning proposal taking all
|
||||||
/// previous votes into account.
|
/// previous votes into account.
|
||||||
function winningProposal() public view
|
function winningProposal() public view
|
||||||
returns (uint winningProposal)
|
returns (uint winningProposal_)
|
||||||
{
|
{
|
||||||
uint winningVoteCount = 0;
|
uint winningVoteCount = 0;
|
||||||
for (uint p = 0; p < proposals.length; p++) {
|
for (uint p = 0; p < proposals.length; p++) {
|
||||||
if (proposals[p].voteCount > winningVoteCount) {
|
if (proposals[p].voteCount > winningVoteCount) {
|
||||||
winningVoteCount = proposals[p].voteCount;
|
winningVoteCount = proposals[p].voteCount;
|
||||||
winningProposal = p;
|
winningProposal_ = p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,12 +174,13 @@ of votes.
|
|||||||
// of the winner contained in the proposals array and then
|
// of the winner contained in the proposals array and then
|
||||||
// returns the name of the winner
|
// returns the name of the winner
|
||||||
function winnerName() public view
|
function winnerName() public view
|
||||||
returns (bytes32 winnerName)
|
returns (bytes32 winnerName_)
|
||||||
{
|
{
|
||||||
winnerName = proposals[winningProposal()].name;
|
winnerName_ = proposals[winningProposal()].name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Possible Improvements
|
Possible Improvements
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
@ -214,7 +219,7 @@ activate themselves.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pragma solidity ^0.4.11;
|
pragma solidity ^0.4.21;
|
||||||
|
|
||||||
contract SimpleAuction {
|
contract SimpleAuction {
|
||||||
// Parameters of the auction. Times are either
|
// Parameters of the auction. Times are either
|
||||||
@ -272,7 +277,7 @@ activate themselves.
|
|||||||
// money back.
|
// money back.
|
||||||
require(msg.value > highestBid);
|
require(msg.value > highestBid);
|
||||||
|
|
||||||
if (highestBidder != 0) {
|
if (highestBid != 0) {
|
||||||
// Sending back the money by simply using
|
// Sending back the money by simply using
|
||||||
// highestBidder.send(highestBid) is a security risk
|
// highestBidder.send(highestBid) is a security risk
|
||||||
// because it could execute an untrusted contract.
|
// because it could execute an untrusted contract.
|
||||||
@ -282,7 +287,7 @@ activate themselves.
|
|||||||
}
|
}
|
||||||
highestBidder = msg.sender;
|
highestBidder = msg.sender;
|
||||||
highestBid = msg.value;
|
highestBid = msg.value;
|
||||||
HighestBidIncreased(msg.sender, msg.value);
|
emit HighestBidIncreased(msg.sender, msg.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Withdraw a bid that was overbid.
|
/// Withdraw a bid that was overbid.
|
||||||
@ -325,7 +330,7 @@ activate themselves.
|
|||||||
|
|
||||||
// 2. Effects
|
// 2. Effects
|
||||||
ended = true;
|
ended = true;
|
||||||
AuctionEnded(highestBidder, highestBid);
|
emit AuctionEnded(highestBidder, highestBid);
|
||||||
|
|
||||||
// 3. Interaction
|
// 3. Interaction
|
||||||
beneficiary.transfer(highestBid);
|
beneficiary.transfer(highestBid);
|
||||||
@ -371,7 +376,7 @@ high or low invalid bids.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pragma solidity ^0.4.11;
|
pragma solidity ^0.4.21;
|
||||||
|
|
||||||
contract BlindAuction {
|
contract BlindAuction {
|
||||||
struct Bid {
|
struct Bid {
|
||||||
@ -509,7 +514,7 @@ high or low invalid bids.
|
|||||||
onlyAfter(revealEnd)
|
onlyAfter(revealEnd)
|
||||||
{
|
{
|
||||||
require(!ended);
|
require(!ended);
|
||||||
AuctionEnded(highestBidder, highestBid);
|
emit AuctionEnded(highestBidder, highestBid);
|
||||||
ended = true;
|
ended = true;
|
||||||
beneficiary.transfer(highestBid);
|
beneficiary.transfer(highestBid);
|
||||||
}
|
}
|
||||||
@ -524,7 +529,7 @@ Safe Remote Purchase
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pragma solidity ^0.4.11;
|
pragma solidity ^0.4.21;
|
||||||
|
|
||||||
contract Purchase {
|
contract Purchase {
|
||||||
uint public value;
|
uint public value;
|
||||||
@ -574,7 +579,7 @@ Safe Remote Purchase
|
|||||||
onlySeller
|
onlySeller
|
||||||
inState(State.Created)
|
inState(State.Created)
|
||||||
{
|
{
|
||||||
Aborted();
|
emit Aborted();
|
||||||
state = State.Inactive;
|
state = State.Inactive;
|
||||||
seller.transfer(this.balance);
|
seller.transfer(this.balance);
|
||||||
}
|
}
|
||||||
@ -589,7 +594,7 @@ Safe Remote Purchase
|
|||||||
condition(msg.value == (2 * value))
|
condition(msg.value == (2 * value))
|
||||||
payable
|
payable
|
||||||
{
|
{
|
||||||
PurchaseConfirmed();
|
emit PurchaseConfirmed();
|
||||||
buyer = msg.sender;
|
buyer = msg.sender;
|
||||||
state = State.Locked;
|
state = State.Locked;
|
||||||
}
|
}
|
||||||
@ -601,7 +606,7 @@ Safe Remote Purchase
|
|||||||
onlyBuyer
|
onlyBuyer
|
||||||
inState(State.Locked)
|
inState(State.Locked)
|
||||||
{
|
{
|
||||||
ItemReceived();
|
emit ItemReceived();
|
||||||
// It is important to change the state first because
|
// It is important to change the state first because
|
||||||
// otherwise, the contracts called using `send` below
|
// otherwise, the contracts called using `send` below
|
||||||
// can call in again here.
|
// can call in again here.
|
||||||
|
@ -86,14 +86,14 @@ Events are convenience interfaces with the EVM logging facilities.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pragma solidity ^0.4.0;
|
pragma solidity ^0.4.21;
|
||||||
|
|
||||||
contract SimpleAuction {
|
contract SimpleAuction {
|
||||||
event HighestBidIncreased(address bidder, uint amount); // Event
|
event HighestBidIncreased(address bidder, uint amount); // Event
|
||||||
|
|
||||||
function bid() public payable {
|
function bid() public payable {
|
||||||
// ...
|
// ...
|
||||||
HighestBidIncreased(msg.sender, msg.value); // Triggering event
|
emit HighestBidIncreased(msg.sender, msg.value); // Triggering event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +112,111 @@ No::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.. _maximum_line_length:
|
||||||
|
|
||||||
|
Maximum Line Length
|
||||||
|
===================
|
||||||
|
|
||||||
|
Keeping lines under the `PEP 8 recommendation <https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_ of 79 (or 99)
|
||||||
|
characters helps readers easily parse the code.
|
||||||
|
|
||||||
|
Wrapped lines should conform to the following guidelines.
|
||||||
|
|
||||||
|
1. The first argument should not be attached to the opening parenthesis.
|
||||||
|
2. One, and only one, indent should be used.
|
||||||
|
3. Each argument should fall on its own line.
|
||||||
|
4. The terminating element, :code:`);`, should be placed on the final line by itself.
|
||||||
|
|
||||||
|
Function Calls
|
||||||
|
|
||||||
|
Yes::
|
||||||
|
|
||||||
|
thisFunctionCallIsReallyLong(
|
||||||
|
longArgument1,
|
||||||
|
longArgument2,
|
||||||
|
longArgument3
|
||||||
|
);
|
||||||
|
|
||||||
|
No::
|
||||||
|
|
||||||
|
thisFunctionCallIsReallyLong(longArgument1,
|
||||||
|
longArgument2,
|
||||||
|
longArgument3
|
||||||
|
);
|
||||||
|
|
||||||
|
thisFunctionCallIsReallyLong(longArgument1,
|
||||||
|
longArgument2,
|
||||||
|
longArgument3
|
||||||
|
);
|
||||||
|
|
||||||
|
thisFunctionCallIsReallyLong(
|
||||||
|
longArgument1, longArgument2,
|
||||||
|
longArgument3
|
||||||
|
);
|
||||||
|
|
||||||
|
thisFunctionCallIsReallyLong(
|
||||||
|
longArgument1,
|
||||||
|
longArgument2,
|
||||||
|
longArgument3
|
||||||
|
);
|
||||||
|
|
||||||
|
thisFunctionCallIsReallyLong(
|
||||||
|
longArgument1,
|
||||||
|
longArgument2,
|
||||||
|
longArgument3);
|
||||||
|
|
||||||
|
Assignment Statements
|
||||||
|
|
||||||
|
Yes::
|
||||||
|
|
||||||
|
thisIsALongNestedMapping[being][set][to_some_value] = someFunction(
|
||||||
|
argument1,
|
||||||
|
argument2,
|
||||||
|
argument3,
|
||||||
|
argument4
|
||||||
|
);
|
||||||
|
|
||||||
|
No::
|
||||||
|
|
||||||
|
thisIsALongNestedMapping[being][set][to_some_value] = someFunction(argument1,
|
||||||
|
argument2,
|
||||||
|
argument3,
|
||||||
|
argument4);
|
||||||
|
|
||||||
|
Event Definitions and Event Emitters
|
||||||
|
|
||||||
|
Yes::
|
||||||
|
|
||||||
|
event LongAndLotsOfArgs(
|
||||||
|
adress sender,
|
||||||
|
adress recipient,
|
||||||
|
uint256 publicKey,
|
||||||
|
uint256 amount,
|
||||||
|
bytes32[] options
|
||||||
|
);
|
||||||
|
|
||||||
|
LongAndLotsOfArgs(
|
||||||
|
sender,
|
||||||
|
recipient,
|
||||||
|
publicKey,
|
||||||
|
amount,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
No::
|
||||||
|
|
||||||
|
event LongAndLotsOfArgs(adress sender,
|
||||||
|
adress recipient,
|
||||||
|
uint256 publicKey,
|
||||||
|
uint256 amount,
|
||||||
|
bytes32[] options);
|
||||||
|
|
||||||
|
LongAndLotsOfArgs(sender,
|
||||||
|
recipient,
|
||||||
|
publicKey,
|
||||||
|
amount,
|
||||||
|
options);
|
||||||
|
|
||||||
Source File Encoding
|
Source File Encoding
|
||||||
====================
|
====================
|
||||||
|
|
||||||
@ -391,7 +496,7 @@ function body to be kept on the same line as the function declaration.
|
|||||||
The closing brace should be at the same indentation level as the function
|
The closing brace should be at the same indentation level as the function
|
||||||
declaration.
|
declaration.
|
||||||
|
|
||||||
The opening brace should be preceeded by a single space.
|
The opening brace should be preceded by a single space.
|
||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
|
|
||||||
@ -421,7 +526,21 @@ No::
|
|||||||
function increment(uint x) public pure returns (uint) {
|
function increment(uint x) public pure returns (uint) {
|
||||||
return x + 1;}
|
return x + 1;}
|
||||||
|
|
||||||
The visibility modifiers for a function should come before any custom
|
You should explicitly label the visibility of all functions, including constructors.
|
||||||
|
|
||||||
|
Yes::
|
||||||
|
|
||||||
|
function explicitlyPublic(uint val) public {
|
||||||
|
doSomething();
|
||||||
|
}
|
||||||
|
|
||||||
|
No::
|
||||||
|
|
||||||
|
function implicitlyPublic(uint val) {
|
||||||
|
doSomething();
|
||||||
|
}
|
||||||
|
|
||||||
|
The visibility modifier for a function should come before any custom
|
||||||
modifiers.
|
modifiers.
|
||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
@ -533,6 +652,50 @@ No::
|
|||||||
doSomething();
|
doSomething();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Multiline output parameters and return statements should follow the same style recommended for wrapping long lines found in the :ref:`Maximum Line Length <maximum_line_length>` section.
|
||||||
|
|
||||||
|
Yes::
|
||||||
|
|
||||||
|
function thisFunctionNameIsReallyLong(
|
||||||
|
address a,
|
||||||
|
address b,
|
||||||
|
address c
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (
|
||||||
|
address someAddressName,
|
||||||
|
uint256 LongArgument,
|
||||||
|
uint256 Argument
|
||||||
|
)
|
||||||
|
{
|
||||||
|
doSomething()
|
||||||
|
|
||||||
|
return (
|
||||||
|
veryLongReturnArg1,
|
||||||
|
veryLongReturnArg2,
|
||||||
|
veryLongReturnArg3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
No::
|
||||||
|
|
||||||
|
function thisFunctionNameIsReallyLong(
|
||||||
|
address a,
|
||||||
|
address b,
|
||||||
|
address c
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (address someAddressName,
|
||||||
|
uint256 LongArgument,
|
||||||
|
uint256 Argument)
|
||||||
|
{
|
||||||
|
doSomething()
|
||||||
|
|
||||||
|
return (veryLongReturnArg1,
|
||||||
|
veryLongReturnArg1,
|
||||||
|
veryLongReturnArg1);
|
||||||
|
}
|
||||||
|
|
||||||
For constructor functions on inherited contracts whose bases require arguments,
|
For constructor functions on inherited contracts whose bases require arguments,
|
||||||
it is recommended to drop the base constructors onto new lines in the same
|
it is recommended to drop the base constructors onto new lines in the same
|
||||||
manner as modifiers if the function declaration is long or hard to read.
|
manner as modifiers if the function declaration is long or hard to read.
|
||||||
@ -684,7 +847,7 @@ naming styles.
|
|||||||
* ``mixedCase`` (differs from CapitalizedWords by initial lowercase character!)
|
* ``mixedCase`` (differs from CapitalizedWords by initial lowercase character!)
|
||||||
* ``Capitalized_Words_With_Underscores``
|
* ``Capitalized_Words_With_Underscores``
|
||||||
|
|
||||||
.. note:: When using abbreviations in CapWords, capitalize all the letters of the abbreviation. Thus HTTPServerError is better than HttpServerError.
|
.. note:: When using initialisms in CapWords, capitalize all the letters of the initialisms. Thus HTTPServerError is better than HttpServerError. When using initialisms is mixedCase, capitalize all the letters of the initialisms, except keep the first one lower case if it is the beginning of the name. Thus xmlHTTPRequest is better than XMLHTTPRequest.
|
||||||
|
|
||||||
|
|
||||||
Names to Avoid
|
Names to Avoid
|
||||||
|
@ -470,7 +470,7 @@ Example that shows how to use internal function types::
|
|||||||
|
|
||||||
Another example that uses external function types::
|
Another example that uses external function types::
|
||||||
|
|
||||||
pragma solidity ^0.4.11;
|
pragma solidity ^0.4.21;
|
||||||
|
|
||||||
contract Oracle {
|
contract Oracle {
|
||||||
struct Request {
|
struct Request {
|
||||||
@ -481,7 +481,7 @@ Another example that uses external function types::
|
|||||||
event NewRequest(uint);
|
event NewRequest(uint);
|
||||||
function query(bytes data, function(bytes memory) external callback) public {
|
function query(bytes data, function(bytes memory) external callback) public {
|
||||||
requests.push(Request(data, callback));
|
requests.push(Request(data, callback));
|
||||||
NewRequest(requests.length - 1);
|
emit NewRequest(requests.length - 1);
|
||||||
}
|
}
|
||||||
function reply(uint requestID, bytes response) public {
|
function reply(uint requestID, bytes response) public {
|
||||||
// Here goes the check that the reply comes from a trusted source
|
// Here goes the check that the reply comes from a trusted source
|
||||||
|
@ -58,8 +58,9 @@ Block and Transaction Properties
|
|||||||
- ``block.gaslimit`` (``uint``): current block gaslimit
|
- ``block.gaslimit`` (``uint``): current block gaslimit
|
||||||
- ``block.number`` (``uint``): current block number
|
- ``block.number`` (``uint``): current block number
|
||||||
- ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch
|
- ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch
|
||||||
|
- ``gasleft() returns (uint256)``: remaining gas
|
||||||
- ``msg.data`` (``bytes``): complete calldata
|
- ``msg.data`` (``bytes``): complete calldata
|
||||||
- ``msg.gas`` (``uint``): remaining gas
|
- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()``
|
||||||
- ``msg.sender`` (``address``): sender of the message (current call)
|
- ``msg.sender`` (``address``): sender of the message (current call)
|
||||||
- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier)
|
- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier)
|
||||||
- ``msg.value`` (``uint``): number of wei sent with the message
|
- ``msg.value`` (``uint``): number of wei sent with the message
|
||||||
@ -107,9 +108,9 @@ 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``.
|
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``.
|
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(...) returns (bytes32)``:
|
``keccak256(...) returns (bytes32)``:
|
||||||
compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||||
``sha256(...) returns (bytes32)``:
|
``sha256(...) returns (bytes32)``:
|
||||||
@ -149,15 +150,15 @@ Address Related
|
|||||||
``<address>.balance`` (``uint256``):
|
``<address>.balance`` (``uint256``):
|
||||||
balance of the :ref:`address` in Wei
|
balance of the :ref:`address` in Wei
|
||||||
``<address>.transfer(uint256 amount)``:
|
``<address>.transfer(uint256 amount)``:
|
||||||
send given amount of Wei to :ref:`address`, throws on failure
|
send given amount of Wei to :ref:`address`, throws on failure, forwards 2300 gas stipend, not adjustable
|
||||||
``<address>.send(uint256 amount) returns (bool)``:
|
``<address>.send(uint256 amount) returns (bool)``:
|
||||||
send given amount of Wei to :ref:`address`, returns ``false`` on failure
|
send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable
|
||||||
``<address>.call(...) returns (bool)``:
|
``<address>.call(...) returns (bool)``:
|
||||||
issue low-level ``CALL``, returns ``false`` on failure
|
issue low-level ``CALL``, returns ``false`` on failure, forwards all available gas, adjustable
|
||||||
``<address>.callcode(...) returns (bool)``:
|
``<address>.callcode(...) returns (bool)``:
|
||||||
issue low-level ``CALLCODE``, returns ``false`` on failure
|
issue low-level ``CALLCODE``, returns ``false`` on failure, forwards all available gas, adjustable
|
||||||
``<address>.delegatecall(...) returns (bool)``:
|
``<address>.delegatecall(...) returns (bool)``:
|
||||||
issue low-level ``DELEGATECALL``, returns ``false`` on failure
|
issue low-level ``DELEGATECALL``, returns ``false`` on failure, forwards all available gas, adjustable
|
||||||
|
|
||||||
For more information, see the section on :ref:`address`.
|
For more information, see the section on :ref:`address`.
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ Using the compiler
|
|||||||
Using the Commandline Compiler
|
Using the Commandline Compiler
|
||||||
******************************
|
******************************
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This section doesn't apply to :ref:`solcjs <solcjs>`.
|
||||||
|
|
||||||
One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler.
|
One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler.
|
||||||
Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage.
|
Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage.
|
||||||
If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. 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. Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. 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``.
|
||||||
@ -98,6 +101,7 @@ Input Description
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 500
|
runs: 500
|
||||||
},
|
},
|
||||||
|
evmVersion: "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople
|
||||||
// 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)
|
||||||
|
@ -2,7 +2,9 @@ file(GLOB sources "*.cpp")
|
|||||||
file(GLOB headers "*.h")
|
file(GLOB headers "*.h")
|
||||||
|
|
||||||
add_library(devcore ${sources} ${headers})
|
add_library(devcore ${sources} ${headers})
|
||||||
target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(devcore PRIVATE ${JSONCPP_LIBRARY} ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
||||||
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
|
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
|
||||||
|
target_include_directories(devcore PUBLIC "${JSONCPP_INCLUDE_DIR}")
|
||||||
|
add_dependencies(devcore jsoncpp)
|
||||||
add_dependencies(devcore solidity_BuildInfo.h)
|
add_dependencies(devcore solidity_BuildInfo.h)
|
||||||
|
109
libdevcore/JSON.cpp
Normal file
109
libdevcore/JSON.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/** @file JSON.cpp
|
||||||
|
* @author Alexander Arlt <alexander.arlt@arlt-labs.com>
|
||||||
|
* @date 2018
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "JSON.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
/// StreamWriterBuilder that can be constructed with specific settings
|
||||||
|
class StreamWriterBuilder: public Json::StreamWriterBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit StreamWriterBuilder(map<string, string> const& _settings)
|
||||||
|
{
|
||||||
|
for (auto const& iter :_settings)
|
||||||
|
this->settings_[iter.first] = iter.second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// CharReaderBuilder with strict-mode settings
|
||||||
|
class StrictModeCharReaderBuilder: public Json::CharReaderBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StrictModeCharReaderBuilder()
|
||||||
|
{
|
||||||
|
Json::CharReaderBuilder::strictMode(&this->settings_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Serialise the JSON object (@a _input) with specific builder (@a _builder)
|
||||||
|
/// \param _input JSON input string
|
||||||
|
/// \param _builder StreamWriterBuilder that is used to create new Json::StreamWriter
|
||||||
|
/// \return serialized json object
|
||||||
|
string print(Json::Value const& _input, Json::StreamWriterBuilder const& _builder)
|
||||||
|
{
|
||||||
|
stringstream stream;
|
||||||
|
unique_ptr<Json::StreamWriter> writer(_builder.newStreamWriter());
|
||||||
|
writer->write(_input, &stream);
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a JSON string (@a _input) with specified builder (@ _builder) and writes resulting JSON object to (@a _json)
|
||||||
|
/// \param _builder CharReaderBuilder that is used to create new Json::CharReaders
|
||||||
|
/// \param _input JSON input string
|
||||||
|
/// \param _json [out] resulting JSON object
|
||||||
|
/// \param _errs [out] Formatted error messages
|
||||||
|
/// \return \c true if the document was successfully parsed, \c false if an error occurred.
|
||||||
|
bool parse(Json::CharReaderBuilder& _builder, string const& _input, Json::Value& _json, string* _errs)
|
||||||
|
{
|
||||||
|
unique_ptr<Json::CharReader> reader(_builder.newCharReader());
|
||||||
|
return reader->parse(_input.c_str(), _input.c_str() + _input.length(), &_json, _errs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
string jsonPrettyPrint(Json::Value const& _input)
|
||||||
|
{
|
||||||
|
static map<string, string> settings{{"indentation", " "}};
|
||||||
|
static StreamWriterBuilder writerBuilder(settings);
|
||||||
|
return print(_input, writerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
string jsonCompactPrint(Json::Value const& _input)
|
||||||
|
{
|
||||||
|
static map<string, string> settings{{"indentation", ""}};
|
||||||
|
static StreamWriterBuilder writerBuilder(settings);
|
||||||
|
return print(_input, writerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jsonParseStrict(string const& _input, Json::Value& _json, string* _errs /* = nullptr */)
|
||||||
|
{
|
||||||
|
static StrictModeCharReaderBuilder readerBuilder;
|
||||||
|
return parse(readerBuilder, _input, _json, _errs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jsonParse(string const& _input, Json::Value& _json, string *_errs /* = nullptr */)
|
||||||
|
{
|
||||||
|
static Json::CharReaderBuilder readerBuilder;
|
||||||
|
return parse(readerBuilder, _input, _json, _errs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dev
|
@ -24,21 +24,28 @@
|
|||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
namespace dev
|
#include <string>
|
||||||
{
|
|
||||||
|
namespace dev {
|
||||||
|
|
||||||
/// Serialise the JSON object (@a _input) with indentation
|
/// Serialise the JSON object (@a _input) with indentation
|
||||||
inline std::string jsonPrettyPrint(Json::Value const& _input)
|
std::string jsonPrettyPrint(Json::Value const& _input);
|
||||||
{
|
|
||||||
return Json::StyledWriter().write(_input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialise the JSON object (@a _input) without indentation
|
/// Serialise the JSON object (@a _input) without indentation
|
||||||
inline std::string jsonCompactPrint(Json::Value const& _input)
|
std::string jsonCompactPrint(Json::Value const& _input);
|
||||||
{
|
|
||||||
Json::FastWriter writer;
|
/// Parse a JSON string (@a _input) with enabled strict-mode and writes resulting JSON object to (@a _json)
|
||||||
writer.omitEndingLineFeed();
|
/// \param _input JSON input string
|
||||||
return writer.write(_input);
|
/// \param _json [out] resulting JSON object
|
||||||
}
|
/// \param _errs [out] Formatted error messages
|
||||||
|
/// \return \c true if the document was successfully parsed, \c false if an error occurred.
|
||||||
|
bool jsonParseStrict(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr);
|
||||||
|
|
||||||
|
/// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json)
|
||||||
|
/// \param _input JSON input string
|
||||||
|
/// \param _json [out] resulting JSON object
|
||||||
|
/// \param _errs [out] Formatted error messages
|
||||||
|
/// \return \c true if the document was successfully parsed, \c false if an error occurred.
|
||||||
|
bool jsonParse(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -353,7 +353,7 @@ void Assembly::injectStart(AssemblyItem const& _i)
|
|||||||
m_items.insert(m_items.begin(), _i);
|
m_items.insert(m_items.begin(), _i);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
|
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
|
||||||
{
|
{
|
||||||
OptimiserSettings settings;
|
OptimiserSettings settings;
|
||||||
settings.isCreation = _isCreation;
|
settings.isCreation = _isCreation;
|
||||||
@ -365,6 +365,7 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
|
|||||||
settings.runCSE = true;
|
settings.runCSE = true;
|
||||||
settings.runConstantOptimiser = true;
|
settings.runConstantOptimiser = true;
|
||||||
}
|
}
|
||||||
|
settings.evmVersion = _evmVersion;
|
||||||
settings.expectedExecutionsPerDeployment = _runs;
|
settings.expectedExecutionsPerDeployment = _runs;
|
||||||
optimise(settings);
|
optimise(settings);
|
||||||
return *this;
|
return *this;
|
||||||
@ -482,6 +483,7 @@ map<u256, u256> Assembly::optimiseInternal(
|
|||||||
ConstantOptimisationMethod::optimiseConstants(
|
ConstantOptimisationMethod::optimiseConstants(
|
||||||
_settings.isCreation,
|
_settings.isCreation,
|
||||||
_settings.isCreation ? 1 : _settings.expectedExecutionsPerDeployment,
|
_settings.isCreation ? 1 : _settings.expectedExecutionsPerDeployment,
|
||||||
|
_settings.evmVersion,
|
||||||
*this,
|
*this,
|
||||||
m_items
|
m_items
|
||||||
);
|
);
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <libevmasm/LinkerObject.h>
|
#include <libevmasm/LinkerObject.h>
|
||||||
#include <libevmasm/Exceptions.h>
|
#include <libevmasm/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libdevcore/Assertions.h>
|
||||||
#include <libdevcore/SHA3.h>
|
#include <libdevcore/SHA3.h>
|
||||||
@ -107,6 +109,7 @@ public:
|
|||||||
bool runDeduplicate = false;
|
bool runDeduplicate = false;
|
||||||
bool runCSE = false;
|
bool runCSE = false;
|
||||||
bool runConstantOptimiser = false;
|
bool runConstantOptimiser = false;
|
||||||
|
solidity::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;
|
||||||
@ -120,7 +123,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, bool _isCreation = true, size_t _runs = 200);
|
Assembly& optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation = true, size_t _runs = 200);
|
||||||
|
|
||||||
/// Create a text representation of the assembly.
|
/// Create a text representation of the assembly.
|
||||||
std::string assemblyString(
|
std::string assemblyString(
|
||||||
|
@ -29,6 +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,
|
||||||
Assembly& _assembly,
|
Assembly& _assembly,
|
||||||
AssemblyItems& _items
|
AssemblyItems& _items
|
||||||
)
|
)
|
||||||
@ -48,6 +49,7 @@ unsigned ConstantOptimisationMethod::optimiseConstants(
|
|||||||
params.multiplicity = it.second;
|
params.multiplicity = it.second;
|
||||||
params.isCreation = _isCreation;
|
params.isCreation = _isCreation;
|
||||||
params.runs = _runs;
|
params.runs = _runs;
|
||||||
|
params.evmVersion = _evmVersion;
|
||||||
LiteralMethod lit(params, item.data());
|
LiteralMethod lit(params, item.data());
|
||||||
bigint literalGas = lit.gasNeeded();
|
bigint literalGas = lit.gasNeeded();
|
||||||
CodeCopyMethod copy(params, item.data());
|
CodeCopyMethod copy(params, item.data());
|
||||||
@ -80,7 +82,12 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
|
|||||||
if (item.type() == Push)
|
if (item.type() == Push)
|
||||||
gas += GasMeter::runGas(Instruction::PUSH1);
|
gas += GasMeter::runGas(Instruction::PUSH1);
|
||||||
else if (item.type() == Operation)
|
else if (item.type() == Operation)
|
||||||
gas += GasMeter::runGas(item.instruction());
|
{
|
||||||
|
if (item.instruction() == Instruction::EXP)
|
||||||
|
gas += GasCosts::expGas;
|
||||||
|
else
|
||||||
|
gas += GasMeter::runGas(item.instruction());
|
||||||
|
}
|
||||||
return gas;
|
return gas;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +293,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const
|
|||||||
{
|
{
|
||||||
size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP);
|
size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP);
|
||||||
return combineGas(
|
return combineGas(
|
||||||
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas),
|
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)),
|
||||||
// Data gas for routine: Some bytes are zero, but we ignore them.
|
// Data gas for routine: Some bytes are zero, but we ignore them.
|
||||||
bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas),
|
bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas),
|
||||||
0
|
0
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include <libevmasm/Exceptions.h>
|
#include <libevmasm/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libdevcore/Assertions.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
@ -50,6 +52,7 @@ public:
|
|||||||
static unsigned optimiseConstants(
|
static unsigned optimiseConstants(
|
||||||
bool _isCreation,
|
bool _isCreation,
|
||||||
size_t _runs,
|
size_t _runs,
|
||||||
|
solidity::EVMVersion _evmVersion,
|
||||||
Assembly& _assembly,
|
Assembly& _assembly,
|
||||||
AssemblyItems& _items
|
AssemblyItems& _items
|
||||||
);
|
);
|
||||||
@ -59,6 +62,7 @@ public:
|
|||||||
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
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
|
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
|
||||||
|
@ -61,7 +61,6 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
|||||||
case Operation:
|
case Operation:
|
||||||
{
|
{
|
||||||
ExpressionClasses& classes = m_state->expressionClasses();
|
ExpressionClasses& classes = m_state->expressionClasses();
|
||||||
gas = runGas(_item.instruction());
|
|
||||||
switch (_item.instruction())
|
switch (_item.instruction())
|
||||||
{
|
{
|
||||||
case Instruction::SSTORE:
|
case Instruction::SSTORE:
|
||||||
@ -72,26 +71,29 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
|||||||
m_state->storageContent().count(slot) &&
|
m_state->storageContent().count(slot) &&
|
||||||
classes.knownNonZero(m_state->storageContent().at(slot))
|
classes.knownNonZero(m_state->storageContent().at(slot))
|
||||||
))
|
))
|
||||||
gas += GasCosts::sstoreResetGas; //@todo take refunds into account
|
gas = GasCosts::sstoreResetGas; //@todo take refunds into account
|
||||||
else
|
else
|
||||||
gas += GasCosts::sstoreSetGas;
|
gas = GasCosts::sstoreSetGas;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Instruction::SLOAD:
|
case Instruction::SLOAD:
|
||||||
gas += GasCosts::sloadGas;
|
gas = GasCosts::sloadGas(m_evmVersion);
|
||||||
break;
|
break;
|
||||||
case Instruction::RETURN:
|
case Instruction::RETURN:
|
||||||
case Instruction::REVERT:
|
case Instruction::REVERT:
|
||||||
|
gas = runGas(_item.instruction());
|
||||||
gas += memoryGas(0, -1);
|
gas += memoryGas(0, -1);
|
||||||
break;
|
break;
|
||||||
case Instruction::MLOAD:
|
case Instruction::MLOAD:
|
||||||
case Instruction::MSTORE:
|
case Instruction::MSTORE:
|
||||||
|
gas = runGas(_item.instruction());
|
||||||
gas += memoryGas(classes.find(Instruction::ADD, {
|
gas += memoryGas(classes.find(Instruction::ADD, {
|
||||||
m_state->relativeStackElement(0),
|
m_state->relativeStackElement(0),
|
||||||
classes.find(AssemblyItem(32))
|
classes.find(AssemblyItem(32))
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
case Instruction::MSTORE8:
|
case Instruction::MSTORE8:
|
||||||
|
gas = runGas(_item.instruction());
|
||||||
gas += memoryGas(classes.find(Instruction::ADD, {
|
gas += memoryGas(classes.find(Instruction::ADD, {
|
||||||
m_state->relativeStackElement(0),
|
m_state->relativeStackElement(0),
|
||||||
classes.find(AssemblyItem(1))
|
classes.find(AssemblyItem(1))
|
||||||
@ -105,10 +107,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
|||||||
case Instruction::CALLDATACOPY:
|
case Instruction::CALLDATACOPY:
|
||||||
case Instruction::CODECOPY:
|
case Instruction::CODECOPY:
|
||||||
case Instruction::RETURNDATACOPY:
|
case Instruction::RETURNDATACOPY:
|
||||||
|
gas = runGas(_item.instruction());
|
||||||
gas += memoryGas(0, -2);
|
gas += memoryGas(0, -2);
|
||||||
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
|
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
|
||||||
break;
|
break;
|
||||||
|
case Instruction::EXTCODESIZE:
|
||||||
|
gas = GasCosts::extCodeGas(m_evmVersion);
|
||||||
|
break;
|
||||||
case Instruction::EXTCODECOPY:
|
case Instruction::EXTCODECOPY:
|
||||||
|
gas = GasCosts::extCodeGas(m_evmVersion);
|
||||||
gas += memoryGas(-1, -3);
|
gas += memoryGas(-1, -3);
|
||||||
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3));
|
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3));
|
||||||
break;
|
break;
|
||||||
@ -137,7 +144,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
|||||||
gas = GasConsumption::infinite();
|
gas = GasConsumption::infinite();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gas = GasCosts::callGas;
|
gas = GasCosts::callGas(m_evmVersion);
|
||||||
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0)))
|
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0)))
|
||||||
gas += (*value);
|
gas += (*value);
|
||||||
else
|
else
|
||||||
@ -155,7 +162,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Instruction::SELFDESTRUCT:
|
case Instruction::SELFDESTRUCT:
|
||||||
gas = GasCosts::selfdestructGas;
|
gas = GasCosts::selfdestructGas(m_evmVersion);
|
||||||
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
|
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
|
||||||
break;
|
break;
|
||||||
case Instruction::CREATE:
|
case Instruction::CREATE:
|
||||||
@ -172,11 +179,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
|||||||
case Instruction::EXP:
|
case Instruction::EXP:
|
||||||
gas = GasCosts::expGas;
|
gas = GasCosts::expGas;
|
||||||
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
|
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
|
||||||
gas += GasCosts::expByteGas * (32 - (h256(*value).firstBitSet() / 8));
|
gas += GasCosts::expByteGas(m_evmVersion) * (32 - (h256(*value).firstBitSet() / 8));
|
||||||
else
|
else
|
||||||
gas += GasCosts::expByteGas * 32;
|
gas += GasCosts::expByteGas(m_evmVersion) * 32;
|
||||||
|
break;
|
||||||
|
case Instruction::BALANCE:
|
||||||
|
gas = GasCosts::balanceGas(m_evmVersion);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
gas = runGas(_item.instruction());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -241,12 +252,9 @@ unsigned GasMeter::runGas(Instruction _instruction)
|
|||||||
case Tier::Mid: return GasCosts::tier4Gas;
|
case Tier::Mid: return GasCosts::tier4Gas;
|
||||||
case Tier::High: return GasCosts::tier5Gas;
|
case Tier::High: return GasCosts::tier5Gas;
|
||||||
case Tier::Ext: return GasCosts::tier6Gas;
|
case Tier::Ext: return GasCosts::tier6Gas;
|
||||||
case Tier::Special: return GasCosts::tier7Gas;
|
|
||||||
case Tier::ExtCode: return GasCosts::extCodeGas;
|
|
||||||
case Tier::Balance: return GasCosts::balanceGas;
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
assertThrow(false, OptimizerException, "Invalid gas tier.");
|
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,11 +21,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <tuple>
|
|
||||||
#include <libevmasm/ExpressionClasses.h>
|
#include <libevmasm/ExpressionClasses.h>
|
||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
namespace eth
|
namespace eth
|
||||||
@ -44,13 +47,25 @@ 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;
|
||||||
static unsigned const extCodeGas = 700;
|
inline unsigned extCodeGas(EVMVersion _evmVersion)
|
||||||
static unsigned const balanceGas = 400;
|
{
|
||||||
|
return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 20;
|
||||||
|
}
|
||||||
|
inline unsigned balanceGas(EVMVersion _evmVersion)
|
||||||
|
{
|
||||||
|
return _evmVersion >= EVMVersion::tangerineWhistle() ? 400 : 20;
|
||||||
|
}
|
||||||
static unsigned const expGas = 10;
|
static unsigned const expGas = 10;
|
||||||
static unsigned const expByteGas = 50;
|
inline unsigned expByteGas(EVMVersion _evmVersion)
|
||||||
|
{
|
||||||
|
return _evmVersion >= EVMVersion::spuriousDragon() ? 50 : 10;
|
||||||
|
}
|
||||||
static unsigned const keccak256Gas = 30;
|
static unsigned const keccak256Gas = 30;
|
||||||
static unsigned const keccak256WordGas = 6;
|
static unsigned const keccak256WordGas = 6;
|
||||||
static unsigned const sloadGas = 200;
|
inline unsigned sloadGas(EVMVersion _evmVersion)
|
||||||
|
{
|
||||||
|
return _evmVersion >= EVMVersion::tangerineWhistle() ? 200 : 50;
|
||||||
|
}
|
||||||
static unsigned const sstoreSetGas = 20000;
|
static unsigned const sstoreSetGas = 20000;
|
||||||
static unsigned const sstoreResetGas = 5000;
|
static unsigned const sstoreResetGas = 5000;
|
||||||
static unsigned const sstoreRefundGas = 15000;
|
static unsigned const sstoreRefundGas = 15000;
|
||||||
@ -59,11 +74,17 @@ 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;
|
||||||
static unsigned const callGas = 700;
|
inline unsigned callGas(EVMVersion _evmVersion)
|
||||||
|
{
|
||||||
|
return _evmVersion >= 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;
|
||||||
static unsigned const selfdestructGas = 5000;
|
inline unsigned selfdestructGas(EVMVersion _evmVersion)
|
||||||
|
{
|
||||||
|
return _evmVersion >= EVMVersion::tangerineWhistle() ? 5000 : 0;
|
||||||
|
}
|
||||||
static unsigned const selfdestructRefundGas = 24000;
|
static unsigned const selfdestructRefundGas = 24000;
|
||||||
static unsigned const memoryGas = 3;
|
static unsigned const memoryGas = 3;
|
||||||
static unsigned const quadCoeffDiv = 512;
|
static unsigned const quadCoeffDiv = 512;
|
||||||
@ -100,8 +121,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Constructs a new gas meter given the current state.
|
/// Constructs a new gas meter given the current state.
|
||||||
explicit GasMeter(std::shared_ptr<KnownState> const& _state, u256 const& _largestMemoryAccess = 0):
|
GasMeter(std::shared_ptr<KnownState> const& _state, solidity::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0):
|
||||||
m_state(_state), 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
|
||||||
/// the state.
|
/// the state.
|
||||||
@ -110,6 +131,8 @@ public:
|
|||||||
|
|
||||||
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
|
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
|
||||||
|
|
||||||
|
/// @returns gas costs for simple instructions with constant gas costs (that do not
|
||||||
|
/// change with EVM versions)
|
||||||
static unsigned runGas(Instruction _instruction);
|
static unsigned runGas(Instruction _instruction);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -123,6 +146,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;
|
||||||
/// 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;
|
||||||
};
|
};
|
||||||
|
@ -50,6 +50,9 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
|
|||||||
{ "OR", Instruction::OR },
|
{ "OR", Instruction::OR },
|
||||||
{ "XOR", Instruction::XOR },
|
{ "XOR", Instruction::XOR },
|
||||||
{ "BYTE", Instruction::BYTE },
|
{ "BYTE", Instruction::BYTE },
|
||||||
|
{ "SHL", Instruction::SHL },
|
||||||
|
{ "SHR", Instruction::SHR },
|
||||||
|
{ "SAR", Instruction::SAR },
|
||||||
{ "ADDMOD", Instruction::ADDMOD },
|
{ "ADDMOD", Instruction::ADDMOD },
|
||||||
{ "MULMOD", Instruction::MULMOD },
|
{ "MULMOD", Instruction::MULMOD },
|
||||||
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
|
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
|
||||||
@ -190,6 +193,9 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
|
|||||||
{ Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLow } },
|
{ Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLow } },
|
||||||
{ Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLow } },
|
{ Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLow } },
|
||||||
{ Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLow } },
|
{ Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLow } },
|
||||||
|
{ Instruction::SHL, { "SHL", 0, 2, 1, false, Tier::VeryLow } },
|
||||||
|
{ Instruction::SHR, { "SHR", 0, 2, 1, false, Tier::VeryLow } },
|
||||||
|
{ Instruction::SAR, { "SAR", 0, 2, 1, false, Tier::VeryLow } },
|
||||||
{ Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } },
|
{ Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } },
|
||||||
{ Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } },
|
{ Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } },
|
||||||
{ Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } },
|
{ Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } },
|
||||||
|
@ -59,8 +59,11 @@ enum class Instruction: uint8_t
|
|||||||
AND, ///< bitwise AND operation
|
AND, ///< bitwise AND operation
|
||||||
OR, ///< bitwise OR operation
|
OR, ///< bitwise OR operation
|
||||||
XOR, ///< bitwise XOR operation
|
XOR, ///< bitwise XOR operation
|
||||||
NOT, ///< bitwise NOT opertation
|
NOT, ///< bitwise NOT operation
|
||||||
BYTE, ///< retrieve single byte from word
|
BYTE, ///< retrieve single byte from word
|
||||||
|
SHL, ///< bitwise SHL operation
|
||||||
|
SHR, ///< bitwise SHR operation
|
||||||
|
SAR, ///< bitwise SAR operation
|
||||||
|
|
||||||
KECCAK256 = 0x20, ///< compute KECCAK-256 hash
|
KECCAK256 = 0x20, ///< compute KECCAK-256 hash
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ using namespace std;
|
|||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
|
|
||||||
PathGasMeter::PathGasMeter(AssemblyItems const& _items):
|
PathGasMeter::PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion):
|
||||||
m_items(_items)
|
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)
|
||||||
if (m_items[i].type() == Tag)
|
if (m_items[i].type() == Tag)
|
||||||
@ -59,7 +59,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
|
|||||||
m_queue.pop_back();
|
m_queue.pop_back();
|
||||||
|
|
||||||
shared_ptr<KnownState> state = path->state;
|
shared_ptr<KnownState> state = path->state;
|
||||||
GasMeter meter(state, path->largestMemoryAccess);
|
GasMeter meter(state, m_evmVersion, path->largestMemoryAccess);
|
||||||
ExpressionClasses& classes = state->expressionClasses();
|
ExpressionClasses& classes = state->expressionClasses();
|
||||||
GasMeter::GasConsumption gas = path->gas;
|
GasMeter::GasConsumption gas = path->gas;
|
||||||
size_t index = path->index;
|
size_t index = path->index;
|
||||||
|
@ -21,10 +21,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libevmasm/GasMeter.h>
|
||||||
|
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <libevmasm/GasMeter.h>
|
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -50,7 +53,7 @@ struct GasPath
|
|||||||
class PathGasMeter
|
class PathGasMeter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PathGasMeter(AssemblyItems const& _items);
|
explicit PathGasMeter(AssemblyItems const& _items, solidity::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);
|
||||||
|
|
||||||
@ -60,6 +63,7 @@ private:
|
|||||||
std::vector<std::unique_ptr<GasPath>> m_queue;
|
std::vector<std::unique_ptr<GasPath>> m_queue;
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -522,7 +522,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap)
|
int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const
|
||||||
{
|
{
|
||||||
solAssert(m_context->variableStackHeights.count(&_var), "");
|
solAssert(m_context->variableStackHeights.count(&_var), "");
|
||||||
int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var];
|
int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var];
|
||||||
@ -537,12 +537,12 @@ int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const&
|
|||||||
return heightDiff;
|
return heightDiff;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeTransform::expectDeposit(int _deposit, int _oldHeight)
|
void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const
|
||||||
{
|
{
|
||||||
solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit.");
|
solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeTransform::checkStackHeight(void const* _astElement)
|
void CodeTransform::checkStackHeight(void const* _astElement) const
|
||||||
{
|
{
|
||||||
solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found.");
|
solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found.");
|
||||||
solAssert(
|
solAssert(
|
||||||
|
@ -133,11 +133,11 @@ private:
|
|||||||
/// Determines the stack height difference to the given variables. Throws
|
/// Determines the stack height difference to the given variables. Throws
|
||||||
/// if it is not yet in scope or the height difference is too large. Returns
|
/// if it is not yet in scope or the height difference is too large. Returns
|
||||||
/// the (positive) stack height difference otherwise.
|
/// the (positive) stack height difference otherwise.
|
||||||
int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap);
|
int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const;
|
||||||
|
|
||||||
void expectDeposit(int _deposit, int _oldHeight);
|
void expectDeposit(int _deposit, int _oldHeight) const;
|
||||||
|
|
||||||
void checkStackHeight(void const* _astElement);
|
void checkStackHeight(void const* _astElement) const;
|
||||||
|
|
||||||
julia::AbstractAssembly& m_assembly;
|
julia::AbstractAssembly& m_assembly;
|
||||||
solidity::assembly::AsmAnalysisInfo& m_info;
|
solidity::assembly::AsmAnalysisInfo& m_info;
|
||||||
|
@ -41,7 +41,7 @@ SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch(Expressio
|
|||||||
|
|
||||||
static SimplificationRules rules;
|
static SimplificationRules rules;
|
||||||
|
|
||||||
FunctionalInstruction const& instruction = boost::get<FunctionalInstruction const&>(_expr);
|
FunctionalInstruction const& instruction = boost::get<FunctionalInstruction>(_expr);
|
||||||
for (auto const& rule: rules.m_rules[byte(instruction.instruction)])
|
for (auto const& rule: rules.m_rules[byte(instruction.instruction)])
|
||||||
{
|
{
|
||||||
rules.resetMatchGroups();
|
rules.resetMatchGroups();
|
||||||
@ -100,7 +100,7 @@ bool Pattern::matches(Expression const& _expr) const
|
|||||||
{
|
{
|
||||||
if (_expr.type() != typeid(Literal))
|
if (_expr.type() != typeid(Literal))
|
||||||
return false;
|
return false;
|
||||||
Literal const& literal = boost::get<Literal const&>(_expr);
|
Literal const& literal = boost::get<Literal>(_expr);
|
||||||
if (literal.kind != assembly::LiteralKind::Number)
|
if (literal.kind != assembly::LiteralKind::Number)
|
||||||
return false;
|
return false;
|
||||||
if (m_data && *m_data != u256(literal.value))
|
if (m_data && *m_data != u256(literal.value))
|
||||||
@ -111,7 +111,7 @@ bool Pattern::matches(Expression const& _expr) const
|
|||||||
{
|
{
|
||||||
if (_expr.type() != typeid(FunctionalInstruction))
|
if (_expr.type() != typeid(FunctionalInstruction))
|
||||||
return false;
|
return false;
|
||||||
FunctionalInstruction const& instr = boost::get<FunctionalInstruction const&>(_expr);
|
FunctionalInstruction const& instr = boost::get<FunctionalInstruction>(_expr);
|
||||||
if (m_instruction != instr.instruction)
|
if (m_instruction != instr.instruction)
|
||||||
return false;
|
return false;
|
||||||
assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, "");
|
assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, "");
|
||||||
@ -168,7 +168,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const
|
|||||||
|
|
||||||
u256 Pattern::d() const
|
u256 Pattern::d() const
|
||||||
{
|
{
|
||||||
Literal const& literal = boost::get<Literal const&>(matchGroupValue());
|
Literal const& literal = boost::get<Literal>(matchGroupValue());
|
||||||
assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, "");
|
assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, "");
|
||||||
return u256(literal.value);
|
return u256(literal.value);
|
||||||
}
|
}
|
||||||
|
@ -19,17 +19,16 @@
|
|||||||
* @date 2014
|
* @date 2014
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Compiler.h"
|
#include <liblll/Compiler.h>
|
||||||
#include "Parser.h"
|
#include <liblll/Parser.h>
|
||||||
#include "CompilerState.h"
|
#include <liblll/CompilerState.h>
|
||||||
#include "CodeFragment.h"
|
#include <liblll/CodeFragment.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
|
|
||||||
|
bytes dev::eth::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, dev::eth::ReadCallback const& _readFile)
|
||||||
bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _errors, ReadCallback const& _readFile)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -37,7 +36,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error
|
|||||||
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);
|
assembly = assembly.optimise(true, _evmVersion);
|
||||||
bytes ret = assembly.assemble().bytecode;
|
bytes ret = assembly.assemble().bytecode;
|
||||||
for (auto i: cs.treesToKill)
|
for (auto i: cs.treesToKill)
|
||||||
killBigints(i);
|
killBigints(i);
|
||||||
@ -67,7 +66,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error
|
|||||||
return bytes();
|
return bytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
|
std::string dev::eth::compileLLLToAsm(std::string const& _src, EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -75,7 +74,7 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v
|
|||||||
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);
|
assembly = assembly.optimise(true, _evmVersion);
|
||||||
string ret = assembly.assemblyString();
|
string ret = assembly.assemblyString();
|
||||||
for (auto i: cs.treesToKill)
|
for (auto i: cs.treesToKill)
|
||||||
killBigints(i);
|
killBigints(i);
|
||||||
|
@ -21,9 +21,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -33,8 +36,8 @@ namespace eth
|
|||||||
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, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
|
std::string compileLLLToAsm(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, 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());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
|||||||
{
|
{
|
||||||
Json::Value contractInput = ret["contracts"][sourceName][contractName];
|
Json::Value contractInput = ret["contracts"][sourceName][contractName];
|
||||||
Json::Value contractOutput = Json::objectValue;
|
Json::Value contractOutput = Json::objectValue;
|
||||||
contractOutput["interface"] = dev::jsonCompactPrint(contractInput["abi"]);
|
contractOutput["interface"] = jsonCompactPrint(contractInput["abi"]);
|
||||||
contractOutput["metadata"] = contractInput["metadata"];
|
contractOutput["metadata"] = contractInput["metadata"];
|
||||||
contractOutput["functionHashes"] = contractInput["evm"]["methodIdentifiers"];
|
contractOutput["functionHashes"] = contractInput["evm"]["methodIdentifiers"];
|
||||||
contractOutput["gasEstimates"] = translateGasEstimates(contractInput["evm"]["gasEstimates"]);
|
contractOutput["gasEstimates"] = translateGasEstimates(contractInput["evm"]["gasEstimates"]);
|
||||||
@ -219,7 +219,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return dev::jsonCompactPrint(output);
|
return jsonCompactPrint(output);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
@ -229,15 +229,15 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
|||||||
|
|
||||||
string compileMulti(string const& _input, bool _optimize, CStyleReadFileCallback _readCallback = nullptr)
|
string compileMulti(string const& _input, bool _optimize, CStyleReadFileCallback _readCallback = nullptr)
|
||||||
{
|
{
|
||||||
Json::Reader reader;
|
string errors;
|
||||||
Json::Value input;
|
Json::Value input;
|
||||||
if (!reader.parse(_input, input, false))
|
if (!jsonParseStrict(_input, input, &errors))
|
||||||
{
|
{
|
||||||
Json::Value errors(Json::arrayValue);
|
Json::Value jsonErrors(Json::arrayValue);
|
||||||
errors.append("Error parsing input JSON: " + reader.getFormattedErrorMessages());
|
jsonErrors.append("Error parsing input JSON: " + errors);
|
||||||
Json::Value output(Json::objectValue);
|
Json::Value output(Json::objectValue);
|
||||||
output["errors"] = errors;
|
output["errors"] = jsonErrors;
|
||||||
return dev::jsonCompactPrint(output);
|
return jsonCompactPrint(output);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -79,6 +79,17 @@ Declaration const* DeclarationContainer::conflictingDeclaration(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeclarationContainer::activateVariable(ASTString const& _name)
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
m_invisibleDeclarations.count(_name) && m_invisibleDeclarations.at(_name).size() == 1,
|
||||||
|
"Tried to activate a non-inactive variable or multiple inactive variables with the same name."
|
||||||
|
);
|
||||||
|
solAssert(m_declarations.count(_name) == 0 || m_declarations.at(_name).empty(), "");
|
||||||
|
m_declarations[_name].emplace_back(m_invisibleDeclarations.at(_name).front());
|
||||||
|
m_invisibleDeclarations.erase(_name);
|
||||||
|
}
|
||||||
|
|
||||||
bool DeclarationContainer::registerDeclaration(
|
bool DeclarationContainer::registerDeclaration(
|
||||||
Declaration const& _declaration,
|
Declaration const& _declaration,
|
||||||
ASTString const* _name,
|
ASTString const* _name,
|
||||||
@ -106,15 +117,17 @@ bool DeclarationContainer::registerDeclaration(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
|
vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const
|
||||||
{
|
{
|
||||||
solAssert(!_name.empty(), "Attempt to resolve empty name.");
|
solAssert(!_name.empty(), "Attempt to resolve empty name.");
|
||||||
auto result = m_declarations.find(_name);
|
vector<Declaration const*> result;
|
||||||
if (result != m_declarations.end())
|
if (m_declarations.count(_name))
|
||||||
return result->second;
|
result = m_declarations.at(_name);
|
||||||
if (_recursive && m_enclosingContainer)
|
if (_alsoInvisible && m_invisibleDeclarations.count(_name))
|
||||||
return m_enclosingContainer->resolveName(_name, true);
|
result += m_invisibleDeclarations.at(_name);
|
||||||
return vector<Declaration const*>({});
|
if (result.empty() && _recursive && m_enclosingContainer)
|
||||||
|
result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
|
vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
|
||||||
@ -129,6 +142,12 @@ vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) con
|
|||||||
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
|
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
|
||||||
similar.push_back(declarationName);
|
similar.push_back(declarationName);
|
||||||
}
|
}
|
||||||
|
for (auto const& declaration: m_invisibleDeclarations)
|
||||||
|
{
|
||||||
|
string const& declarationName = declaration.first;
|
||||||
|
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
|
||||||
|
similar.push_back(declarationName);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_enclosingContainer)
|
if (m_enclosingContainer)
|
||||||
similar += m_enclosingContainer->similarNames(_name);
|
similar += m_enclosingContainer->similarNames(_name);
|
||||||
|
@ -51,13 +51,17 @@ public:
|
|||||||
/// @param _update if true, replaces a potential declaration that is already present
|
/// @param _update if true, replaces a potential declaration that is already present
|
||||||
/// @returns false if the name was already declared.
|
/// @returns false if the name was already declared.
|
||||||
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
|
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
|
||||||
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
|
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const;
|
||||||
ASTNode const* enclosingNode() const { return m_enclosingNode; }
|
ASTNode const* enclosingNode() const { return m_enclosingNode; }
|
||||||
DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
|
DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
|
||||||
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
|
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
|
||||||
/// @returns whether declaration is valid, and if not also returns previous declaration.
|
/// @returns whether declaration is valid, and if not also returns previous declaration.
|
||||||
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
|
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
|
||||||
|
|
||||||
|
/// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for
|
||||||
|
/// VariableDeclarationStatements.
|
||||||
|
void activateVariable(ASTString const& _name);
|
||||||
|
|
||||||
/// @returns existing declaration names similar to @a _name.
|
/// @returns existing declaration names similar to @a _name.
|
||||||
/// Searches this and all parent containers.
|
/// Searches this and all parent containers.
|
||||||
std::vector<ASTString> similarNames(ASTString const& _name) const;
|
std::vector<ASTString> similarNames(ASTString const& _name) const;
|
||||||
|
@ -39,6 +39,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
|
|||||||
make_shared<MagicVariableDeclaration>("assert", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)),
|
make_shared<MagicVariableDeclaration>("assert", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)),
|
||||||
make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
|
make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
|
||||||
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
|
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
|
||||||
|
make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
|
||||||
make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)),
|
make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)),
|
||||||
make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)),
|
make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)),
|
||||||
make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)),
|
make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)),
|
||||||
|
@ -50,12 +50,13 @@ NameAndTypeResolver::NameAndTypeResolver(
|
|||||||
m_scopes[nullptr]->registerDeclaration(*declaration);
|
m_scopes[nullptr]->registerDeclaration(*declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope)
|
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope)
|
||||||
{
|
{
|
||||||
|
bool useC99Scoping = _sourceUnit.annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
// The helper registers all declarations in m_scopes as a side-effect of its construction.
|
// The helper registers all declarations in m_scopes as a side-effect of its construction.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope);
|
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, useC99Scoping, m_errorReporter, _currentScope);
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
catch (FatalError const&)
|
||||||
{
|
{
|
||||||
@ -106,7 +107,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
|||||||
else
|
else
|
||||||
for (Declaration const* declaration: declarations)
|
for (Declaration const* declaration: declarations)
|
||||||
if (!DeclarationRegistrationHelper::registerDeclaration(
|
if (!DeclarationRegistrationHelper::registerDeclaration(
|
||||||
target, *declaration, alias.second.get(), &imp->location(), true, m_errorReporter
|
target, *declaration, alias.second.get(), &imp->location(), true, false, m_errorReporter
|
||||||
))
|
))
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
@ -114,7 +115,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
|||||||
for (auto const& nameAndDeclaration: scope->second->declarations())
|
for (auto const& nameAndDeclaration: scope->second->declarations())
|
||||||
for (auto const& declaration: nameAndDeclaration.second)
|
for (auto const& declaration: nameAndDeclaration.second)
|
||||||
if (!DeclarationRegistrationHelper::registerDeclaration(
|
if (!DeclarationRegistrationHelper::registerDeclaration(
|
||||||
target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter
|
target, *declaration, &nameAndDeclaration.first, &imp->location(), true, false, m_errorReporter
|
||||||
))
|
))
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
@ -151,6 +152,12 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NameAndTypeResolver::activateVariable(string const& _name)
|
||||||
|
{
|
||||||
|
solAssert(m_currentScope, "");
|
||||||
|
m_currentScope->activateVariable(_name);
|
||||||
|
}
|
||||||
|
|
||||||
vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const
|
vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const
|
||||||
{
|
{
|
||||||
auto iterator = m_scopes.find(_scope);
|
auto iterator = m_scopes.find(_scope);
|
||||||
@ -159,15 +166,15 @@ vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _na
|
|||||||
return iterator->second->resolveName(_name, false);
|
return iterator->second->resolveName(_name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const
|
vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles) const
|
||||||
{
|
{
|
||||||
return m_currentScope->resolveName(_name, _recursive);
|
return m_currentScope->resolveName(_name, true, _includeInvisibles);
|
||||||
}
|
}
|
||||||
|
|
||||||
Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path, bool _recursive) const
|
Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path) const
|
||||||
{
|
{
|
||||||
solAssert(!_path.empty(), "");
|
solAssert(!_path.empty(), "");
|
||||||
vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), _recursive);
|
vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), true);
|
||||||
for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
|
for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
|
||||||
{
|
{
|
||||||
if (!m_scopes.count(candidates.front()))
|
if (!m_scopes.count(candidates.front()))
|
||||||
@ -229,7 +236,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
|||||||
for (auto const& instruction: c_instructions)
|
for (auto const& instruction: c_instructions)
|
||||||
{
|
{
|
||||||
string const instructionName{boost::algorithm::to_lower_copy(instruction.first)};
|
string const instructionName{boost::algorithm::to_lower_copy(instruction.first)};
|
||||||
auto declarations = nameFromCurrentScope(instructionName);
|
auto declarations = nameFromCurrentScope(instructionName, true);
|
||||||
for (Declaration const* const declaration: declarations)
|
for (Declaration const* const declaration: declarations)
|
||||||
{
|
{
|
||||||
solAssert(!!declaration, "");
|
solAssert(!!declaration, "");
|
||||||
@ -244,19 +251,24 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NameAndTypeResolver::setScope(ASTNode const* _node)
|
||||||
|
{
|
||||||
|
m_currentScope = m_scopes[_node].get();
|
||||||
|
}
|
||||||
|
|
||||||
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
|
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
|
||||||
{
|
{
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
m_currentScope = m_scopes[contract->scope()].get();
|
setScope(contract->scope());
|
||||||
solAssert(!!m_currentScope, "");
|
solAssert(!!m_currentScope, "");
|
||||||
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
|
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
|
||||||
if (!resolveNamesAndTypes(*baseContract, true))
|
if (!resolveNamesAndTypes(*baseContract, true))
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
m_currentScope = m_scopes[contract].get();
|
setScope(contract);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
@ -273,7 +285,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
|||||||
// these can contain code, only resolve parameters for now
|
// these can contain code, only resolve parameters for now
|
||||||
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
||||||
{
|
{
|
||||||
m_currentScope = m_scopes[contract].get();
|
setScope(contract);
|
||||||
if (!resolveNamesAndTypes(*node, false))
|
if (!resolveNamesAndTypes(*node, false))
|
||||||
{
|
{
|
||||||
success = false;
|
success = false;
|
||||||
@ -287,12 +299,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
|||||||
if (!_resolveInsideCode)
|
if (!_resolveInsideCode)
|
||||||
return success;
|
return success;
|
||||||
|
|
||||||
m_currentScope = m_scopes[contract].get();
|
setScope(contract);
|
||||||
|
|
||||||
// now resolve references inside the code
|
// now resolve references inside the code
|
||||||
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
||||||
{
|
{
|
||||||
m_currentScope = m_scopes[contract].get();
|
setScope(contract);
|
||||||
if (!resolveNamesAndTypes(*node, true))
|
if (!resolveNamesAndTypes(*node, true))
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
@ -301,7 +313,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_scopes.count(&_node))
|
if (m_scopes.count(&_node))
|
||||||
m_currentScope = m_scopes[&_node].get();
|
setScope(&_node);
|
||||||
return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
|
return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -434,9 +446,11 @@ string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const
|
|||||||
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
||||||
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
|
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
|
||||||
ASTNode& _astRoot,
|
ASTNode& _astRoot,
|
||||||
|
bool _useC99Scoping,
|
||||||
ErrorReporter& _errorReporter,
|
ErrorReporter& _errorReporter,
|
||||||
ASTNode const* _currentScope
|
ASTNode const* _currentScope
|
||||||
):
|
):
|
||||||
|
m_useC99Scoping(_useC99Scoping),
|
||||||
m_scopes(_scopes),
|
m_scopes(_scopes),
|
||||||
m_currentScope(_currentScope),
|
m_currentScope(_currentScope),
|
||||||
m_errorReporter(_errorReporter)
|
m_errorReporter(_errorReporter)
|
||||||
@ -451,18 +465,23 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
|||||||
string const* _name,
|
string const* _name,
|
||||||
SourceLocation const* _errorLocation,
|
SourceLocation const* _errorLocation,
|
||||||
bool _warnOnShadow,
|
bool _warnOnShadow,
|
||||||
|
bool _inactive,
|
||||||
ErrorReporter& _errorReporter
|
ErrorReporter& _errorReporter
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!_errorLocation)
|
if (!_errorLocation)
|
||||||
_errorLocation = &_declaration.location();
|
_errorLocation = &_declaration.location();
|
||||||
|
|
||||||
|
string name = _name ? *_name : _declaration.name();
|
||||||
Declaration const* shadowedDeclaration = nullptr;
|
Declaration const* shadowedDeclaration = nullptr;
|
||||||
if (_warnOnShadow && !_declaration.name().empty() && _container.enclosingContainer())
|
if (_warnOnShadow && !name.empty() && _container.enclosingContainer())
|
||||||
for (auto const* decl: _container.enclosingContainer()->resolveName(_declaration.name(), true))
|
for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true))
|
||||||
shadowedDeclaration = decl;
|
shadowedDeclaration = decl;
|
||||||
|
|
||||||
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
|
// We use "invisible" for both inactive variables in blocks and for members invisible in contracts.
|
||||||
|
// They cannot both be true at the same time.
|
||||||
|
solAssert(!(_inactive && !_declaration.isVisibleInContract()), "");
|
||||||
|
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract() || _inactive))
|
||||||
{
|
{
|
||||||
SourceLocation firstDeclarationLocation;
|
SourceLocation firstDeclarationLocation;
|
||||||
SourceLocation secondDeclarationLocation;
|
SourceLocation secondDeclarationLocation;
|
||||||
@ -604,6 +623,34 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
|
|||||||
closeCurrentScope();
|
closeCurrentScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeclarationRegistrationHelper::visit(Block& _block)
|
||||||
|
{
|
||||||
|
_block.setScope(m_currentScope);
|
||||||
|
if (m_useC99Scoping)
|
||||||
|
enterNewSubScope(_block);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeclarationRegistrationHelper::endVisit(Block&)
|
||||||
|
{
|
||||||
|
if (m_useC99Scoping)
|
||||||
|
closeCurrentScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeclarationRegistrationHelper::visit(ForStatement& _for)
|
||||||
|
{
|
||||||
|
_for.setScope(m_currentScope);
|
||||||
|
if (m_useC99Scoping)
|
||||||
|
enterNewSubScope(_for);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeclarationRegistrationHelper::endVisit(ForStatement&)
|
||||||
|
{
|
||||||
|
if (m_useC99Scoping)
|
||||||
|
closeCurrentScope();
|
||||||
|
}
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
|
void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
|
||||||
{
|
{
|
||||||
// Register the local variables with the function
|
// Register the local variables with the function
|
||||||
@ -631,14 +678,14 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&)
|
|||||||
closeCurrentScope();
|
closeCurrentScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
|
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope)
|
||||||
{
|
{
|
||||||
map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
|
map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
|
||||||
bool newlyAdded;
|
bool newlyAdded;
|
||||||
shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
|
shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
|
||||||
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container));
|
tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container));
|
||||||
solAssert(newlyAdded, "Unable to add new scope.");
|
solAssert(newlyAdded, "Unable to add new scope.");
|
||||||
m_currentScope = &_declaration;
|
m_currentScope = &_subScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::closeCurrentScope()
|
void DeclarationRegistrationHelper::closeCurrentScope()
|
||||||
@ -666,7 +713,12 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
|
|||||||
if (fun->isConstructor())
|
if (fun->isConstructor())
|
||||||
warnAboutShadowing = false;
|
warnAboutShadowing = false;
|
||||||
|
|
||||||
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter);
|
// Register declaration as inactive if we are in block scope and C99 mode.
|
||||||
|
bool inactive =
|
||||||
|
m_useC99Scoping &&
|
||||||
|
(dynamic_cast<Block const*>(m_currentScope) || dynamic_cast<ForStatement const*>(m_currentScope));
|
||||||
|
|
||||||
|
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter);
|
||||||
|
|
||||||
_declaration.setScope(m_currentScope);
|
_declaration.setScope(m_currentScope);
|
||||||
if (_opensScope)
|
if (_opensScope)
|
||||||
|
@ -56,7 +56,7 @@ public:
|
|||||||
/// @returns false in case of error.
|
/// @returns false in case of error.
|
||||||
/// @param _currentScope should be nullptr but can be used to inject new declarations into
|
/// @param _currentScope should be nullptr but can be used to inject new declarations into
|
||||||
/// existing scopes, used by the snippets feature.
|
/// existing scopes, used by the snippets feature.
|
||||||
bool registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope = nullptr);
|
bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr);
|
||||||
/// Applies the effect of import directives.
|
/// Applies the effect of import directives.
|
||||||
bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
|
bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
|
||||||
/// Resolves all names and types referenced from the given AST Node.
|
/// Resolves all names and types referenced from the given AST Node.
|
||||||
@ -69,20 +69,24 @@ public:
|
|||||||
/// that create their own scope.
|
/// that create their own scope.
|
||||||
/// @returns false in case of error.
|
/// @returns false in case of error.
|
||||||
bool updateDeclaration(Declaration const& _declaration);
|
bool updateDeclaration(Declaration const& _declaration);
|
||||||
|
/// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for
|
||||||
|
/// VariableDeclarationStatements.
|
||||||
|
void activateVariable(std::string const& _name);
|
||||||
|
|
||||||
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
|
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
|
||||||
/// the global scope is used (i.e. the one containing only the pre-defined global variables).
|
/// the global scope is used (i.e. the one containing only the pre-defined global variables).
|
||||||
/// @returns a pointer to the declaration on success or nullptr on failure.
|
/// @returns a pointer to the declaration on success or nullptr on failure.
|
||||||
|
/// SHOULD only be used for testing.
|
||||||
std::vector<Declaration const*> resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const;
|
std::vector<Declaration const*> resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const;
|
||||||
|
|
||||||
/// Resolves a name in the "current" scope. Should only be called during the initial
|
/// Resolves a name in the "current" scope, but also searches parent scopes.
|
||||||
/// resolving phase.
|
/// Should only be called during the initial resolving phase.
|
||||||
std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const;
|
std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles = false) const;
|
||||||
|
|
||||||
/// Resolves a path starting from the "current" scope. Should only be called during the initial
|
/// Resolves a path starting from the "current" scope, but also searches parent scopes.
|
||||||
/// resolving phase.
|
/// Should only be called during the initial resolving phase.
|
||||||
/// @note Returns a null pointer if any component in the path was not unique or not found.
|
/// @note Returns a null pointer if any component in the path was not unique or not found.
|
||||||
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path, bool _recursive = true) const;
|
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const;
|
||||||
|
|
||||||
/// returns the vector of declarations without repetitions
|
/// returns the vector of declarations without repetitions
|
||||||
std::vector<Declaration const*> cleanedDeclarations(
|
std::vector<Declaration const*> cleanedDeclarations(
|
||||||
@ -96,6 +100,9 @@ public:
|
|||||||
/// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions.
|
/// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions.
|
||||||
std::string similarNameSuggestions(ASTString const& _name) const;
|
std::string similarNameSuggestions(ASTString const& _name) const;
|
||||||
|
|
||||||
|
/// Sets the current scope.
|
||||||
|
void setScope(ASTNode const* _node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
|
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
|
||||||
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
|
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
|
||||||
@ -135,6 +142,7 @@ public:
|
|||||||
DeclarationRegistrationHelper(
|
DeclarationRegistrationHelper(
|
||||||
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
|
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
|
||||||
ASTNode& _astRoot,
|
ASTNode& _astRoot,
|
||||||
|
bool _useC99Scoping,
|
||||||
ErrorReporter& _errorReporter,
|
ErrorReporter& _errorReporter,
|
||||||
ASTNode const* _currentScope = nullptr
|
ASTNode const* _currentScope = nullptr
|
||||||
);
|
);
|
||||||
@ -145,6 +153,7 @@ public:
|
|||||||
std::string const* _name,
|
std::string const* _name,
|
||||||
SourceLocation const* _errorLocation,
|
SourceLocation const* _errorLocation,
|
||||||
bool _warnOnShadow,
|
bool _warnOnShadow,
|
||||||
|
bool _inactive,
|
||||||
ErrorReporter& _errorReporter
|
ErrorReporter& _errorReporter
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -163,12 +172,16 @@ private:
|
|||||||
void endVisit(FunctionDefinition& _function) override;
|
void endVisit(FunctionDefinition& _function) override;
|
||||||
bool visit(ModifierDefinition& _modifier) override;
|
bool visit(ModifierDefinition& _modifier) override;
|
||||||
void endVisit(ModifierDefinition& _modifier) override;
|
void endVisit(ModifierDefinition& _modifier) override;
|
||||||
|
bool visit(Block& _block) override;
|
||||||
|
void endVisit(Block& _block) override;
|
||||||
|
bool visit(ForStatement& _forLoop) override;
|
||||||
|
void endVisit(ForStatement& _forLoop) override;
|
||||||
void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override;
|
void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override;
|
||||||
bool visit(VariableDeclaration& _declaration) override;
|
bool visit(VariableDeclaration& _declaration) override;
|
||||||
bool visit(EventDefinition& _event) override;
|
bool visit(EventDefinition& _event) override;
|
||||||
void endVisit(EventDefinition& _event) override;
|
void endVisit(EventDefinition& _event) override;
|
||||||
|
|
||||||
void enterNewSubScope(Declaration const& _declaration);
|
void enterNewSubScope(ASTNode& _subScope);
|
||||||
void closeCurrentScope();
|
void closeCurrentScope();
|
||||||
void registerDeclaration(Declaration& _declaration, bool _opensScope);
|
void registerDeclaration(Declaration& _declaration, bool _opensScope);
|
||||||
|
|
||||||
@ -177,6 +190,7 @@ private:
|
|||||||
/// @returns the canonical name of the current scope.
|
/// @returns the canonical name of the current scope.
|
||||||
std::string currentCanonicalName() const;
|
std::string currentCanonicalName() const;
|
||||||
|
|
||||||
|
bool m_useC99Scoping = false;
|
||||||
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
|
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
|
||||||
ASTNode const* m_currentScope = nullptr;
|
ASTNode const* m_currentScope = nullptr;
|
||||||
VariableScope* m_currentFunction = nullptr;
|
VariableScope* m_currentFunction = nullptr;
|
||||||
|
@ -43,6 +43,56 @@ bool ReferencesResolver::resolve(ASTNode const& _root)
|
|||||||
return !m_errorOccurred;
|
return !m_errorOccurred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ReferencesResolver::visit(Block const& _block)
|
||||||
|
{
|
||||||
|
if (!m_resolveInsideCode)
|
||||||
|
return false;
|
||||||
|
m_experimental050Mode = _block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
|
// C99-scoped variables
|
||||||
|
if (m_experimental050Mode)
|
||||||
|
m_resolver.setScope(&_block);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReferencesResolver::endVisit(Block const& _block)
|
||||||
|
{
|
||||||
|
if (!m_resolveInsideCode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// C99-scoped variables
|
||||||
|
if (m_experimental050Mode)
|
||||||
|
m_resolver.setScope(_block.scope());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReferencesResolver::visit(ForStatement const& _for)
|
||||||
|
{
|
||||||
|
if (!m_resolveInsideCode)
|
||||||
|
return false;
|
||||||
|
m_experimental050Mode = _for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
|
// C99-scoped variables
|
||||||
|
if (m_experimental050Mode)
|
||||||
|
m_resolver.setScope(&_for);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReferencesResolver::endVisit(ForStatement const& _for)
|
||||||
|
{
|
||||||
|
if (!m_resolveInsideCode)
|
||||||
|
return;
|
||||||
|
if (m_experimental050Mode)
|
||||||
|
m_resolver.setScope(_for.scope());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement)
|
||||||
|
{
|
||||||
|
if (!m_resolveInsideCode)
|
||||||
|
return;
|
||||||
|
if (m_experimental050Mode)
|
||||||
|
for (auto const& var: _varDeclStatement.declarations())
|
||||||
|
if (var)
|
||||||
|
m_resolver.activateVariable(var->name());
|
||||||
|
}
|
||||||
|
|
||||||
bool ReferencesResolver::visit(Identifier const& _identifier)
|
bool ReferencesResolver::visit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
|
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
|
||||||
@ -228,8 +278,10 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Will be re-generated later with correct information
|
// Will be re-generated later with correct information
|
||||||
|
// We use the latest EVM version because we will re-run it anyway.
|
||||||
assembly::AsmAnalysisInfo analysisInfo;
|
assembly::AsmAnalysisInfo analysisInfo;
|
||||||
assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
|
boost::optional<Error::Type> errorTypeForLoose = m_experimental050Mode ? Error::Type::SyntaxError : Error::Type::Warning;
|
||||||
|
assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,11 @@ public:
|
|||||||
bool resolve(ASTNode const& _root);
|
bool resolve(ASTNode const& _root);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool visit(Block const&) override { return m_resolveInsideCode; }
|
virtual bool visit(Block const& _block) override;
|
||||||
|
virtual void endVisit(Block const& _block) override;
|
||||||
|
virtual bool visit(ForStatement const& _for) override;
|
||||||
|
virtual void endVisit(ForStatement const& _for) override;
|
||||||
|
virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
|
||||||
virtual bool visit(Identifier const& _identifier) override;
|
virtual bool visit(Identifier const& _identifier) override;
|
||||||
virtual bool visit(ElementaryTypeName const& _typeName) override;
|
virtual bool visit(ElementaryTypeName const& _typeName) override;
|
||||||
virtual bool visit(FunctionDefinition const& _functionDefinition) override;
|
virtual bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||||
@ -90,6 +94,7 @@ private:
|
|||||||
std::vector<ParameterList const*> m_returnParameters;
|
std::vector<ParameterList const*> m_returnParameters;
|
||||||
bool const m_resolveInsideCode;
|
bool const m_resolveInsideCode;
|
||||||
bool m_errorOccurred = false;
|
bool m_errorOccurred = false;
|
||||||
|
bool m_experimental050Mode = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,23 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
|
|||||||
|
|
||||||
bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||||
{
|
{
|
||||||
|
bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
|
|
||||||
|
if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
|
||||||
|
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
|
||||||
|
{
|
||||||
|
if (v050)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_memberAccess.location(),
|
||||||
|
"\"msg.gas\" has been deprecated in favor of \"gasleft()\""
|
||||||
|
);
|
||||||
|
else
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_memberAccess.location(),
|
||||||
|
"\"msg.gas\" has been deprecated in favor of \"gasleft()\""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_nonPayablePublic && !m_library)
|
if (m_nonPayablePublic && !m_library)
|
||||||
if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
|
if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
|
||||||
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value")
|
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value")
|
||||||
@ -151,7 +168,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
|||||||
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
|
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
|
||||||
if (type->kind() == FunctionType::Kind::BareCallCode)
|
if (type->kind() == FunctionType::Kind::BareCallCode)
|
||||||
{
|
{
|
||||||
if (m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
|
if (v050)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
|
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
|
||||||
|
@ -93,8 +93,10 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name.");
|
m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name.");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_sourceUnit->annotation().experimentalFeatures.insert(ExperimentalFeatureNames.at(literal));
|
auto feature = ExperimentalFeatureNames.at(literal);
|
||||||
m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
|
||||||
|
if (!ExperimentalFeatureOnlyAnalysis.count(feature))
|
||||||
|
m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,10 +174,18 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
|
|||||||
|
|
||||||
bool SyntaxChecker::visit(Throw const& _throwStatement)
|
bool SyntaxChecker::visit(Throw const& _throwStatement)
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
_throwStatement.location(),
|
|
||||||
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
|
if (v050)
|
||||||
);
|
m_errorReporter.syntaxError(
|
||||||
|
_throwStatement.location(),
|
||||||
|
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
|
||||||
|
);
|
||||||
|
else
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_throwStatement.location(),
|
||||||
|
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -202,13 +212,20 @@ bool SyntaxChecker::visit(PlaceholderStatement const&)
|
|||||||
|
|
||||||
bool SyntaxChecker::visit(FunctionDefinition const& _function)
|
bool SyntaxChecker::visit(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
|
bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
|
|
||||||
if (_function.noVisibilitySpecified())
|
if (_function.noVisibilitySpecified())
|
||||||
m_errorReporter.warning(
|
{
|
||||||
_function.location(),
|
if (v050)
|
||||||
"No visibility specified. Defaulting to \"" +
|
m_errorReporter.syntaxError(_function.location(), "No visibility specified.");
|
||||||
Declaration::visibilityToString(_function.visibility()) +
|
else
|
||||||
"\"."
|
m_errorReporter.warning(
|
||||||
);
|
_function.location(),
|
||||||
|
"No visibility specified. Defaulting to \"" +
|
||||||
|
Declaration::visibilityToString(_function.visibility()) +
|
||||||
|
"\"."
|
||||||
|
);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,29 @@ using namespace std;
|
|||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
bool typeSupportedByOldABIEncoder(Type const& _type)
|
||||||
|
{
|
||||||
|
if (_type.dataStoredIn(DataLocation::Storage))
|
||||||
|
return true;
|
||||||
|
else if (_type.category() == Type::Category::Struct)
|
||||||
|
return false;
|
||||||
|
else if (_type.category() == Type::Category::Array)
|
||||||
|
{
|
||||||
|
auto const& arrayType = dynamic_cast<ArrayType const&>(_type);
|
||||||
|
auto base = arrayType.baseType();
|
||||||
|
if (!typeSupportedByOldABIEncoder(*base))
|
||||||
|
return false;
|
||||||
|
else if (base->category() == Type::Category::Array && base->isDynamicallySized())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool TypeChecker::checkTypeRequirements(ASTNode const& _contract)
|
bool TypeChecker::checkTypeRequirements(ASTNode const& _contract)
|
||||||
{
|
{
|
||||||
@ -561,13 +584,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
|
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
|
||||||
if (
|
if (
|
||||||
_function.visibility() > FunctionDefinition::Visibility::Internal &&
|
_function.visibility() > FunctionDefinition::Visibility::Internal &&
|
||||||
type(*var)->category() == Type::Category::Struct &&
|
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
|
||||||
!type(*var)->dataStoredIn(DataLocation::Storage) &&
|
!typeSupportedByOldABIEncoder(*type(*var))
|
||||||
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)
|
|
||||||
)
|
)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
var->location(),
|
var->location(),
|
||||||
"Structs are only supported in the new experimental ABI encoder. "
|
"This type is only supported in the new experimental ABI encoder. "
|
||||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -804,7 +826,12 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
solAssert(!!declaration, "");
|
solAssert(!!declaration, "");
|
||||||
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
|
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
{
|
{
|
||||||
if (ref->second.isSlot || ref->second.isOffset)
|
if (var->isConstant())
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly.");
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
else if (ref->second.isSlot || ref->second.isOffset)
|
||||||
{
|
{
|
||||||
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
|
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
|
||||||
{
|
{
|
||||||
@ -817,11 +844,6 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
return size_t(-1);
|
return size_t(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (var->isConstant())
|
|
||||||
{
|
|
||||||
m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly.");
|
|
||||||
return size_t(-1);
|
|
||||||
}
|
|
||||||
else if (!var->isLocalVariable())
|
else if (!var->isLocalVariable())
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
|
m_errorReporter.typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
|
||||||
@ -872,9 +894,15 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
};
|
};
|
||||||
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
|
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
|
||||||
_inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
|
_inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
|
||||||
|
boost::optional<Error::Type> errorTypeForLoose =
|
||||||
|
m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050) ?
|
||||||
|
Error::Type::SyntaxError :
|
||||||
|
Error::Type::Warning;
|
||||||
assembly::AsmAnalyzer analyzer(
|
assembly::AsmAnalyzer analyzer(
|
||||||
*_inlineAssembly.annotation().analysisInfo,
|
*_inlineAssembly.annotation().analysisInfo,
|
||||||
m_errorReporter,
|
m_errorReporter,
|
||||||
|
m_evmVersion,
|
||||||
|
errorTypeForLoose,
|
||||||
assembly::AsmFlavour::Loose,
|
assembly::AsmFlavour::Loose,
|
||||||
identifierAccess
|
identifierAccess
|
||||||
);
|
);
|
||||||
@ -955,6 +983,16 @@ void TypeChecker::endVisit(Return const& _return)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypeChecker::endVisit(EmitStatement const& _emit)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall ||
|
||||||
|
dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event
|
||||||
|
)
|
||||||
|
m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event invocation.");
|
||||||
|
m_insideEmitStatement = false;
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
||||||
{
|
{
|
||||||
if (!_statement.initialValue())
|
if (!_statement.initialValue())
|
||||||
@ -972,7 +1010,11 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
|||||||
string errorText{"Uninitialized storage pointer."};
|
string errorText{"Uninitialized storage pointer."};
|
||||||
if (varDecl.referenceLocation() == VariableDeclaration::Location::Default)
|
if (varDecl.referenceLocation() == VariableDeclaration::Location::Default)
|
||||||
errorText += " Did you mean '<type> memory " + varDecl.name() + "'?";
|
errorText += " Did you mean '<type> memory " + varDecl.name() + "'?";
|
||||||
m_errorReporter.warning(varDecl.location(), errorText);
|
solAssert(m_scope, "");
|
||||||
|
if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
|
||||||
|
m_errorReporter.declarationError(varDecl.location(), errorText);
|
||||||
|
else
|
||||||
|
m_errorReporter.warning(varDecl.location(), errorText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<MappingType const*>(type(varDecl).get()))
|
else if (dynamic_cast<MappingType const*>(type(varDecl).get()))
|
||||||
@ -1527,6 +1569,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct)
|
else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct)
|
||||||
m_errorReporter.warning(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\"");
|
m_errorReporter.warning(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\"");
|
||||||
}
|
}
|
||||||
|
if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event)
|
||||||
|
{
|
||||||
|
if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
|
||||||
|
m_errorReporter.typeError(_functionCall.location(), "Event invocations have to be prefixed by \"emit\".");
|
||||||
|
else
|
||||||
|
m_errorReporter.warning(_functionCall.location(), "Invoking events without \"emit\" prefix is deprecated.");
|
||||||
|
}
|
||||||
|
|
||||||
TypePointers parameterTypes = functionType->parameterTypes();
|
TypePointers parameterTypes = functionType->parameterTypes();
|
||||||
|
|
||||||
@ -1809,6 +1858,20 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
|
|
||||||
if (exprType->category() == Type::Category::Contract)
|
if (exprType->category() == Type::Category::Contract)
|
||||||
{
|
{
|
||||||
|
// Warn about using address members on contracts
|
||||||
|
bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
|
for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr))
|
||||||
|
if (addressMember.name == memberName && *annotation.type == *addressMember.type)
|
||||||
|
{
|
||||||
|
solAssert(!v050, "Address member still present on contract in v0.5.0.");
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_memberAccess.location(),
|
||||||
|
"Using contract member \"" + memberName +"\" inherited from the address type is deprecated." +
|
||||||
|
" Convert the contract to \"address\" type to access the member."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn about using send or transfer with a non-payable fallback function.
|
||||||
if (auto callType = dynamic_cast<FunctionType const*>(type(_memberAccess).get()))
|
if (auto callType = dynamic_cast<FunctionType const*>(type(_memberAccess).get()))
|
||||||
{
|
{
|
||||||
auto kind = callType->kind();
|
auto kind = callType->kind();
|
||||||
@ -2000,6 +2063,8 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
|
|||||||
|
|
||||||
void TypeChecker::endVisit(Literal const& _literal)
|
void TypeChecker::endVisit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
|
bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
|
|
||||||
if (_literal.looksLikeAddress())
|
if (_literal.looksLikeAddress())
|
||||||
{
|
{
|
||||||
if (_literal.passesAddressChecksum())
|
if (_literal.passesAddressChecksum())
|
||||||
@ -2013,6 +2078,21 @@ void TypeChecker::endVisit(Literal const& _literal)
|
|||||||
"For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals"
|
"For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None)
|
||||||
|
{
|
||||||
|
if (v050)
|
||||||
|
m_errorReporter.fatalTypeError(
|
||||||
|
_literal.location(),
|
||||||
|
"Hexadecimal numbers cannot be used with unit denominations. "
|
||||||
|
"You can use an expression of the form \"0x1234 * 1 day\" instead."
|
||||||
|
);
|
||||||
|
else
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_literal.location(),
|
||||||
|
"Hexadecimal numbers with unit denominations are deprecated. "
|
||||||
|
"You can use an expression of the form \"0x1234 * 1 day\" instead."
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!_literal.annotation().type)
|
if (!_literal.annotation().type)
|
||||||
_literal.annotation().type = Type::forLiteral(_literal);
|
_literal.annotation().type = Type::forLiteral(_literal);
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
@ -43,7 +45,10 @@ class TypeChecker: private ASTConstVisitor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @param _errorReporter provides the error logging functionality.
|
/// @param _errorReporter provides the error logging functionality.
|
||||||
TypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
|
TypeChecker(EVMVersion _evmVersion, ErrorReporter& _errorReporter):
|
||||||
|
m_evmVersion(_evmVersion),
|
||||||
|
m_errorReporter(_errorReporter)
|
||||||
|
{}
|
||||||
|
|
||||||
/// Performs type checking on the given contract and all of its sub-nodes.
|
/// Performs type checking on the given contract and all of its sub-nodes.
|
||||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||||
@ -94,6 +99,8 @@ private:
|
|||||||
virtual bool visit(WhileStatement const& _whileStatement) override;
|
virtual bool visit(WhileStatement const& _whileStatement) override;
|
||||||
virtual bool visit(ForStatement const& _forStatement) override;
|
virtual bool visit(ForStatement const& _forStatement) override;
|
||||||
virtual void endVisit(Return const& _return) override;
|
virtual void endVisit(Return const& _return) override;
|
||||||
|
virtual bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; }
|
||||||
|
virtual void endVisit(EmitStatement const& _emit) override;
|
||||||
virtual bool visit(VariableDeclarationStatement const& _variable) override;
|
virtual bool visit(VariableDeclarationStatement const& _variable) override;
|
||||||
virtual void endVisit(ExpressionStatement const& _statement) override;
|
virtual void endVisit(ExpressionStatement const& _statement) override;
|
||||||
virtual bool visit(Conditional const& _conditional) override;
|
virtual bool visit(Conditional const& _conditional) override;
|
||||||
@ -130,6 +137,11 @@ private:
|
|||||||
|
|
||||||
ContractDefinition const* m_scope = nullptr;
|
ContractDefinition const* m_scope = nullptr;
|
||||||
|
|
||||||
|
EVMVersion m_evmVersion;
|
||||||
|
|
||||||
|
/// Flag indicating whether we are currently inside an EmitStatement.
|
||||||
|
bool m_insideEmitStatement = false;
|
||||||
|
|
||||||
ErrorReporter& m_errorReporter;
|
ErrorReporter& m_errorReporter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,20 +96,6 @@ set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<Sour
|
|||||||
return sourceUnits;
|
return sourceUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceUnit const& Declaration::sourceUnit() const
|
|
||||||
{
|
|
||||||
solAssert(!!m_scope, "");
|
|
||||||
ASTNode const* scope = m_scope;
|
|
||||||
while (dynamic_cast<Declaration const*>(scope) && dynamic_cast<Declaration const*>(scope)->m_scope)
|
|
||||||
scope = dynamic_cast<Declaration const*>(scope)->m_scope;
|
|
||||||
return dynamic_cast<SourceUnit const&>(*scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
string Declaration::sourceUnitName() const
|
|
||||||
{
|
|
||||||
return sourceUnit().annotation().path;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportAnnotation& ImportDirective::annotation() const
|
ImportAnnotation& ImportDirective::annotation() const
|
||||||
{
|
{
|
||||||
if (!m_annotation)
|
if (!m_annotation)
|
||||||
@ -408,12 +394,36 @@ UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const
|
|||||||
return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
|
return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SourceUnit const& Scopable::sourceUnit() const
|
||||||
|
{
|
||||||
|
ASTNode const* s = scope();
|
||||||
|
solAssert(s, "");
|
||||||
|
// will not always be a declaratoion
|
||||||
|
while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope())
|
||||||
|
s = dynamic_cast<Scopable const*>(s)->scope();
|
||||||
|
return dynamic_cast<SourceUnit const&>(*s);
|
||||||
|
}
|
||||||
|
|
||||||
|
string Scopable::sourceUnitName() const
|
||||||
|
{
|
||||||
|
return sourceUnit().annotation().path;
|
||||||
|
}
|
||||||
|
|
||||||
bool VariableDeclaration::isLValue() const
|
bool VariableDeclaration::isLValue() const
|
||||||
{
|
{
|
||||||
// External function parameters and constant declared variables are Read-Only
|
// External function parameters and constant declared variables are Read-Only
|
||||||
return !isExternalCallableParameter() && !m_isConstant;
|
return !isExternalCallableParameter() && !m_isConstant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VariableDeclaration::isLocalVariable() const
|
||||||
|
{
|
||||||
|
auto s = scope();
|
||||||
|
return
|
||||||
|
dynamic_cast<CallableDeclaration const*>(s) ||
|
||||||
|
dynamic_cast<Block const*>(s) ||
|
||||||
|
dynamic_cast<ForStatement const*>(s);
|
||||||
|
}
|
||||||
|
|
||||||
bool VariableDeclaration::isCallableParameter() const
|
bool VariableDeclaration::isCallableParameter() const
|
||||||
{
|
{
|
||||||
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
|
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
|
||||||
@ -459,8 +469,7 @@ bool VariableDeclaration::isExternalCallableParameter() const
|
|||||||
|
|
||||||
bool VariableDeclaration::canHaveAutoType() const
|
bool VariableDeclaration::canHaveAutoType() const
|
||||||
{
|
{
|
||||||
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
|
return isLocalVariable() && !isCallableParameter();
|
||||||
return (!!callable && !isCallableParameter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer VariableDeclaration::type() const
|
TypePointer VariableDeclaration::type() const
|
||||||
|
@ -139,10 +139,33 @@ private:
|
|||||||
std::vector<ASTPointer<ASTNode>> m_nodes;
|
std::vector<ASTPointer<ASTNode>> m_nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class that is added to each AST node that is stored inside a scope
|
||||||
|
* (including scopes).
|
||||||
|
*/
|
||||||
|
class Scopable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
|
||||||
|
/// Available only after name and type resolution step.
|
||||||
|
ASTNode const* scope() const { return m_scope; }
|
||||||
|
void setScope(ASTNode const* _scope) { m_scope = _scope; }
|
||||||
|
|
||||||
|
/// @returns the source unit this scopable is present in.
|
||||||
|
SourceUnit const& sourceUnit() const;
|
||||||
|
|
||||||
|
/// @returns the source name this scopable is present in.
|
||||||
|
/// Can be combined with annotation().canonicalName (if present) to form a globally unique name.
|
||||||
|
std::string sourceUnitName() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ASTNode const* m_scope = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract AST class for a declaration (contract, function, struct, variable, import directive).
|
* Abstract AST class for a declaration (contract, function, struct, variable, import directive).
|
||||||
*/
|
*/
|
||||||
class Declaration: public ASTNode
|
class Declaration: public ASTNode, public Scopable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Visibility ordered from restricted to unrestricted.
|
/// Visibility ordered from restricted to unrestricted.
|
||||||
@ -171,7 +194,7 @@ public:
|
|||||||
ASTPointer<ASTString> const& _name,
|
ASTPointer<ASTString> const& _name,
|
||||||
Visibility _visibility = Visibility::Default
|
Visibility _visibility = Visibility::Default
|
||||||
):
|
):
|
||||||
ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
|
ASTNode(_location), m_name(_name), m_visibility(_visibility) {}
|
||||||
|
|
||||||
/// @returns the declared name.
|
/// @returns the declared name.
|
||||||
ASTString const& name() const { return *m_name; }
|
ASTString const& name() const { return *m_name; }
|
||||||
@ -181,17 +204,6 @@ public:
|
|||||||
virtual bool isVisibleInContract() const { return visibility() != Visibility::External; }
|
virtual bool isVisibleInContract() const { return visibility() != Visibility::External; }
|
||||||
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; }
|
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; }
|
||||||
|
|
||||||
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
|
|
||||||
/// Available only after name and type resolution step.
|
|
||||||
ASTNode const* scope() const { return m_scope; }
|
|
||||||
void setScope(ASTNode const* _scope) { m_scope = _scope; }
|
|
||||||
|
|
||||||
/// @returns the source unit this declaration is present in.
|
|
||||||
SourceUnit const& sourceUnit() const;
|
|
||||||
|
|
||||||
/// @returns the source name this declaration is present in.
|
|
||||||
/// Can be combined with annotation().canonicalName to form a globally unique name.
|
|
||||||
std::string sourceUnitName() const;
|
|
||||||
std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
|
std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
|
||||||
|
|
||||||
virtual bool isLValue() const { return false; }
|
virtual bool isLValue() const { return false; }
|
||||||
@ -213,7 +225,6 @@ protected:
|
|||||||
private:
|
private:
|
||||||
ASTPointer<ASTString> m_name;
|
ASTPointer<ASTString> m_name;
|
||||||
Visibility m_visibility;
|
Visibility m_visibility;
|
||||||
ASTNode const* m_scope;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -289,6 +300,8 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class that is added to each AST node that can store local variables.
|
* Abstract class that is added to each AST node that can store local variables.
|
||||||
|
* Local variables in functions are always added to functions, even though they are not
|
||||||
|
* in scope for the whole function.
|
||||||
*/
|
*/
|
||||||
class VariableScope
|
class VariableScope
|
||||||
{
|
{
|
||||||
@ -662,7 +675,7 @@ public:
|
|||||||
virtual bool isLValue() const override;
|
virtual bool isLValue() const override;
|
||||||
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
|
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
|
||||||
|
|
||||||
bool isLocalVariable() const { return !!dynamic_cast<CallableDeclaration const*>(scope()); }
|
bool isLocalVariable() const;
|
||||||
/// @returns true if this variable is a parameter or return parameter of a function.
|
/// @returns true if this variable is a parameter or return parameter of a function.
|
||||||
bool isCallableParameter() const;
|
bool isCallableParameter() const;
|
||||||
/// @returns true if this variable is a return parameter of a function.
|
/// @returns true if this variable is a return parameter of a function.
|
||||||
@ -1004,7 +1017,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* Brace-enclosed block containing zero or more statements.
|
* Brace-enclosed block containing zero or more statements.
|
||||||
*/
|
*/
|
||||||
class Block: public Statement
|
class Block: public Statement, public Scopable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Block(
|
Block(
|
||||||
@ -1111,7 +1124,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* For loop statement
|
* For loop statement
|
||||||
*/
|
*/
|
||||||
class ForStatement: public BreakableStatement
|
class ForStatement: public BreakableStatement, public Scopable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ForStatement(
|
ForStatement(
|
||||||
@ -1196,6 +1209,27 @@ public:
|
|||||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The emit statement is used to emit events: emit EventName(arg1, ..., argn)
|
||||||
|
*/
|
||||||
|
class EmitStatement: public Statement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit EmitStatement(
|
||||||
|
SourceLocation const& _location,
|
||||||
|
ASTPointer<ASTString> const& _docString,
|
||||||
|
ASTPointer<FunctionCall> const& _functionCall
|
||||||
|
):
|
||||||
|
Statement(_location, _docString), m_eventCall(_functionCall) {}
|
||||||
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
|
FunctionCall const& eventCall() const { return *m_eventCall; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ASTPointer<FunctionCall> m_eventCall;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of a variable as a statement inside a function. It requires a type name (which can
|
* Definition of a variable as a statement inside a function. It requires a type name (which can
|
||||||
* also be "var") but the actual assignment can be missing.
|
* also be "var") but the actual assignment can be missing.
|
||||||
|
@ -69,6 +69,7 @@ class Continue;
|
|||||||
class Break;
|
class Break;
|
||||||
class Return;
|
class Return;
|
||||||
class Throw;
|
class Throw;
|
||||||
|
class EmitStatement;
|
||||||
class VariableDeclarationStatement;
|
class VariableDeclarationStatement;
|
||||||
class ExpressionStatement;
|
class ExpressionStatement;
|
||||||
class Expression;
|
class Expression;
|
||||||
|
@ -324,6 +324,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
|
|||||||
{
|
{
|
||||||
std::vector<pair<string, Json::Value>> attributes = {
|
std::vector<pair<string, Json::Value>> attributes = {
|
||||||
make_pair("name", _node.name()),
|
make_pair("name", _node.name()),
|
||||||
|
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
|
||||||
// FIXME: remove with next breaking release
|
// FIXME: remove with next breaking release
|
||||||
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View),
|
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View),
|
||||||
make_pair("payable", _node.isPayable()),
|
make_pair("payable", _node.isPayable()),
|
||||||
@ -365,6 +366,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node)
|
|||||||
{
|
{
|
||||||
setJsonNode(_node, "ModifierDefinition", {
|
setJsonNode(_node, "ModifierDefinition", {
|
||||||
make_pair("name", _node.name()),
|
make_pair("name", _node.name()),
|
||||||
|
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
|
||||||
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
|
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
|
||||||
make_pair("parameters", toJson(_node.parameterList())),
|
make_pair("parameters", toJson(_node.parameterList())),
|
||||||
make_pair("body", toJson(_node.body()))
|
make_pair("body", toJson(_node.body()))
|
||||||
@ -386,6 +388,7 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
|
|||||||
m_inEvent = true;
|
m_inEvent = true;
|
||||||
setJsonNode(_node, "EventDefinition", {
|
setJsonNode(_node, "EventDefinition", {
|
||||||
make_pair("name", _node.name()),
|
make_pair("name", _node.name()),
|
||||||
|
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
|
||||||
make_pair("parameters", toJson(_node.parameterList())),
|
make_pair("parameters", toJson(_node.parameterList())),
|
||||||
make_pair("anonymous", _node.isAnonymous())
|
make_pair("anonymous", _node.isAnonymous())
|
||||||
});
|
});
|
||||||
@ -537,7 +540,15 @@ bool ASTJsonConverter::visit(Return const& _node)
|
|||||||
|
|
||||||
bool ASTJsonConverter::visit(Throw const& _node)
|
bool ASTJsonConverter::visit(Throw const& _node)
|
||||||
{
|
{
|
||||||
setJsonNode(_node, "Throw", {});;
|
setJsonNode(_node, "Throw", {});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ASTJsonConverter::visit(EmitStatement const& _node)
|
||||||
|
{
|
||||||
|
setJsonNode(_node, "EmitStatement", {
|
||||||
|
make_pair("eventCall", toJson(_node.eventCall()))
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@ public:
|
|||||||
bool visit(Break const& _node) override;
|
bool visit(Break const& _node) override;
|
||||||
bool visit(Return const& _node) override;
|
bool visit(Return const& _node) override;
|
||||||
bool visit(Throw const& _node) override;
|
bool visit(Throw const& _node) override;
|
||||||
|
bool visit(EmitStatement const& _node) override;
|
||||||
bool visit(VariableDeclarationStatement const& _node) override;
|
bool visit(VariableDeclarationStatement const& _node) override;
|
||||||
bool visit(ExpressionStatement const& _node) override;
|
bool visit(ExpressionStatement const& _node) override;
|
||||||
bool visit(Conditional const& _node) override;
|
bool visit(Conditional const& _node) override;
|
||||||
|
@ -258,6 +258,13 @@ bool ASTPrinter::visit(Throw const& _node)
|
|||||||
return goDeeper();
|
return goDeeper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTPrinter::visit(EmitStatement const& _node)
|
||||||
|
{
|
||||||
|
writeLine("EmitStatement");
|
||||||
|
printSourcePart(_node);
|
||||||
|
return goDeeper();
|
||||||
|
}
|
||||||
|
|
||||||
bool ASTPrinter::visit(VariableDeclarationStatement const& _node)
|
bool ASTPrinter::visit(VariableDeclarationStatement const& _node)
|
||||||
{
|
{
|
||||||
writeLine("VariableDeclarationStatement");
|
writeLine("VariableDeclarationStatement");
|
||||||
@ -517,6 +524,11 @@ void ASTPrinter::endVisit(Throw const&)
|
|||||||
m_indentation--;
|
m_indentation--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTPrinter::endVisit(EmitStatement const&)
|
||||||
|
{
|
||||||
|
m_indentation--;
|
||||||
|
}
|
||||||
|
|
||||||
void ASTPrinter::endVisit(VariableDeclarationStatement const&)
|
void ASTPrinter::endVisit(VariableDeclarationStatement const&)
|
||||||
{
|
{
|
||||||
m_indentation--;
|
m_indentation--;
|
||||||
|
@ -76,6 +76,7 @@ public:
|
|||||||
bool visit(Break const& _node) override;
|
bool visit(Break const& _node) override;
|
||||||
bool visit(Return const& _node) override;
|
bool visit(Return const& _node) override;
|
||||||
bool visit(Throw const& _node) override;
|
bool visit(Throw const& _node) override;
|
||||||
|
bool visit(EmitStatement const& _node) override;
|
||||||
bool visit(VariableDeclarationStatement const& _node) override;
|
bool visit(VariableDeclarationStatement const& _node) override;
|
||||||
bool visit(ExpressionStatement const& _node) override;
|
bool visit(ExpressionStatement const& _node) override;
|
||||||
bool visit(Conditional const& _node) override;
|
bool visit(Conditional const& _node) override;
|
||||||
@ -120,6 +121,7 @@ public:
|
|||||||
void endVisit(Break const&) override;
|
void endVisit(Break const&) override;
|
||||||
void endVisit(Return const&) override;
|
void endVisit(Return const&) override;
|
||||||
void endVisit(Throw const&) override;
|
void endVisit(Throw const&) override;
|
||||||
|
void endVisit(EmitStatement const&) override;
|
||||||
void endVisit(VariableDeclarationStatement const&) override;
|
void endVisit(VariableDeclarationStatement const&) override;
|
||||||
void endVisit(ExpressionStatement const&) override;
|
void endVisit(ExpressionStatement const&) override;
|
||||||
void endVisit(Conditional const&) override;
|
void endVisit(Conditional const&) override;
|
||||||
|
@ -73,6 +73,7 @@ public:
|
|||||||
virtual bool visit(Break& _node) { return visitNode(_node); }
|
virtual bool visit(Break& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Return& _node) { return visitNode(_node); }
|
virtual bool visit(Return& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Throw& _node) { return visitNode(_node); }
|
virtual bool visit(Throw& _node) { return visitNode(_node); }
|
||||||
|
virtual bool visit(EmitStatement& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
|
virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); }
|
virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Conditional& _node) { return visitNode(_node); }
|
virtual bool visit(Conditional& _node) { return visitNode(_node); }
|
||||||
@ -118,6 +119,7 @@ public:
|
|||||||
virtual void endVisit(Break& _node) { endVisitNode(_node); }
|
virtual void endVisit(Break& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Return& _node) { endVisitNode(_node); }
|
virtual void endVisit(Return& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Throw& _node) { endVisitNode(_node); }
|
virtual void endVisit(Throw& _node) { endVisitNode(_node); }
|
||||||
|
virtual void endVisit(EmitStatement& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); }
|
virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); }
|
virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Conditional& _node) { endVisitNode(_node); }
|
virtual void endVisit(Conditional& _node) { endVisitNode(_node); }
|
||||||
@ -175,6 +177,7 @@ public:
|
|||||||
virtual bool visit(Break const& _node) { return visitNode(_node); }
|
virtual bool visit(Break const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Return const& _node) { return visitNode(_node); }
|
virtual bool visit(Return const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Throw const& _node) { return visitNode(_node); }
|
virtual bool visit(Throw const& _node) { return visitNode(_node); }
|
||||||
|
virtual bool visit(EmitStatement const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); }
|
virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); }
|
virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Conditional const& _node) { return visitNode(_node); }
|
virtual bool visit(Conditional const& _node) { return visitNode(_node); }
|
||||||
@ -220,6 +223,7 @@ public:
|
|||||||
virtual void endVisit(Break const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Break const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Return const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Return const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Throw const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Throw const& _node) { endVisitNode(_node); }
|
||||||
|
virtual void endVisit(EmitStatement const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); }
|
virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); }
|
virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Conditional const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Conditional const& _node) { endVisitNode(_node); }
|
||||||
|
@ -541,6 +541,20 @@ void Throw::accept(ASTConstVisitor& _visitor) const
|
|||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitStatement::accept(ASTVisitor& _visitor)
|
||||||
|
{
|
||||||
|
if (_visitor.visit(*this))
|
||||||
|
m_eventCall->accept(_visitor);
|
||||||
|
_visitor.endVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitStatement::accept(ASTConstVisitor& _visitor) const
|
||||||
|
{
|
||||||
|
if (_visitor.visit(*this))
|
||||||
|
m_eventCall->accept(_visitor);
|
||||||
|
_visitor.endVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void ExpressionStatement::accept(ASTVisitor& _visitor)
|
void ExpressionStatement::accept(ASTVisitor& _visitor)
|
||||||
{
|
{
|
||||||
if (_visitor.visit(*this))
|
if (_visitor.visit(*this))
|
||||||
|
@ -29,8 +29,8 @@ namespace solidity
|
|||||||
|
|
||||||
enum class ExperimentalFeature
|
enum class ExperimentalFeature
|
||||||
{
|
{
|
||||||
SMTChecker,
|
|
||||||
ABIEncoderV2, // new ABI encoder that makes use of JULIA
|
ABIEncoderV2, // new ABI encoder that makes use of JULIA
|
||||||
|
SMTChecker,
|
||||||
V050, // v0.5.0 breaking changes
|
V050, // v0.5.0 breaking changes
|
||||||
Test,
|
Test,
|
||||||
TestOnlyAnalysis
|
TestOnlyAnalysis
|
||||||
@ -40,12 +40,13 @@ static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis
|
|||||||
{
|
{
|
||||||
{ ExperimentalFeature::SMTChecker, true },
|
{ ExperimentalFeature::SMTChecker, true },
|
||||||
{ ExperimentalFeature::TestOnlyAnalysis, true },
|
{ ExperimentalFeature::TestOnlyAnalysis, true },
|
||||||
|
{ ExperimentalFeature::V050, true }
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
|
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
|
||||||
{
|
{
|
||||||
{ "SMTChecker", ExperimentalFeature::SMTChecker },
|
|
||||||
{ "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
|
{ "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
|
||||||
|
{ "SMTChecker", ExperimentalFeature::SMTChecker },
|
||||||
{ "v0.5.0", ExperimentalFeature::V050 },
|
{ "v0.5.0", ExperimentalFeature::V050 },
|
||||||
{ "__test", ExperimentalFeature::Test },
|
{ "__test", ExperimentalFeature::Test },
|
||||||
{ "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
|
{ "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
|
||||||
|
@ -131,28 +131,28 @@ namespace
|
|||||||
|
|
||||||
string parenthesizeIdentifier(string const& _internal)
|
string parenthesizeIdentifier(string const& _internal)
|
||||||
{
|
{
|
||||||
return "$_" + _internal + "_$";
|
return "(" + _internal + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Range>
|
template <class Range>
|
||||||
string identifierList(Range const&& _list)
|
string identifierList(Range const&& _list)
|
||||||
{
|
{
|
||||||
return parenthesizeIdentifier(boost::algorithm::join(_list, "_$_"));
|
return parenthesizeIdentifier(boost::algorithm::join(_list, ","));
|
||||||
}
|
}
|
||||||
|
|
||||||
string identifier(TypePointer const& _type)
|
string richIdentifier(TypePointer const& _type)
|
||||||
{
|
{
|
||||||
return _type ? _type->identifier() : "";
|
return _type ? _type->richIdentifier() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
string identifierList(vector<TypePointer> const& _list)
|
string identifierList(vector<TypePointer> const& _list)
|
||||||
{
|
{
|
||||||
return identifierList(_list | boost::adaptors::transformed(identifier));
|
return identifierList(_list | boost::adaptors::transformed(richIdentifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
string identifierList(TypePointer const& _type)
|
string identifierList(TypePointer const& _type)
|
||||||
{
|
{
|
||||||
return parenthesizeIdentifier(identifier(_type));
|
return parenthesizeIdentifier(richIdentifier(_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
string identifierList(TypePointer const& _type1, TypePointer const& _type2)
|
string identifierList(TypePointer const& _type1, TypePointer const& _type2)
|
||||||
@ -165,11 +165,22 @@ string identifierList(TypePointer const& _type1, TypePointer const& _type2)
|
|||||||
|
|
||||||
string parenthesizeUserIdentifier(string const& _internal)
|
string parenthesizeUserIdentifier(string const& _internal)
|
||||||
{
|
{
|
||||||
return parenthesizeIdentifier(boost::algorithm::replace_all_copy(_internal, "$", "$$$"));
|
return parenthesizeIdentifier(_internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string Type::escapeIdentifier(string const& _identifier)
|
||||||
|
{
|
||||||
|
string ret = _identifier;
|
||||||
|
// FIXME: should be _$$$_
|
||||||
|
boost::algorithm::replace_all(ret, "$", "$$$");
|
||||||
|
boost::algorithm::replace_all(ret, ",", "_$_");
|
||||||
|
boost::algorithm::replace_all(ret, "(", "$_");
|
||||||
|
boost::algorithm::replace_all(ret, ")", "_$");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
|
TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
|
||||||
{
|
{
|
||||||
solAssert(Token::isElementaryTypeName(_type.token()),
|
solAssert(Token::isElementaryTypeName(_type.token()),
|
||||||
@ -334,7 +345,7 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegerType::identifier() const
|
string IntegerType::richIdentifier() const
|
||||||
{
|
{
|
||||||
if (isAddress())
|
if (isAddress())
|
||||||
return "t_address";
|
return "t_address";
|
||||||
@ -504,7 +515,7 @@ FixedPointType::FixedPointType(int _totalBits, int _fractionalDigits, FixedPoint
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
string FixedPointType::identifier() const
|
string FixedPointType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits);
|
return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits);
|
||||||
}
|
}
|
||||||
@ -936,7 +947,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string RationalNumberType::identifier() const
|
string RationalNumberType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str();
|
return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str();
|
||||||
}
|
}
|
||||||
@ -1077,7 +1088,7 @@ bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string StringLiteralType::identifier() const
|
string StringLiteralType::richIdentifier() const
|
||||||
{
|
{
|
||||||
// Since we have to return a valid identifier and the string itself may contain
|
// Since we have to return a valid identifier and the string itself may contain
|
||||||
// anything, we hash it.
|
// anything, we hash it.
|
||||||
@ -1177,7 +1188,7 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c
|
|||||||
return MemberList::MemberMap{MemberList::Member{"length", make_shared<IntegerType>(8)}};
|
return MemberList::MemberMap{MemberList::Member{"length", make_shared<IntegerType>(8)}};
|
||||||
}
|
}
|
||||||
|
|
||||||
string FixedBytesType::identifier() const
|
string FixedBytesType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_bytes" + std::to_string(m_bytes);
|
return "t_bytes" + std::to_string(m_bytes);
|
||||||
}
|
}
|
||||||
@ -1370,7 +1381,7 @@ bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ArrayType::identifier() const
|
string ArrayType::richIdentifier() const
|
||||||
{
|
{
|
||||||
string id;
|
string id;
|
||||||
if (isString())
|
if (isString())
|
||||||
@ -1578,8 +1589,6 @@ bool ArrayType::canBeUsedExternally(bool _inLibrary) const
|
|||||||
return true;
|
return true;
|
||||||
else if (!m_baseType->canBeUsedExternally(_inLibrary))
|
else if (!m_baseType->canBeUsedExternally(_inLibrary))
|
||||||
return false;
|
return false;
|
||||||
else if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized())
|
|
||||||
return false;
|
|
||||||
else
|
else
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1604,7 +1613,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer)
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ContractType::identifier() const
|
string ContractType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id());
|
return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id());
|
||||||
}
|
}
|
||||||
@ -1756,7 +1765,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
|||||||
return this->m_struct == convertTo.m_struct;
|
return this->m_struct == convertTo.m_struct;
|
||||||
}
|
}
|
||||||
|
|
||||||
string StructType::identifier() const
|
string StructType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix();
|
return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix();
|
||||||
}
|
}
|
||||||
@ -1988,7 +1997,7 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
|
|||||||
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
string EnumType::identifier() const
|
string EnumType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id());
|
return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id());
|
||||||
}
|
}
|
||||||
@ -2074,7 +2083,7 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string TupleType::identifier() const
|
string TupleType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_tuple" + identifierList(components());
|
return "t_tuple" + identifierList(components());
|
||||||
}
|
}
|
||||||
@ -2153,32 +2162,19 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
|
|||||||
m_stateMutability(_function.stateMutability()),
|
m_stateMutability(_function.stateMutability()),
|
||||||
m_declaration(&_function)
|
m_declaration(&_function)
|
||||||
{
|
{
|
||||||
TypePointers params;
|
|
||||||
vector<string> paramNames;
|
|
||||||
TypePointers retParams;
|
|
||||||
vector<string> retParamNames;
|
|
||||||
|
|
||||||
if (_isInternal && m_stateMutability == StateMutability::Payable)
|
if (_isInternal && m_stateMutability == StateMutability::Payable)
|
||||||
m_stateMutability = StateMutability::NonPayable;
|
m_stateMutability = StateMutability::NonPayable;
|
||||||
|
|
||||||
params.reserve(_function.parameters().size());
|
|
||||||
paramNames.reserve(_function.parameters().size());
|
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
|
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
|
||||||
{
|
{
|
||||||
paramNames.push_back(var->name());
|
m_parameterNames.push_back(var->name());
|
||||||
params.push_back(var->annotation().type);
|
m_parameterTypes.push_back(var->annotation().type);
|
||||||
}
|
}
|
||||||
retParams.reserve(_function.returnParameters().size());
|
|
||||||
retParamNames.reserve(_function.returnParameters().size());
|
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters())
|
for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters())
|
||||||
{
|
{
|
||||||
retParamNames.push_back(var->name());
|
m_returnParameterNames.push_back(var->name());
|
||||||
retParams.push_back(var->annotation().type);
|
m_returnParameterTypes.push_back(var->annotation().type);
|
||||||
}
|
}
|
||||||
swap(params, m_parameterTypes);
|
|
||||||
swap(paramNames, m_parameterNames);
|
|
||||||
swap(retParams, m_returnParameterTypes);
|
|
||||||
swap(retParamNames, m_returnParameterNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
||||||
@ -2186,16 +2182,14 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
m_stateMutability(StateMutability::View),
|
m_stateMutability(StateMutability::View),
|
||||||
m_declaration(&_varDecl)
|
m_declaration(&_varDecl)
|
||||||
{
|
{
|
||||||
TypePointers paramTypes;
|
|
||||||
vector<string> paramNames;
|
|
||||||
auto returnType = _varDecl.annotation().type;
|
auto returnType = _varDecl.annotation().type;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
||||||
{
|
{
|
||||||
paramTypes.push_back(mappingType->keyType());
|
m_parameterTypes.push_back(mappingType->keyType());
|
||||||
paramNames.push_back("");
|
m_parameterNames.push_back("");
|
||||||
returnType = mappingType->valueType();
|
returnType = mappingType->valueType();
|
||||||
}
|
}
|
||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
|
||||||
@ -2204,15 +2198,13 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
// Return byte arrays as as whole.
|
// Return byte arrays as as whole.
|
||||||
break;
|
break;
|
||||||
returnType = arrayType->baseType();
|
returnType = arrayType->baseType();
|
||||||
paramNames.push_back("");
|
m_parameterNames.push_back("");
|
||||||
paramTypes.push_back(make_shared<IntegerType>(256));
|
m_parameterTypes.push_back(make_shared<IntegerType>(256));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointers retParams;
|
|
||||||
vector<string> retParamNames;
|
|
||||||
if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
|
if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
|
||||||
{
|
{
|
||||||
for (auto const& member: structType->members(nullptr))
|
for (auto const& member: structType->members(nullptr))
|
||||||
@ -2223,24 +2215,22 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get()))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get()))
|
||||||
if (!arrayType->isByteArray())
|
if (!arrayType->isByteArray())
|
||||||
continue;
|
continue;
|
||||||
retParams.push_back(member.type);
|
m_returnParameterTypes.push_back(ReferenceType::copyForLocationIfReference(
|
||||||
retParamNames.push_back(member.name);
|
DataLocation::Memory,
|
||||||
|
member.type
|
||||||
|
));
|
||||||
|
m_returnParameterNames.push_back(member.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
retParams.push_back(ReferenceType::copyForLocationIfReference(
|
m_returnParameterTypes.push_back(ReferenceType::copyForLocationIfReference(
|
||||||
DataLocation::Memory,
|
DataLocation::Memory,
|
||||||
returnType
|
returnType
|
||||||
));
|
));
|
||||||
retParamNames.push_back("");
|
m_returnParameterNames.push_back("");
|
||||||
}
|
}
|
||||||
|
|
||||||
swap(paramTypes, m_parameterTypes);
|
|
||||||
swap(paramNames, m_parameterNames);
|
|
||||||
swap(retParams, m_returnParameterTypes);
|
|
||||||
swap(retParamNames, m_returnParameterNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionType::FunctionType(EventDefinition const& _event):
|
FunctionType::FunctionType(EventDefinition const& _event):
|
||||||
@ -2248,17 +2238,11 @@ FunctionType::FunctionType(EventDefinition const& _event):
|
|||||||
m_stateMutability(StateMutability::NonPayable),
|
m_stateMutability(StateMutability::NonPayable),
|
||||||
m_declaration(&_event)
|
m_declaration(&_event)
|
||||||
{
|
{
|
||||||
TypePointers params;
|
|
||||||
vector<string> paramNames;
|
|
||||||
params.reserve(_event.parameters().size());
|
|
||||||
paramNames.reserve(_event.parameters().size());
|
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _event.parameters())
|
for (ASTPointer<VariableDeclaration> const& var: _event.parameters())
|
||||||
{
|
{
|
||||||
paramNames.push_back(var->name());
|
m_parameterNames.push_back(var->name());
|
||||||
params.push_back(var->annotation().type);
|
m_parameterTypes.push_back(var->annotation().type);
|
||||||
}
|
}
|
||||||
swap(params, m_parameterTypes);
|
|
||||||
swap(paramNames, m_parameterNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionType::FunctionType(FunctionTypeName const& _typeName):
|
FunctionType::FunctionType(FunctionTypeName const& _typeName):
|
||||||
@ -2334,7 +2318,7 @@ TypePointers FunctionType::parameterTypes() const
|
|||||||
return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
|
return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
|
||||||
}
|
}
|
||||||
|
|
||||||
string FunctionType::identifier() const
|
string FunctionType::richIdentifier() const
|
||||||
{
|
{
|
||||||
string id = "t_function_";
|
string id = "t_function_";
|
||||||
switch (m_kind)
|
switch (m_kind)
|
||||||
@ -2360,6 +2344,7 @@ string FunctionType::identifier() const
|
|||||||
case Kind::Log2: id += "log2"; break;
|
case Kind::Log2: id += "log2"; break;
|
||||||
case Kind::Log3: id += "log3"; break;
|
case Kind::Log3: id += "log3"; break;
|
||||||
case Kind::Log4: id += "log4"; break;
|
case Kind::Log4: id += "log4"; break;
|
||||||
|
case Kind::GasLeft: id += "gasleft"; break;
|
||||||
case Kind::Event: id += "event"; break;
|
case Kind::Event: id += "event"; break;
|
||||||
case Kind::SetGas: id += "setgas"; break;
|
case Kind::SetGas: id += "setgas"; break;
|
||||||
case Kind::SetValue: id += "setvalue"; break;
|
case Kind::SetValue: id += "setvalue"; break;
|
||||||
@ -2827,7 +2812,7 @@ ASTPointer<ASTString> FunctionType::documentation() const
|
|||||||
return ASTPointer<ASTString>();
|
return ASTPointer<ASTString>();
|
||||||
}
|
}
|
||||||
|
|
||||||
string MappingType::identifier() const
|
string MappingType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_mapping" + identifierList(m_keyType, m_valueType);
|
return "t_mapping" + identifierList(m_keyType, m_valueType);
|
||||||
}
|
}
|
||||||
@ -2850,7 +2835,7 @@ string MappingType::canonicalName() const
|
|||||||
return "mapping(" + keyType()->canonicalName() + " => " + valueType()->canonicalName() + ")";
|
return "mapping(" + keyType()->canonicalName() + " => " + valueType()->canonicalName() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
string TypeType::identifier() const
|
string TypeType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_type" + identifierList(actualType());
|
return "t_type" + identifierList(actualType());
|
||||||
}
|
}
|
||||||
@ -2935,7 +2920,7 @@ u256 ModifierType::storageSize() const
|
|||||||
solAssert(false, "Storage size of non-storable type type requested.");
|
solAssert(false, "Storage size of non-storable type type requested.");
|
||||||
}
|
}
|
||||||
|
|
||||||
string ModifierType::identifier() const
|
string ModifierType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_modifier" + identifierList(m_parameterTypes);
|
return "t_modifier" + identifierList(m_parameterTypes);
|
||||||
}
|
}
|
||||||
@ -2964,7 +2949,7 @@ string ModifierType::toString(bool _short) const
|
|||||||
return name + ")";
|
return name + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
string ModuleType::identifier() const
|
string ModuleType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return "t_module_" + std::to_string(m_sourceUnit.id());
|
return "t_module_" + std::to_string(m_sourceUnit.id());
|
||||||
}
|
}
|
||||||
@ -2990,7 +2975,7 @@ string ModuleType::toString(bool) const
|
|||||||
return string("module \"") + m_sourceUnit.annotation().path + string("\"");
|
return string("module \"") + m_sourceUnit.annotation().path + string("\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
string MagicType::identifier() const
|
string MagicType::richIdentifier() const
|
||||||
{
|
{
|
||||||
switch (m_kind)
|
switch (m_kind)
|
||||||
{
|
{
|
||||||
|
@ -163,10 +163,20 @@ public:
|
|||||||
/// @returns a valid solidity identifier such that two types should compare equal if and
|
/// @returns a valid solidity identifier such that two types should compare equal if and
|
||||||
/// only if they have the same identifier.
|
/// only if they have the same identifier.
|
||||||
/// The identifier should start with "t_".
|
/// The identifier should start with "t_".
|
||||||
|
/// Can contain characters which are invalid in identifiers.
|
||||||
|
virtual std::string richIdentifier() const = 0;
|
||||||
|
/// @returns a valid solidity identifier such that two types should compare equal if and
|
||||||
|
/// only if they have the same identifier.
|
||||||
|
/// The identifier should start with "t_".
|
||||||
|
/// Will not contain any character which would be invalid as an identifier.
|
||||||
|
std::string identifier() const { return escapeIdentifier(richIdentifier()); }
|
||||||
|
|
||||||
/// More complex identifier strings use "parentheses", where $_ is interpreted as as
|
/// More complex identifier strings use "parentheses", where $_ is interpreted as as
|
||||||
/// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that
|
/// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that
|
||||||
/// appears as part of a user-supplied identifier is escaped as _$$$_.
|
/// appears as part of a user-supplied identifier is escaped as _$$$_.
|
||||||
virtual std::string identifier() const = 0;
|
/// @returns an escaped identifier (will not contain any parenthesis or commas)
|
||||||
|
static std::string escapeIdentifier(std::string const& _identifier);
|
||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
@ -306,7 +316,7 @@ public:
|
|||||||
|
|
||||||
explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned);
|
explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned);
|
||||||
|
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
@ -353,7 +363,7 @@ public:
|
|||||||
|
|
||||||
explicit FixedPointType(int _totalBits, int _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
|
explicit FixedPointType(int _totalBits, int _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
|
||||||
|
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
@ -410,7 +420,7 @@ public:
|
|||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
@ -459,7 +469,7 @@ public:
|
|||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
@ -493,7 +503,7 @@ public:
|
|||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
||||||
@ -521,7 +531,7 @@ class BoolType: public Type
|
|||||||
public:
|
public:
|
||||||
BoolType() {}
|
BoolType() {}
|
||||||
virtual Category category() const override { return Category::Bool; }
|
virtual Category category() const override { return Category::Bool; }
|
||||||
virtual std::string identifier() const override { return "t_bool"; }
|
virtual std::string richIdentifier() const override { return "t_bool"; }
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
@ -621,7 +631,7 @@ public:
|
|||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(const Type& _other) const override;
|
virtual bool operator==(const Type& _other) const override;
|
||||||
virtual unsigned calldataEncodedSize(bool _padded) const override;
|
virtual unsigned calldataEncodedSize(bool _padded) const override;
|
||||||
virtual bool isDynamicallySized() const override { return m_hasDynamicLength; }
|
virtual bool isDynamicallySized() const override { return m_hasDynamicLength; }
|
||||||
@ -678,7 +688,7 @@ public:
|
|||||||
/// Contracts can be converted to themselves and to integers.
|
/// Contracts can be converted to themselves and to integers.
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual unsigned calldataEncodedSize(bool _padded ) const override
|
virtual unsigned calldataEncodedSize(bool _padded ) const override
|
||||||
{
|
{
|
||||||
@ -736,7 +746,7 @@ public:
|
|||||||
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) {}
|
||||||
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual unsigned calldataEncodedSize(bool _padded) const override;
|
virtual unsigned calldataEncodedSize(bool _padded) const override;
|
||||||
virtual bool isDynamicallyEncoded() const override;
|
virtual bool isDynamicallyEncoded() const override;
|
||||||
@ -791,7 +801,7 @@ public:
|
|||||||
virtual Category category() const override { return Category::Enum; }
|
virtual Category category() const override { return Category::Enum; }
|
||||||
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
|
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual unsigned calldataEncodedSize(bool _padded) const override
|
virtual unsigned calldataEncodedSize(bool _padded) const override
|
||||||
{
|
{
|
||||||
@ -832,7 +842,7 @@ public:
|
|||||||
virtual Category category() const override { return Category::Tuple; }
|
virtual Category category() const override { return Category::Tuple; }
|
||||||
explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {}
|
explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {}
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
|
virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual std::string toString(bool) const override;
|
virtual std::string toString(bool) const override;
|
||||||
@ -892,7 +902,8 @@ public:
|
|||||||
ByteArrayPush, ///< .push() to a dynamically sized byte array in storage
|
ByteArrayPush, ///< .push() to a dynamically sized byte array in storage
|
||||||
ObjectCreation, ///< array creation using new
|
ObjectCreation, ///< array creation using new
|
||||||
Assert, ///< assert()
|
Assert, ///< assert()
|
||||||
Require ///< require()
|
Require, ///< require()
|
||||||
|
GasLeft ///< gasleft()
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual Category category() const override { return Category::Function; }
|
virtual Category category() const override { return Category::Function; }
|
||||||
@ -966,7 +977,7 @@ public:
|
|||||||
/// @returns the "self" parameter type for a bound function
|
/// @returns the "self" parameter type for a bound function
|
||||||
TypePointer const& selfType() const;
|
TypePointer const& selfType() const;
|
||||||
|
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
@ -1070,7 +1081,7 @@ public:
|
|||||||
MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
|
MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
|
||||||
m_keyType(_keyType), m_valueType(_valueType) {}
|
m_keyType(_keyType), m_valueType(_valueType) {}
|
||||||
|
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
virtual std::string canonicalName() const override;
|
virtual std::string canonicalName() const override;
|
||||||
@ -1107,7 +1118,7 @@ public:
|
|||||||
TypePointer const& actualType() const { return m_actualType; }
|
TypePointer const& actualType() const { return m_actualType; }
|
||||||
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
virtual u256 storageSize() const override;
|
virtual u256 storageSize() const override;
|
||||||
@ -1135,7 +1146,7 @@ public:
|
|||||||
virtual u256 storageSize() const override;
|
virtual u256 storageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned sizeOnStack() const override { return 0; }
|
virtual unsigned sizeOnStack() const override { return 0; }
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
|
|
||||||
@ -1156,7 +1167,7 @@ public:
|
|||||||
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
|
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
|
||||||
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
virtual bool canLiveOutsideStorage() const override { return true; }
|
virtual bool canLiveOutsideStorage() const override { return true; }
|
||||||
@ -1186,7 +1197,7 @@ public:
|
|||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::string identifier() const override;
|
virtual std::string richIdentifier() const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
virtual bool canLiveOutsideStorage() const override { return true; }
|
virtual bool canLiveOutsideStorage() const override { return true; }
|
||||||
@ -1210,7 +1221,7 @@ class InaccessibleDynamicType: public Type
|
|||||||
public:
|
public:
|
||||||
virtual Category category() const override { return Category::InaccessibleDynamic; }
|
virtual Category category() const override { return Category::InaccessibleDynamic; }
|
||||||
|
|
||||||
virtual std::string identifier() const override { return "t_inaccessible"; }
|
virtual std::string richIdentifier() const override { return "t_inaccessible"; }
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
|
virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
|
virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
|
@ -22,10 +22,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/CompilerContext.h>
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
|
#include <libevmasm/Assembly.h>
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <libsolidity/codegen/CompilerContext.h>
|
|
||||||
#include <libevmasm/Assembly.h>
|
|
||||||
|
|
||||||
namespace dev {
|
namespace dev {
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
@ -33,11 +36,11 @@ namespace solidity {
|
|||||||
class Compiler
|
class Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
|
explicit Compiler(EVMVersion _evmVersion = EVMVersion{}, bool _optimize = false, unsigned _runs = 200):
|
||||||
m_optimize(_optimize),
|
m_optimize(_optimize),
|
||||||
m_optimizeRuns(_runs),
|
m_optimizeRuns(_runs),
|
||||||
m_runtimeContext(),
|
m_runtimeContext(_evmVersion),
|
||||||
m_context(&m_runtimeContext)
|
m_context(_evmVersion, &m_runtimeContext)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/// Compiles a contract.
|
/// Compiles a contract.
|
||||||
|
@ -319,7 +319,7 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
ErrorList errors;
|
ErrorList errors;
|
||||||
ErrorReporter errorReporter(errors);
|
ErrorReporter errorReporter(errors);
|
||||||
auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--");
|
auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--");
|
||||||
auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner);
|
auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false);
|
||||||
#ifdef SOL_OUTPUT_ASM
|
#ifdef SOL_OUTPUT_ASM
|
||||||
cout << assembly::AsmPrinter()(*parserResult) << endl;
|
cout << assembly::AsmPrinter()(*parserResult) << endl;
|
||||||
#endif
|
#endif
|
||||||
@ -329,6 +329,8 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
analyzerResult = assembly::AsmAnalyzer(
|
analyzerResult = assembly::AsmAnalyzer(
|
||||||
analysisInfo,
|
analysisInfo,
|
||||||
errorReporter,
|
errorReporter,
|
||||||
|
m_evmVersion,
|
||||||
|
boost::none,
|
||||||
assembly::AsmFlavour::Strict,
|
assembly::AsmFlavour::Strict,
|
||||||
identifierAccess.resolve
|
identifierAccess.resolve
|
||||||
).analyze(*parserResult);
|
).analyze(*parserResult);
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include <libsolidity/codegen/ABIFunctions.h>
|
#include <libsolidity/codegen/ABIFunctions.h>
|
||||||
|
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
@ -50,14 +52,17 @@ namespace solidity {
|
|||||||
class CompilerContext
|
class CompilerContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit CompilerContext(CompilerContext* _runtimeContext = nullptr):
|
explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr):
|
||||||
m_asm(std::make_shared<eth::Assembly>()),
|
m_asm(std::make_shared<eth::Assembly>()),
|
||||||
|
m_evmVersion(_evmVersion),
|
||||||
m_runtimeContext(_runtimeContext)
|
m_runtimeContext(_runtimeContext)
|
||||||
{
|
{
|
||||||
if (m_runtimeContext)
|
if (m_runtimeContext)
|
||||||
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; }
|
||||||
|
|
||||||
/// 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; }
|
||||||
/// @returns true if the given feature is enabled.
|
/// @returns true if the given feature is enabled.
|
||||||
@ -204,7 +209,7 @@ public:
|
|||||||
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, true, _runs); }
|
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); }
|
||||||
|
|
||||||
/// @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() { return m_runtimeContext; }
|
CompilerContext* runtimeContext() { return m_runtimeContext; }
|
||||||
@ -287,6 +292,8 @@ private:
|
|||||||
} m_functionCompilationQueue;
|
} m_functionCompilationQueue;
|
||||||
|
|
||||||
eth::AssemblyPointer m_asm;
|
eth::AssemblyPointer m_asm;
|
||||||
|
/// Version of the EVM to compile against.
|
||||||
|
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.
|
||||||
|
@ -903,6 +903,15 @@ bool ContractCompiler::visit(Throw const& _throw)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ContractCompiler::visit(EmitStatement const& _emit)
|
||||||
|
{
|
||||||
|
CompilerContext::LocationSetter locationSetter(m_context, _emit);
|
||||||
|
StackHeightChecker checker(m_context);
|
||||||
|
compileExpression(_emit.eventCall());
|
||||||
|
checker.check();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
|
bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
|
||||||
{
|
{
|
||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
@ -1050,7 +1059,7 @@ void ContractCompiler::compileExpression(Expression const& _expression, TypePoin
|
|||||||
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
|
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyPointer ContractCompiler::cloneRuntime()
|
eth::AssemblyPointer ContractCompiler::cloneRuntime() const
|
||||||
{
|
{
|
||||||
eth::Assembly a;
|
eth::Assembly a;
|
||||||
a << Instruction::CALLDATASIZE;
|
a << Instruction::CALLDATASIZE;
|
||||||
@ -1061,7 +1070,7 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime()
|
|||||||
// this is the address which has to be substituted by the linker.
|
// this is the address which has to be substituted by the linker.
|
||||||
//@todo implement as special "marker" AssemblyItem.
|
//@todo implement as special "marker" AssemblyItem.
|
||||||
a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
|
a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
|
||||||
a << u256(eth::GasCosts::callGas + 10) << Instruction::GAS << Instruction::SUB;
|
a << u256(eth::GasCosts::callGas(m_context.evmVersion()) + 10) << Instruction::GAS << Instruction::SUB;
|
||||||
a << Instruction::DELEGATECALL;
|
a << Instruction::DELEGATECALL;
|
||||||
//Propagate error condition (if DELEGATECALL pushes 0 on stack).
|
//Propagate error condition (if DELEGATECALL pushes 0 on stack).
|
||||||
a << Instruction::ISZERO;
|
a << Instruction::ISZERO;
|
||||||
|
@ -43,7 +43,7 @@ public:
|
|||||||
m_runtimeCompiler(_runtimeCompiler),
|
m_runtimeCompiler(_runtimeCompiler),
|
||||||
m_context(_context)
|
m_context(_context)
|
||||||
{
|
{
|
||||||
m_context = CompilerContext(_runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
|
m_context = CompilerContext(_context.evmVersion(), _runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compileContract(
|
void compileContract(
|
||||||
@ -109,6 +109,7 @@ private:
|
|||||||
virtual bool visit(Break const& _breakStatement) override;
|
virtual bool visit(Break const& _breakStatement) override;
|
||||||
virtual bool visit(Return const& _return) override;
|
virtual bool visit(Return const& _return) override;
|
||||||
virtual bool visit(Throw const& _throw) override;
|
virtual bool visit(Throw const& _throw) override;
|
||||||
|
virtual bool visit(EmitStatement const& _emit) override;
|
||||||
virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
|
virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
|
||||||
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
|
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
|
||||||
virtual bool visit(PlaceholderStatement const&) override;
|
virtual bool visit(PlaceholderStatement const&) override;
|
||||||
@ -124,7 +125,7 @@ private:
|
|||||||
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
||||||
|
|
||||||
/// @returns the runtime assembly for clone contracts.
|
/// @returns the runtime assembly for clone contracts.
|
||||||
static eth::AssemblyPointer cloneRuntime();
|
eth::AssemblyPointer cloneRuntime() const;
|
||||||
|
|
||||||
bool const m_optimise;
|
bool const m_optimise;
|
||||||
/// Pointer to the runtime compiler in case this is a creation compiler.
|
/// Pointer to the runtime compiler in case this is a creation compiler.
|
||||||
|
@ -765,7 +765,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::AddMod:
|
case FunctionType::Kind::AddMod:
|
||||||
case FunctionType::Kind::MulMod:
|
case FunctionType::Kind::MulMod:
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < 3; i ++)
|
arguments[2]->accept(*this);
|
||||||
|
utils().convertType(*arguments[2]->annotation().type, IntegerType(256));
|
||||||
|
m_context << Instruction::DUP1 << Instruction::ISZERO;
|
||||||
|
m_context.appendConditionalInvalid();
|
||||||
|
for (unsigned i = 1; i < 3; i ++)
|
||||||
{
|
{
|
||||||
arguments[2 - i]->accept(*this);
|
arguments[2 - i]->accept(*this);
|
||||||
utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256));
|
utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256));
|
||||||
@ -902,6 +906,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
m_context << success;
|
m_context << success;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::GasLeft:
|
||||||
|
m_context << Instruction::GAS;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
solAssert(false, "Invalid function type.");
|
solAssert(false, "Invalid function type.");
|
||||||
}
|
}
|
||||||
@ -1603,6 +1610,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
|
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
|
||||||
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
|
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
|
||||||
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
||||||
|
bool useStaticCall =
|
||||||
|
_functionType.stateMutability() <= StateMutability::View &&
|
||||||
|
m_context.experimentalFeatureActive(ExperimentalFeature::V050) &&
|
||||||
|
m_context.evmVersion().hasStaticCall();
|
||||||
|
|
||||||
unsigned retSize = 0;
|
unsigned retSize = 0;
|
||||||
if (returnSuccessCondition)
|
if (returnSuccessCondition)
|
||||||
@ -1667,16 +1678,19 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
utils().storeFreeMemoryPointer();
|
utils().storeFreeMemoryPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Touch the end of the output area so that we do not pay for memory resize during the call
|
if (!m_context.evmVersion().canOverchargeGasForCall())
|
||||||
// (which we would have to subtract from the gas left)
|
|
||||||
// We could also just use MLOAD; POP right before the gas calculation, but the optimizer
|
|
||||||
// would remove that, so we use MSTORE here.
|
|
||||||
if (!_functionType.gasSet() && retSize > 0)
|
|
||||||
{
|
{
|
||||||
m_context << u256(0);
|
// Touch the end of the output area so that we do not pay for memory resize during the call
|
||||||
utils().fetchFreeMemoryPointer();
|
// (which we would have to subtract from the gas left)
|
||||||
// This touches too much, but that way we save some rounding arithmetics
|
// We could also just use MLOAD; POP right before the gas calculation, but the optimizer
|
||||||
m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
|
// would remove that, so we use MSTORE here.
|
||||||
|
if (!_functionType.gasSet() && retSize > 0)
|
||||||
|
{
|
||||||
|
m_context << u256(0);
|
||||||
|
utils().fetchFreeMemoryPointer();
|
||||||
|
// This touches too much, but that way we save some rounding arithmetics
|
||||||
|
m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy function identifier to memory.
|
// Copy function identifier to memory.
|
||||||
@ -1728,6 +1742,8 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
// [value,] addr, gas (stack top)
|
// [value,] addr, gas (stack top)
|
||||||
if (isDelegateCall)
|
if (isDelegateCall)
|
||||||
solAssert(!_functionType.valueSet(), "Value set for delegatecall");
|
solAssert(!_functionType.valueSet(), "Value set for delegatecall");
|
||||||
|
else if (useStaticCall)
|
||||||
|
solAssert(!_functionType.valueSet(), "Value set for staticcall");
|
||||||
else if (_functionType.valueSet())
|
else if (_functionType.valueSet())
|
||||||
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
|
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
|
||||||
else
|
else
|
||||||
@ -1745,24 +1761,27 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
|
|
||||||
if (_functionType.gasSet())
|
if (_functionType.gasSet())
|
||||||
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
|
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
|
||||||
else if (m_context.experimentalFeatureActive(ExperimentalFeature::V050))
|
else if (m_context.evmVersion().canOverchargeGasForCall())
|
||||||
// Send all gas (requires tangerine whistle EVM)
|
// Send all gas (requires tangerine whistle EVM)
|
||||||
m_context << Instruction::GAS;
|
m_context << Instruction::GAS;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// send all gas except the amount needed to execute "SUB" and "CALL"
|
// send all gas except the amount needed to execute "SUB" and "CALL"
|
||||||
// @todo this retains too much gas for now, needs to be fine-tuned.
|
// @todo this retains too much gas for now, needs to be fine-tuned.
|
||||||
u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
|
u256 gasNeededByCaller = eth::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||||
if (_functionType.valueSet())
|
if (_functionType.valueSet())
|
||||||
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
|
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
|
||||||
if (!existenceChecked)
|
if (!existenceChecked)
|
||||||
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
|
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
|
||||||
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
|
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
|
||||||
}
|
}
|
||||||
|
// Order is important here, STATICCALL might overlap with DELEGATECALL.
|
||||||
if (isDelegateCall)
|
if (isDelegateCall)
|
||||||
m_context << Instruction::DELEGATECALL;
|
m_context << Instruction::DELEGATECALL;
|
||||||
else if (isCallCode)
|
else if (isCallCode)
|
||||||
m_context << Instruction::CALLCODE;
|
m_context << Instruction::CALLCODE;
|
||||||
|
else if (useStaticCall)
|
||||||
|
m_context << Instruction::STATICCALL;
|
||||||
else
|
else
|
||||||
m_context << Instruction::CALL;
|
m_context << Instruction::CALL;
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <libsolidity/formal/SMTLib2Interface.h>
|
#include <libsolidity/formal/SMTLib2Interface.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SSAVariable.h>
|
||||||
|
#include <libsolidity/formal/SymbolicIntVariable.h>
|
||||||
#include <libsolidity/formal/VariableUsage.h>
|
#include <libsolidity/formal/VariableUsage.h>
|
||||||
|
|
||||||
#include <libsolidity/interface/ErrorReporter.h>
|
#include <libsolidity/interface/ErrorReporter.h>
|
||||||
@ -69,8 +71,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function)
|
|||||||
// We only handle local variables, so we clear at the beginning of the function.
|
// We only handle local variables, so we clear at the beginning of the function.
|
||||||
// If we add storage variables, those should be cleared differently.
|
// If we add storage variables, those should be cleared differently.
|
||||||
m_interface->reset();
|
m_interface->reset();
|
||||||
m_currentSequenceCounter.clear();
|
m_variables.clear();
|
||||||
m_nextFreeSequenceCounter.clear();
|
|
||||||
m_pathConditions.clear();
|
m_pathConditions.clear();
|
||||||
m_conditionalExecutionHappened = false;
|
m_conditionalExecutionHappened = false;
|
||||||
initializeLocalVariables(_function);
|
initializeLocalVariables(_function);
|
||||||
@ -91,14 +92,18 @@ bool SMTChecker::visit(IfStatement const& _node)
|
|||||||
|
|
||||||
checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE.");
|
checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE.");
|
||||||
|
|
||||||
auto countersEndFalse = m_currentSequenceCounter;
|
|
||||||
auto countersEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition()));
|
auto countersEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition()));
|
||||||
vector<Declaration const*> touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement());
|
vector<Declaration const*> touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement());
|
||||||
|
decltype(countersEndTrue) countersEndFalse;
|
||||||
if (_node.falseStatement())
|
if (_node.falseStatement())
|
||||||
{
|
{
|
||||||
countersEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition()));
|
countersEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition()));
|
||||||
touchedVariables += m_variableUsage->touchedVariables(*_node.falseStatement());
|
touchedVariables += m_variableUsage->touchedVariables(*_node.falseStatement());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
countersEndFalse = m_variables;
|
||||||
|
}
|
||||||
|
|
||||||
mergeVariables(touchedVariables, expr(_node.condition()), countersEndTrue, countersEndFalse);
|
mergeVariables(touchedVariables, expr(_node.condition()), countersEndTrue, countersEndFalse);
|
||||||
|
|
||||||
@ -152,7 +157,7 @@ bool SMTChecker::visit(ForStatement const& _node)
|
|||||||
checkBooleanNotConstant(*_node.condition(), "For loop condition is always $VALUE.");
|
checkBooleanNotConstant(*_node.condition(), "For loop condition is always $VALUE.");
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter;
|
VariableSequenceCounters sequenceCountersStart = m_variables;
|
||||||
m_interface->push();
|
m_interface->push();
|
||||||
if (_node.condition())
|
if (_node.condition())
|
||||||
m_interface->addAssertion(expr(*_node.condition()));
|
m_interface->addAssertion(expr(*_node.condition()));
|
||||||
@ -163,7 +168,7 @@ bool SMTChecker::visit(ForStatement const& _node)
|
|||||||
m_interface->pop();
|
m_interface->pop();
|
||||||
|
|
||||||
m_conditionalExecutionHappened = true;
|
m_conditionalExecutionHappened = true;
|
||||||
m_currentSequenceCounter = sequenceCountersStart;
|
std::swap(sequenceCountersStart, m_variables);
|
||||||
|
|
||||||
resetVariables(touchedVariables);
|
resetVariables(touchedVariables);
|
||||||
|
|
||||||
@ -240,14 +245,14 @@ void SMTChecker::endVisit(TupleExpression const& _tuple)
|
|||||||
void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location)
|
void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
checkCondition(
|
checkCondition(
|
||||||
_value < minValue(_type),
|
_value < SymbolicIntVariable::minValue(_type),
|
||||||
_location,
|
_location,
|
||||||
"Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")",
|
"Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")",
|
||||||
"value",
|
"value",
|
||||||
&_value
|
&_value
|
||||||
);
|
);
|
||||||
checkCondition(
|
checkCondition(
|
||||||
_value > maxValue(_type),
|
_value > SymbolicIntVariable::maxValue(_type),
|
||||||
_location,
|
_location,
|
||||||
"Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")",
|
"Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")",
|
||||||
"value",
|
"value",
|
||||||
@ -365,7 +370,7 @@ 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<IntegerType const*>(_identifier.annotation().type.get()))
|
else if (SSAVariable::supportedType(_identifier.annotation().type.get()))
|
||||||
defineExpr(_identifier, currentValue(*decl));
|
defineExpr(_identifier, currentValue(*decl));
|
||||||
else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
|
else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
|
||||||
{
|
{
|
||||||
@ -514,7 +519,7 @@ SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _s
|
|||||||
|
|
||||||
SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition)
|
SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition)
|
||||||
{
|
{
|
||||||
VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter;
|
VariableSequenceCounters beforeVars = m_variables;
|
||||||
|
|
||||||
if (_condition)
|
if (_condition)
|
||||||
pushPathCondition(*_condition);
|
pushPathCondition(*_condition);
|
||||||
@ -523,8 +528,9 @@ SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _s
|
|||||||
popPathCondition();
|
popPathCondition();
|
||||||
|
|
||||||
m_conditionalExecutionHappened = true;
|
m_conditionalExecutionHappened = true;
|
||||||
std::swap(sequenceCountersStart, m_currentSequenceCounter);
|
std::swap(m_variables, beforeVars);
|
||||||
return sequenceCountersStart;
|
|
||||||
|
return beforeVars;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTChecker::checkCondition(
|
void SMTChecker::checkCondition(
|
||||||
@ -709,8 +715,8 @@ void SMTChecker::mergeVariables(vector<Declaration const*> const& _variables, sm
|
|||||||
set<Declaration const*> uniqueVars(_variables.begin(), _variables.end());
|
set<Declaration const*> uniqueVars(_variables.begin(), _variables.end());
|
||||||
for (auto const* decl: uniqueVars)
|
for (auto const* decl: uniqueVars)
|
||||||
{
|
{
|
||||||
int trueCounter = _countersEndTrue.at(decl);
|
int trueCounter = _countersEndTrue.at(decl).index();
|
||||||
int falseCounter = _countersEndFalse.at(decl);
|
int falseCounter = _countersEndFalse.at(decl).index();
|
||||||
solAssert(trueCounter != falseCounter, "");
|
solAssert(trueCounter != falseCounter, "");
|
||||||
m_interface->addAssertion(newValue(*decl) == smt::Expression::ite(
|
m_interface->addAssertion(newValue(*decl) == smt::Expression::ite(
|
||||||
_condition,
|
_condition,
|
||||||
@ -722,14 +728,10 @@ void SMTChecker::mergeVariables(vector<Declaration const*> const& _variables, sm
|
|||||||
|
|
||||||
bool SMTChecker::createVariable(VariableDeclaration const& _varDecl)
|
bool SMTChecker::createVariable(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
if (dynamic_cast<IntegerType const*>(_varDecl.type().get()))
|
if (SSAVariable::supportedType(_varDecl.type().get()))
|
||||||
{
|
{
|
||||||
solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, "");
|
|
||||||
solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, "");
|
|
||||||
solAssert(m_variables.count(&_varDecl) == 0, "");
|
solAssert(m_variables.count(&_varDecl) == 0, "");
|
||||||
m_currentSequenceCounter[&_varDecl] = 0;
|
m_variables.emplace(&_varDecl, SSAVariable(&_varDecl, *m_interface));
|
||||||
m_nextFreeSequenceCounter[&_varDecl] = 1;
|
|
||||||
m_variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -742,11 +744,6 @@ bool SMTChecker::createVariable(VariableDeclaration const& _varDecl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string SMTChecker::uniqueSymbol(Declaration const& _decl)
|
|
||||||
{
|
|
||||||
return _decl.name() + "_" + to_string(_decl.id());
|
|
||||||
}
|
|
||||||
|
|
||||||
string SMTChecker::uniqueSymbol(Expression const& _expr)
|
string SMTChecker::uniqueSymbol(Expression const& _expr)
|
||||||
{
|
{
|
||||||
return "expr_" + to_string(_expr.id());
|
return "expr_" + to_string(_expr.id());
|
||||||
@ -754,48 +751,38 @@ string SMTChecker::uniqueSymbol(Expression const& _expr)
|
|||||||
|
|
||||||
bool SMTChecker::knownVariable(Declaration const& _decl)
|
bool SMTChecker::knownVariable(Declaration const& _decl)
|
||||||
{
|
{
|
||||||
return m_currentSequenceCounter.count(&_decl);
|
return m_variables.count(&_decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::Expression SMTChecker::currentValue(Declaration const& _decl)
|
smt::Expression SMTChecker::currentValue(Declaration const& _decl)
|
||||||
{
|
{
|
||||||
solAssert(m_currentSequenceCounter.count(&_decl), "");
|
solAssert(knownVariable(_decl), "");
|
||||||
return valueAtSequence(_decl, m_currentSequenceCounter.at(&_decl));
|
return m_variables.at(&_decl)();
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::Expression SMTChecker::valueAtSequence(const Declaration& _decl, int _sequence)
|
smt::Expression SMTChecker::valueAtSequence(Declaration const& _decl, int _sequence)
|
||||||
{
|
{
|
||||||
return var(_decl)(_sequence);
|
solAssert(knownVariable(_decl), "");
|
||||||
|
return m_variables.at(&_decl)(_sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::Expression SMTChecker::newValue(Declaration const& _decl)
|
smt::Expression SMTChecker::newValue(Declaration const& _decl)
|
||||||
{
|
{
|
||||||
solAssert(m_nextFreeSequenceCounter.count(&_decl), "");
|
solAssert(knownVariable(_decl), "");
|
||||||
m_currentSequenceCounter[&_decl] = m_nextFreeSequenceCounter[&_decl]++;
|
++m_variables.at(&_decl);
|
||||||
return currentValue(_decl);
|
return m_variables.at(&_decl)();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTChecker::setZeroValue(Declaration const& _decl)
|
void SMTChecker::setZeroValue(Declaration const& _decl)
|
||||||
{
|
{
|
||||||
solAssert(_decl.type()->category() == Type::Category::Integer, "");
|
solAssert(knownVariable(_decl), "");
|
||||||
m_interface->addAssertion(currentValue(_decl) == 0);
|
m_variables.at(&_decl).setZeroValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTChecker::setUnknownValue(Declaration const& _decl)
|
void SMTChecker::setUnknownValue(Declaration const& _decl)
|
||||||
{
|
{
|
||||||
auto const& intType = dynamic_cast<IntegerType const&>(*_decl.type());
|
solAssert(knownVariable(_decl), "");
|
||||||
m_interface->addAssertion(currentValue(_decl) >= minValue(intType));
|
m_variables.at(&_decl).setUnknownValue();
|
||||||
m_interface->addAssertion(currentValue(_decl) <= maxValue(intType));
|
|
||||||
}
|
|
||||||
|
|
||||||
smt::Expression SMTChecker::minValue(IntegerType const& _t)
|
|
||||||
{
|
|
||||||
return smt::Expression(_t.minValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
smt::Expression SMTChecker::maxValue(IntegerType const& _t)
|
|
||||||
{
|
|
||||||
return smt::Expression(_t.maxValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::Expression SMTChecker::expr(Expression const& _e)
|
smt::Expression SMTChecker::expr(Expression const& _e)
|
||||||
@ -842,12 +829,6 @@ void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value)
|
|||||||
m_interface->addAssertion(expr(_e) == _value);
|
m_interface->addAssertion(expr(_e) == _value);
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::Expression SMTChecker::var(Declaration const& _decl)
|
|
||||||
{
|
|
||||||
solAssert(m_variables.count(&_decl), "");
|
|
||||||
return m_variables.at(&_decl);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SMTChecker::popPathCondition()
|
void SMTChecker::popPathCondition()
|
||||||
{
|
{
|
||||||
solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty.");
|
solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty.");
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include <libsolidity/formal/SolverInterface.h>
|
#include <libsolidity/formal/SolverInterface.h>
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SSAVariable.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
|
||||||
#include <libsolidity/interface/ReadFile.h>
|
#include <libsolidity/interface/ReadFile.h>
|
||||||
@ -76,7 +78,7 @@ private:
|
|||||||
void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location);
|
void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location);
|
||||||
|
|
||||||
/// Maps a variable to an SSA index.
|
/// Maps a variable to an SSA index.
|
||||||
using VariableSequenceCounters = std::map<Declaration const*, int>;
|
using VariableSequenceCounters = std::map<Declaration const*, SSAVariable>;
|
||||||
|
|
||||||
/// Visits the branch given by the statement, pushes and pops the current path conditions.
|
/// Visits the branch given by the statement, pushes and pops the current path conditions.
|
||||||
/// @param _condition if present, asserts that this condition is true within the branch.
|
/// @param _condition if present, asserts that this condition is true within the branch.
|
||||||
@ -118,7 +120,6 @@ private:
|
|||||||
/// This fails if the type is not supported.
|
/// This fails if the type is not supported.
|
||||||
bool createVariable(VariableDeclaration const& _varDecl);
|
bool createVariable(VariableDeclaration const& _varDecl);
|
||||||
|
|
||||||
static std::string uniqueSymbol(Declaration const& _decl);
|
|
||||||
static std::string uniqueSymbol(Expression const& _expr);
|
static std::string uniqueSymbol(Expression const& _expr);
|
||||||
|
|
||||||
/// @returns true if _delc is a variable that is known at the current point, i.e.
|
/// @returns true if _delc is a variable that is known at the current point, i.e.
|
||||||
@ -139,18 +140,12 @@ private:
|
|||||||
/// Resets the variable to an unknown value (in its range).
|
/// Resets the variable to an unknown value (in its range).
|
||||||
void setUnknownValue(Declaration const& decl);
|
void setUnknownValue(Declaration const& decl);
|
||||||
|
|
||||||
static smt::Expression minValue(IntegerType const& _t);
|
|
||||||
static smt::Expression maxValue(IntegerType const& _t);
|
|
||||||
|
|
||||||
/// Returns the expression corresponding to the AST node. Throws if the expression does not exist.
|
/// Returns the expression corresponding to the AST node. Throws if the expression does not exist.
|
||||||
smt::Expression expr(Expression const& _e);
|
smt::Expression expr(Expression const& _e);
|
||||||
/// Creates the expression (value can be arbitrary)
|
/// Creates the expression (value can be arbitrary)
|
||||||
void createExpr(Expression const& _e);
|
void createExpr(Expression const& _e);
|
||||||
/// Creates the expression and sets its value.
|
/// Creates the expression and sets its value.
|
||||||
void defineExpr(Expression const& _e, smt::Expression _value);
|
void defineExpr(Expression const& _e, smt::Expression _value);
|
||||||
/// Returns the function declaration corresponding to the given variable.
|
|
||||||
/// The function takes one argument which is the "sequence number".
|
|
||||||
smt::Expression var(Declaration const& _decl);
|
|
||||||
|
|
||||||
/// Adds a new path condition
|
/// Adds a new path condition
|
||||||
void pushPathCondition(smt::Expression const& _e);
|
void pushPathCondition(smt::Expression const& _e);
|
||||||
@ -166,10 +161,8 @@ private:
|
|||||||
std::shared_ptr<smt::SolverInterface> m_interface;
|
std::shared_ptr<smt::SolverInterface> m_interface;
|
||||||
std::shared_ptr<VariableUsage> m_variableUsage;
|
std::shared_ptr<VariableUsage> m_variableUsage;
|
||||||
bool m_conditionalExecutionHappened = false;
|
bool m_conditionalExecutionHappened = false;
|
||||||
std::map<Declaration const*, int> m_currentSequenceCounter;
|
|
||||||
std::map<Declaration const*, int> m_nextFreeSequenceCounter;
|
|
||||||
std::map<Expression const*, smt::Expression> m_expressions;
|
std::map<Expression const*, smt::Expression> m_expressions;
|
||||||
std::map<Declaration const*, smt::Expression> m_variables;
|
std::map<Declaration const*, SSAVariable> m_variables;
|
||||||
std::vector<smt::Expression> m_pathConditions;
|
std::vector<smt::Expression> m_pathConditions;
|
||||||
ErrorReporter& m_errorReporter;
|
ErrorReporter& m_errorReporter;
|
||||||
|
|
||||||
|
73
libsolidity/formal/SSAVariable.cpp
Normal file
73
libsolidity/formal/SSAVariable.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SSAVariable.h>
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SymbolicIntVariable.h>
|
||||||
|
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
SSAVariable::SSAVariable(
|
||||||
|
Declaration const* _decl,
|
||||||
|
smt::SolverInterface& _interface
|
||||||
|
)
|
||||||
|
{
|
||||||
|
resetIndex();
|
||||||
|
|
||||||
|
if (dynamic_cast<IntegerType const*>(_decl->type().get()))
|
||||||
|
m_symbolicVar = make_shared<SymbolicIntVariable>(_decl, _interface);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(false, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSAVariable::supportedType(Type const* _decl)
|
||||||
|
{
|
||||||
|
return dynamic_cast<IntegerType const*>(_decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSAVariable::resetIndex()
|
||||||
|
{
|
||||||
|
m_currentSequenceCounter = 0;
|
||||||
|
m_nextFreeSequenceCounter.reset (new int);
|
||||||
|
*m_nextFreeSequenceCounter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SSAVariable::index() const
|
||||||
|
{
|
||||||
|
return m_currentSequenceCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SSAVariable::next() const
|
||||||
|
{
|
||||||
|
return *m_nextFreeSequenceCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSAVariable::setZeroValue()
|
||||||
|
{
|
||||||
|
m_symbolicVar->setZeroValue(index());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSAVariable::setUnknownValue()
|
||||||
|
{
|
||||||
|
m_symbolicVar->setUnknownValue(index());
|
||||||
|
}
|
88
libsolidity/formal/SSAVariable.h
Normal file
88
libsolidity/formal/SSAVariable.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SymbolicVariable.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
class Declaration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the SSA representation of a program variable.
|
||||||
|
*/
|
||||||
|
class SSAVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @param _decl Used to determine the type and forwarded to the symbolic var.
|
||||||
|
/// @param _interface Forwarded to the symbolic var such that it can give constraints to the solver.
|
||||||
|
SSAVariable(
|
||||||
|
Declaration const* _decl,
|
||||||
|
smt::SolverInterface& _interface
|
||||||
|
);
|
||||||
|
|
||||||
|
void resetIndex();
|
||||||
|
|
||||||
|
/// This function returns the current index of this SSA variable.
|
||||||
|
int index() const;
|
||||||
|
/// This function returns the next free index of this SSA variable.
|
||||||
|
int next() const;
|
||||||
|
|
||||||
|
int operator++()
|
||||||
|
{
|
||||||
|
return m_currentSequenceCounter = (*m_nextFreeSequenceCounter)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression operator()() const
|
||||||
|
{
|
||||||
|
return valueAtSequence(index());
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression operator()(int _seq) const
|
||||||
|
{
|
||||||
|
return valueAtSequence(_seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// These two functions forward the call to the symbolic var
|
||||||
|
/// which generates the constraints according to the type.
|
||||||
|
void setZeroValue();
|
||||||
|
void setUnknownValue();
|
||||||
|
|
||||||
|
/// So far Int is supported.
|
||||||
|
static bool supportedType(Type const* _decl);
|
||||||
|
|
||||||
|
private:
|
||||||
|
smt::Expression valueAtSequence(int _seq) const
|
||||||
|
{
|
||||||
|
return (*m_symbolicVar)(_seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SymbolicVariable> m_symbolicVar = nullptr;
|
||||||
|
int m_currentSequenceCounter;
|
||||||
|
/// The next free sequence counter is a shared pointer because we want
|
||||||
|
/// the copy and the copied to share it.
|
||||||
|
std::shared_ptr<int> m_nextFreeSequenceCounter;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
56
libsolidity/formal/SymbolicIntVariable.cpp
Normal file
56
libsolidity/formal/SymbolicIntVariable.cpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SymbolicIntVariable.h>
|
||||||
|
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
SymbolicIntVariable::SymbolicIntVariable(
|
||||||
|
Declaration const* _decl,
|
||||||
|
smt::SolverInterface& _interface
|
||||||
|
):
|
||||||
|
SymbolicVariable(_decl, _interface)
|
||||||
|
{
|
||||||
|
solAssert(m_declaration->type()->category() == Type::Category::Integer, "");
|
||||||
|
m_expression = make_shared<smt::Expression>(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolicIntVariable::setZeroValue(int _seq)
|
||||||
|
{
|
||||||
|
m_interface.addAssertion(valueAtSequence(_seq) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolicIntVariable::setUnknownValue(int _seq)
|
||||||
|
{
|
||||||
|
auto const& intType = dynamic_cast<IntegerType const&>(*m_declaration->type());
|
||||||
|
m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType));
|
||||||
|
m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType));
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t)
|
||||||
|
{
|
||||||
|
return smt::Expression(_t.minValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression SymbolicIntVariable::maxValue(IntegerType const& _t)
|
||||||
|
{
|
||||||
|
return smt::Expression(_t.maxValue());
|
||||||
|
}
|
50
libsolidity/formal/SymbolicIntVariable.h
Normal file
50
libsolidity/formal/SymbolicIntVariable.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SymbolicVariable.h>
|
||||||
|
|
||||||
|
#include <libsolidity/ast/Types.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialization of SymbolicVariable for Integers
|
||||||
|
*/
|
||||||
|
class SymbolicIntVariable: public SymbolicVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SymbolicIntVariable(
|
||||||
|
Declaration const* _decl,
|
||||||
|
smt::SolverInterface& _interface
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Sets the var to 0.
|
||||||
|
void setZeroValue(int _seq);
|
||||||
|
/// Sets the variable to the full valid value range.
|
||||||
|
void setUnknownValue(int _seq);
|
||||||
|
|
||||||
|
static smt::Expression minValue(IntegerType const& _t);
|
||||||
|
static smt::Expression maxValue(IntegerType const& _t);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
40
libsolidity/formal/SymbolicVariable.cpp
Normal file
40
libsolidity/formal/SymbolicVariable.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SymbolicVariable.h>
|
||||||
|
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
SymbolicVariable::SymbolicVariable(
|
||||||
|
Declaration const* _decl,
|
||||||
|
smt::SolverInterface& _interface
|
||||||
|
):
|
||||||
|
m_declaration(_decl),
|
||||||
|
m_interface(_interface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
string SymbolicVariable::uniqueSymbol() const
|
||||||
|
{
|
||||||
|
return m_declaration->name() + "_" + to_string(m_declaration->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
69
libsolidity/formal/SymbolicVariable.h
Normal file
69
libsolidity/formal/SymbolicVariable.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SolverInterface.h>
|
||||||
|
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
class Declaration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the symbolic version of a program variable.
|
||||||
|
*/
|
||||||
|
class SymbolicVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SymbolicVariable(
|
||||||
|
Declaration const* _decl,
|
||||||
|
smt::SolverInterface& _interface
|
||||||
|
);
|
||||||
|
|
||||||
|
smt::Expression operator()(int _seq) const
|
||||||
|
{
|
||||||
|
return valueAtSequence(_seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string uniqueSymbol() const;
|
||||||
|
|
||||||
|
/// Sets the var to the default value of its type.
|
||||||
|
virtual void setZeroValue(int _seq) = 0;
|
||||||
|
/// The unknown value is the full range of valid values,
|
||||||
|
/// and that's sub-type dependent.
|
||||||
|
virtual void setUnknownValue(int _seq) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
smt::Expression valueAtSequence(int _seq) const
|
||||||
|
{
|
||||||
|
return (*m_expression)(_seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
Declaration const* m_declaration;
|
||||||
|
std::shared_ptr<smt::Expression> m_expression = nullptr;
|
||||||
|
smt::SolverInterface& m_interface;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ using namespace dev::solidity::smt;
|
|||||||
Z3Interface::Z3Interface():
|
Z3Interface::Z3Interface():
|
||||||
m_solver(m_context)
|
m_solver(m_context)
|
||||||
{
|
{
|
||||||
|
z3::set_param("rewriter.pull_cheap_ite", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Z3Interface::reset()
|
void Z3Interface::reset()
|
||||||
|
@ -54,7 +54,10 @@ bool AsmAnalyzer::analyze(Block const& _block)
|
|||||||
|
|
||||||
bool AsmAnalyzer::operator()(Label const& _label)
|
bool AsmAnalyzer::operator()(Label const& _label)
|
||||||
{
|
{
|
||||||
solAssert(m_flavour == AsmFlavour::Loose, "");
|
checkLooseFeature(
|
||||||
|
_label.location,
|
||||||
|
"The use of labels is deprecated. Please use \"if\", \"switch\", \"for\" or function calls instead."
|
||||||
|
);
|
||||||
m_info.stackHeightInfo[&_label] = m_stackHeight;
|
m_info.stackHeightInfo[&_label] = m_stackHeight;
|
||||||
warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location);
|
warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location);
|
||||||
return true;
|
return true;
|
||||||
@ -62,7 +65,10 @@ bool AsmAnalyzer::operator()(Label const& _label)
|
|||||||
|
|
||||||
bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
|
bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
|
||||||
{
|
{
|
||||||
solAssert(m_flavour == AsmFlavour::Loose, "");
|
checkLooseFeature(
|
||||||
|
_instruction.location,
|
||||||
|
"The use of non-functional instructions is deprecated. Please use functional notation instead."
|
||||||
|
);
|
||||||
auto const& info = instructionInfo(_instruction.instruction);
|
auto const& info = instructionInfo(_instruction.instruction);
|
||||||
m_stackHeight += info.ret - info.args;
|
m_stackHeight += info.ret - info.args;
|
||||||
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
|
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
|
||||||
@ -82,6 +88,19 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value) > u256(-1))
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_literal.location,
|
||||||
|
"Number literal too large (> 256 bits)"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (_literal.kind == assembly::LiteralKind::Boolean)
|
||||||
|
{
|
||||||
|
solAssert(m_flavour == AsmFlavour::IULIA, "");
|
||||||
|
solAssert(_literal.value == "true" || _literal.value == "false", "");
|
||||||
|
}
|
||||||
m_info.stackHeightInfo[&_literal] = m_stackHeight;
|
m_info.stackHeightInfo[&_literal] = m_stackHeight;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -157,18 +176,31 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
|
|||||||
|
|
||||||
bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement)
|
bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement)
|
||||||
{
|
{
|
||||||
size_t initialStackHeight = m_stackHeight;
|
int initialStackHeight = m_stackHeight;
|
||||||
bool success = boost::apply_visitor(*this, _statement.expression);
|
bool success = boost::apply_visitor(*this, _statement.expression);
|
||||||
if (m_flavour != AsmFlavour::Loose)
|
if (m_stackHeight != initialStackHeight && (m_flavour != AsmFlavour::Loose || m_errorTypeForLoose))
|
||||||
if (!expectDeposit(0, initialStackHeight, _statement.location))
|
{
|
||||||
|
Error::Type errorType = m_flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError;
|
||||||
|
string msg =
|
||||||
|
"Top-level expressions are not supposed to return values (this expression returns " +
|
||||||
|
boost::lexical_cast<string>(m_stackHeight - initialStackHeight) +
|
||||||
|
" value" +
|
||||||
|
(m_stackHeight - initialStackHeight == 1 ? "" : "s") +
|
||||||
|
"). Use ``pop()`` or assign them.";
|
||||||
|
m_errorReporter.error(errorType, _statement.location, msg);
|
||||||
|
if (errorType != Error::Type::Warning)
|
||||||
success = false;
|
success = false;
|
||||||
|
}
|
||||||
m_info.stackHeightInfo[&_statement] = m_stackHeight;
|
m_info.stackHeightInfo[&_statement] = m_stackHeight;
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment)
|
bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment)
|
||||||
{
|
{
|
||||||
solAssert(m_flavour == AsmFlavour::Loose, "");
|
checkLooseFeature(
|
||||||
|
_assignment.location,
|
||||||
|
"The use of stack assignment is deprecated. Please use assignment in functional notation instead."
|
||||||
|
);
|
||||||
bool success = checkAssignment(_assignment.variableName, size_t(-1));
|
bool success = checkAssignment(_assignment.variableName, size_t(-1));
|
||||||
m_info.stackHeightInfo[&_assignment] = m_stackHeight;
|
m_info.stackHeightInfo[&_assignment] = m_stackHeight;
|
||||||
return success;
|
return success;
|
||||||
@ -520,26 +552,66 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc
|
|||||||
|
|
||||||
void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location)
|
void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
static set<solidity::Instruction> futureInstructions{
|
// We assume that returndatacopy, returndatasize and staticcall are either all available
|
||||||
solidity::Instruction::CREATE2,
|
// or all not available.
|
||||||
solidity::Instruction::RETURNDATACOPY,
|
solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), "");
|
||||||
solidity::Instruction::RETURNDATASIZE,
|
|
||||||
solidity::Instruction::STATICCALL
|
if (_instr == solidity::Instruction::CREATE2)
|
||||||
};
|
|
||||||
if (futureInstructions.count(_instr))
|
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
_location,
|
_location,
|
||||||
"The \"" +
|
"The \"" +
|
||||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||||
+ "\" instruction is only available after " +
|
+ "\" instruction is not supported by the VM version \"" +
|
||||||
"the Metropolis hard fork. Before that it acts as an invalid instruction."
|
"" + m_evmVersion.name() +
|
||||||
|
"\" you are currently compiling for. " +
|
||||||
|
"It will be interpreted as an invalid instruction on this VM."
|
||||||
|
);
|
||||||
|
else if ((
|
||||||
|
_instr == solidity::Instruction::RETURNDATACOPY ||
|
||||||
|
_instr == solidity::Instruction::RETURNDATASIZE ||
|
||||||
|
_instr == solidity::Instruction::STATICCALL
|
||||||
|
) && !m_evmVersion.supportsReturndata())
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_location,
|
||||||
|
"The \"" +
|
||||||
|
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||||
|
+ "\" instruction is only available for Byzantium-compatible VMs. " +
|
||||||
|
"You are currently compiling for \"" +
|
||||||
|
m_evmVersion.name() +
|
||||||
|
"\", where it will be interpreted as an invalid instruction."
|
||||||
|
);
|
||||||
|
else if ((
|
||||||
|
_instr == solidity::Instruction::SHL ||
|
||||||
|
_instr == solidity::Instruction::SHR ||
|
||||||
|
_instr == solidity::Instruction::SAR
|
||||||
|
) && !m_evmVersion.hasBitwiseShifting())
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_location,
|
||||||
|
"The \"" +
|
||||||
|
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||||
|
+ "\" instruction is only available for Constantinople-compatible VMs. " +
|
||||||
|
"You are currently compiling for \"" +
|
||||||
|
m_evmVersion.name() +
|
||||||
|
"\", where it will be interpreted as an invalid instruction."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
|
if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
|
||||||
m_errorReporter.warning(
|
{
|
||||||
|
solAssert(m_flavour == AsmFlavour::Loose, "");
|
||||||
|
m_errorReporter.error(
|
||||||
|
m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning,
|
||||||
_location,
|
_location,
|
||||||
"Jump instructions and labels are low-level EVM features that can lead to "
|
"Jump instructions and labels are low-level EVM features that can lead to "
|
||||||
"incorrect stack access. Because of that they are discouraged. "
|
"incorrect stack access. Because of that they are discouraged. "
|
||||||
"Please consider using \"switch\", \"if\" or \"for\" statements instead."
|
"Please consider using \"switch\", \"if\" or \"for\" statements instead."
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description)
|
||||||
|
{
|
||||||
|
if (m_flavour != AsmFlavour::Loose)
|
||||||
|
solAssert(false, _description);
|
||||||
|
else if (m_errorTypeForLoose)
|
||||||
|
m_errorReporter.error(*m_errorTypeForLoose, _location, _description);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libsolidity/interface/Exceptions.h>
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
#include <libsolidity/inlineasm/AsmScope.h>
|
#include <libsolidity/inlineasm/AsmScope.h>
|
||||||
|
|
||||||
@ -29,6 +30,7 @@
|
|||||||
#include <libsolidity/inlineasm/AsmDataForward.h>
|
#include <libsolidity/inlineasm/AsmDataForward.h>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -54,9 +56,18 @@ public:
|
|||||||
explicit AsmAnalyzer(
|
explicit AsmAnalyzer(
|
||||||
AsmAnalysisInfo& _analysisInfo,
|
AsmAnalysisInfo& _analysisInfo,
|
||||||
ErrorReporter& _errorReporter,
|
ErrorReporter& _errorReporter,
|
||||||
|
EVMVersion _evmVersion,
|
||||||
|
boost::optional<Error::Type> _errorTypeForLoose,
|
||||||
AsmFlavour _flavour = AsmFlavour::Loose,
|
AsmFlavour _flavour = AsmFlavour::Loose,
|
||||||
julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver()
|
julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver()
|
||||||
): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {}
|
):
|
||||||
|
m_resolver(_resolver),
|
||||||
|
m_info(_analysisInfo),
|
||||||
|
m_errorReporter(_errorReporter),
|
||||||
|
m_evmVersion(_evmVersion),
|
||||||
|
m_flavour(_flavour),
|
||||||
|
m_errorTypeForLoose(_errorTypeForLoose)
|
||||||
|
{}
|
||||||
|
|
||||||
bool analyze(assembly::Block const& _block);
|
bool analyze(assembly::Block const& _block);
|
||||||
|
|
||||||
@ -89,6 +100,11 @@ private:
|
|||||||
void expectValidType(std::string const& type, SourceLocation const& _location);
|
void expectValidType(std::string const& type, SourceLocation const& _location);
|
||||||
void warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location);
|
void warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location);
|
||||||
|
|
||||||
|
/// Depending on @a m_flavour and @a m_errorTypeForLoose, throws an internal compiler
|
||||||
|
/// exception (if the flavour is not Loose), reports an error/warning
|
||||||
|
/// (if m_errorTypeForLoose is set) or does nothing.
|
||||||
|
void checkLooseFeature(SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
int m_stackHeight = 0;
|
int m_stackHeight = 0;
|
||||||
julia::ExternalIdentifierAccess::Resolver m_resolver;
|
julia::ExternalIdentifierAccess::Resolver m_resolver;
|
||||||
Scope* m_currentScope = nullptr;
|
Scope* m_currentScope = nullptr;
|
||||||
@ -97,7 +113,9 @@ private:
|
|||||||
std::set<Scope::Variable const*> m_activeVariables;
|
std::set<Scope::Variable const*> m_activeVariables;
|
||||||
AsmAnalysisInfo& m_info;
|
AsmAnalysisInfo& m_info;
|
||||||
ErrorReporter& m_errorReporter;
|
ErrorReporter& m_errorReporter;
|
||||||
|
EVMVersion m_evmVersion;
|
||||||
AsmFlavour m_flavour = AsmFlavour::Loose;
|
AsmFlavour m_flavour = AsmFlavour::Loose;
|
||||||
|
boost::optional<Error::Type> m_errorTypeForLoose;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,13 +34,16 @@ using namespace dev;
|
|||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
using namespace dev::solidity::assembly;
|
using namespace dev::solidity::assembly;
|
||||||
|
|
||||||
shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
|
shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner)
|
||||||
{
|
{
|
||||||
m_recursionDepth = 0;
|
m_recursionDepth = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_scanner = _scanner;
|
m_scanner = _scanner;
|
||||||
return make_shared<Block>(parseBlock());
|
auto block = make_shared<Block>(parseBlock());
|
||||||
|
if (!_reuseScanner)
|
||||||
|
expectToken(Token::EOS);
|
||||||
|
return block;
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
catch (FatalError const&)
|
||||||
{
|
{
|
||||||
|
@ -41,8 +41,9 @@ public:
|
|||||||
ParserBase(_errorReporter), m_flavour(_flavour) {}
|
ParserBase(_errorReporter), m_flavour(_flavour) {}
|
||||||
|
|
||||||
/// Parses an inline assembly block starting with `{` and ending with `}`.
|
/// Parses an inline assembly block starting with `{` and ending with `}`.
|
||||||
|
/// @param _reuseScanner if true, do check for end of input after the `}`.
|
||||||
/// @returns an empty shared pointer on error.
|
/// @returns an empty shared pointer on error.
|
||||||
std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner);
|
std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>;
|
using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>;
|
||||||
|
@ -69,7 +69,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
|
|||||||
m_errors.clear();
|
m_errors.clear();
|
||||||
m_analysisSuccessful = false;
|
m_analysisSuccessful = false;
|
||||||
m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName);
|
m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName);
|
||||||
m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner);
|
m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false);
|
||||||
if (!m_errorReporter.errors().empty())
|
if (!m_errorReporter.errors().empty())
|
||||||
return false;
|
return false;
|
||||||
solAssert(m_parserResult, "");
|
solAssert(m_parserResult, "");
|
||||||
@ -91,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann
|
|||||||
bool AssemblyStack::analyzeParsed()
|
bool AssemblyStack::analyzeParsed()
|
||||||
{
|
{
|
||||||
m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
|
m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
|
||||||
assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, languageToAsmFlavour(m_language));
|
assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language));
|
||||||
m_analysisSuccessful = analyzer.analyze(*m_parserResult);
|
m_analysisSuccessful = analyzer.analyze(*m_parserResult);
|
||||||
return m_analysisSuccessful;
|
return m_analysisSuccessful;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libsolidity/interface/ErrorReporter.h>
|
#include <libsolidity/interface/ErrorReporter.h>
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
#include <libevmasm/LinkerObject.h>
|
#include <libevmasm/LinkerObject.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -54,8 +56,8 @@ public:
|
|||||||
enum class Language { JULIA, Assembly, StrictAssembly };
|
enum class Language { JULIA, Assembly, StrictAssembly };
|
||||||
enum class Machine { EVM, EVM15, eWasm };
|
enum class Machine { EVM, EVM15, eWasm };
|
||||||
|
|
||||||
explicit AssemblyStack(Language _language = Language::Assembly):
|
explicit AssemblyStack(EVMVersion _evmVersion = EVMVersion(), Language _language = Language::Assembly):
|
||||||
m_language(_language), m_errorReporter(m_errors)
|
m_language(_language), m_evmVersion(_evmVersion), m_errorReporter(m_errors)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/// @returns the scanner used during parsing
|
/// @returns the scanner used during parsing
|
||||||
@ -82,6 +84,7 @@ private:
|
|||||||
bool analyzeParsed();
|
bool analyzeParsed();
|
||||||
|
|
||||||
Language m_language = Language::Assembly;
|
Language m_language = Language::Assembly;
|
||||||
|
EVMVersion m_evmVersion;
|
||||||
|
|
||||||
std::shared_ptr<Scanner> m_scanner;
|
std::shared_ptr<Scanner> m_scanner;
|
||||||
|
|
||||||
|
@ -74,6 +74,12 @@ void CompilerStack::setRemappings(vector<string> const& _remappings)
|
|||||||
swap(m_remappings, remappings);
|
swap(m_remappings, remappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerStack::setEVMVersion(EVMVersion _version)
|
||||||
|
{
|
||||||
|
solAssert(m_stackState < State::ParsingSuccessful, "Set EVM version after parsing.");
|
||||||
|
m_evmVersion = _version;
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerStack::reset(bool _keepSources)
|
void CompilerStack::reset(bool _keepSources)
|
||||||
{
|
{
|
||||||
if (_keepSources)
|
if (_keepSources)
|
||||||
@ -88,6 +94,7 @@ void CompilerStack::reset(bool _keepSources)
|
|||||||
m_sources.clear();
|
m_sources.clear();
|
||||||
}
|
}
|
||||||
m_libraries.clear();
|
m_libraries.clear();
|
||||||
|
m_evmVersion = EVMVersion();
|
||||||
m_optimize = false;
|
m_optimize = false;
|
||||||
m_optimizeRuns = 200;
|
m_optimizeRuns = 200;
|
||||||
m_globalContext.reset();
|
m_globalContext.reset();
|
||||||
@ -198,7 +205,7 @@ bool CompilerStack::analyze()
|
|||||||
m_contracts[contract->fullyQualifiedName()].contract = contract;
|
m_contracts[contract->fullyQualifiedName()].contract = contract;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeChecker typeChecker(m_errorReporter);
|
TypeChecker typeChecker(m_evmVersion, m_errorReporter);
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
@ -677,7 +684,7 @@ void CompilerStack::compileContract(
|
|||||||
for (auto const* dependency: _contract.annotation().contractDependencies)
|
for (auto const* dependency: _contract.annotation().contractDependencies)
|
||||||
compileContract(*dependency, _compiledContracts);
|
compileContract(*dependency, _compiledContracts);
|
||||||
|
|
||||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns);
|
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimize, m_optimizeRuns);
|
||||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||||
string metadata = createMetadata(compiledContract);
|
string metadata = createMetadata(compiledContract);
|
||||||
bytes cborEncodedHash =
|
bytes cborEncodedHash =
|
||||||
@ -736,7 +743,7 @@ void CompilerStack::compileContract(
|
|||||||
{
|
{
|
||||||
if (!_contract.isLibrary())
|
if (!_contract.isLibrary())
|
||||||
{
|
{
|
||||||
Compiler cloneCompiler(m_optimize, m_optimizeRuns);
|
Compiler cloneCompiler(m_evmVersion, m_optimize, m_optimizeRuns);
|
||||||
cloneCompiler.compileClone(_contract, _compiledContracts);
|
cloneCompiler.compileClone(_contract, _compiledContracts);
|
||||||
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
||||||
}
|
}
|
||||||
@ -838,6 +845,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const
|
|||||||
}
|
}
|
||||||
meta["settings"]["optimizer"]["enabled"] = m_optimize;
|
meta["settings"]["optimizer"]["enabled"] = m_optimize;
|
||||||
meta["settings"]["optimizer"]["runs"] = m_optimizeRuns;
|
meta["settings"]["optimizer"]["runs"] = m_optimizeRuns;
|
||||||
|
meta["settings"]["evmVersion"] = m_evmVersion.name();
|
||||||
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
|
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
|
||||||
_contract.contract->annotation().canonicalName;
|
_contract.contract->annotation().canonicalName;
|
||||||
|
|
||||||
@ -951,11 +959,12 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
|||||||
return Json::Value();
|
return Json::Value();
|
||||||
|
|
||||||
using Gas = GasEstimator::GasConsumption;
|
using Gas = GasEstimator::GasConsumption;
|
||||||
|
GasEstimator gasEstimator(m_evmVersion);
|
||||||
Json::Value output(Json::objectValue);
|
Json::Value output(Json::objectValue);
|
||||||
|
|
||||||
if (eth::AssemblyItems const* items = assemblyItems(_contractName))
|
if (eth::AssemblyItems const* items = assemblyItems(_contractName))
|
||||||
{
|
{
|
||||||
Gas executionGas = GasEstimator::functionalEstimation(*items);
|
Gas executionGas = gasEstimator.functionalEstimation(*items);
|
||||||
u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
|
u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
|
||||||
Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
|
Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
|
||||||
|
|
||||||
@ -976,14 +985,14 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
|||||||
for (auto it: contract.interfaceFunctions())
|
for (auto it: contract.interfaceFunctions())
|
||||||
{
|
{
|
||||||
string sig = it.second->externalSignature();
|
string sig = it.second->externalSignature();
|
||||||
externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
|
externalFunctions[sig] = gasToJson(gasEstimator.functionalEstimation(*items, sig));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contract.fallbackFunction())
|
if (contract.fallbackFunction())
|
||||||
/// This needs to be set to an invalid signature in order to trigger the fallback,
|
/// This needs to be set to an invalid signature in order to trigger the fallback,
|
||||||
/// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound.
|
/// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound.
|
||||||
/// An empty string ("") would work to trigger the shortcut only.
|
/// An empty string ("") would work to trigger the shortcut only.
|
||||||
externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
|
externalFunctions[""] = gasToJson(gasEstimator.functionalEstimation(*items, "INVALID"));
|
||||||
|
|
||||||
if (!externalFunctions.empty())
|
if (!externalFunctions.empty())
|
||||||
output["external"] = externalFunctions;
|
output["external"] = externalFunctions;
|
||||||
@ -999,7 +1008,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
|||||||
size_t entry = functionEntryPoint(_contractName, *it);
|
size_t entry = functionEntryPoint(_contractName, *it);
|
||||||
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
|
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
|
||||||
if (entry > 0)
|
if (entry > 0)
|
||||||
gas = GasEstimator::functionalEstimation(*items, entry, *it);
|
gas = gasEstimator.functionalEstimation(*items, entry, *it);
|
||||||
|
|
||||||
/// TODO: This could move into a method shared with externalSignature()
|
/// TODO: This could move into a method shared with externalSignature()
|
||||||
FunctionType type(*it);
|
FunctionType type(*it);
|
||||||
|
@ -23,20 +23,26 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/interface/ErrorReporter.h>
|
||||||
|
#include <libsolidity/interface/ReadFile.h>
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
|
#include <libevmasm/SourceLocation.h>
|
||||||
|
#include <libevmasm/LinkerObject.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
#include <libdevcore/FixedHash.h>
|
||||||
|
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <json/json.h>
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
#include <libdevcore/FixedHash.h>
|
|
||||||
#include <libevmasm/SourceLocation.h>
|
|
||||||
#include <libevmasm/LinkerObject.h>
|
|
||||||
#include <libsolidity/interface/ErrorReporter.h>
|
|
||||||
#include <libsolidity/interface/ReadFile.h>
|
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -116,6 +122,8 @@ public:
|
|||||||
m_optimizeRuns = _runs;
|
m_optimizeRuns = _runs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setEVMVersion(EVMVersion _version = EVMVersion{});
|
||||||
|
|
||||||
/// Sets the list of requested contract names. If empty, no filtering is performed and every contract
|
/// Sets the list of requested contract names. If empty, no filtering is performed and every contract
|
||||||
/// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing.
|
/// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing.
|
||||||
void setRequestedContractNames(std::set<std::string> const& _contractNames = std::set<std::string>{})
|
void setRequestedContractNames(std::set<std::string> const& _contractNames = std::set<std::string>{})
|
||||||
@ -310,6 +318,7 @@ private:
|
|||||||
ReadCallback::Callback m_smtQuery;
|
ReadCallback::Callback m_smtQuery;
|
||||||
bool m_optimize = false;
|
bool m_optimize = false;
|
||||||
unsigned m_optimizeRuns = 200;
|
unsigned m_optimizeRuns = 200;
|
||||||
|
EVMVersion m_evmVersion;
|
||||||
std::set<std::string> m_requestedContractNames;
|
std::set<std::string> m_requestedContractNames;
|
||||||
std::map<std::string, h160> m_libraries;
|
std::map<std::string, h160> m_libraries;
|
||||||
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
|
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
|
||||||
|
93
libsolidity/interface/EVMVersion.h
Normal file
93
libsolidity/interface/EVMVersion.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* EVM versioning.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/operators.hpp>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version specifier of the EVM we want to compile to.
|
||||||
|
* Defaults to the latest version.
|
||||||
|
*/
|
||||||
|
class EVMVersion:
|
||||||
|
boost::less_than_comparable<EVMVersion>,
|
||||||
|
boost::equality_comparable<EVMVersion>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EVMVersion() {}
|
||||||
|
|
||||||
|
static EVMVersion homestead() { return {Version::Homestead}; }
|
||||||
|
static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; }
|
||||||
|
static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; }
|
||||||
|
static EVMVersion byzantium() { return {Version::Byzantium}; }
|
||||||
|
static EVMVersion constantinople() { return {Version::Constantinople}; }
|
||||||
|
|
||||||
|
static boost::optional<EVMVersion> fromString(std::string const& _version)
|
||||||
|
{
|
||||||
|
for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium()})
|
||||||
|
if (_version == v.name())
|
||||||
|
return v;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; }
|
||||||
|
bool operator<(EVMVersion const& _other) const { return m_version < _other.m_version; }
|
||||||
|
|
||||||
|
std::string name() const
|
||||||
|
{
|
||||||
|
switch (m_version)
|
||||||
|
{
|
||||||
|
case Version::Homestead: return "homestead";
|
||||||
|
case Version::TangerineWhistle: return "tangerineWhistle";
|
||||||
|
case Version::SpuriousDragon: return "spuriousDragon";
|
||||||
|
case Version::Byzantium: return "byzantium";
|
||||||
|
case Version::Constantinople: return "constantinople";
|
||||||
|
}
|
||||||
|
return "INVALID";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Has the RETURNDATACOPY and RETURNDATASIZE opcodes.
|
||||||
|
bool supportsReturndata() const { return *this >= byzantium(); }
|
||||||
|
bool hasStaticCall() const { return *this >= byzantium(); }
|
||||||
|
bool hasBitwiseShifting() const { return *this >= constantinople(); }
|
||||||
|
|
||||||
|
/// Whether we have to retain the costs for the call opcode itself (false),
|
||||||
|
/// or whether we can just forward easily all remaining gas (true).
|
||||||
|
bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople };
|
||||||
|
|
||||||
|
EVMVersion(Version _version): m_version(_version) {}
|
||||||
|
|
||||||
|
Version m_version = Version::Byzantium;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ using namespace dev::solidity;
|
|||||||
GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation(
|
GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation(
|
||||||
AssemblyItems const& _items,
|
AssemblyItems const& _items,
|
||||||
vector<ASTNode const*> const& _ast
|
vector<ASTNode const*> const& _ast
|
||||||
)
|
) const
|
||||||
{
|
{
|
||||||
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
|
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
|
||||||
map<SourceLocation, GasConsumption> particularCosts;
|
map<SourceLocation, GasConsumption> particularCosts;
|
||||||
@ -49,7 +49,7 @@ GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimatio
|
|||||||
for (BasicBlock const& block: cfg.optimisedBlocks())
|
for (BasicBlock const& block: cfg.optimisedBlocks())
|
||||||
{
|
{
|
||||||
solAssert(!!block.startState, "");
|
solAssert(!!block.startState, "");
|
||||||
GasMeter meter(block.startState->copy());
|
GasMeter meter(block.startState->copy(), m_evmVersion);
|
||||||
auto const end = _items.begin() + block.end;
|
auto const end = _items.begin() + block.end;
|
||||||
for (auto iter = _items.begin() + block.begin; iter != end; ++iter)
|
for (auto iter = _items.begin() + block.begin; iter != end; ++iter)
|
||||||
particularCosts[iter->location()] += meter.estimateMax(*iter);
|
particularCosts[iter->location()] += meter.estimateMax(*iter);
|
||||||
@ -127,7 +127,7 @@ map<ASTNode const*, GasMeter::GasConsumption> GasEstimator::breakToStatementLeve
|
|||||||
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
||||||
AssemblyItems const& _items,
|
AssemblyItems const& _items,
|
||||||
string const& _signature
|
string const& _signature
|
||||||
)
|
) const
|
||||||
{
|
{
|
||||||
auto state = make_shared<KnownState>();
|
auto state = make_shared<KnownState>();
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PathGasMeter meter(_items);
|
PathGasMeter meter(_items, m_evmVersion);
|
||||||
return meter.estimateMax(0, state);
|
return meter.estimateMax(0, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
|||||||
AssemblyItems const& _items,
|
AssemblyItems const& _items,
|
||||||
size_t const& _offset,
|
size_t const& _offset,
|
||||||
FunctionDefinition const& _function
|
FunctionDefinition const& _function
|
||||||
)
|
) const
|
||||||
{
|
{
|
||||||
auto state = make_shared<KnownState>();
|
auto state = make_shared<KnownState>();
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
|||||||
if (parametersSize > 0)
|
if (parametersSize > 0)
|
||||||
state->feedItem(swapInstruction(parametersSize));
|
state->feedItem(swapInstruction(parametersSize));
|
||||||
|
|
||||||
return PathGasMeter(_items).estimateMax(_offset, state);
|
return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
set<ASTNode const*> GasEstimator::finestNodesAtLocation(
|
set<ASTNode const*> GasEstimator::finestNodesAtLocation(
|
||||||
|
@ -22,11 +22,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/interface/EVMVersion.h>
|
||||||
|
|
||||||
|
#include <libevmasm/GasMeter.h>
|
||||||
|
#include <libevmasm/Assembly.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <libevmasm/GasMeter.h>
|
|
||||||
#include <libevmasm/Assembly.h>
|
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -44,13 +47,15 @@ public:
|
|||||||
using ASTGasConsumptionSelfAccumulated =
|
using ASTGasConsumptionSelfAccumulated =
|
||||||
std::map<ASTNode const*, std::array<GasConsumption, 2>>;
|
std::map<ASTNode const*, std::array<GasConsumption, 2>>;
|
||||||
|
|
||||||
|
explicit GasEstimator(EVMVersion _evmVersion): m_evmVersion(_evmVersion) {}
|
||||||
|
|
||||||
/// Estimates the gas consumption for every assembly item in the given assembly and stores
|
/// Estimates the gas consumption for every assembly item in the given assembly and stores
|
||||||
/// it by source location.
|
/// it by source location.
|
||||||
/// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
|
/// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
|
||||||
static ASTGasConsumptionSelfAccumulated structuralEstimation(
|
ASTGasConsumptionSelfAccumulated structuralEstimation(
|
||||||
eth::AssemblyItems const& _items,
|
eth::AssemblyItems const& _items,
|
||||||
std::vector<ASTNode const*> const& _ast
|
std::vector<ASTNode const*> const& _ast
|
||||||
);
|
) const;
|
||||||
/// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
|
/// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
|
||||||
/// the following source locations are part of the mapping:
|
/// the following source locations are part of the mapping:
|
||||||
/// 1. source locations of statements that do not contain other statements
|
/// 1. source locations of statements that do not contain other statements
|
||||||
@ -62,23 +67,24 @@ public:
|
|||||||
|
|
||||||
/// @returns the estimated gas consumption by the (public or external) function with the
|
/// @returns the estimated gas consumption by the (public or external) function with the
|
||||||
/// given signature. If no signature is given, estimates the maximum gas usage.
|
/// given signature. If no signature is given, estimates the maximum gas usage.
|
||||||
static GasConsumption functionalEstimation(
|
GasConsumption functionalEstimation(
|
||||||
eth::AssemblyItems const& _items,
|
eth::AssemblyItems const& _items,
|
||||||
std::string const& _signature = ""
|
std::string const& _signature = ""
|
||||||
);
|
) const;
|
||||||
|
|
||||||
/// @returns the estimated gas consumption by the given function which starts at the given
|
/// @returns the estimated gas consumption by the given function which starts at the given
|
||||||
/// offset into the list of assembly items.
|
/// offset into the list of assembly items.
|
||||||
/// @note this does not work correctly for recursive functions.
|
/// @note this does not work correctly for recursive functions.
|
||||||
static GasConsumption functionalEstimation(
|
GasConsumption functionalEstimation(
|
||||||
eth::AssemblyItems const& _items,
|
eth::AssemblyItems const& _items,
|
||||||
size_t const& _offset,
|
size_t const& _offset,
|
||||||
FunctionDefinition const& _function
|
FunctionDefinition const& _function
|
||||||
);
|
) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @returns the set of AST nodes which are the finest nodes at their location.
|
/// @returns the set of AST nodes which are the finest nodes at their location.
|
||||||
static std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
|
static std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
|
||||||
|
EVMVersion m_evmVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,15 +31,11 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
void SourceReferenceFormatter::printSourceLocation(
|
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
|
||||||
ostream& _stream,
|
|
||||||
SourceLocation const* _location,
|
|
||||||
function<Scanner const&(string const&)> const& _scannerFromSourceName
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (!_location || !_location->sourceName)
|
if (!_location || !_location->sourceName)
|
||||||
return; // Nothing we can print here
|
return; // Nothing we can print here
|
||||||
auto const& scanner = _scannerFromSourceName(*_location->sourceName);
|
auto const& scanner = m_scannerFromSourceName(*_location->sourceName);
|
||||||
int startLine;
|
int startLine;
|
||||||
int startColumn;
|
int startColumn;
|
||||||
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
|
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
|
||||||
@ -64,72 +60,67 @@ void SourceReferenceFormatter::printSourceLocation(
|
|||||||
endColumn = startColumn + locationLength;
|
endColumn = startColumn + locationLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stream << line << endl;
|
m_stream << line << endl;
|
||||||
|
|
||||||
for_each(
|
for_each(
|
||||||
line.cbegin(),
|
line.cbegin(),
|
||||||
line.cbegin() + startColumn,
|
line.cbegin() + startColumn,
|
||||||
[&_stream](char const& ch) { _stream << (ch == '\t' ? '\t' : ' '); }
|
[this](char const& ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
|
||||||
);
|
);
|
||||||
_stream << "^";
|
m_stream << "^";
|
||||||
if (endColumn > startColumn + 2)
|
if (endColumn > startColumn + 2)
|
||||||
_stream << string(endColumn - startColumn - 2, '-');
|
m_stream << string(endColumn - startColumn - 2, '-');
|
||||||
if (endColumn > startColumn + 1)
|
if (endColumn > startColumn + 1)
|
||||||
_stream << "^";
|
m_stream << "^";
|
||||||
_stream << endl;
|
m_stream << endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_stream <<
|
m_stream <<
|
||||||
scanner.lineAtPosition(_location->start) <<
|
scanner.lineAtPosition(_location->start) <<
|
||||||
endl <<
|
endl <<
|
||||||
string(startColumn, ' ') <<
|
string(startColumn, ' ') <<
|
||||||
"^\n" <<
|
"^ (Relevant source part starts here and spans across multiple lines)." <<
|
||||||
"Spanning multiple lines.\n";
|
endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceReferenceFormatter::printSourceName(
|
void SourceReferenceFormatter::printSourceName(SourceLocation const* _location)
|
||||||
ostream& _stream,
|
|
||||||
SourceLocation const* _location,
|
|
||||||
function<Scanner const&(string const&)> const& _scannerFromSourceName
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (!_location || !_location->sourceName)
|
if (!_location || !_location->sourceName)
|
||||||
return; // Nothing we can print here
|
return; // Nothing we can print here
|
||||||
auto const& scanner = _scannerFromSourceName(*_location->sourceName);
|
auto const& scanner = m_scannerFromSourceName(*_location->sourceName);
|
||||||
int startLine;
|
int startLine;
|
||||||
int startColumn;
|
int startColumn;
|
||||||
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
|
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
|
||||||
_stream << *_location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
|
m_stream << *_location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceReferenceFormatter::printExceptionInformation(
|
void SourceReferenceFormatter::printExceptionInformation(
|
||||||
ostream& _stream,
|
|
||||||
Exception const& _exception,
|
Exception const& _exception,
|
||||||
string const& _name,
|
string const& _name
|
||||||
function<Scanner const&(string const&)> const& _scannerFromSourceName
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
||||||
auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
|
auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
|
||||||
|
|
||||||
printSourceName(_stream, location, _scannerFromSourceName);
|
printSourceName(location);
|
||||||
|
|
||||||
_stream << _name;
|
m_stream << _name;
|
||||||
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
|
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
|
||||||
_stream << ": " << *description << endl;
|
m_stream << ": " << *description << endl;
|
||||||
else
|
else
|
||||||
_stream << endl;
|
m_stream << endl;
|
||||||
|
|
||||||
printSourceLocation(_stream, location, _scannerFromSourceName);
|
printSourceLocation(location);
|
||||||
|
|
||||||
if (secondarylocation && !secondarylocation->infos.empty())
|
if (secondarylocation && !secondarylocation->infos.empty())
|
||||||
{
|
{
|
||||||
for (auto info: secondarylocation->infos)
|
for (auto info: secondarylocation->infos)
|
||||||
{
|
{
|
||||||
printSourceName(_stream, &info.second, _scannerFromSourceName);
|
printSourceName(&info.second);
|
||||||
_stream << info.first << endl;
|
m_stream << info.first << endl;
|
||||||
printSourceLocation(_stream, &info.second, _scannerFromSourceName);
|
printSourceLocation(&info.second);
|
||||||
}
|
}
|
||||||
_stream << endl;
|
m_stream << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,22 +38,23 @@ namespace solidity
|
|||||||
class Scanner; // forward
|
class Scanner; // forward
|
||||||
class CompilerStack; // forward
|
class CompilerStack; // forward
|
||||||
|
|
||||||
struct SourceReferenceFormatter
|
class SourceReferenceFormatter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ScannerFromSourceNameFun = std::function<Scanner const&(std::string const&)>;
|
using ScannerFromSourceNameFun = std::function<Scanner const&(std::string const&)>;
|
||||||
|
|
||||||
|
explicit SourceReferenceFormatter(
|
||||||
|
std::ostream& _stream,
|
||||||
|
ScannerFromSourceNameFun _scannerFromSourceName
|
||||||
|
):
|
||||||
|
m_stream(_stream),
|
||||||
|
m_scannerFromSourceName(std::move(_scannerFromSourceName))
|
||||||
|
{}
|
||||||
|
|
||||||
/// Prints source location if it is given.
|
/// Prints source location if it is given.
|
||||||
static void printSourceLocation(
|
void printSourceLocation(SourceLocation const* _location);
|
||||||
std::ostream& _stream,
|
void printExceptionInformation(Exception const& _exception, std::string const& _name);
|
||||||
SourceLocation const* _location,
|
|
||||||
ScannerFromSourceNameFun const& _scannerFromSourceName
|
|
||||||
);
|
|
||||||
static void printExceptionInformation(
|
|
||||||
std::ostream& _stream,
|
|
||||||
Exception const& _exception,
|
|
||||||
std::string const& _name,
|
|
||||||
ScannerFromSourceNameFun const& _scannerFromSourceName
|
|
||||||
);
|
|
||||||
static std::string formatExceptionInformation(
|
static std::string formatExceptionInformation(
|
||||||
Exception const& _exception,
|
Exception const& _exception,
|
||||||
std::string const& _name,
|
std::string const& _name,
|
||||||
@ -61,16 +62,17 @@ public:
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
std::ostringstream errorOutput;
|
std::ostringstream errorOutput;
|
||||||
printExceptionInformation(errorOutput, _exception, _name, _scannerFromSourceName);
|
|
||||||
|
SourceReferenceFormatter formatter(errorOutput, _scannerFromSourceName);
|
||||||
|
formatter.printExceptionInformation(_exception, _name);
|
||||||
return errorOutput.str();
|
return errorOutput.str();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
/// Prints source name if location is given.
|
/// Prints source name if location is given.
|
||||||
static void printSourceName(
|
void printSourceName(SourceLocation const* _location);
|
||||||
std::ostream& _stream,
|
|
||||||
SourceLocation const* _location,
|
std::ostream& m_stream;
|
||||||
ScannerFromSourceNameFun const& _scannerFromSourceName
|
ScannerFromSourceNameFun m_scannerFromSourceName;
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#include <libdevcore/JSON.h>
|
#include <libdevcore/JSON.h>
|
||||||
#include <libdevcore/SHA3.h>
|
#include <libdevcore/SHA3.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
@ -236,7 +238,11 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
|||||||
return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language.");
|
return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language.");
|
||||||
|
|
||||||
Json::Value const& sources = _input["sources"];
|
Json::Value const& sources = _input["sources"];
|
||||||
if (!sources)
|
|
||||||
|
if (!sources.isObject() && !sources.isNull())
|
||||||
|
return formatFatalError("JSONError", "\"sources\" is not a JSON object.");
|
||||||
|
|
||||||
|
if (sources.empty())
|
||||||
return formatFatalError("JSONError", "No input sources specified.");
|
return formatFatalError("JSONError", "No input sources specified.");
|
||||||
|
|
||||||
Json::Value errors = Json::arrayValue;
|
Json::Value errors = Json::arrayValue;
|
||||||
@ -312,6 +318,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
|||||||
|
|
||||||
Json::Value const& settings = _input.get("settings", Json::Value());
|
Json::Value const& settings = _input.get("settings", Json::Value());
|
||||||
|
|
||||||
|
if (settings.isMember("evmVersion"))
|
||||||
|
{
|
||||||
|
boost::optional<EVMVersion> version = EVMVersion::fromString(settings.get("evmVersion", {}).asString());
|
||||||
|
if (!version)
|
||||||
|
return formatFatalError("JSONError", "Invalid EVM version requested.");
|
||||||
|
m_compilerStack.setEVMVersion(*version);
|
||||||
|
}
|
||||||
|
|
||||||
vector<string> remappings;
|
vector<string> remappings;
|
||||||
for (auto const& remapping: settings.get("remappings", Json::Value()))
|
for (auto const& remapping: settings.get("remappings", Json::Value()))
|
||||||
remappings.push_back(remapping.asString());
|
remappings.push_back(remapping.asString());
|
||||||
@ -323,13 +337,43 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
|||||||
m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
|
m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
|
||||||
|
|
||||||
map<string, h160> libraries;
|
map<string, h160> libraries;
|
||||||
Json::Value jsonLibraries = settings.get("libraries", Json::Value());
|
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
||||||
|
if (!jsonLibraries.isObject())
|
||||||
|
return formatFatalError("JSONError", "\"libraries\" is not a JSON object.");
|
||||||
for (auto const& sourceName: jsonLibraries.getMemberNames())
|
for (auto const& sourceName: jsonLibraries.getMemberNames())
|
||||||
{
|
{
|
||||||
auto const& jsonSourceName = jsonLibraries[sourceName];
|
auto const& jsonSourceName = jsonLibraries[sourceName];
|
||||||
|
if (!jsonSourceName.isObject())
|
||||||
|
return formatFatalError("JSONError", "library entry is not a JSON object.");
|
||||||
for (auto const& library: jsonSourceName.getMemberNames())
|
for (auto const& library: jsonSourceName.getMemberNames())
|
||||||
// @TODO use libraries only for the given source
|
{
|
||||||
libraries[library] = h160(jsonSourceName[library].asString());
|
string address = jsonSourceName[library].asString();
|
||||||
|
|
||||||
|
if (!boost::starts_with(address, "0x"))
|
||||||
|
return formatFatalError(
|
||||||
|
"JSONError",
|
||||||
|
"Library address is not prefixed with \"0x\"."
|
||||||
|
);
|
||||||
|
|
||||||
|
if (address.length() != 42)
|
||||||
|
return formatFatalError(
|
||||||
|
"JSONError",
|
||||||
|
"Library address is of invalid length."
|
||||||
|
);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// @TODO use libraries only for the given source
|
||||||
|
libraries[library] = h160(address);
|
||||||
|
}
|
||||||
|
catch (dev::BadHexCharacter)
|
||||||
|
{
|
||||||
|
return formatFatalError(
|
||||||
|
"JSONError",
|
||||||
|
"Invalid library address (\"" + address + "\") supplied."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_compilerStack.setLibraries(libraries);
|
m_compilerStack.setLibraries(libraries);
|
||||||
|
|
||||||
@ -550,12 +594,11 @@ Json::Value StandardCompiler::compile(Json::Value const& _input)
|
|||||||
string StandardCompiler::compile(string const& _input)
|
string StandardCompiler::compile(string const& _input)
|
||||||
{
|
{
|
||||||
Json::Value input;
|
Json::Value input;
|
||||||
Json::Reader reader;
|
string errors;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!reader.parse(_input, input, false))
|
if (!jsonParseStrict(_input, input, &errors))
|
||||||
return jsonCompactPrint(formatFatalError("JSONError", reader.getFormattedErrorMessages()));
|
return jsonCompactPrint(formatFatalError("JSONError", errors));
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
@ -897,7 +897,9 @@ ASTPointer<Statement> Parser::parseStatement()
|
|||||||
case Token::Assembly:
|
case Token::Assembly:
|
||||||
return parseInlineAssembly(docString);
|
return parseInlineAssembly(docString);
|
||||||
case Token::Identifier:
|
case Token::Identifier:
|
||||||
if (m_insideModifier && m_scanner->currentLiteral() == "_")
|
if (m_scanner->currentLiteral() == "emit")
|
||||||
|
statement = parseEmitStatement(docString);
|
||||||
|
else if (m_insideModifier && m_scanner->currentLiteral() == "_")
|
||||||
{
|
{
|
||||||
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
|
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
@ -926,7 +928,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
|
|||||||
}
|
}
|
||||||
|
|
||||||
assembly::Parser asmParser(m_errorReporter);
|
assembly::Parser asmParser(m_errorReporter);
|
||||||
shared_ptr<assembly::Block> block = asmParser.parse(m_scanner);
|
shared_ptr<assembly::Block> block = asmParser.parse(m_scanner, true);
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
return nodeFactory.createNode<InlineAssembly>(_docString, block);
|
return nodeFactory.createNode<InlineAssembly>(_docString, block);
|
||||||
}
|
}
|
||||||
@ -1015,6 +1017,38 @@ ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const&
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const& _docString)
|
||||||
|
{
|
||||||
|
ASTNodeFactory nodeFactory(*this);
|
||||||
|
m_scanner->next();
|
||||||
|
ASTNodeFactory eventCallNodeFactory(*this);
|
||||||
|
|
||||||
|
if (m_scanner->currentToken() != Token::Identifier)
|
||||||
|
fatalParserError("Expected event name or path.");
|
||||||
|
|
||||||
|
vector<ASTPointer<PrimaryExpression>> path;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
path.push_back(parseIdentifier());
|
||||||
|
if (m_scanner->currentToken() != Token::Period)
|
||||||
|
break;
|
||||||
|
m_scanner->next();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto eventName = expressionFromIndexAccessStructure(path, {});
|
||||||
|
expectToken(Token::LParen);
|
||||||
|
|
||||||
|
vector<ASTPointer<Expression>> arguments;
|
||||||
|
vector<ASTPointer<ASTString>> names;
|
||||||
|
std::tie(arguments, names) = parseFunctionCallArguments();
|
||||||
|
eventCallNodeFactory.markEndPosition();
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
expectToken(Token::RParen);
|
||||||
|
auto eventCall = eventCallNodeFactory.createNode<FunctionCall>(eventName, arguments, names);
|
||||||
|
auto statement = nodeFactory.createNode<EmitStatement>(_docString, eventCall);
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
|
|
||||||
ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
|
ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
|
||||||
{
|
{
|
||||||
RecursionGuard recursionGuard(*this);
|
RecursionGuard recursionGuard(*this);
|
||||||
|
@ -104,6 +104,7 @@ private:
|
|||||||
ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString);
|
||||||
ASTPointer<WhileStatement> parseDoWhileStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<WhileStatement> parseDoWhileStatement(ASTPointer<ASTString> const& _docString);
|
||||||
ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
|
||||||
|
ASTPointer<EmitStatement> parseEmitStatement(ASTPointer<ASTString> const& docString);
|
||||||
/// A "simple statement" can be a variable declaration statement or an expression statement.
|
/// A "simple statement" can be a variable declaration statement or an expression statement.
|
||||||
ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString);
|
||||||
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
|
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
|
||||||
|
@ -133,7 +133,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else if (mode == Binary || mode == Hex)
|
else if (mode == Binary || mode == Hex)
|
||||||
{
|
{
|
||||||
auto bs = compileLLL(src, optimise ? true : false, &errors, readFileAsString);
|
auto bs = compileLLL(src, EVMVersion{}, optimise ? true : false, &errors, readFileAsString);
|
||||||
if (mode == Hex)
|
if (mode == Hex)
|
||||||
cout << toHex(bs) << endl;
|
cout << toHex(bs) << endl;
|
||||||
else if (mode == Binary)
|
else if (mode == Binary)
|
||||||
@ -145,7 +145,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else if (mode == Assembly)
|
else if (mode == Assembly)
|
||||||
{
|
{
|
||||||
cout << compileLLLToAsm(src, optimise ? true : false, &errors, readFileAsString) << endl;
|
cout << compileLLLToAsm(src, EVMVersion{}, optimise ? true : false, &errors, readFileAsString) << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& i: errors)
|
for (auto const& i: errors)
|
||||||
|
@ -168,11 +168,12 @@ case $(uname -s) in
|
|||||||
# Debian
|
# Debian
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
Debian)
|
Debian*)
|
||||||
#Debian
|
#Debian
|
||||||
|
. /etc/os-release
|
||||||
install_z3=""
|
install_z3=""
|
||||||
case $(lsb_release -cs) in
|
case $VERSION_ID in
|
||||||
wheezy)
|
7)
|
||||||
#wheezy
|
#wheezy
|
||||||
echo "Installing solidity dependencies on Debian Wheezy (7.x)."
|
echo "Installing solidity dependencies on Debian Wheezy (7.x)."
|
||||||
echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet."
|
echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet."
|
||||||
@ -182,16 +183,16 @@ case $(uname -s) in
|
|||||||
echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support."
|
echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support."
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
jessie)
|
8)
|
||||||
#jessie
|
#jessie
|
||||||
echo "Installing solidity dependencies on Debian Jesse (8.x)."
|
echo "Installing solidity dependencies on Debian Jesse (8.x)."
|
||||||
;;
|
;;
|
||||||
stretch)
|
9)
|
||||||
#stretch
|
#stretch
|
||||||
echo "Installing solidity dependencies on Debian Stretch (9.x)."
|
echo "Installing solidity dependencies on Debian Stretch (9.x)."
|
||||||
install_z3="libz3-dev"
|
install_z3="libz3-dev"
|
||||||
;;
|
;;
|
||||||
buster)
|
10)
|
||||||
#buster
|
#buster
|
||||||
echo "Installing solidity dependencies on Debian Buster (10.x)."
|
echo "Installing solidity dependencies on Debian Buster (10.x)."
|
||||||
install_z3="libz3-dev"
|
install_z3="libz3-dev"
|
||||||
|
@ -168,7 +168,7 @@ override_dh_shlibdeps:
|
|||||||
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
|
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
|
||||||
|
|
||||||
override_dh_auto_configure:
|
override_dh_auto_configure:
|
||||||
dh_auto_configure -- -DINSTALL_LLLC=Off
|
dh_auto_configure -- -DINSTALL_LLLC=Off
|
||||||
EOF
|
EOF
|
||||||
cat <<EOF > debian/copyright
|
cat <<EOF > debian/copyright
|
||||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user