Merge pull request #7934 from ethereum/develop

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

View File

@ -4,25 +4,27 @@
The docker images are build locally on the developer machine: The docker images are build locally on the developer machine:
```!sh ```sh
cd .circleci/docker/ cd .circleci/docker/
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904 -f Dockerfile.ubuntu1904 . docker build -t ethereum/solidity-buildpack-deps:ubuntu1904-<revision> -f Dockerfile.ubuntu1904 .
docker push ethereum/solidity-buildpack-deps: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 ### Testing docker images locally
```!sh ```sh
cd solidity cd solidity
# Mounts your local solidity directory in docker container for testing # 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 cd /src/solidity
<commands_to_test_build_with_new_docker_image> <commands_to_test_build_with_new_docker_image>
``` ```

View File

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

View File

@ -62,7 +62,7 @@ RUN git clone --recursive -b boost-1.69.0 https://github.com/boostorg/boost.git
rm -rf /usr/src/boost rm -rf /usr/src/boost
# Z3 # 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; \ /usr/src/z3; \
cd /usr/src/z3; \ cd /usr/src/z3; \
python scripts/mk_make.py --prefix=/usr ; \ python scripts/mk_make.py --prefix=/usr ; \
@ -89,40 +89,21 @@ RUN set -ex; \
ninja install/strip; \ ninja install/strip; \
rm -rf /usr/src/libprotobuf-mutator 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 # EVMONE
RUN set -ex; \ RUN set -ex; \
cd /usr/src; \ 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; \ cd evmone; \
mkdir build; \ mkdir build; \
cd build; \ cd build; \
# isoltest links against the evmone shared library
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \ ninja; \
ninja install/strip; \ 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 rm -rf /usr/src/evmone
FROM base FROM base

View File

@ -74,34 +74,10 @@ RUN set -ex; \
ar r /usr/lib/libFuzzingEngine.a *.o; \ ar r /usr/lib/libFuzzingEngine.a *.o; \
rm -rf /var/lib/libfuzzer 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 # EVMONE
ARG EVMONE_HASH="f10d12c190f55a9d373e78b2dc0074d35d752c02cb536bb6fe754fb3719dd69e" ARG EVMONE_HASH="fa4f40daf7cf9ccbcca6c78345977e084ea2136a8eae661e4d19471be852b15b"
ARG EVMONE_MAJOR="0" ARG EVMONE_MAJOR="0"
ARG EVMONE_MINOR="1" ARG EVMONE_MINOR="3"
ARG EVMONE_MICRO="0" ARG EVMONE_MICRO="0"
RUN set -ex; \ RUN set -ex; \
EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \ EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \

View File

@ -74,37 +74,14 @@ RUN set -ex; \
ar r /usr/lib/libFuzzingEngine.a *.o; \ ar r /usr/lib/libFuzzingEngine.a *.o; \
rm -rf /var/lib/libfuzzer 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 # EVMONE
RUN set -ex; \ RUN set -ex; \
cd /usr/src; \ 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; \ cd evmone; \
mkdir build; \ mkdir build; \
cd build; \ cd build; \
# isoltest links against the evmone shared library
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \ ninja; \
ninja install/strip; \ ninja install/strip; \

View File

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

View File

@ -29,9 +29,9 @@ set -e
REPODIR="$(realpath $(dirname $0)/..)" REPODIR="$(realpath $(dirname $0)/..)"
for OPTIMIZE in 0 1; do 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 EVM=$EVM OPTIMIZE=$OPTIMIZE ${REPODIR}/.circleci/soltest.sh
done done
done done
EVM=constantinople OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh

View File

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

View File

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

View File

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

View File

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

View File

@ -86,7 +86,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
elseif(EMSCRIPTEN) elseif(EMSCRIPTEN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0")
# Leave only exported symbols as public and aggressively remove others # 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 # Optimisation level
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
# Re-enable exception catching (optimisations above -O1 disable it) # 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") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1")
# Export the Emscripten-generated auxiliary methods which are needed by solc-js. # 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. # 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. # Do not build as a WebAssembly target - we need an asm.js output.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0") 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()
endif() endif()

View File

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

View File

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

View File

@ -208,6 +208,48 @@ Restrictions for libraries in comparison to contracts:
(These might be lifted at a later point.) (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:
Call Protection For Libraries Call Protection For Libraries

View File

@ -86,7 +86,7 @@ but for quicker feedback, you might want to run specific tests.
Solidity includes different types of tests, most of them bundled into the 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``. `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``. 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, 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 ``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 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. 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: 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``. 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: 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 ``*``. where ``TestName`` can be a wildcard ``*``.
For example, here is an example test you might run; For example, here is an example test you might run;

View File

@ -204,7 +204,7 @@ The evaluation order of expressions is not specified (more formally, the order
in which the children of one node in the expression tree are evaluated is not 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 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 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 .. index:: ! assignment

View File

@ -4,6 +4,25 @@
Safe Remote Purchase 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; pragma solidity >=0.4.22 <0.7.0;
@ -12,7 +31,7 @@ Safe Remote Purchase
uint public value; uint public value;
address payable public seller; address payable public seller;
address payable public buyer; 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` // The state variable has a default value of the first member, `State.created`
State public state; State public state;
@ -57,6 +76,7 @@ Safe Remote Purchase
event Aborted(); event Aborted();
event PurchaseConfirmed(); event PurchaseConfirmed();
event ItemReceived(); event ItemReceived();
event SellerRefunded();
/// Abort the purchase and reclaim the ether. /// Abort the purchase and reclaim the ether.
/// Can only be called by the seller before /// Can only be called by the seller before
@ -68,6 +88,10 @@ Safe Remote Purchase
{ {
emit Aborted(); emit Aborted();
state = State.Inactive; 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); seller.transfer(address(this).balance);
} }
@ -97,12 +121,24 @@ Safe Remote Purchase
// It is important to change the state first because // It is important to change the state first because
// otherwise, the contracts called using `send` below // otherwise, the contracts called using `send` below
// can call in again here. // can call in again here.
state = State.Inactive; state = State.Release;
// NOTE: This actually allows both the buyer and the seller to
// block the refund - the withdraw pattern should be used.
buyer.transfer(value); buyer.transfer(value);
seller.transfer(address(this).balance); }
/// This function refunds the seller, i.e.
/// pays back the locked funds of the seller.
function refundSeller()
public
onlySeller
inState(State.Release)
{
emit SellerRefunded();
// It is important to change the state first because
// otherwise, the contracts called using `send` below
// can call in again here.
state = State.Inactive;
seller.transfer(3 * value);
} }
} }

