Merge pull request #7934 from ethereum/develop

Merge develop to release for 0.5.14
This commit is contained in:
chriseth 2019-12-09 16:17:10 +01:00 committed by GitHub
commit 01f1aaa4c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
393 changed files with 7196 additions and 2640 deletions

View File

@ -4,25 +4,27 @@
The docker images are build locally on the developer machine:
```!sh
```sh
cd .circleci/docker/
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904 -f Dockerfile.ubuntu1904 .
docker push ethereum/solidity-buildpack-deps:ubuntu1904
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904-<revision> -f Dockerfile.ubuntu1904 .
docker push ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
```
which you can find on Dockerhub after the push at:
The current revision is stored in a [circle ci pipeline parameter](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `docker-image-rev`. Please update the value assigned to this parameter at the time of a docker image update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`<revision>` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ.
https://hub.docker.com/r/ethereum/solidity-buildpack-deps
Once the docker image has been built and pushed to Dockerhub, you can find it at:
where the image tag reflects the target OS to build Solidity and run its test on.
https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
where the image tag reflects the target OS and revision to build Solidity and run its tests on.
### Testing docker images locally
```!sh
```sh
cd solidity
# Mounts your local solidity directory in docker container for testing
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904 /bin/bash
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904-<revision> /bin/bash
cd /src/solidity
<commands_to_test_build_with_new_docker_image>
```
```

View File

@ -5,7 +5,11 @@
# - t: test
# - ubu: ubuntu
# - ems: Emscripten
version: 2
version: 2.1
parameters:
docker-image-rev:
type: string
default: "3"
defaults:
@ -106,7 +110,7 @@ defaults:
- test_ubuntu1904_clang: &test_ubuntu1904_clang
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.docker-image-rev >>
steps:
- checkout
- attach_workspace:
@ -117,7 +121,7 @@ defaults:
- test_ubuntu1904: &test_ubuntu1904
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >>
steps:
- checkout
- attach_workspace:
@ -287,10 +291,11 @@ jobs:
b_ubu_clang: &build_ubuntu1904_clang
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.docker-image-rev >>
environment:
CC: clang
CXX: clang++
CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
- run: *run_build
@ -299,7 +304,9 @@ jobs:
b_ubu: &build_ubuntu1904
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >>
environment:
CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
- run: *run_build
@ -310,12 +317,13 @@ jobs:
<<: *build_ubuntu1904
environment:
FORCE_RELEASE: ON
CMAKE_OPTIONS: -DLLL=ON
b_ubu18: &build_ubuntu1804
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1804
- image: ethereum/solidity-buildpack-deps:ubuntu1804-<< pipeline.parameters.docker-image-rev >>
environment:
CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2
CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 -DLLL=ON
CMAKE_BUILD_TYPE: RelWithDebugInfo
steps:
- checkout
@ -360,7 +368,7 @@ jobs:
<<: *build_ubuntu1904
environment:
CMAKE_BUILD_TYPE: Debug
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF -DLLL=ON
steps:
- checkout
- run: *run_build
@ -400,6 +408,7 @@ jobs:
- image: archlinux/base
environment:
TERM: xterm
CMAKE_OPTIONS: -DLLL=ON
steps:
- run:
name: Install build dependencies
@ -416,6 +425,7 @@ jobs:
environment:
TERM: xterm
CMAKE_BUILD_TYPE: Debug
CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
- restore_cache:
@ -472,7 +482,7 @@ jobs:
b_ems:
docker:
- image: trzeci/emscripten:sdk-tag-1.38.22-64bit
- image: trzeci/emscripten:sdk-tag-1.39.3-64bit
environment:
TERM: xterm
steps:
@ -519,7 +529,7 @@ jobs:
b_docs:
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >>
steps:
- checkout
- run: *setup_prerelease_commit_hash
@ -544,7 +554,7 @@ jobs:
t_ubu_cli: &t_ubu_cli
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >>
environment:
TERM: xterm
steps:
@ -597,7 +607,22 @@ jobs:
npm --version
test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt)
t_ems_external_gnosis:
t_ems_compile_ext_gnosis:
docker:
- image: circleci/node:10
environment:
TERM: xterm
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
name: External GnosisSafe compilation
command: |
export COMPILE_ONLY=1
test/externalTests/gnosis.sh /tmp/workspace/soljson.js || test/externalTests/gnosis.sh /tmp/workspace/soljson.js
t_ems_test_ext_gnosis:
docker:
- image: circleci/node:10
environment:
@ -613,7 +638,22 @@ jobs:
- run: *gitter_notify_failure
- run: *gitter_notify_success
t_ems_external_zeppelin:
t_ems_compile_ext_zeppelin:
docker:
- image: circleci/node:10
environment:
TERM: xterm
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
name: External Zeppelin compilation
command: |
export COMPILE_ONLY=1
test/externalTests/zeppelin.sh /tmp/workspace/soljson.js || test/externalTests/zeppelin.sh /tmp/workspace/soljson.js
t_ems_test_ext_zeppelin:
docker:
- image: circleci/node:10
environment:
@ -629,7 +669,26 @@ jobs:
- run: *gitter_notify_failure
- run: *gitter_notify_success
t_ems_external_colony:
t_ems_compile_ext_colony:
docker:
- image: circleci/node:10
environment:
TERM: xterm
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
name: Install test dependencies
command: |
sudo apt-get -qy install lsof
- run:
name: External ColonyNetworks compilation
command: |
export COMPILE_ONLY=1
test/externalTests/colony.sh /tmp/workspace/soljson.js || test/externalTests/colony.sh /tmp/workspace/soljson.js
t_ems_test_ext_colony:
docker:
- image: circleci/node:10
environment:
@ -693,6 +752,9 @@ workflows:
# Emscripten build and selected tests
- b_ems: *workflow_trigger_on_tags
- t_ems_solcjs: *workflow_emscripten
- t_ems_compile_ext_colony: *workflow_emscripten
- t_ems_compile_ext_gnosis: *workflow_emscripten
- t_ems_compile_ext_zeppelin: *workflow_emscripten
nightly:
@ -706,12 +768,6 @@ workflows:
- develop_060
jobs:
# Emscripten builds and external tests
- b_ems: *workflow_trigger_on_tags
- t_ems_external_zeppelin: *workflow_emscripten
- t_ems_external_gnosis: *workflow_emscripten
- t_ems_external_colony: *workflow_emscripten
# OSSFUZZ builds and (regression) tests
- b_ubu_ossfuzz: *workflow_trigger_on_tags
- t_ubu_ossfuzz: *workflow_ubuntu1904_ossfuzz

View File

@ -62,7 +62,7 @@ RUN git clone --recursive -b boost-1.69.0 https://github.com/boostorg/boost.git
rm -rf /usr/src/boost
# Z3
RUN git clone --depth 1 -b z3-4.8.6 https://github.com/Z3Prover/z3.git \
RUN git clone --depth 1 -b z3-4.8.7 https://github.com/Z3Prover/z3.git \
/usr/src/z3; \
cd /usr/src/z3; \
python scripts/mk_make.py --prefix=/usr ; \
@ -89,43 +89,24 @@ RUN set -ex; \
ninja install/strip; \
rm -rf /usr/src/libprotobuf-mutator
# ETHASH
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \
cd ethash; \
mkdir build; \
cd build; \
cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/ethash
# INTX
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \
cd intx; \
mkdir build; \
cd build; \
cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/intx;
# EVMONE
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
cd evmone; \
mkdir build; \
cd build; \
# isoltest links against the evmone shared library
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \
ninja install/strip; \
# abiv2_proto_ossfuzz links against the evmone standalone static library
cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/evmone
FROM base
COPY --from=libraries /usr/lib /usr/lib
COPY --from=libraries /usr/bin /usr/bin
COPY --from=libraries /usr/include /usr/include
COPY --from=libraries /usr/include /usr/include

View File

@ -74,34 +74,10 @@ RUN set -ex; \
ar r /usr/lib/libFuzzingEngine.a *.o; \
rm -rf /var/lib/libfuzzer
# ETHASH
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \
cd ethash; \
mkdir build; \
cd build; \
cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/ethash
# INTX
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \
cd intx; \
mkdir build; \
cd build; \
cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/intx;
# EVMONE
ARG EVMONE_HASH="f10d12c190f55a9d373e78b2dc0074d35d752c02cb536bb6fe754fb3719dd69e"
ARG EVMONE_HASH="fa4f40daf7cf9ccbcca6c78345977e084ea2136a8eae661e4d19471be852b15b"
ARG EVMONE_MAJOR="0"
ARG EVMONE_MINOR="1"
ARG EVMONE_MINOR="3"
ARG EVMONE_MICRO="0"
RUN set -ex; \
EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \

View File

@ -74,37 +74,14 @@ RUN set -ex; \
ar r /usr/lib/libFuzzingEngine.a *.o; \
rm -rf /var/lib/libfuzzer
# ETHASH
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \
cd ethash; \
mkdir build; \
cd build; \
cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/ethash
# INTX
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \
cd intx; \
mkdir build; \
cd build; \
cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/intx;
# EVMONE
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
cd evmone; \
mkdir build; \
cd build; \
# isoltest links against the evmone shared library
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \
ninja install/strip; \

View File

@ -43,17 +43,17 @@ then
./scripts/install_obsolete_jsoncpp_1_7_4.sh
# z3
wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.6/z3-4.8.6-x64-osx-10.14.6.zip
unzip z3-4.8.6-x64-osx-10.14.6.zip
rm -f z3-4.8.6-x64-osx-10.14.6.zip
cp z3-4.8.6-x64-osx-10.14.6/bin/libz3.a /usr/local/lib
cp z3-4.8.6-x64-osx-10.14.6/bin/z3 /usr/local/bin
cp z3-4.8.6-x64-osx-10.14.6/include/* /usr/local/include
rm -rf z3-4.8.6-x64-osx-10.14.6
wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.7/z3-4.8.7-x64-osx-10.14.6.zip
unzip z3-4.8.7-x64-osx-10.14.6.zip
rm -f z3-4.8.7-x64-osx-10.14.6.zip
cp z3-4.8.7-x64-osx-10.14.6/bin/libz3.a /usr/local/lib
cp z3-4.8.7-x64-osx-10.14.6/bin/z3 /usr/local/bin
cp z3-4.8.7-x64-osx-10.14.6/include/* /usr/local/include
rm -rf z3-4.8.7-x64-osx-10.14.6
# evmone
wget https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-darwin-x86_64.tar.gz
tar xzpf evmone-0.1.0-darwin-x86_64.tar.gz -C /usr/local
rm -f evmone-0.1.0-darwin-x86_64.tar.gz
wget https://github.com/ethereum/evmone/releases/download/v0.3.0/evmone-0.3.0-darwin-x86_64.tar.gz
tar xzpf evmone-0.3.0-darwin-x86_64.tar.gz -C /usr/local
rm -f evmone-0.3.0-darwin-x86_64.tar.gz
fi

View File

@ -29,9 +29,9 @@ set -e
REPODIR="$(realpath $(dirname $0)/..)"
for OPTIMIZE in 0 1; do
for EVM in homestead byzantium constantinople petersburg; do
for EVM in homestead byzantium constantinople petersburg istanbul; do
EVM=$EVM OPTIMIZE=$OPTIMIZE ${REPODIR}/.circleci/soltest.sh
done
done
EVM=constantinople OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh
EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh

View File

@ -110,7 +110,7 @@ matrix:
before_install:
- nvm install 8
- nvm use 8
- docker pull trzeci/emscripten:sdk-tag-1.38.22-64bit
- docker pull trzeci/emscripten:sdk-tag-1.39.3-64bit
env:
- SOLC_EMSCRIPTEN=On
- SOLC_INSTALL_DEPS_TRAVIS=Off
@ -127,7 +127,7 @@ matrix:
#
# This key here has no significant on anything, apart from caching. Please keep
# it in sync with the version above.
- EMSCRIPTEN_VERSION_KEY="1.38.22"
- EMSCRIPTEN_VERSION_KEY="1.39.3"
# OS X Mavericks (10.9)
# https://en.wikipedia.org/wiki/OS_X_Mavericks

View File

@ -10,7 +10,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.5.13")
set(PROJECT_VERSION "0.5.14")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
include(TestBigEndian)

View File

@ -1,3 +1,31 @@
### 0.5.14 (2019-12-09)
Language Features:
* Allow to obtain the selector of public or external library functions via a member ``.selector``.
* Inline Assembly: Support constants that reference other constants.
* Parser: Allow splitting hexadecimal and regular string literals into multiple parts.
Compiler Features:
* Commandline Interface: Allow translation from yul / strict assembly to EWasm using ``solc --yul --yul-dialect evm --machine eWasm``
* Set the default EVM version to "Istanbul".
* SMTChecker: Add support to constructors including constructor inheritance.
* Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable.
* Yul Optimizer: Perform loop-invariant code motion.
Bugfixes:
* SMTChecker: Fix internal error when using ``abi.decode``.
* SMTChecker: Fix internal error when using arrays or mappings of functions.
* SMTChecker: Fix internal error in array of structs type.
* Version Checker: ``^0`` should match ``0.5.0``, but no prerelease.
* Yul: Consider infinite loops and recursion to be not removable.
Build System:
* Update to emscripten version 1.39.3.
### 0.5.13 (2019-11-14)
Language Features:
@ -22,6 +50,7 @@ Bugfixes:
* SMTChecker: Fix internal error when implicitly converting string literals to fixed bytes.
* Type Checker: Disallow constructor of the same class to be used as modifier.
* Type Checker: Treat magic variables as unknown identifiers in inline assembly.
* Code Generator: Fix internal error when trying to convert ``super`` to a different type

View File

@ -31,7 +31,7 @@ Instructions about how to build and install the Solidity compiler can be found i
A "Hello World" program in Solidity is of even less use than in other languages, but still:
```
```solidity
pragma solidity ^0.5.0;
contract HelloWorld {
@ -44,10 +44,10 @@ contract HelloWorld {
To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is an
browser-based IDE. Here are some example contracts:
1. [Voting](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#voting)
2. [Blind Auction](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#blind-auction)
3. [Safe remote purchase](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#safe-remote-purchase)
4. [Micropayment Channel](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#micropayment-channel)
1. [Voting](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#voting)
2. [Blind Auction](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#blind-auction)
3. [Safe remote purchase](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#safe-remote-purchase)
4. [Micropayment Channel](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#micropayment-channel)
## Documentation

View File

@ -86,7 +86,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
elseif(EMSCRIPTEN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0")
# Leave only exported symbols as public and aggressively remove others
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -fvisibility=hidden")
# Optimisation level
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
# Re-enable exception catching (optimisations above -O1 disable it)
@ -110,9 +110,12 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1")
# Export the Emscripten-generated auxiliary methods which are needed by solc-js.
# Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','Pointer_stringify','lengthBytesUTF8','_malloc','stringToUTF8','setValue']")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','_malloc','stringToUTF8','setValue']")
# Do not build as a WebAssembly target - we need an asm.js output.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0")
# Disable warnings about not being pure asm.js due to memory growth.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm")
endif()
endif()

View File

@ -37,9 +37,9 @@ endif()
ExternalProject_Add(jsoncpp-project
PREFIX "${prefix}"
DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads"
DOWNLOAD_NAME jsoncpp-1.8.4.tar.gz
URL https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.tar.gz
URL_HASH SHA256=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6
DOWNLOAD_NAME jsoncpp-1.9.2.tar.gz
URL https://github.com/open-source-parsers/jsoncpp/archive/1.9.2.tar.gz
URL_HASH SHA256=77a402fb577b2e0e5d0bdc1cf9c65278915cdb25171e3452c68b6da8a561f8f0
CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}

View File

@ -758,6 +758,10 @@
"bugs": [],
"released": "2019-11-14"
},
"0.5.14": {
"bugs": [],
"released": "2019-12-09"
},
"0.5.2": {
"bugs": [
"SignedArrayStorageCopy",

View File

@ -208,6 +208,48 @@ Restrictions for libraries in comparison to contracts:
(These might be lifted at a later point.)
.. _library-selectors:
Function Signatures and Selectors in Libraries
==============================================
While external calls to public or external library functions are possible, the calling convention for such calls
is considered to be internal to Solidity and not the same as specified for the regular :ref:`contract ABI<ABI>`.
External library functions support more argument types than external contract functions, for example recursive structs
and storage pointers. For that reason, the function signatures used to compute the 4-byte selector are computed
following an internal naming schema and arguments of types not supported in the contract ABI use an internal encoding.
The following identifiers are used for the types in the signatures:
- Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI.
- Non-storage array types follow the same convention as in the contract ABI, i.e. ``<type>[]`` for dynamic arrays and
``<type>[M]`` for fixed-size arrays of ``M`` elements.
- Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``.
- Storage pointer types use the type identifier of their corresponding non-storage type, but append a single space
followed by ``storage`` to it.
The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a
``uint256`` value referring to the storage slot to which they point.
Similarly to the contract ABI, the selector consists of the first four bytes of the Keccak256-hash of the signature.
Its value can be obtained from Solidity using the ``.selector`` member as follows:
::
pragma solidity >0.5.13 <0.7.0;
library L {
function f(uint256) external {}
}
contract C {
function g() public pure returns (bytes4) {
return L.f.selector;
}
}
.. _call-protection:
Call Protection For Libraries

View File

@ -86,7 +86,7 @@ but for quicker feedback, you might want to run specific tests.
Solidity includes different types of tests, most of them bundled into the
`Boost C++ Test Framework <https://www.boost.org/doc/libs/1_69_0/libs/test/doc/html/index.html>`_ application ``soltest``.
Running ``build/test/soltest` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes.
Running ``build/test/soltest`` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes.
Some tests require the ``evmone`` library, others require ``libz3``.
@ -94,7 +94,7 @@ The test system will automatically try to discover the location of the ``evmone`
starting from the current directory. The required file is called ``libevmone.so`` on Linux systems,
``evmone.dll`` on Windows systems and ``libevmone.dylib`` on MacOS. If it is not found, the relevant tests
are skipped. To run all tests, download the library from
`Github <https://github.com/ethereum/evmone/releases/tag/v0.1.0>`_
`Github <https://github.com/ethereum/evmone/releases/tag/v0.3.0>`_
and either place it in the project root path or inside the ``deps`` folder.
If you do not have libz3 installed on your system, you should disable the SMT tests:
@ -113,7 +113,7 @@ See especially:
If you are running this in plain Command Prompt, use ``.\build\test\Release\soltest.exe -- --no-smt``.
To run a subset of tests, you can use filters:
``./scripts/soltest.sh -t TestSuite/TestName,
``./scripts/soltest.sh -t TestSuite/TestName``,
where ``TestName`` can be a wildcard ``*``.
For example, here is an example test you might run;

View File

@ -204,7 +204,7 @@ The evaluation order of expressions is not specified (more formally, the order
in which the children of one node in the expression tree are evaluated is not
specified, but they are of course evaluated before the node itself). It is only
guaranteed that statements are executed in order and short-circuiting for
boolean expressions is done. See :ref:`order` for more information.
boolean expressions is done.
.. index:: ! assignment

View File

@ -4,6 +4,25 @@
Safe Remote Purchase
********************
Purchasing goods remotely currently requires multiple parties that need to trust each other.
The simplest configuration involves a seller and a buyer. The buyer would like to receive
an item from the seller and the seller would like to get money (or an equivalent)
in return. The problematic part is the shipment here: There is no way to determine for
sure that the item arrived at the buyer.
There are multiple ways to solve this problem, but all fall short in one or the other way.
In the following example, both parties have to put twice the value of the item into the
contract as escrow. As soon as this happened, the money will stay locked inside
the contract until the buyer confirms that they received the item. After that,
the buyer is returned the value (half of their deposit) and the seller gets three
times the value (their deposit plus the value). The idea behind
this is that both parties have an incentive to resolve the situation or otherwise
their money is locked forever.
This contract of course does not solve the problem, but gives an overview of how
you can use state machine-like constructs inside a contract.
::
pragma solidity >=0.4.22 <0.7.0;
@ -12,7 +31,7 @@ Safe Remote Purchase
uint public value;
address payable public seller;
address payable public buyer;
enum State { Created, Locked, Inactive }
enum State { Created, Locked, Release, Inactive }
// The state variable has a default value of the first member, `State.created`
State public state;
@ -57,6 +76,7 @@ Safe Remote Purchase
event Aborted();
event PurchaseConfirmed();
event ItemReceived();
event SellerRefunded();
/// Abort the purchase and reclaim the ether.
/// Can only be called by the seller before
@ -68,6 +88,10 @@ Safe Remote Purchase
{
emit Aborted();
state = State.Inactive;
// We use transfer here directly. It is
// reentrancy-safe, because it is the
// last call in this function and we
// already changed the state.
seller.transfer(address(this).balance);
}
@ -97,12 +121,24 @@ Safe Remote Purchase
// It is important to change the state first because
// otherwise, the contracts called using `send` below
// can call in again here.
state = State.Inactive;
// NOTE: This actually allows both the buyer and the seller to
// block the refund - the withdraw pattern should be used.
state = State.Release;
buyer.transfer(value);
seller.transfer(address(this).balance);
}
/// This function refunds the seller, i.e.
/// pays back the locked funds of the seller.
function refundSeller()
public
onlySeller
inState(State.Release)
{
emit SellerRefunded();
// It is important to change the state first because
// otherwise, the contracts called using `send` below
// can call in again here.
state = State.Inactive;
seller.transfer(3 * value);
}
}

