mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7934 from ethereum/develop
Merge develop to release for 0.5.14
This commit is contained in:
commit
01f1aaa4c7
@ -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>
|
||||
```
|
||||
```
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"; \
|
||||
|
@ -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; \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
29
Changelog.md
29
Changelog.md
@ -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
|
||||
|
||||
|
||||
|
||||
|
10
README.md
10
README.md
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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}
|
||||
|
@ -758,6 +758,10 @@
|
||||
"bugs": [],
|
||||
"released": "2019-11-14"
|
||||
},
|
||||
"0.5.14": {
|
||||
"bugs": [],
|
||||
"released": "2019-12-09"
|
||||
},
|
||||
"0.5.2": {
|
||||
"bugs": [
|
||||
"SignedArrayStorageCopy",
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
10
docs/yul.rst
10
docs/yul.rst
@ -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.
|
||||
|
||||
|
@ -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 + "\"";
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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>;
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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...>;
|
||||
}
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -33,5 +33,8 @@ struct OptimizerException: virtual AssemblyException {};
|
||||
struct StackTooDeepException: virtual OptimizerException {};
|
||||
struct ItemNotAvailableException: virtual OptimizerException {};
|
||||
|
||||
DEV_SIMPLE_EXCEPTION(InvalidDeposit);
|
||||
DEV_SIMPLE_EXCEPTION(InvalidOpcode);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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 << " ";
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
});
|
||||
|
||||
|
@ -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>{};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -98,7 +98,7 @@ private:
|
||||
|
||||
EVMVersion(Version _version): m_version(_version) {}
|
||||
|
||||
Version m_version = Version::Petersburg;
|
||||
Version m_version = Version::Istanbul;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -798,7 +798,7 @@ Token Scanner::scanHexString()
|
||||
|
||||
literal.complete();
|
||||
advance(); // consume quote
|
||||
return Token::StringLiteral;
|
||||
return Token::HexStringLiteral;
|
||||
}
|
||||
|
||||
// Parse for regex [:digit:]+(_[:digit:]+)*
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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). */ \
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
42
libsolidity/ast/ASTUtils.cpp
Normal file
42
libsolidity/ast/ASTUtils.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
30
libsolidity/ast/ASTUtils.h
Normal file
30
libsolidity/ast/ASTUtils.h
Normal 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);
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -22,8 +22,6 @@
|
||||
|
||||
#include <libyul/AsmDataForward.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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>;
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user