View File

@ -66,6 +66,7 @@ They have varying degrees of completeness and up-to-dateness. The English
version stands as a reference. version stands as a reference.
* `French <http://solidity-fr.readthedocs.io>`_ (in progress) * `French <http://solidity-fr.readthedocs.io>`_ (in progress)
* `Italian <https://github.com/damianoazzolini/solidity>`_ (in progress)
* `Japanese <https://solidity-jp.readthedocs.io>`_ * `Japanese <https://solidity-jp.readthedocs.io>`_
* `Korean <http://solidity-kr.readthedocs.io>`_ (in progress) * `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) * `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated)

View File

@ -459,7 +459,7 @@ a non-rational number).
String Literals and Types String Literals 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. 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
-------------------- --------------------
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. Hexadecimal literals behave like :ref:`string literals <string_literals>` and have the same convertibility restrictions.

View File

@ -262,7 +262,15 @@ Contract Related
the current contract, explicitly convertible to :ref:`address` the current contract, explicitly convertible to :ref:`address`
``selfdestruct(address payable recipient)``: ``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. Furthermore, all functions of the current contract are callable directly including the current function.

View File

@ -57,7 +57,7 @@ Either add ``--libraries "file.sol:Math:0x12345678901234567890123456789012345678
If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. If ``solc`` is called with the option ``--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:: .. note::
The library placeholder used to be the fully qualified name of the library itself 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`` - ``constantinople``
- Opcodes ``create2`, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly. - Opcodes ``create2`, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly.
- Shifting operators use shifting opcodes and thus need less gas. - Shifting operators use shifting opcodes and thus need less gas.
- ``petersburg`` (**default**) - ``petersburg``
- The compiler behaves the same way as with constantinople. - The compiler behaves the same way as with constantinople.
- ``istanbul`` - ``istanbul`` (**default**)
- Opcodes ``chainid`` and ``selfbalance`` are available in assembly. - Opcodes ``chainid`` and ``selfbalance`` are available in assembly.
- ``berlin`` (**experimental**) - ``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. 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 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. The following subsections describe the format through an example.
Comments are of course not permitted and used here only for explanatory purposes. Comments are of course not permitted and used here only for explanatory purposes.

View File

@ -168,7 +168,7 @@ The ``continue`` and ``break`` statements can only be used inside loop bodies
and have to be in the same function as the loop (or both have to be at the and have to be in the same function as the loop (or both have to be at the
top level). top level).
The condition part of the for-loop has to evaluate to exactly one value. 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. 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 Identifiers are visible in
the block they are defined in (including all sub-nodes and sub-blocks). 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 (the first block) are visible in all other parts of the for-loop
(but not outside of the loop). (but not outside of the loop).
Identifiers declared in the other parts of the for loop respect the regular Identifiers declared in the other parts of the for loop respect the regular
syntactical scoping rules. 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 The parameters and return parameters of functions are visible in the
function body and their names cannot overlap. function body and their names cannot overlap.

View File

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

View File