View File

@ -66,6 +66,7 @@ They have varying degrees of completeness and up-to-dateness. The English
version stands as a reference.
* `French <http://solidity-fr.readthedocs.io>`_ (in progress)
* `Italian <https://github.com/damianoazzolini/solidity>`_ (in progress)
* `Japanese <https://solidity-jp.readthedocs.io>`_
* `Korean <http://solidity-kr.readthedocs.io>`_ (in progress)
* `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)

View File

@ -459,7 +459,7 @@ a non-rational number).
String Literals and Types
-------------------------
String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``.
String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``), and they can also be split into multiple consecutive parts (``"foo" "bar"`` is equivalent to ``"foobar"``) which can be helpful when dealing with long strings. They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``.
For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type.
@ -498,7 +498,7 @@ terminate the string literal. Newline only terminates the string literal if it i
Hexadecimal Literals
--------------------
Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values.
Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``), and they can also be split into multiple consecutive parts (``hex"00112233" hex"44556677"`` is equivalent to ``hex"0011223344556677"``). Their content must be a hexadecimal string and their value will be the binary representation of those values.
Hexadecimal literals behave like :ref:`string literals <string_literals>` and have the same convertibility restrictions.

View File

@ -262,7 +262,15 @@ Contract Related
the current contract, explicitly convertible to :ref:`address`
``selfdestruct(address payable recipient)``:
destroy the current contract, sending its funds to the given :ref:`address`
Destroy the current contract, sending its funds to the given :ref:`address`
and end execution.
Note that ``selfdestruct`` has some peculiarities inherited from the EVM:
- the receiving contract's receive function is not executed.
- the contract is only really destroyed at the end of the transaction and ``revert`` s might "undo" the destruction.
Furthermore, all functions of the current contract are callable directly including the current function.

View File

@ -57,7 +57,7 @@ Either add ``--libraries "file.sol:Math:0x12345678901234567890123456789012345678
If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case.
If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses.
If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a "success" state and report any errors via the JSON output.
.. note::
The library placeholder used to be the fully qualified name of the library itself
@ -120,9 +120,9 @@ at each version. Backward compatibility is not guaranteed between each version.
- ``constantinople``
- Opcodes ``create2`, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly.
- Shifting operators use shifting opcodes and thus need less gas.
- ``petersburg`` (**default**)
- ``petersburg``
- The compiler behaves the same way as with constantinople.
- ``istanbul``
- ``istanbul`` (**default**)
- Opcodes ``chainid`` and ``selfbalance`` are available in assembly.
- ``berlin`` (**experimental**)
@ -140,6 +140,8 @@ The fields are generally subject to change,
some are optional (as noted), but we try to only make backwards compatible changes.
The compiler API expects a JSON formatted input and outputs the compilation result in a JSON formatted output.
The standard error output is not used and the process will always terminate in a "success" state, even
if there were errors. Errors are always reported as part of the JSON output.
The following subsections describe the format through an example.
Comments are of course not permitted and used here only for explanatory purposes.

View File

@ -168,7 +168,7 @@ The ``continue`` and ``break`` statements can only be used inside loop bodies
and have to be in the same function as the loop (or both have to be at the
top level).
The condition part of the for-loop has to evaluate to exactly one value.
Functions cannot be defined inside for loop init blocks.
Functions cannot be defined anywhere inside for loop init blocks.
Literals cannot be larger than the their type. The largest type defined is 256-bit wide.
@ -182,11 +182,17 @@ introduce new identifiers into these scopes.
Identifiers are visible in
the block they are defined in (including all sub-nodes and sub-blocks).
As an exception, identifiers defined in the "init" part of the for-loop
As an exception, identifiers defined directly in the "init" part of the for-loop
(the first block) are visible in all other parts of the for-loop
(but not outside of the loop).
Identifiers declared in the other parts of the for loop respect the regular
syntactical scoping rules.
This means a for-loop of the form ``for { I... } C { P... } { B... }`` is equivalent
to ``{ I... for {} C { P... } { B... } }``.
The parameters and return parameters of functions are visible in the
function body and their names cannot overlap.

View File

@ -23,32 +23,59 @@
#include <libdevcore/Exceptions.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/Keccak256.h>
#include <libdevcore/FixedHash.h>
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace dev;
namespace
{
static char const* upperHexChars = "0123456789ABCDEF";
static char const* lowerHexChars = "0123456789abcdef";
}
string dev::toHex(uint8_t _data, HexCase _case)
{
assertThrow(_case != HexCase::Mixed, BadHexCase, "Mixed case can only be used for byte arrays.");
char const* chars = _case == HexCase::Upper ? upperHexChars : lowerHexChars;
return std::string{
chars[(unsigned(_data) / 16) & 0xf],
chars[unsigned(_data) & 0xf]
};
}
string dev::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case)
{
std::ostringstream ret;
if (_prefix == HexPrefix::Add)
ret << "0x";
std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0);
size_t i = 0;
if (_prefix == HexPrefix::Add)
{
ret[i++] = '0';
ret[i++] = 'x';
}
// Mixed case will be handled inside the loop.
char const* chars = _case == HexCase::Upper ? upperHexChars : lowerHexChars;
int rix = _data.size() - 1;
for (uint8_t c: _data)
{
// switch hex case every four hexchars
auto hexcase = std::nouppercase;
if (_case == HexCase::Upper)
hexcase = std::uppercase;
else if (_case == HexCase::Mixed)
hexcase = (rix-- & 2) == 0 ? std::nouppercase : std::uppercase;
if (_case == HexCase::Mixed)
chars = (rix-- & 2) == 0 ? lowerHexChars : upperHexChars;
ret << std::hex << hexcase << std::setfill('0') << std::setw(2) << size_t(c);
ret[i++] = chars[(unsigned(c) / 16) & 0xf];
ret[i++] = chars[unsigned(c) & 0xf];
}
assertThrow(i == ret.size(), Exception, "");
return ret.str();
return ret;
}
int dev::fromHex(char _i, WhenError _throw)
@ -60,7 +87,7 @@ int dev::fromHex(char _i, WhenError _throw)
if (_i >= 'A' && _i <= 'F')
return _i - 'A' + 10;
if (_throw == WhenError::Throw)
BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i));
assertThrow(false, BadHexCharacter, to_string(_i));
else
return -1;
}
@ -73,22 +100,18 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw)
if (_s.size() % 2)
{
int h = fromHex(_s[s++], WhenError::DontThrow);
int h = fromHex(_s[s++], _throw);
if (h != -1)
ret.push_back(h);
else if (_throw == WhenError::Throw)
BOOST_THROW_EXCEPTION(BadHexCharacter());
else
return bytes();
}
for (unsigned i = s; i < _s.size(); i += 2)
{
int h = fromHex(_s[i], WhenError::DontThrow);
int l = fromHex(_s[i + 1], WhenError::DontThrow);
int h = fromHex(_s[i], _throw);
int l = fromHex(_s[i + 1], _throw);
if (h != -1 && l != -1)
ret.push_back((uint8_t)(h * 16 + l));
else if (_throw == WhenError::Throw)
BOOST_THROW_EXCEPTION(BadHexCharacter());
else
return bytes();
}
@ -155,3 +178,14 @@ bool dev::isValidDecimal(string const& _string)
return false;
return true;
}
string dev::formatAsStringOrNumber(string const& _value)
{
assertThrow(_value.length() <= 32, StringTooLong, "String to be formatted longer than 32 bytes.");
for (auto const& c: _value)
if (c <= 0x1f || c >= 0x7f || c == '"')
return "0x" + h256(_value, h256::AlignLeft).hex();
return "\"" + _value + "\"";
}

View File

@ -130,9 +130,12 @@ enum class HexCase
Mixed = 2,
};
/// Convert a series of bytes to the corresponding string of hex duplets.
/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169"
/// Convert a single byte to a string of hex characters (of length two),
/// optionally with uppercase hex letters.
std::string toHex(uint8_t _data, HexCase _case = HexCase::Lower);
/// Convert a series of bytes to the corresponding string of hex duplets,
/// optionally with "0x" prefix and with uppercase hex letters.
std::string toHex(bytes const& _data, HexPrefix _prefix = HexPrefix::DontAdd, HexCase _case = HexCase::Lower);
/// Converts a (printable) ASCII hex character into the corresponding integer value.
@ -207,10 +210,6 @@ inline bytes toCompactBigEndian(T _val, unsigned _min = 0)
toBigEndian(_val, ret);
return ret;
}
inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0)
{
return (_min || _val) ? bytes{ _val } : bytes{};
}
/// Convenience function for conversion of a u256 to hex
inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd)
@ -219,13 +218,18 @@ inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd)
return (prefix == HexPrefix::Add) ? "0x" + str : str;
}
inline std::string toCompactHexWithPrefix(u256 const& _value)
{
return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add);
}
/// Returns decimal representation for small numbers and hex for large numbers.
inline std::string formatNumber(bigint const& _value)
{
if (_value < 0)
return "-" + formatNumber(-_value);
if (_value > 0x1000000)
return toHex(toCompactBigEndian(_value), HexPrefix::Add);
return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add);
else
return _value.str();
}
@ -233,17 +237,11 @@ inline std::string formatNumber(bigint const& _value)
inline std::string formatNumber(u256 const& _value)
{
if (_value > 0x1000000)
return toHex(toCompactBigEndian(_value), HexPrefix::Add);
return toCompactHexWithPrefix(_value);
else
return _value.str();
}
inline std::string toCompactHexWithPrefix(u256 val)
{
std::ostringstream ret;
ret << std::hex << val;
return "0x" + ret.str();
}
// Algorithms for string and string-like collections.
@ -292,7 +290,6 @@ void iterateReplacing(std::vector<T>& _vector, F const& _f)
_vector = std::move(modifiedVector);
}
namespace detail
{
template <typename T, typename F, std::size_t... I>
@ -357,6 +354,11 @@ std::string getChecksummedAddress(std::string const& _addr);
bool isValidHex(std::string const& _string);
bool isValidDecimal(std::string const& _string);
/// @returns a quoted string if all characters are printable ASCII chars,
/// or its hex representation otherwise.
/// _value cannot be longer than 32 bytes.
std::string formatAsStringOrNumber(std::string const& _value);
template<typename Container, typename Compare>
bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare)
{

View File

@ -46,11 +46,12 @@ private:
DEV_SIMPLE_EXCEPTION(InvalidAddress);
DEV_SIMPLE_EXCEPTION(BadHexCharacter);
DEV_SIMPLE_EXCEPTION(BadHexCase);
DEV_SIMPLE_EXCEPTION(FileError);
DEV_SIMPLE_EXCEPTION(DataTooLong);
DEV_SIMPLE_EXCEPTION(StringTooLong);
// error information to be added to exceptions
using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>;
using errinfo_comment = boost::error_info<struct tag_comment, std::string>;
}

View File

@ -17,6 +17,7 @@
#include <libdevcore/IpfsHash.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/picosha2.h>
#include <libdevcore/CommonData.h>
@ -55,11 +56,7 @@ string base58Encode(bytes const& _data)
bytes dev::ipfsHash(string _data)
{
if (_data.length() >= 1024 * 256)
BOOST_THROW_EXCEPTION(
DataTooLong() <<
errinfo_comment("Ipfs hash for large (chunked) files not yet implemented.")
);
assertThrow(_data.length() < 1024 * 256, DataTooLong, "IPFS hash for large (chunked) files not yet implemented.");
bytes lengthAsVarint = varintEncoding(_data.size());

View File

@ -32,8 +32,8 @@
using namespace std;
static_assert(
(JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 8) && (JSONCPP_VERSION_PATCH == 4),
"Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.8.4."
(JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 9) && (JSONCPP_VERSION_PATCH == 2),
"Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.9.2."
);
namespace dev
@ -111,16 +111,4 @@ bool jsonParseStrict(string const& _input, Json::Value& _json, string* _errs /*
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);
}
bool jsonParseFile(string const& _fileName, Json::Value& _json, string *_errs /* = nullptr */)
{
return jsonParse(readFileAsString(_fileName), _json, _errs);
}
} // namespace dev

View File

@ -41,18 +41,4 @@ std::string jsonCompactPrint(Json::Value const& _input);
/// \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);
/// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json)
/// \param _input file containing JSON input
/// \param _json [out] resulting JSON object
/// \param _errs [out] Formatted error messages
/// \return \c true if the document was successfully parsed, \c false if an error occurred.
bool jsonParseFile(std::string const& _fileName, Json::Value& _json, std::string* _errs = nullptr);
}

View File

@ -20,109 +20,41 @@
#pragma once
#include <functional>
#include <boost/variant/static_visitor.hpp>
namespace dev
{
/// Generic visitor used as follows:
/// boost::apply_visitor(GenericVisitor<Class1, Class2>(
/// [](Class1& _c) { _c.f(); },
/// [](Class2& _c) { _c.g(); }
/// ), variant);
/// This one does not have a fallback and will fail at
/// compile-time if you do not specify all variants.
/**
* Generic visitor used as follows:
* std::visit(GenericVisitor{
* [](Class1& _c) { _c.f(); },
* [](Class2& _c) { _c.g(); }
* }, variant);
* This one does not have a fallback and will fail at
* compile-time if you do not specify all variants.
*
* Fallback with no return (it will not fail if you do not specify all variants):
* std::visit(GenericVisitor{
* VisitorFallback<>{},
* [](Class1& _c) { _c.f(); },
* [](Class2& _c) { _c.g(); }
* }, variant);
*
* Fallback with return type R (the fallback returns `R{}`:
* std::visit(GenericVisitor{
* VisitorFallback<R>{},
* [](Class1& _c) { _c.f(); },
* [](Class2& _c) { _c.g(); }
* }, variant);
*/
template <class...>
struct GenericVisitor{};
template <typename...> struct VisitorFallback;
template <class Visitable, class... Others>
struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...>
{
using GenericVisitor<Others...>::operator ();
explicit GenericVisitor(
std::function<void(Visitable&)> _visitor,
std::function<void(Others&)>... _otherVisitors
):
GenericVisitor<Others...>(std::move(_otherVisitors)...),
m_visitor(std::move(_visitor))
{}
template <typename R>
struct VisitorFallback<R> { template<typename T> R operator()(T&&) const { return {}; } };
void operator()(Visitable& _v) const { m_visitor(_v); }
std::function<void(Visitable&)> m_visitor;
};
template <>
struct GenericVisitor<>: public boost::static_visitor<> {
void operator()() const {}
};
/// Generic visitor with fallback:
/// boost::apply_visitor(GenericFallbackVisitor<Class1, Class2>(
/// [](Class1& _c) { _c.f(); },
/// [](Class2& _c) { _c.g(); }
/// ), variant);
/// This one DOES have a fallback and will NOT fail at
/// compile-time if you do not specify all variants.
template <class...>
struct GenericFallbackVisitor{};
template <class Visitable, class... Others>
struct GenericFallbackVisitor<Visitable, Others...>: public GenericFallbackVisitor<Others...>
{
explicit GenericFallbackVisitor(
std::function<void(Visitable&)> _visitor,
std::function<void(Others&)>... _otherVisitors
):
GenericFallbackVisitor<Others...>(std::move(_otherVisitors)...),
m_visitor(std::move(_visitor))
{}
using GenericFallbackVisitor<Others...>::operator ();
void operator()(Visitable& _v) const { m_visitor(_v); }
std::function<void(Visitable&)> m_visitor;
};
template <>
struct GenericFallbackVisitor<>: public boost::static_visitor<> {
template <class T>
void operator()(T&) const { }
};
/// Generic visitor with fallback that can return a value:
/// boost::apply_visitor(GenericFallbackReturnsVisitor<ReturnType, Class1, Class2>(
/// [](Class1& _c) { return _c.f(); },
/// [](Class2& _c) { return _c.g(); }
/// ), variant);
/// This one DOES have a fallback and will NOT fail at
/// compile-time if you do not specify all variants.
/// The fallback {}-constructs the return value.
template <class R, class...>
struct GenericFallbackReturnsVisitor{};
template <class R, class Visitable, class... Others>
struct GenericFallbackReturnsVisitor<R, Visitable, Others...>: public GenericFallbackReturnsVisitor<R, Others...>
{
explicit GenericFallbackReturnsVisitor(
std::function<R(Visitable&)> _visitor,
std::function<R(Others&)>... _otherVisitors
):
GenericFallbackReturnsVisitor<R, Others...>(std::move(_otherVisitors)...),
m_visitor(std::move(_visitor))
{}
using GenericFallbackReturnsVisitor<R, Others...>::operator ();
R operator()(Visitable& _v) const { return m_visitor(_v); }
std::function<R(Visitable&)> m_visitor;
};
template <class R>
struct GenericFallbackReturnsVisitor<R>: public boost::static_visitor<R> {
template <class T>
R operator()(T&) const { return {}; }
};
template<>
struct VisitorFallback<> { template<typename T> void operator()(T&&) const {} };
template <typename... Visitors> struct GenericVisitor: Visitors... { using Visitors::operator()...; };
template <typename... Visitors> GenericVisitor(Visitors...) -> GenericVisitor<Visitors...>;
}

View File

@ -323,7 +323,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
collection.append(createJsonValue("PUSH data", i.location().start, i.location().end, toStringInHex(i.data())));
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
assertThrow(false, InvalidOpcode, "");
}
}
@ -519,8 +519,11 @@ map<u256, u256> Assembly::optimiseInternal(
LinkerObject const& Assembly::assemble() const
{
// Return the already assembled object, if present.
if (!m_assembledObject.bytecode.empty())
return m_assembledObject;
// Otherwise ensure the object is actually clear.
assertThrow(m_assembledObject.linkReferences.empty(), AssemblyException, "Unexpected link references.");
size_t subTagSize = 1;
for (auto const& sub: m_subs)
@ -638,7 +641,7 @@ LinkerObject const& Assembly::assemble() const
ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST);
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling.");
}
}

View File

@ -81,7 +81,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
default:
break;
}
BOOST_THROW_EXCEPTION(InvalidOpcode());
assertThrow(false, InvalidOpcode, "");
}
int AssemblyItem::arguments() const
@ -212,7 +212,7 @@ string AssemblyItem::toAssemblyText() const
assertThrow(false, AssemblyException, "Invalid assembly item.");
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
assertThrow(false, InvalidOpcode, "");
}
if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction)
{
@ -277,7 +277,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
_out << " ???";
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
assertThrow(false, InvalidOpcode, "");
}
return _out;
}

View File

@ -158,11 +158,10 @@ AssemblyItems CSECodeGenerator::generateCode(
for (auto id: {p.first, p.second})
if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
{
if (seqNr < _initialSequenceNumber)
// Invalid sequenced operation.
// @todo quick fix for now. Proper fix needs to choose representative with higher
// sequence number during dependency analysis.
BOOST_THROW_EXCEPTION(StackTooDeepException());
// Invalid sequenced operation.
// @todo quick fix for now. Proper fix needs to choose representative with higher
// sequence number during dependency analysis.
assertThrow(seqNr >= _initialSequenceNumber, StackTooDeepException, "");
sequencedExpressions.insert(make_pair(seqNr, id));
}
@ -222,12 +221,9 @@ void CSECodeGenerator::addDependencies(Id _c)
return; // we already computed the dependencies for _c
ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
assertThrow(expr.item, OptimizerException, "");
if (expr.item->type() == UndefinedItem)
BOOST_THROW_EXCEPTION(
// If this exception happens, we need to find a different way to generate the
// compound expression.
ItemNotAvailableException() << errinfo_comment("Undefined item requested but not available.")
);
// If this exception happens, we need to find a different way to generate the
// compound expression.
assertThrow(expr.item->type() != UndefinedItem, ItemNotAvailableException, "Undefined item requested but not available.");
for (Id argument: expr.arguments)
{
addDependencies(argument);

View File

@ -96,7 +96,7 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
{
assertThrow(_data.size() > 0, OptimizerException, "Empty bytecode generated.");
return bigint(GasMeter::dataGas(_data, m_params.isCreation));
return bigint(GasMeter::dataGas(_data, m_params.isCreation, m_params.evmVersion));
}
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
@ -131,7 +131,7 @@ bigint LiteralMethod::gasNeeded() const
return combineGas(
simpleRunGas({Instruction::PUSH1}),
// PUSHX plus data
(m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)),
(m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)),
0
);
}
@ -142,7 +142,7 @@ bigint CodeCopyMethod::gasNeeded() const
// Run gas: we ignore memory increase costs
simpleRunGas(copyRoutine()) + GasCosts::copyGas,
// Data gas for copy routines: Some bytes are zero, but we ignore them.
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas),
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
// Data gas for data itself
dataGas(toBigEndian(m_value))
);
@ -322,7 +322,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const
return combineGas(
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)),
// 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(m_params.evmVersion) : GasCosts::createDataGas),
0
);
}

View File

@ -33,5 +33,8 @@ struct OptimizerException: virtual AssemblyException {};
struct StackTooDeepException: virtual OptimizerException {};
struct ItemNotAvailableException: virtual OptimizerException {};
DEV_SIMPLE_EXCEPTION(InvalidDeposit);
DEV_SIMPLE_EXCEPTION(InvalidOpcode);
}
}

View File

@ -266,13 +266,13 @@ unsigned GasMeter::runGas(Instruction _instruction)
return 0;
}
u256 GasMeter::dataGas(bytes const& _data, bool _inCreation)
u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion)
{
bigint gas = 0;
if (_inCreation)
{
for (auto b: _data)
gas += (b != 0) ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas;
gas += (b != 0) ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::txDataZeroGas;
}
else
gas = bigint(GasCosts::createDataGas) * _data.size();

View File

@ -53,7 +53,12 @@ namespace GasCosts
}
inline unsigned balanceGas(langutil::EVMVersion _evmVersion)
{
return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 400 : 20;
if (_evmVersion >= langutil::EVMVersion::istanbul())
return 700;
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
return 400;
else
return 20;
}
static unsigned const expGas = 10;
inline unsigned expByteGas(langutil::EVMVersion _evmVersion)
@ -64,7 +69,12 @@ namespace GasCosts
static unsigned const keccak256WordGas = 6;
inline unsigned sloadGas(langutil::EVMVersion _evmVersion)
{
return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 200 : 50;
if (_evmVersion >= langutil::EVMVersion::istanbul())
return 800;
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
return 200;
else
return 50;
}
static unsigned const sstoreSetGas = 20000;
static unsigned const sstoreResetGas = 5000;
@ -92,7 +102,10 @@ namespace GasCosts
static unsigned const txGas = 21000;
static unsigned const txCreateGas = 53000;
static unsigned const txDataZeroGas = 4;
static unsigned const txDataNonZeroGas = 68;
inline unsigned txDataNonZeroGas(langutil::EVMVersion _evmVersion)
{
return _evmVersion >= langutil::EVMVersion::istanbul() ? 16 : 68;
}
static unsigned const copyGas = 3;
}
@ -139,7 +152,7 @@ public:
/// @returns the gas cost of the supplied data, depending whether it is in creation code, or not.
/// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas
/// otherwise code will be stored and have to pay "createDataGas" cost.
static u256 dataGas(bytes const& _data, bool _inCreation);
static u256 dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion);
private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.

View File

@ -31,9 +31,6 @@ namespace dev
namespace eth
{
DEV_SIMPLE_EXCEPTION(InvalidDeposit);
DEV_SIMPLE_EXCEPTION(InvalidOpcode);
/// Virtual machine bytecode instruction.
enum class Instruction: uint8_t
{

View File

@ -54,17 +54,17 @@ ostream& KnownState::stream(ostream& _out) const
_out << "=== State ===" << endl;
_out << "Stack height: " << dec << m_stackHeight << endl;
_out << "Equivalence classes: " << endl;
_out << "Equivalence classes:" << endl;
for (Id eqClass = 0; eqClass < m_expressionClasses->size(); ++eqClass)
streamExpressionClass(_out, eqClass);
_out << "Stack: " << endl;
_out << "Stack:" << endl;
for (auto const& it: m_stackElements)
{
_out << " " << dec << it.first << ": ";
streamExpressionClass(_out, it.second);
}
_out << "Storage: " << endl;
_out << "Storage:" << endl;
for (auto const& it: m_storageContent)
{
_out << " ";
@ -72,7 +72,7 @@ ostream& KnownState::stream(ostream& _out) const
_out << ": ";
streamExpressionClass(_out, it.second);
}
_out << "Memory: " << endl;
_out << "Memory:" << endl;
for (auto const& it: m_memoryContent)
{
_out << " ";

View File

@ -35,7 +35,9 @@ namespace eth
*/
struct LinkerObject
{
/// The bytecode.
bytes bytecode;
/// Map from offsets in bytecode to library identifiers. The addresses starting at those offsets
/// need to be replaced by the actual addresses by the linker.
std::map<size_t, std::string> linkReferences;
@ -47,7 +49,7 @@ struct LinkerObject
void link(std::map<std::string, h160> const& _libraryAddresses);
/// @returns a hex representation of the bytecode of the given object, replacing unlinked
/// addresses by placeholders.
/// addresses by placeholders. This output is lowercase.
std::string toHex() const;
/// @returns a 36 character string that is used as a placeholder for the library

View File

@ -40,7 +40,7 @@ GasMeter::GasConsumption PathGasMeter::estimateMax(
shared_ptr<KnownState> const& _state
)
{
auto path = unique_ptr<GasPath>(new GasPath());
auto path = make_unique<GasPath>();
path->index = _startIndex;
path->state = _state->copy();
queue(move(path));
@ -120,7 +120,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
for (u256 const& tag: jumpTags)
{
auto newPath = unique_ptr<GasPath>(new GasPath());
auto newPath = make_unique<GasPath>();
newPath->index = m_items.size();
if (m_tagPositions.count(tag))
newPath->index = m_tagPositions.at(tag);

View File

@ -67,47 +67,48 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// arithmetic on constants
{{Pattern::Builtins::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
{{Pattern::Builtins::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
{{Pattern::Builtins::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
{{Pattern::Builtins::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
{{Pattern::Builtins::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
{{Pattern::Builtins::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
{{Pattern::Builtins::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
{{Pattern::Builtins::EXP, {A, B}}, [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false},
{{Pattern::Builtins::NOT, {A}}, [=]{ return ~A.d(); }, false},
{{Pattern::Builtins::LT, {A, B}}, [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false},
{{Pattern::Builtins::GT, {A, B}}, [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false},
{{Pattern::Builtins::SLT, {A, B}}, [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
{{Pattern::Builtins::SGT, {A, B}}, [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false},
{{Pattern::Builtins::EQ, {A, B}}, [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false},
{{Pattern::Builtins::ISZERO, {A}}, [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false},
{{Pattern::Builtins::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false},
{{Pattern::Builtins::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false},
{{Pattern::Builtins::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false},
{{Pattern::Builtins::BYTE, {A, B}}, [=]{
{Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }, false},
{Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }, false},
{Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }, false},
{Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
{Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
{Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
{Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
{Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false},
{Builtins::NOT(A), [=]{ return ~A.d(); }, false},
{Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false},
{Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false},
{Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
{Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false},
{Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false},
{Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false},
{Builtins::AND(A, B), [=]{ return A.d() & B.d(); }, false},
{Builtins::OR(A, B), [=]{ return A.d() | B.d(); }, false},
{Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }, false},
{Builtins::BYTE(A, B), [=]{
return
A.d() >= Pattern::WordSize / 8 ?
0 :
(B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff;
}, false},
{{Pattern::Builtins::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false},
{{Pattern::Builtins::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false},
{{Pattern::Builtins::SIGNEXTEND, {A, B}}, [=]() -> Word {
{Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false},
{Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false},
{Builtins::SIGNEXTEND(A, B), [=]() -> Word {
if (A.d() >= Pattern::WordSize / 8 - 1)
return B.d();
unsigned testBit = unsigned(A.d()) * 8 + 7;
Word mask = (Word(1) << testBit) - 1;
return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask;
}, false},
{{Pattern::Builtins::SHL, {A, B}}, [=]{
{Builtins::SHL(A, B), [=]{
if (A.d() >= Pattern::WordSize)
return Word(0);
return shlWorkaround(B.d(), unsigned(A.d()));
}, false},
{{Pattern::Builtins::SHR, {A, B}}, [=]{
{Builtins::SHR(A, B), [=]{
if (A.d() >= Pattern::WordSize)
return Word(0);
return B.d() >> unsigned(A.d());
@ -115,6 +116,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
};
}
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
Pattern,
@ -125,50 +127,51 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// invariants involving known constants
{{Pattern::Builtins::ADD, {X, 0}}, [=]{ return X; }, false},
{{Pattern::Builtins::ADD, {0, X}}, [=]{ return X; }, false},
{{Pattern::Builtins::SUB, {X, 0}}, [=]{ return X; }, false},
{{Pattern::Builtins::SUB, {~Word(0), X}}, [=]() -> Pattern { return {Pattern::Builtins::NOT, {X}}; }, false},
{{Pattern::Builtins::MUL, {X, 0}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::MUL, {0, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::MUL, {X, 1}}, [=]{ return X; }, false},
{{Pattern::Builtins::MUL, {1, X}}, [=]{ return X; }, false},
{{Pattern::Builtins::MUL, {X, Word(-1)}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false},
{{Pattern::Builtins::MUL, {Word(-1), X}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false},
{{Pattern::Builtins::DIV, {X, 0}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::DIV, {0, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::DIV, {X, 1}}, [=]{ return X; }, false},
{{Pattern::Builtins::SDIV, {X, 0}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::SDIV, {0, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::SDIV, {X, 1}}, [=]{ return X; }, false},
{{Pattern::Builtins::AND, {X, ~Word(0)}}, [=]{ return X; }, false},
{{Pattern::Builtins::AND, {~Word(0), X}}, [=]{ return X; }, false},
{{Pattern::Builtins::AND, {X, 0}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::AND, {0, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::OR, {X, 0}}, [=]{ return X; }, false},
{{Pattern::Builtins::OR, {0, X}}, [=]{ return X; }, false},
{{Pattern::Builtins::OR, {X, ~Word(0)}}, [=]{ return ~Word(0); }, true},
{{Pattern::Builtins::OR, {~Word(0), X}}, [=]{ return ~Word(0); }, true},
{{Pattern::Builtins::XOR, {X, 0}}, [=]{ return X; }, false},
{{Pattern::Builtins::XOR, {0, X}}, [=]{ return X; }, false},
{{Pattern::Builtins::MOD, {X, 0}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::MOD, {0, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::EQ, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false },
{{Pattern::Builtins::EQ, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false },
{{Pattern::Builtins::SHL, {0, X}}, [=]{ return X; }, false},
{{Pattern::Builtins::SHR, {0, X}}, [=]{ return X; }, false},
{{Pattern::Builtins::SHL, {X, 0}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::SHR, {X, 0}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::GT, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false},
{{Pattern::Builtins::LT, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false},
{{Pattern::Builtins::GT, {X, ~Word(0)}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::LT, {~Word(0), X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::GT, {0, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::LT, {X, 0}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::AND, {{Pattern::Builtins::BYTE, {X, Y}}, {Word(0xff)}}}, [=]() -> Pattern { return {Pattern::Builtins::BYTE, {X, Y}}; }, false},
{{Pattern::Builtins::BYTE, {Pattern::WordSize / 8 - 1, X}}, [=]() -> Pattern { return {Pattern::Builtins::AND, {X, Word(0xff)}}; }, false}
{Builtins::ADD(X, 0), [=]{ return X; }, false},
{Builtins::ADD(0, X), [=]{ return X; }, false},
{Builtins::SUB(X, 0), [=]{ return X; }, false},
{Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }, false},
{Builtins::MUL(X, 0), [=]{ return Word(0); }, true},
{Builtins::MUL(0, X), [=]{ return Word(0); }, true},
{Builtins::MUL(X, 1), [=]{ return X; }, false},
{Builtins::MUL(1, X), [=]{ return X; }, false},
{Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }, false},
{Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }, false},
{Builtins::DIV(X, 0), [=]{ return Word(0); }, true},
{Builtins::DIV(0, X), [=]{ return Word(0); }, true},
{Builtins::DIV(X, 1), [=]{ return X; }, false},
{Builtins::SDIV(X, 0), [=]{ return Word(0); }, true},
{Builtins::SDIV(0, X), [=]{ return Word(0); }, true},
{Builtins::SDIV(X, 1), [=]{ return X; }, false},
{Builtins::AND(X, ~Word(0)), [=]{ return X; }, false},
{Builtins::AND(~Word(0), X), [=]{ return X; }, false},
{Builtins::AND(X, 0), [=]{ return Word(0); }, true},
{Builtins::AND(0, X), [=]{ return Word(0); }, true},
{Builtins::OR(X, 0), [=]{ return X; }, false},
{Builtins::OR(0, X), [=]{ return X; }, false},
{Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }, true},
{Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }, true},
{Builtins::XOR(X, 0), [=]{ return X; }, false},
{Builtins::XOR(0, X), [=]{ return X; }, false},
{Builtins::MOD(X, 0), [=]{ return Word(0); }, true},
{Builtins::MOD(0, X), [=]{ return Word(0); }, true},
{Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); }, false },
{Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); }, false },
{Builtins::SHL(0, X), [=]{ return X; }, false},
{Builtins::SHR(0, X), [=]{ return X; }, false},
{Builtins::SHL(X, 0), [=]{ return Word(0); }, true},
{Builtins::SHR(X, 0), [=]{ return Word(0); }, true},
{Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false},
{Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false},
{Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }, true},
{Builtins::LT(~Word(0), X), [=]{ return Word(0); }, true},
{Builtins::GT(0, X), [=]{ return Word(0); }, true},
{Builtins::LT(X, 0), [=]{ return Word(0); }, true},
{Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }, false},
{Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }, false}
};
}
@ -182,18 +185,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// operations involving an expression and itself
{{Pattern::Builtins::AND, {X, X}}, [=]{ return X; }, true},
{{Pattern::Builtins::OR, {X, X}}, [=]{ return X; }, true},
{{Pattern::Builtins::XOR, {X, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::SUB, {X, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::EQ, {X, X}}, [=]{ return Word(1); }, true},
{{Pattern::Builtins::LT, {X, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::SLT, {X, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::GT, {X, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::SGT, {X, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::MOD, {X, X}}, [=]{ return Word(0); }, true}
{Builtins::AND(X, X), [=]{ return X; }, true},
{Builtins::OR(X, X), [=]{ return X; }, true},
{Builtins::XOR(X, X), [=]{ return Word(0); }, true},
{Builtins::SUB(X, X), [=]{ return Word(0); }, true},
{Builtins::EQ(X, X), [=]{ return Word(1); }, true},
{Builtins::LT(X, X), [=]{ return Word(0); }, true},
{Builtins::SLT(X, X), [=]{ return Word(0); }, true},
{Builtins::GT(X, X), [=]{ return Word(0); }, true},
{Builtins::SGT(X, X), [=]{ return Word(0); }, true},
{Builtins::MOD(X, X), [=]{ return Word(0); }, true}
};
}
@ -207,25 +211,26 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// logical instruction combinations
{{Pattern::Builtins::NOT, {{Pattern::Builtins::NOT, {X}}}}, [=]{ return X; }, false},
{{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {X, Y}}}}, [=]{ return Y; }, true},
{{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {Y, X}}}}, [=]{ return Y; }, true},
{{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {X, Y}}, X}}, [=]{ return Y; }, true},
{{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {Y, X}}, X}}, [=]{ return Y; }, true},
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {X, Y}}}}, [=]{ return X; }, true},
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {Y, X}}}}, [=]{ return X; }, true},
{{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {X, Y}}, X}}, [=]{ return X; }, true},
{{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {Y, X}}, X}}, [=]{ return X; }, true},
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {X, Y}}}}, [=]{ return X; }, true},
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {Y, X}}}}, [=]{ return X; }, true},
{{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {X, Y}}, X}}, [=]{ return X; }, true},
{{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {Y, X}}, X}}, [=]{ return X; }, true},
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::AND, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return Word(0); }, true},
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return ~Word(0); }, true},
{{Pattern::Builtins::OR, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return ~Word(0); }, true},
{Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }, false},
{Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }, true},
{Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }, true},
{Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }, true},
{Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }, true},
{Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }, true},
{Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }, true},
{Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }, true},
{Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }, true},
{Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }, true},
{Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }, true},
{Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }, true},
{Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }, true},
{Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }, true},
{Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }, true},
{Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }, true},
{Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }, true},
};
}
@ -240,6 +245,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
@ -248,15 +254,15 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
{
Word value = Word(1) << i;
rules.push_back({
{Pattern::Builtins::MOD, {X, value}},
[=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; },
Builtins::MOD(X, value),
[=]() -> Pattern { return Builtins::AND(X, value - 1); },
false
});
}
// Replace SHL >=256, X with 0
rules.push_back({
{Pattern::Builtins::SHL, {A, X}},
Builtins::SHL(A, X),
[=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize; }
@ -264,7 +270,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
// Replace SHR >=256, X with 0
rules.push_back({
{Pattern::Builtins::SHR, {A, X}},
Builtins::SHR(A, X),
[=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize; }
@ -272,29 +278,29 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
// Replace BYTE(A, X), A >= 32 with 0
rules.push_back({
{Pattern::Builtins::BYTE, {A, X}},
Builtins::BYTE(A, X),
[=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize / 8; }
});
for (auto const& op: std::vector<Instruction>{
Pattern::Builtins::ADDRESS,
Pattern::Builtins::CALLER,
Pattern::Builtins::ORIGIN,
Pattern::Builtins::COINBASE
for (auto instr: {
Instruction::ADDRESS,
Instruction::CALLER,
Instruction::ORIGIN,
Instruction::COINBASE
})
{
assertThrow(Pattern::WordSize > 160, OptimizerException, "");
Word const mask = (Word(1) << 160) - 1;
rules.push_back({
{Pattern::Builtins::AND, {{op, mask}}},
[=]() -> Pattern { return op; },
Builtins::AND(Pattern{instr}, mask),
[=]() -> Pattern { return {instr}; },
false
});
rules.push_back({
{Pattern::Builtins::AND, {{mask, op}}},
[=]() -> Pattern { return op; },
Builtins::AND(mask, Pattern{instr}),
[=]() -> Pattern { return {instr}; },
false
});
}
@ -311,30 +317,35 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6(
Pattern Y
)
{
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
// Double negation of opcodes with boolean result
for (auto const& op: std::vector<Instruction>{
Pattern::Builtins::EQ,
Pattern::Builtins::LT,
Pattern::Builtins::SLT,
Pattern::Builtins::GT,
Pattern::Builtins::SGT
for (auto instr: {
Instruction::EQ,
Instruction::LT,
Instruction::SLT,
Instruction::GT,
Instruction::SGT
})
{
typename Builtins::PatternGeneratorInstance op{instr};
rules.push_back({
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}},
[=]() -> Pattern { return {op, {X, Y}}; },
Builtins::ISZERO(Builtins::ISZERO(op(X, Y))),
[=]() -> Pattern { return op(X, Y); },
false
});
}
rules.push_back({
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}},
[=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; },
Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))),
[=]() -> Pattern { return Builtins::ISZERO(X); },
false
});
rules.push_back({
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}},
[=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; },
Builtins::ISZERO(Builtins::XOR(X, Y)),
[=]() -> Pattern { return Builtins::EQ(X, Y); },
false
});
@ -351,42 +362,44 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
// Associative operations
for (auto const& opFun: std::vector<std::pair<Instruction,std::function<Word(Word const&,Word const&)>>>{
{Pattern::Builtins::ADD, std::plus<Word>()},
{Pattern::Builtins::MUL, std::multiplies<Word>()},
{Pattern::Builtins::AND, std::bit_and<Word>()},
{Pattern::Builtins::OR, std::bit_or<Word>()},
{Pattern::Builtins::XOR, std::bit_xor<Word>()}
for (auto&& instrAndFunc: std::vector<std::pair<Instruction, std::function<Word(Word, Word)>>>{
{Instruction::ADD, std::plus<Word>()},
{Instruction::MUL, std::multiplies<Word>()},
{Instruction::AND, std::bit_and<Word>()},
{Instruction::OR, std::bit_or<Word>()},
{Instruction::XOR, std::bit_xor<Word>()}
})
{
auto op = opFun.first;
auto fun = opFun.second;
typename Builtins::PatternGeneratorInstance op{instrAndFunc.first};
std::function<Word(Word, Word)> fun = instrAndFunc.second;
// Moving constants to the outside, order matters here - we first add rules
// for constants and then for non-constants.
// xa can be (X, A) or (A, X)
for (auto xa: {std::vector<Pattern>{X, A}, std::vector<Pattern>{A, X}})
for (auto const& opXA: {op(X, A), op(A, X)})
{
rules += std::vector<SimplificationRule<Pattern>>{{
// (X+A)+B -> X+(A+B)
{op, {{op, xa}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; },
op(opXA, B),
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); },
false
}, {
// (X+A)+Y -> (X+Y)+A
{op, {{op, xa}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; },
op(opXA, Y),
[=]() -> Pattern { return op(op(X, Y), A); },
false
}, {
// B+(X+A) -> X+(A+B)
{op, {B, {op, xa}}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; },
op(B, opXA),
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); },
false
}, {
// Y+(X+A) -> (Y+X)+A
{op, {Y, {op, xa}}},
[=]() -> Pattern { return {op, {{op, {Y, X}}, A}}; },
op(Y, opXA),
[=]() -> Pattern { return op(op(Y, X), A); },
false
}};
}
@ -395,13 +408,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
// Combine two SHL by constant
rules.push_back({
// SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X)
{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
Builtins::SHL(B, Builtins::SHL(A, X)),
[=]() -> Pattern {
bigint sum = bigint(A.d()) + B.d();
if (sum >= Pattern::WordSize)
return {Pattern::Builtins::AND, {X, Word(0)}};
return Builtins::AND(X, Word(0));
else
return {Pattern::Builtins::SHL, {Word(sum), X}};
return Builtins::SHL(Word(sum), X);
},
false
});
@ -409,13 +422,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
// Combine two SHR by constant
rules.push_back({
// SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X)
{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
Builtins::SHR(B, Builtins::SHR(A, X)),
[=]() -> Pattern {
bigint sum = bigint(A.d()) + B.d();
if (sum >= Pattern::WordSize)
return {Pattern::Builtins::AND, {X, Word(0)}};
return Builtins::AND(X, Word(0));
else
return {Pattern::Builtins::SHR, {Word(sum), X}};
return Builtins::SHR(Word(sum), X);
},
false
});
@ -423,16 +436,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
// Combine SHL-SHR by constant
rules.push_back({
// SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
Builtins::SHR(B, Builtins::SHL(A, X)),
[=]() -> Pattern {
Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d());
if (A.d() > B.d())
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}};
return Builtins::AND(Builtins::SHL(A.d() - B.d(), X), mask);
else if (B.d() > A.d())
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}};
return Builtins::AND(Builtins::SHR(B.d() - A.d(), X), mask);
else
return {Pattern::Builtins::AND, {X, mask}};
return Builtins::AND(X, mask);
},
false,
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
@ -441,41 +454,42 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
// Combine SHR-SHL by constant
rules.push_back({
// SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
Builtins::SHL(B, Builtins::SHR(A, X)),
[=]() -> Pattern {
Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d()));
if (A.d() > B.d())
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}};
return Builtins::AND(Builtins::SHR(A.d() - B.d(), X), mask);
else if (B.d() > A.d())
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}};
return Builtins::AND(Builtins::SHL(B.d() - A.d(), X), mask);
else
return {Pattern::Builtins::AND, {X, mask}};
return Builtins::AND(X, mask);
},
false,
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
});
// Move AND with constant across SHL and SHR by constant
for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR})
for (auto instr: {Instruction::SHL, Instruction::SHR})
{
typename Builtins::PatternGeneratorInstance shiftOp{instr};
auto replacement = [=]() -> Pattern {
Word mask =
shiftOp == Pattern::Builtins::SHL ?
instr == Instruction::SHL ?
shlWorkaround(A.d(), unsigned(B.d())) :
A.d() >> unsigned(B.d());
return {Pattern::Builtins::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}};
return Builtins::AND(shiftOp(B.d(), X), std::move(mask));
};
rules.push_back({
// SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
{shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}},
shiftOp(B, Builtins::AND(X, A)),
replacement,
false,
[=] { return B.d() < Pattern::WordSize; }
});
rules.push_back({
// SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
{shiftOp, {{B}, {Pattern::Builtins::AND, {{A}, {X}}}}},
shiftOp(B, Builtins::AND(A, X)),
replacement,
false,
[=] { return B.d() < Pattern::WordSize; }
@ -484,27 +498,27 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
rules.push_back({
// MUL(X, SHL(Y, 1)) -> SHL(Y, X)
{Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
Builtins::MUL(X, Builtins::SHL(Y, Word(1))),
[=]() -> Pattern {
return {Pattern::Builtins::SHL, {Y, X}};
return Builtins::SHL(Y, X);
},
// Actually only changes the order, does not remove.
true
});
rules.push_back({
// MUL(SHL(X, 1), Y) -> SHL(X, Y)
{Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}},
Builtins::MUL(Builtins::SHL(X, Word(1)), Y),
[=]() -> Pattern {
return {Pattern::Builtins::SHL, {X, Y}};
return Builtins::SHL(X, Y);
},
false
});
rules.push_back({
// DIV(X, SHL(Y, 1)) -> SHR(Y, X)
{Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
Builtins::DIV(X, Builtins::SHL(Y, Word(1))),
[=]() -> Pattern {
return {Pattern::Builtins::SHR, {Y, X}};
return Builtins::SHR(Y, X);
},
// Actually only changes the order, does not remove.
true
@ -519,16 +533,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
rules.push_back({
// AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B)
{Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}},
[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
Builtins::AND(A, Builtins::SHR(B, X)),
[=]() -> Pattern { return Builtins::SHR(B, X); },
false,
feasibilityFunction
});
rules.push_back({
// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
{Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}},
[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
Builtins::AND(Builtins::SHR(B, X), A),
[=]() -> Pattern { return Builtins::SHR(B, X); },
false,
feasibilityFunction
});
@ -545,34 +559,35 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8(
Pattern Y
)
{
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
// move constants across subtractions
rules += std::vector<SimplificationRule<Pattern>>{
{
// X - A -> X + (-A)
{Pattern::Builtins::SUB, {X, A}},
[=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; },
Builtins::SUB(X, A),
[=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); },
false
}, {
// (X + A) - Y -> (X - Y) + A
{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}},
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
Builtins::SUB(Builtins::ADD(X, A), Y),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); },
false
}, {
// (A + X) - Y -> (X - Y) + A
{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}},
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
Builtins::SUB(Builtins::ADD(A, X), Y),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); },
false
}, {
// X - (Y + A) -> (X - Y) + (-A)
{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}},
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
Builtins::SUB(X, Builtins::ADD(Y, A)),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); },
false
}, {
// X - (A + Y) -> (X - Y) + (-A)
{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}},
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
Builtins::SUB(X, Builtins::ADD(A, Y)),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); },
false
}
};
@ -591,30 +606,31 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart9(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
assertThrow(Pattern::WordSize > 160, OptimizerException, "");
Word const mask = (Word(1) << 160) - 1;
// CREATE
rules.push_back({
{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}},
[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
Builtins::AND(Builtins::CREATE(W, X, Y), mask),
[=]() -> Pattern { return Builtins::CREATE(W, X, Y); },
false
});
rules.push_back({
{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}},
[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
Builtins::AND(mask, Builtins::CREATE(W, X, Y)),
[=]() -> Pattern { return Builtins::CREATE(W, X, Y); },
false
});
// CREATE2
rules.push_back({
{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}},
[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
Builtins::AND(Builtins::CREATE2(W, X, Y, Z), mask),
[=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); },
false
});
rules.push_back({
{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}},
[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
Builtins::AND(mask, Builtins::CREATE2(W, X, Y, Z)),
[=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); },
false
});