@ -130,9 +130,12 @@ enum class HexCase
Mixed = 2, Mixed = 2,
}; };
/// Convert a series of bytes to the corresponding string of hex duplets. /// Convert a single byte to a string of hex characters (of length two),
/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte. /// optionally with uppercase hex letters.
/// @example toHex("A\x69") == "4169" 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); 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. /// 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); toBigEndian(_val, ret);
return 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 /// Convenience function for conversion of a u256 to hex
inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd)
@ -219,13 +218,18 @@ inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd)
return (prefix == HexPrefix::Add) ? "0x" + str : str; 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. /// Returns decimal representation for small numbers and hex for large numbers.
inline std::string formatNumber(bigint const& _value) inline std::string formatNumber(bigint const& _value)
{ {
if (_value < 0) if (_value < 0)
return "-" + formatNumber(-_value); return "-" + formatNumber(-_value);
if (_value > 0x1000000) if (_value > 0x1000000)
return toHex(toCompactBigEndian(_value), HexPrefix::Add); return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add);
else else
return _value.str(); return _value.str();
} }
@ -233,17 +237,11 @@ inline std::string formatNumber(bigint const& _value)
inline std::string formatNumber(u256 const& _value) inline std::string formatNumber(u256 const& _value)
{ {
if (_value > 0x1000000) if (_value > 0x1000000)
return toHex(toCompactBigEndian(_value), HexPrefix::Add); return toCompactHexWithPrefix(_value);
else else
return _value.str(); 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. // Algorithms for string and string-like collections.
@ -292,7 +290,6 @@ void iterateReplacing(std::vector<T>& _vector, F const& _f)
_vector = std::move(modifiedVector); _vector = std::move(modifiedVector);
} }
namespace detail namespace detail
{ {
template <typename T, typename F, std::size_t... I> 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 isValidHex(std::string const& _string);
bool isValidDecimal(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> template<typename Container, typename Compare>
bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare)
{ {

View File

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

View File

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

View File

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

View File

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

View File

@ -20,109 +20,41 @@
#pragma once #pragma once
#include <functional>
#include <boost/variant/static_visitor.hpp>
namespace dev namespace dev
{ {
/// Generic visitor used as follows: /**
/// boost::apply_visitor(GenericVisitor<Class1, Class2>( * Generic visitor used as follows:
/// [](Class1& _c) { _c.f(); }, * std::visit(GenericVisitor{
/// [](Class2& _c) { _c.g(); } * [](Class1& _c) { _c.f(); },
/// ), variant); * [](Class2& _c) { _c.g(); }
/// This one does not have a fallback and will fail at * }, variant);
/// compile-time if you do not specify all variants. * 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...> template <typename...> struct VisitorFallback;
struct GenericVisitor{};
template <class Visitable, class... Others> template <typename R>
struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...> struct VisitorFallback<R> { template<typename T> R operator()(T&&) const { return {}; } };
{
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))
{}
void operator()(Visitable& _v) const { m_visitor(_v); } template<>
struct VisitorFallback<> { template<typename T> void operator()(T&&) const {} };
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 <typename... Visitors> struct GenericVisitor: Visitors... { using Visitors::operator()...; };
template <typename... Visitors> GenericVisitor(Visitors...) -> GenericVisitor<Visitors...>;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -53,7 +53,12 @@ namespace GasCosts
} }
inline unsigned balanceGas(langutil::EVMVersion _evmVersion) 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; static unsigned const expGas = 10;
inline unsigned expByteGas(langutil::EVMVersion _evmVersion) inline unsigned expByteGas(langutil::EVMVersion _evmVersion)
@ -64,7 +69,12 @@ namespace GasCosts
static unsigned const keccak256WordGas = 6; static unsigned const keccak256WordGas = 6;
inline unsigned sloadGas(langutil::EVMVersion _evmVersion) 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 sstoreSetGas = 20000;
static unsigned const sstoreResetGas = 5000; static unsigned const sstoreResetGas = 5000;
@ -92,7 +102,10 @@ namespace GasCosts
static unsigned const txGas = 21000; static unsigned const txGas = 21000;
static unsigned const txCreateGas = 53000; static unsigned const txCreateGas = 53000;
static unsigned const txDataZeroGas = 4; 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; 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. /// @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 /// 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. /// 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: private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -99,7 +99,7 @@ Rules::Rules()
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); 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_type(Operation),
m_instruction(_instruction), m_instruction(_instruction),
m_arguments(_arguments) m_arguments(_arguments)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@
#include <libsolidity/analysis/TypeChecker.h> #include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libsolidity/ast/TypeProvider.h> #include <libsolidity/ast/TypeProvider.h>
#include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysis.h>
@ -641,6 +642,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
solAssert(var->type(), "Expected variable type!"); solAssert(var->type(), "Expected variable type!");
if (var->isConstant()) if (var->isConstant())
{ {
var = rootVariableDeclaration(*var);
if (!var->value()) if (!var->value())
{ {
m_errorReporter.typeError(_identifier.location, "Constant has no 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 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); return size_t(-1);
} }
else if (_context == yul::IdentifierContext::LValue) else if (_context == yul::IdentifierContext::LValue)

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,42 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
namespace dev
{
namespace solidity
{
VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl)
{
solAssert(_varDecl.isConstant(), "Constant variable expected");
VariableDeclaration const* rootDecl = &_varDecl;
Identifier const* identifier;
while ((identifier = dynamic_cast<Identifier const*>(rootDecl->value().get())))
{
auto referencedVarDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration);
solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration");
rootDecl = referencedVarDecl;
}
return rootDecl;
}
}
}

View File

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

View File

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

View File

@ -329,7 +329,7 @@ MemberList const& Type::members(ContractDefinition const* _currentScope) const
MemberList::MemberMap members = nativeMembers(_currentScope); MemberList::MemberMap members = nativeMembers(_currentScope);
if (_currentScope) if (_currentScope)
members += boundFunctions(*this, *_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]; return *m_members[_currentScope];
} }
@ -1433,6 +1433,9 @@ Type const* ContractType::encodingType() const
BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{ {
if (m_super)
return false;
if (*this == _convertTo) if (*this == _convertTo)
return true; return true;
if (_convertTo.category() == Category::Contract) if (_convertTo.category() == Category::Contract)
@ -1450,8 +1453,12 @@ BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
BoolResult ContractType::isExplicitlyConvertibleTo(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)) if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo))
return isPayable() || (addressType->stateMutability() < StateMutability::Payable); return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
return isImplicitlyConvertibleTo(_convertTo); return isImplicitlyConvertibleTo(_convertTo);
} }
@ -2962,6 +2969,20 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
); );
return members; 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: default:
return MemberList::MemberMap(); return MemberList::MemberMap();
} }