View File

@ -21,6 +21,7 @@
#pragma once
#include <libevmasm/Instruction.h>
#include <libdevcore/CommonData.h>
#include <functional>
namespace dev
@ -55,84 +56,105 @@ struct SimplificationRule
std::function<bool()> feasible;
};
template <typename Pattern>
struct EVMBuiltins
{
using InstrType = Instruction;
static auto constexpr STOP = Instruction::STOP;
static auto constexpr ADD = Instruction::ADD;
static auto constexpr SUB = Instruction::SUB;
static auto constexpr MUL = Instruction::MUL;
static auto constexpr DIV = Instruction::DIV;
static auto constexpr SDIV = Instruction::SDIV;
static auto constexpr MOD = Instruction::MOD;
static auto constexpr SMOD = Instruction::SMOD;
static auto constexpr EXP = Instruction::EXP;
static auto constexpr NOT = Instruction::NOT;
static auto constexpr LT = Instruction::LT;
static auto constexpr GT = Instruction::GT;
static auto constexpr SLT = Instruction::SLT;
static auto constexpr SGT = Instruction::SGT;
static auto constexpr EQ = Instruction::EQ;
static auto constexpr ISZERO = Instruction::ISZERO;
static auto constexpr AND = Instruction::AND;
static auto constexpr OR = Instruction::OR;
static auto constexpr XOR = Instruction::XOR;
static auto constexpr BYTE = Instruction::BYTE;
static auto constexpr SHL = Instruction::SHL;
static auto constexpr SHR = Instruction::SHR;
static auto constexpr SAR = Instruction::SAR;
static auto constexpr ADDMOD = Instruction::ADDMOD;
static auto constexpr MULMOD = Instruction::MULMOD;
static auto constexpr SIGNEXTEND = Instruction::SIGNEXTEND;
static auto constexpr KECCAK256 = Instruction::KECCAK256;
static auto constexpr ADDRESS = Instruction::ADDRESS;
static auto constexpr BALANCE = Instruction::BALANCE;
static auto constexpr ORIGIN = Instruction::ORIGIN;
static auto constexpr CALLER = Instruction::CALLER;
static auto constexpr CALLVALUE = Instruction::CALLVALUE;
static auto constexpr CALLDATALOAD = Instruction::CALLDATALOAD;
static auto constexpr CALLDATASIZE = Instruction::CALLDATASIZE;
static auto constexpr CALLDATACOPY = Instruction::CALLDATACOPY;
static auto constexpr CODESIZE = Instruction::CODESIZE;
static auto constexpr CODECOPY = Instruction::CODECOPY;
static auto constexpr GASPRICE = Instruction::GASPRICE;
static auto constexpr EXTCODESIZE = Instruction::EXTCODESIZE;
static auto constexpr EXTCODECOPY = Instruction::EXTCODECOPY;
static auto constexpr RETURNDATASIZE = Instruction::RETURNDATASIZE;
static auto constexpr RETURNDATACOPY = Instruction::RETURNDATACOPY;
static auto constexpr EXTCODEHASH = Instruction::EXTCODEHASH;
static auto constexpr BLOCKHASH = Instruction::BLOCKHASH;
static auto constexpr COINBASE = Instruction::COINBASE;
static auto constexpr TIMESTAMP = Instruction::TIMESTAMP;
static auto constexpr NUMBER = Instruction::NUMBER;
static auto constexpr DIFFICULTY = Instruction::DIFFICULTY;
static auto constexpr GASLIMIT = Instruction::GASLIMIT;
static auto constexpr CHAINID = Instruction::CHAINID;
static auto constexpr SELFBALANCE = Instruction::SELFBALANCE;
static auto constexpr POP = Instruction::POP;
static auto constexpr MLOAD = Instruction::MLOAD;
static auto constexpr MSTORE = Instruction::MSTORE;
static auto constexpr MSTORE8 = Instruction::MSTORE8;
static auto constexpr SLOAD = Instruction::SLOAD;
static auto constexpr SSTORE = Instruction::SSTORE;
static auto constexpr PC = Instruction::PC;
static auto constexpr MSIZE = Instruction::MSIZE;
static auto constexpr GAS = Instruction::GAS;
static auto constexpr LOG0 = Instruction::LOG0;
static auto constexpr LOG1 = Instruction::LOG1;
static auto constexpr LOG2 = Instruction::LOG2;
static auto constexpr LOG3 = Instruction::LOG3;
static auto constexpr LOG4 = Instruction::LOG4;
static auto constexpr CREATE = Instruction::CREATE;
static auto constexpr CALL = Instruction::CALL;
static auto constexpr CALLCODE = Instruction::CALLCODE;
static auto constexpr STATICCALL = Instruction::STATICCALL;
static auto constexpr RETURN = Instruction::RETURN;
static auto constexpr DELEGATECALL = Instruction::DELEGATECALL;
static auto constexpr CREATE2 = Instruction::CREATE2;
static auto constexpr REVERT = Instruction::REVERT;
static auto constexpr INVALID = Instruction::INVALID;
static auto constexpr SELFDESTRUCT = Instruction::SELFDESTRUCT;
template<Instruction inst>
struct PatternGenerator
{
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
{
return {inst, {std::forward<Args>(_args)...}};
};
};
struct PatternGeneratorInstance
{
Instruction instruction;
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
{
return {instruction, {std::forward<Args>(_args)...}};
};
};
static auto constexpr STOP = PatternGenerator<Instruction::STOP>{};
static auto constexpr ADD = PatternGenerator<Instruction::ADD>{};
static auto constexpr SUB = PatternGenerator<Instruction::SUB>{};
static auto constexpr MUL = PatternGenerator<Instruction::MUL>{};
static auto constexpr DIV = PatternGenerator<Instruction::DIV>{};
static auto constexpr SDIV = PatternGenerator<Instruction::SDIV>{};
static auto constexpr MOD = PatternGenerator<Instruction::MOD>{};
static auto constexpr SMOD = PatternGenerator<Instruction::SMOD>{};
static auto constexpr EXP = PatternGenerator<Instruction::EXP>{};
static auto constexpr NOT = PatternGenerator<Instruction::NOT>{};
static auto constexpr LT = PatternGenerator<Instruction::LT>{};
static auto constexpr GT = PatternGenerator<Instruction::GT>{};
static auto constexpr SLT = PatternGenerator<Instruction::SLT>{};
static auto constexpr SGT = PatternGenerator<Instruction::SGT>{};
static auto constexpr EQ = PatternGenerator<Instruction::EQ>{};
static auto constexpr ISZERO = PatternGenerator<Instruction::ISZERO>{};
static auto constexpr AND = PatternGenerator<Instruction::AND>{};
static auto constexpr OR = PatternGenerator<Instruction::OR>{};
static auto constexpr XOR = PatternGenerator<Instruction::XOR>{};
static auto constexpr BYTE = PatternGenerator<Instruction::BYTE>{};
static auto constexpr SHL = PatternGenerator<Instruction::SHL>{};
static auto constexpr SHR = PatternGenerator<Instruction::SHR>{};
static auto constexpr SAR = PatternGenerator<Instruction::SAR>{};
static auto constexpr ADDMOD = PatternGenerator<Instruction::ADDMOD>{};
static auto constexpr MULMOD = PatternGenerator<Instruction::MULMOD>{};
static auto constexpr SIGNEXTEND = PatternGenerator<Instruction::SIGNEXTEND>{};
static auto constexpr KECCAK256 = PatternGenerator<Instruction::KECCAK256>{};
static auto constexpr ADDRESS = PatternGenerator<Instruction::ADDRESS>{};
static auto constexpr BALANCE = PatternGenerator<Instruction::BALANCE>{};
static auto constexpr ORIGIN = PatternGenerator<Instruction::ORIGIN>{};
static auto constexpr CALLER = PatternGenerator<Instruction::CALLER>{};
static auto constexpr CALLVALUE = PatternGenerator<Instruction::CALLVALUE>{};
static auto constexpr CALLDATALOAD = PatternGenerator<Instruction::CALLDATALOAD>{};
static auto constexpr CALLDATASIZE = PatternGenerator<Instruction::CALLDATASIZE>{};
static auto constexpr CALLDATACOPY = PatternGenerator<Instruction::CALLDATACOPY>{};
static auto constexpr CODESIZE = PatternGenerator<Instruction::CODESIZE>{};
static auto constexpr CODECOPY = PatternGenerator<Instruction::CODECOPY>{};
static auto constexpr GASPRICE = PatternGenerator<Instruction::GASPRICE>{};
static auto constexpr EXTCODESIZE = PatternGenerator<Instruction::EXTCODESIZE>{};
static auto constexpr EXTCODECOPY = PatternGenerator<Instruction::EXTCODECOPY>{};
static auto constexpr RETURNDATASIZE = PatternGenerator<Instruction::RETURNDATASIZE>{};
static auto constexpr RETURNDATACOPY = PatternGenerator<Instruction::RETURNDATACOPY>{};
static auto constexpr EXTCODEHASH = PatternGenerator<Instruction::EXTCODEHASH>{};
static auto constexpr BLOCKHASH = PatternGenerator<Instruction::BLOCKHASH>{};
static auto constexpr COINBASE = PatternGenerator<Instruction::COINBASE>{};
static auto constexpr TIMESTAMP = PatternGenerator<Instruction::TIMESTAMP>{};
static auto constexpr NUMBER = PatternGenerator<Instruction::NUMBER>{};
static auto constexpr DIFFICULTY = PatternGenerator<Instruction::DIFFICULTY>{};
static auto constexpr GASLIMIT = PatternGenerator<Instruction::GASLIMIT>{};
static auto constexpr CHAINID = PatternGenerator<Instruction::CHAINID>{};
static auto constexpr SELFBALANCE = PatternGenerator<Instruction::SELFBALANCE>{};
static auto constexpr POP = PatternGenerator<Instruction::POP>{};
static auto constexpr MLOAD = PatternGenerator<Instruction::MLOAD>{};
static auto constexpr MSTORE = PatternGenerator<Instruction::MSTORE>{};
static auto constexpr MSTORE8 = PatternGenerator<Instruction::MSTORE8>{};
static auto constexpr SLOAD = PatternGenerator<Instruction::SLOAD>{};
static auto constexpr SSTORE = PatternGenerator<Instruction::SSTORE>{};
static auto constexpr PC = PatternGenerator<Instruction::PC>{};
static auto constexpr MSIZE = PatternGenerator<Instruction::MSIZE>{};
static auto constexpr GAS = PatternGenerator<Instruction::GAS>{};
static auto constexpr LOG0 = PatternGenerator<Instruction::LOG0>{};
static auto constexpr LOG1 = PatternGenerator<Instruction::LOG1>{};
static auto constexpr LOG2 = PatternGenerator<Instruction::LOG2>{};
static auto constexpr LOG3 = PatternGenerator<Instruction::LOG3>{};
static auto constexpr LOG4 = PatternGenerator<Instruction::LOG4>{};
static auto constexpr CREATE = PatternGenerator<Instruction::CREATE>{};
static auto constexpr CALL = PatternGenerator<Instruction::CALL>{};
static auto constexpr CALLCODE = PatternGenerator<Instruction::CALLCODE>{};
static auto constexpr STATICCALL = PatternGenerator<Instruction::STATICCALL>{};
static auto constexpr RETURN = PatternGenerator<Instruction::RETURN>{};
static auto constexpr DELEGATECALL = PatternGenerator<Instruction::DELEGATECALL>{};
static auto constexpr CREATE2 = PatternGenerator<Instruction::CREATE2>{};
static auto constexpr REVERT = PatternGenerator<Instruction::REVERT>{};
static auto constexpr INVALID = PatternGenerator<Instruction::INVALID>{};
static auto constexpr SELFDESTRUCT = PatternGenerator<Instruction::SELFDESTRUCT>{};
};
}

View File

@ -99,7 +99,7 @@ Rules::Rules()
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
}
Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments):
Pattern::Pattern(Instruction _instruction, std::initializer_list<Pattern> _arguments):
m_type(Operation),
m_instruction(_instruction),
m_arguments(_arguments)

View File

@ -26,6 +26,8 @@
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SimplificationRule.h>
#include <libdevcore/CommonData.h>
#include <boost/noncopyable.hpp>
#include <functional>
@ -87,18 +89,20 @@ public:
using Expression = ExpressionClasses::Expression;
using Id = ExpressionClasses::Id;
using Builtins = dev::eth::EVMBuiltins;
using Builtins = dev::eth::EVMBuiltins<Pattern>;
static constexpr size_t WordSize = 256;
using Word = u256;
// Matches a specific constant value.
Pattern(unsigned _value): Pattern(u256(_value)) {}
Pattern(int _value): Pattern(u256(_value)) {}
Pattern(long unsigned _value): Pattern(u256(_value)) {}
// Matches a specific constant value.
Pattern(u256 const& _value): m_type(Push), m_requireDataMatch(true), m_data(std::make_shared<u256>(_value)) {}
// Matches a specific assembly item type or anything if not given.
Pattern(AssemblyItemType _type = UndefinedItem): m_type(_type) {}
// Matches a given instruction with given arguments
Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments = {});
Pattern(Instruction _instruction, std::initializer_list<Pattern> _arguments = {});
/// Sets this pattern to be part of the match group with the identifier @a _group.
/// Inside one rule, all patterns in the same match group have to match expressions from the
/// same expression equivalence class.

View File

@ -98,7 +98,7 @@ private:
EVMVersion(Version _version): m_version(_version) {}
Version m_version = Version::Petersburg;
Version m_version = Version::Istanbul;
};
}

View File

@ -798,7 +798,7 @@ Token Scanner::scanHexString()
literal.complete();
advance(); // consume quote
return Token::StringLiteral;
return Token::HexStringLiteral;
}
// Parse for regex [:digit:]+(_[:digit:]+)*

View File

@ -88,7 +88,7 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio
if (!comp.matches(_version))
return false;
if (comp.version.numbers[0] == 0)
if (comp.version.numbers[0] == 0 && comp.levelsPresent != 1)
comp.levelsPresent = 2;
else
comp.levelsPresent = 1;
@ -105,6 +105,7 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio
didCompare = true;
cmp = _version.numbers[i] - version.numbers[i];
}
if (cmp == 0 && !_version.prerelease.empty() && didCompare)
cmp = -1;

View File

@ -221,6 +221,7 @@ namespace langutil
K(FalseLiteral, "false", 0) \
T(Number, nullptr, 0) \
T(StringLiteral, nullptr, 0) \
T(HexStringLiteral, nullptr, 0) \
T(CommentLiteral, nullptr, 0) \
\
/* Identifiers (not keywords or future reserved words). */ \

View File

@ -41,6 +41,8 @@ set(sources
ast/ASTJsonConverter.h
ast/ASTPrinter.cpp
ast/ASTPrinter.h
ast/ASTUtils.cpp
ast/ASTUtils.h
ast/ASTVisitor.h
ast/ExperimentalFeatures.h
ast/Types.cpp

View File

@ -35,7 +35,7 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
FunctionDefinition const& _function
)
{
auto functionFlow = unique_ptr<FunctionFlow>(new FunctionFlow());
auto functionFlow = make_unique<FunctionFlow>();
functionFlow->entry = _nodeContainer.newNode();
functionFlow->exit = _nodeContainer.newNode();
functionFlow->revert = _nodeContainer.newNode();

View File

@ -48,7 +48,7 @@ NameAndTypeResolver::NameAndTypeResolver(
m_globalContext(_globalContext)
{
if (!m_scopes[nullptr])
m_scopes[nullptr].reset(new DeclarationContainer());
m_scopes[nullptr] = make_shared<DeclarationContainer>();
for (Declaration const* declaration: _globalContext.declarations())
{
solAssert(m_scopes[nullptr]->registerDeclaration(*declaration), "Unable to register global declaration.");
@ -545,7 +545,7 @@ bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
{
if (!m_scopes[&_sourceUnit])
// By importing, it is possible that the container already exists.
m_scopes[&_sourceUnit].reset(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
m_scopes[&_sourceUnit] = make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get());
m_currentScope = &_sourceUnit;
return true;
}
@ -561,7 +561,7 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
SourceUnit const* importee = _import.annotation().sourceUnit;
solAssert(!!importee, "");
if (!m_scopes[importee])
m_scopes[importee].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
m_scopes[importee] = make_shared<DeclarationContainer>(nullptr, m_scopes[nullptr].get());
m_scopes[&_import] = m_scopes[importee];
registerDeclaration(_import, false);
return true;
@ -705,7 +705,7 @@ void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope)
{
map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
bool newlyAdded;
shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
shared_ptr<DeclarationContainer> container{make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get())};
tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container));
solAssert(newlyAdded, "Unable to add new scope.");
m_currentScope = &_subScope;

View File

@ -22,6 +22,7 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libyul/AsmAnalysis.h>
@ -641,6 +642,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
solAssert(var->type(), "Expected variable type!");
if (var->isConstant())
{
var = rootVariableDeclaration(*var);
if (!var->value())
{
m_errorReporter.typeError(_identifier.location, "Constant has no value.");
@ -651,7 +654,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
type(*var->value())->category() != Type::Category::RationalNumber
))
{
m_errorReporter.typeError(_identifier.location, "Only direct number constants are supported by inline assembly.");
m_errorReporter.typeError(_identifier.location, "Only direct number constants and references to such constants are supported by inline assembly.");
return size_t(-1);
}
else if (_context == yul::IdentifierContext::LValue)

View File

@ -21,7 +21,9 @@
#include <libyul/backends/evm/EVMDialect.h>
#include <liblangutil/ErrorReporter.h>
#include <libevmasm/SemanticInformation.h>
#include <functional>
#include <variant>
using namespace std;
using namespace dev;
@ -31,7 +33,7 @@ using namespace dev::solidity;
namespace
{
class AssemblyViewPureChecker: public boost::static_visitor<void>
class AssemblyViewPureChecker
{
public:
explicit AssemblyViewPureChecker(
@ -52,21 +54,21 @@ public:
{
checkInstruction(_instr.location, _instr.instruction);
for (auto const& arg: _instr.arguments)
boost::apply_visitor(*this, arg);
std::visit(*this, arg);
}
void operator()(yul::ExpressionStatement const& _expr)
{
boost::apply_visitor(*this, _expr.expression);
std::visit(*this, _expr.expression);
}
void operator()(yul::StackAssignment const&) {}
void operator()(yul::Assignment const& _assignment)
{
boost::apply_visitor(*this, *_assignment.value);
std::visit(*this, *_assignment.value);
}
void operator()(yul::VariableDeclaration const& _varDecl)
{
if (_varDecl.value)
boost::apply_visitor(*this, *_varDecl.value);
std::visit(*this, *_varDecl.value);
}
void operator()(yul::FunctionDefinition const& _funDef)
{
@ -80,16 +82,16 @@ public:
checkInstruction(_funCall.location, *fun->instruction);
for (auto const& arg: _funCall.arguments)
boost::apply_visitor(*this, arg);
std::visit(*this, arg);
}
void operator()(yul::If const& _if)
{
boost::apply_visitor(*this, *_if.condition);
std::visit(*this, *_if.condition);
(*this)(_if.body);
}
void operator()(yul::Switch const& _switch)
{
boost::apply_visitor(*this, *_switch.expression);
std::visit(*this, *_switch.expression);
for (auto const& _case: _switch.cases)
{
if (_case.value)
@ -100,7 +102,7 @@ public:
void operator()(yul::ForLoop const& _for)
{
(*this)(_for.pre);
boost::apply_visitor(*this, *_for.condition);
std::visit(*this, *_for.condition);
(*this)(_for.body);
(*this)(_for.post);
}
@ -113,7 +115,7 @@ public:
void operator()(yul::Block const& _block)
{
for (auto const& s: _block.statements)
boost::apply_visitor(*this, s);
std::visit(*this, s);
}
private:

View File

@ -55,11 +55,6 @@ ASTNode::ASTNode(SourceLocation const& _location):
{
}
ASTNode::~ASTNode()
{
delete m_annotation;
}
void ASTNode::resetID()
{
IDDispenser::reset();
@ -68,14 +63,14 @@ void ASTNode::resetID()
ASTAnnotation& ASTNode::annotation() const
{
if (!m_annotation)
m_annotation = new ASTAnnotation();
m_annotation = make_unique<ASTAnnotation>();
return *m_annotation;
}
SourceUnitAnnotation& SourceUnit::annotation() const
{
if (!m_annotation)
m_annotation = new SourceUnitAnnotation();
m_annotation = make_unique<SourceUnitAnnotation>();
return dynamic_cast<SourceUnitAnnotation&>(*m_annotation);
}
@ -99,7 +94,7 @@ set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<Sour
ImportAnnotation& ImportDirective::annotation() const
{
if (!m_annotation)
m_annotation = new ImportAnnotation();
m_annotation = make_unique<ImportAnnotation>();
return dynamic_cast<ImportAnnotation&>(*m_annotation);
}
@ -168,7 +163,7 @@ vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() cons
if (!m_interfaceEvents)
{
set<string> eventsSeen;
m_interfaceEvents.reset(new vector<EventDefinition const*>());
m_interfaceEvents = make_unique<vector<EventDefinition const*>>();
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (EventDefinition const* e: contract->events())
{
@ -193,7 +188,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
if (!m_interfaceFunctionList)
{
set<string> signaturesSeen;
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
m_interfaceFunctionList = make_unique<vector<pair<FixedHash<4>, FunctionTypePointer>>>();
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
{
vector<FunctionTypePointer> functions;
@ -225,7 +220,7 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const
{
if (!m_inheritableMembers)
{
m_inheritableMembers.reset(new vector<Declaration const*>());
m_inheritableMembers = make_unique<vector<Declaration const*>>();
auto addInheritableMember = [&](Declaration const* _decl)
{
solAssert(_decl, "addInheritableMember got a nullpointer.");
@ -259,14 +254,14 @@ TypePointer ContractDefinition::type() const
ContractDefinitionAnnotation& ContractDefinition::annotation() const
{
if (!m_annotation)
m_annotation = new ContractDefinitionAnnotation();
m_annotation = make_unique<ContractDefinitionAnnotation>();
return dynamic_cast<ContractDefinitionAnnotation&>(*m_annotation);
}
TypeNameAnnotation& TypeName::annotation() const
{
if (!m_annotation)
m_annotation = new TypeNameAnnotation();
m_annotation = make_unique<TypeNameAnnotation>();
return dynamic_cast<TypeNameAnnotation&>(*m_annotation);
}
@ -278,7 +273,7 @@ TypePointer StructDefinition::type() const
TypeDeclarationAnnotation& StructDefinition::annotation() const
{
if (!m_annotation)
m_annotation = new TypeDeclarationAnnotation();
m_annotation = make_unique<TypeDeclarationAnnotation>();
return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation);
}
@ -297,7 +292,7 @@ TypePointer EnumDefinition::type() const
TypeDeclarationAnnotation& EnumDefinition::annotation() const
{
if (!m_annotation)
m_annotation = new TypeDeclarationAnnotation();
m_annotation = make_unique<TypeDeclarationAnnotation>();
return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation);
}
@ -357,7 +352,7 @@ string FunctionDefinition::externalSignature() const
FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
{
if (!m_annotation)
m_annotation = new FunctionDefinitionAnnotation();
m_annotation = make_unique<FunctionDefinitionAnnotation>();
return dynamic_cast<FunctionDefinitionAnnotation&>(*m_annotation);
}
@ -369,7 +364,7 @@ TypePointer ModifierDefinition::type() const
ModifierDefinitionAnnotation& ModifierDefinition::annotation() const
{
if (!m_annotation)
m_annotation = new ModifierDefinitionAnnotation();
m_annotation = make_unique<ModifierDefinitionAnnotation>();
return dynamic_cast<ModifierDefinitionAnnotation&>(*m_annotation);
}
@ -389,14 +384,14 @@ FunctionTypePointer EventDefinition::functionType(bool _internal) const
EventDefinitionAnnotation& EventDefinition::annotation() const
{
if (!m_annotation)
m_annotation = new EventDefinitionAnnotation();
m_annotation = make_unique<EventDefinitionAnnotation>();
return dynamic_cast<EventDefinitionAnnotation&>(*m_annotation);
}
UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const
{
if (!m_annotation)
m_annotation = new UserDefinedTypeNameAnnotation();
m_annotation = make_unique<UserDefinedTypeNameAnnotation>();
return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
}
@ -604,63 +599,63 @@ FunctionTypePointer VariableDeclaration::functionType(bool _internal) const
VariableDeclarationAnnotation& VariableDeclaration::annotation() const
{
if (!m_annotation)
m_annotation = new VariableDeclarationAnnotation();
m_annotation = make_unique<VariableDeclarationAnnotation>();
return dynamic_cast<VariableDeclarationAnnotation&>(*m_annotation);
}
StatementAnnotation& Statement::annotation() const
{
if (!m_annotation)
m_annotation = new StatementAnnotation();
m_annotation = make_unique<StatementAnnotation>();
return dynamic_cast<StatementAnnotation&>(*m_annotation);
}
InlineAssemblyAnnotation& InlineAssembly::annotation() const
{
if (!m_annotation)
m_annotation = new InlineAssemblyAnnotation();
m_annotation = make_unique<InlineAssemblyAnnotation>();
return dynamic_cast<InlineAssemblyAnnotation&>(*m_annotation);
}
ReturnAnnotation& Return::annotation() const
{
if (!m_annotation)
m_annotation = new ReturnAnnotation();
m_annotation = make_unique<ReturnAnnotation>();
return dynamic_cast<ReturnAnnotation&>(*m_annotation);
}
ExpressionAnnotation& Expression::annotation() const
{
if (!m_annotation)
m_annotation = new ExpressionAnnotation();
m_annotation = make_unique<ExpressionAnnotation>();
return dynamic_cast<ExpressionAnnotation&>(*m_annotation);
}
MemberAccessAnnotation& MemberAccess::annotation() const
{
if (!m_annotation)
m_annotation = new MemberAccessAnnotation();
m_annotation = make_unique<MemberAccessAnnotation>();
return dynamic_cast<MemberAccessAnnotation&>(*m_annotation);
}
BinaryOperationAnnotation& BinaryOperation::annotation() const
{
if (!m_annotation)
m_annotation = new BinaryOperationAnnotation();
m_annotation = make_unique<BinaryOperationAnnotation>();
return dynamic_cast<BinaryOperationAnnotation&>(*m_annotation);
}
FunctionCallAnnotation& FunctionCall::annotation() const
{
if (!m_annotation)
m_annotation = new FunctionCallAnnotation();
m_annotation = make_unique<FunctionCallAnnotation>();
return dynamic_cast<FunctionCallAnnotation&>(*m_annotation);
}
IdentifierAnnotation& Identifier::annotation() const
{
if (!m_annotation)
m_annotation = new IdentifierAnnotation();
m_annotation = make_unique<IdentifierAnnotation>();
return dynamic_cast<IdentifierAnnotation&>(*m_annotation);
}

View File

@ -67,7 +67,7 @@ public:
using SourceLocation = langutil::SourceLocation;
explicit ASTNode(SourceLocation const& _location);
virtual ~ASTNode();
virtual ~ASTNode() {}
/// @returns an identifier of this AST node that is unique for a single compilation run.
size_t id() const { return m_id; }
@ -111,7 +111,7 @@ public:
protected:
size_t const m_id = 0;
/// Annotation - is specialised in derived classes, is created upon request (because of polymorphism).
mutable ASTAnnotation* m_annotation = nullptr;
mutable std::unique_ptr<ASTAnnotation> m_annotation;
private:
SourceLocation m_location;

View File

@ -801,6 +801,7 @@ string ASTJsonConverter::literalTokenKind(Token _token)
case dev::solidity::Token::Number:
return "number";
case dev::solidity::Token::StringLiteral:
case dev::solidity::Token::HexStringLiteral:
return "string";
case dev::solidity::Token::TrueLiteral:
case dev::solidity::Token::FalseLiteral:

View File

@ -0,0 +1,42 @@
/*
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/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
namespace dev
{
namespace solidity
{
VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl)
{
solAssert(_varDecl.isConstant(), "Constant variable expected");
VariableDeclaration const* rootDecl = &_varDecl;
Identifier const* identifier;
while ((identifier = dynamic_cast<Identifier const*>(rootDecl->value().get())))
{
auto referencedVarDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration);
solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration");
rootDecl = referencedVarDecl;
}
return rootDecl;
}
}
}

View File

@ -0,0 +1,30 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace dev
{
namespace solidity
{
/// Find the topmost referenced variable declaration when the given variable
/// declaration value is an identifier. Works only for constant variable declarations.
VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl);
}
}

View File

@ -331,6 +331,7 @@ TypePointer TypeProvider::forLiteral(Literal const& _literal)
case Token::Number:
return rationalNumber(_literal);
case Token::StringLiteral:
case Token::HexStringLiteral:
return stringLiteral(_literal.value());
default:
return nullptr;

View File

@ -329,7 +329,7 @@ MemberList const& Type::members(ContractDefinition const* _currentScope) const
MemberList::MemberMap members = nativeMembers(_currentScope);
if (_currentScope)
members += boundFunctions(*this, *_currentScope);
m_members[_currentScope] = unique_ptr<MemberList>(new MemberList(move(members)));
m_members[_currentScope] = make_unique<MemberList>(move(members));
}
return *m_members[_currentScope];
}
@ -1433,6 +1433,9 @@ Type const* ContractType::encodingType() const
BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (m_super)
return false;
if (*this == _convertTo)
return true;
if (_convertTo.category() == Category::Contract)
@ -1450,8 +1453,12 @@ BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
BoolResult ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (m_super)
return false;
if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo))
return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
return isImplicitlyConvertibleTo(_convertTo);
}
@ -2962,6 +2969,20 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
);
return members;
}
case Kind::DelegateCall:
{
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration);
solAssert(functionDefinition, "");
solAssert(functionDefinition->visibility() != Declaration::Visibility::Private, "");
if (functionDefinition->visibility() != Declaration::Visibility::Internal)
{
auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope());
solAssert(contract, "");
solAssert(contract->isLibrary(), "");
return {{"selector", TypeProvider::fixedBytes(4)}};
}
return {};
}
default:
return MemberList::MemberMap();
}

View File

@ -23,6 +23,7 @@
#include <libsolidity/codegen/ABIFunctions.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Whiskers.h>
#include <libdevcore/StringUtils.h>
@ -975,7 +976,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral(
for (size_t i = 0; i < words; ++i)
{
wordParams[i]["offset"] = to_string(i * 32);
wordParams[i]["wordValue"] = "0x" + h256(value.substr(32 * i, 32), h256::AlignLeft).hex();
wordParams[i]["wordValue"] = formatAsStringOrNumber(value.substr(32 * i, 32));
}
templ("word", wordParams);
return templ.render();
@ -990,7 +991,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral(
}
)");
templ("functionName", functionName);
templ("wordValue", "0x" + h256(value, h256::AlignLeft).hex());
templ("wordValue", formatAsStringOrNumber(value));
return templ.render();
}
});

View File

@ -438,7 +438,7 @@ void CompilerContext::appendInlineAssembly(
parserResult = std::move(obj.code);
#ifdef SOL_OUTPUT_ASM
cout << "After optimizer: " << endl;
cout << "After optimizer:" << endl;
cout << yul::AsmPrinter()(*parserResult) << endl;
#endif
}

View File

@ -711,6 +711,9 @@ void CompilerUtils::convertType(
Type::Category stackTypeCategory = _typeOnStack.category();
Type::Category targetTypeCategory = _targetType.category();
if (auto contrType = dynamic_cast<ContractType const*>(&_typeOnStack))
solAssert(!contrType->isSuper(), "Cannot convert magic variable \"super\"");
bool enumOverflowCheckPending = (targetTypeCategory == Type::Category::Enum || stackTypeCategory == Type::Category::Enum);
bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer;
if (chopSignBitsPending)

View File

@ -21,6 +21,7 @@
*/
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/ContractCompiler.h>
@ -631,6 +632,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
{
if (variable->isConstant())
{
variable = rootVariableDeclaration(*variable);
u256 value;
if (variable->value()->annotation().type->category() == Type::Category::RationalNumber)
{

View File

@ -345,7 +345,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
if (_tuple.components().size() == 1)
m_currentLValue = move(lvalues[0]);
else
m_currentLValue.reset(new TupleObject(m_context, move(lvalues)));
m_currentLValue = make_unique<TupleObject>(m_context, move(lvalues));
}
}
return false;

View File

@ -136,7 +136,7 @@ template <class _LValueType, class... _Arguments>
void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments const&... _arguments)
{
solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one.");
std::unique_ptr<_LValueType> lvalue(new _LValueType(m_context, _arguments...));
std::unique_ptr<_LValueType> lvalue = std::make_unique<_LValueType>(m_context, _arguments...);
if (_expression.annotation().lValueRequested)
m_currentLValue = move(lvalue);
else

View File

@ -23,6 +23,8 @@
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Whiskers.h>
#include <libdevcore/StringUtils.h>
@ -1756,7 +1758,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const
for (size_t i = 0; i < words; ++i)
{
wordParams[i]["offset"] = to_string(32 + i * 32);
wordParams[i]["wordValue"] = "0x" + h256(data.substr(32 * i, 32), h256::AlignLeft).hex();
wordParams[i]["wordValue"] = formatAsStringOrNumber(data.substr(32 * i, 32));
}
templ("word", wordParams);
return templ.render();

View File

@ -833,9 +833,9 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
yul::Statement modified = bodyCopier(_inlineAsm.operations());
solAssert(modified.type() == typeid(yul::Block), "");
solAssert(holds_alternative<yul::Block>(modified), "");
m_code << yul::AsmPrinter()(boost::get<yul::Block>(std::move(modified))) << "\n";
m_code << yul::AsmPrinter()(std::get<yul::Block>(std::move(modified))) << "\n";
return false;
}

View File

@ -27,19 +27,25 @@ using namespace dev;
using namespace langutil;
using namespace dev::solidity;
BMC::BMC(smt::EncodingContext& _context, ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses):
BMC::BMC(
smt::EncodingContext& _context,
ErrorReporter& _errorReporter,
map<h256, string> const& _smtlib2Responses,
smt::SMTSolverChoice _enabledSolvers
):
SMTEncoder(_context),
m_outerErrorReporter(_errorReporter),
m_interface(make_shared<smt::SMTPortfolio>(_smtlib2Responses))
m_interface(make_shared<smt::SMTPortfolio>(_smtlib2Responses, _enabledSolvers))
{
#if defined (HAVE_Z3) || defined (HAVE_CVC4)
if (!_smtlib2Responses.empty())
m_errorReporter.warning(
"SMT-LIB2 query responses were given in the auxiliary input, "
"but this Solidity binary uses an SMT solver (Z3/CVC4) directly."
"These responses will be ignored."
"Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses."
);
if (_enabledSolvers.some())
if (!_smtlib2Responses.empty())
m_errorReporter.warning(
"SMT-LIB2 query responses were given in the auxiliary input, "
"but this Solidity binary uses an SMT solver (Z3/CVC4) directly."
"These responses will be ignored."
"Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses."
);
#endif
}
@ -110,11 +116,6 @@ bool BMC::visit(ContractDefinition const& _contract)
{
initContract(_contract);
/// Check targets created by state variable initialization.
smt::Expression constraints = m_context.assertions();
checkVerificationTargets(constraints);
m_verificationTargets.clear();
SMTEncoder::visit(_contract);
return false;
@ -122,6 +123,17 @@ bool BMC::visit(ContractDefinition const& _contract)
void BMC::endVisit(ContractDefinition const& _contract)
{
if (auto constructor = _contract.constructor())
constructor->accept(*this);
else
{
inlineConstructorHierarchy(_contract);
/// Check targets created by state variable initialization.
smt::Expression constraints = m_context.assertions();
checkVerificationTargets(constraints);
m_verificationTargets.clear();
}
SMTEncoder::endVisit(_contract);
}
@ -132,10 +144,14 @@ bool BMC::visit(FunctionDefinition const& _function)
solAssert(m_currentContract, "");
auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
if (find(hierarchy.begin(), hierarchy.end(), contract) == hierarchy.end())
initializeStateVariables(*contract);
createStateVariables(*contract);
if (m_callStack.empty())
{
reset();
initFunction(_function);
resetStateVariables();
}
/// Already visits the children.
SMTEncoder::visit(_function);
@ -447,10 +463,6 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall)
// The reason why we need to pushCallStack here instead of visit(FunctionDefinition)
// is that there we don't have `_funCall`.
pushCallStack({funDef, &_funCall});
// If an internal function is called to initialize
// a state variable.
if (m_callStack.empty())
initFunction(*funDef);
funDef->accept(*this);
}

View File

@ -53,7 +53,12 @@ namespace solidity
class BMC: public SMTEncoder
{
public:
BMC(smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter, std::map<h256, std::string> const& _smtlib2Responses);
BMC(
smt::EncodingContext& _context,
langutil::ErrorReporter& _errorReporter,
std::map<h256, std::string> const& _smtlib2Responses,
smt::SMTSolverChoice _enabledSolvers
);
void analyze(SourceUnit const& _sources, std::set<Expression const*> _safeAssertions);

View File

@ -35,17 +35,23 @@ using namespace dev::solidity;
CHC::CHC(
smt::EncodingContext& _context,
ErrorReporter& _errorReporter,
map<h256, string> const& _smtlib2Responses
map<h256, string> const& _smtlib2Responses,
smt::SMTSolverChoice _enabledSolvers
):
SMTEncoder(_context),
#ifdef HAVE_Z3
m_interface(make_shared<smt::Z3CHCInterface>()),
m_interface(
_enabledSolvers.z3 ?
dynamic_pointer_cast<smt::CHCSolverInterface>(make_shared<smt::Z3CHCInterface>()) :
dynamic_pointer_cast<smt::CHCSolverInterface>(make_shared<smt::CHCSmtLib2Interface>(_smtlib2Responses))
),
#else
m_interface(make_shared<smt::CHCSmtLib2Interface>(_smtlib2Responses)),
#endif
m_outerErrorReporter(_errorReporter)
{
(void)_smtlib2Responses;
(void)_enabledSolvers;
}
void CHC::analyze(SourceUnit const& _source)
@ -65,6 +71,15 @@ void CHC::analyze(SourceUnit const& _source)
m_context.setAssertionAccumulation(false);
m_variableUsage.setFunctionInlining(false);
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
auto genesisSort = make_shared<smt::FunctionSort>(
vector<smt::SortPointer>(),
boolSort
);
m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis");
auto genesis = (*m_genesisPredicate)({});
addRule(genesis, genesis.name);
_source.accept(*this);
}
@ -94,10 +109,10 @@ bool CHC::visit(ContractDefinition const& _contract)
else
m_stateSorts.push_back(smt::smtSort(*var->type()));
clearIndices();
clearIndices(&_contract);
string interfaceName = "interface_" + _contract.name() + "_" + to_string(_contract.id());
m_interfacePredicate = createSymbolicBlock(interfaceSort(), interfaceName);
string suffix = _contract.name() + "_" + to_string(_contract.id());
m_interfacePredicate = createSymbolicBlock(interfaceSort(), "interface_" + suffix);
// TODO create static instances for Bool/Int sorts in SolverInterface.
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
@ -105,27 +120,11 @@ bool CHC::visit(ContractDefinition const& _contract)
vector<smt::SortPointer>(),
boolSort
);
m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error");
// If the contract has a constructor it is handled as a function.
// Otherwise we zero-initialize all state vars.
if (!_contract.constructor())
{
string constructorName = "constructor_" + _contract.name() + "_" + to_string(_contract.id());
m_constructorPredicate = createSymbolicBlock(constructorSort(), constructorName);
smt::Expression constructorPred = (*m_constructorPredicate)({});
addRule(constructorPred, constructorName);
for (auto const& var: m_stateVariables)
{
auto const& symbVar = m_context.variable(*var);
symbVar->increaseIndex();
m_interface->declareVariable(symbVar->currentName(), symbVar->sort());
m_context.setZeroValue(*symbVar);
}
connectBlocks(constructorPred, interface());
}
m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error_" + suffix);
m_constructorPredicate = createSymbolicBlock(constructorSort(), "implicit_constructor_" + to_string(_contract.id()));
auto stateExprs = currentStateVariables();
setCurrentBlock(*m_interfacePredicate, &stateExprs);
SMTEncoder::visit(_contract);
return false;
@ -136,6 +135,23 @@ void CHC::endVisit(ContractDefinition const& _contract)
if (!shouldVisit(_contract))
return;
for (auto const& var: m_stateVariables)
{
solAssert(m_context.knownVariable(*var), "");
m_context.setZeroValue(*var);
}
auto genesisPred = (*m_genesisPredicate)({});
auto implicitConstructor = (*m_constructorPredicate)(currentStateVariables());
connectBlocks(genesisPred, implicitConstructor);
m_currentBlock = implicitConstructor;
if (auto constructor = _contract.constructor())
constructor->accept(*this);
else
inlineConstructorHierarchy(_contract);
connectBlocks(m_currentBlock, interface());
for (unsigned i = 0; i < m_verificationTargets.size(); ++i)
{
auto const& target = m_verificationTargets.at(i);
@ -152,6 +168,16 @@ bool CHC::visit(FunctionDefinition const& _function)
if (!shouldVisit(_function))
return false;
// This is the case for base constructor inlining.
if (m_currentFunction)
{
solAssert(m_currentFunction->isConstructor(), "");
solAssert(_function.isConstructor(), "");
solAssert(_function.scope() != m_currentContract, "");
SMTEncoder::visit(_function);
return false;
}
solAssert(!m_currentFunction, "Inlining internal function calls not yet implemented");
m_currentFunction = &_function;
@ -163,20 +189,11 @@ bool CHC::visit(FunctionDefinition const& _function)
auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables());
auto bodyPred = predicate(*bodyBlock);
// Store the constraints related to variable initialization.
smt::Expression const& initAssertions = m_context.assertions();
m_context.pushSolver();
connectBlocks(interface(), functionPred);
connectBlocks(m_currentBlock, functionPred);
connectBlocks(functionPred, bodyPred);
m_context.popSolver();
setCurrentBlock(*bodyBlock);
// We need to re-add the constraints that were created for initialization of variables.
m_context.addAssertion(initAssertions);
SMTEncoder::visit(*m_currentFunction);
return false;
@ -187,10 +204,39 @@ void CHC::endVisit(FunctionDefinition const& _function)
if (!shouldVisit(_function))
return;
connectBlocks(m_currentBlock, interface());
// This is the case for base constructor inlining.
if (m_currentFunction != &_function)
{
solAssert(m_currentFunction && m_currentFunction->isConstructor(), "");
solAssert(_function.isConstructor(), "");
solAssert(_function.scope() != m_currentContract, "");
}
else
{
// We create an extra exit block for constructors that simply
// connects to the interface in case an explicit constructor
// exists in the hierarchy.
// It is not connected directly here, as normal functions are,
// because of the case where there are only implicit constructors.
// This is done in endVisit(ContractDefinition).
if (_function.isConstructor())
{
auto constructorExit = createSymbolicBlock(interfaceSort(), "constructor_exit_" + to_string(_function.id()));
connectBlocks(m_currentBlock, predicate(*constructorExit, currentStateVariables()));
clearIndices(m_currentContract, m_currentFunction);
auto stateExprs = currentStateVariables();
setCurrentBlock(*constructorExit, &stateExprs);
}
else
{
connectBlocks(m_currentBlock, interface());
clearIndices(m_currentContract, m_currentFunction);
auto stateExprs = currentStateVariables();
setCurrentBlock(*m_interfacePredicate, &stateExprs);
}
m_currentFunction = nullptr;
}
solAssert(&_function == m_currentFunction, "");
m_currentFunction = nullptr;
SMTEncoder::endVisit(_function);
}
@ -445,7 +491,6 @@ void CHC::reset()
m_verificationTargets.clear();
m_safeAssertions.clear();
m_unknownFunctionCallSeen = false;
m_blockCounter = 0;
m_breakDest = nullptr;
m_continueDest = nullptr;
}
@ -470,28 +515,31 @@ bool CHC::shouldVisit(FunctionDefinition const& _function) const
{
if (
_function.isPublic() &&
_function.isImplemented() &&
!_function.isConstructor()
_function.isImplemented()
)
return true;
return false;
}
void CHC::setCurrentBlock(smt::SymbolicFunctionVariable const& _block)
void CHC::setCurrentBlock(
smt::SymbolicFunctionVariable const& _block,
vector<smt::Expression> const* _arguments
)
{
m_context.popSolver();
clearIndices();
solAssert(m_currentContract, "");
clearIndices(m_currentContract, m_currentFunction);
m_context.pushSolver();
m_currentBlock = predicate(_block);
if (_arguments)
m_currentBlock = predicate(_block, *_arguments);
else
m_currentBlock = predicate(_block);
}
smt::SortPointer CHC::constructorSort()
{
solAssert(m_currentContract, "");
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
if (!m_currentContract->constructor())
return make_shared<smt::FunctionSort>(vector<smt::SortPointer>{}, boolSort);
return sort(*m_currentContract->constructor());
// TODO this will change once we support function calls.
return interfaceSort();
}
smt::SortPointer CHC::interfaceSort()
@ -556,19 +604,6 @@ unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPoin
return block;
}
smt::Expression CHC::constructor()
{
solAssert(m_currentContract, "");
if (!m_currentContract->constructor())
return (*m_constructorPredicate)({});
vector<smt::Expression> paramExprs;
for (auto const& var: m_currentContract->constructor()->parameters())
paramExprs.push_back(m_context.variable(*var)->currentValue());
return (*m_constructorPredicate)(paramExprs);
}
smt::Expression CHC::interface()
{
vector<smt::Expression> paramExprs;
@ -613,37 +648,31 @@ void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to
addRule(edge, _from.name + "_to_" + _to.name);
}
vector<smt::Expression> CHC::currentStateVariables()
{
solAssert(m_currentContract, "");
vector<smt::Expression> exprs;
for (auto const& var: m_stateVariables)
exprs.push_back(m_context.variable(*var)->currentValue());
return exprs;
}
vector<smt::Expression> CHC::currentFunctionVariables()
{
solAssert(m_currentFunction, "");
vector<smt::Expression> paramExprs;
for (auto const& var: m_stateVariables)
paramExprs.push_back(m_context.variable(*var)->currentValue());
for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters())
paramExprs.push_back(m_context.variable(*var)->currentValue());
return paramExprs;
if (m_currentFunction)
for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters())
paramExprs.push_back(m_context.variable(*var)->currentValue());
return currentStateVariables() + paramExprs;
}
vector<smt::Expression> CHC::currentBlockVariables()
{
solAssert(m_currentFunction, "");
vector<smt::Expression> paramExprs;
for (auto const& var: m_currentFunction->localVariables())
paramExprs.push_back(m_context.variable(*var)->currentValue());
return currentFunctionVariables() + paramExprs;
}
void CHC::clearIndices()
{
for (auto const& var: m_stateVariables)
m_context.variable(*var)->resetIndex();
if (m_currentFunction)
{
for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters())
m_context.variable(*var)->resetIndex();
for (auto const& var: m_currentFunction->localVariables())
m_context.variable(*var)->resetIndex();
}
paramExprs.push_back(m_context.variable(*var)->currentValue());
return currentFunctionVariables() + paramExprs;
}
string CHC::predicateName(ASTNode const* _node)
@ -674,7 +703,6 @@ smt::Expression CHC::predicate(
return _block(_arguments);
}
void CHC::addRule(smt::Expression const& _rule, string const& _ruleName)
{
m_interface->addRule(_rule, _ruleName);

View File

@ -47,7 +47,8 @@ public:
CHC(
smt::EncodingContext& _context,
langutil::ErrorReporter& _errorReporter,
std::map<h256, std::string> const& _smtlib2Responses
std::map<h256, std::string> const& _smtlib2Responses,
smt::SMTSolverChoice _enabledSolvers
);
void analyze(SourceUnit const& _sources);
@ -83,7 +84,7 @@ private:
void eraseKnowledge();
bool shouldVisit(ContractDefinition const& _contract) const;
bool shouldVisit(FunctionDefinition const& _function) const;
void setCurrentBlock(smt::SymbolicFunctionVariable const& _block);
void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector<smt::Expression> const* _arguments = nullptr);
//@}
/// Sort helpers.
@ -99,8 +100,6 @@ private:
/// @returns a new block of given _sort and _name.
std::unique_ptr<smt::SymbolicFunctionVariable> createSymbolicBlock(smt::SortPointer _sort, std::string const& _name);
/// Constructor predicate over current variables.
smt::Expression constructor();
/// Interface predicate over current variables.
smt::Expression interface();
/// Error predicate over current variables.
@ -116,17 +115,16 @@ private:
void connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints = smt::Expression(true));
/// @returns the current symbolic values of the current state variables.
std::vector<smt::Expression> currentStateVariables();
/// @returns the current symbolic values of the current function's
/// input and output parameters.
std::vector<smt::Expression> currentFunctionVariables();
/// @returns the samve as currentFunctionVariables plus
/// @returns the same as currentFunctionVariables plus
/// local variables.
std::vector<smt::Expression> currentBlockVariables();
/// Sets the SSA indices of the variables in scope to 0.
/// Used when starting a new block.
void clearIndices();
/// @returns the predicate name for a given node.
std::string predicateName(ASTNode const* _node);
/// @returns a predicate application over the current scoped variables.
@ -152,8 +150,11 @@ private:
/// Predicates.
//@{
/// Constructor predicate.
/// Default constructor sets state vars to 0.
/// Genesis predicate.
std::unique_ptr<smt::SymbolicFunctionVariable> m_genesisPredicate;
/// Implicit constructor predicate.
/// Explicit constructors are handled as functions.
std::unique_ptr<smt::SymbolicFunctionVariable> m_constructorPredicate;
/// Artificial Interface predicate.

View File

@ -35,7 +35,7 @@ void CVC4Interface::reset()
m_variables.clear();
m_solver.reset();
m_solver.setOption("produce-models", true);
m_solver.setTimeLimit(queryTimeout);
m_solver.setResourceLimit(resourceLimit);
}
void CVC4Interface::push()

View File

@ -63,6 +63,12 @@ private:
CVC4::ExprManager m_context;
CVC4::SmtEngine m_solver;
std::map<std::string, CVC4::Expr> m_variables;
// CVC4 "basic resources" limit.
// This is used to make the runs more deterministic and platform/machine independent.
// The tests start failing for CVC4 with less than 6000,
// so using double that.
static int const resourceLimit = 12000;
};
}