View File

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

View File

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

View File

@ -711,6 +711,9 @@ void CompilerUtils::convertType(
Type::Category stackTypeCategory = _typeOnStack.category(); Type::Category stackTypeCategory = _typeOnStack.category();
Type::Category targetTypeCategory = _targetType.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 enumOverflowCheckPending = (targetTypeCategory == Type::Category::Enum || stackTypeCategory == Type::Category::Enum);
bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer; bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer;
if (chopSignBitsPending) if (chopSignBitsPending)

View File

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

View File

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

View File

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

View File

@ -23,6 +23,8 @@
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h> #include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/codegen/CompilerUtils.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Whiskers.h> #include <libdevcore/Whiskers.h>
#include <libdevcore/StringUtils.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) for (size_t i = 0; i < words; ++i)
{ {
wordParams[i]["offset"] = to_string(32 + i * 32); 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); templ("word", wordParams);
return templ.render(); return templ.render();

View File

@ -833,9 +833,9 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
yul::Statement modified = bodyCopier(_inlineAsm.operations()); 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; return false;
} }

View File

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

View File

@ -53,7 +53,12 @@ namespace solidity
class BMC: public SMTEncoder class BMC: public SMTEncoder
{ {
public: 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); void analyze(SourceUnit const& _sources, std::set<Expression const*> _safeAssertions);

View File

@ -35,17 +35,23 @@ using namespace dev::solidity;
CHC::CHC( CHC::CHC(
smt::EncodingContext& _context, smt::EncodingContext& _context,
ErrorReporter& _errorReporter, ErrorReporter& _errorReporter,
map<h256, string> const& _smtlib2Responses map<h256, string> const& _smtlib2Responses,
smt::SMTSolverChoice _enabledSolvers
): ):
SMTEncoder(_context), SMTEncoder(_context),
#ifdef HAVE_Z3 #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 #else
m_interface(make_shared<smt::CHCSmtLib2Interface>(_smtlib2Responses)), m_interface(make_shared<smt::CHCSmtLib2Interface>(_smtlib2Responses)),
#endif #endif
m_outerErrorReporter(_errorReporter) m_outerErrorReporter(_errorReporter)
{ {
(void)_smtlib2Responses; (void)_smtlib2Responses;
(void)_enabledSolvers;
} }
void CHC::analyze(SourceUnit const& _source) void CHC::analyze(SourceUnit const& _source)
@ -65,6 +71,15 @@ void CHC::analyze(SourceUnit const& _source)
m_context.setAssertionAccumulation(false); m_context.setAssertionAccumulation(false);
m_variableUsage.setFunctionInlining(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); _source.accept(*this);
} }
@ -94,10 +109,10 @@ bool CHC::visit(ContractDefinition const& _contract)
else else
m_stateSorts.push_back(smt::smtSort(*var->type())); m_stateSorts.push_back(smt::smtSort(*var->type()));
clearIndices(); clearIndices(&_contract);
string interfaceName = "interface_" + _contract.name() + "_" + to_string(_contract.id()); string suffix = _contract.name() + "_" + to_string(_contract.id());
m_interfacePredicate = createSymbolicBlock(interfaceSort(), interfaceName); m_interfacePredicate = createSymbolicBlock(interfaceSort(), "interface_" + suffix);
// TODO create static instances for Bool/Int sorts in SolverInterface. // TODO create static instances for Bool/Int sorts in SolverInterface.
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool); auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
@ -105,27 +120,11 @@ bool CHC::visit(ContractDefinition const& _contract)
vector<smt::SortPointer>(), vector<smt::SortPointer>(),
boolSort boolSort
); );
m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error");
// If the contract has a constructor it is handled as a function. m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error_" + suffix);
// Otherwise we zero-initialize all state vars. m_constructorPredicate = createSymbolicBlock(constructorSort(), "implicit_constructor_" + to_string(_contract.id()));
if (!_contract.constructor()) auto stateExprs = currentStateVariables();
{ setCurrentBlock(*m_interfacePredicate, &stateExprs);
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());
}
SMTEncoder::visit(_contract); SMTEncoder::visit(_contract);
return false; return false;
@ -136,6 +135,23 @@ void CHC::endVisit(ContractDefinition const& _contract)
if (!shouldVisit(_contract)) if (!shouldVisit(_contract))
return; 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) for (unsigned i = 0; i < m_verificationTargets.size(); ++i)
{ {
auto const& target = m_verificationTargets.at(i); auto const& target = m_verificationTargets.at(i);
@ -152,6 +168,16 @@ bool CHC::visit(FunctionDefinition const& _function)
if (!shouldVisit(_function)) if (!shouldVisit(_function))
return false; 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"); solAssert(!m_currentFunction, "Inlining internal function calls not yet implemented");
m_currentFunction = &_function; m_currentFunction = &_function;
@ -163,20 +189,11 @@ bool CHC::visit(FunctionDefinition const& _function)
auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables()); auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables());
auto bodyPred = predicate(*bodyBlock); auto bodyPred = predicate(*bodyBlock);
// Store the constraints related to variable initialization. connectBlocks(m_currentBlock, functionPred);
smt::Expression const& initAssertions = m_context.assertions();
m_context.pushSolver();
connectBlocks(interface(), functionPred);
connectBlocks(functionPred, bodyPred); connectBlocks(functionPred, bodyPred);
m_context.popSolver();
setCurrentBlock(*bodyBlock); setCurrentBlock(*bodyBlock);
// We need to re-add the constraints that were created for initialization of variables.
m_context.addAssertion(initAssertions);
SMTEncoder::visit(*m_currentFunction); SMTEncoder::visit(*m_currentFunction);
return false; return false;
@ -187,10 +204,39 @@ void CHC::endVisit(FunctionDefinition const& _function)
if (!shouldVisit(_function)) if (!shouldVisit(_function))
return; 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); SMTEncoder::endVisit(_function);
} }
@ -445,7 +491,6 @@ void CHC::reset()
m_verificationTargets.clear(); m_verificationTargets.clear();
m_safeAssertions.clear(); m_safeAssertions.clear();
m_unknownFunctionCallSeen = false; m_unknownFunctionCallSeen = false;
m_blockCounter = 0;
m_breakDest = nullptr; m_breakDest = nullptr;
m_continueDest = nullptr; m_continueDest = nullptr;
} }
@ -470,28 +515,31 @@ bool CHC::shouldVisit(FunctionDefinition const& _function) const
{ {
if ( if (
_function.isPublic() && _function.isPublic() &&
_function.isImplemented() && _function.isImplemented()
!_function.isConstructor()
) )
return true; return true;
return false; return false;
} }
void CHC::setCurrentBlock(smt::SymbolicFunctionVariable const& _block) void CHC::setCurrentBlock(
smt::SymbolicFunctionVariable const& _block,
vector<smt::Expression> const* _arguments
)
{ {
m_context.popSolver(); m_context.popSolver();
clearIndices(); solAssert(m_currentContract, "");
clearIndices(m_currentContract, m_currentFunction);
m_context.pushSolver(); m_context.pushSolver();
m_currentBlock = predicate(_block); if (_arguments)
m_currentBlock = predicate(_block, *_arguments);
else
m_currentBlock = predicate(_block);
} }
smt::SortPointer CHC::constructorSort() smt::SortPointer CHC::constructorSort()
{ {
solAssert(m_currentContract, ""); // TODO this will change once we support function calls.
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool); return interfaceSort();
if (!m_currentContract->constructor())
return make_shared<smt::FunctionSort>(vector<smt::SortPointer>{}, boolSort);
return sort(*m_currentContract->constructor());
} }
smt::SortPointer CHC::interfaceSort() smt::SortPointer CHC::interfaceSort()
@ -556,19 +604,6 @@ unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPoin
return block; 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() smt::Expression CHC::interface()
{ {
vector<smt::Expression> paramExprs; 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); 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() vector<smt::Expression> CHC::currentFunctionVariables()
{ {
solAssert(m_currentFunction, "");
vector<smt::Expression> paramExprs; vector<smt::Expression> paramExprs;
for (auto const& var: m_stateVariables) if (m_currentFunction)
paramExprs.push_back(m_context.variable(*var)->currentValue()); for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters())
for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters()) paramExprs.push_back(m_context.variable(*var)->currentValue());
paramExprs.push_back(m_context.variable(*var)->currentValue()); return currentStateVariables() + paramExprs;
return paramExprs;
} }
vector<smt::Expression> CHC::currentBlockVariables() vector<smt::Expression> CHC::currentBlockVariables()
{ {
solAssert(m_currentFunction, "");
vector<smt::Expression> paramExprs; 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) 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()) 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) string CHC::predicateName(ASTNode const* _node)
@ -674,7 +703,6 @@ smt::Expression CHC::predicate(
return _block(_arguments); return _block(_arguments);
} }
void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) void CHC::addRule(smt::Expression const& _rule, string const& _ruleName)
{ {
m_interface->addRule(_rule, _ruleName); m_interface->addRule(_rule, _ruleName);

View File

@ -47,7 +47,8 @@ public:
CHC( CHC(
smt::EncodingContext& _context, smt::EncodingContext& _context,
langutil::ErrorReporter& _errorReporter, 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); void analyze(SourceUnit const& _sources);
@ -83,7 +84,7 @@ private:
void eraseKnowledge(); void eraseKnowledge();
bool shouldVisit(ContractDefinition const& _contract) const; bool shouldVisit(ContractDefinition const& _contract) const;
bool shouldVisit(FunctionDefinition const& _function) 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. /// Sort helpers.
@ -99,8 +100,6 @@ private:
/// @returns a new block of given _sort and _name. /// @returns a new block of given _sort and _name.
std::unique_ptr<smt::SymbolicFunctionVariable> createSymbolicBlock(smt::SortPointer _sort, std::string const& _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. /// Interface predicate over current variables.
smt::Expression interface(); smt::Expression interface();
/// Error predicate over current variables. /// 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)); 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 /// @returns the current symbolic values of the current function's
/// input and output parameters. /// input and output parameters.
std::vector<smt::Expression> currentFunctionVariables(); std::vector<smt::Expression> currentFunctionVariables();
/// @returns the samve as currentFunctionVariables plus /// @returns the same as currentFunctionVariables plus
/// local variables. /// local variables.
std::vector<smt::Expression> currentBlockVariables(); 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. /// @returns the predicate name for a given node.
std::string predicateName(ASTNode const* _node); std::string predicateName(ASTNode const* _node);
/// @returns a predicate application over the current scoped variables. /// @returns a predicate application over the current scoped variables.
@ -152,8 +150,11 @@ private:
/// Predicates. /// Predicates.
//@{ //@{
/// Constructor predicate. /// Genesis predicate.
/// Default constructor sets state vars to 0. std::unique_ptr<smt::SymbolicFunctionVariable> m_genesisPredicate;
/// Implicit constructor predicate.
/// Explicit constructors are handled as functions.
std::unique_ptr<smt::SymbolicFunctionVariable> m_constructorPredicate; std::unique_ptr<smt::SymbolicFunctionVariable> m_constructorPredicate;
/// Artificial Interface predicate. /// Artificial Interface predicate.

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@
#include <libsolidity/formal/BMC.h> #include <libsolidity/formal/BMC.h>
#include <libsolidity/formal/CHC.h> #include <libsolidity/formal/CHC.h>
#include <libsolidity/formal/EncodingContext.h> #include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/SolverInterface.h>
#include <libsolidity/interface/ReadFile.h> #include <libsolidity/interface/ReadFile.h>
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
@ -43,7 +44,13 @@ namespace solidity
class ModelChecker class ModelChecker
{ {
public: 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); void analyze(SourceUnit const& _sources);

View File

@ -22,6 +22,7 @@
#include <libsolidity/formal/SymbolicTypes.h> #include <libsolidity/formal/SymbolicTypes.h>
#include <boost/range/adaptors.hpp> #include <boost/range/adaptors.hpp>
#include <boost/range/adaptor/reversed.hpp>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -39,11 +40,28 @@ bool SMTEncoder::visit(ContractDefinition const& _contract)
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
for (auto const& node: _contract.subNodes()) 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); node->accept(*this);
vector<FunctionDefinition const*> resolvedFunctions = _contract.definedFunctions(); vector<FunctionDefinition const*> resolvedFunctions = _contract.definedFunctions();
for (auto const& base: _contract.annotation().linearizedBaseContracts) 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()) for (auto const& baseFunction: base->definedFunctions())
{ {
if (baseFunction->isConstructor()) if (baseFunction->isConstructor())
@ -62,9 +80,18 @@ bool SMTEncoder::visit(ContractDefinition const& _contract)
if (!overridden) if (!overridden)
resolvedFunctions.push_back(baseFunction); 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) 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; return false;
} }
@ -73,13 +100,16 @@ void SMTEncoder::endVisit(ContractDefinition const& _contract)
{ {
m_context.resetAllVariables(); m_context.resetAllVariables();
m_baseConstructorCalls.clear();
solAssert(m_currentContract == &_contract, ""); solAssert(m_currentContract == &_contract, "");
m_currentContract = nullptr; m_currentContract = nullptr;
} }
void SMTEncoder::endVisit(VariableDeclaration const& _varDecl) 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()); assignment(_varDecl, *_varDecl.value());
} }
@ -90,25 +120,22 @@ bool SMTEncoder::visit(ModifierDefinition const&)
bool SMTEncoder::visit(FunctionDefinition const& _function) bool SMTEncoder::visit(FunctionDefinition const& _function)
{ {
// Not visited by a function call
if (m_callStack.empty())
initFunction(_function);
m_modifierDepthStack.push_back(-1); m_modifierDepthStack.push_back(-1);
if (_function.isConstructor()) if (_function.isConstructor())
{ inlineConstructorHierarchy(dynamic_cast<ContractDefinition const&>(*_function.scope()));
m_errorReporter.warning(
_function.location(), // Base constructors' parameters should be set by explicit calls,
"Assertion checker does not yet support constructors." // but the most derived one needs to be initialized.
); if (_function.scope() == m_currentContract)
} initializeLocalVariables(_function);
else
{ _function.parameterList().accept(*this);
_function.parameterList().accept(*this); if (_function.returnParameterList())
if (_function.returnParameterList()) _function.returnParameterList()->accept(*this);
_function.returnParameterList()->accept(*this);
visitFunctionOrModifier(); visitFunctionOrModifier();
}
return false; return false;
} }
@ -130,27 +157,87 @@ void SMTEncoder::visitFunctionOrModifier()
solAssert(m_modifierDepthStack.back() < int(function.modifiers().size()), ""); solAssert(m_modifierDepthStack.back() < int(function.modifiers().size()), "");
ASTPointer<ModifierInvocation> const& modifierInvocation = function.modifiers()[m_modifierDepthStack.back()]; ASTPointer<ModifierInvocation> const& modifierInvocation = function.modifiers()[m_modifierDepthStack.back()];
solAssert(modifierInvocation, ""); solAssert(modifierInvocation, "");
modifierInvocation->accept(*this); auto refDecl = modifierInvocation->name()->annotation().referencedDeclaration;
auto const& modifierDef = dynamic_cast<ModifierDefinition const&>( if (dynamic_cast<ContractDefinition const*>(refDecl))
*modifierInvocation->name()->annotation().referencedDeclaration visitFunctionOrModifier();
); else if (auto modifierDef = dynamic_cast<ModifierDefinition const*>(refDecl))
vector<smt::Expression> modifierArgsExpr; inlineModifierInvocation(modifierInvocation.get(), modifierDef);
if (auto const* arguments = modifierInvocation->arguments()) else
{ solAssert(false, "");
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();
} }
--m_modifierDepthStack.back(); --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&) bool SMTEncoder::visit(PlaceholderStatement const&)
{ {
solAssert(!m_callStack.empty(), ""); solAssert(!m_callStack.empty(), "");
@ -208,17 +295,14 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
solAssert(symbTuple, ""); solAssert(symbTuple, "");
auto const& components = symbTuple->components(); auto const& components = symbTuple->components();
auto const& declarations = _varDecl.declarations(); auto const& declarations = _varDecl.declarations();
if (!components.empty()) solAssert(components.size() == declarations.size(), "");
{ for (unsigned i = 0; i < declarations.size(); ++i)
solAssert(components.size() == declarations.size(), ""); if (
for (unsigned i = 0; i < declarations.size(); ++i) components.at(i) &&
if ( declarations.at(i) &&
components.at(i) && m_context.knownVariable(*declarations.at(i))
declarations.at(i) && )
m_context.knownVariable(*declarations.at(i)) assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type()));
)
assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type()));
}
} }
} }
else if (m_context.knownVariable(*_varDecl.declarations().front())) 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)); auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_tuple));
solAssert(symbTuple, ""); 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; auto sComponent = symbComponents.at(i);
for (auto const& component: _tuple.components()) auto tComponent = tupleComponents.at(i);
if (component) if (sComponent && tComponent)
{ {
if (auto varDecl = identifierToVariable(*component)) if (auto varDecl = identifierToVariable(*tComponent))
components.push_back(m_context.variable(*varDecl)); m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl));
else
{
solAssert(m_context.knownExpression(*component), "");
components.push_back(m_context.expression(*component));
}
}
else else
components.push_back(nullptr); {
solAssert(components.size() == _tuple.components().size(), ""); solAssert(m_context.knownExpression(*tComponent), "");
symbTuple->setComponents(move(components)); m_context.addAssertion(sComponent->currentValue() == expr(*tComponent));
}
}
} }
} }
else 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) void SMTEncoder::initContract(ContractDefinition const& _contract)
{ {
solAssert(m_currentContract == nullptr, ""); solAssert(m_currentContract == nullptr, "");
m_currentContract = &_contract; m_currentContract = &_contract;
initializeStateVariables(_contract); m_context.reset();
m_context.pushSolver();
createStateVariables(_contract);
clearIndices(m_currentContract, nullptr);
} }
void SMTEncoder::initFunction(FunctionDefinition const& _function) void SMTEncoder::initFunction(FunctionDefinition const& _function)
{ {
solAssert(m_callStack.empty(), ""); solAssert(m_callStack.empty(), "");
solAssert(m_currentContract, "");
m_context.reset(); m_context.reset();
m_context.pushSolver(); m_context.pushSolver();
m_pathConditions.clear(); m_pathConditions.clear();
pushCallStack({&_function, nullptr}); pushCallStack({&_function, nullptr});
m_uninterpretedTerms.clear(); m_uninterpretedTerms.clear();
resetStateVariables(); createStateVariables(*m_currentContract);
initializeLocalVariables(_function); createLocalVariables(_function);
m_arrayAssignmentHappened = false; m_arrayAssignmentHappened = false;
clearIndices(m_currentContract, &_function);
} }
void SMTEncoder::visitAssert(FunctionCall const& _funCall) void SMTEncoder::visitAssert(FunctionCall const& _funCall)
@ -609,12 +706,20 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
defineExpr(_identifier, m_context.thisAddress()); defineExpr(_identifier, m_context.thisAddress());
m_uninterpretedTerms.insert(&_identifier); m_uninterpretedTerms.insert(&_identifier);
} }
else if (smt::isSupportedType(_identifier.annotation().type->category())) else
// TODO: handle MagicVariableDeclaration here createExpr(_identifier);
m_errorReporter.warning( }
_identifier.location(),
"Assertion checker does not yet support the type of this variable." 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) void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
@ -764,8 +869,11 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
{ {
createExpr(_indexAccess); createExpr(_indexAccess);
if (_indexAccess.annotation().type->category() == Type::Category::TypeType)
return;
shared_ptr<smt::SymbolicVariable> array; 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); auto varDecl = identifierToVariable(*id);
solAssert(varDecl, ""); solAssert(varDecl, "");
@ -780,7 +888,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
return; 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), ""); solAssert(m_context.knownExpression(*innerAccess), "");
array = m_context.expression(*innerAccess); array = m_context.expression(*innerAccess);
@ -794,12 +902,6 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
return; return;
} }
if (!_indexAccess.indexExpression())
{
solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, "");
return;
}
solAssert(array, ""); solAssert(array, "");
defineExpr(_indexAccess, smt::Expression::select( defineExpr(_indexAccess, smt::Expression::select(
array->currentValue(), 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()) for (auto var: _contract.stateVariablesIncludingInherited())
createVariable(*var); 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()) for (auto const& variable: _function.localVariables())
if (createVariable(*variable)) createVariable(*variable);
m_context.setZeroValue(*variable);
for (auto const& param: _function.parameters()) for (auto const& param: _function.parameters())
if (createVariable(*param)) createVariable(*param);
m_context.setUnknownValue(*param);
if (_function.returnParameterList()) if (_function.returnParameterList())
for (auto const& retParam: _function.returnParameters()) for (auto const& retParam: _function.returnParameters())
if (createVariable(*retParam)) createVariable(*retParam);
m_context.setZeroValue(*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() void SMTEncoder::resetStateVariables()
@ -1425,6 +1562,20 @@ void SMTEncoder::resetVariableIndices(VariableIndices const& _indices)
m_context.variable(*var.first)->index() = var.second; 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* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess)
{ {
Expression const* base = &_indexAccess.baseExpression(); 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)); auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_funCall));
solAssert(symbTuple, ""); 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; auto sComponent = symbComponents.at(i);
for (auto param: returnParams) auto param = returnParams.at(i);
solAssert(param, "");
if (sComponent)
{ {
solAssert(m_context.knownVariable(*param), ""); 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) else if (returnParams.size() == 1)

View File

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

View File

@ -30,15 +30,21 @@ using namespace dev;
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::solidity::smt; 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)); m_solvers.emplace_back(make_unique<smt::SMTLib2Interface>(_smtlib2Responses));
#ifdef HAVE_Z3 #ifdef HAVE_Z3
m_solvers.emplace_back(make_unique<smt::Z3Interface>()); if (_enabledSolvers.z3)
m_solvers.emplace_back(make_unique<smt::Z3Interface>());
#endif #endif
#ifdef HAVE_CVC4 #ifdef HAVE_CVC4
m_solvers.emplace_back(make_unique<smt::CVC4Interface>()); if (_enabledSolvers.cvc4)
m_solvers.emplace_back(make_unique<smt::CVC4Interface>());
#endif #endif
(void)_enabledSolvers;
} }
void SMTPortfolio::reset() void SMTPortfolio::reset()