View File

@ -22,9 +22,13 @@ using namespace dev;
using namespace langutil;
using namespace dev::solidity;
ModelChecker::ModelChecker(ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses):
m_bmc(m_context, _errorReporter, _smtlib2Responses),
m_chc(m_context, _errorReporter, _smtlib2Responses),
ModelChecker::ModelChecker(
ErrorReporter& _errorReporter,
map<h256, string> const& _smtlib2Responses,
smt::SMTSolverChoice _enabledSolvers
):
m_bmc(m_context, _errorReporter, _smtlib2Responses, _enabledSolvers),
m_chc(m_context, _errorReporter, _smtlib2Responses, _enabledSolvers),
m_context()
{
}

View File

@ -25,6 +25,7 @@
#include <libsolidity/formal/BMC.h>
#include <libsolidity/formal/CHC.h>
#include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/SolverInterface.h>
#include <libsolidity/interface/ReadFile.h>
#include <liblangutil/ErrorReporter.h>
@ -43,7 +44,13 @@ namespace solidity
class ModelChecker
{
public:
ModelChecker(langutil::ErrorReporter& _errorReporter, std::map<h256, std::string> const& _smtlib2Responses);
/// @param _enabledSolvers represents a runtime choice of which SMT solvers
/// should be used, even if all are available. The default choice is to use all.
ModelChecker(
langutil::ErrorReporter& _errorReporter,
std::map<h256, std::string> const& _smtlib2Responses,
smt::SMTSolverChoice _enabledSolvers = smt::SMTSolverChoice::All()
);
void analyze(SourceUnit const& _sources);

View File

@ -22,6 +22,7 @@
#include <libsolidity/formal/SymbolicTypes.h>
#include <boost/range/adaptors.hpp>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace dev;
@ -39,11 +40,28 @@ bool SMTEncoder::visit(ContractDefinition const& _contract)
solAssert(m_currentContract, "");
for (auto const& node: _contract.subNodes())
if (!dynamic_pointer_cast<FunctionDefinition>(node))
if (
!dynamic_pointer_cast<FunctionDefinition>(node) &&
!dynamic_pointer_cast<VariableDeclaration>(node)
)
node->accept(*this);
vector<FunctionDefinition const*> resolvedFunctions = _contract.definedFunctions();
for (auto const& base: _contract.annotation().linearizedBaseContracts)
{
// Look for all the constructor invocations bottom up.
if (auto const& constructor = base->constructor())
for (auto const& invocation: constructor->modifiers())
{
auto refDecl = invocation->name()->annotation().referencedDeclaration;
if (auto const& baseContract = dynamic_cast<ContractDefinition const*>(refDecl))
{
solAssert(!m_baseConstructorCalls.count(baseContract), "");
m_baseConstructorCalls[baseContract] = invocation.get();
}
}
// Check for function overrides.
for (auto const& baseFunction: base->definedFunctions())
{
if (baseFunction->isConstructor())
@ -62,9 +80,18 @@ bool SMTEncoder::visit(ContractDefinition const& _contract)
if (!overridden)
resolvedFunctions.push_back(baseFunction);
}
}
// Functions are visited first since they might be used
// for state variable initialization which is part of
// the constructor.
// Constructors are visited as part of the constructor
// hierarchy inlining.
for (auto const& function: resolvedFunctions)
function->accept(*this);
if (!function->isConstructor())
function->accept(*this);
// Constructors need to be handled by the engines separately.
return false;
}
@ -73,13 +100,16 @@ void SMTEncoder::endVisit(ContractDefinition const& _contract)
{
m_context.resetAllVariables();
m_baseConstructorCalls.clear();
solAssert(m_currentContract == &_contract, "");
m_currentContract = nullptr;
}
void SMTEncoder::endVisit(VariableDeclaration const& _varDecl)
{
if (_varDecl.isLocalVariable() && _varDecl.type()->isValueType() &&_varDecl.value())
// State variables are handled by the constructor.
if (_varDecl.isLocalVariable() &&_varDecl.value())
assignment(_varDecl, *_varDecl.value());
}
@ -90,25 +120,22 @@ bool SMTEncoder::visit(ModifierDefinition const&)
bool SMTEncoder::visit(FunctionDefinition const& _function)
{
// Not visited by a function call
if (m_callStack.empty())
initFunction(_function);
m_modifierDepthStack.push_back(-1);
if (_function.isConstructor())
{
m_errorReporter.warning(
_function.location(),
"Assertion checker does not yet support constructors."
);
}
else
{
_function.parameterList().accept(*this);
if (_function.returnParameterList())
_function.returnParameterList()->accept(*this);
visitFunctionOrModifier();
}
inlineConstructorHierarchy(dynamic_cast<ContractDefinition const&>(*_function.scope()));
// Base constructors' parameters should be set by explicit calls,
// but the most derived one needs to be initialized.
if (_function.scope() == m_currentContract)
initializeLocalVariables(_function);
_function.parameterList().accept(*this);
if (_function.returnParameterList())
_function.returnParameterList()->accept(*this);
visitFunctionOrModifier();
return false;
}
@ -130,27 +157,87 @@ void SMTEncoder::visitFunctionOrModifier()
solAssert(m_modifierDepthStack.back() < int(function.modifiers().size()), "");
ASTPointer<ModifierInvocation> const& modifierInvocation = function.modifiers()[m_modifierDepthStack.back()];
solAssert(modifierInvocation, "");
modifierInvocation->accept(*this);
auto const& modifierDef = dynamic_cast<ModifierDefinition const&>(
*modifierInvocation->name()->annotation().referencedDeclaration
);
vector<smt::Expression> modifierArgsExpr;
if (auto const* arguments = modifierInvocation->arguments())
{
auto const& modifierParams = modifierDef.parameters();
solAssert(modifierParams.size() == arguments->size(), "");
for (unsigned i = 0; i < arguments->size(); ++i)
modifierArgsExpr.push_back(expr(*arguments->at(i), modifierParams.at(i)->type()));
}
initializeFunctionCallParameters(modifierDef, modifierArgsExpr);
pushCallStack({&modifierDef, modifierInvocation.get()});
modifierDef.body().accept(*this);
popCallStack();
auto refDecl = modifierInvocation->name()->annotation().referencedDeclaration;
if (dynamic_cast<ContractDefinition const*>(refDecl))
visitFunctionOrModifier();
else if (auto modifierDef = dynamic_cast<ModifierDefinition const*>(refDecl))
inlineModifierInvocation(modifierInvocation.get(), modifierDef);
else
solAssert(false, "");
}
--m_modifierDepthStack.back();
}
void SMTEncoder::inlineModifierInvocation(ModifierInvocation const* _invocation, CallableDeclaration const* _definition)
{
solAssert(_invocation, "");
_invocation->accept(*this);
vector<smt::Expression> args;
if (auto const* arguments = _invocation->arguments())
{
auto const& modifierParams = _definition->parameters();
solAssert(modifierParams.size() == arguments->size(), "");
for (unsigned i = 0; i < arguments->size(); ++i)
args.push_back(expr(*arguments->at(i), modifierParams.at(i)->type()));
}
initializeFunctionCallParameters(*_definition, args);
pushCallStack({_definition, _invocation});
if (auto modifier = dynamic_cast<ModifierDefinition const*>(_definition))
{
modifier->body().accept(*this);
popCallStack();
}
else if (auto function = dynamic_cast<FunctionDefinition const*>(_definition))
{
if (function->isImplemented())
function->accept(*this);
// Functions are popped from the callstack in endVisit(FunctionDefinition)
}
}
void SMTEncoder::inlineConstructorHierarchy(ContractDefinition const& _contract)
{
auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
auto it = find(begin(hierarchy), end(hierarchy), &_contract);
solAssert(it != end(hierarchy), "");
auto nextBase = it + 1;
// Initialize the base contracts here as long as their constructors are implicit,
// stop when the first explicit constructor is found.
while (nextBase != end(hierarchy))
{
if (auto baseConstructor = (*nextBase)->constructor())
{
createLocalVariables(*baseConstructor);
// If any subcontract explicitly called baseConstructor, use those arguments.
if (m_baseConstructorCalls.count(*nextBase))
inlineModifierInvocation(m_baseConstructorCalls.at(*nextBase), baseConstructor);
else if (baseConstructor->isImplemented())
{
// The first constructor found is handled like a function
// and its pushed into the callstack there.
// This if avoids duplication in the callstack.
if (!m_callStack.empty())
pushCallStack({baseConstructor, nullptr});
baseConstructor->accept(*this);
// popped by endVisit(FunctionDefinition)
}
break;
}
else
{
initializeStateVariables(**nextBase);
++nextBase;
}
}
initializeStateVariables(_contract);
}
bool SMTEncoder::visit(PlaceholderStatement const&)
{
solAssert(!m_callStack.empty(), "");
@ -208,17 +295,14 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
solAssert(symbTuple, "");
auto const& components = symbTuple->components();
auto const& declarations = _varDecl.declarations();
if (!components.empty())
{
solAssert(components.size() == declarations.size(), "");
for (unsigned i = 0; i < declarations.size(); ++i)
if (
components.at(i) &&
declarations.at(i) &&
m_context.knownVariable(*declarations.at(i))
)
assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type()));
}
solAssert(components.size() == declarations.size(), "");
for (unsigned i = 0; i < declarations.size(); ++i)
if (
components.at(i) &&
declarations.at(i) &&
m_context.knownVariable(*declarations.at(i))
)
assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type()));
}
}
else if (m_context.knownVariable(*_varDecl.declarations().front()))
@ -320,24 +404,23 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
{
auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_tuple));
solAssert(symbTuple, "");
if (symbTuple->components().empty())
auto const& symbComponents = symbTuple->components();
auto const& tupleComponents = _tuple.components();
solAssert(symbComponents.size() == _tuple.components().size(), "");
for (unsigned i = 0; i < symbComponents.size(); ++i)
{
vector<shared_ptr<smt::SymbolicVariable>> components;
for (auto const& component: _tuple.components())
if (component)
{
if (auto varDecl = identifierToVariable(*component))
components.push_back(m_context.variable(*varDecl));
else
{
solAssert(m_context.knownExpression(*component), "");
components.push_back(m_context.expression(*component));
}
}
auto sComponent = symbComponents.at(i);
auto tComponent = tupleComponents.at(i);
if (sComponent && tComponent)
{
if (auto varDecl = identifierToVariable(*tComponent))
m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl));
else
components.push_back(nullptr);
solAssert(components.size() == _tuple.components().size(), "");
symbTuple->setComponents(move(components));
{
solAssert(m_context.knownExpression(*tComponent), "");
m_context.addAssertion(sComponent->currentValue() == expr(*tComponent));
}
}
}
}
else
@ -541,25 +624,39 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
}
}
bool SMTEncoder::visit(ModifierInvocation const& _node)
{
if (auto const* args = _node.arguments())
for (auto const& arg: *args)
if (arg)
arg->accept(*this);
return false;
}
void SMTEncoder::initContract(ContractDefinition const& _contract)
{
solAssert(m_currentContract == nullptr, "");
m_currentContract = &_contract;
initializeStateVariables(_contract);
m_context.reset();
m_context.pushSolver();
createStateVariables(_contract);
clearIndices(m_currentContract, nullptr);
}
void SMTEncoder::initFunction(FunctionDefinition const& _function)
{
solAssert(m_callStack.empty(), "");
solAssert(m_currentContract, "");
m_context.reset();
m_context.pushSolver();
m_pathConditions.clear();
pushCallStack({&_function, nullptr});
m_uninterpretedTerms.clear();
resetStateVariables();
initializeLocalVariables(_function);
createStateVariables(*m_currentContract);
createLocalVariables(_function);
m_arrayAssignmentHappened = false;
clearIndices(m_currentContract, &_function);
}
void SMTEncoder::visitAssert(FunctionCall const& _funCall)
@ -609,12 +706,20 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
defineExpr(_identifier, m_context.thisAddress());
m_uninterpretedTerms.insert(&_identifier);
}
else if (smt::isSupportedType(_identifier.annotation().type->category()))
// TODO: handle MagicVariableDeclaration here
m_errorReporter.warning(
_identifier.location(),
"Assertion checker does not yet support the type of this variable."
);
else
createExpr(_identifier);
}
void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName)
{
auto const& typeType = dynamic_cast<TypeType const&>(*_typeName.annotation().type);
auto result = smt::newSymbolicVariable(
*TypeProvider::uint256(),
typeType.actualType()->toString(false),
m_context
);
solAssert(!result.first && result.second, "");
m_context.createExpression(_typeName, result.second);
}
void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
@ -764,8 +869,11 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
{
createExpr(_indexAccess);
if (_indexAccess.annotation().type->category() == Type::Category::TypeType)
return;
shared_ptr<smt::SymbolicVariable> array;
if (auto const& id = dynamic_cast<Identifier const*>(&_indexAccess.baseExpression()))
if (auto const* id = dynamic_cast<Identifier const*>(&_indexAccess.baseExpression()))
{
auto varDecl = identifierToVariable(*id);
solAssert(varDecl, "");
@ -780,7 +888,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
return;
}
}
else if (auto const& innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression()))
else if (auto const* innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression()))
{
solAssert(m_context.knownExpression(*innerAccess), "");
array = m_context.expression(*innerAccess);
@ -794,12 +902,6 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
return;
}
if (!_indexAccess.indexExpression())
{
solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, "");
return;
}
solAssert(array, "");
defineExpr(_indexAccess, smt::Expression::select(
array->currentValue(),
@ -1226,26 +1328,61 @@ void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _fu
}
}
void SMTEncoder::initializeStateVariables(ContractDefinition const& _contract)
void SMTEncoder::createStateVariables(ContractDefinition const& _contract)
{
for (auto var: _contract.stateVariablesIncludingInherited())
createVariable(*var);
}
void SMTEncoder::initializeLocalVariables(FunctionDefinition const& _function)
void SMTEncoder::initializeStateVariables(ContractDefinition const& _contract)
{
for (auto var: _contract.stateVariables())
{
solAssert(m_context.knownVariable(*var), "");
m_context.setZeroValue(*var);
}
for (auto var: _contract.stateVariables())
if (var->value())
{
var->value()->accept(*this);
assignment(*var, *var->value());
}
}
void SMTEncoder::createLocalVariables(FunctionDefinition const& _function)
{
for (auto const& variable: _function.localVariables())
if (createVariable(*variable))
m_context.setZeroValue(*variable);
createVariable(*variable);
for (auto const& param: _function.parameters())
if (createVariable(*param))
m_context.setUnknownValue(*param);
createVariable(*param);
if (_function.returnParameterList())
for (auto const& retParam: _function.returnParameters())
if (createVariable(*retParam))
m_context.setZeroValue(*retParam);
createVariable(*retParam);
}
void SMTEncoder::initializeLocalVariables(FunctionDefinition const& _function)
{
for (auto const& variable: _function.localVariables())
{
solAssert(m_context.knownVariable(*variable), "");
m_context.setZeroValue(*variable);
}
for (auto const& param: _function.parameters())
{
solAssert(m_context.knownVariable(*param), "");
m_context.setUnknownValue(*param);
}
if (_function.returnParameterList())
for (auto const& retParam: _function.returnParameters())
{
solAssert(m_context.knownVariable(*retParam), "");
m_context.setZeroValue(*retParam);
}
}
void SMTEncoder::resetStateVariables()
@ -1425,6 +1562,20 @@ void SMTEncoder::resetVariableIndices(VariableIndices const& _indices)
m_context.variable(*var.first)->index() = var.second;
}
void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function)
{
solAssert(_contract, "");
for (auto var: _contract->stateVariablesIncludingInherited())
m_context.variable(*var)->resetIndex();
if (_function)
{
for (auto const& var: _function->parameters() + _function->returnParameters())
m_context.variable(*var)->resetIndex();
for (auto const& var: _function->localVariables())
m_context.variable(*var)->resetIndex();
}
}
Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess)
{
Expression const* base = &_indexAccess.baseExpression();
@ -1503,15 +1654,18 @@ void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall)
{
auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_funCall));
solAssert(symbTuple, "");
if (symbTuple->components().empty())
auto const& symbComponents = symbTuple->components();
solAssert(symbComponents.size() == returnParams.size(), "");
for (unsigned i = 0; i < symbComponents.size(); ++i)
{
vector<shared_ptr<smt::SymbolicVariable>> components;
for (auto param: returnParams)
auto sComponent = symbComponents.at(i);
auto param = returnParams.at(i);
solAssert(param, "");
if (sComponent)
{
solAssert(m_context.knownVariable(*param), "");
components.push_back(m_context.variable(*param));
m_context.addAssertion(sComponent->currentValue() == currentValue(*param));
}
symbTuple->setComponents(move(components));
}
}
else if (returnParams.size() == 1)

View File

@ -27,6 +27,7 @@
#include <libsolidity/formal/SymbolicVariables.h>
#include <libsolidity/formal/VariableUsage.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/ReadFile.h>
#include <liblangutil/ErrorReporter.h>
@ -81,7 +82,9 @@ protected:
bool visit(BinaryOperation const& _node) override;
void endVisit(BinaryOperation const& _node) override;
void endVisit(FunctionCall const& _node) override;
bool visit(ModifierInvocation const& _node) override;
void endVisit(Identifier const& _node) override;
void endVisit(ElementaryTypeNameExpression const& _node) override;
void endVisit(Literal const& _node) override;
void endVisit(Return const& _node) override;
bool visit(MemberAccess const& _node) override;
@ -119,6 +122,12 @@ protected:
/// visit depth.
void visitFunctionOrModifier();
/// Inlines a modifier or base constructor call.
void inlineModifierInvocation(ModifierInvocation const* _invocation, CallableDeclaration const* _definition);
/// Inlines the constructor hierarchy into a single constructor.
void inlineConstructorHierarchy(ContractDefinition const& _contract);
/// Defines a new global variable or function.
void defineGlobalVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false);
@ -158,7 +167,9 @@ protected:
using CallStackEntry = std::pair<CallableDeclaration const*, ASTNode const*>;
void createStateVariables(ContractDefinition const& _contract);
void initializeStateVariables(ContractDefinition const& _contract);
void createLocalVariables(FunctionDefinition const& _function);
void initializeLocalVariables(FunctionDefinition const& _function);
void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smt::Expression> const& _callArgs);
void resetStateVariables();
@ -206,6 +217,9 @@ protected:
VariableIndices copyVariableIndices();
/// Resets the variable indices.
void resetVariableIndices(VariableIndices const& _indices);
/// Used when starting a new block.
void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr);
/// @returns variables that are touched in _node's subtree.
std::set<VariableDeclaration const*> touchedVariables(ASTNode const& _node);
@ -250,6 +264,8 @@ protected:
/// Needs to be a stack because of function calls.
std::vector<int> m_modifierDepthStack;
std::map<ContractDefinition const*, ModifierInvocation const*> m_baseConstructorCalls;
ContractDefinition const* m_currentContract = nullptr;
/// Stores the context of the encoding.

View File

@ -30,15 +30,21 @@ using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::smt;
SMTPortfolio::SMTPortfolio(map<h256, string> const& _smtlib2Responses)
SMTPortfolio::SMTPortfolio(
map<h256, string> const& _smtlib2Responses,
SMTSolverChoice _enabledSolvers
)
{
m_solvers.emplace_back(make_unique<smt::SMTLib2Interface>(_smtlib2Responses));
#ifdef HAVE_Z3
m_solvers.emplace_back(make_unique<smt::Z3Interface>());
if (_enabledSolvers.z3)
m_solvers.emplace_back(make_unique<smt::Z3Interface>());
#endif
#ifdef HAVE_CVC4
m_solvers.emplace_back(make_unique<smt::CVC4Interface>());
if (_enabledSolvers.cvc4)
m_solvers.emplace_back(make_unique<smt::CVC4Interface>());
#endif
(void)_enabledSolvers;
}
void SMTPortfolio::reset()

View File

@ -42,7 +42,10 @@ namespace smt
class SMTPortfolio: public SolverInterface, public boost::noncopyable
{
public:
SMTPortfolio(std::map<h256, std::string> const& _smtlib2Responses);
SMTPortfolio(
std::map<h256, std::string> const& _smtlib2Responses,
SMTSolverChoice _enabledSolvers
);
void reset() override;

View File

@ -36,6 +36,21 @@ namespace solidity
namespace smt
{
struct SMTSolverChoice
{
bool cvc4 = false;
bool z3 = false;
static constexpr SMTSolverChoice All() { return {true, true}; }
static constexpr SMTSolverChoice CVC4() { return {true, false}; }
static constexpr SMTSolverChoice Z3() { return {false, true}; }
static constexpr SMTSolverChoice None() { return {false, false}; }
bool none() { return !some(); }
bool some() { return cvc4 || z3; }
bool all() { return cvc4 && z3; }
};
enum class CheckResult
{
SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR
@ -359,10 +374,6 @@ public:
/// @returns how many SMT solvers this interface has.
virtual unsigned solvers() { return 1; }
protected:
// SMT query timeout in milliseconds.
static int const queryTimeout = 10000;
};
}

View File

@ -63,7 +63,7 @@ SortPointer smtSort(solidity::Type const& _type)
{
auto mapType = dynamic_cast<solidity::MappingType const*>(&_type);
solAssert(mapType, "");
return make_shared<ArraySort>(smtSort(*mapType->keyType()), smtSort(*mapType->valueType()));
return make_shared<ArraySort>(smtSortAbstractFunction(*mapType->keyType()), smtSortAbstractFunction(*mapType->valueType()));
}
else if (isStringLiteral(_type.category()))
{
@ -77,7 +77,7 @@ SortPointer smtSort(solidity::Type const& _type)
solAssert(isArray(_type.category()), "");
auto arrayType = dynamic_cast<solidity::ArrayType const*>(&_type);
solAssert(arrayType, "");
return make_shared<ArraySort>(make_shared<Sort>(Kind::Int), smtSort(*arrayType->baseType()));
return make_shared<ArraySort>(make_shared<Sort>(Kind::Int), smtSortAbstractFunction(*arrayType->baseType()));
}
}
default:
@ -94,6 +94,13 @@ vector<SortPointer> smtSort(vector<solidity::TypePointer> const& _types)
return sorts;
}
SortPointer smtSortAbstractFunction(solidity::Type const& _type)
{
if (isFunction(_type.category()))
return make_shared<Sort>(Kind::Int);
return smtSort(_type);
}
Kind smtKind(solidity::Type::Category _category)
{
if (isNumber(_category))

View File

@ -32,6 +32,9 @@ namespace smt
/// Returns the SMT sort that models the Solidity type _type.
SortPointer smtSort(solidity::Type const& _type);
std::vector<SortPointer> smtSort(std::vector<solidity::TypePointer> const& _types);
/// If _type has type Function, abstract it to Integer.
/// Otherwise return smtSort(_type).
SortPointer smtSortAbstractFunction(solidity::Type const& _type);
/// Returns the SMT kind that models the Solidity type type category _category.
Kind smtKind(solidity::Type::Category _category);

View File

@ -248,12 +248,16 @@ SymbolicTupleVariable::SymbolicTupleVariable(
SymbolicVariable(_type, _type, move(_uniqueName), _context)
{
solAssert(isTuple(m_type->category()), "");
}
void SymbolicTupleVariable::setComponents(vector<shared_ptr<SymbolicVariable>> _components)
{
solAssert(m_components.empty(), "");
auto const& tupleType = dynamic_cast<solidity::TupleType const*>(m_type);
solAssert(_components.size() == tupleType->components().size(), "");
m_components = move(_components);
auto const& tupleType = dynamic_cast<TupleType const&>(*m_type);
auto const& componentsTypes = tupleType.components();
for (unsigned i = 0; i < componentsTypes.size(); ++i)
if (componentsTypes.at(i))
{
string componentName = m_uniqueName + "_component_" + to_string(i);
auto result = smt::newSymbolicVariable(*componentsTypes.at(i), componentName, m_context);
solAssert(result.second, "");
m_components.emplace_back(move(result.second));
}
else
m_components.emplace_back(nullptr);
}

View File

@ -250,8 +250,6 @@ public:
return m_components;
}
void setComponents(std::vector<std::shared_ptr<SymbolicVariable>> _components);
private:
std::vector<std::shared_ptr<SymbolicVariable>> m_components;
};

View File

@ -29,10 +29,9 @@ Z3CHCInterface::Z3CHCInterface():
m_context(m_z3Interface->context()),
m_solver(*m_context)
{
// This needs to be set globally.
// These need to be set globally.
z3::set_param("rewriter.pull_cheap_ite", true);
// This needs to be set in the context.
m_context->set("timeout", queryTimeout);
z3::set_param("rlimit", Z3Interface::resourceLimit);
// Spacer options.
// These needs to be set in the solver.

View File

@ -54,9 +54,6 @@ private:
z3::context* m_context;
// Horn solver.
z3::fixedpoint m_solver;
// SMT query timeout in milliseconds.
static int const queryTimeout = 10000;
};
}

View File

@ -27,10 +27,9 @@ using namespace dev::solidity::smt;
Z3Interface::Z3Interface():
m_solver(m_context)
{
// This needs to be set globally.
// These need to be set globally.
z3::set_param("rewriter.pull_cheap_ite", true);
// This needs to be set in the context.
m_context.set("timeout", queryTimeout);
z3::set_param("rlimit", resourceLimit);
}
void Z3Interface::reset()

View File

@ -50,6 +50,12 @@ public:
z3::context* context() { return &m_context; }
// Z3 "basic resources" limit.
// This is used to make the runs more deterministic and platform/machine independent.
// The tests start failing for Z3 with less than 20000000,
// so using double that.
static int const resourceLimit = 40000000;
private:
void declareFunction(std::string const& _name, Sort const& _sort);

View File

@ -512,7 +512,7 @@ string const* CompilerStack::sourceMapping(string const& _contractName) const
if (!c.sourceMapping)
{
if (auto items = assemblyItems(_contractName))
c.sourceMapping.reset(new string(computeSourceMapping(*items)));
c.sourceMapping = make_unique<string>(computeSourceMapping(*items));
}
return c.sourceMapping.get();
}
@ -526,7 +526,7 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c
if (!c.runtimeSourceMapping)
{
if (auto items = runtimeAssemblyItems(_contractName))
c.runtimeSourceMapping.reset(new string(computeSourceMapping(*items)));
c.runtimeSourceMapping = make_unique<string>(computeSourceMapping(*items));
}
return c.runtimeSourceMapping.get();
}
@ -663,7 +663,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
// caches the result
if (!_contract.abi)
_contract.abi.reset(new Json::Value(ABI::generate(*_contract.contract)));
_contract.abi = make_unique<Json::Value>(ABI::generate(*_contract.contract));
return *_contract.abi;
}
@ -685,7 +685,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
// caches the result
if (!_contract.storageLayout)
_contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract)));
_contract.storageLayout = make_unique<Json::Value>(StorageLayout().generate(*_contract.contract));
return *_contract.storageLayout;
}
@ -707,7 +707,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
// caches the result
if (!_contract.userDocumentation)
_contract.userDocumentation.reset(new Json::Value(Natspec::userDocumentation(*_contract.contract)));
_contract.userDocumentation = make_unique<Json::Value>(Natspec::userDocumentation(*_contract.contract));
return *_contract.userDocumentation;
}
@ -729,7 +729,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
// caches the result
if (!_contract.devDocumentation)
_contract.devDocumentation.reset(new Json::Value(Natspec::devDocumentation(*_contract.contract)));
_contract.devDocumentation = make_unique<Json::Value>(Natspec::devDocumentation(*_contract.contract));
return *_contract.devDocumentation;
}
@ -762,7 +762,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const
// cache the result
if (!_contract.metadata)
_contract.metadata.reset(new string(createMetadata(_contract)));
_contract.metadata = make_unique<string>(createMetadata(_contract));
return *_contract.metadata;
}
@ -1060,25 +1060,17 @@ void CompilerStack::generateEWasm(ContractDefinition const& _contract)
return;
// Re-parse the Yul IR in EVM dialect
yul::AssemblyStack evmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
evmStack.parseAndAnalyze("", compiledContract.yulIROptimized);
yul::AssemblyStack stack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
stack.parseAndAnalyze("", compiledContract.yulIROptimized);
// Turn into eWasm dialect
yul::Object ewasmObject = yul::EVMToEWasmTranslator(
yul::EVMDialect::strictAssemblyForEVMObjects(m_evmVersion)
).run(*evmStack.parserResult());
stack.optimize();
stack.translate(yul::AssemblyStack::Language::EWasm);
stack.optimize();
// Re-inject into an assembly stack for the eWasm dialect
yul::AssemblyStack ewasmStack(m_evmVersion, yul::AssemblyStack::Language::EWasm, m_optimiserSettings);
// TODO this is a hack for now - provide as structured AST!
ewasmStack.parseAndAnalyze("", "{}");
*ewasmStack.parserResult() = move(ewasmObject);
ewasmStack.optimize();
//cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl;
//cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl;
// Turn into eWasm text representation.
auto result = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm);
auto result = stack.assemble(yul::AssemblyStack::Machine::eWasm);
compiledContract.eWasm = std::move(result.assembly);
compiledContract.eWasmObject = std::move(*result.bytecode);
}
@ -1410,7 +1402,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
if (eth::AssemblyItems const* items = assemblyItems(_contractName))
{
Gas executionGas = gasEstimator.functionalEstimation(*items);
Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false)};
Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false, m_evmVersion)};
Json::Value creation(Json::objectValue);
creation["codeDepositCost"] = gasToJson(codeDepositGas);