View File

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

View File

@ -36,6 +36,21 @@ namespace solidity
namespace smt 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 enum class CheckResult
{ {
SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR
@ -359,10 +374,6 @@ public:
/// @returns how many SMT solvers this interface has. /// @returns how many SMT solvers this interface has.
virtual unsigned solvers() { return 1; } virtual unsigned solvers() { return 1; }
protected:
// SMT query timeout in milliseconds.
static int const queryTimeout = 10000;
}; };
} }

View File

@ -63,7 +63,7 @@ SortPointer smtSort(solidity::Type const& _type)
{ {
auto mapType = dynamic_cast<solidity::MappingType const*>(&_type); auto mapType = dynamic_cast<solidity::MappingType const*>(&_type);
solAssert(mapType, ""); 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())) else if (isStringLiteral(_type.category()))
{ {
@ -77,7 +77,7 @@ SortPointer smtSort(solidity::Type const& _type)
solAssert(isArray(_type.category()), ""); solAssert(isArray(_type.category()), "");
auto arrayType = dynamic_cast<solidity::ArrayType const*>(&_type); auto arrayType = dynamic_cast<solidity::ArrayType const*>(&_type);
solAssert(arrayType, ""); 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: default:
@ -94,6 +94,13 @@ vector<SortPointer> smtSort(vector<solidity::TypePointer> const& _types)
return sorts; 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) Kind smtKind(solidity::Type::Category _category)
{ {
if (isNumber(_category)) if (isNumber(_category))

View File

@ -32,6 +32,9 @@ namespace smt
/// Returns the SMT sort that models the Solidity type _type. /// Returns the SMT sort that models the Solidity type _type.
SortPointer smtSort(solidity::Type const& _type); SortPointer smtSort(solidity::Type const& _type);
std::vector<SortPointer> smtSort(std::vector<solidity::TypePointer> const& _types); 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. /// Returns the SMT kind that models the Solidity type type category _category.
Kind smtKind(solidity::Type::Category _category); Kind smtKind(solidity::Type::Category _category);

View File

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

View File

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

View File

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

View File

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

View File

@ -27,10 +27,9 @@ using namespace dev::solidity::smt;
Z3Interface::Z3Interface(): Z3Interface::Z3Interface():
m_solver(m_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); z3::set_param("rewriter.pull_cheap_ite", true);
// This needs to be set in the context. z3::set_param("rlimit", resourceLimit);
m_context.set("timeout", queryTimeout);
} }
void Z3Interface::reset() void Z3Interface::reset()

View File

@ -50,6 +50,12 @@ public:
z3::context* context() { return &m_context; } 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: private:
void declareFunction(std::string const& _name, Sort const& _sort); void declareFunction(std::string const& _name, Sort const& _sort);

View File

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

View File

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

View File

@ -350,7 +350,7 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
if (m_scanner->currentToken() == Token::LParen) if (m_scanner->currentToken() == Token::LParen)
{ {
m_scanner->next(); m_scanner->next();
arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments())); arguments = make_unique<vector<ASTPointer<Expression>>>(parseFunctionCallListArguments());
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RParen); expectToken(Token::RParen);
} }
@ -811,7 +811,7 @@ ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
if (m_scanner->currentToken() == Token::LParen) if (m_scanner->currentToken() == Token::LParen)
{ {
m_scanner->next(); m_scanner->next();
arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments())); arguments = make_unique<vector<ASTPointer<Expression>>>(parseFunctionCallListArguments());
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RParen); expectToken(Token::RParen);
} }
@ -1614,9 +1614,22 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
} }
break; break;
case Token::StringLiteral: 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(); 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; break;
}
case Token::Identifier: case Token::Identifier:
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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