View File

@ -24,6 +24,7 @@
#include <libsolidity/ast/ASTJsonConverter.h>
#include <libyul/AssemblyStack.h>
#include <libyul/Exceptions.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/JSON.h>
@ -817,6 +818,16 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
"Unimplemented feature (" + _exception.lineInfo() + ")"
));
}
catch (yul::YulException const& _exception)
{
errors.append(formatErrorWithException(
_exception,
false,
"YulException",
"general",
"Yul exception"
));
}
catch (Exception const& _exception)
{
errors.append(formatError(

View File

@ -350,7 +350,7 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
if (m_scanner->currentToken() == Token::LParen)
{
m_scanner->next();
arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments()));
arguments = make_unique<vector<ASTPointer<Expression>>>(parseFunctionCallListArguments());
nodeFactory.markEndPosition();
expectToken(Token::RParen);
}
@ -811,7 +811,7 @@ ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
if (m_scanner->currentToken() == Token::LParen)
{
m_scanner->next();
arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments()));
arguments = make_unique<vector<ASTPointer<Expression>>>(parseFunctionCallListArguments());
nodeFactory.markEndPosition();
expectToken(Token::RParen);
}
@ -1614,9 +1614,22 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
}
break;
case Token::StringLiteral:
case Token::HexStringLiteral:
{
string literal = m_scanner->currentLiteral();
Token firstToken = m_scanner->currentToken();
while (m_scanner->peekNextToken() == firstToken)
{
m_scanner->next();
literal += m_scanner->currentLiteral();
}
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
m_scanner->next();
if (m_scanner->currentToken() == Token::Illegal)
fatalParserError(to_string(m_scanner->currentError()));
expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal));
break;
}
case Token::Identifier:
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());

View File

@ -60,12 +60,12 @@ bool AsmAnalyzer::analyze(Block const& _block)
success = (*this)(_block);
if (!success)
solAssert(m_errorReporter.hasErrors(), "No success but no error.");
yulAssert(m_errorReporter.hasErrors(), "No success but no error.");
}
catch (FatalError const&)
{
// This FatalError con occur if the errorReporter has too many errors.
solAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported.");
yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported.");
}
return success && !m_errorReporter.hasErrors();
}
@ -83,13 +83,13 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
{},
_object.dataNames()
).analyze(*_object.code);
solAssert(success && errorList.empty(), "Invalid assembly/yul code.");
yulAssert(success && errorList.empty(), "Invalid assembly/yul code.");
return analysisInfo;
}
bool AsmAnalyzer::operator()(Label const& _label)
{
solAssert(!_label.name.empty(), "");
yulAssert(!_label.name.empty(), "");
checkLooseFeature(
_label.location,
"The use of labels is disallowed. Please use \"if\", \"switch\", \"for\" or function calls instead."
@ -134,8 +134,8 @@ bool AsmAnalyzer::operator()(Literal const& _literal)
}
else if (_literal.kind == LiteralKind::Boolean)
{
solAssert(m_dialect.flavour == AsmFlavour::Yul, "");
solAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "");
yulAssert(m_dialect.flavour == AsmFlavour::Yul, "");
yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "");
}
m_info.stackHeightInfo[&_literal] = m_stackHeight;
return true;
@ -143,10 +143,10 @@ bool AsmAnalyzer::operator()(Literal const& _literal)
bool AsmAnalyzer::operator()(Identifier const& _identifier)
{
solAssert(!_identifier.name.empty(), "");
yulAssert(!_identifier.name.empty(), "");
size_t numErrorsBefore = m_errorReporter.errors().size();
bool success = true;
if (m_currentScope->lookup(_identifier.name, Scope::Visitor(
if (m_currentScope->lookup(_identifier.name, GenericVisitor{
[&](Scope::Variable const& _var)
{
if (!m_activeVariables.count(&_var))
@ -171,7 +171,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier)
);
success = false;
}
)))
}))
{
}
else
@ -197,14 +197,14 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier)
bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
{
solAssert(m_dialect.flavour != AsmFlavour::Yul, "");
yulAssert(m_dialect.flavour != AsmFlavour::Yul, "");
bool success = true;
for (auto const& arg: _instr.arguments | boost::adaptors::reversed)
if (!expectExpression(arg))
success = false;
// Parser already checks that the number of arguments is correct.
auto const& info = instructionInfo(_instr.instruction);
solAssert(info.args == int(_instr.arguments.size()), "");
yulAssert(info.args == int(_instr.arguments.size()), "");
m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instr] = m_stackHeight;
warnOnInstructions(_instr.instruction, _instr.location);
@ -214,7 +214,7 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
bool AsmAnalyzer::operator()(ExpressionStatement const& _statement)
{
int initialStackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, _statement.expression);
bool success = std::visit(*this, _statement.expression);
if (m_stackHeight != initialStackHeight && (m_dialect.flavour != AsmFlavour::Loose || m_errorTypeForLoose))
{
Error::Type errorType = m_dialect.flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError;
@ -245,11 +245,11 @@ bool AsmAnalyzer::operator()(StackAssignment const& _assignment)
bool AsmAnalyzer::operator()(Assignment const& _assignment)
{
solAssert(_assignment.value, "");
yulAssert(_assignment.value, "");
int const expectedItems = _assignment.variableNames.size();
solAssert(expectedItems >= 1, "");
yulAssert(expectedItems >= 1, "");
int const stackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, *_assignment.value);
bool success = std::visit(*this, *_assignment.value);
if ((m_stackHeight - stackHeight) != expectedItems)
{
m_errorReporter.declarationError(
@ -276,7 +276,7 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
if (_varDecl.value)
{
int const stackHeight = m_stackHeight;
success = boost::apply_visitor(*this, *_varDecl.value);
success = std::visit(*this, *_varDecl.value);
int numValues = m_stackHeight - stackHeight;
if (numValues != numVariables)
{
@ -298,7 +298,7 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
for (auto const& variable: _varDecl.variables)
{
expectValidType(variable.type.str(), variable.location);
m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)));
m_activeVariables.insert(&std::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)));
}
m_info.stackHeightInfo[&_varDecl] = m_stackHeight;
return success;
@ -306,14 +306,14 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef)
{
solAssert(!_funDef.name.empty(), "");
yulAssert(!_funDef.name.empty(), "");
Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get();
solAssert(virtualBlock, "");
yulAssert(virtualBlock, "");
Scope& varScope = scope(virtualBlock);
for (auto const& var: _funDef.parameters + _funDef.returnVariables)
{
expectValidType(var.type.str(), var.location);
m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name)));
m_activeVariables.insert(&std::get<Scope::Variable>(varScope.identifiers.at(var.name)));
}
int const stackHeight = m_stackHeight;
@ -328,7 +328,7 @@ bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef)
bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
{
solAssert(!_funCall.functionName.name.empty(), "");
yulAssert(!_funCall.functionName.name.empty(), "");
bool success = true;
size_t parameters = 0;
size_t returns = 0;
@ -341,7 +341,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
if (f->literalArguments)
needsLiteralArguments = true;
}
else if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor(
else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{
[&](Scope::Variable const&)
{
m_errorReporter.typeError(
@ -364,7 +364,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
parameters = _fun.arguments.size();
returns = _fun.returns.size();
}
)))
}))
{
m_errorReporter.declarationError(_funCall.functionName.location, "Function not found.");
success = false;
@ -388,15 +388,15 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
success = false;
else if (needsLiteralArguments)
{
if (arg.type() != typeid(Literal))
if (!holds_alternative<Literal>(arg))
m_errorReporter.typeError(
_funCall.functionName.location,
"Function expects direct literals as arguments."
);
else if (!m_dataNames.count(boost::get<Literal>(arg).value))
else if (!m_dataNames.count(std::get<Literal>(arg).value))
m_errorReporter.typeError(
_funCall.functionName.location,
"Unknown data object \"" + boost::get<Literal>(arg).value.str() + "\"."
"Unknown data object \"" + std::get<Literal>(arg).value.str() + "\"."
);
}
}
@ -426,7 +426,7 @@ bool AsmAnalyzer::operator()(If const& _if)
bool AsmAnalyzer::operator()(Switch const& _switch)
{
solAssert(_switch.expression, "");
yulAssert(_switch.expression, "");
bool success = true;
@ -498,7 +498,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch)
bool AsmAnalyzer::operator()(ForLoop const& _for)
{
solAssert(_for.condition, "");
yulAssert(_for.condition, "");
Scope* outerScope = m_currentScope;
@ -556,7 +556,7 @@ bool AsmAnalyzer::operator()(Block const& _block)
int const initialStackHeight = m_stackHeight;
for (auto const& s: _block.statements)
if (!boost::apply_visitor(*this, s))
if (!std::visit(*this, s))
success = false;
m_stackHeight -= scope(&_block).numberOfVariables();
@ -585,7 +585,7 @@ bool AsmAnalyzer::expectExpression(Expression const& _expr)
{
bool success = true;
int const initialHeight = m_stackHeight;
if (!boost::apply_visitor(*this, _expr))
if (!std::visit(*this, _expr))
success = false;
if (!expectDeposit(1, initialHeight, locationOf(_expr)))
success = false;
@ -609,19 +609,19 @@ bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation con
bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize)
{
solAssert(!_variable.name.empty(), "");
yulAssert(!_variable.name.empty(), "");
bool success = true;
size_t numErrorsBefore = m_errorReporter.errors().size();
size_t variableSize(-1);
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
{
// Check that it is a variable
if (var->type() != typeid(Scope::Variable))
if (!holds_alternative<Scope::Variable>(*var))
{
m_errorReporter.typeError(_variable.location, "Assignment requires variable.");
success = false;
}
else if (!m_activeVariables.count(&boost::get<Scope::Variable>(*var)))
else if (!m_activeVariables.count(&std::get<Scope::Variable>(*var)))
{
m_errorReporter.declarationError(
_variable.location,
@ -665,9 +665,9 @@ bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize
Scope& AsmAnalyzer::scope(Block const* _block)
{
solAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present.");
yulAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present.");
auto scopePtr = m_info.scopes.at(_block);
solAssert(scopePtr, "Scope requested but not present.");
yulAssert(scopePtr, "Scope requested but not present.");
return *scopePtr;
}
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
@ -686,10 +686,10 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio
{
// We assume that returndatacopy, returndatasize and staticcall are either all available
// or all not available.
solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), "");
yulAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), "");
// Similarly we assume bitwise shifting and create2 go together.
solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
solAssert(m_dialect.flavour != AsmFlavour::Yul, "");
yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
yulAssert(m_dialect.flavour != AsmFlavour::Yul, "");
auto errorForVM = [=](string const& vmKindMessage) {
m_errorReporter.typeError(
@ -768,7 +768,7 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio
void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description)
{
if (m_dialect.flavour != AsmFlavour::Loose)
solAssert(false, _description);
yulAssert(false, _description);
else if (m_errorTypeForLoose)
m_errorReporter.error(*m_errorTypeForLoose, _location, _description);
}

View File

@ -30,8 +30,6 @@
#include <libyul/backends/evm/AbstractAssembly.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <boost/variant.hpp>
#include <functional>
#include <list>
#include <memory>
@ -53,7 +51,7 @@ struct AsmAnalysisInfo;
* references and performs other checks.
* If all these checks pass, code generation should not throw errors.
*/
class AsmAnalyzer: public boost::static_visitor<bool>
class AsmAnalyzer
{
public:
explicit AsmAnalyzer(

View File

@ -22,8 +22,6 @@
#include <libyul/AsmDataForward.h>
#include <boost/variant.hpp>
#include <map>
#include <memory>
#include <vector>

View File

@ -28,7 +28,6 @@
#include <libevmasm/Instruction.h>
#include <liblangutil/SourceLocation.h>
#include <boost/variant.hpp>
#include <boost/noncopyable.hpp>
#include <map>
@ -83,7 +82,7 @@ struct Break { langutil::SourceLocation location; };
/// Continue statement (valid within for loop)
struct Continue { langutil::SourceLocation location; };
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
struct LocationExtractor
{
template <class T> langutil::SourceLocation operator()(T const& _node) const
{
@ -94,7 +93,7 @@ struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
/// Extracts the source location from an inline assembly node.
template <class T> inline langutil::SourceLocation locationOf(T const& _node)
{
return boost::apply_visitor(LocationExtractor(), _node);
return std::visit(LocationExtractor(), _node);
}
}

View File

@ -22,7 +22,7 @@
#pragma once
#include <boost/variant.hpp>
#include <variant>
namespace yul
{
@ -48,7 +48,7 @@ struct Block;
struct TypedName;
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
using Expression = std::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = std::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
}

View File

@ -21,6 +21,7 @@
*/
#include <libyul/AsmParser.h>
#include <libyul/Exceptions.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/ErrorReporter.h>
#include <libdevcore/Common.h>
@ -52,7 +53,7 @@ shared_ptr<Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _
}
catch (FatalError const&)
{
solAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported.");
yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported.");
}
return nullptr;
@ -184,7 +185,7 @@ Statement Parser::parseStatement()
while (true)
{
if (elementary.type() != typeid(Identifier))
if (!holds_alternative<Identifier>(elementary))
{
auto const token = currentToken() == Token::Comma ? "," : ":=";
@ -196,7 +197,7 @@ Statement Parser::parseStatement()
);
}
auto const& identifier = boost::get<Identifier>(elementary);
auto const& identifier = std::get<Identifier>(elementary);
if (m_dialect.builtin(identifier.name))
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
@ -212,22 +213,22 @@ Statement Parser::parseStatement()
}
Assignment assignment =
createWithLocation<Assignment>(boost::get<Identifier>(elementary).location);
createWithLocation<Assignment>(std::get<Identifier>(elementary).location);
assignment.variableNames = std::move(variableNames);
expectToken(Token::AssemblyAssign);
assignment.value.reset(new Expression(parseExpression()));
assignment.value = make_unique<Expression>(parseExpression());
assignment.location.end = locationOf(*assignment.value).end;
return Statement{std::move(assignment)};
}
case Token::Colon:
{
if (elementary.type() != typeid(Identifier))
if (!holds_alternative<Identifier>(elementary))
fatalParserError("Label name must precede \":\".");
Identifier const& identifier = boost::get<Identifier>(elementary);
Identifier const& identifier = std::get<Identifier>(elementary);
advance();
@ -245,20 +246,20 @@ Statement Parser::parseStatement()
break;
}
if (elementary.type() == typeid(Identifier))
if (holds_alternative<Identifier>(elementary))
{
Identifier& identifier = boost::get<Identifier>(elementary);
Identifier& identifier = std::get<Identifier>(elementary);
return ExpressionStatement{identifier.location, { move(identifier) }};
}
else if (elementary.type() == typeid(Literal))
else if (holds_alternative<Literal>(elementary))
{
Expression expr = boost::get<Literal>(elementary);
Expression expr = std::get<Literal>(elementary);
return ExpressionStatement{locationOf(expr), expr};
}
else
{
solAssert(elementary.type() == typeid(Instruction), "Invalid elementary operation.");
return boost::get<Instruction>(elementary);
yulAssert(holds_alternative<Instruction>(elementary), "Invalid elementary operation.");
return std::get<Instruction>(elementary);
}
}
@ -272,12 +273,12 @@ Case Parser::parseCase()
{
advance();
ElementaryOperation literal = parseElementaryOperation();
if (literal.type() != typeid(Literal))
if (!holds_alternative<Literal>(literal))
fatalParserError("Literal expected.");
_case.value = make_unique<Literal>(boost::get<Literal>(std::move(literal)));
_case.value = make_unique<Literal>(std::get<Literal>(std::move(literal)));
}
else
solAssert(false, "Case or default case expected.");
yulAssert(false, "Case or default case expected.");
_case.body = parseBlock();
_case.location.end = _case.body.location.end;
return _case;
@ -311,12 +312,12 @@ Expression Parser::parseExpression()
RecursionGuard recursionGuard(*this);
ElementaryOperation operation = parseElementaryOperation();
if (operation.type() == typeid(FunctionCall))
if (holds_alternative<FunctionCall>(operation))
return parseCall(std::move(operation));
else if (operation.type() == typeid(Instruction))
else if (holds_alternative<Instruction>(operation))
{
solAssert(m_dialect.flavour == AsmFlavour::Loose, "");
Instruction const& instr = boost::get<Instruction>(operation);
yulAssert(m_dialect.flavour == AsmFlavour::Loose, "");
Instruction const& instr = std::get<Instruction>(operation);
// Disallow instructions returning multiple values (and DUP/SWAP) as expression.
if (
instructionInfo(instr.instruction).ret != 1 ||
@ -345,19 +346,19 @@ Expression Parser::parseExpression()
}
if (currentToken() == Token::LParen)
return parseCall(std::move(operation));
else if (operation.type() == typeid(Instruction))
else if (holds_alternative<Instruction>(operation))
{
// Instructions not taking arguments are allowed as expressions.
solAssert(m_dialect.flavour == AsmFlavour::Loose, "");
Instruction& instr = boost::get<Instruction>(operation);
yulAssert(m_dialect.flavour == AsmFlavour::Loose, "");
Instruction& instr = std::get<Instruction>(operation);
return FunctionalInstruction{std::move(instr.location), instr.instruction, {}};
}
else if (operation.type() == typeid(Identifier))
return boost::get<Identifier>(operation);
else if (holds_alternative<Identifier>(operation))
return std::get<Identifier>(operation);
else
{
solAssert(operation.type() == typeid(Literal), "");
return boost::get<Literal>(operation);
yulAssert(holds_alternative<Literal>(operation), "");
return std::get<Literal>(operation);
}
}
@ -537,10 +538,10 @@ FunctionDefinition Parser::parseFunctionDefinition()
Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
{
RecursionGuard recursionGuard(*this);
if (_initialOp.type() == typeid(Instruction))
if (holds_alternative<Instruction>(_initialOp))
{
solAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul");
Instruction& instruction = boost::get<Instruction>(_initialOp);
yulAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul");
Instruction& instruction = std::get<Instruction>(_initialOp);
FunctionalInstruction ret;
ret.instruction = instruction.instruction;
ret.location = std::move(instruction.location);
@ -591,16 +592,16 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
expectToken(Token::RParen);
return ret;
}
else if (_initialOp.type() == typeid(Identifier) || _initialOp.type() == typeid(FunctionCall))
else if (holds_alternative<Identifier>(_initialOp) || holds_alternative<FunctionCall>(_initialOp))
{
FunctionCall ret;
if (_initialOp.type() == typeid(Identifier))
if (holds_alternative<Identifier>(_initialOp))
{
ret.functionName = std::move(boost::get<Identifier>(_initialOp));
ret.functionName = std::move(std::get<Identifier>(_initialOp));
ret.location = ret.functionName.location;
}
else
ret = std::move(boost::get<FunctionCall>(_initialOp));
ret = std::move(std::get<FunctionCall>(_initialOp));
expectToken(Token::LParen);
if (currentToken() != Token::RParen)
{

View File

@ -30,9 +30,9 @@
#include <liblangutil/ParserBase.h>
#include <memory>
#include <variant>
#include <vector>
namespace yul
{
@ -56,7 +56,7 @@ public:
static std::map<std::string, dev::eth::Instruction> const& instructions();
protected:
using ElementaryOperation = boost::variant<Instruction, Literal, Identifier, FunctionCall>;
using ElementaryOperation = std::variant<Instruction, Literal, Identifier, FunctionCall>;
/// Creates an inline assembly node with the given source location.
template <class T> T createWithLocation(langutil::SourceLocation const& _loc = {}) const

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