mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #7934 from ethereum/develop
Merge develop to release for 0.5.14
This commit is contained in:
		
						commit
						01f1aaa4c7
					
				| @ -4,25 +4,27 @@ | ||||
| 
 | ||||
| The docker images are build locally on the developer machine: | ||||
| 
 | ||||
| ```!sh | ||||
| ```sh | ||||
| cd .circleci/docker/ | ||||
| 
 | ||||
| docker build -t ethereum/solidity-buildpack-deps:ubuntu1904 -f Dockerfile.ubuntu1904 . | ||||
| docker push ethereum/solidity-buildpack-deps:ubuntu1904 | ||||
| docker build -t ethereum/solidity-buildpack-deps:ubuntu1904-<revision> -f Dockerfile.ubuntu1904 . | ||||
| docker push ethereum/solidity-buildpack-deps:ubuntu1904-<revision> | ||||
| ``` | ||||
| 
 | ||||
| which you can find on Dockerhub after the push at: | ||||
| The current revision is stored in a [circle ci pipeline parameter](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `docker-image-rev`. Please update the value assigned to this parameter at the time of a docker image update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`<revision>` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ. | ||||
| 
 | ||||
|     https://hub.docker.com/r/ethereum/solidity-buildpack-deps | ||||
| Once the docker image has been built and pushed to Dockerhub, you can find it at: | ||||
| 
 | ||||
| where the image tag reflects the target OS to build Solidity and run its test on. | ||||
|     https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904-<revision> | ||||
| 
 | ||||
| where the image tag reflects the target OS and revision to build Solidity and run its tests on. | ||||
| 
 | ||||
| ### Testing docker images locally | ||||
| 
 | ||||
| ```!sh | ||||
| ```sh | ||||
| cd solidity | ||||
| # Mounts your local solidity directory in docker container for testing | ||||
| docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904 /bin/bash | ||||
| docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904-<revision> /bin/bash | ||||
| cd /src/solidity | ||||
| <commands_to_test_build_with_new_docker_image> | ||||
| ``` | ||||
| ``` | ||||
|  | ||||
| @ -5,7 +5,11 @@ | ||||
| #     - t: test | ||||
| #     - ubu: ubuntu | ||||
| #     - ems: Emscripten | ||||
| version: 2 | ||||
| version: 2.1 | ||||
| parameters: | ||||
|   docker-image-rev: | ||||
|     type: string | ||||
|     default: "3" | ||||
| 
 | ||||
| defaults: | ||||
| 
 | ||||
| @ -106,7 +110,7 @@ defaults: | ||||
| 
 | ||||
|   - test_ubuntu1904_clang: &test_ubuntu1904_clang | ||||
|       docker: | ||||
|         - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang | ||||
|         - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.docker-image-rev >> | ||||
|       steps: | ||||
|         - checkout | ||||
|         - attach_workspace: | ||||
| @ -117,7 +121,7 @@ defaults: | ||||
| 
 | ||||
|   - test_ubuntu1904: &test_ubuntu1904 | ||||
|       docker: | ||||
|         - image: ethereum/solidity-buildpack-deps:ubuntu1904 | ||||
|         - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> | ||||
|       steps: | ||||
|         - checkout | ||||
|         - attach_workspace: | ||||
| @ -287,10 +291,11 @@ jobs: | ||||
| 
 | ||||
|   b_ubu_clang: &build_ubuntu1904_clang | ||||
|     docker: | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.docker-image-rev >> | ||||
|     environment: | ||||
|       CC: clang | ||||
|       CXX: clang++ | ||||
|       CMAKE_OPTIONS: -DLLL=ON | ||||
|     steps: | ||||
|       - checkout | ||||
|       - run: *run_build | ||||
| @ -299,7 +304,9 @@ jobs: | ||||
| 
 | ||||
|   b_ubu: &build_ubuntu1904 | ||||
|     docker: | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1904 | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> | ||||
|     environment: | ||||
|       CMAKE_OPTIONS: -DLLL=ON | ||||
|     steps: | ||||
|       - checkout | ||||
|       - run: *run_build | ||||
| @ -310,12 +317,13 @@ jobs: | ||||
|     <<: *build_ubuntu1904 | ||||
|     environment: | ||||
|       FORCE_RELEASE: ON | ||||
|       CMAKE_OPTIONS: -DLLL=ON | ||||
| 
 | ||||
|   b_ubu18: &build_ubuntu1804 | ||||
|     docker: | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1804 | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1804-<< pipeline.parameters.docker-image-rev >> | ||||
|     environment: | ||||
|       CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 | ||||
|       CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 -DLLL=ON | ||||
|       CMAKE_BUILD_TYPE: RelWithDebugInfo | ||||
|     steps: | ||||
|       - checkout | ||||
| @ -360,7 +368,7 @@ jobs: | ||||
|     <<: *build_ubuntu1904 | ||||
|     environment: | ||||
|       CMAKE_BUILD_TYPE: Debug | ||||
|       CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF | ||||
|       CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF -DLLL=ON | ||||
|     steps: | ||||
|       - checkout | ||||
|       - run: *run_build | ||||
| @ -400,6 +408,7 @@ jobs: | ||||
|       - image: archlinux/base | ||||
|     environment: | ||||
|       TERM: xterm | ||||
|       CMAKE_OPTIONS: -DLLL=ON | ||||
|     steps: | ||||
|       - run: | ||||
|           name: Install build dependencies | ||||
| @ -416,6 +425,7 @@ jobs: | ||||
|     environment: | ||||
|       TERM: xterm | ||||
|       CMAKE_BUILD_TYPE: Debug | ||||
|       CMAKE_OPTIONS: -DLLL=ON | ||||
|     steps: | ||||
|       - checkout | ||||
|       - restore_cache: | ||||
| @ -472,7 +482,7 @@ jobs: | ||||
| 
 | ||||
|   b_ems: | ||||
|     docker: | ||||
|       - image: trzeci/emscripten:sdk-tag-1.38.22-64bit | ||||
|       - image: trzeci/emscripten:sdk-tag-1.39.3-64bit | ||||
|     environment: | ||||
|       TERM: xterm | ||||
|     steps: | ||||
| @ -519,7 +529,7 @@ jobs: | ||||
| 
 | ||||
|   b_docs: | ||||
|     docker: | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1904 | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> | ||||
|     steps: | ||||
|       - checkout | ||||
|       - run: *setup_prerelease_commit_hash | ||||
| @ -544,7 +554,7 @@ jobs: | ||||
| 
 | ||||
|   t_ubu_cli: &t_ubu_cli | ||||
|     docker: | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1904 | ||||
|       - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> | ||||
|     environment: | ||||
|       TERM: xterm | ||||
|     steps: | ||||
| @ -597,7 +607,22 @@ jobs: | ||||
|             npm --version | ||||
|             test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt) | ||||
| 
 | ||||
|   t_ems_external_gnosis: | ||||
|   t_ems_compile_ext_gnosis: | ||||
|     docker: | ||||
|       - image: circleci/node:10 | ||||
|     environment: | ||||
|       TERM: xterm | ||||
|     steps: | ||||
|       - checkout | ||||
|       - attach_workspace: | ||||
|           at: /tmp/workspace | ||||
|       - run: | ||||
|           name: External GnosisSafe compilation | ||||
|           command: | | ||||
|             export COMPILE_ONLY=1 | ||||
|             test/externalTests/gnosis.sh /tmp/workspace/soljson.js || test/externalTests/gnosis.sh /tmp/workspace/soljson.js | ||||
| 
 | ||||
|   t_ems_test_ext_gnosis: | ||||
|     docker: | ||||
|       - image: circleci/node:10 | ||||
|     environment: | ||||
| @ -613,7 +638,22 @@ jobs: | ||||
|       - run: *gitter_notify_failure | ||||
|       - run: *gitter_notify_success | ||||
| 
 | ||||
|   t_ems_external_zeppelin: | ||||
|   t_ems_compile_ext_zeppelin: | ||||
|     docker: | ||||
|       - image: circleci/node:10 | ||||
|     environment: | ||||
|       TERM: xterm | ||||
|     steps: | ||||
|       - checkout | ||||
|       - attach_workspace: | ||||
|           at: /tmp/workspace | ||||
|       - run: | ||||
|           name: External Zeppelin compilation | ||||
|           command: | | ||||
|             export COMPILE_ONLY=1 | ||||
|             test/externalTests/zeppelin.sh /tmp/workspace/soljson.js || test/externalTests/zeppelin.sh /tmp/workspace/soljson.js | ||||
| 
 | ||||
|   t_ems_test_ext_zeppelin: | ||||
|     docker: | ||||
|       - image: circleci/node:10 | ||||
|     environment: | ||||
| @ -629,7 +669,26 @@ jobs: | ||||
|       - run: *gitter_notify_failure | ||||
|       - run: *gitter_notify_success | ||||
| 
 | ||||
|   t_ems_external_colony: | ||||
|   t_ems_compile_ext_colony: | ||||
|     docker: | ||||
|       - image: circleci/node:10 | ||||
|     environment: | ||||
|       TERM: xterm | ||||
|     steps: | ||||
|       - checkout | ||||
|       - attach_workspace: | ||||
|           at: /tmp/workspace | ||||
|       - run: | ||||
|           name: Install test dependencies | ||||
|           command: | | ||||
|             sudo apt-get -qy install lsof | ||||
|       - run: | ||||
|           name: External ColonyNetworks compilation | ||||
|           command: | | ||||
|             export COMPILE_ONLY=1 | ||||
|             test/externalTests/colony.sh /tmp/workspace/soljson.js || test/externalTests/colony.sh /tmp/workspace/soljson.js | ||||
| 
 | ||||
|   t_ems_test_ext_colony: | ||||
|     docker: | ||||
|       - image: circleci/node:10 | ||||
|     environment: | ||||
| @ -693,6 +752,9 @@ workflows: | ||||
|       # Emscripten build and selected tests | ||||
|       - b_ems: *workflow_trigger_on_tags | ||||
|       - t_ems_solcjs: *workflow_emscripten | ||||
|       - t_ems_compile_ext_colony: *workflow_emscripten | ||||
|       - t_ems_compile_ext_gnosis: *workflow_emscripten | ||||
|       - t_ems_compile_ext_zeppelin: *workflow_emscripten | ||||
| 
 | ||||
|   nightly: | ||||
| 
 | ||||
| @ -706,12 +768,6 @@ workflows: | ||||
|                 - develop_060 | ||||
| 
 | ||||
|     jobs: | ||||
|       # Emscripten builds and external tests | ||||
|       - b_ems: *workflow_trigger_on_tags | ||||
|       - t_ems_external_zeppelin: *workflow_emscripten | ||||
|       - t_ems_external_gnosis: *workflow_emscripten | ||||
|       - t_ems_external_colony: *workflow_emscripten | ||||
| 
 | ||||
|       # OSSFUZZ builds and (regression) tests | ||||
|       - b_ubu_ossfuzz: *workflow_trigger_on_tags | ||||
|       - t_ubu_ossfuzz: *workflow_ubuntu1904_ossfuzz | ||||
|  | ||||
| @ -62,7 +62,7 @@ RUN git clone --recursive -b boost-1.69.0 https://github.com/boostorg/boost.git | ||||
|     rm -rf /usr/src/boost | ||||
| 
 | ||||
| # Z3 | ||||
| RUN git clone --depth 1 -b z3-4.8.6 https://github.com/Z3Prover/z3.git \ | ||||
| RUN git clone --depth 1 -b z3-4.8.7 https://github.com/Z3Prover/z3.git \ | ||||
|     /usr/src/z3; \ | ||||
|     cd /usr/src/z3; \ | ||||
|     python scripts/mk_make.py --prefix=/usr ; \ | ||||
| @ -89,43 +89,24 @@ RUN set -ex; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/libprotobuf-mutator | ||||
| 
 | ||||
| # ETHASH | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ | ||||
| 	cd ethash; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/ethash | ||||
| 
 | ||||
| # INTX | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ | ||||
| 	cd intx; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/intx; | ||||
| 
 | ||||
| # EVMONE | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ | ||||
| 	git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ | ||||
| 	cd evmone; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	# isoltest links against the evmone shared library | ||||
| 	cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	# abiv2_proto_ossfuzz links against the evmone standalone static library | ||||
| 	cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX="/usr" ..; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/evmone | ||||
| 
 | ||||
| FROM base | ||||
| COPY --from=libraries /usr/lib /usr/lib | ||||
| COPY --from=libraries /usr/bin /usr/bin | ||||
| COPY --from=libraries /usr/include /usr/include | ||||
| COPY --from=libraries /usr/include /usr/include | ||||
|  | ||||
| @ -74,34 +74,10 @@ RUN set -ex; \ | ||||
| 	ar r /usr/lib/libFuzzingEngine.a *.o; \ | ||||
| 	rm -rf /var/lib/libfuzzer | ||||
| 
 | ||||
| # ETHASH | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ | ||||
| 	cd ethash; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/ethash | ||||
| 
 | ||||
| # INTX | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ | ||||
| 	cd intx; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/intx; | ||||
| 
 | ||||
| # EVMONE | ||||
| ARG EVMONE_HASH="f10d12c190f55a9d373e78b2dc0074d35d752c02cb536bb6fe754fb3719dd69e" | ||||
| ARG EVMONE_HASH="fa4f40daf7cf9ccbcca6c78345977e084ea2136a8eae661e4d19471be852b15b" | ||||
| ARG EVMONE_MAJOR="0" | ||||
| ARG EVMONE_MINOR="1" | ||||
| ARG EVMONE_MINOR="3" | ||||
| ARG EVMONE_MICRO="0" | ||||
| RUN set -ex; \ | ||||
| 	EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \ | ||||
|  | ||||
| @ -74,37 +74,14 @@ RUN set -ex; \ | ||||
|     ar r /usr/lib/libFuzzingEngine.a *.o; \ | ||||
| 	rm -rf /var/lib/libfuzzer | ||||
| 
 | ||||
| # ETHASH | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ | ||||
| 	cd ethash; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/ethash | ||||
| 
 | ||||
| # INTX | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ | ||||
| 	cd intx; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/intx; | ||||
| 
 | ||||
| # EVMONE | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ | ||||
| 	git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ | ||||
| 	cd evmone; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	# isoltest links against the evmone shared library | ||||
| 	cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
|  | ||||
| @ -43,17 +43,17 @@ then | ||||
|   ./scripts/install_obsolete_jsoncpp_1_7_4.sh | ||||
| 
 | ||||
|   # z3 | ||||
|   wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.6/z3-4.8.6-x64-osx-10.14.6.zip | ||||
|   unzip z3-4.8.6-x64-osx-10.14.6.zip | ||||
|   rm -f z3-4.8.6-x64-osx-10.14.6.zip | ||||
|   cp z3-4.8.6-x64-osx-10.14.6/bin/libz3.a /usr/local/lib | ||||
|   cp z3-4.8.6-x64-osx-10.14.6/bin/z3 /usr/local/bin | ||||
|   cp z3-4.8.6-x64-osx-10.14.6/include/* /usr/local/include | ||||
|   rm -rf z3-4.8.6-x64-osx-10.14.6 | ||||
|   wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.7/z3-4.8.7-x64-osx-10.14.6.zip | ||||
|   unzip z3-4.8.7-x64-osx-10.14.6.zip | ||||
|   rm -f z3-4.8.7-x64-osx-10.14.6.zip | ||||
|   cp z3-4.8.7-x64-osx-10.14.6/bin/libz3.a /usr/local/lib | ||||
|   cp z3-4.8.7-x64-osx-10.14.6/bin/z3 /usr/local/bin | ||||
|   cp z3-4.8.7-x64-osx-10.14.6/include/* /usr/local/include | ||||
|   rm -rf z3-4.8.7-x64-osx-10.14.6 | ||||
| 
 | ||||
|   # evmone | ||||
|   wget https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-darwin-x86_64.tar.gz | ||||
|   tar xzpf evmone-0.1.0-darwin-x86_64.tar.gz -C /usr/local | ||||
|   rm -f evmone-0.1.0-darwin-x86_64.tar.gz | ||||
|   wget https://github.com/ethereum/evmone/releases/download/v0.3.0/evmone-0.3.0-darwin-x86_64.tar.gz | ||||
|   tar xzpf evmone-0.3.0-darwin-x86_64.tar.gz -C /usr/local | ||||
|   rm -f evmone-0.3.0-darwin-x86_64.tar.gz | ||||
| fi | ||||
| 
 | ||||
|  | ||||
| @ -29,9 +29,9 @@ set -e | ||||
| REPODIR="$(realpath $(dirname $0)/..)" | ||||
| 
 | ||||
| for OPTIMIZE in 0 1; do | ||||
|     for EVM in homestead byzantium constantinople petersburg; do | ||||
|     for EVM in homestead byzantium constantinople petersburg istanbul; do | ||||
|         EVM=$EVM OPTIMIZE=$OPTIMIZE ${REPODIR}/.circleci/soltest.sh | ||||
|     done | ||||
| done | ||||
| 
 | ||||
| EVM=constantinople OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh | ||||
| EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh | ||||
|  | ||||
| @ -110,7 +110,7 @@ matrix: | ||||
|           before_install: | ||||
|               - nvm install 8 | ||||
|               - nvm use 8 | ||||
|               - docker pull trzeci/emscripten:sdk-tag-1.38.22-64bit | ||||
|               - docker pull trzeci/emscripten:sdk-tag-1.39.3-64bit | ||||
|           env: | ||||
|               - SOLC_EMSCRIPTEN=On | ||||
|               - SOLC_INSTALL_DEPS_TRAVIS=Off | ||||
| @ -127,7 +127,7 @@ matrix: | ||||
|               # | ||||
|               # This key here has no significant on anything, apart from caching. Please keep | ||||
|               # it in sync with the version above. | ||||
|               - EMSCRIPTEN_VERSION_KEY="1.38.22" | ||||
|               - EMSCRIPTEN_VERSION_KEY="1.39.3" | ||||
| 
 | ||||
|         # OS X Mavericks (10.9) | ||||
|         # https://en.wikipedia.org/wiki/OS_X_Mavericks | ||||
|  | ||||
| @ -10,7 +10,7 @@ include(EthPolicy) | ||||
| eth_policy() | ||||
| 
 | ||||
| # project name and version should be set after cmake_policy CMP0048 | ||||
| set(PROJECT_VERSION "0.5.13") | ||||
| set(PROJECT_VERSION "0.5.14") | ||||
| project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) | ||||
| 
 | ||||
| include(TestBigEndian) | ||||
|  | ||||
							
								
								
									
										29
									
								
								Changelog.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								Changelog.md
									
									
									
									
									
								
							| @ -1,3 +1,31 @@ | ||||
| ### 0.5.14 (2019-12-09) | ||||
| 
 | ||||
| Language Features: | ||||
|  * Allow to obtain the selector of public or external library functions via a member ``.selector``. | ||||
|  * Inline Assembly: Support constants that reference other constants. | ||||
|  * Parser: Allow splitting hexadecimal and regular string literals into multiple parts. | ||||
| 
 | ||||
| 
 | ||||
| Compiler Features: | ||||
|  * Commandline Interface: Allow translation from yul / strict assembly to EWasm using ``solc --yul --yul-dialect evm --machine eWasm`` | ||||
|  * Set the default EVM version to "Istanbul". | ||||
|  * SMTChecker: Add support to constructors including constructor inheritance. | ||||
|  * Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable. | ||||
|  * Yul Optimizer: Perform loop-invariant code motion. | ||||
| 
 | ||||
| 
 | ||||
| Bugfixes: | ||||
|  * SMTChecker: Fix internal error when using ``abi.decode``. | ||||
|  * SMTChecker: Fix internal error when using arrays or mappings of functions. | ||||
|  * SMTChecker: Fix internal error in array of structs type. | ||||
|  * Version Checker: ``^0`` should match ``0.5.0``, but no prerelease. | ||||
|  * Yul: Consider infinite loops and recursion to be not removable. | ||||
| 
 | ||||
| 
 | ||||
| Build System: | ||||
|  * Update to emscripten version 1.39.3. | ||||
| 
 | ||||
| 
 | ||||
| ### 0.5.13 (2019-11-14) | ||||
| 
 | ||||
| Language Features: | ||||
| @ -22,6 +50,7 @@ Bugfixes: | ||||
|  * SMTChecker: Fix internal error when implicitly converting string literals to fixed bytes. | ||||
|  * Type Checker: Disallow constructor of the same class to be used as modifier. | ||||
|  * Type Checker: Treat magic variables as unknown identifiers in inline assembly. | ||||
|  * Code Generator: Fix internal error when trying to convert ``super`` to a different type | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @ -31,7 +31,7 @@ Instructions about how to build and install the Solidity compiler can be found i | ||||
| 
 | ||||
| A "Hello World" program in Solidity is of even less use than in other languages, but still: | ||||
| 
 | ||||
| ``` | ||||
| ```solidity | ||||
| pragma solidity ^0.5.0; | ||||
| 
 | ||||
| contract HelloWorld { | ||||
| @ -44,10 +44,10 @@ contract HelloWorld { | ||||
| To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is an | ||||
| browser-based IDE. Here are some example contracts: | ||||
| 
 | ||||
| 1. [Voting](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#voting) | ||||
| 2. [Blind Auction](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#blind-auction) | ||||
| 3. [Safe remote purchase](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#safe-remote-purchase) | ||||
| 4. [Micropayment Channel](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#micropayment-channel) | ||||
| 1. [Voting](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#voting) | ||||
| 2. [Blind Auction](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#blind-auction) | ||||
| 3. [Safe remote purchase](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#safe-remote-purchase) | ||||
| 4. [Micropayment Channel](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#micropayment-channel) | ||||
| 
 | ||||
| ## Documentation | ||||
| 
 | ||||
|  | ||||
| @ -86,7 +86,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA | ||||
| 		elseif(EMSCRIPTEN) | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0") | ||||
| 			# Leave only exported symbols as public and aggressively remove others | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden") | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -fvisibility=hidden") | ||||
| 			# Optimisation level | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") | ||||
| 			# Re-enable exception catching (optimisations above -O1 disable it) | ||||
| @ -110,9 +110,12 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1") | ||||
| 			# Export the Emscripten-generated auxiliary methods which are needed by solc-js. | ||||
| 			# Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt. | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','Pointer_stringify','lengthBytesUTF8','_malloc','stringToUTF8','setValue']") | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','_malloc','stringToUTF8','setValue']") | ||||
| 			# Do not build as a WebAssembly target - we need an asm.js output. | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0") | ||||
| 
 | ||||
| 			# Disable warnings about not being pure asm.js due to memory growth. | ||||
| 			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm") | ||||
| 		endif() | ||||
| 	endif() | ||||
| 
 | ||||
|  | ||||
| @ -37,9 +37,9 @@ endif() | ||||
| ExternalProject_Add(jsoncpp-project | ||||
|     PREFIX "${prefix}" | ||||
|     DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads" | ||||
|     DOWNLOAD_NAME jsoncpp-1.8.4.tar.gz | ||||
|     URL https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.tar.gz | ||||
|     URL_HASH SHA256=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6 | ||||
|     DOWNLOAD_NAME jsoncpp-1.9.2.tar.gz | ||||
|     URL https://github.com/open-source-parsers/jsoncpp/archive/1.9.2.tar.gz | ||||
|     URL_HASH SHA256=77a402fb577b2e0e5d0bdc1cf9c65278915cdb25171e3452c68b6da8a561f8f0 | ||||
|     CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND} | ||||
|     CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> | ||||
|                -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} | ||||
|  | ||||
| @ -758,6 +758,10 @@ | ||||
|         "bugs": [], | ||||
|         "released": "2019-11-14" | ||||
|     }, | ||||
|     "0.5.14": { | ||||
|         "bugs": [], | ||||
|         "released": "2019-12-09" | ||||
|     }, | ||||
|     "0.5.2": { | ||||
|         "bugs": [ | ||||
|             "SignedArrayStorageCopy", | ||||
|  | ||||
| @ -208,6 +208,48 @@ Restrictions for libraries in comparison to contracts: | ||||
| 
 | ||||
| (These might be lifted at a later point.) | ||||
| 
 | ||||
| .. _library-selectors: | ||||
| 
 | ||||
| Function Signatures and Selectors in Libraries | ||||
| ============================================== | ||||
| 
 | ||||
| While external calls to public or external library functions are possible, the calling convention for such calls | ||||
| is considered to be internal to Solidity and not the same as specified for the regular :ref:`contract ABI<ABI>`. | ||||
| External library functions support more argument types than external contract functions, for example recursive structs | ||||
| and storage pointers. For that reason, the function signatures used to compute the 4-byte selector are computed | ||||
| following an internal naming schema and arguments of types not supported in the contract ABI use an internal encoding. | ||||
| 
 | ||||
| The following identifiers are used for the types in the signatures: | ||||
| 
 | ||||
|  - Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI. | ||||
|  - Non-storage array types follow the same convention as in the contract ABI, i.e. ``<type>[]`` for dynamic arrays and | ||||
|    ``<type>[M]`` for fixed-size arrays of ``M`` elements. | ||||
|  - Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``. | ||||
|  - Storage pointer types use the type identifier of their corresponding non-storage type, but append a single space | ||||
|    followed by ``storage`` to it. | ||||
| 
 | ||||
| The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a | ||||
| ``uint256`` value referring to the storage slot to which they point. | ||||
| 
 | ||||
| Similarly to the contract ABI, the selector consists of the first four bytes of the Keccak256-hash of the signature. | ||||
| Its value can be obtained from Solidity using the ``.selector`` member as follows: | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|     pragma solidity >0.5.13 <0.7.0; | ||||
| 
 | ||||
|     library L { | ||||
|         function f(uint256) external {} | ||||
|     } | ||||
| 
 | ||||
|     contract C { | ||||
|         function g() public pure returns (bytes4) { | ||||
|             return L.f.selector; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| .. _call-protection: | ||||
| 
 | ||||
| Call Protection For Libraries | ||||
|  | ||||
| @ -86,7 +86,7 @@ but for quicker feedback, you might want to run specific tests. | ||||
| 
 | ||||
| Solidity includes different types of tests, most of them bundled into the | ||||
| `Boost C++ Test Framework <https://www.boost.org/doc/libs/1_69_0/libs/test/doc/html/index.html>`_ application ``soltest``. | ||||
| Running ``build/test/soltest` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. | ||||
| Running ``build/test/soltest`` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. | ||||
| 
 | ||||
| Some tests require the ``evmone`` library, others require ``libz3``. | ||||
| 
 | ||||
| @ -94,7 +94,7 @@ The test system will automatically try to discover the location of the ``evmone` | ||||
| starting from the current directory. The required file is called ``libevmone.so`` on Linux systems, | ||||
| ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on MacOS. If it is not found, the relevant tests | ||||
| are skipped. To run all tests, download the library from | ||||
| `Github <https://github.com/ethereum/evmone/releases/tag/v0.1.0>`_ | ||||
| `Github <https://github.com/ethereum/evmone/releases/tag/v0.3.0>`_ | ||||
| and either place it in the project root path or inside the ``deps`` folder. | ||||
| 
 | ||||
| If you do not have libz3 installed on your system, you should disable the SMT tests: | ||||
| @ -113,7 +113,7 @@ See especially: | ||||
|     If you are running this in plain Command Prompt, use ``.\build\test\Release\soltest.exe -- --no-smt``. | ||||
| 
 | ||||
| To run a subset of tests, you can use filters: | ||||
| ``./scripts/soltest.sh -t TestSuite/TestName, | ||||
| ``./scripts/soltest.sh -t TestSuite/TestName``, | ||||
| where ``TestName`` can be a wildcard ``*``. | ||||
| 
 | ||||
| For example, here is an example test you might run; | ||||
|  | ||||
| @ -204,7 +204,7 @@ The evaluation order of expressions is not specified (more formally, the order | ||||
| in which the children of one node in the expression tree are evaluated is not | ||||
| specified, but they are of course evaluated before the node itself). It is only | ||||
| guaranteed that statements are executed in order and short-circuiting for | ||||
| boolean expressions is done. See :ref:`order` for more information. | ||||
| boolean expressions is done. | ||||
| 
 | ||||
| .. index:: ! assignment | ||||
| 
 | ||||
|  | ||||
| @ -4,6 +4,25 @@ | ||||
| Safe Remote Purchase | ||||
| ******************** | ||||
| 
 | ||||
| Purchasing goods remotely currently requires multiple parties that need to trust each other. | ||||
| The simplest configuration involves a seller and a buyer. The buyer would like to receive | ||||
| an item from the seller and the seller would like to get money (or an equivalent) | ||||
| in return. The problematic part is the shipment here: There is no way to determine for | ||||
| sure that the item arrived at the buyer. | ||||
| 
 | ||||
| There are multiple ways to solve this problem, but all fall short in one or the other way. | ||||
| In the following example, both parties have to put twice the value of the item into the | ||||
| contract as escrow. As soon as this happened, the money will stay locked inside | ||||
| the contract until the buyer confirms that they received the item. After that, | ||||
| the buyer is returned the value (half of their deposit) and the seller gets three | ||||
| times the value (their deposit plus the value). The idea behind | ||||
| this is that both parties have an incentive to resolve the situation or otherwise | ||||
| their money is locked forever. | ||||
| 
 | ||||
| This contract of course does not solve the problem, but gives an overview of how | ||||
| you can use state machine-like constructs inside a contract. | ||||
| 
 | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
| @ -12,7 +31,7 @@ Safe Remote Purchase | ||||
|         uint public value; | ||||
|         address payable public seller; | ||||
|         address payable public buyer; | ||||
|         enum State { Created, Locked, Inactive } | ||||
|         enum State { Created, Locked, Release, Inactive } | ||||
|         // The state variable has a default value of the first member, `State.created` | ||||
|         State public state; | ||||
| 
 | ||||
| @ -57,6 +76,7 @@ Safe Remote Purchase | ||||
|         event Aborted(); | ||||
|         event PurchaseConfirmed(); | ||||
|         event ItemReceived(); | ||||
|         event SellerRefunded(); | ||||
| 
 | ||||
|         /// Abort the purchase and reclaim the ether. | ||||
|         /// Can only be called by the seller before | ||||
| @ -68,6 +88,10 @@ Safe Remote Purchase | ||||
|         { | ||||
|             emit Aborted(); | ||||
|             state = State.Inactive; | ||||
|             // We use transfer here directly. It is | ||||
|             // reentrancy-safe, because it is the | ||||
|             // last call in this function and we | ||||
|             // already changed the state. | ||||
|             seller.transfer(address(this).balance); | ||||
|         } | ||||
| 
 | ||||
| @ -97,12 +121,24 @@ Safe Remote Purchase | ||||
|             // It is important to change the state first because | ||||
|             // otherwise, the contracts called using `send` below | ||||
|             // can call in again here. | ||||
|             state = State.Inactive; | ||||
| 
 | ||||
|             // NOTE: This actually allows both the buyer and the seller to | ||||
|             // block the refund - the withdraw pattern should be used. | ||||
|             state = State.Release; | ||||
| 
 | ||||
|             buyer.transfer(value); | ||||
|             seller.transfer(address(this).balance); | ||||
|         } | ||||
| 
 | ||||
|         /// This function refunds the seller, i.e. | ||||
|         /// pays back the locked funds of the seller. | ||||
|         function refundSeller() | ||||
|             public | ||||
|             onlySeller | ||||
|             inState(State.Release) | ||||
|         { | ||||
|             emit SellerRefunded(); | ||||
|             // It is important to change the state first because | ||||
|             // otherwise, the contracts called using `send` below | ||||
|             // can call in again here. | ||||
|             state = State.Inactive; | ||||
| 
 | ||||
|             seller.transfer(3 * value); | ||||
|         } | ||||
|     } | ||||
| @ -66,6 +66,7 @@ They have varying degrees of completeness and up-to-dateness. The English | ||||
| version stands as a reference. | ||||
| 
 | ||||
| * `French <http://solidity-fr.readthedocs.io>`_ (in progress) | ||||
| * `Italian <https://github.com/damianoazzolini/solidity>`_ (in progress) | ||||
| * `Japanese <https://solidity-jp.readthedocs.io>`_ | ||||
| * `Korean <http://solidity-kr.readthedocs.io>`_ (in progress) | ||||
| * `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated) | ||||
|  | ||||
| @ -459,7 +459,7 @@ a non-rational number). | ||||
| String Literals and Types | ||||
| ------------------------- | ||||
| 
 | ||||
| String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``).  They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four.  As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. | ||||
| String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``), and they can also be split into multiple consecutive parts (``"foo" "bar"`` is equivalent to ``"foobar"``) which can be helpful when dealing with long strings.  They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four.  As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. | ||||
| 
 | ||||
| For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. | ||||
| 
 | ||||
| @ -498,7 +498,7 @@ terminate the string literal. Newline only terminates the string literal if it i | ||||
| Hexadecimal Literals | ||||
| -------------------- | ||||
| 
 | ||||
| Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. | ||||
| Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``), and they can also be split into multiple consecutive parts (``hex"00112233" hex"44556677"`` is equivalent to ``hex"0011223344556677"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. | ||||
| 
 | ||||
| Hexadecimal literals behave like :ref:`string literals <string_literals>` and have the same convertibility restrictions. | ||||
| 
 | ||||
|  | ||||
| @ -262,7 +262,15 @@ Contract Related | ||||
|     the current contract, explicitly convertible to :ref:`address` | ||||
| 
 | ||||
| ``selfdestruct(address payable recipient)``: | ||||
|     destroy the current contract, sending its funds to the given :ref:`address` | ||||
|     Destroy the current contract, sending its funds to the given :ref:`address` | ||||
|     and end execution. | ||||
|     Note that ``selfdestruct`` has some peculiarities inherited from the EVM: | ||||
| 
 | ||||
|     - the receiving contract's receive function is not executed. | ||||
|     - the contract is only really destroyed at the end of the transaction and ``revert`` s might "undo" the destruction. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Furthermore, all functions of the current contract are callable directly including the current function. | ||||
| 
 | ||||
|  | ||||
| @ -57,7 +57,7 @@ Either add ``--libraries "file.sol:Math:0x12345678901234567890123456789012345678 | ||||
| 
 | ||||
| If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. | ||||
| 
 | ||||
| If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. | ||||
| If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a "success" state and report any errors via the JSON output. | ||||
| 
 | ||||
| .. note:: | ||||
|     The library placeholder used to be the fully qualified name of the library itself | ||||
| @ -120,9 +120,9 @@ at each version. Backward compatibility is not guaranteed between each version. | ||||
| - ``constantinople`` | ||||
|    - Opcodes ``create2`, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly. | ||||
|    - Shifting operators use shifting opcodes and thus need less gas. | ||||
| - ``petersburg`` (**default**) | ||||
| - ``petersburg`` | ||||
|    - The compiler behaves the same way as with constantinople. | ||||
| - ``istanbul`` | ||||
| - ``istanbul`` (**default**) | ||||
|    - Opcodes ``chainid`` and ``selfbalance`` are available in assembly. | ||||
| - ``berlin`` (**experimental**) | ||||
| 
 | ||||
| @ -140,6 +140,8 @@ The fields are generally subject to change, | ||||
| some are optional (as noted), but we try to only make backwards compatible changes. | ||||
| 
 | ||||
| The compiler API expects a JSON formatted input and outputs the compilation result in a JSON formatted output. | ||||
| The standard error output is not used and the process will always terminate in a "success" state, even | ||||
| if there were errors. Errors are always reported as part of the JSON output. | ||||
| 
 | ||||
| The following subsections describe the format through an example. | ||||
| Comments are of course not permitted and used here only for explanatory purposes. | ||||
|  | ||||
							
								
								
									
										10
									
								
								docs/yul.rst
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								docs/yul.rst
									
									
									
									
									
								
							| @ -168,7 +168,7 @@ The ``continue`` and ``break`` statements can only be used inside loop bodies | ||||
| and have to be in the same function as the loop (or both have to be at the | ||||
| top level). | ||||
| The condition part of the for-loop has to evaluate to exactly one value. | ||||
| Functions cannot be defined inside for loop init blocks. | ||||
| Functions cannot be defined anywhere inside for loop init blocks. | ||||
| 
 | ||||
| Literals cannot be larger than the their type. The largest type defined is 256-bit wide. | ||||
| 
 | ||||
| @ -182,11 +182,17 @@ introduce new identifiers into these scopes. | ||||
| 
 | ||||
| Identifiers are visible in | ||||
| the block they are defined in (including all sub-nodes and sub-blocks). | ||||
| As an exception, identifiers defined in the "init" part of the for-loop | ||||
| 
 | ||||
| As an exception, identifiers defined directly in the "init" part of the for-loop | ||||
| (the first block) are visible in all other parts of the for-loop | ||||
| (but not outside of the loop). | ||||
| Identifiers declared in the other parts of the for loop respect the regular | ||||
| syntactical scoping rules. | ||||
| 
 | ||||
| This means a for-loop of the form ``for { I... } C { P... } { B... }`` is equivalent | ||||
| to ``{ I... for {} C { P... } { B... } }``. | ||||
| 
 | ||||
| 
 | ||||
| The parameters and return parameters of functions are visible in the | ||||
| function body and their names cannot overlap. | ||||
| 
 | ||||
|  | ||||
| @ -23,32 +23,59 @@ | ||||
| #include <libdevcore/Exceptions.h> | ||||
| #include <libdevcore/Assertions.h> | ||||
| #include <libdevcore/Keccak256.h> | ||||
| #include <libdevcore/FixedHash.h> | ||||
| 
 | ||||
| #include <boost/algorithm/string.hpp> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace dev; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| 
 | ||||
| static char const* upperHexChars = "0123456789ABCDEF"; | ||||
| static char const* lowerHexChars = "0123456789abcdef"; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| string dev::toHex(uint8_t _data, HexCase _case) | ||||
| { | ||||
| 	assertThrow(_case != HexCase::Mixed, BadHexCase, "Mixed case can only be used for byte arrays."); | ||||
| 
 | ||||
| 	char const* chars = _case == HexCase::Upper ? upperHexChars : lowerHexChars; | ||||
| 
 | ||||
| 	return std::string{ | ||||
| 		chars[(unsigned(_data) / 16) & 0xf], | ||||
| 		chars[unsigned(_data) & 0xf] | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| string dev::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case) | ||||
| { | ||||
| 	std::ostringstream ret; | ||||
| 	if (_prefix == HexPrefix::Add) | ||||
| 		ret << "0x"; | ||||
| 	std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0); | ||||
| 
 | ||||
| 	size_t i = 0; | ||||
| 	if (_prefix == HexPrefix::Add) | ||||
| 	{ | ||||
| 		ret[i++] = '0'; | ||||
| 		ret[i++] = 'x'; | ||||
| 	} | ||||
| 
 | ||||
| 	// Mixed case will be handled inside the loop.
 | ||||
| 	char const* chars = _case == HexCase::Upper ? upperHexChars : lowerHexChars; | ||||
| 	int rix = _data.size() - 1; | ||||
| 	for (uint8_t c: _data) | ||||
| 	{ | ||||
| 		// switch hex case every four hexchars
 | ||||
| 		auto hexcase = std::nouppercase; | ||||
| 		if (_case == HexCase::Upper) | ||||
| 			hexcase = std::uppercase; | ||||
| 		else if (_case == HexCase::Mixed) | ||||
| 			hexcase = (rix-- & 2) == 0 ? std::nouppercase : std::uppercase; | ||||
| 		if (_case == HexCase::Mixed) | ||||
| 			chars = (rix-- & 2) == 0 ? lowerHexChars : upperHexChars; | ||||
| 
 | ||||
| 		ret << std::hex << hexcase << std::setfill('0') << std::setw(2) << size_t(c); | ||||
| 		ret[i++] = chars[(unsigned(c) / 16) & 0xf]; | ||||
| 		ret[i++] = chars[unsigned(c) & 0xf]; | ||||
| 	} | ||||
| 	assertThrow(i == ret.size(), Exception, ""); | ||||
| 
 | ||||
| 	return ret.str(); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int dev::fromHex(char _i, WhenError _throw) | ||||
| @ -60,7 +87,7 @@ int dev::fromHex(char _i, WhenError _throw) | ||||
| 	if (_i >= 'A' && _i <= 'F') | ||||
| 		return _i - 'A' + 10; | ||||
| 	if (_throw == WhenError::Throw) | ||||
| 		BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); | ||||
| 		assertThrow(false, BadHexCharacter, to_string(_i)); | ||||
| 	else | ||||
| 		return -1; | ||||
| } | ||||
| @ -73,22 +100,18 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) | ||||
| 
 | ||||
| 	if (_s.size() % 2) | ||||
| 	{ | ||||
| 		int h = fromHex(_s[s++], WhenError::DontThrow); | ||||
| 		int h = fromHex(_s[s++], _throw); | ||||
| 		if (h != -1) | ||||
| 			ret.push_back(h); | ||||
| 		else if (_throw == WhenError::Throw) | ||||
| 			BOOST_THROW_EXCEPTION(BadHexCharacter()); | ||||
| 		else | ||||
| 			return bytes(); | ||||
| 	} | ||||
| 	for (unsigned i = s; i < _s.size(); i += 2) | ||||
| 	{ | ||||
| 		int h = fromHex(_s[i], WhenError::DontThrow); | ||||
| 		int l = fromHex(_s[i + 1], WhenError::DontThrow); | ||||
| 		int h = fromHex(_s[i], _throw); | ||||
| 		int l = fromHex(_s[i + 1], _throw); | ||||
| 		if (h != -1 && l != -1) | ||||
| 			ret.push_back((uint8_t)(h * 16 + l)); | ||||
| 		else if (_throw == WhenError::Throw) | ||||
| 			BOOST_THROW_EXCEPTION(BadHexCharacter()); | ||||
| 		else | ||||
| 			return bytes(); | ||||
| 	} | ||||
| @ -155,3 +178,14 @@ bool dev::isValidDecimal(string const& _string) | ||||
| 		return false; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| string dev::formatAsStringOrNumber(string const& _value) | ||||
| { | ||||
| 	assertThrow(_value.length() <= 32, StringTooLong, "String to be formatted longer than 32 bytes."); | ||||
| 
 | ||||
| 	for (auto const& c: _value) | ||||
| 		if (c <= 0x1f || c >= 0x7f || c == '"') | ||||
| 			return "0x" + h256(_value, h256::AlignLeft).hex(); | ||||
| 
 | ||||
| 	return "\"" + _value + "\""; | ||||
| } | ||||
|  | ||||
| @ -130,9 +130,12 @@ enum class HexCase | ||||
| 	Mixed = 2, | ||||
| }; | ||||
| 
 | ||||
| /// Convert a series of bytes to the corresponding string of hex duplets.
 | ||||
| /// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
 | ||||
| /// @example toHex("A\x69") == "4169"
 | ||||
| /// Convert a single byte to a string of hex characters (of length two),
 | ||||
| /// optionally with uppercase hex letters.
 | ||||
| std::string toHex(uint8_t _data, HexCase _case = HexCase::Lower); | ||||
| 
 | ||||
| /// Convert a series of bytes to the corresponding string of hex duplets,
 | ||||
| /// optionally with "0x" prefix and with uppercase hex letters.
 | ||||
| std::string toHex(bytes const& _data, HexPrefix _prefix = HexPrefix::DontAdd, HexCase _case = HexCase::Lower); | ||||
| 
 | ||||
| /// Converts a (printable) ASCII hex character into the corresponding integer value.
 | ||||
| @ -207,10 +210,6 @@ inline bytes toCompactBigEndian(T _val, unsigned _min = 0) | ||||
| 	toBigEndian(_val, ret); | ||||
| 	return ret; | ||||
| } | ||||
| inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0) | ||||
| { | ||||
| 	return (_min || _val) ? bytes{ _val } : bytes{}; | ||||
| } | ||||
| 
 | ||||
| /// Convenience function for conversion of a u256 to hex
 | ||||
| inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) | ||||
| @ -219,13 +218,18 @@ inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) | ||||
| 	return (prefix == HexPrefix::Add) ? "0x" + str : str; | ||||
| } | ||||
| 
 | ||||
| inline std::string toCompactHexWithPrefix(u256 const& _value) | ||||
| { | ||||
| 	return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add); | ||||
| } | ||||
| 
 | ||||
| /// Returns decimal representation for small numbers and hex for large numbers.
 | ||||
| inline std::string formatNumber(bigint const& _value) | ||||
| { | ||||
| 	if (_value < 0) | ||||
| 		return "-" + formatNumber(-_value); | ||||
| 	if (_value > 0x1000000) | ||||
| 		return toHex(toCompactBigEndian(_value), HexPrefix::Add); | ||||
| 		return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add); | ||||
| 	else | ||||
| 		return _value.str(); | ||||
| } | ||||
| @ -233,17 +237,11 @@ inline std::string formatNumber(bigint const& _value) | ||||
| inline std::string formatNumber(u256 const& _value) | ||||
| { | ||||
| 	if (_value > 0x1000000) | ||||
| 		return toHex(toCompactBigEndian(_value), HexPrefix::Add); | ||||
| 		return toCompactHexWithPrefix(_value); | ||||
| 	else | ||||
| 		return _value.str(); | ||||
| } | ||||
| 
 | ||||
| inline std::string toCompactHexWithPrefix(u256 val) | ||||
| { | ||||
| 	std::ostringstream ret; | ||||
| 	ret << std::hex << val; | ||||
| 	return "0x" + ret.str(); | ||||
| } | ||||
| 
 | ||||
| // Algorithms for string and string-like collections.
 | ||||
| 
 | ||||
| @ -292,7 +290,6 @@ void iterateReplacing(std::vector<T>& _vector, F const& _f) | ||||
| 		_vector = std::move(modifiedVector); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| namespace detail | ||||
| { | ||||
| template <typename T, typename F, std::size_t... I> | ||||
| @ -357,6 +354,11 @@ std::string getChecksummedAddress(std::string const& _addr); | ||||
| bool isValidHex(std::string const& _string); | ||||
| bool isValidDecimal(std::string const& _string); | ||||
| 
 | ||||
| /// @returns a quoted string if all characters are printable ASCII chars,
 | ||||
| /// or its hex representation otherwise.
 | ||||
| /// _value cannot be longer than 32 bytes.
 | ||||
| std::string formatAsStringOrNumber(std::string const& _value); | ||||
| 
 | ||||
| template<typename Container, typename Compare> | ||||
| bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) | ||||
| { | ||||
|  | ||||
| @ -46,11 +46,12 @@ private: | ||||
| 
 | ||||
| DEV_SIMPLE_EXCEPTION(InvalidAddress); | ||||
| DEV_SIMPLE_EXCEPTION(BadHexCharacter); | ||||
| DEV_SIMPLE_EXCEPTION(BadHexCase); | ||||
| DEV_SIMPLE_EXCEPTION(FileError); | ||||
| DEV_SIMPLE_EXCEPTION(DataTooLong); | ||||
| DEV_SIMPLE_EXCEPTION(StringTooLong); | ||||
| 
 | ||||
| // error information to be added to exceptions
 | ||||
| using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>; | ||||
| using errinfo_comment = boost::error_info<struct tag_comment, std::string>; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -17,6 +17,7 @@ | ||||
| 
 | ||||
| #include <libdevcore/IpfsHash.h> | ||||
| 
 | ||||
| #include <libdevcore/Assertions.h> | ||||
| #include <libdevcore/Exceptions.h> | ||||
| #include <libdevcore/picosha2.h> | ||||
| #include <libdevcore/CommonData.h> | ||||
| @ -55,11 +56,7 @@ string base58Encode(bytes const& _data) | ||||
| 
 | ||||
| bytes dev::ipfsHash(string _data) | ||||
| { | ||||
| 	if (_data.length() >= 1024 * 256) | ||||
| 		BOOST_THROW_EXCEPTION( | ||||
| 			DataTooLong() << | ||||
| 			errinfo_comment("Ipfs hash for large (chunked) files not yet implemented.") | ||||
| 		); | ||||
| 	assertThrow(_data.length() < 1024 * 256, DataTooLong, "IPFS hash for large (chunked) files not yet implemented."); | ||||
| 
 | ||||
| 	bytes lengthAsVarint = varintEncoding(_data.size()); | ||||
| 
 | ||||
|  | ||||
| @ -32,8 +32,8 @@ | ||||
| using namespace std; | ||||
| 
 | ||||
| static_assert( | ||||
| 	(JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 8) && (JSONCPP_VERSION_PATCH == 4), | ||||
| 	"Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.8.4." | ||||
| 	(JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 9) && (JSONCPP_VERSION_PATCH == 2), | ||||
| 	"Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.9.2." | ||||
| ); | ||||
| 
 | ||||
| namespace dev | ||||
| @ -111,16 +111,4 @@ bool jsonParseStrict(string const& _input, Json::Value& _json, string* _errs /* | ||||
| 	return parse(readerBuilder, _input, _json, _errs); | ||||
| } | ||||
| 
 | ||||
| bool jsonParse(string const& _input, Json::Value& _json, string *_errs /* = nullptr */) | ||||
| { | ||||
| 	static Json::CharReaderBuilder readerBuilder; | ||||
| 	return parse(readerBuilder, _input, _json, _errs); | ||||
| } | ||||
| 
 | ||||
| bool jsonParseFile(string const& _fileName, Json::Value& _json, string *_errs /* = nullptr */) | ||||
| { | ||||
| 	return jsonParse(readFileAsString(_fileName), _json, _errs); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } // namespace dev
 | ||||
|  | ||||
| @ -41,18 +41,4 @@ std::string jsonCompactPrint(Json::Value const& _input); | ||||
| /// \return \c true if the document was successfully parsed, \c false if an error occurred.
 | ||||
| bool jsonParseStrict(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); | ||||
| 
 | ||||
| /// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json)
 | ||||
| /// \param _input JSON input string
 | ||||
| /// \param _json [out] resulting JSON object
 | ||||
| /// \param _errs [out] Formatted error messages
 | ||||
| /// \return \c true if the document was successfully parsed, \c false if an error occurred.
 | ||||
| bool jsonParse(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); | ||||
| 
 | ||||
| /// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json)
 | ||||
| /// \param _input file containing JSON input
 | ||||
| /// \param _json [out] resulting JSON object
 | ||||
| /// \param _errs [out] Formatted error messages
 | ||||
| /// \return \c true if the document was successfully parsed, \c false if an error occurred.
 | ||||
| bool jsonParseFile(std::string const& _fileName, Json::Value& _json, std::string* _errs = nullptr); | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -20,109 +20,41 @@ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <boost/variant/static_visitor.hpp> | ||||
| 
 | ||||
| namespace dev | ||||
| { | ||||
| 
 | ||||
| /// Generic visitor used as follows:
 | ||||
| /// boost::apply_visitor(GenericVisitor<Class1, Class2>(
 | ||||
| ///     [](Class1& _c) { _c.f(); },
 | ||||
| ///     [](Class2& _c) { _c.g(); }
 | ||||
| /// ), variant);
 | ||||
| /// This one does not have a fallback and will fail at
 | ||||
| /// compile-time if you do not specify all variants.
 | ||||
| /**
 | ||||
|  * Generic visitor used as follows: | ||||
|  * std::visit(GenericVisitor{ | ||||
|  *     [](Class1& _c) { _c.f(); }, | ||||
|  *     [](Class2& _c) { _c.g(); } | ||||
|  * }, variant); | ||||
|  * This one does not have a fallback and will fail at | ||||
|  * compile-time if you do not specify all variants. | ||||
|  * | ||||
|  * Fallback with no return (it will not fail if you do not specify all variants): | ||||
|  * std::visit(GenericVisitor{ | ||||
|  *     VisitorFallback<>{}, | ||||
|  *     [](Class1& _c) { _c.f(); }, | ||||
|  *     [](Class2& _c) { _c.g(); } | ||||
|  * }, variant); | ||||
|  * | ||||
|  * Fallback with return type R (the fallback returns `R{}`: | ||||
|  * std::visit(GenericVisitor{ | ||||
|  *     VisitorFallback<R>{}, | ||||
|  *     [](Class1& _c) { _c.f(); }, | ||||
|  *     [](Class2& _c) { _c.g(); } | ||||
|  * }, variant); | ||||
|  */ | ||||
| 
 | ||||
| template <class...> | ||||
| struct GenericVisitor{}; | ||||
| template <typename...> struct VisitorFallback; | ||||
| 
 | ||||
| template <class Visitable, class... Others> | ||||
| struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...> | ||||
| { | ||||
| 	using GenericVisitor<Others...>::operator (); | ||||
| 	explicit GenericVisitor( | ||||
| 		std::function<void(Visitable&)> _visitor, | ||||
| 		std::function<void(Others&)>... _otherVisitors | ||||
| 	): | ||||
| 		GenericVisitor<Others...>(std::move(_otherVisitors)...), | ||||
| 		m_visitor(std::move(_visitor)) | ||||
| 	{} | ||||
| template <typename R> | ||||
| struct VisitorFallback<R> { template<typename T> R operator()(T&&) const { return {}; } }; | ||||
| 
 | ||||
| 	void operator()(Visitable& _v) const { m_visitor(_v); } | ||||
| 
 | ||||
| 	std::function<void(Visitable&)> m_visitor; | ||||
| }; | ||||
| template <> | ||||
| struct GenericVisitor<>: public boost::static_visitor<> { | ||||
| 	void operator()() const {} | ||||
| }; | ||||
| 
 | ||||
| /// Generic visitor with fallback:
 | ||||
| /// boost::apply_visitor(GenericFallbackVisitor<Class1, Class2>(
 | ||||
| ///     [](Class1& _c) { _c.f(); },
 | ||||
| ///     [](Class2& _c) { _c.g(); }
 | ||||
| /// ), variant);
 | ||||
| /// This one DOES have a fallback and will NOT fail at
 | ||||
| /// compile-time if you do not specify all variants.
 | ||||
| 
 | ||||
| template <class...> | ||||
| struct GenericFallbackVisitor{}; | ||||
| 
 | ||||
| template <class Visitable, class... Others> | ||||
| struct GenericFallbackVisitor<Visitable, Others...>: public GenericFallbackVisitor<Others...> | ||||
| { | ||||
| 	explicit GenericFallbackVisitor( | ||||
| 		std::function<void(Visitable&)> _visitor, | ||||
| 		std::function<void(Others&)>... _otherVisitors | ||||
| 	): | ||||
| 		GenericFallbackVisitor<Others...>(std::move(_otherVisitors)...), | ||||
| 		m_visitor(std::move(_visitor)) | ||||
| 	{} | ||||
| 
 | ||||
| 	using GenericFallbackVisitor<Others...>::operator (); | ||||
| 	void operator()(Visitable& _v) const { m_visitor(_v); } | ||||
| 
 | ||||
| 	std::function<void(Visitable&)> m_visitor; | ||||
| }; | ||||
| template <> | ||||
| struct GenericFallbackVisitor<>: public boost::static_visitor<> { | ||||
| 	template <class T> | ||||
| 	void operator()(T&) const { } | ||||
| }; | ||||
| 
 | ||||
| /// Generic visitor with fallback that can return a value:
 | ||||
| /// boost::apply_visitor(GenericFallbackReturnsVisitor<ReturnType, Class1, Class2>(
 | ||||
| ///     [](Class1& _c) { return _c.f(); },
 | ||||
| ///     [](Class2& _c) { return _c.g(); }
 | ||||
| /// ), variant);
 | ||||
| /// This one DOES have a fallback and will NOT fail at
 | ||||
| /// compile-time if you do not specify all variants.
 | ||||
| /// The fallback {}-constructs the return value.
 | ||||
| 
 | ||||
| template <class R, class...> | ||||
| struct GenericFallbackReturnsVisitor{}; | ||||
| 
 | ||||
| template <class R, class Visitable, class... Others> | ||||
| struct GenericFallbackReturnsVisitor<R, Visitable, Others...>: public GenericFallbackReturnsVisitor<R, Others...> | ||||
| { | ||||
| 	explicit GenericFallbackReturnsVisitor( | ||||
| 		std::function<R(Visitable&)> _visitor, | ||||
| 		std::function<R(Others&)>... _otherVisitors | ||||
| 	): | ||||
| 		GenericFallbackReturnsVisitor<R, Others...>(std::move(_otherVisitors)...), | ||||
| 		m_visitor(std::move(_visitor)) | ||||
| 	{} | ||||
| 
 | ||||
| 	using GenericFallbackReturnsVisitor<R, Others...>::operator (); | ||||
| 	R operator()(Visitable& _v) const { return m_visitor(_v); } | ||||
| 
 | ||||
| 	std::function<R(Visitable&)> m_visitor; | ||||
| }; | ||||
| template <class R> | ||||
| struct GenericFallbackReturnsVisitor<R>: public boost::static_visitor<R> { | ||||
| 	template <class T> | ||||
| 	R operator()(T&) const { return {}; } | ||||
| }; | ||||
| template<> | ||||
| struct VisitorFallback<> { template<typename T> void operator()(T&&) const {} }; | ||||
| 
 | ||||
| template <typename... Visitors> struct GenericVisitor: Visitors... { using Visitors::operator()...; }; | ||||
| template <typename... Visitors> GenericVisitor(Visitors...) -> GenericVisitor<Visitors...>; | ||||
| } | ||||
|  | ||||
| @ -323,7 +323,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const | ||||
| 			collection.append(createJsonValue("PUSH data", i.location().start, i.location().end, toStringInHex(i.data()))); | ||||
| 			break; | ||||
| 		default: | ||||
| 			BOOST_THROW_EXCEPTION(InvalidOpcode()); | ||||
| 			assertThrow(false, InvalidOpcode, ""); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -519,8 +519,11 @@ map<u256, u256> Assembly::optimiseInternal( | ||||
| 
 | ||||
| LinkerObject const& Assembly::assemble() const | ||||
| { | ||||
| 	// Return the already assembled object, if present.
 | ||||
| 	if (!m_assembledObject.bytecode.empty()) | ||||
| 		return m_assembledObject; | ||||
| 	// Otherwise ensure the object is actually clear.
 | ||||
| 	assertThrow(m_assembledObject.linkReferences.empty(), AssemblyException, "Unexpected link references."); | ||||
| 
 | ||||
| 	size_t subTagSize = 1; | ||||
| 	for (auto const& sub: m_subs) | ||||
| @ -638,7 +641,7 @@ LinkerObject const& Assembly::assemble() const | ||||
| 			ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST); | ||||
| 			break; | ||||
| 		default: | ||||
| 			BOOST_THROW_EXCEPTION(InvalidOpcode()); | ||||
| 			assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling."); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -81,7 +81,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	BOOST_THROW_EXCEPTION(InvalidOpcode()); | ||||
| 	assertThrow(false, InvalidOpcode, ""); | ||||
| } | ||||
| 
 | ||||
| int AssemblyItem::arguments() const | ||||
| @ -212,7 +212,7 @@ string AssemblyItem::toAssemblyText() const | ||||
| 		assertThrow(false, AssemblyException, "Invalid assembly item."); | ||||
| 		break; | ||||
| 	default: | ||||
| 		BOOST_THROW_EXCEPTION(InvalidOpcode()); | ||||
| 		assertThrow(false, InvalidOpcode, ""); | ||||
| 	} | ||||
| 	if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction) | ||||
| 	{ | ||||
| @ -277,7 +277,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) | ||||
| 		_out << " ???"; | ||||
| 		break; | ||||
| 	default: | ||||
| 		BOOST_THROW_EXCEPTION(InvalidOpcode()); | ||||
| 		assertThrow(false, InvalidOpcode, ""); | ||||
| 	} | ||||
| 	return _out; | ||||
| } | ||||
|  | ||||
| @ -158,11 +158,10 @@ AssemblyItems CSECodeGenerator::generateCode( | ||||
| 		for (auto id: {p.first, p.second}) | ||||
| 			if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) | ||||
| 			{ | ||||
| 				if (seqNr < _initialSequenceNumber) | ||||
| 					// Invalid sequenced operation.
 | ||||
| 					// @todo quick fix for now. Proper fix needs to choose representative with higher
 | ||||
| 					// sequence number during dependency analysis.
 | ||||
| 					BOOST_THROW_EXCEPTION(StackTooDeepException()); | ||||
| 				// Invalid sequenced operation.
 | ||||
| 				// @todo quick fix for now. Proper fix needs to choose representative with higher
 | ||||
| 				// sequence number during dependency analysis.
 | ||||
| 				assertThrow(seqNr >= _initialSequenceNumber, StackTooDeepException, ""); | ||||
| 				sequencedExpressions.insert(make_pair(seqNr, id)); | ||||
| 			} | ||||
| 
 | ||||
| @ -222,12 +221,9 @@ void CSECodeGenerator::addDependencies(Id _c) | ||||
| 		return; // we already computed the dependencies for _c
 | ||||
| 	ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); | ||||
| 	assertThrow(expr.item, OptimizerException, ""); | ||||
| 	if (expr.item->type() == UndefinedItem) | ||||
| 		BOOST_THROW_EXCEPTION( | ||||
| 			// If this exception happens, we need to find a different way to generate the
 | ||||
| 			// compound expression.
 | ||||
| 			ItemNotAvailableException() << errinfo_comment("Undefined item requested but not available.") | ||||
| 		); | ||||
| 	// If this exception happens, we need to find a different way to generate the
 | ||||
| 	// compound expression.
 | ||||
| 	assertThrow(expr.item->type() != UndefinedItem, ItemNotAvailableException, "Undefined item requested but not available."); | ||||
| 	for (Id argument: expr.arguments) | ||||
| 	{ | ||||
| 		addDependencies(argument); | ||||
|  | ||||
| @ -96,7 +96,7 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items) | ||||
| bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const | ||||
| { | ||||
| 	assertThrow(_data.size() > 0, OptimizerException, "Empty bytecode generated."); | ||||
| 	return bigint(GasMeter::dataGas(_data, m_params.isCreation)); | ||||
| 	return bigint(GasMeter::dataGas(_data, m_params.isCreation, m_params.evmVersion)); | ||||
| } | ||||
| 
 | ||||
| size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items) | ||||
| @ -131,7 +131,7 @@ bigint LiteralMethod::gasNeeded() const | ||||
| 	return combineGas( | ||||
| 		simpleRunGas({Instruction::PUSH1}), | ||||
| 		// PUSHX plus data
 | ||||
| 		(m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)), | ||||
| 		(m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)), | ||||
| 		0 | ||||
| 	); | ||||
| } | ||||
| @ -142,7 +142,7 @@ bigint CodeCopyMethod::gasNeeded() const | ||||
| 		// Run gas: we ignore memory increase costs
 | ||||
| 		simpleRunGas(copyRoutine()) + GasCosts::copyGas, | ||||
| 		// Data gas for copy routines: Some bytes are zero, but we ignore them.
 | ||||
| 		bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas), | ||||
| 		bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), | ||||
| 		// Data gas for data itself
 | ||||
| 		dataGas(toBigEndian(m_value)) | ||||
| 	); | ||||
| @ -322,7 +322,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const | ||||
| 	return combineGas( | ||||
| 		simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), | ||||
| 		// Data gas for routine: Some bytes are zero, but we ignore them.
 | ||||
| 		bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas), | ||||
| 		bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), | ||||
| 		0 | ||||
| 	); | ||||
| } | ||||
|  | ||||
| @ -33,5 +33,8 @@ struct OptimizerException: virtual AssemblyException {}; | ||||
| struct StackTooDeepException: virtual OptimizerException {}; | ||||
| struct ItemNotAvailableException: virtual OptimizerException {}; | ||||
| 
 | ||||
| DEV_SIMPLE_EXCEPTION(InvalidDeposit); | ||||
| DEV_SIMPLE_EXCEPTION(InvalidOpcode); | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -266,13 +266,13 @@ unsigned GasMeter::runGas(Instruction _instruction) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| u256 GasMeter::dataGas(bytes const& _data, bool _inCreation) | ||||
| u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion) | ||||
| { | ||||
| 	bigint gas = 0; | ||||
| 	if (_inCreation) | ||||
| 	{ | ||||
| 		for (auto b: _data) | ||||
| 			gas += (b != 0) ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; | ||||
| 			gas += (b != 0) ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::txDataZeroGas; | ||||
| 	} | ||||
| 	else | ||||
| 		gas = bigint(GasCosts::createDataGas) * _data.size(); | ||||
|  | ||||
| @ -53,7 +53,12 @@ namespace GasCosts | ||||
| 	} | ||||
| 	inline unsigned balanceGas(langutil::EVMVersion _evmVersion) | ||||
| 	{ | ||||
| 		return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 400 : 20; | ||||
| 		if (_evmVersion >= langutil::EVMVersion::istanbul()) | ||||
| 			return 700; | ||||
| 		else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) | ||||
| 			return 400; | ||||
| 		else | ||||
| 			return 20; | ||||
| 	} | ||||
| 	static unsigned const expGas = 10; | ||||
| 	inline unsigned expByteGas(langutil::EVMVersion _evmVersion) | ||||
| @ -64,7 +69,12 @@ namespace GasCosts | ||||
| 	static unsigned const keccak256WordGas = 6; | ||||
| 	inline unsigned sloadGas(langutil::EVMVersion _evmVersion) | ||||
| 	{ | ||||
| 		return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 200 : 50; | ||||
| 		if (_evmVersion >= langutil::EVMVersion::istanbul()) | ||||
| 			return 800; | ||||
| 		else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) | ||||
| 			return 200; | ||||
| 		else | ||||
| 			return 50; | ||||
| 	} | ||||
| 	static unsigned const sstoreSetGas = 20000; | ||||
| 	static unsigned const sstoreResetGas = 5000; | ||||
| @ -92,7 +102,10 @@ namespace GasCosts | ||||
| 	static unsigned const txGas = 21000; | ||||
| 	static unsigned const txCreateGas = 53000; | ||||
| 	static unsigned const txDataZeroGas = 4; | ||||
| 	static unsigned const txDataNonZeroGas = 68; | ||||
| 	inline unsigned txDataNonZeroGas(langutil::EVMVersion _evmVersion) | ||||
| 	{ | ||||
| 		return _evmVersion >= langutil::EVMVersion::istanbul() ? 16 : 68; | ||||
| 	} | ||||
| 	static unsigned const copyGas = 3; | ||||
| } | ||||
| 
 | ||||
| @ -139,7 +152,7 @@ public: | ||||
| 	/// @returns the gas cost of the supplied data, depending whether it is in creation code, or not.
 | ||||
| 	/// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas
 | ||||
| 	/// otherwise code will be stored and have to pay "createDataGas" cost.
 | ||||
| 	static u256 dataGas(bytes const& _data, bool _inCreation); | ||||
| 	static u256 dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion); | ||||
| 
 | ||||
| private: | ||||
| 	/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
 | ||||
|  | ||||
| @ -31,9 +31,6 @@ namespace dev | ||||
| namespace eth | ||||
| { | ||||
| 
 | ||||
| DEV_SIMPLE_EXCEPTION(InvalidDeposit); | ||||
| DEV_SIMPLE_EXCEPTION(InvalidOpcode); | ||||
| 
 | ||||
| /// Virtual machine bytecode instruction.
 | ||||
| enum class Instruction: uint8_t | ||||
| { | ||||
|  | ||||
| @ -54,17 +54,17 @@ ostream& KnownState::stream(ostream& _out) const | ||||
| 
 | ||||
| 	_out << "=== State ===" << endl; | ||||
| 	_out << "Stack height: " << dec << m_stackHeight << endl; | ||||
| 	_out << "Equivalence classes: " << endl; | ||||
| 	_out << "Equivalence classes:" << endl; | ||||
| 	for (Id eqClass = 0; eqClass < m_expressionClasses->size(); ++eqClass) | ||||
| 		streamExpressionClass(_out, eqClass); | ||||
| 
 | ||||
| 	_out << "Stack: " << endl; | ||||
| 	_out << "Stack:" << endl; | ||||
| 	for (auto const& it: m_stackElements) | ||||
| 	{ | ||||
| 		_out << "  " << dec << it.first << ": "; | ||||
| 		streamExpressionClass(_out, it.second); | ||||
| 	} | ||||
| 	_out << "Storage: " << endl; | ||||
| 	_out << "Storage:" << endl; | ||||
| 	for (auto const& it: m_storageContent) | ||||
| 	{ | ||||
| 		_out << "  "; | ||||
| @ -72,7 +72,7 @@ ostream& KnownState::stream(ostream& _out) const | ||||
| 		_out << ": "; | ||||
| 		streamExpressionClass(_out, it.second); | ||||
| 	} | ||||
| 	_out << "Memory: " << endl; | ||||
| 	_out << "Memory:" << endl; | ||||
| 	for (auto const& it: m_memoryContent) | ||||
| 	{ | ||||
| 		_out << "  "; | ||||
|  | ||||
| @ -35,7 +35,9 @@ namespace eth | ||||
|  */ | ||||
| struct LinkerObject | ||||
| { | ||||
| 	/// The bytecode.
 | ||||
| 	bytes bytecode; | ||||
| 
 | ||||
| 	/// Map from offsets in bytecode to library identifiers. The addresses starting at those offsets
 | ||||
| 	/// need to be replaced by the actual addresses by the linker.
 | ||||
| 	std::map<size_t, std::string> linkReferences; | ||||
| @ -47,7 +49,7 @@ struct LinkerObject | ||||
| 	void link(std::map<std::string, h160> const& _libraryAddresses); | ||||
| 
 | ||||
| 	/// @returns a hex representation of the bytecode of the given object, replacing unlinked
 | ||||
| 	/// addresses by placeholders.
 | ||||
| 	/// addresses by placeholders. This output is lowercase.
 | ||||
| 	std::string toHex() const; | ||||
| 
 | ||||
| 	/// @returns a 36 character string that is used as a placeholder for the library
 | ||||
|  | ||||
| @ -40,7 +40,7 @@ GasMeter::GasConsumption PathGasMeter::estimateMax( | ||||
| 	shared_ptr<KnownState> const& _state | ||||
| ) | ||||
| { | ||||
| 	auto path = unique_ptr<GasPath>(new GasPath()); | ||||
| 	auto path = make_unique<GasPath>(); | ||||
| 	path->index = _startIndex; | ||||
| 	path->state = _state->copy(); | ||||
| 	queue(move(path)); | ||||
| @ -120,7 +120,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem() | ||||
| 
 | ||||
| 		for (u256 const& tag: jumpTags) | ||||
| 		{ | ||||
| 			auto newPath = unique_ptr<GasPath>(new GasPath()); | ||||
| 			auto newPath = make_unique<GasPath>(); | ||||
| 			newPath->index = m_items.size(); | ||||
| 			if (m_tagPositions.count(tag)) | ||||
| 				newPath->index = m_tagPositions.at(tag); | ||||
|  | ||||
| @ -67,47 +67,48 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1( | ||||
| ) | ||||
| { | ||||
| 	using Word = typename Pattern::Word; | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 	return std::vector<SimplificationRule<Pattern>> { | ||||
| 		// arithmetic on constants
 | ||||
| 		{{Pattern::Builtins::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false}, | ||||
| 		{{Pattern::Builtins::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false}, | ||||
| 		{{Pattern::Builtins::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false}, | ||||
| 		{{Pattern::Builtins::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, | ||||
| 		{{Pattern::Builtins::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, | ||||
| 		{{Pattern::Builtins::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, | ||||
| 		{{Pattern::Builtins::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, | ||||
| 		{{Pattern::Builtins::EXP, {A, B}}, [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, | ||||
| 		{{Pattern::Builtins::NOT, {A}}, [=]{ return ~A.d(); }, false}, | ||||
| 		{{Pattern::Builtins::LT, {A, B}}, [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, | ||||
| 		{{Pattern::Builtins::GT, {A, B}}, [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, | ||||
| 		{{Pattern::Builtins::SLT, {A, B}}, [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, | ||||
| 		{{Pattern::Builtins::SGT, {A, B}}, [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, | ||||
| 		{{Pattern::Builtins::EQ, {A, B}}, [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, | ||||
| 		{{Pattern::Builtins::ISZERO, {A}}, [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, | ||||
| 		{{Pattern::Builtins::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false}, | ||||
| 		{{Pattern::Builtins::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false}, | ||||
| 		{{Pattern::Builtins::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false}, | ||||
| 		{{Pattern::Builtins::BYTE, {A, B}}, [=]{ | ||||
| 		{Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }, false}, | ||||
| 		{Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }, false}, | ||||
| 		{Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }, false}, | ||||
| 		{Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, | ||||
| 		{Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, | ||||
| 		{Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, | ||||
| 		{Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, | ||||
| 		{Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, | ||||
| 		{Builtins::NOT(A), [=]{ return ~A.d(); }, false}, | ||||
| 		{Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, | ||||
| 		{Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, | ||||
| 		{Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, | ||||
| 		{Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, | ||||
| 		{Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, | ||||
| 		{Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, | ||||
| 		{Builtins::AND(A, B), [=]{ return A.d() & B.d(); }, false}, | ||||
| 		{Builtins::OR(A, B), [=]{ return A.d() | B.d(); }, false}, | ||||
| 		{Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }, false}, | ||||
| 		{Builtins::BYTE(A, B), [=]{ | ||||
| 			return | ||||
| 				A.d() >= Pattern::WordSize / 8 ? | ||||
| 				0 : | ||||
| 				(B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff; | ||||
| 		}, false}, | ||||
| 		{{Pattern::Builtins::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, | ||||
| 		{{Pattern::Builtins::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, | ||||
| 		{{Pattern::Builtins::SIGNEXTEND, {A, B}}, [=]() -> Word { | ||||
| 		{Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, | ||||
| 		{Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, | ||||
| 		{Builtins::SIGNEXTEND(A, B), [=]() -> Word { | ||||
| 			if (A.d() >= Pattern::WordSize / 8 - 1) | ||||
| 				return B.d(); | ||||
| 			unsigned testBit = unsigned(A.d()) * 8 + 7; | ||||
| 			Word mask = (Word(1) << testBit) - 1; | ||||
| 			return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask; | ||||
| 		}, false}, | ||||
| 		{{Pattern::Builtins::SHL, {A, B}}, [=]{ | ||||
| 		{Builtins::SHL(A, B), [=]{ | ||||
| 			if (A.d() >= Pattern::WordSize) | ||||
| 				return Word(0); | ||||
| 			return shlWorkaround(B.d(), unsigned(A.d())); | ||||
| 		}, false}, | ||||
| 		{{Pattern::Builtins::SHR, {A, B}}, [=]{ | ||||
| 		{Builtins::SHR(A, B), [=]{ | ||||
| 			if (A.d() >= Pattern::WordSize) | ||||
| 				return Word(0); | ||||
| 			return B.d() >> unsigned(A.d()); | ||||
| @ -115,6 +116,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1( | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <class Pattern> | ||||
| std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2( | ||||
| 	Pattern, | ||||
| @ -125,50 +127,51 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2( | ||||
| ) | ||||
| { | ||||
| 	using Word = typename Pattern::Word; | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 	return std::vector<SimplificationRule<Pattern>> { | ||||
| 		// invariants involving known constants
 | ||||
| 		{{Pattern::Builtins::ADD, {X, 0}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::ADD, {0, X}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::SUB, {X, 0}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::SUB, {~Word(0), X}}, [=]() -> Pattern { return {Pattern::Builtins::NOT, {X}}; }, false}, | ||||
| 		{{Pattern::Builtins::MUL, {X, 0}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::MUL, {0, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::MUL, {X, 1}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::MUL, {1, X}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::MUL, {X, Word(-1)}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false}, | ||||
| 		{{Pattern::Builtins::MUL, {Word(-1), X}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false}, | ||||
| 		{{Pattern::Builtins::DIV, {X, 0}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::DIV, {0, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::DIV, {X, 1}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::SDIV, {X, 0}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::SDIV, {0, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::SDIV, {X, 1}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::AND, {X, ~Word(0)}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::AND, {~Word(0), X}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::AND, {X, 0}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::AND, {0, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::OR, {X, 0}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::OR, {0, X}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::OR, {X, ~Word(0)}}, [=]{ return ~Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::OR, {~Word(0), X}}, [=]{ return ~Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::XOR, {X, 0}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::XOR, {0, X}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::MOD, {X, 0}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::MOD, {0, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::EQ, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }, | ||||
| 		{{Pattern::Builtins::EQ, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }, | ||||
| 		{{Pattern::Builtins::SHL, {0, X}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::SHR, {0, X}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::SHL, {X, 0}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::SHR, {X, 0}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::GT, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false}, | ||||
| 		{{Pattern::Builtins::LT, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false}, | ||||
| 		{{Pattern::Builtins::GT, {X, ~Word(0)}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::LT, {~Word(0), X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::GT, {0, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::LT, {X, 0}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::AND, {{Pattern::Builtins::BYTE, {X, Y}}, {Word(0xff)}}}, [=]() -> Pattern { return {Pattern::Builtins::BYTE, {X, Y}}; }, false}, | ||||
| 		{{Pattern::Builtins::BYTE, {Pattern::WordSize / 8 - 1, X}}, [=]() -> Pattern { return {Pattern::Builtins::AND, {X, Word(0xff)}}; }, false} | ||||
| 		{Builtins::ADD(X, 0), [=]{ return X; }, false}, | ||||
| 		{Builtins::ADD(0, X), [=]{ return X; }, false}, | ||||
| 		{Builtins::SUB(X, 0), [=]{ return X; }, false}, | ||||
| 		{Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }, false}, | ||||
| 		{Builtins::MUL(X, 0), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::MUL(0, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::MUL(X, 1), [=]{ return X; }, false}, | ||||
| 		{Builtins::MUL(1, X), [=]{ return X; }, false}, | ||||
| 		{Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, | ||||
| 		{Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, | ||||
| 		{Builtins::DIV(X, 0), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::DIV(0, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::DIV(X, 1), [=]{ return X; }, false}, | ||||
| 		{Builtins::SDIV(X, 0), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::SDIV(0, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::SDIV(X, 1), [=]{ return X; }, false}, | ||||
| 		{Builtins::AND(X, ~Word(0)), [=]{ return X; }, false}, | ||||
| 		{Builtins::AND(~Word(0), X), [=]{ return X; }, false}, | ||||
| 		{Builtins::AND(X, 0), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::AND(0, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::OR(X, 0), [=]{ return X; }, false}, | ||||
| 		{Builtins::OR(0, X), [=]{ return X; }, false}, | ||||
| 		{Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }, true}, | ||||
| 		{Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }, true}, | ||||
| 		{Builtins::XOR(X, 0), [=]{ return X; }, false}, | ||||
| 		{Builtins::XOR(0, X), [=]{ return X; }, false}, | ||||
| 		{Builtins::MOD(X, 0), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::MOD(0, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, | ||||
| 		{Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, | ||||
| 		{Builtins::SHL(0, X), [=]{ return X; }, false}, | ||||
| 		{Builtins::SHR(0, X), [=]{ return X; }, false}, | ||||
| 		{Builtins::SHL(X, 0), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::SHR(X, 0), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, | ||||
| 		{Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, | ||||
| 		{Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::LT(~Word(0), X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::GT(0, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::LT(X, 0), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }, false}, | ||||
| 		{Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }, false} | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| @ -182,18 +185,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3( | ||||
| ) | ||||
| { | ||||
| 	using Word = typename Pattern::Word; | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 	return std::vector<SimplificationRule<Pattern>> { | ||||
| 		// operations involving an expression and itself
 | ||||
| 		{{Pattern::Builtins::AND, {X, X}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::OR, {X, X}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::XOR, {X, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::SUB, {X, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::EQ, {X, X}}, [=]{ return Word(1); }, true}, | ||||
| 		{{Pattern::Builtins::LT, {X, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::SLT, {X, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::GT, {X, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::SGT, {X, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::MOD, {X, X}}, [=]{ return Word(0); }, true} | ||||
| 		{Builtins::AND(X, X), [=]{ return X; }, true}, | ||||
| 		{Builtins::OR(X, X), [=]{ return X; }, true}, | ||||
| 		{Builtins::XOR(X, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::SUB(X, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::EQ(X, X), [=]{ return Word(1); }, true}, | ||||
| 		{Builtins::LT(X, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::SLT(X, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::GT(X, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::SGT(X, X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::MOD(X, X), [=]{ return Word(0); }, true} | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| @ -207,25 +211,26 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4( | ||||
| ) | ||||
| { | ||||
| 	using Word = typename Pattern::Word; | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 	return std::vector<SimplificationRule<Pattern>> { | ||||
| 		// logical instruction combinations
 | ||||
| 		{{Pattern::Builtins::NOT, {{Pattern::Builtins::NOT, {X}}}}, [=]{ return X; }, false}, | ||||
| 		{{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {X, Y}}}}, [=]{ return Y; }, true}, | ||||
| 		{{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {Y, X}}}}, [=]{ return Y; }, true}, | ||||
| 		{{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {X, Y}}, X}}, [=]{ return Y; }, true}, | ||||
| 		{{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {Y, X}}, X}}, [=]{ return Y; }, true}, | ||||
| 		{{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {X, Y}}}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {Y, X}}}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {X, Y}}, X}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {Y, X}}, X}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {X, Y}}}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {Y, X}}}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {X, Y}}, X}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {Y, X}}, X}}, [=]{ return X; }, true}, | ||||
| 		{{Pattern::Builtins::AND, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::AND, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::OR, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return ~Word(0); }, true}, | ||||
| 		{{Pattern::Builtins::OR, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return ~Word(0); }, true}, | ||||
| 		{Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }, false}, | ||||
| 		{Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }, true}, | ||||
| 		{Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }, true}, | ||||
| 		{Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }, true}, | ||||
| 		{Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }, true}, | ||||
| 		{Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }, true}, | ||||
| 		{Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }, true}, | ||||
| 		{Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }, true}, | ||||
| 		{Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }, true}, | ||||
| 		{Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }, true}, | ||||
| 		{Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }, true}, | ||||
| 		{Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }, true}, | ||||
| 		{Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }, true}, | ||||
| 		{Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }, true}, | ||||
| 		{Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }, true}, | ||||
| 		{Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }, true}, | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| @ -240,6 +245,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5( | ||||
| ) | ||||
| { | ||||
| 	using Word = typename Pattern::Word; | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 
 | ||||
| 	std::vector<SimplificationRule<Pattern>> rules; | ||||
| 
 | ||||
| @ -248,15 +254,15 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5( | ||||
| 	{ | ||||
| 		Word value = Word(1) << i; | ||||
| 		rules.push_back({ | ||||
| 			{Pattern::Builtins::MOD, {X, value}}, | ||||
| 			[=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; }, | ||||
| 			Builtins::MOD(X, value), | ||||
| 			[=]() -> Pattern { return Builtins::AND(X, value - 1); }, | ||||
| 			false | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	// Replace SHL >=256, X with 0
 | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::SHL, {A, X}}, | ||||
| 		Builtins::SHL(A, X), | ||||
| 		[=]() -> Pattern { return Word(0); }, | ||||
| 		true, | ||||
| 		[=]() { return A.d() >= Pattern::WordSize; } | ||||
| @ -264,7 +270,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5( | ||||
| 
 | ||||
| 	// Replace SHR >=256, X with 0
 | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::SHR, {A, X}}, | ||||
| 		Builtins::SHR(A, X), | ||||
| 		[=]() -> Pattern { return Word(0); }, | ||||
| 		true, | ||||
| 		[=]() { return A.d() >= Pattern::WordSize; } | ||||
| @ -272,29 +278,29 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5( | ||||
| 
 | ||||
| 	// Replace BYTE(A, X), A >= 32 with 0
 | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::BYTE, {A, X}}, | ||||
| 		Builtins::BYTE(A, X), | ||||
| 		[=]() -> Pattern { return Word(0); }, | ||||
| 		true, | ||||
| 		[=]() { return A.d() >= Pattern::WordSize / 8; } | ||||
| 	}); | ||||
| 
 | ||||
| 	for (auto const& op: std::vector<Instruction>{ | ||||
| 		Pattern::Builtins::ADDRESS, | ||||
| 		Pattern::Builtins::CALLER, | ||||
| 		Pattern::Builtins::ORIGIN, | ||||
| 		Pattern::Builtins::COINBASE | ||||
| 	for (auto instr: { | ||||
| 		Instruction::ADDRESS, | ||||
| 		Instruction::CALLER, | ||||
| 		Instruction::ORIGIN, | ||||
| 		Instruction::COINBASE | ||||
| 	}) | ||||
| 	{ | ||||
| 		assertThrow(Pattern::WordSize > 160, OptimizerException, ""); | ||||
| 		Word const mask = (Word(1) << 160) - 1; | ||||
| 		rules.push_back({ | ||||
| 			{Pattern::Builtins::AND, {{op, mask}}}, | ||||
| 			[=]() -> Pattern { return op; }, | ||||
| 			Builtins::AND(Pattern{instr}, mask), | ||||
| 			[=]() -> Pattern { return {instr}; }, | ||||
| 			false | ||||
| 		}); | ||||
| 		rules.push_back({ | ||||
| 			{Pattern::Builtins::AND, {{mask, op}}}, | ||||
| 			[=]() -> Pattern { return op; }, | ||||
| 			Builtins::AND(mask, Pattern{instr}), | ||||
| 			[=]() -> Pattern { return {instr}; }, | ||||
| 			false | ||||
| 		}); | ||||
| 	} | ||||
| @ -311,30 +317,35 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6( | ||||
| 	Pattern Y | ||||
| ) | ||||
| { | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 
 | ||||
| 	std::vector<SimplificationRule<Pattern>> rules; | ||||
| 	// Double negation of opcodes with boolean result
 | ||||
| 	for (auto const& op: std::vector<Instruction>{ | ||||
| 		Pattern::Builtins::EQ, | ||||
| 		Pattern::Builtins::LT, | ||||
| 		Pattern::Builtins::SLT, | ||||
| 		Pattern::Builtins::GT, | ||||
| 		Pattern::Builtins::SGT | ||||
| 	for (auto instr: { | ||||
| 		Instruction::EQ, | ||||
| 		Instruction::LT, | ||||
| 		Instruction::SLT, | ||||
| 		Instruction::GT, | ||||
| 		Instruction::SGT | ||||
| 	}) | ||||
| 	{ | ||||
| 		typename Builtins::PatternGeneratorInstance op{instr}; | ||||
| 		rules.push_back({ | ||||
| 			{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}}, | ||||
| 			[=]() -> Pattern { return {op, {X, Y}}; }, | ||||
| 			Builtins::ISZERO(Builtins::ISZERO(op(X, Y))), | ||||
| 			[=]() -> Pattern { return op(X, Y); }, | ||||
| 			false | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}}, | ||||
| 		[=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, | ||||
| 		Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))), | ||||
| 		[=]() -> Pattern { return Builtins::ISZERO(X); }, | ||||
| 		false | ||||
| 	}); | ||||
| 
 | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}}, | ||||
| 		[=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; }, | ||||
| 		Builtins::ISZERO(Builtins::XOR(X, Y)), | ||||
| 		[=]() -> Pattern { return Builtins::EQ(X, Y); }, | ||||
| 		false | ||||
| 	}); | ||||
| 
 | ||||
| @ -351,42 +362,44 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7( | ||||
| ) | ||||
| { | ||||
| 	using Word = typename Pattern::Word; | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 
 | ||||
| 	std::vector<SimplificationRule<Pattern>> rules; | ||||
| 	// Associative operations
 | ||||
| 	for (auto const& opFun: std::vector<std::pair<Instruction,std::function<Word(Word const&,Word const&)>>>{ | ||||
| 		{Pattern::Builtins::ADD, std::plus<Word>()}, | ||||
| 		{Pattern::Builtins::MUL, std::multiplies<Word>()}, | ||||
| 		{Pattern::Builtins::AND, std::bit_and<Word>()}, | ||||
| 		{Pattern::Builtins::OR, std::bit_or<Word>()}, | ||||
| 		{Pattern::Builtins::XOR, std::bit_xor<Word>()} | ||||
| 	for (auto&& instrAndFunc: std::vector<std::pair<Instruction, std::function<Word(Word, Word)>>>{ | ||||
| 		{Instruction::ADD, std::plus<Word>()}, | ||||
| 		{Instruction::MUL, std::multiplies<Word>()}, | ||||
| 		{Instruction::AND, std::bit_and<Word>()}, | ||||
| 		{Instruction::OR, std::bit_or<Word>()}, | ||||
| 		{Instruction::XOR, std::bit_xor<Word>()} | ||||
| 	}) | ||||
| 	{ | ||||
| 		auto op = opFun.first; | ||||
| 		auto fun = opFun.second; | ||||
| 		typename Builtins::PatternGeneratorInstance op{instrAndFunc.first}; | ||||
| 		std::function<Word(Word, Word)> fun = instrAndFunc.second; | ||||
| 		// Moving constants to the outside, order matters here - we first add rules
 | ||||
| 		// for constants and then for non-constants.
 | ||||
| 		// xa can be (X, A) or (A, X)
 | ||||
| 		for (auto xa: {std::vector<Pattern>{X, A}, std::vector<Pattern>{A, X}}) | ||||
| 		for (auto const& opXA: {op(X, A), op(A, X)}) | ||||
| 		{ | ||||
| 			rules += std::vector<SimplificationRule<Pattern>>{{ | ||||
| 				// (X+A)+B -> X+(A+B)
 | ||||
| 				{op, {{op, xa}, B}}, | ||||
| 				[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }, | ||||
| 				op(opXA, B), | ||||
| 				[=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, | ||||
| 				false | ||||
| 			}, { | ||||
| 				// (X+A)+Y -> (X+Y)+A
 | ||||
| 				{op, {{op, xa}, Y}}, | ||||
| 				[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }, | ||||
| 				op(opXA, Y), | ||||
| 				[=]() -> Pattern { return op(op(X, Y), A); }, | ||||
| 				false | ||||
| 			}, { | ||||
| 				// B+(X+A) -> X+(A+B)
 | ||||
| 				{op, {B, {op, xa}}}, | ||||
| 				[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }, | ||||
| 				op(B, opXA), | ||||
| 				[=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, | ||||
| 				false | ||||
| 			}, { | ||||
| 				// Y+(X+A) -> (Y+X)+A
 | ||||
| 				{op, {Y, {op, xa}}}, | ||||
| 				[=]() -> Pattern { return {op, {{op, {Y, X}}, A}}; }, | ||||
| 				op(Y, opXA), | ||||
| 				[=]() -> Pattern { return op(op(Y, X), A); }, | ||||
| 				false | ||||
| 			}}; | ||||
| 		} | ||||
| @ -395,13 +408,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7( | ||||
| 	// Combine two SHL by constant
 | ||||
| 	rules.push_back({ | ||||
| 		// SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X)
 | ||||
| 		{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}}, | ||||
| 		Builtins::SHL(B, Builtins::SHL(A, X)), | ||||
| 		[=]() -> Pattern { | ||||
| 			bigint sum = bigint(A.d()) + B.d(); | ||||
| 			if (sum >= Pattern::WordSize) | ||||
| 				return {Pattern::Builtins::AND, {X, Word(0)}}; | ||||
| 				return Builtins::AND(X, Word(0)); | ||||
| 			else | ||||
| 				return {Pattern::Builtins::SHL, {Word(sum), X}}; | ||||
| 				return Builtins::SHL(Word(sum), X); | ||||
| 		}, | ||||
| 		false | ||||
| 	}); | ||||
| @ -409,13 +422,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7( | ||||
| 	// Combine two SHR by constant
 | ||||
| 	rules.push_back({ | ||||
| 		// SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X)
 | ||||
| 		{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}}, | ||||
| 		Builtins::SHR(B, Builtins::SHR(A, X)), | ||||
| 		[=]() -> Pattern { | ||||
| 			bigint sum = bigint(A.d()) + B.d(); | ||||
| 			if (sum >= Pattern::WordSize) | ||||
| 				return {Pattern::Builtins::AND, {X, Word(0)}}; | ||||
| 				return Builtins::AND(X, Word(0)); | ||||
| 			else | ||||
| 				return {Pattern::Builtins::SHR, {Word(sum), X}}; | ||||
| 				return Builtins::SHR(Word(sum), X); | ||||
| 		}, | ||||
| 		false | ||||
| 	}); | ||||
| @ -423,16 +436,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7( | ||||
| 	// Combine SHL-SHR by constant
 | ||||
| 	rules.push_back({ | ||||
| 		// SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
 | ||||
| 		{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}}, | ||||
| 		Builtins::SHR(B, Builtins::SHL(A, X)), | ||||
| 		[=]() -> Pattern { | ||||
| 			Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d()); | ||||
| 
 | ||||
| 			if (A.d() > B.d()) | ||||
| 				return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}}; | ||||
| 				return Builtins::AND(Builtins::SHL(A.d() - B.d(), X), mask); | ||||
| 			else if (B.d() > A.d()) | ||||
| 				return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}}; | ||||
| 				return Builtins::AND(Builtins::SHR(B.d() - A.d(), X), mask); | ||||
| 			else | ||||
| 				return {Pattern::Builtins::AND, {X, mask}}; | ||||
| 				return Builtins::AND(X, mask); | ||||
| 		}, | ||||
| 		false, | ||||
| 		[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } | ||||
| @ -441,41 +454,42 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7( | ||||
| 	// Combine SHR-SHL by constant
 | ||||
| 	rules.push_back({ | ||||
| 		// SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
 | ||||
| 		{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}}, | ||||
| 		Builtins::SHL(B, Builtins::SHR(A, X)), | ||||
| 		[=]() -> Pattern { | ||||
| 			Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d())); | ||||
| 
 | ||||
| 			if (A.d() > B.d()) | ||||
| 				return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}}; | ||||
| 				return Builtins::AND(Builtins::SHR(A.d() - B.d(), X), mask); | ||||
| 			else if (B.d() > A.d()) | ||||
| 				return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}}; | ||||
| 				return Builtins::AND(Builtins::SHL(B.d() - A.d(), X), mask); | ||||
| 			else | ||||
| 				return {Pattern::Builtins::AND, {X, mask}}; | ||||
| 				return Builtins::AND(X, mask); | ||||
| 		}, | ||||
| 		false, | ||||
| 		[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } | ||||
| 	}); | ||||
| 
 | ||||
| 	// Move AND with constant across SHL and SHR by constant
 | ||||
| 	for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR}) | ||||
| 	for (auto instr: {Instruction::SHL, Instruction::SHR}) | ||||
| 	{ | ||||
| 		typename Builtins::PatternGeneratorInstance shiftOp{instr}; | ||||
| 		auto replacement = [=]() -> Pattern { | ||||
| 			Word mask = | ||||
| 				shiftOp == Pattern::Builtins::SHL ? | ||||
| 				instr == Instruction::SHL ? | ||||
| 				shlWorkaround(A.d(), unsigned(B.d())) : | ||||
| 				A.d() >> unsigned(B.d()); | ||||
| 			return {Pattern::Builtins::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}}; | ||||
| 			return Builtins::AND(shiftOp(B.d(), X), std::move(mask)); | ||||
| 		}; | ||||
| 		rules.push_back({ | ||||
| 			// SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
 | ||||
| 			{shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}}, | ||||
| 			shiftOp(B, Builtins::AND(X, A)), | ||||
| 			replacement, | ||||
| 			false, | ||||
| 			[=] { return B.d() < Pattern::WordSize; } | ||||
| 		}); | ||||
| 		rules.push_back({ | ||||
| 			// SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
 | ||||
| 			{shiftOp, {{B}, {Pattern::Builtins::AND, {{A}, {X}}}}}, | ||||
| 			shiftOp(B, Builtins::AND(A, X)), | ||||
| 			replacement, | ||||
| 			false, | ||||
| 			[=] { return B.d() < Pattern::WordSize; } | ||||
| @ -484,27 +498,27 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7( | ||||
| 
 | ||||
| 	rules.push_back({ | ||||
| 		// MUL(X, SHL(Y, 1)) -> SHL(Y, X)
 | ||||
| 		{Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}}, | ||||
| 		Builtins::MUL(X, Builtins::SHL(Y, Word(1))), | ||||
| 		[=]() -> Pattern { | ||||
| 			return {Pattern::Builtins::SHL, {Y, X}}; | ||||
| 			return Builtins::SHL(Y, X); | ||||
| 		}, | ||||
| 		// Actually only changes the order, does not remove.
 | ||||
| 		true | ||||
| 	}); | ||||
| 	rules.push_back({ | ||||
| 		// MUL(SHL(X, 1), Y) -> SHL(X, Y)
 | ||||
| 		{Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}}, | ||||
| 		Builtins::MUL(Builtins::SHL(X, Word(1)), Y), | ||||
| 		[=]() -> Pattern { | ||||
| 			return {Pattern::Builtins::SHL, {X, Y}}; | ||||
| 			return Builtins::SHL(X, Y); | ||||
| 		}, | ||||
| 		false | ||||
| 	}); | ||||
| 
 | ||||
| 	rules.push_back({ | ||||
| 		// DIV(X, SHL(Y, 1)) -> SHR(Y, X)
 | ||||
| 		{Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}}, | ||||
| 		Builtins::DIV(X, Builtins::SHL(Y, Word(1))), | ||||
| 		[=]() -> Pattern { | ||||
| 			return {Pattern::Builtins::SHR, {Y, X}}; | ||||
| 			return Builtins::SHR(Y, X); | ||||
| 		}, | ||||
| 		// Actually only changes the order, does not remove.
 | ||||
| 		true | ||||
| @ -519,16 +533,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7( | ||||
| 
 | ||||
| 	rules.push_back({ | ||||
| 		// AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B)
 | ||||
| 		{Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}}, | ||||
| 		[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; }, | ||||
| 		Builtins::AND(A, Builtins::SHR(B, X)), | ||||
| 		[=]() -> Pattern { return Builtins::SHR(B, X); }, | ||||
| 		false, | ||||
| 		feasibilityFunction | ||||
| 	}); | ||||
| 
 | ||||
| 	rules.push_back({ | ||||
| 		// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
 | ||||
| 		{Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}}, | ||||
| 		[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; }, | ||||
| 		Builtins::AND(Builtins::SHR(B, X), A), | ||||
| 		[=]() -> Pattern { return Builtins::SHR(B, X); }, | ||||
| 		false, | ||||
| 		feasibilityFunction | ||||
| 	}); | ||||
| @ -545,34 +559,35 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8( | ||||
| 	Pattern Y | ||||
| ) | ||||
| { | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 	std::vector<SimplificationRule<Pattern>> rules; | ||||
| 
 | ||||
| 	// move constants across subtractions
 | ||||
| 	rules += std::vector<SimplificationRule<Pattern>>{ | ||||
| 		{ | ||||
| 			// X - A -> X + (-A)
 | ||||
| 			{Pattern::Builtins::SUB, {X, A}}, | ||||
| 			[=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; }, | ||||
| 			Builtins::SUB(X, A), | ||||
| 			[=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); }, | ||||
| 			false | ||||
| 		}, { | ||||
| 			// (X + A) - Y -> (X - Y) + A
 | ||||
| 			{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}}, | ||||
| 			[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; }, | ||||
| 			Builtins::SUB(Builtins::ADD(X, A), Y), | ||||
| 			[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, | ||||
| 			false | ||||
| 		}, { | ||||
| 			// (A + X) - Y -> (X - Y) + A
 | ||||
| 			{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}}, | ||||
| 			[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; }, | ||||
| 			Builtins::SUB(Builtins::ADD(A, X), Y), | ||||
| 			[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, | ||||
| 			false | ||||
| 		}, { | ||||
| 			// X - (Y + A) -> (X - Y) + (-A)
 | ||||
| 			{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}}, | ||||
| 			[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; }, | ||||
| 			Builtins::SUB(X, Builtins::ADD(Y, A)), | ||||
| 			[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, | ||||
| 			false | ||||
| 		}, { | ||||
| 			// X - (A + Y) -> (X - Y) + (-A)
 | ||||
| 			{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}}, | ||||
| 			[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; }, | ||||
| 			Builtins::SUB(X, Builtins::ADD(A, Y)), | ||||
| 			[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, | ||||
| 			false | ||||
| 		} | ||||
| 	}; | ||||
| @ -591,30 +606,31 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart9( | ||||
| ) | ||||
| { | ||||
| 	using Word = typename Pattern::Word; | ||||
| 	using Builtins = typename Pattern::Builtins; | ||||
| 	std::vector<SimplificationRule<Pattern>> rules; | ||||
| 
 | ||||
| 	assertThrow(Pattern::WordSize > 160, OptimizerException, ""); | ||||
| 	Word const mask = (Word(1) << 160) - 1; | ||||
| 	// CREATE
 | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}}, | ||||
| 		[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; }, | ||||
| 		Builtins::AND(Builtins::CREATE(W, X, Y), mask), | ||||
| 		[=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, | ||||
| 		false | ||||
| 	}); | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}}, | ||||
| 		[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; }, | ||||
| 		Builtins::AND(mask, Builtins::CREATE(W, X, Y)), | ||||
| 		[=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, | ||||
| 		false | ||||
| 	}); | ||||
| 	// CREATE2
 | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}}, | ||||
| 		[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; }, | ||||
| 		Builtins::AND(Builtins::CREATE2(W, X, Y, Z), mask), | ||||
| 		[=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, | ||||
| 		false | ||||
| 	}); | ||||
| 	rules.push_back({ | ||||
| 		{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}}, | ||||
| 		[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; }, | ||||
| 		Builtins::AND(mask, Builtins::CREATE2(W, X, Y, Z)), | ||||
| 		[=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, | ||||
| 		false | ||||
| 	}); | ||||
| 
 | ||||
|  | ||||
| @ -21,6 +21,7 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <libevmasm/Instruction.h> | ||||
| #include <libdevcore/CommonData.h> | ||||
| #include <functional> | ||||
| 
 | ||||
| namespace dev | ||||
| @ -55,84 +56,105 @@ struct SimplificationRule | ||||
| 	std::function<bool()> feasible; | ||||
| }; | ||||
| 
 | ||||
| template <typename Pattern> | ||||
| struct EVMBuiltins | ||||
| { | ||||
| 	using InstrType = Instruction; | ||||
| 	static auto constexpr STOP = Instruction::STOP; | ||||
| 	static auto constexpr ADD = Instruction::ADD; | ||||
| 	static auto constexpr SUB = Instruction::SUB; | ||||
| 	static auto constexpr MUL = Instruction::MUL; | ||||
| 	static auto constexpr DIV = Instruction::DIV; | ||||
| 	static auto constexpr SDIV = Instruction::SDIV; | ||||
| 	static auto constexpr MOD = Instruction::MOD; | ||||
| 	static auto constexpr SMOD = Instruction::SMOD; | ||||
| 	static auto constexpr EXP = Instruction::EXP; | ||||
| 	static auto constexpr NOT = Instruction::NOT; | ||||
| 	static auto constexpr LT = Instruction::LT; | ||||
| 	static auto constexpr GT = Instruction::GT; | ||||
| 	static auto constexpr SLT = Instruction::SLT; | ||||
| 	static auto constexpr SGT = Instruction::SGT; | ||||
| 	static auto constexpr EQ = Instruction::EQ; | ||||
| 	static auto constexpr ISZERO = Instruction::ISZERO; | ||||
| 	static auto constexpr AND = Instruction::AND; | ||||
| 	static auto constexpr OR = Instruction::OR; | ||||
| 	static auto constexpr XOR = Instruction::XOR; | ||||
| 	static auto constexpr BYTE = Instruction::BYTE; | ||||
| 	static auto constexpr SHL = Instruction::SHL; | ||||
| 	static auto constexpr SHR = Instruction::SHR; | ||||
| 	static auto constexpr SAR = Instruction::SAR; | ||||
| 	static auto constexpr ADDMOD = Instruction::ADDMOD; | ||||
| 	static auto constexpr MULMOD = Instruction::MULMOD; | ||||
| 	static auto constexpr SIGNEXTEND = Instruction::SIGNEXTEND; | ||||
| 	static auto constexpr KECCAK256 = Instruction::KECCAK256; | ||||
| 	static auto constexpr ADDRESS = Instruction::ADDRESS; | ||||
| 	static auto constexpr BALANCE = Instruction::BALANCE; | ||||
| 	static auto constexpr ORIGIN = Instruction::ORIGIN; | ||||
| 	static auto constexpr CALLER = Instruction::CALLER; | ||||
| 	static auto constexpr CALLVALUE = Instruction::CALLVALUE; | ||||
| 	static auto constexpr CALLDATALOAD = Instruction::CALLDATALOAD; | ||||
| 	static auto constexpr CALLDATASIZE = Instruction::CALLDATASIZE; | ||||
| 	static auto constexpr CALLDATACOPY = Instruction::CALLDATACOPY; | ||||
| 	static auto constexpr CODESIZE = Instruction::CODESIZE; | ||||
| 	static auto constexpr CODECOPY = Instruction::CODECOPY; | ||||
| 	static auto constexpr GASPRICE = Instruction::GASPRICE; | ||||
| 	static auto constexpr EXTCODESIZE = Instruction::EXTCODESIZE; | ||||
| 	static auto constexpr EXTCODECOPY = Instruction::EXTCODECOPY; | ||||
| 	static auto constexpr RETURNDATASIZE = Instruction::RETURNDATASIZE; | ||||
| 	static auto constexpr RETURNDATACOPY = Instruction::RETURNDATACOPY; | ||||
| 	static auto constexpr EXTCODEHASH = Instruction::EXTCODEHASH; | ||||
| 	static auto constexpr BLOCKHASH = Instruction::BLOCKHASH; | ||||
| 	static auto constexpr COINBASE = Instruction::COINBASE; | ||||
| 	static auto constexpr TIMESTAMP = Instruction::TIMESTAMP; | ||||
| 	static auto constexpr NUMBER = Instruction::NUMBER; | ||||
| 	static auto constexpr DIFFICULTY = Instruction::DIFFICULTY; | ||||
| 	static auto constexpr GASLIMIT = Instruction::GASLIMIT; | ||||
| 	static auto constexpr CHAINID = Instruction::CHAINID; | ||||
| 	static auto constexpr SELFBALANCE = Instruction::SELFBALANCE; | ||||
| 	static auto constexpr POP = Instruction::POP; | ||||
| 	static auto constexpr MLOAD = Instruction::MLOAD; | ||||
| 	static auto constexpr MSTORE = Instruction::MSTORE; | ||||
| 	static auto constexpr MSTORE8 = Instruction::MSTORE8; | ||||
| 	static auto constexpr SLOAD = Instruction::SLOAD; | ||||
| 	static auto constexpr SSTORE = Instruction::SSTORE; | ||||
| 	static auto constexpr PC = Instruction::PC; | ||||
| 	static auto constexpr MSIZE = Instruction::MSIZE; | ||||
| 	static auto constexpr GAS = Instruction::GAS; | ||||
| 	static auto constexpr LOG0 = Instruction::LOG0; | ||||
| 	static auto constexpr LOG1 = Instruction::LOG1; | ||||
| 	static auto constexpr LOG2 = Instruction::LOG2; | ||||
| 	static auto constexpr LOG3 = Instruction::LOG3; | ||||
| 	static auto constexpr LOG4 = Instruction::LOG4; | ||||
| 	static auto constexpr CREATE = Instruction::CREATE; | ||||
| 	static auto constexpr CALL = Instruction::CALL; | ||||
| 	static auto constexpr CALLCODE = Instruction::CALLCODE; | ||||
| 	static auto constexpr STATICCALL = Instruction::STATICCALL; | ||||
| 	static auto constexpr RETURN = Instruction::RETURN; | ||||
| 	static auto constexpr DELEGATECALL = Instruction::DELEGATECALL; | ||||
| 	static auto constexpr CREATE2 = Instruction::CREATE2; | ||||
| 	static auto constexpr REVERT = Instruction::REVERT; | ||||
| 	static auto constexpr INVALID = Instruction::INVALID; | ||||
| 	static auto constexpr SELFDESTRUCT = Instruction::SELFDESTRUCT; | ||||
| 
 | ||||
| 	template<Instruction inst> | ||||
| 	struct PatternGenerator | ||||
| 	{ | ||||
| 		template<typename... Args> constexpr Pattern operator()(Args&&... _args) const | ||||
| 		{ | ||||
| 			return {inst, {std::forward<Args>(_args)...}}; | ||||
| 		}; | ||||
| 	}; | ||||
| 
 | ||||
| 	struct PatternGeneratorInstance | ||||
| 	{ | ||||
| 		Instruction instruction; | ||||
| 		template<typename... Args> constexpr Pattern operator()(Args&&... _args) const | ||||
| 		{ | ||||
| 			return {instruction, {std::forward<Args>(_args)...}}; | ||||
| 		}; | ||||
| 	}; | ||||
| 
 | ||||
| 
 | ||||
| 	static auto constexpr STOP = PatternGenerator<Instruction::STOP>{}; | ||||
| 	static auto constexpr ADD = PatternGenerator<Instruction::ADD>{}; | ||||
| 	static auto constexpr SUB = PatternGenerator<Instruction::SUB>{}; | ||||
| 	static auto constexpr MUL = PatternGenerator<Instruction::MUL>{}; | ||||
| 	static auto constexpr DIV = PatternGenerator<Instruction::DIV>{}; | ||||
| 	static auto constexpr SDIV = PatternGenerator<Instruction::SDIV>{}; | ||||
| 	static auto constexpr MOD = PatternGenerator<Instruction::MOD>{}; | ||||
| 	static auto constexpr SMOD = PatternGenerator<Instruction::SMOD>{}; | ||||
| 	static auto constexpr EXP = PatternGenerator<Instruction::EXP>{}; | ||||
| 	static auto constexpr NOT = PatternGenerator<Instruction::NOT>{}; | ||||
| 	static auto constexpr LT = PatternGenerator<Instruction::LT>{}; | ||||
| 	static auto constexpr GT = PatternGenerator<Instruction::GT>{}; | ||||
| 	static auto constexpr SLT = PatternGenerator<Instruction::SLT>{}; | ||||
| 	static auto constexpr SGT = PatternGenerator<Instruction::SGT>{}; | ||||
| 	static auto constexpr EQ = PatternGenerator<Instruction::EQ>{}; | ||||
| 	static auto constexpr ISZERO = PatternGenerator<Instruction::ISZERO>{}; | ||||
| 	static auto constexpr AND = PatternGenerator<Instruction::AND>{}; | ||||
| 	static auto constexpr OR = PatternGenerator<Instruction::OR>{}; | ||||
| 	static auto constexpr XOR = PatternGenerator<Instruction::XOR>{}; | ||||
| 	static auto constexpr BYTE = PatternGenerator<Instruction::BYTE>{}; | ||||
| 	static auto constexpr SHL = PatternGenerator<Instruction::SHL>{}; | ||||
| 	static auto constexpr SHR = PatternGenerator<Instruction::SHR>{}; | ||||
| 	static auto constexpr SAR = PatternGenerator<Instruction::SAR>{}; | ||||
| 	static auto constexpr ADDMOD = PatternGenerator<Instruction::ADDMOD>{}; | ||||
| 	static auto constexpr MULMOD = PatternGenerator<Instruction::MULMOD>{}; | ||||
| 	static auto constexpr SIGNEXTEND = PatternGenerator<Instruction::SIGNEXTEND>{}; | ||||
| 	static auto constexpr KECCAK256 = PatternGenerator<Instruction::KECCAK256>{}; | ||||
| 	static auto constexpr ADDRESS = PatternGenerator<Instruction::ADDRESS>{}; | ||||
| 	static auto constexpr BALANCE = PatternGenerator<Instruction::BALANCE>{}; | ||||
| 	static auto constexpr ORIGIN = PatternGenerator<Instruction::ORIGIN>{}; | ||||
| 	static auto constexpr CALLER = PatternGenerator<Instruction::CALLER>{}; | ||||
| 	static auto constexpr CALLVALUE = PatternGenerator<Instruction::CALLVALUE>{}; | ||||
| 	static auto constexpr CALLDATALOAD = PatternGenerator<Instruction::CALLDATALOAD>{}; | ||||
| 	static auto constexpr CALLDATASIZE = PatternGenerator<Instruction::CALLDATASIZE>{}; | ||||
| 	static auto constexpr CALLDATACOPY = PatternGenerator<Instruction::CALLDATACOPY>{}; | ||||
| 	static auto constexpr CODESIZE = PatternGenerator<Instruction::CODESIZE>{}; | ||||
| 	static auto constexpr CODECOPY = PatternGenerator<Instruction::CODECOPY>{}; | ||||
| 	static auto constexpr GASPRICE = PatternGenerator<Instruction::GASPRICE>{}; | ||||
| 	static auto constexpr EXTCODESIZE = PatternGenerator<Instruction::EXTCODESIZE>{}; | ||||
| 	static auto constexpr EXTCODECOPY = PatternGenerator<Instruction::EXTCODECOPY>{}; | ||||
| 	static auto constexpr RETURNDATASIZE = PatternGenerator<Instruction::RETURNDATASIZE>{}; | ||||
| 	static auto constexpr RETURNDATACOPY = PatternGenerator<Instruction::RETURNDATACOPY>{}; | ||||
| 	static auto constexpr EXTCODEHASH = PatternGenerator<Instruction::EXTCODEHASH>{}; | ||||
| 	static auto constexpr BLOCKHASH = PatternGenerator<Instruction::BLOCKHASH>{}; | ||||
| 	static auto constexpr COINBASE = PatternGenerator<Instruction::COINBASE>{}; | ||||
| 	static auto constexpr TIMESTAMP = PatternGenerator<Instruction::TIMESTAMP>{}; | ||||
| 	static auto constexpr NUMBER = PatternGenerator<Instruction::NUMBER>{}; | ||||
| 	static auto constexpr DIFFICULTY = PatternGenerator<Instruction::DIFFICULTY>{}; | ||||
| 	static auto constexpr GASLIMIT = PatternGenerator<Instruction::GASLIMIT>{}; | ||||
| 	static auto constexpr CHAINID = PatternGenerator<Instruction::CHAINID>{}; | ||||
| 	static auto constexpr SELFBALANCE = PatternGenerator<Instruction::SELFBALANCE>{}; | ||||
| 	static auto constexpr POP = PatternGenerator<Instruction::POP>{}; | ||||
| 	static auto constexpr MLOAD = PatternGenerator<Instruction::MLOAD>{}; | ||||
| 	static auto constexpr MSTORE = PatternGenerator<Instruction::MSTORE>{}; | ||||
| 	static auto constexpr MSTORE8 = PatternGenerator<Instruction::MSTORE8>{}; | ||||
| 	static auto constexpr SLOAD = PatternGenerator<Instruction::SLOAD>{}; | ||||
| 	static auto constexpr SSTORE = PatternGenerator<Instruction::SSTORE>{}; | ||||
| 	static auto constexpr PC = PatternGenerator<Instruction::PC>{}; | ||||
| 	static auto constexpr MSIZE = PatternGenerator<Instruction::MSIZE>{}; | ||||
| 	static auto constexpr GAS = PatternGenerator<Instruction::GAS>{}; | ||||
| 	static auto constexpr LOG0 = PatternGenerator<Instruction::LOG0>{}; | ||||
| 	static auto constexpr LOG1 = PatternGenerator<Instruction::LOG1>{}; | ||||
| 	static auto constexpr LOG2 = PatternGenerator<Instruction::LOG2>{}; | ||||
| 	static auto constexpr LOG3 = PatternGenerator<Instruction::LOG3>{}; | ||||
| 	static auto constexpr LOG4 = PatternGenerator<Instruction::LOG4>{}; | ||||
| 	static auto constexpr CREATE = PatternGenerator<Instruction::CREATE>{}; | ||||
| 	static auto constexpr CALL = PatternGenerator<Instruction::CALL>{}; | ||||
| 	static auto constexpr CALLCODE = PatternGenerator<Instruction::CALLCODE>{}; | ||||
| 	static auto constexpr STATICCALL = PatternGenerator<Instruction::STATICCALL>{}; | ||||
| 	static auto constexpr RETURN = PatternGenerator<Instruction::RETURN>{}; | ||||
| 	static auto constexpr DELEGATECALL = PatternGenerator<Instruction::DELEGATECALL>{}; | ||||
| 	static auto constexpr CREATE2 = PatternGenerator<Instruction::CREATE2>{}; | ||||
| 	static auto constexpr REVERT = PatternGenerator<Instruction::REVERT>{}; | ||||
| 	static auto constexpr INVALID = PatternGenerator<Instruction::INVALID>{}; | ||||
| 	static auto constexpr SELFDESTRUCT = PatternGenerator<Instruction::SELFDESTRUCT>{}; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -99,7 +99,7 @@ Rules::Rules() | ||||
| 	assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); | ||||
| } | ||||
| 
 | ||||
| Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments): | ||||
| Pattern::Pattern(Instruction _instruction, std::initializer_list<Pattern> _arguments): | ||||
| 	m_type(Operation), | ||||
| 	m_instruction(_instruction), | ||||
| 	m_arguments(_arguments) | ||||
|  | ||||
| @ -26,6 +26,8 @@ | ||||
| #include <libevmasm/ExpressionClasses.h> | ||||
| #include <libevmasm/SimplificationRule.h> | ||||
| 
 | ||||
| #include <libdevcore/CommonData.h> | ||||
| 
 | ||||
| #include <boost/noncopyable.hpp> | ||||
| 
 | ||||
| #include <functional> | ||||
| @ -87,18 +89,20 @@ public: | ||||
| 	using Expression = ExpressionClasses::Expression; | ||||
| 	using Id = ExpressionClasses::Id; | ||||
| 
 | ||||
| 	using Builtins = dev::eth::EVMBuiltins; | ||||
| 	using Builtins = dev::eth::EVMBuiltins<Pattern>; | ||||
| 	static constexpr size_t WordSize = 256; | ||||
| 	using Word = u256; | ||||
| 
 | ||||
| 	// Matches a specific constant value.
 | ||||
| 	Pattern(unsigned _value): Pattern(u256(_value)) {} | ||||
| 	Pattern(int _value): Pattern(u256(_value)) {} | ||||
| 	Pattern(long unsigned _value): Pattern(u256(_value)) {} | ||||
| 	// Matches a specific constant value.
 | ||||
| 	Pattern(u256 const& _value): m_type(Push), m_requireDataMatch(true), m_data(std::make_shared<u256>(_value)) {} | ||||
| 	// Matches a specific assembly item type or anything if not given.
 | ||||
| 	Pattern(AssemblyItemType _type = UndefinedItem): m_type(_type) {} | ||||
| 	// Matches a given instruction with given arguments
 | ||||
| 	Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments = {}); | ||||
| 	Pattern(Instruction _instruction, std::initializer_list<Pattern> _arguments = {}); | ||||
| 	/// Sets this pattern to be part of the match group with the identifier @a _group.
 | ||||
| 	/// Inside one rule, all patterns in the same match group have to match expressions from the
 | ||||
| 	/// same expression equivalence class.
 | ||||
|  | ||||
| @ -98,7 +98,7 @@ private: | ||||
| 
 | ||||
| 	EVMVersion(Version _version): m_version(_version) {} | ||||
| 
 | ||||
| 	Version m_version = Version::Petersburg; | ||||
| 	Version m_version = Version::Istanbul; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -798,7 +798,7 @@ Token Scanner::scanHexString() | ||||
| 
 | ||||
| 	literal.complete(); | ||||
| 	advance();  // consume quote
 | ||||
| 	return Token::StringLiteral; | ||||
| 	return Token::HexStringLiteral; | ||||
| } | ||||
| 
 | ||||
| // Parse for regex [:digit:]+(_[:digit:]+)*
 | ||||
|  | ||||
| @ -88,7 +88,7 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio | ||||
| 		if (!comp.matches(_version)) | ||||
| 			return false; | ||||
| 
 | ||||
| 		if (comp.version.numbers[0] == 0) | ||||
| 		if (comp.version.numbers[0] == 0 && comp.levelsPresent != 1) | ||||
| 			comp.levelsPresent = 2; | ||||
| 		else | ||||
| 			comp.levelsPresent = 1; | ||||
| @ -105,6 +105,7 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio | ||||
| 				didCompare = true; | ||||
| 				cmp = _version.numbers[i] - version.numbers[i]; | ||||
| 			} | ||||
| 
 | ||||
| 		if (cmp == 0 && !_version.prerelease.empty() && didCompare) | ||||
| 			cmp = -1; | ||||
| 
 | ||||
|  | ||||
| @ -221,6 +221,7 @@ namespace langutil | ||||
| 	K(FalseLiteral, "false", 0)                                        \ | ||||
| 	T(Number, nullptr, 0)                                              \ | ||||
| 	T(StringLiteral, nullptr, 0)                                       \ | ||||
| 	T(HexStringLiteral, nullptr, 0)                                    \ | ||||
| 	T(CommentLiteral, nullptr, 0)                                      \ | ||||
| 	\ | ||||
| 	/* Identifiers (not keywords or future reserved words). */         \ | ||||
|  | ||||
| @ -41,6 +41,8 @@ set(sources | ||||
| 	ast/ASTJsonConverter.h | ||||
| 	ast/ASTPrinter.cpp | ||||
| 	ast/ASTPrinter.h | ||||
| 	ast/ASTUtils.cpp | ||||
| 	ast/ASTUtils.h | ||||
| 	ast/ASTVisitor.h | ||||
| 	ast/ExperimentalFeatures.h | ||||
| 	ast/Types.cpp | ||||
|  | ||||
| @ -35,7 +35,7 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow( | ||||
| 	FunctionDefinition const& _function | ||||
| ) | ||||
| { | ||||
| 	auto functionFlow = unique_ptr<FunctionFlow>(new FunctionFlow()); | ||||
| 	auto functionFlow = make_unique<FunctionFlow>(); | ||||
| 	functionFlow->entry = _nodeContainer.newNode(); | ||||
| 	functionFlow->exit = _nodeContainer.newNode(); | ||||
| 	functionFlow->revert = _nodeContainer.newNode(); | ||||
|  | ||||
| @ -48,7 +48,7 @@ NameAndTypeResolver::NameAndTypeResolver( | ||||
| 	m_globalContext(_globalContext) | ||||
| { | ||||
| 	if (!m_scopes[nullptr]) | ||||
| 		m_scopes[nullptr].reset(new DeclarationContainer()); | ||||
| 		m_scopes[nullptr] = make_shared<DeclarationContainer>(); | ||||
| 	for (Declaration const* declaration: _globalContext.declarations()) | ||||
| 	{ | ||||
| 		solAssert(m_scopes[nullptr]->registerDeclaration(*declaration), "Unable to register global declaration."); | ||||
| @ -545,7 +545,7 @@ bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit) | ||||
| { | ||||
| 	if (!m_scopes[&_sourceUnit]) | ||||
| 		// By importing, it is possible that the container already exists.
 | ||||
| 		m_scopes[&_sourceUnit].reset(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); | ||||
| 		m_scopes[&_sourceUnit] = make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get()); | ||||
| 	m_currentScope = &_sourceUnit; | ||||
| 	return true; | ||||
| } | ||||
| @ -561,7 +561,7 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import) | ||||
| 	SourceUnit const* importee = _import.annotation().sourceUnit; | ||||
| 	solAssert(!!importee, ""); | ||||
| 	if (!m_scopes[importee]) | ||||
| 		m_scopes[importee].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get())); | ||||
| 		m_scopes[importee] = make_shared<DeclarationContainer>(nullptr, m_scopes[nullptr].get()); | ||||
| 	m_scopes[&_import] = m_scopes[importee]; | ||||
| 	registerDeclaration(_import, false); | ||||
| 	return true; | ||||
| @ -705,7 +705,7 @@ void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) | ||||
| { | ||||
| 	map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter; | ||||
| 	bool newlyAdded; | ||||
| 	shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); | ||||
| 	shared_ptr<DeclarationContainer> container{make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get())}; | ||||
| 	tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container)); | ||||
| 	solAssert(newlyAdded, "Unable to add new scope."); | ||||
| 	m_currentScope = &_subScope; | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| 
 | ||||
| #include <libsolidity/analysis/TypeChecker.h> | ||||
| #include <libsolidity/ast/AST.h> | ||||
| #include <libsolidity/ast/ASTUtils.h> | ||||
| #include <libsolidity/ast/TypeProvider.h> | ||||
| 
 | ||||
| #include <libyul/AsmAnalysis.h> | ||||
| @ -641,6 +642,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) | ||||
| 			solAssert(var->type(), "Expected variable type!"); | ||||
| 			if (var->isConstant()) | ||||
| 			{ | ||||
| 				var = rootVariableDeclaration(*var); | ||||
| 
 | ||||
| 				if (!var->value()) | ||||
| 				{ | ||||
| 					m_errorReporter.typeError(_identifier.location, "Constant has no value."); | ||||
| @ -651,7 +654,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) | ||||
| 					type(*var->value())->category() != Type::Category::RationalNumber | ||||
| 				)) | ||||
| 				{ | ||||
| 					m_errorReporter.typeError(_identifier.location, "Only direct number constants are supported by inline assembly."); | ||||
| 					m_errorReporter.typeError(_identifier.location, "Only direct number constants and references to such constants are supported by inline assembly."); | ||||
| 					return size_t(-1); | ||||
| 				} | ||||
| 				else if (_context == yul::IdentifierContext::LValue) | ||||
|  | ||||
| @ -21,7 +21,9 @@ | ||||
| #include <libyul/backends/evm/EVMDialect.h> | ||||
| #include <liblangutil/ErrorReporter.h> | ||||
| #include <libevmasm/SemanticInformation.h> | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <variant> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace dev; | ||||
| @ -31,7 +33,7 @@ using namespace dev::solidity; | ||||
| namespace | ||||
| { | ||||
| 
 | ||||
| class AssemblyViewPureChecker: public boost::static_visitor<void> | ||||
| class AssemblyViewPureChecker | ||||
| { | ||||
| public: | ||||
| 	explicit AssemblyViewPureChecker( | ||||
| @ -52,21 +54,21 @@ public: | ||||
| 	{ | ||||
| 		checkInstruction(_instr.location, _instr.instruction); | ||||
| 		for (auto const& arg: _instr.arguments) | ||||
| 			boost::apply_visitor(*this, arg); | ||||
| 			std::visit(*this, arg); | ||||
| 	} | ||||
| 	void operator()(yul::ExpressionStatement const& _expr) | ||||
| 	{ | ||||
| 		boost::apply_visitor(*this, _expr.expression); | ||||
| 		std::visit(*this, _expr.expression); | ||||
| 	} | ||||
| 	void operator()(yul::StackAssignment const&) {} | ||||
| 	void operator()(yul::Assignment const& _assignment) | ||||
| 	{ | ||||
| 		boost::apply_visitor(*this, *_assignment.value); | ||||
| 		std::visit(*this, *_assignment.value); | ||||
| 	} | ||||
| 	void operator()(yul::VariableDeclaration const& _varDecl) | ||||
| 	{ | ||||
| 		if (_varDecl.value) | ||||
| 			boost::apply_visitor(*this, *_varDecl.value); | ||||
| 			std::visit(*this, *_varDecl.value); | ||||
| 	} | ||||
| 	void operator()(yul::FunctionDefinition const& _funDef) | ||||
| 	{ | ||||
| @ -80,16 +82,16 @@ public: | ||||
| 					checkInstruction(_funCall.location, *fun->instruction); | ||||
| 
 | ||||
| 		for (auto const& arg: _funCall.arguments) | ||||
| 			boost::apply_visitor(*this, arg); | ||||
| 			std::visit(*this, arg); | ||||
| 	} | ||||
| 	void operator()(yul::If const& _if) | ||||
| 	{ | ||||
| 		boost::apply_visitor(*this, *_if.condition); | ||||
| 		std::visit(*this, *_if.condition); | ||||
| 		(*this)(_if.body); | ||||
| 	} | ||||
| 	void operator()(yul::Switch const& _switch) | ||||
| 	{ | ||||
| 		boost::apply_visitor(*this, *_switch.expression); | ||||
| 		std::visit(*this, *_switch.expression); | ||||
| 		for (auto const& _case: _switch.cases) | ||||
| 		{ | ||||
| 			if (_case.value) | ||||
| @ -100,7 +102,7 @@ public: | ||||
| 	void operator()(yul::ForLoop const& _for) | ||||
| 	{ | ||||
| 		(*this)(_for.pre); | ||||
| 		boost::apply_visitor(*this, *_for.condition); | ||||
| 		std::visit(*this, *_for.condition); | ||||
| 		(*this)(_for.body); | ||||
| 		(*this)(_for.post); | ||||
| 	} | ||||
| @ -113,7 +115,7 @@ public: | ||||
| 	void operator()(yul::Block const& _block) | ||||
| 	{ | ||||
| 		for (auto const& s: _block.statements) | ||||
| 			boost::apply_visitor(*this, s); | ||||
| 			std::visit(*this, s); | ||||
| 	} | ||||
| 
 | ||||
| private: | ||||
|  | ||||
| @ -55,11 +55,6 @@ ASTNode::ASTNode(SourceLocation const& _location): | ||||
| { | ||||
| } | ||||
| 
 | ||||
| ASTNode::~ASTNode() | ||||
| { | ||||
| 	delete m_annotation; | ||||
| } | ||||
| 
 | ||||
| void ASTNode::resetID() | ||||
| { | ||||
| 	IDDispenser::reset(); | ||||
| @ -68,14 +63,14 @@ void ASTNode::resetID() | ||||
| ASTAnnotation& ASTNode::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new ASTAnnotation(); | ||||
| 		m_annotation = make_unique<ASTAnnotation>(); | ||||
| 	return *m_annotation; | ||||
| } | ||||
| 
 | ||||
| SourceUnitAnnotation& SourceUnit::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new SourceUnitAnnotation(); | ||||
| 		m_annotation = make_unique<SourceUnitAnnotation>(); | ||||
| 	return dynamic_cast<SourceUnitAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| @ -99,7 +94,7 @@ set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<Sour | ||||
| ImportAnnotation& ImportDirective::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new ImportAnnotation(); | ||||
| 		m_annotation = make_unique<ImportAnnotation>(); | ||||
| 	return dynamic_cast<ImportAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| @ -168,7 +163,7 @@ vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() cons | ||||
| 	if (!m_interfaceEvents) | ||||
| 	{ | ||||
| 		set<string> eventsSeen; | ||||
| 		m_interfaceEvents.reset(new vector<EventDefinition const*>()); | ||||
| 		m_interfaceEvents = make_unique<vector<EventDefinition const*>>(); | ||||
| 		for (ContractDefinition const* contract: annotation().linearizedBaseContracts) | ||||
| 			for (EventDefinition const* e: contract->events()) | ||||
| 			{ | ||||
| @ -193,7 +188,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter | ||||
| 	if (!m_interfaceFunctionList) | ||||
| 	{ | ||||
| 		set<string> signaturesSeen; | ||||
| 		m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>()); | ||||
| 		m_interfaceFunctionList = make_unique<vector<pair<FixedHash<4>, FunctionTypePointer>>>(); | ||||
| 		for (ContractDefinition const* contract: annotation().linearizedBaseContracts) | ||||
| 		{ | ||||
| 			vector<FunctionTypePointer> functions; | ||||
| @ -225,7 +220,7 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const | ||||
| { | ||||
| 	if (!m_inheritableMembers) | ||||
| 	{ | ||||
| 		m_inheritableMembers.reset(new vector<Declaration const*>()); | ||||
| 		m_inheritableMembers = make_unique<vector<Declaration const*>>(); | ||||
| 		auto addInheritableMember = [&](Declaration const* _decl) | ||||
| 		{ | ||||
| 			solAssert(_decl, "addInheritableMember got a nullpointer."); | ||||
| @ -259,14 +254,14 @@ TypePointer ContractDefinition::type() const | ||||
| ContractDefinitionAnnotation& ContractDefinition::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new ContractDefinitionAnnotation(); | ||||
| 		m_annotation = make_unique<ContractDefinitionAnnotation>(); | ||||
| 	return dynamic_cast<ContractDefinitionAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| TypeNameAnnotation& TypeName::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new TypeNameAnnotation(); | ||||
| 		m_annotation = make_unique<TypeNameAnnotation>(); | ||||
| 	return dynamic_cast<TypeNameAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| @ -278,7 +273,7 @@ TypePointer StructDefinition::type() const | ||||
| TypeDeclarationAnnotation& StructDefinition::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new TypeDeclarationAnnotation(); | ||||
| 		m_annotation = make_unique<TypeDeclarationAnnotation>(); | ||||
| 	return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| @ -297,7 +292,7 @@ TypePointer EnumDefinition::type() const | ||||
| TypeDeclarationAnnotation& EnumDefinition::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new TypeDeclarationAnnotation(); | ||||
| 		m_annotation = make_unique<TypeDeclarationAnnotation>(); | ||||
| 	return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| @ -357,7 +352,7 @@ string FunctionDefinition::externalSignature() const | ||||
| FunctionDefinitionAnnotation& FunctionDefinition::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new FunctionDefinitionAnnotation(); | ||||
| 		m_annotation = make_unique<FunctionDefinitionAnnotation>(); | ||||
| 	return dynamic_cast<FunctionDefinitionAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| @ -369,7 +364,7 @@ TypePointer ModifierDefinition::type() const | ||||
| ModifierDefinitionAnnotation& ModifierDefinition::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new ModifierDefinitionAnnotation(); | ||||
| 		m_annotation = make_unique<ModifierDefinitionAnnotation>(); | ||||
| 	return dynamic_cast<ModifierDefinitionAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| @ -389,14 +384,14 @@ FunctionTypePointer EventDefinition::functionType(bool _internal) const | ||||
| EventDefinitionAnnotation& EventDefinition::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new EventDefinitionAnnotation(); | ||||
| 		m_annotation = make_unique<EventDefinitionAnnotation>(); | ||||
| 	return dynamic_cast<EventDefinitionAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new UserDefinedTypeNameAnnotation(); | ||||
| 		m_annotation = make_unique<UserDefinedTypeNameAnnotation>(); | ||||
| 	return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| @ -604,63 +599,63 @@ FunctionTypePointer VariableDeclaration::functionType(bool _internal) const | ||||
| VariableDeclarationAnnotation& VariableDeclaration::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new VariableDeclarationAnnotation(); | ||||
| 		m_annotation = make_unique<VariableDeclarationAnnotation>(); | ||||
| 	return dynamic_cast<VariableDeclarationAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| StatementAnnotation& Statement::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new StatementAnnotation(); | ||||
| 		m_annotation = make_unique<StatementAnnotation>(); | ||||
| 	return dynamic_cast<StatementAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| InlineAssemblyAnnotation& InlineAssembly::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new InlineAssemblyAnnotation(); | ||||
| 		m_annotation = make_unique<InlineAssemblyAnnotation>(); | ||||
| 	return dynamic_cast<InlineAssemblyAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| ReturnAnnotation& Return::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new ReturnAnnotation(); | ||||
| 		m_annotation = make_unique<ReturnAnnotation>(); | ||||
| 	return dynamic_cast<ReturnAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| ExpressionAnnotation& Expression::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new ExpressionAnnotation(); | ||||
| 		m_annotation = make_unique<ExpressionAnnotation>(); | ||||
| 	return dynamic_cast<ExpressionAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| MemberAccessAnnotation& MemberAccess::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new MemberAccessAnnotation(); | ||||
| 		m_annotation = make_unique<MemberAccessAnnotation>(); | ||||
| 	return dynamic_cast<MemberAccessAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| BinaryOperationAnnotation& BinaryOperation::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new BinaryOperationAnnotation(); | ||||
| 		m_annotation = make_unique<BinaryOperationAnnotation>(); | ||||
| 	return dynamic_cast<BinaryOperationAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| FunctionCallAnnotation& FunctionCall::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new FunctionCallAnnotation(); | ||||
| 		m_annotation = make_unique<FunctionCallAnnotation>(); | ||||
| 	return dynamic_cast<FunctionCallAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
| IdentifierAnnotation& Identifier::annotation() const | ||||
| { | ||||
| 	if (!m_annotation) | ||||
| 		m_annotation = new IdentifierAnnotation(); | ||||
| 		m_annotation = make_unique<IdentifierAnnotation>(); | ||||
| 	return dynamic_cast<IdentifierAnnotation&>(*m_annotation); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -67,7 +67,7 @@ public: | ||||
| 	using SourceLocation = langutil::SourceLocation; | ||||
| 
 | ||||
| 	explicit ASTNode(SourceLocation const& _location); | ||||
| 	virtual ~ASTNode(); | ||||
| 	virtual ~ASTNode() {} | ||||
| 
 | ||||
| 	/// @returns an identifier of this AST node that is unique for a single compilation run.
 | ||||
| 	size_t id() const { return m_id; } | ||||
| @ -111,7 +111,7 @@ public: | ||||
| protected: | ||||
| 	size_t const m_id = 0; | ||||
| 	/// Annotation - is specialised in derived classes, is created upon request (because of polymorphism).
 | ||||
| 	mutable ASTAnnotation* m_annotation = nullptr; | ||||
| 	mutable std::unique_ptr<ASTAnnotation> m_annotation; | ||||
| 
 | ||||
| private: | ||||
| 	SourceLocation m_location; | ||||
|  | ||||
| @ -801,6 +801,7 @@ string ASTJsonConverter::literalTokenKind(Token _token) | ||||
| 	case dev::solidity::Token::Number: | ||||
| 		return "number"; | ||||
| 	case dev::solidity::Token::StringLiteral: | ||||
| 	case dev::solidity::Token::HexStringLiteral: | ||||
| 		return "string"; | ||||
| 	case dev::solidity::Token::TrueLiteral: | ||||
| 	case dev::solidity::Token::FalseLiteral: | ||||
|  | ||||
							
								
								
									
										42
									
								
								libsolidity/ast/ASTUtils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								libsolidity/ast/ASTUtils.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| /*
 | ||||
| 	This file is part of solidity. | ||||
| 
 | ||||
| 	solidity is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	solidity is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| #include <libsolidity/ast/AST.h> | ||||
| #include <libsolidity/ast/ASTUtils.h> | ||||
| 
 | ||||
| namespace dev | ||||
| { | ||||
| namespace solidity | ||||
| { | ||||
| 
 | ||||
| VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl) | ||||
| { | ||||
| 	solAssert(_varDecl.isConstant(), "Constant variable expected"); | ||||
| 
 | ||||
| 	VariableDeclaration const* rootDecl = &_varDecl; | ||||
| 	Identifier const* identifier; | ||||
| 	while ((identifier = dynamic_cast<Identifier const*>(rootDecl->value().get()))) | ||||
| 	{ | ||||
| 		auto referencedVarDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration); | ||||
| 		solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration"); | ||||
| 		rootDecl = referencedVarDecl; | ||||
| 	} | ||||
| 	return rootDecl; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| } | ||||
							
								
								
									
										30
									
								
								libsolidity/ast/ASTUtils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								libsolidity/ast/ASTUtils.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| /*
 | ||||
| 	This file is part of solidity. | ||||
| 
 | ||||
| 	solidity is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	solidity is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| namespace dev | ||||
| { | ||||
| namespace solidity | ||||
| { | ||||
| 
 | ||||
| /// Find the topmost referenced variable declaration when the given variable
 | ||||
| /// declaration value is an identifier. Works only for constant variable declarations.
 | ||||
| VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl); | ||||
| 
 | ||||
| } | ||||
| } | ||||
| @ -331,6 +331,7 @@ TypePointer TypeProvider::forLiteral(Literal const& _literal) | ||||
| 	case Token::Number: | ||||
| 		return rationalNumber(_literal); | ||||
| 	case Token::StringLiteral: | ||||
| 	case Token::HexStringLiteral: | ||||
| 		return stringLiteral(_literal.value()); | ||||
| 	default: | ||||
| 		return nullptr; | ||||
|  | ||||
| @ -329,7 +329,7 @@ MemberList const& Type::members(ContractDefinition const* _currentScope) const | ||||
| 		MemberList::MemberMap members = nativeMembers(_currentScope); | ||||
| 		if (_currentScope) | ||||
| 			members += boundFunctions(*this, *_currentScope); | ||||
| 		m_members[_currentScope] = unique_ptr<MemberList>(new MemberList(move(members))); | ||||
| 		m_members[_currentScope] = make_unique<MemberList>(move(members)); | ||||
| 	} | ||||
| 	return *m_members[_currentScope]; | ||||
| } | ||||
| @ -1433,6 +1433,9 @@ Type const* ContractType::encodingType() const | ||||
| 
 | ||||
| BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const | ||||
| { | ||||
| 	if (m_super) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (*this == _convertTo) | ||||
| 		return true; | ||||
| 	if (_convertTo.category() == Category::Contract) | ||||
| @ -1450,8 +1453,12 @@ BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const | ||||
| 
 | ||||
| BoolResult ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const | ||||
| { | ||||
| 	if (m_super) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo)) | ||||
| 		return isPayable() || (addressType->stateMutability() < StateMutability::Payable); | ||||
| 
 | ||||
| 	return isImplicitlyConvertibleTo(_convertTo); | ||||
| } | ||||
| 
 | ||||
| @ -2962,6 +2969,20 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con | ||||
| 			); | ||||
| 		return members; | ||||
| 	} | ||||
| 	case Kind::DelegateCall: | ||||
| 	{ | ||||
| 		auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration); | ||||
| 		solAssert(functionDefinition, ""); | ||||
| 		solAssert(functionDefinition->visibility() != Declaration::Visibility::Private, ""); | ||||
| 		if (functionDefinition->visibility() != Declaration::Visibility::Internal) | ||||
| 		{ | ||||
| 			auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope()); | ||||
| 			solAssert(contract, ""); | ||||
| 			solAssert(contract->isLibrary(), ""); | ||||
| 			return {{"selector", TypeProvider::fixedBytes(4)}}; | ||||
| 		} | ||||
| 		return {}; | ||||
| 	} | ||||
| 	default: | ||||
| 		return MemberList::MemberMap(); | ||||
| 	} | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
| #include <libsolidity/codegen/ABIFunctions.h> | ||||
| 
 | ||||
| #include <libsolidity/codegen/CompilerUtils.h> | ||||
| #include <libdevcore/CommonData.h> | ||||
| #include <libdevcore/Whiskers.h> | ||||
| #include <libdevcore/StringUtils.h> | ||||
| 
 | ||||
| @ -975,7 +976,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( | ||||
| 			for (size_t i = 0; i < words; ++i) | ||||
| 			{ | ||||
| 				wordParams[i]["offset"] = to_string(i * 32); | ||||
| 				wordParams[i]["wordValue"] = "0x" + h256(value.substr(32 * i, 32), h256::AlignLeft).hex(); | ||||
| 				wordParams[i]["wordValue"] = formatAsStringOrNumber(value.substr(32 * i, 32)); | ||||
| 			} | ||||
| 			templ("word", wordParams); | ||||
| 			return templ.render(); | ||||
| @ -990,7 +991,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( | ||||
| 				} | ||||
| 			)"); | ||||
| 			templ("functionName", functionName); | ||||
| 			templ("wordValue", "0x" + h256(value, h256::AlignLeft).hex()); | ||||
| 			templ("wordValue", formatAsStringOrNumber(value)); | ||||
| 			return templ.render(); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| @ -438,7 +438,7 @@ void CompilerContext::appendInlineAssembly( | ||||
| 		parserResult = std::move(obj.code); | ||||
| 
 | ||||
| #ifdef SOL_OUTPUT_ASM | ||||
| 		cout << "After optimizer: " << endl; | ||||
| 		cout << "After optimizer:" << endl; | ||||
| 		cout << yul::AsmPrinter()(*parserResult) << endl; | ||||
| #endif | ||||
| 	} | ||||
|  | ||||
| @ -711,6 +711,9 @@ void CompilerUtils::convertType( | ||||
| 	Type::Category stackTypeCategory = _typeOnStack.category(); | ||||
| 	Type::Category targetTypeCategory = _targetType.category(); | ||||
| 
 | ||||
| 	if (auto contrType = dynamic_cast<ContractType const*>(&_typeOnStack)) | ||||
| 		solAssert(!contrType->isSuper(), "Cannot convert magic variable \"super\""); | ||||
| 
 | ||||
| 	bool enumOverflowCheckPending = (targetTypeCategory == Type::Category::Enum || stackTypeCategory == Type::Category::Enum); | ||||
| 	bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer; | ||||
| 	if (chopSignBitsPending) | ||||
|  | ||||
| @ -21,6 +21,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include <libsolidity/ast/AST.h> | ||||
| #include <libsolidity/ast/ASTUtils.h> | ||||
| #include <libsolidity/ast/TypeProvider.h> | ||||
| #include <libsolidity/codegen/CompilerUtils.h> | ||||
| #include <libsolidity/codegen/ContractCompiler.h> | ||||
| @ -631,6 +632,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) | ||||
| 			{ | ||||
| 				if (variable->isConstant()) | ||||
| 				{ | ||||
| 					variable = rootVariableDeclaration(*variable); | ||||
| 					u256 value; | ||||
| 					if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) | ||||
| 					{ | ||||
|  | ||||
| @ -345,7 +345,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) | ||||
| 			if (_tuple.components().size() == 1) | ||||
| 				m_currentLValue = move(lvalues[0]); | ||||
| 			else | ||||
| 				m_currentLValue.reset(new TupleObject(m_context, move(lvalues))); | ||||
| 				m_currentLValue = make_unique<TupleObject>(m_context, move(lvalues)); | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
|  | ||||
| @ -136,7 +136,7 @@ template <class _LValueType, class... _Arguments> | ||||
| void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments const&... _arguments) | ||||
| { | ||||
| 	solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one."); | ||||
| 	std::unique_ptr<_LValueType> lvalue(new _LValueType(m_context, _arguments...)); | ||||
| 	std::unique_ptr<_LValueType> lvalue = std::make_unique<_LValueType>(m_context, _arguments...); | ||||
| 	if (_expression.annotation().lValueRequested) | ||||
| 		m_currentLValue = move(lvalue); | ||||
| 	else | ||||
|  | ||||
| @ -23,6 +23,8 @@ | ||||
| #include <libsolidity/codegen/MultiUseYulFunctionCollector.h> | ||||
| #include <libsolidity/ast/AST.h> | ||||
| #include <libsolidity/codegen/CompilerUtils.h> | ||||
| 
 | ||||
| #include <libdevcore/CommonData.h> | ||||
| #include <libdevcore/Whiskers.h> | ||||
| #include <libdevcore/StringUtils.h> | ||||
| 
 | ||||
| @ -1756,7 +1758,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const | ||||
| 			for (size_t i = 0; i < words; ++i) | ||||
| 			{ | ||||
| 				wordParams[i]["offset"] = to_string(32 + i * 32); | ||||
| 				wordParams[i]["wordValue"] = "0x" + h256(data.substr(32 * i, 32), h256::AlignLeft).hex(); | ||||
| 				wordParams[i]["wordValue"] = formatAsStringOrNumber(data.substr(32 * i, 32)); | ||||
| 			} | ||||
| 			templ("word", wordParams); | ||||
| 			return templ.render(); | ||||
|  | ||||
| @ -833,9 +833,9 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) | ||||
| 
 | ||||
| 	yul::Statement modified = bodyCopier(_inlineAsm.operations()); | ||||
| 
 | ||||
| 	solAssert(modified.type() == typeid(yul::Block), ""); | ||||
| 	solAssert(holds_alternative<yul::Block>(modified), ""); | ||||
| 
 | ||||
| 	m_code << yul::AsmPrinter()(boost::get<yul::Block>(std::move(modified))) << "\n"; | ||||
| 	m_code << yul::AsmPrinter()(std::get<yul::Block>(std::move(modified))) << "\n"; | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -27,19 +27,25 @@ using namespace dev; | ||||
| using namespace langutil; | ||||
| using namespace dev::solidity; | ||||
| 
 | ||||
| BMC::BMC(smt::EncodingContext& _context, ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses): | ||||
| BMC::BMC( | ||||
| 	smt::EncodingContext& _context, | ||||
| 	ErrorReporter& _errorReporter, | ||||
| 	map<h256, string> const& _smtlib2Responses, | ||||
| 	smt::SMTSolverChoice _enabledSolvers | ||||
| ): | ||||
| 	SMTEncoder(_context), | ||||
| 	m_outerErrorReporter(_errorReporter), | ||||
| 	m_interface(make_shared<smt::SMTPortfolio>(_smtlib2Responses)) | ||||
| 	m_interface(make_shared<smt::SMTPortfolio>(_smtlib2Responses, _enabledSolvers)) | ||||
| { | ||||
| #if defined (HAVE_Z3) || defined (HAVE_CVC4) | ||||
| 	if (!_smtlib2Responses.empty()) | ||||
| 		m_errorReporter.warning( | ||||
| 			"SMT-LIB2 query responses were given in the auxiliary input, " | ||||
| 			"but this Solidity binary uses an SMT solver (Z3/CVC4) directly." | ||||
| 			"These responses will be ignored." | ||||
| 			"Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses." | ||||
| 		); | ||||
| 	if (_enabledSolvers.some()) | ||||
| 		if (!_smtlib2Responses.empty()) | ||||
| 			m_errorReporter.warning( | ||||
| 				"SMT-LIB2 query responses were given in the auxiliary input, " | ||||
| 				"but this Solidity binary uses an SMT solver (Z3/CVC4) directly." | ||||
| 				"These responses will be ignored." | ||||
| 				"Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses." | ||||
| 			); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| @ -110,11 +116,6 @@ bool BMC::visit(ContractDefinition const& _contract) | ||||
| { | ||||
| 	initContract(_contract); | ||||
| 
 | ||||
| 	/// Check targets created by state variable initialization.
 | ||||
| 	smt::Expression constraints = m_context.assertions(); | ||||
| 	checkVerificationTargets(constraints); | ||||
| 	m_verificationTargets.clear(); | ||||
| 
 | ||||
| 	SMTEncoder::visit(_contract); | ||||
| 
 | ||||
| 	return false; | ||||
| @ -122,6 +123,17 @@ bool BMC::visit(ContractDefinition const& _contract) | ||||
| 
 | ||||
| void BMC::endVisit(ContractDefinition const& _contract) | ||||
| { | ||||
| 	if (auto constructor = _contract.constructor()) | ||||
| 		constructor->accept(*this); | ||||
| 	else | ||||
| 	{ | ||||
| 		inlineConstructorHierarchy(_contract); | ||||
| 		/// Check targets created by state variable initialization.
 | ||||
| 		smt::Expression constraints = m_context.assertions(); | ||||
| 		checkVerificationTargets(constraints); | ||||
| 		m_verificationTargets.clear(); | ||||
| 	} | ||||
| 
 | ||||
| 	SMTEncoder::endVisit(_contract); | ||||
| } | ||||
| 
 | ||||
| @ -132,10 +144,14 @@ bool BMC::visit(FunctionDefinition const& _function) | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 	auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; | ||||
| 	if (find(hierarchy.begin(), hierarchy.end(), contract) == hierarchy.end()) | ||||
| 		initializeStateVariables(*contract); | ||||
| 		createStateVariables(*contract); | ||||
| 
 | ||||
| 	if (m_callStack.empty()) | ||||
| 	{ | ||||
| 		reset(); | ||||
| 		initFunction(_function); | ||||
| 		resetStateVariables(); | ||||
| 	} | ||||
| 
 | ||||
| 	/// Already visits the children.
 | ||||
| 	SMTEncoder::visit(_function); | ||||
| @ -447,10 +463,6 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) | ||||
| 		// The reason why we need to pushCallStack here instead of visit(FunctionDefinition)
 | ||||
| 		// is that there we don't have `_funCall`.
 | ||||
| 		pushCallStack({funDef, &_funCall}); | ||||
| 		// If an internal function is called to initialize
 | ||||
| 		// a state variable.
 | ||||
| 		if (m_callStack.empty()) | ||||
| 			initFunction(*funDef); | ||||
| 		funDef->accept(*this); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -53,7 +53,12 @@ namespace solidity | ||||
| class BMC: public SMTEncoder | ||||
| { | ||||
| public: | ||||
| 	BMC(smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter, std::map<h256, std::string> const& _smtlib2Responses); | ||||
| 	BMC( | ||||
| 		smt::EncodingContext& _context, | ||||
| 		langutil::ErrorReporter& _errorReporter, | ||||
| 		std::map<h256, std::string> const& _smtlib2Responses, | ||||
| 		smt::SMTSolverChoice _enabledSolvers | ||||
| 	); | ||||
| 
 | ||||
| 	void analyze(SourceUnit const& _sources, std::set<Expression const*> _safeAssertions); | ||||
| 
 | ||||
|  | ||||
| @ -35,17 +35,23 @@ using namespace dev::solidity; | ||||
| CHC::CHC( | ||||
| 	smt::EncodingContext& _context, | ||||
| 	ErrorReporter& _errorReporter, | ||||
| 	map<h256, string> const& _smtlib2Responses | ||||
| 	map<h256, string> const& _smtlib2Responses, | ||||
| 	smt::SMTSolverChoice _enabledSolvers | ||||
| ): | ||||
| 	SMTEncoder(_context), | ||||
| #ifdef HAVE_Z3 | ||||
| 	m_interface(make_shared<smt::Z3CHCInterface>()), | ||||
| 	m_interface( | ||||
| 		_enabledSolvers.z3 ? | ||||
| 		dynamic_pointer_cast<smt::CHCSolverInterface>(make_shared<smt::Z3CHCInterface>()) : | ||||
| 		dynamic_pointer_cast<smt::CHCSolverInterface>(make_shared<smt::CHCSmtLib2Interface>(_smtlib2Responses)) | ||||
| 	), | ||||
| #else | ||||
| 	m_interface(make_shared<smt::CHCSmtLib2Interface>(_smtlib2Responses)), | ||||
| #endif | ||||
| 	m_outerErrorReporter(_errorReporter) | ||||
| { | ||||
| 	(void)_smtlib2Responses; | ||||
| 	(void)_enabledSolvers; | ||||
| } | ||||
| 
 | ||||
| void CHC::analyze(SourceUnit const& _source) | ||||
| @ -65,6 +71,15 @@ void CHC::analyze(SourceUnit const& _source) | ||||
| 	m_context.setAssertionAccumulation(false); | ||||
| 	m_variableUsage.setFunctionInlining(false); | ||||
| 
 | ||||
| 	auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool); | ||||
| 	auto genesisSort = make_shared<smt::FunctionSort>( | ||||
| 		vector<smt::SortPointer>(), | ||||
| 		boolSort | ||||
| 	); | ||||
| 	m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis"); | ||||
| 	auto genesis = (*m_genesisPredicate)({}); | ||||
| 	addRule(genesis, genesis.name); | ||||
| 
 | ||||
| 	_source.accept(*this); | ||||
| } | ||||
| 
 | ||||
| @ -94,10 +109,10 @@ bool CHC::visit(ContractDefinition const& _contract) | ||||
| 		else | ||||
| 			m_stateSorts.push_back(smt::smtSort(*var->type())); | ||||
| 
 | ||||
| 	clearIndices(); | ||||
| 	clearIndices(&_contract); | ||||
| 
 | ||||
| 	string interfaceName = "interface_" + _contract.name() + "_" + to_string(_contract.id()); | ||||
| 	m_interfacePredicate = createSymbolicBlock(interfaceSort(), interfaceName); | ||||
| 	string suffix = _contract.name() + "_" + to_string(_contract.id()); | ||||
| 	m_interfacePredicate = createSymbolicBlock(interfaceSort(), "interface_" + suffix); | ||||
| 
 | ||||
| 	// TODO create static instances for Bool/Int sorts in SolverInterface.
 | ||||
| 	auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool); | ||||
| @ -105,27 +120,11 @@ bool CHC::visit(ContractDefinition const& _contract) | ||||
| 		vector<smt::SortPointer>(), | ||||
| 		boolSort | ||||
| 	); | ||||
| 	m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error"); | ||||
| 
 | ||||
| 	// If the contract has a constructor it is handled as a function.
 | ||||
| 	// Otherwise we zero-initialize all state vars.
 | ||||
| 	if (!_contract.constructor()) | ||||
| 	{ | ||||
| 		string constructorName = "constructor_" + _contract.name() + "_" + to_string(_contract.id()); | ||||
| 		m_constructorPredicate = createSymbolicBlock(constructorSort(), constructorName); | ||||
| 		smt::Expression constructorPred = (*m_constructorPredicate)({}); | ||||
| 		addRule(constructorPred, constructorName); | ||||
| 
 | ||||
| 		for (auto const& var: m_stateVariables) | ||||
| 		{ | ||||
| 			auto const& symbVar = m_context.variable(*var); | ||||
| 			symbVar->increaseIndex(); | ||||
| 			m_interface->declareVariable(symbVar->currentName(), symbVar->sort()); | ||||
| 			m_context.setZeroValue(*symbVar); | ||||
| 		} | ||||
| 
 | ||||
| 		connectBlocks(constructorPred, interface()); | ||||
| 	} | ||||
| 	m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error_" + suffix); | ||||
| 	m_constructorPredicate = createSymbolicBlock(constructorSort(), "implicit_constructor_" + to_string(_contract.id())); | ||||
| 	auto stateExprs = currentStateVariables(); | ||||
| 	setCurrentBlock(*m_interfacePredicate, &stateExprs); | ||||
| 
 | ||||
| 	SMTEncoder::visit(_contract); | ||||
| 	return false; | ||||
| @ -136,6 +135,23 @@ void CHC::endVisit(ContractDefinition const& _contract) | ||||
| 	if (!shouldVisit(_contract)) | ||||
| 		return; | ||||
| 
 | ||||
| 	for (auto const& var: m_stateVariables) | ||||
| 	{ | ||||
| 		solAssert(m_context.knownVariable(*var), ""); | ||||
| 		m_context.setZeroValue(*var); | ||||
| 	} | ||||
| 	auto genesisPred = (*m_genesisPredicate)({}); | ||||
| 	auto implicitConstructor = (*m_constructorPredicate)(currentStateVariables()); | ||||
| 	connectBlocks(genesisPred, implicitConstructor); | ||||
| 	m_currentBlock = implicitConstructor; | ||||
| 
 | ||||
| 	if (auto constructor = _contract.constructor()) | ||||
| 		constructor->accept(*this); | ||||
| 	else | ||||
| 		inlineConstructorHierarchy(_contract); | ||||
| 
 | ||||
| 	connectBlocks(m_currentBlock, interface()); | ||||
| 
 | ||||
| 	for (unsigned i = 0; i < m_verificationTargets.size(); ++i) | ||||
| 	{ | ||||
| 		auto const& target = m_verificationTargets.at(i); | ||||
| @ -152,6 +168,16 @@ bool CHC::visit(FunctionDefinition const& _function) | ||||
| 	if (!shouldVisit(_function)) | ||||
| 		return false; | ||||
| 
 | ||||
| 	// This is the case for base constructor inlining.
 | ||||
| 	if (m_currentFunction) | ||||
| 	{ | ||||
| 		solAssert(m_currentFunction->isConstructor(), ""); | ||||
| 		solAssert(_function.isConstructor(), ""); | ||||
| 		solAssert(_function.scope() != m_currentContract, ""); | ||||
| 		SMTEncoder::visit(_function); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	solAssert(!m_currentFunction, "Inlining internal function calls not yet implemented"); | ||||
| 	m_currentFunction = &_function; | ||||
| 
 | ||||
| @ -163,20 +189,11 @@ bool CHC::visit(FunctionDefinition const& _function) | ||||
| 	auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables()); | ||||
| 	auto bodyPred = predicate(*bodyBlock); | ||||
| 
 | ||||
| 	// Store the constraints related to variable initialization.
 | ||||
| 	smt::Expression const& initAssertions = m_context.assertions(); | ||||
| 	m_context.pushSolver(); | ||||
| 
 | ||||
| 	connectBlocks(interface(), functionPred); | ||||
| 	connectBlocks(m_currentBlock, functionPred); | ||||
| 	connectBlocks(functionPred, bodyPred); | ||||
| 
 | ||||
| 	m_context.popSolver(); | ||||
| 
 | ||||
| 	setCurrentBlock(*bodyBlock); | ||||
| 
 | ||||
| 	// We need to re-add the constraints that were created for initialization of variables.
 | ||||
| 	m_context.addAssertion(initAssertions); | ||||
| 
 | ||||
| 	SMTEncoder::visit(*m_currentFunction); | ||||
| 
 | ||||
| 	return false; | ||||
| @ -187,10 +204,39 @@ void CHC::endVisit(FunctionDefinition const& _function) | ||||
| 	if (!shouldVisit(_function)) | ||||
| 		return; | ||||
| 
 | ||||
| 	connectBlocks(m_currentBlock, interface()); | ||||
| 	// This is the case for base constructor inlining.
 | ||||
| 	if (m_currentFunction != &_function) | ||||
| 	{ | ||||
| 		solAssert(m_currentFunction && m_currentFunction->isConstructor(), ""); | ||||
| 		solAssert(_function.isConstructor(), ""); | ||||
| 		solAssert(_function.scope() != m_currentContract, ""); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		// We create an extra exit block for constructors that simply
 | ||||
| 		// connects to the interface in case an explicit constructor
 | ||||
| 		// exists in the hierarchy.
 | ||||
| 		// It is not connected directly here, as normal functions are,
 | ||||
| 		// because of the case where there are only implicit constructors.
 | ||||
| 		// This is done in endVisit(ContractDefinition).
 | ||||
| 		if (_function.isConstructor()) | ||||
| 		{ | ||||
| 			auto constructorExit = createSymbolicBlock(interfaceSort(), "constructor_exit_" + to_string(_function.id())); | ||||
| 			connectBlocks(m_currentBlock, predicate(*constructorExit, currentStateVariables())); | ||||
| 			clearIndices(m_currentContract, m_currentFunction); | ||||
| 			auto stateExprs = currentStateVariables(); | ||||
| 			setCurrentBlock(*constructorExit, &stateExprs); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			connectBlocks(m_currentBlock, interface()); | ||||
| 			clearIndices(m_currentContract, m_currentFunction); | ||||
| 			auto stateExprs = currentStateVariables(); | ||||
| 			setCurrentBlock(*m_interfacePredicate, &stateExprs); | ||||
| 		} | ||||
| 		m_currentFunction = nullptr; | ||||
| 	} | ||||
| 
 | ||||
| 	solAssert(&_function == m_currentFunction, ""); | ||||
| 	m_currentFunction = nullptr; | ||||
| 	SMTEncoder::endVisit(_function); | ||||
| } | ||||
| 
 | ||||
| @ -445,7 +491,6 @@ void CHC::reset() | ||||
| 	m_verificationTargets.clear(); | ||||
| 	m_safeAssertions.clear(); | ||||
| 	m_unknownFunctionCallSeen = false; | ||||
| 	m_blockCounter = 0; | ||||
| 	m_breakDest = nullptr; | ||||
| 	m_continueDest = nullptr; | ||||
| } | ||||
| @ -470,28 +515,31 @@ bool CHC::shouldVisit(FunctionDefinition const& _function) const | ||||
| { | ||||
| 	if ( | ||||
| 		_function.isPublic() && | ||||
| 		_function.isImplemented() && | ||||
| 		!_function.isConstructor() | ||||
| 		_function.isImplemented() | ||||
| 	) | ||||
| 		return true; | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void CHC::setCurrentBlock(smt::SymbolicFunctionVariable const& _block) | ||||
| void CHC::setCurrentBlock( | ||||
| 	smt::SymbolicFunctionVariable const& _block, | ||||
| 	vector<smt::Expression> const* _arguments | ||||
| ) | ||||
| { | ||||
| 	m_context.popSolver(); | ||||
| 	clearIndices(); | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 	clearIndices(m_currentContract, m_currentFunction); | ||||
| 	m_context.pushSolver(); | ||||
| 	m_currentBlock = predicate(_block); | ||||
| 	if (_arguments) | ||||
| 		m_currentBlock = predicate(_block, *_arguments); | ||||
| 	else | ||||
| 		m_currentBlock = predicate(_block); | ||||
| } | ||||
| 
 | ||||
| smt::SortPointer CHC::constructorSort() | ||||
| { | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 	auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool); | ||||
| 	if (!m_currentContract->constructor()) | ||||
| 		return make_shared<smt::FunctionSort>(vector<smt::SortPointer>{}, boolSort); | ||||
| 	return sort(*m_currentContract->constructor()); | ||||
| 	// TODO this will change once we support function calls.
 | ||||
| 	return interfaceSort(); | ||||
| } | ||||
| 
 | ||||
| smt::SortPointer CHC::interfaceSort() | ||||
| @ -556,19 +604,6 @@ unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPoin | ||||
| 	return block; | ||||
| } | ||||
| 
 | ||||
| smt::Expression CHC::constructor() | ||||
| { | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 
 | ||||
| 	if (!m_currentContract->constructor()) | ||||
| 		return (*m_constructorPredicate)({}); | ||||
| 
 | ||||
| 	vector<smt::Expression> paramExprs; | ||||
| 	for (auto const& var: m_currentContract->constructor()->parameters()) | ||||
| 		paramExprs.push_back(m_context.variable(*var)->currentValue()); | ||||
| 	return (*m_constructorPredicate)(paramExprs); | ||||
| } | ||||
| 
 | ||||
| smt::Expression CHC::interface() | ||||
| { | ||||
| 	vector<smt::Expression> paramExprs; | ||||
| @ -613,37 +648,31 @@ void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to | ||||
| 	addRule(edge, _from.name + "_to_" + _to.name); | ||||
| } | ||||
| 
 | ||||
| vector<smt::Expression> CHC::currentStateVariables() | ||||
| { | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 	vector<smt::Expression> exprs; | ||||
| 	for (auto const& var: m_stateVariables) | ||||
| 		exprs.push_back(m_context.variable(*var)->currentValue()); | ||||
| 	return exprs; | ||||
| } | ||||
| 
 | ||||
| vector<smt::Expression> CHC::currentFunctionVariables() | ||||
| { | ||||
| 	solAssert(m_currentFunction, ""); | ||||
| 	vector<smt::Expression> paramExprs; | ||||
| 	for (auto const& var: m_stateVariables) | ||||
| 		paramExprs.push_back(m_context.variable(*var)->currentValue()); | ||||
| 	for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters()) | ||||
| 		paramExprs.push_back(m_context.variable(*var)->currentValue()); | ||||
| 	return paramExprs; | ||||
| 	if (m_currentFunction) | ||||
| 		for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters()) | ||||
| 			paramExprs.push_back(m_context.variable(*var)->currentValue()); | ||||
| 	return currentStateVariables() + paramExprs; | ||||
| } | ||||
| 
 | ||||
| vector<smt::Expression> CHC::currentBlockVariables() | ||||
| { | ||||
| 	solAssert(m_currentFunction, ""); | ||||
| 	vector<smt::Expression> paramExprs; | ||||
| 	for (auto const& var: m_currentFunction->localVariables()) | ||||
| 		paramExprs.push_back(m_context.variable(*var)->currentValue()); | ||||
| 	return currentFunctionVariables() + paramExprs; | ||||
| } | ||||
| 
 | ||||
| void CHC::clearIndices() | ||||
| { | ||||
| 	for (auto const& var: m_stateVariables) | ||||
| 		m_context.variable(*var)->resetIndex(); | ||||
| 	if (m_currentFunction) | ||||
| 	{ | ||||
| 		for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters()) | ||||
| 			m_context.variable(*var)->resetIndex(); | ||||
| 		for (auto const& var: m_currentFunction->localVariables()) | ||||
| 			m_context.variable(*var)->resetIndex(); | ||||
| 	} | ||||
| 			paramExprs.push_back(m_context.variable(*var)->currentValue()); | ||||
| 	return currentFunctionVariables() + paramExprs; | ||||
| } | ||||
| 
 | ||||
| string CHC::predicateName(ASTNode const* _node) | ||||
| @ -674,7 +703,6 @@ smt::Expression CHC::predicate( | ||||
| 	return _block(_arguments); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) | ||||
| { | ||||
| 	m_interface->addRule(_rule, _ruleName); | ||||
|  | ||||
| @ -47,7 +47,8 @@ public: | ||||
| 	CHC( | ||||
| 		smt::EncodingContext& _context, | ||||
| 		langutil::ErrorReporter& _errorReporter, | ||||
| 		std::map<h256, std::string> const& _smtlib2Responses | ||||
| 		std::map<h256, std::string> const& _smtlib2Responses, | ||||
| 		smt::SMTSolverChoice _enabledSolvers | ||||
| 	); | ||||
| 
 | ||||
| 	void analyze(SourceUnit const& _sources); | ||||
| @ -83,7 +84,7 @@ private: | ||||
| 	void eraseKnowledge(); | ||||
| 	bool shouldVisit(ContractDefinition const& _contract) const; | ||||
| 	bool shouldVisit(FunctionDefinition const& _function) const; | ||||
| 	void setCurrentBlock(smt::SymbolicFunctionVariable const& _block); | ||||
| 	void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector<smt::Expression> const* _arguments = nullptr); | ||||
| 	//@}
 | ||||
| 
 | ||||
| 	/// Sort helpers.
 | ||||
| @ -99,8 +100,6 @@ private: | ||||
| 	/// @returns a new block of given _sort and _name.
 | ||||
| 	std::unique_ptr<smt::SymbolicFunctionVariable> createSymbolicBlock(smt::SortPointer _sort, std::string const& _name); | ||||
| 
 | ||||
| 	/// Constructor predicate over current variables.
 | ||||
| 	smt::Expression constructor(); | ||||
| 	/// Interface predicate over current variables.
 | ||||
| 	smt::Expression interface(); | ||||
| 	/// Error predicate over current variables.
 | ||||
| @ -116,17 +115,16 @@ private: | ||||
| 
 | ||||
| 	void connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints = smt::Expression(true)); | ||||
| 
 | ||||
| 	/// @returns the current symbolic values of the current state variables.
 | ||||
| 	std::vector<smt::Expression> currentStateVariables(); | ||||
| 
 | ||||
| 	/// @returns the current symbolic values of the current function's
 | ||||
| 	/// input and output parameters.
 | ||||
| 	std::vector<smt::Expression> currentFunctionVariables(); | ||||
| 	/// @returns the samve as currentFunctionVariables plus
 | ||||
| 	/// @returns the same as currentFunctionVariables plus
 | ||||
| 	/// local variables.
 | ||||
| 	std::vector<smt::Expression> currentBlockVariables(); | ||||
| 
 | ||||
| 	/// Sets the SSA indices of the variables in scope to 0.
 | ||||
| 	/// Used when starting a new block.
 | ||||
| 	void clearIndices(); | ||||
| 
 | ||||
| 	/// @returns the predicate name for a given node.
 | ||||
| 	std::string predicateName(ASTNode const* _node); | ||||
| 	/// @returns a predicate application over the current scoped variables.
 | ||||
| @ -152,8 +150,11 @@ private: | ||||
| 
 | ||||
| 	/// Predicates.
 | ||||
| 	//@{
 | ||||
| 	/// Constructor predicate.
 | ||||
| 	/// Default constructor sets state vars to 0.
 | ||||
| 	/// Genesis predicate.
 | ||||
| 	std::unique_ptr<smt::SymbolicFunctionVariable> m_genesisPredicate; | ||||
| 
 | ||||
| 	/// Implicit constructor predicate.
 | ||||
| 	/// Explicit constructors are handled as functions.
 | ||||
| 	std::unique_ptr<smt::SymbolicFunctionVariable> m_constructorPredicate; | ||||
| 
 | ||||
| 	/// Artificial Interface predicate.
 | ||||
|  | ||||
| @ -35,7 +35,7 @@ void CVC4Interface::reset() | ||||
| 	m_variables.clear(); | ||||
| 	m_solver.reset(); | ||||
| 	m_solver.setOption("produce-models", true); | ||||
| 	m_solver.setTimeLimit(queryTimeout); | ||||
| 	m_solver.setResourceLimit(resourceLimit); | ||||
| } | ||||
| 
 | ||||
| void CVC4Interface::push() | ||||
|  | ||||
| @ -63,6 +63,12 @@ private: | ||||
| 	CVC4::ExprManager m_context; | ||||
| 	CVC4::SmtEngine m_solver; | ||||
| 	std::map<std::string, CVC4::Expr> m_variables; | ||||
| 
 | ||||
| 	// CVC4 "basic resources" limit.
 | ||||
| 	// This is used to make the runs more deterministic and platform/machine independent.
 | ||||
| 	// The tests start failing for CVC4 with less than 6000,
 | ||||
| 	// so using double that.
 | ||||
| 	static int const resourceLimit = 12000; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -22,9 +22,13 @@ using namespace dev; | ||||
| using namespace langutil; | ||||
| using namespace dev::solidity; | ||||
| 
 | ||||
| ModelChecker::ModelChecker(ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses): | ||||
| 	m_bmc(m_context, _errorReporter, _smtlib2Responses), | ||||
| 	m_chc(m_context, _errorReporter, _smtlib2Responses), | ||||
| ModelChecker::ModelChecker( | ||||
| 	ErrorReporter& _errorReporter, | ||||
| 	map<h256, string> const& _smtlib2Responses, | ||||
| 	smt::SMTSolverChoice _enabledSolvers | ||||
| ): | ||||
| 	m_bmc(m_context, _errorReporter, _smtlib2Responses, _enabledSolvers), | ||||
| 	m_chc(m_context, _errorReporter, _smtlib2Responses, _enabledSolvers), | ||||
| 	m_context() | ||||
| { | ||||
| } | ||||
|  | ||||
| @ -25,6 +25,7 @@ | ||||
| #include <libsolidity/formal/BMC.h> | ||||
| #include <libsolidity/formal/CHC.h> | ||||
| #include <libsolidity/formal/EncodingContext.h> | ||||
| #include <libsolidity/formal/SolverInterface.h> | ||||
| 
 | ||||
| #include <libsolidity/interface/ReadFile.h> | ||||
| #include <liblangutil/ErrorReporter.h> | ||||
| @ -43,7 +44,13 @@ namespace solidity | ||||
| class ModelChecker | ||||
| { | ||||
| public: | ||||
| 	ModelChecker(langutil::ErrorReporter& _errorReporter, std::map<h256, std::string> const& _smtlib2Responses); | ||||
| 	/// @param _enabledSolvers represents a runtime choice of which SMT solvers
 | ||||
| 	/// should be used, even if all are available. The default choice is to use all.
 | ||||
| 	ModelChecker( | ||||
| 		langutil::ErrorReporter& _errorReporter, | ||||
| 		std::map<h256, std::string> const& _smtlib2Responses, | ||||
| 		smt::SMTSolverChoice _enabledSolvers = smt::SMTSolverChoice::All() | ||||
| 	); | ||||
| 
 | ||||
| 	void analyze(SourceUnit const& _sources); | ||||
| 
 | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| #include <libsolidity/formal/SymbolicTypes.h> | ||||
| 
 | ||||
| #include <boost/range/adaptors.hpp> | ||||
| #include <boost/range/adaptor/reversed.hpp> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace dev; | ||||
| @ -39,11 +40,28 @@ bool SMTEncoder::visit(ContractDefinition const& _contract) | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 
 | ||||
| 	for (auto const& node: _contract.subNodes()) | ||||
| 		if (!dynamic_pointer_cast<FunctionDefinition>(node)) | ||||
| 		if ( | ||||
| 			!dynamic_pointer_cast<FunctionDefinition>(node) && | ||||
| 			!dynamic_pointer_cast<VariableDeclaration>(node) | ||||
| 		) | ||||
| 			node->accept(*this); | ||||
| 
 | ||||
| 	vector<FunctionDefinition const*> resolvedFunctions = _contract.definedFunctions(); | ||||
| 	for (auto const& base: _contract.annotation().linearizedBaseContracts) | ||||
| 	{ | ||||
| 		// Look for all the constructor invocations bottom up.
 | ||||
| 		if (auto const& constructor =  base->constructor()) | ||||
| 			for (auto const& invocation: constructor->modifiers()) | ||||
| 			{ | ||||
| 				auto refDecl = invocation->name()->annotation().referencedDeclaration; | ||||
| 				if (auto const& baseContract = dynamic_cast<ContractDefinition const*>(refDecl)) | ||||
| 				{ | ||||
| 					solAssert(!m_baseConstructorCalls.count(baseContract), ""); | ||||
| 					m_baseConstructorCalls[baseContract] = invocation.get(); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 		// Check for function overrides.
 | ||||
| 		for (auto const& baseFunction: base->definedFunctions()) | ||||
| 		{ | ||||
| 			if (baseFunction->isConstructor()) | ||||
| @ -62,9 +80,18 @@ bool SMTEncoder::visit(ContractDefinition const& _contract) | ||||
| 			if (!overridden) | ||||
| 				resolvedFunctions.push_back(baseFunction); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Functions are visited first since they might be used
 | ||||
| 	// for state variable initialization which is part of
 | ||||
| 	// the constructor.
 | ||||
| 	// Constructors are visited as part of the constructor
 | ||||
| 	// hierarchy inlining.
 | ||||
| 	for (auto const& function: resolvedFunctions) | ||||
| 		function->accept(*this); | ||||
| 		if (!function->isConstructor()) | ||||
| 			function->accept(*this); | ||||
| 
 | ||||
| 	// Constructors need to be handled by the engines separately.
 | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| @ -73,13 +100,16 @@ void SMTEncoder::endVisit(ContractDefinition const& _contract) | ||||
| { | ||||
| 	m_context.resetAllVariables(); | ||||
| 
 | ||||
| 	m_baseConstructorCalls.clear(); | ||||
| 
 | ||||
| 	solAssert(m_currentContract == &_contract, ""); | ||||
| 	m_currentContract = nullptr; | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::endVisit(VariableDeclaration const& _varDecl) | ||||
| { | ||||
| 	if (_varDecl.isLocalVariable() && _varDecl.type()->isValueType() &&_varDecl.value()) | ||||
| 	// State variables are handled by the constructor.
 | ||||
| 	if (_varDecl.isLocalVariable() &&_varDecl.value()) | ||||
| 		assignment(_varDecl, *_varDecl.value()); | ||||
| } | ||||
| 
 | ||||
| @ -90,25 +120,22 @@ bool SMTEncoder::visit(ModifierDefinition const&) | ||||
| 
 | ||||
| bool SMTEncoder::visit(FunctionDefinition const& _function) | ||||
| { | ||||
| 	// Not visited by a function call
 | ||||
| 	if (m_callStack.empty()) | ||||
| 		initFunction(_function); | ||||
| 
 | ||||
| 	m_modifierDepthStack.push_back(-1); | ||||
| 
 | ||||
| 	if (_function.isConstructor()) | ||||
| 	{ | ||||
| 		m_errorReporter.warning( | ||||
| 			_function.location(), | ||||
| 			"Assertion checker does not yet support constructors." | ||||
| 		); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		_function.parameterList().accept(*this); | ||||
| 		if (_function.returnParameterList()) | ||||
| 			_function.returnParameterList()->accept(*this); | ||||
| 		visitFunctionOrModifier(); | ||||
| 	} | ||||
| 		inlineConstructorHierarchy(dynamic_cast<ContractDefinition const&>(*_function.scope())); | ||||
| 
 | ||||
| 	// Base constructors' parameters should be set by explicit calls,
 | ||||
| 	// but the most derived one needs to be initialized.
 | ||||
| 	if (_function.scope() == m_currentContract) | ||||
| 		initializeLocalVariables(_function); | ||||
| 
 | ||||
| 	_function.parameterList().accept(*this); | ||||
| 	if (_function.returnParameterList()) | ||||
| 		_function.returnParameterList()->accept(*this); | ||||
| 
 | ||||
| 	visitFunctionOrModifier(); | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| @ -130,27 +157,87 @@ void SMTEncoder::visitFunctionOrModifier() | ||||
| 		solAssert(m_modifierDepthStack.back() < int(function.modifiers().size()), ""); | ||||
| 		ASTPointer<ModifierInvocation> const& modifierInvocation = function.modifiers()[m_modifierDepthStack.back()]; | ||||
| 		solAssert(modifierInvocation, ""); | ||||
| 		modifierInvocation->accept(*this); | ||||
| 		auto const& modifierDef = dynamic_cast<ModifierDefinition const&>( | ||||
| 			*modifierInvocation->name()->annotation().referencedDeclaration | ||||
| 		); | ||||
| 		vector<smt::Expression> modifierArgsExpr; | ||||
| 		if (auto const* arguments = modifierInvocation->arguments()) | ||||
| 		{ | ||||
| 			auto const& modifierParams = modifierDef.parameters(); | ||||
| 			solAssert(modifierParams.size() == arguments->size(), ""); | ||||
| 			for (unsigned i = 0; i < arguments->size(); ++i) | ||||
| 				modifierArgsExpr.push_back(expr(*arguments->at(i), modifierParams.at(i)->type())); | ||||
| 		} | ||||
| 		initializeFunctionCallParameters(modifierDef, modifierArgsExpr); | ||||
| 		pushCallStack({&modifierDef, modifierInvocation.get()}); | ||||
| 		modifierDef.body().accept(*this); | ||||
| 		popCallStack(); | ||||
| 		auto refDecl = modifierInvocation->name()->annotation().referencedDeclaration; | ||||
| 		if (dynamic_cast<ContractDefinition const*>(refDecl)) | ||||
| 			visitFunctionOrModifier(); | ||||
| 		else if (auto modifierDef = dynamic_cast<ModifierDefinition const*>(refDecl)) | ||||
| 			inlineModifierInvocation(modifierInvocation.get(), modifierDef); | ||||
| 		else | ||||
| 			solAssert(false, ""); | ||||
| 	} | ||||
| 
 | ||||
| 	--m_modifierDepthStack.back(); | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::inlineModifierInvocation(ModifierInvocation const* _invocation, CallableDeclaration const* _definition) | ||||
| { | ||||
| 	solAssert(_invocation, ""); | ||||
| 	_invocation->accept(*this); | ||||
| 
 | ||||
| 	vector<smt::Expression> args; | ||||
| 	if (auto const* arguments = _invocation->arguments()) | ||||
| 	{ | ||||
| 		auto const& modifierParams = _definition->parameters(); | ||||
| 		solAssert(modifierParams.size() == arguments->size(), ""); | ||||
| 		for (unsigned i = 0; i < arguments->size(); ++i) | ||||
| 			args.push_back(expr(*arguments->at(i), modifierParams.at(i)->type())); | ||||
| 	} | ||||
| 
 | ||||
| 	initializeFunctionCallParameters(*_definition, args); | ||||
| 
 | ||||
| 	pushCallStack({_definition, _invocation}); | ||||
| 	if (auto modifier = dynamic_cast<ModifierDefinition const*>(_definition)) | ||||
| 	{ | ||||
| 		modifier->body().accept(*this); | ||||
| 		popCallStack(); | ||||
| 	} | ||||
| 	else if (auto function = dynamic_cast<FunctionDefinition const*>(_definition)) | ||||
| 	{ | ||||
| 		if (function->isImplemented()) | ||||
| 			function->accept(*this); | ||||
| 		// Functions are popped from the callstack in endVisit(FunctionDefinition)
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::inlineConstructorHierarchy(ContractDefinition const& _contract) | ||||
| { | ||||
| 	auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; | ||||
| 	auto it = find(begin(hierarchy), end(hierarchy), &_contract); | ||||
| 	solAssert(it != end(hierarchy), ""); | ||||
| 
 | ||||
| 	auto nextBase = it + 1; | ||||
| 	// Initialize the base contracts here as long as their constructors are implicit,
 | ||||
| 	// stop when the first explicit constructor is found.
 | ||||
| 	while (nextBase != end(hierarchy)) | ||||
| 	{ | ||||
| 		if (auto baseConstructor = (*nextBase)->constructor()) | ||||
| 		{ | ||||
| 			createLocalVariables(*baseConstructor); | ||||
| 			// If any subcontract explicitly called baseConstructor, use those arguments.
 | ||||
| 			if (m_baseConstructorCalls.count(*nextBase)) | ||||
| 				inlineModifierInvocation(m_baseConstructorCalls.at(*nextBase), baseConstructor); | ||||
| 			else if (baseConstructor->isImplemented()) | ||||
| 			{ | ||||
| 				// The first constructor found is handled like a function
 | ||||
| 				// and its pushed into the callstack there.
 | ||||
| 				// This if avoids duplication in the callstack.
 | ||||
| 				if (!m_callStack.empty()) | ||||
| 					pushCallStack({baseConstructor, nullptr}); | ||||
| 				baseConstructor->accept(*this); | ||||
| 				// popped by endVisit(FunctionDefinition)
 | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			initializeStateVariables(**nextBase); | ||||
| 			++nextBase; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	initializeStateVariables(_contract); | ||||
| } | ||||
| 
 | ||||
| bool SMTEncoder::visit(PlaceholderStatement const&) | ||||
| { | ||||
| 	solAssert(!m_callStack.empty(), ""); | ||||
| @ -208,17 +295,14 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl) | ||||
| 			solAssert(symbTuple, ""); | ||||
| 			auto const& components = symbTuple->components(); | ||||
| 			auto const& declarations = _varDecl.declarations(); | ||||
| 			if (!components.empty()) | ||||
| 			{ | ||||
| 				solAssert(components.size() == declarations.size(), ""); | ||||
| 				for (unsigned i = 0; i < declarations.size(); ++i) | ||||
| 					if ( | ||||
| 						components.at(i) && | ||||
| 						declarations.at(i) && | ||||
| 						m_context.knownVariable(*declarations.at(i)) | ||||
| 					) | ||||
| 						assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type())); | ||||
| 			} | ||||
| 			solAssert(components.size() == declarations.size(), ""); | ||||
| 			for (unsigned i = 0; i < declarations.size(); ++i) | ||||
| 				if ( | ||||
| 					components.at(i) && | ||||
| 					declarations.at(i) && | ||||
| 					m_context.knownVariable(*declarations.at(i)) | ||||
| 				) | ||||
| 					assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type())); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (m_context.knownVariable(*_varDecl.declarations().front())) | ||||
| @ -320,24 +404,23 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) | ||||
| 	{ | ||||
| 		auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_tuple)); | ||||
| 		solAssert(symbTuple, ""); | ||||
| 		if (symbTuple->components().empty()) | ||||
| 		auto const& symbComponents = symbTuple->components(); | ||||
| 		auto const& tupleComponents = _tuple.components(); | ||||
| 		solAssert(symbComponents.size() == _tuple.components().size(), ""); | ||||
| 		for (unsigned i = 0; i < symbComponents.size(); ++i) | ||||
| 		{ | ||||
| 			vector<shared_ptr<smt::SymbolicVariable>> components; | ||||
| 			for (auto const& component: _tuple.components()) | ||||
| 				if (component) | ||||
| 				{ | ||||
| 					if (auto varDecl = identifierToVariable(*component)) | ||||
| 						components.push_back(m_context.variable(*varDecl)); | ||||
| 					else | ||||
| 					{ | ||||
| 						solAssert(m_context.knownExpression(*component), ""); | ||||
| 						components.push_back(m_context.expression(*component)); | ||||
| 					} | ||||
| 				} | ||||
| 			auto sComponent = symbComponents.at(i); | ||||
| 			auto tComponent = tupleComponents.at(i); | ||||
| 			if (sComponent && tComponent) | ||||
| 			{ | ||||
| 				if (auto varDecl = identifierToVariable(*tComponent)) | ||||
| 					m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl)); | ||||
| 				else | ||||
| 					components.push_back(nullptr); | ||||
| 			solAssert(components.size() == _tuple.components().size(), ""); | ||||
| 			symbTuple->setComponents(move(components)); | ||||
| 				{ | ||||
| 					solAssert(m_context.knownExpression(*tComponent), ""); | ||||
| 					m_context.addAssertion(sComponent->currentValue() == expr(*tComponent)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| @ -541,25 +624,39 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool SMTEncoder::visit(ModifierInvocation const& _node) | ||||
| { | ||||
| 	if (auto const* args = _node.arguments()) | ||||
| 		for (auto const& arg: *args) | ||||
| 			if (arg) | ||||
| 				arg->accept(*this); | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::initContract(ContractDefinition const& _contract) | ||||
| { | ||||
| 	solAssert(m_currentContract == nullptr, ""); | ||||
| 	m_currentContract = &_contract; | ||||
| 
 | ||||
| 	initializeStateVariables(_contract); | ||||
| 	m_context.reset(); | ||||
| 	m_context.pushSolver(); | ||||
| 	createStateVariables(_contract); | ||||
| 	clearIndices(m_currentContract, nullptr); | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::initFunction(FunctionDefinition const& _function) | ||||
| { | ||||
| 	solAssert(m_callStack.empty(), ""); | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 	m_context.reset(); | ||||
| 	m_context.pushSolver(); | ||||
| 	m_pathConditions.clear(); | ||||
| 	pushCallStack({&_function, nullptr}); | ||||
| 	m_uninterpretedTerms.clear(); | ||||
| 	resetStateVariables(); | ||||
| 	initializeLocalVariables(_function); | ||||
| 	createStateVariables(*m_currentContract); | ||||
| 	createLocalVariables(_function); | ||||
| 	m_arrayAssignmentHappened = false; | ||||
| 	clearIndices(m_currentContract, &_function); | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::visitAssert(FunctionCall const& _funCall) | ||||
| @ -609,12 +706,20 @@ void SMTEncoder::endVisit(Identifier const& _identifier) | ||||
| 		defineExpr(_identifier, m_context.thisAddress()); | ||||
| 		m_uninterpretedTerms.insert(&_identifier); | ||||
| 	} | ||||
| 	else if (smt::isSupportedType(_identifier.annotation().type->category())) | ||||
| 		// TODO: handle MagicVariableDeclaration here
 | ||||
| 		m_errorReporter.warning( | ||||
| 			_identifier.location(), | ||||
| 			"Assertion checker does not yet support the type of this variable." | ||||
| 		); | ||||
| 	else | ||||
| 		createExpr(_identifier); | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName) | ||||
| { | ||||
| 	auto const& typeType = dynamic_cast<TypeType const&>(*_typeName.annotation().type); | ||||
| 	auto result = smt::newSymbolicVariable( | ||||
| 		*TypeProvider::uint256(), | ||||
| 		typeType.actualType()->toString(false), | ||||
| 		m_context | ||||
| 	); | ||||
| 	solAssert(!result.first && result.second, ""); | ||||
| 	m_context.createExpression(_typeName, result.second); | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) | ||||
| @ -764,8 +869,11 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) | ||||
| { | ||||
| 	createExpr(_indexAccess); | ||||
| 
 | ||||
| 	if (_indexAccess.annotation().type->category() == Type::Category::TypeType) | ||||
| 		return; | ||||
| 
 | ||||
| 	shared_ptr<smt::SymbolicVariable> array; | ||||
| 	if (auto const& id = dynamic_cast<Identifier const*>(&_indexAccess.baseExpression())) | ||||
| 	if (auto const* id = dynamic_cast<Identifier const*>(&_indexAccess.baseExpression())) | ||||
| 	{ | ||||
| 		auto varDecl = identifierToVariable(*id); | ||||
| 		solAssert(varDecl, ""); | ||||
| @ -780,7 +888,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	else if (auto const& innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression())) | ||||
| 	else if (auto const* innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression())) | ||||
| 	{ | ||||
| 		solAssert(m_context.knownExpression(*innerAccess), ""); | ||||
| 		array = m_context.expression(*innerAccess); | ||||
| @ -794,12 +902,6 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!_indexAccess.indexExpression()) | ||||
| 	{ | ||||
| 		solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, ""); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	solAssert(array, ""); | ||||
| 	defineExpr(_indexAccess, smt::Expression::select( | ||||
| 		array->currentValue(), | ||||
| @ -1226,26 +1328,61 @@ void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _fu | ||||
| 			} | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::initializeStateVariables(ContractDefinition const& _contract) | ||||
| void SMTEncoder::createStateVariables(ContractDefinition const& _contract) | ||||
| { | ||||
| 	for (auto var: _contract.stateVariablesIncludingInherited()) | ||||
| 		createVariable(*var); | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::initializeLocalVariables(FunctionDefinition const& _function) | ||||
| void SMTEncoder::initializeStateVariables(ContractDefinition const& _contract) | ||||
| { | ||||
| 	for (auto var: _contract.stateVariables()) | ||||
| 	{ | ||||
| 		solAssert(m_context.knownVariable(*var), ""); | ||||
| 		m_context.setZeroValue(*var); | ||||
| 	} | ||||
| 
 | ||||
| 	for (auto var: _contract.stateVariables()) | ||||
| 		if (var->value()) | ||||
| 		{ | ||||
| 			var->value()->accept(*this); | ||||
| 			assignment(*var, *var->value()); | ||||
| 		} | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::createLocalVariables(FunctionDefinition const& _function) | ||||
| { | ||||
| 	for (auto const& variable: _function.localVariables()) | ||||
| 		if (createVariable(*variable)) | ||||
| 			m_context.setZeroValue(*variable); | ||||
| 		createVariable(*variable); | ||||
| 
 | ||||
| 	for (auto const& param: _function.parameters()) | ||||
| 		if (createVariable(*param)) | ||||
| 			m_context.setUnknownValue(*param); | ||||
| 		createVariable(*param); | ||||
| 
 | ||||
| 	if (_function.returnParameterList()) | ||||
| 		for (auto const& retParam: _function.returnParameters()) | ||||
| 			if (createVariable(*retParam)) | ||||
| 				m_context.setZeroValue(*retParam); | ||||
| 			createVariable(*retParam); | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::initializeLocalVariables(FunctionDefinition const& _function) | ||||
| { | ||||
| 	for (auto const& variable: _function.localVariables()) | ||||
| 	{ | ||||
| 		solAssert(m_context.knownVariable(*variable), ""); | ||||
| 		m_context.setZeroValue(*variable); | ||||
| 	} | ||||
| 
 | ||||
| 	for (auto const& param: _function.parameters()) | ||||
| 	{ | ||||
| 		solAssert(m_context.knownVariable(*param), ""); | ||||
| 		m_context.setUnknownValue(*param); | ||||
| 	} | ||||
| 
 | ||||
| 	if (_function.returnParameterList()) | ||||
| 		for (auto const& retParam: _function.returnParameters()) | ||||
| 		{ | ||||
| 			solAssert(m_context.knownVariable(*retParam), ""); | ||||
| 			m_context.setZeroValue(*retParam); | ||||
| 		} | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::resetStateVariables() | ||||
| @ -1425,6 +1562,20 @@ void SMTEncoder::resetVariableIndices(VariableIndices const& _indices) | ||||
| 		m_context.variable(*var.first)->index() = var.second; | ||||
| } | ||||
| 
 | ||||
| void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function) | ||||
| { | ||||
| 	solAssert(_contract, ""); | ||||
| 	for (auto var: _contract->stateVariablesIncludingInherited()) | ||||
| 		m_context.variable(*var)->resetIndex(); | ||||
| 	if (_function) | ||||
| 	{ | ||||
| 		for (auto const& var: _function->parameters() + _function->returnParameters()) | ||||
| 			m_context.variable(*var)->resetIndex(); | ||||
| 		for (auto const& var: _function->localVariables()) | ||||
| 			m_context.variable(*var)->resetIndex(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess) | ||||
| { | ||||
| 	Expression const* base = &_indexAccess.baseExpression(); | ||||
| @ -1503,15 +1654,18 @@ void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall) | ||||
| 	{ | ||||
| 		auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_funCall)); | ||||
| 		solAssert(symbTuple, ""); | ||||
| 		if (symbTuple->components().empty()) | ||||
| 		auto const& symbComponents = symbTuple->components(); | ||||
| 		solAssert(symbComponents.size() == returnParams.size(), ""); | ||||
| 		for (unsigned i = 0; i < symbComponents.size(); ++i) | ||||
| 		{ | ||||
| 			vector<shared_ptr<smt::SymbolicVariable>> components; | ||||
| 			for (auto param: returnParams) | ||||
| 			auto sComponent = symbComponents.at(i); | ||||
| 			auto param = returnParams.at(i); | ||||
| 			solAssert(param, ""); | ||||
| 			if (sComponent) | ||||
| 			{ | ||||
| 				solAssert(m_context.knownVariable(*param), ""); | ||||
| 				components.push_back(m_context.variable(*param)); | ||||
| 				m_context.addAssertion(sComponent->currentValue() == currentValue(*param)); | ||||
| 			} | ||||
| 			symbTuple->setComponents(move(components)); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (returnParams.size() == 1) | ||||
|  | ||||
| @ -27,6 +27,7 @@ | ||||
| #include <libsolidity/formal/SymbolicVariables.h> | ||||
| #include <libsolidity/formal/VariableUsage.h> | ||||
| 
 | ||||
| #include <libsolidity/ast/AST.h> | ||||
| #include <libsolidity/ast/ASTVisitor.h> | ||||
| #include <libsolidity/interface/ReadFile.h> | ||||
| #include <liblangutil/ErrorReporter.h> | ||||
| @ -81,7 +82,9 @@ protected: | ||||
| 	bool visit(BinaryOperation const& _node) override; | ||||
| 	void endVisit(BinaryOperation const& _node) override; | ||||
| 	void endVisit(FunctionCall const& _node) override; | ||||
| 	bool visit(ModifierInvocation const& _node) override; | ||||
| 	void endVisit(Identifier const& _node) override; | ||||
| 	void endVisit(ElementaryTypeNameExpression const& _node) override; | ||||
| 	void endVisit(Literal const& _node) override; | ||||
| 	void endVisit(Return const& _node) override; | ||||
| 	bool visit(MemberAccess const& _node) override; | ||||
| @ -119,6 +122,12 @@ protected: | ||||
| 	/// visit depth.
 | ||||
| 	void visitFunctionOrModifier(); | ||||
| 
 | ||||
| 	/// Inlines a modifier or base constructor call.
 | ||||
| 	void inlineModifierInvocation(ModifierInvocation const* _invocation, CallableDeclaration const* _definition); | ||||
| 
 | ||||
| 	/// Inlines the constructor hierarchy into a single constructor.
 | ||||
| 	void inlineConstructorHierarchy(ContractDefinition const& _contract); | ||||
| 
 | ||||
| 	/// Defines a new global variable or function.
 | ||||
| 	void defineGlobalVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false); | ||||
| 
 | ||||
| @ -158,7 +167,9 @@ protected: | ||||
| 
 | ||||
| 	using CallStackEntry = std::pair<CallableDeclaration const*, ASTNode const*>; | ||||
| 
 | ||||
| 	void createStateVariables(ContractDefinition const& _contract); | ||||
| 	void initializeStateVariables(ContractDefinition const& _contract); | ||||
| 	void createLocalVariables(FunctionDefinition const& _function); | ||||
| 	void initializeLocalVariables(FunctionDefinition const& _function); | ||||
| 	void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smt::Expression> const& _callArgs); | ||||
| 	void resetStateVariables(); | ||||
| @ -206,6 +217,9 @@ protected: | ||||
| 	VariableIndices copyVariableIndices(); | ||||
| 	/// Resets the variable indices.
 | ||||
| 	void resetVariableIndices(VariableIndices const& _indices); | ||||
| 	/// Used when starting a new block.
 | ||||
| 	void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr); | ||||
| 
 | ||||
| 
 | ||||
| 	/// @returns variables that are touched in _node's subtree.
 | ||||
| 	std::set<VariableDeclaration const*> touchedVariables(ASTNode const& _node); | ||||
| @ -250,6 +264,8 @@ protected: | ||||
| 	/// Needs to be a stack because of function calls.
 | ||||
| 	std::vector<int> m_modifierDepthStack; | ||||
| 
 | ||||
| 	std::map<ContractDefinition const*, ModifierInvocation const*> m_baseConstructorCalls; | ||||
| 
 | ||||
| 	ContractDefinition const* m_currentContract = nullptr; | ||||
| 
 | ||||
| 	/// Stores the context of the encoding.
 | ||||
|  | ||||
| @ -30,15 +30,21 @@ using namespace dev; | ||||
| using namespace dev::solidity; | ||||
| using namespace dev::solidity::smt; | ||||
| 
 | ||||
| SMTPortfolio::SMTPortfolio(map<h256, string> const& _smtlib2Responses) | ||||
| SMTPortfolio::SMTPortfolio( | ||||
| 	map<h256, string> const& _smtlib2Responses, | ||||
| 	SMTSolverChoice _enabledSolvers | ||||
| ) | ||||
| { | ||||
| 	m_solvers.emplace_back(make_unique<smt::SMTLib2Interface>(_smtlib2Responses)); | ||||
| #ifdef HAVE_Z3 | ||||
| 	m_solvers.emplace_back(make_unique<smt::Z3Interface>()); | ||||
| 	if (_enabledSolvers.z3) | ||||
| 		m_solvers.emplace_back(make_unique<smt::Z3Interface>()); | ||||
| #endif | ||||
| #ifdef HAVE_CVC4 | ||||
| 	m_solvers.emplace_back(make_unique<smt::CVC4Interface>()); | ||||
| 	if (_enabledSolvers.cvc4) | ||||
| 		m_solvers.emplace_back(make_unique<smt::CVC4Interface>()); | ||||
| #endif | ||||
| 	(void)_enabledSolvers; | ||||
| } | ||||
| 
 | ||||
| void SMTPortfolio::reset() | ||||
|  | ||||
| @ -42,7 +42,10 @@ namespace smt | ||||
| class SMTPortfolio: public SolverInterface, public boost::noncopyable | ||||
| { | ||||
| public: | ||||
| 	SMTPortfolio(std::map<h256, std::string> const& _smtlib2Responses); | ||||
| 	SMTPortfolio( | ||||
| 		std::map<h256, std::string> const& _smtlib2Responses, | ||||
| 		SMTSolverChoice _enabledSolvers | ||||
| 	); | ||||
| 
 | ||||
| 	void reset() override; | ||||
| 
 | ||||
|  | ||||
| @ -36,6 +36,21 @@ namespace solidity | ||||
| namespace smt | ||||
| { | ||||
| 
 | ||||
| struct SMTSolverChoice | ||||
| { | ||||
| 	bool cvc4 = false; | ||||
| 	bool z3 = false; | ||||
| 
 | ||||
| 	static constexpr SMTSolverChoice All() { return {true, true}; } | ||||
| 	static constexpr SMTSolverChoice CVC4() { return {true, false}; } | ||||
| 	static constexpr SMTSolverChoice Z3() { return {false, true}; } | ||||
| 	static constexpr SMTSolverChoice None() { return {false, false}; } | ||||
| 
 | ||||
| 	bool none() { return !some(); } | ||||
| 	bool some() { return cvc4 || z3; } | ||||
| 	bool all() { return cvc4 && z3; } | ||||
| }; | ||||
| 
 | ||||
| enum class CheckResult | ||||
| { | ||||
| 	SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR | ||||
| @ -359,10 +374,6 @@ public: | ||||
| 
 | ||||
| 	/// @returns how many SMT solvers this interface has.
 | ||||
| 	virtual unsigned solvers() { return 1; } | ||||
| 
 | ||||
| protected: | ||||
| 	// SMT query timeout in milliseconds.
 | ||||
| 	static int const queryTimeout = 10000; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -63,7 +63,7 @@ SortPointer smtSort(solidity::Type const& _type) | ||||
| 		{ | ||||
| 			auto mapType = dynamic_cast<solidity::MappingType const*>(&_type); | ||||
| 			solAssert(mapType, ""); | ||||
| 			return make_shared<ArraySort>(smtSort(*mapType->keyType()), smtSort(*mapType->valueType())); | ||||
| 			return make_shared<ArraySort>(smtSortAbstractFunction(*mapType->keyType()), smtSortAbstractFunction(*mapType->valueType())); | ||||
| 		} | ||||
| 		else if (isStringLiteral(_type.category())) | ||||
| 		{ | ||||
| @ -77,7 +77,7 @@ SortPointer smtSort(solidity::Type const& _type) | ||||
| 			solAssert(isArray(_type.category()), ""); | ||||
| 			auto arrayType = dynamic_cast<solidity::ArrayType const*>(&_type); | ||||
| 			solAssert(arrayType, ""); | ||||
| 			return make_shared<ArraySort>(make_shared<Sort>(Kind::Int), smtSort(*arrayType->baseType())); | ||||
| 			return make_shared<ArraySort>(make_shared<Sort>(Kind::Int), smtSortAbstractFunction(*arrayType->baseType())); | ||||
| 		} | ||||
| 	} | ||||
| 	default: | ||||
| @ -94,6 +94,13 @@ vector<SortPointer> smtSort(vector<solidity::TypePointer> const& _types) | ||||
| 	return sorts; | ||||
| } | ||||
| 
 | ||||
| SortPointer smtSortAbstractFunction(solidity::Type const& _type) | ||||
| { | ||||
| 	if (isFunction(_type.category())) | ||||
| 		return make_shared<Sort>(Kind::Int); | ||||
| 	return smtSort(_type); | ||||
| } | ||||
| 
 | ||||
| Kind smtKind(solidity::Type::Category _category) | ||||
| { | ||||
| 	if (isNumber(_category)) | ||||
|  | ||||
| @ -32,6 +32,9 @@ namespace smt | ||||
| /// Returns the SMT sort that models the Solidity type _type.
 | ||||
| SortPointer smtSort(solidity::Type const& _type); | ||||
| std::vector<SortPointer> smtSort(std::vector<solidity::TypePointer> const& _types); | ||||
| /// If _type has type Function, abstract it to Integer.
 | ||||
| /// Otherwise return smtSort(_type).
 | ||||
| SortPointer smtSortAbstractFunction(solidity::Type const& _type); | ||||
| /// Returns the SMT kind that models the Solidity type type category _category.
 | ||||
| Kind smtKind(solidity::Type::Category _category); | ||||
| 
 | ||||
|  | ||||
| @ -248,12 +248,16 @@ SymbolicTupleVariable::SymbolicTupleVariable( | ||||
| 	SymbolicVariable(_type, _type, move(_uniqueName), _context) | ||||
| { | ||||
| 	solAssert(isTuple(m_type->category()), ""); | ||||
| } | ||||
| 
 | ||||
| void SymbolicTupleVariable::setComponents(vector<shared_ptr<SymbolicVariable>> _components) | ||||
| { | ||||
| 	solAssert(m_components.empty(), ""); | ||||
| 	auto const& tupleType = dynamic_cast<solidity::TupleType const*>(m_type); | ||||
| 	solAssert(_components.size() == tupleType->components().size(), ""); | ||||
| 	m_components = move(_components); | ||||
| 	auto const& tupleType = dynamic_cast<TupleType const&>(*m_type); | ||||
| 	auto const& componentsTypes = tupleType.components(); | ||||
| 	for (unsigned i = 0; i < componentsTypes.size(); ++i) | ||||
| 		if (componentsTypes.at(i)) | ||||
| 		{ | ||||
| 			string componentName = m_uniqueName + "_component_" + to_string(i); | ||||
| 			auto result = smt::newSymbolicVariable(*componentsTypes.at(i), componentName, m_context); | ||||
| 			solAssert(result.second, ""); | ||||
| 			m_components.emplace_back(move(result.second)); | ||||
| 		} | ||||
| 		else | ||||
| 			m_components.emplace_back(nullptr); | ||||
| } | ||||
|  | ||||
| @ -250,8 +250,6 @@ public: | ||||
| 		return m_components; | ||||
| 	} | ||||
| 
 | ||||
| 	void setComponents(std::vector<std::shared_ptr<SymbolicVariable>> _components); | ||||
| 
 | ||||
| private: | ||||
| 	std::vector<std::shared_ptr<SymbolicVariable>> m_components; | ||||
| }; | ||||
|  | ||||
| @ -29,10 +29,9 @@ Z3CHCInterface::Z3CHCInterface(): | ||||
| 	m_context(m_z3Interface->context()), | ||||
| 	m_solver(*m_context) | ||||
| { | ||||
| 	// This needs to be set globally.
 | ||||
| 	// These need to be set globally.
 | ||||
| 	z3::set_param("rewriter.pull_cheap_ite", true); | ||||
| 	// This needs to be set in the context.
 | ||||
| 	m_context->set("timeout", queryTimeout); | ||||
| 	z3::set_param("rlimit", Z3Interface::resourceLimit); | ||||
| 
 | ||||
| 	// Spacer options.
 | ||||
| 	// These needs to be set in the solver.
 | ||||
|  | ||||
| @ -54,9 +54,6 @@ private: | ||||
| 	z3::context* m_context; | ||||
| 	// Horn solver.
 | ||||
| 	z3::fixedpoint m_solver; | ||||
| 
 | ||||
| 	// SMT query timeout in milliseconds.
 | ||||
| 	static int const queryTimeout = 10000; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -27,10 +27,9 @@ using namespace dev::solidity::smt; | ||||
| Z3Interface::Z3Interface(): | ||||
| 	m_solver(m_context) | ||||
| { | ||||
| 	// This needs to be set globally.
 | ||||
| 	// These need to be set globally.
 | ||||
| 	z3::set_param("rewriter.pull_cheap_ite", true); | ||||
| 	// This needs to be set in the context.
 | ||||
| 	m_context.set("timeout", queryTimeout); | ||||
| 	z3::set_param("rlimit", resourceLimit); | ||||
| } | ||||
| 
 | ||||
| void Z3Interface::reset() | ||||
|  | ||||
| @ -50,6 +50,12 @@ public: | ||||
| 
 | ||||
| 	z3::context* context() { return &m_context; } | ||||
| 
 | ||||
| 	// Z3 "basic resources" limit.
 | ||||
| 	// This is used to make the runs more deterministic and platform/machine independent.
 | ||||
| 	// The tests start failing for Z3 with less than 20000000,
 | ||||
| 	// so using double that.
 | ||||
| 	static int const resourceLimit = 40000000; | ||||
| 
 | ||||
| private: | ||||
| 	void declareFunction(std::string const& _name, Sort const& _sort); | ||||
| 
 | ||||
|  | ||||
| @ -512,7 +512,7 @@ string const* CompilerStack::sourceMapping(string const& _contractName) const | ||||
| 	if (!c.sourceMapping) | ||||
| 	{ | ||||
| 		if (auto items = assemblyItems(_contractName)) | ||||
| 			c.sourceMapping.reset(new string(computeSourceMapping(*items))); | ||||
| 			c.sourceMapping = make_unique<string>(computeSourceMapping(*items)); | ||||
| 	} | ||||
| 	return c.sourceMapping.get(); | ||||
| } | ||||
| @ -526,7 +526,7 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c | ||||
| 	if (!c.runtimeSourceMapping) | ||||
| 	{ | ||||
| 		if (auto items = runtimeAssemblyItems(_contractName)) | ||||
| 			c.runtimeSourceMapping.reset(new string(computeSourceMapping(*items))); | ||||
| 			c.runtimeSourceMapping = make_unique<string>(computeSourceMapping(*items)); | ||||
| 	} | ||||
| 	return c.runtimeSourceMapping.get(); | ||||
| } | ||||
| @ -663,7 +663,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const | ||||
| 
 | ||||
| 	// caches the result
 | ||||
| 	if (!_contract.abi) | ||||
| 		_contract.abi.reset(new Json::Value(ABI::generate(*_contract.contract))); | ||||
| 		_contract.abi = make_unique<Json::Value>(ABI::generate(*_contract.contract)); | ||||
| 
 | ||||
| 	return *_contract.abi; | ||||
| } | ||||
| @ -685,7 +685,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const | ||||
| 
 | ||||
| 	// caches the result
 | ||||
| 	if (!_contract.storageLayout) | ||||
| 		_contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract))); | ||||
| 		_contract.storageLayout = make_unique<Json::Value>(StorageLayout().generate(*_contract.contract)); | ||||
| 
 | ||||
| 	return *_contract.storageLayout; | ||||
| } | ||||
| @ -707,7 +707,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const | ||||
| 
 | ||||
| 	// caches the result
 | ||||
| 	if (!_contract.userDocumentation) | ||||
| 		_contract.userDocumentation.reset(new Json::Value(Natspec::userDocumentation(*_contract.contract))); | ||||
| 		_contract.userDocumentation = make_unique<Json::Value>(Natspec::userDocumentation(*_contract.contract)); | ||||
| 
 | ||||
| 	return *_contract.userDocumentation; | ||||
| } | ||||
| @ -729,7 +729,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const | ||||
| 
 | ||||
| 	// caches the result
 | ||||
| 	if (!_contract.devDocumentation) | ||||
| 		_contract.devDocumentation.reset(new Json::Value(Natspec::devDocumentation(*_contract.contract))); | ||||
| 		_contract.devDocumentation = make_unique<Json::Value>(Natspec::devDocumentation(*_contract.contract)); | ||||
| 
 | ||||
| 	return *_contract.devDocumentation; | ||||
| } | ||||
| @ -762,7 +762,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const | ||||
| 
 | ||||
| 	// cache the result
 | ||||
| 	if (!_contract.metadata) | ||||
| 		_contract.metadata.reset(new string(createMetadata(_contract))); | ||||
| 		_contract.metadata = make_unique<string>(createMetadata(_contract)); | ||||
| 
 | ||||
| 	return *_contract.metadata; | ||||
| } | ||||
| @ -1060,25 +1060,17 @@ void CompilerStack::generateEWasm(ContractDefinition const& _contract) | ||||
| 		return; | ||||
| 
 | ||||
| 	// Re-parse the Yul IR in EVM dialect
 | ||||
| 	yul::AssemblyStack evmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings); | ||||
| 	evmStack.parseAndAnalyze("", compiledContract.yulIROptimized); | ||||
| 	yul::AssemblyStack stack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings); | ||||
| 	stack.parseAndAnalyze("", compiledContract.yulIROptimized); | ||||
| 
 | ||||
| 	// Turn into eWasm dialect
 | ||||
| 	yul::Object ewasmObject = yul::EVMToEWasmTranslator( | ||||
| 		yul::EVMDialect::strictAssemblyForEVMObjects(m_evmVersion) | ||||
| 	).run(*evmStack.parserResult()); | ||||
| 	stack.optimize(); | ||||
| 	stack.translate(yul::AssemblyStack::Language::EWasm); | ||||
| 	stack.optimize(); | ||||
| 
 | ||||
| 	// Re-inject into an assembly stack for the eWasm dialect
 | ||||
| 	yul::AssemblyStack ewasmStack(m_evmVersion, yul::AssemblyStack::Language::EWasm, m_optimiserSettings); | ||||
| 	// TODO this is a hack for now - provide as structured AST!
 | ||||
| 	ewasmStack.parseAndAnalyze("", "{}"); | ||||
| 	*ewasmStack.parserResult() = move(ewasmObject); | ||||
| 	ewasmStack.optimize(); | ||||
| 
 | ||||
| 	//cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl;
 | ||||
| 	//cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl;
 | ||||
| 
 | ||||
| 	// Turn into eWasm text representation.
 | ||||
| 	auto result = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm); | ||||
| 	auto result = stack.assemble(yul::AssemblyStack::Machine::eWasm); | ||||
| 	compiledContract.eWasm = std::move(result.assembly); | ||||
| 	compiledContract.eWasmObject = std::move(*result.bytecode); | ||||
| } | ||||
| @ -1410,7 +1402,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const | ||||
| 	if (eth::AssemblyItems const* items = assemblyItems(_contractName)) | ||||
| 	{ | ||||
| 		Gas executionGas = gasEstimator.functionalEstimation(*items); | ||||
| 		Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false)}; | ||||
| 		Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false, m_evmVersion)}; | ||||
| 
 | ||||
| 		Json::Value creation(Json::objectValue); | ||||
| 		creation["codeDepositCost"] = gasToJson(codeDepositGas); | ||||
|  | ||||
| @ -24,6 +24,7 @@ | ||||
| 
 | ||||
| #include <libsolidity/ast/ASTJsonConverter.h> | ||||
| #include <libyul/AssemblyStack.h> | ||||
| #include <libyul/Exceptions.h> | ||||
| #include <liblangutil/SourceReferenceFormatter.h> | ||||
| #include <libevmasm/Instruction.h> | ||||
| #include <libdevcore/JSON.h> | ||||
| @ -817,6 +818,16 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting | ||||
| 			"Unimplemented feature (" + _exception.lineInfo() + ")" | ||||
| 		)); | ||||
| 	} | ||||
| 	catch (yul::YulException const& _exception) | ||||
| 	{ | ||||
| 		errors.append(formatErrorWithException( | ||||
| 			_exception, | ||||
| 			false, | ||||
| 			"YulException", | ||||
| 			"general", | ||||
| 			"Yul exception" | ||||
| 		)); | ||||
| 	} | ||||
| 	catch (Exception const& _exception) | ||||
| 	{ | ||||
| 		errors.append(formatError( | ||||
|  | ||||
| @ -350,7 +350,7 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() | ||||
| 	if (m_scanner->currentToken() == Token::LParen) | ||||
| 	{ | ||||
| 		m_scanner->next(); | ||||
| 		arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments())); | ||||
| 		arguments = make_unique<vector<ASTPointer<Expression>>>(parseFunctionCallListArguments()); | ||||
| 		nodeFactory.markEndPosition(); | ||||
| 		expectToken(Token::RParen); | ||||
| 	} | ||||
| @ -811,7 +811,7 @@ ASTPointer<ModifierInvocation> Parser::parseModifierInvocation() | ||||
| 	if (m_scanner->currentToken() == Token::LParen) | ||||
| 	{ | ||||
| 		m_scanner->next(); | ||||
| 		arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments())); | ||||
| 		arguments = make_unique<vector<ASTPointer<Expression>>>(parseFunctionCallListArguments()); | ||||
| 		nodeFactory.markEndPosition(); | ||||
| 		expectToken(Token::RParen); | ||||
| 	} | ||||
| @ -1614,9 +1614,22 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() | ||||
| 		} | ||||
| 		break; | ||||
| 	case Token::StringLiteral: | ||||
| 	case Token::HexStringLiteral: | ||||
| 	{ | ||||
| 		string literal = m_scanner->currentLiteral(); | ||||
| 		Token firstToken = m_scanner->currentToken(); | ||||
| 		while (m_scanner->peekNextToken() == firstToken) | ||||
| 		{ | ||||
| 			m_scanner->next(); | ||||
| 			literal += m_scanner->currentLiteral(); | ||||
| 		} | ||||
| 		nodeFactory.markEndPosition(); | ||||
| 		expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); | ||||
| 		m_scanner->next(); | ||||
| 		if (m_scanner->currentToken() == Token::Illegal) | ||||
| 			fatalParserError(to_string(m_scanner->currentError())); | ||||
| 		expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal)); | ||||
| 		break; | ||||
| 	} | ||||
| 	case Token::Identifier: | ||||
| 		nodeFactory.markEndPosition(); | ||||
| 		expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); | ||||
|  | ||||
| @ -60,12 +60,12 @@ bool AsmAnalyzer::analyze(Block const& _block) | ||||
| 
 | ||||
| 		success = (*this)(_block); | ||||
| 		if (!success) | ||||
| 			solAssert(m_errorReporter.hasErrors(), "No success but no error."); | ||||
| 			yulAssert(m_errorReporter.hasErrors(), "No success but no error."); | ||||
| 	} | ||||
| 	catch (FatalError const&) | ||||
| 	{ | ||||
| 		// This FatalError con occur if the errorReporter has too many errors.
 | ||||
| 		solAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); | ||||
| 		yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); | ||||
| 	} | ||||
| 	return success && !m_errorReporter.hasErrors(); | ||||
| } | ||||
| @ -83,13 +83,13 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, | ||||
| 		{}, | ||||
| 		_object.dataNames() | ||||
| 	).analyze(*_object.code); | ||||
| 	solAssert(success && errorList.empty(), "Invalid assembly/yul code."); | ||||
| 	yulAssert(success && errorList.empty(), "Invalid assembly/yul code."); | ||||
| 	return analysisInfo; | ||||
| } | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(Label const& _label) | ||||
| { | ||||
| 	solAssert(!_label.name.empty(), ""); | ||||
| 	yulAssert(!_label.name.empty(), ""); | ||||
| 	checkLooseFeature( | ||||
| 		_label.location, | ||||
| 		"The use of labels is disallowed. Please use \"if\", \"switch\", \"for\" or function calls instead." | ||||
| @ -134,8 +134,8 @@ bool AsmAnalyzer::operator()(Literal const& _literal) | ||||
| 	} | ||||
| 	else if (_literal.kind == LiteralKind::Boolean) | ||||
| 	{ | ||||
| 		solAssert(m_dialect.flavour == AsmFlavour::Yul, ""); | ||||
| 		solAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); | ||||
| 		yulAssert(m_dialect.flavour == AsmFlavour::Yul, ""); | ||||
| 		yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); | ||||
| 	} | ||||
| 	m_info.stackHeightInfo[&_literal] = m_stackHeight; | ||||
| 	return true; | ||||
| @ -143,10 +143,10 @@ bool AsmAnalyzer::operator()(Literal const& _literal) | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(Identifier const& _identifier) | ||||
| { | ||||
| 	solAssert(!_identifier.name.empty(), ""); | ||||
| 	yulAssert(!_identifier.name.empty(), ""); | ||||
| 	size_t numErrorsBefore = m_errorReporter.errors().size(); | ||||
| 	bool success = true; | ||||
| 	if (m_currentScope->lookup(_identifier.name, Scope::Visitor( | ||||
| 	if (m_currentScope->lookup(_identifier.name, GenericVisitor{ | ||||
| 		[&](Scope::Variable const& _var) | ||||
| 		{ | ||||
| 			if (!m_activeVariables.count(&_var)) | ||||
| @ -171,7 +171,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) | ||||
| 			); | ||||
| 			success = false; | ||||
| 		} | ||||
| 	))) | ||||
| 	})) | ||||
| 	{ | ||||
| 	} | ||||
| 	else | ||||
| @ -197,14 +197,14 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) | ||||
| { | ||||
| 	solAssert(m_dialect.flavour != AsmFlavour::Yul, ""); | ||||
| 	yulAssert(m_dialect.flavour != AsmFlavour::Yul, ""); | ||||
| 	bool success = true; | ||||
| 	for (auto const& arg: _instr.arguments | boost::adaptors::reversed) | ||||
| 		if (!expectExpression(arg)) | ||||
| 			success = false; | ||||
| 	// Parser already checks that the number of arguments is correct.
 | ||||
| 	auto const& info = instructionInfo(_instr.instruction); | ||||
| 	solAssert(info.args == int(_instr.arguments.size()), ""); | ||||
| 	yulAssert(info.args == int(_instr.arguments.size()), ""); | ||||
| 	m_stackHeight += info.ret - info.args; | ||||
| 	m_info.stackHeightInfo[&_instr] = m_stackHeight; | ||||
| 	warnOnInstructions(_instr.instruction, _instr.location); | ||||
| @ -214,7 +214,7 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) | ||||
| bool AsmAnalyzer::operator()(ExpressionStatement const& _statement) | ||||
| { | ||||
| 	int initialStackHeight = m_stackHeight; | ||||
| 	bool success = boost::apply_visitor(*this, _statement.expression); | ||||
| 	bool success = std::visit(*this, _statement.expression); | ||||
| 	if (m_stackHeight != initialStackHeight && (m_dialect.flavour != AsmFlavour::Loose || m_errorTypeForLoose)) | ||||
| 	{ | ||||
| 		Error::Type errorType = m_dialect.flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; | ||||
| @ -245,11 +245,11 @@ bool AsmAnalyzer::operator()(StackAssignment const& _assignment) | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(Assignment const& _assignment) | ||||
| { | ||||
| 	solAssert(_assignment.value, ""); | ||||
| 	yulAssert(_assignment.value, ""); | ||||
| 	int const expectedItems = _assignment.variableNames.size(); | ||||
| 	solAssert(expectedItems >= 1, ""); | ||||
| 	yulAssert(expectedItems >= 1, ""); | ||||
| 	int const stackHeight = m_stackHeight; | ||||
| 	bool success = boost::apply_visitor(*this, *_assignment.value); | ||||
| 	bool success = std::visit(*this, *_assignment.value); | ||||
| 	if ((m_stackHeight - stackHeight) != expectedItems) | ||||
| 	{ | ||||
| 		m_errorReporter.declarationError( | ||||
| @ -276,7 +276,7 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) | ||||
| 	if (_varDecl.value) | ||||
| 	{ | ||||
| 		int const stackHeight = m_stackHeight; | ||||
| 		success = boost::apply_visitor(*this, *_varDecl.value); | ||||
| 		success = std::visit(*this, *_varDecl.value); | ||||
| 		int numValues = m_stackHeight - stackHeight; | ||||
| 		if (numValues != numVariables) | ||||
| 		{ | ||||
| @ -298,7 +298,7 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) | ||||
| 	for (auto const& variable: _varDecl.variables) | ||||
| 	{ | ||||
| 		expectValidType(variable.type.str(), variable.location); | ||||
| 		m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name))); | ||||
| 		m_activeVariables.insert(&std::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name))); | ||||
| 	} | ||||
| 	m_info.stackHeightInfo[&_varDecl] = m_stackHeight; | ||||
| 	return success; | ||||
| @ -306,14 +306,14 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) | ||||
| { | ||||
| 	solAssert(!_funDef.name.empty(), ""); | ||||
| 	yulAssert(!_funDef.name.empty(), ""); | ||||
| 	Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); | ||||
| 	solAssert(virtualBlock, ""); | ||||
| 	yulAssert(virtualBlock, ""); | ||||
| 	Scope& varScope = scope(virtualBlock); | ||||
| 	for (auto const& var: _funDef.parameters + _funDef.returnVariables) | ||||
| 	{ | ||||
| 		expectValidType(var.type.str(), var.location); | ||||
| 		m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name))); | ||||
| 		m_activeVariables.insert(&std::get<Scope::Variable>(varScope.identifiers.at(var.name))); | ||||
| 	} | ||||
| 
 | ||||
| 	int const stackHeight = m_stackHeight; | ||||
| @ -328,7 +328,7 @@ bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(FunctionCall const& _funCall) | ||||
| { | ||||
| 	solAssert(!_funCall.functionName.name.empty(), ""); | ||||
| 	yulAssert(!_funCall.functionName.name.empty(), ""); | ||||
| 	bool success = true; | ||||
| 	size_t parameters = 0; | ||||
| 	size_t returns = 0; | ||||
| @ -341,7 +341,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) | ||||
| 		if (f->literalArguments) | ||||
| 			needsLiteralArguments = true; | ||||
| 	} | ||||
| 	else if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor( | ||||
| 	else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{ | ||||
| 		[&](Scope::Variable const&) | ||||
| 		{ | ||||
| 			m_errorReporter.typeError( | ||||
| @ -364,7 +364,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) | ||||
| 			parameters = _fun.arguments.size(); | ||||
| 			returns = _fun.returns.size(); | ||||
| 		} | ||||
| 	))) | ||||
| 	})) | ||||
| 	{ | ||||
| 		m_errorReporter.declarationError(_funCall.functionName.location, "Function not found."); | ||||
| 		success = false; | ||||
| @ -388,15 +388,15 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) | ||||
| 			success = false; | ||||
| 		else if (needsLiteralArguments) | ||||
| 		{ | ||||
| 			if (arg.type() != typeid(Literal)) | ||||
| 			if (!holds_alternative<Literal>(arg)) | ||||
| 				m_errorReporter.typeError( | ||||
| 					_funCall.functionName.location, | ||||
| 					"Function expects direct literals as arguments." | ||||
| 				); | ||||
| 			else if (!m_dataNames.count(boost::get<Literal>(arg).value)) | ||||
| 			else if (!m_dataNames.count(std::get<Literal>(arg).value)) | ||||
| 				m_errorReporter.typeError( | ||||
| 					_funCall.functionName.location, | ||||
| 					"Unknown data object \"" + boost::get<Literal>(arg).value.str() + "\"." | ||||
| 					"Unknown data object \"" + std::get<Literal>(arg).value.str() + "\"." | ||||
| 				); | ||||
| 		} | ||||
| 	} | ||||
| @ -426,7 +426,7 @@ bool AsmAnalyzer::operator()(If const& _if) | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(Switch const& _switch) | ||||
| { | ||||
| 	solAssert(_switch.expression, ""); | ||||
| 	yulAssert(_switch.expression, ""); | ||||
| 
 | ||||
| 	bool success = true; | ||||
| 
 | ||||
| @ -498,7 +498,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch) | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(ForLoop const& _for) | ||||
| { | ||||
| 	solAssert(_for.condition, ""); | ||||
| 	yulAssert(_for.condition, ""); | ||||
| 
 | ||||
| 	Scope* outerScope = m_currentScope; | ||||
| 
 | ||||
| @ -556,7 +556,7 @@ bool AsmAnalyzer::operator()(Block const& _block) | ||||
| 	int const initialStackHeight = m_stackHeight; | ||||
| 
 | ||||
| 	for (auto const& s: _block.statements) | ||||
| 		if (!boost::apply_visitor(*this, s)) | ||||
| 		if (!std::visit(*this, s)) | ||||
| 			success = false; | ||||
| 
 | ||||
| 	m_stackHeight -= scope(&_block).numberOfVariables(); | ||||
| @ -585,7 +585,7 @@ bool AsmAnalyzer::expectExpression(Expression const& _expr) | ||||
| { | ||||
| 	bool success = true; | ||||
| 	int const initialHeight = m_stackHeight; | ||||
| 	if (!boost::apply_visitor(*this, _expr)) | ||||
| 	if (!std::visit(*this, _expr)) | ||||
| 		success = false; | ||||
| 	if (!expectDeposit(1, initialHeight, locationOf(_expr))) | ||||
| 		success = false; | ||||
| @ -609,19 +609,19 @@ bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation con | ||||
| 
 | ||||
| bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize) | ||||
| { | ||||
| 	solAssert(!_variable.name.empty(), ""); | ||||
| 	yulAssert(!_variable.name.empty(), ""); | ||||
| 	bool success = true; | ||||
| 	size_t numErrorsBefore = m_errorReporter.errors().size(); | ||||
| 	size_t variableSize(-1); | ||||
| 	if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) | ||||
| 	{ | ||||
| 		// Check that it is a variable
 | ||||
| 		if (var->type() != typeid(Scope::Variable)) | ||||
| 		if (!holds_alternative<Scope::Variable>(*var)) | ||||
| 		{ | ||||
| 			m_errorReporter.typeError(_variable.location, "Assignment requires variable."); | ||||
| 			success = false; | ||||
| 		} | ||||
| 		else if (!m_activeVariables.count(&boost::get<Scope::Variable>(*var))) | ||||
| 		else if (!m_activeVariables.count(&std::get<Scope::Variable>(*var))) | ||||
| 		{ | ||||
| 			m_errorReporter.declarationError( | ||||
| 				_variable.location, | ||||
| @ -665,9 +665,9 @@ bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize | ||||
| 
 | ||||
| Scope& AsmAnalyzer::scope(Block const* _block) | ||||
| { | ||||
| 	solAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present."); | ||||
| 	yulAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present."); | ||||
| 	auto scopePtr = m_info.scopes.at(_block); | ||||
| 	solAssert(scopePtr, "Scope requested but not present."); | ||||
| 	yulAssert(scopePtr, "Scope requested but not present."); | ||||
| 	return *scopePtr; | ||||
| } | ||||
| void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) | ||||
| @ -686,10 +686,10 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio | ||||
| { | ||||
| 	// We assume that returndatacopy, returndatasize and staticcall are either all available
 | ||||
| 	// or all not available.
 | ||||
| 	solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); | ||||
| 	yulAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); | ||||
| 	// Similarly we assume bitwise shifting and create2 go together.
 | ||||
| 	solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); | ||||
| 	solAssert(m_dialect.flavour != AsmFlavour::Yul, ""); | ||||
| 	yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); | ||||
| 	yulAssert(m_dialect.flavour != AsmFlavour::Yul, ""); | ||||
| 
 | ||||
| 	auto errorForVM = [=](string const& vmKindMessage) { | ||||
| 		m_errorReporter.typeError( | ||||
| @ -768,7 +768,7 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio | ||||
| void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description) | ||||
| { | ||||
| 	if (m_dialect.flavour != AsmFlavour::Loose) | ||||
| 		solAssert(false, _description); | ||||
| 		yulAssert(false, _description); | ||||
| 	else if (m_errorTypeForLoose) | ||||
| 		m_errorReporter.error(*m_errorTypeForLoose, _location, _description); | ||||
| } | ||||
|  | ||||
| @ -30,8 +30,6 @@ | ||||
| #include <libyul/backends/evm/AbstractAssembly.h> | ||||
| #include <libyul/backends/evm/EVMDialect.h> | ||||
| 
 | ||||
| #include <boost/variant.hpp> | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <list> | ||||
| #include <memory> | ||||
| @ -53,7 +51,7 @@ struct AsmAnalysisInfo; | ||||
|  * references and performs other checks. | ||||
|  * If all these checks pass, code generation should not throw errors. | ||||
|  */ | ||||
| class AsmAnalyzer: public boost::static_visitor<bool> | ||||
| class AsmAnalyzer | ||||
| { | ||||
| public: | ||||
| 	explicit AsmAnalyzer( | ||||
|  | ||||
| @ -22,8 +22,6 @@ | ||||
| 
 | ||||
| #include <libyul/AsmDataForward.h> | ||||
| 
 | ||||
| #include <boost/variant.hpp> | ||||
| 
 | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
|  | ||||
| @ -28,7 +28,6 @@ | ||||
| #include <libevmasm/Instruction.h> | ||||
| #include <liblangutil/SourceLocation.h> | ||||
| 
 | ||||
| #include <boost/variant.hpp> | ||||
| #include <boost/noncopyable.hpp> | ||||
| 
 | ||||
| #include <map> | ||||
| @ -83,7 +82,7 @@ struct Break { langutil::SourceLocation location; }; | ||||
| /// Continue statement (valid within for loop)
 | ||||
| struct Continue { langutil::SourceLocation location; }; | ||||
| 
 | ||||
| struct LocationExtractor: boost::static_visitor<langutil::SourceLocation> | ||||
| struct LocationExtractor | ||||
| { | ||||
| 	template <class T> langutil::SourceLocation operator()(T const& _node) const | ||||
| 	{ | ||||
| @ -94,7 +93,7 @@ struct LocationExtractor: boost::static_visitor<langutil::SourceLocation> | ||||
| /// Extracts the source location from an inline assembly node.
 | ||||
| template <class T> inline langutil::SourceLocation locationOf(T const& _node) | ||||
| { | ||||
| 	return boost::apply_visitor(LocationExtractor(), _node); | ||||
| 	return std::visit(LocationExtractor(), _node); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -22,7 +22,7 @@ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <boost/variant.hpp> | ||||
| #include <variant> | ||||
| 
 | ||||
| namespace yul | ||||
| { | ||||
| @ -48,7 +48,7 @@ struct Block; | ||||
| 
 | ||||
| struct TypedName; | ||||
| 
 | ||||
| using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; | ||||
| using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>; | ||||
| using Expression = std::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; | ||||
| using Statement = std::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -21,6 +21,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include <libyul/AsmParser.h> | ||||
| #include <libyul/Exceptions.h> | ||||
| #include <liblangutil/Scanner.h> | ||||
| #include <liblangutil/ErrorReporter.h> | ||||
| #include <libdevcore/Common.h> | ||||
| @ -52,7 +53,7 @@ shared_ptr<Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _ | ||||
| 	} | ||||
| 	catch (FatalError const&) | ||||
| 	{ | ||||
| 		solAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); | ||||
| 		yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); | ||||
| 	} | ||||
| 
 | ||||
| 	return nullptr; | ||||
| @ -184,7 +185,7 @@ Statement Parser::parseStatement() | ||||
| 
 | ||||
| 		while (true) | ||||
| 		{ | ||||
| 			if (elementary.type() != typeid(Identifier)) | ||||
| 			if (!holds_alternative<Identifier>(elementary)) | ||||
| 			{ | ||||
| 				auto const token = currentToken() == Token::Comma ? "," : ":="; | ||||
| 
 | ||||
| @ -196,7 +197,7 @@ Statement Parser::parseStatement() | ||||
| 				); | ||||
| 			} | ||||
| 
 | ||||
| 			auto const& identifier = boost::get<Identifier>(elementary); | ||||
| 			auto const& identifier = std::get<Identifier>(elementary); | ||||
| 
 | ||||
| 			if (m_dialect.builtin(identifier.name)) | ||||
| 				fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\"."); | ||||
| @ -212,22 +213,22 @@ Statement Parser::parseStatement() | ||||
| 		} | ||||
| 
 | ||||
| 		Assignment assignment = | ||||
| 			createWithLocation<Assignment>(boost::get<Identifier>(elementary).location); | ||||
| 			createWithLocation<Assignment>(std::get<Identifier>(elementary).location); | ||||
| 		assignment.variableNames = std::move(variableNames); | ||||
| 
 | ||||
| 		expectToken(Token::AssemblyAssign); | ||||
| 
 | ||||
| 		assignment.value.reset(new Expression(parseExpression())); | ||||
| 		assignment.value = make_unique<Expression>(parseExpression()); | ||||
| 		assignment.location.end = locationOf(*assignment.value).end; | ||||
| 
 | ||||
| 		return Statement{std::move(assignment)}; | ||||
| 	} | ||||
| 	case Token::Colon: | ||||
| 	{ | ||||
| 		if (elementary.type() != typeid(Identifier)) | ||||
| 		if (!holds_alternative<Identifier>(elementary)) | ||||
| 			fatalParserError("Label name must precede \":\"."); | ||||
| 
 | ||||
| 		Identifier const& identifier = boost::get<Identifier>(elementary); | ||||
| 		Identifier const& identifier = std::get<Identifier>(elementary); | ||||
| 
 | ||||
| 		advance(); | ||||
| 
 | ||||
| @ -245,20 +246,20 @@ Statement Parser::parseStatement() | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (elementary.type() == typeid(Identifier)) | ||||
| 	if (holds_alternative<Identifier>(elementary)) | ||||
| 	{ | ||||
| 		Identifier& identifier = boost::get<Identifier>(elementary); | ||||
| 		Identifier& identifier = std::get<Identifier>(elementary); | ||||
| 		return ExpressionStatement{identifier.location, { move(identifier) }}; | ||||
| 	} | ||||
| 	else if (elementary.type() == typeid(Literal)) | ||||
| 	else if (holds_alternative<Literal>(elementary)) | ||||
| 	{ | ||||
| 		Expression expr = boost::get<Literal>(elementary); | ||||
| 		Expression expr = std::get<Literal>(elementary); | ||||
| 		return ExpressionStatement{locationOf(expr), expr}; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		solAssert(elementary.type() == typeid(Instruction), "Invalid elementary operation."); | ||||
| 		return boost::get<Instruction>(elementary); | ||||
| 		yulAssert(holds_alternative<Instruction>(elementary), "Invalid elementary operation."); | ||||
| 		return std::get<Instruction>(elementary); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -272,12 +273,12 @@ Case Parser::parseCase() | ||||
| 	{ | ||||
| 		advance(); | ||||
| 		ElementaryOperation literal = parseElementaryOperation(); | ||||
| 		if (literal.type() != typeid(Literal)) | ||||
| 		if (!holds_alternative<Literal>(literal)) | ||||
| 			fatalParserError("Literal expected."); | ||||
| 		_case.value = make_unique<Literal>(boost::get<Literal>(std::move(literal))); | ||||
| 		_case.value = make_unique<Literal>(std::get<Literal>(std::move(literal))); | ||||
| 	} | ||||
| 	else | ||||
| 		solAssert(false, "Case or default case expected."); | ||||
| 		yulAssert(false, "Case or default case expected."); | ||||
| 	_case.body = parseBlock(); | ||||
| 	_case.location.end = _case.body.location.end; | ||||
| 	return _case; | ||||
| @ -311,12 +312,12 @@ Expression Parser::parseExpression() | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 
 | ||||
| 	ElementaryOperation operation = parseElementaryOperation(); | ||||
| 	if (operation.type() == typeid(FunctionCall)) | ||||
| 	if (holds_alternative<FunctionCall>(operation)) | ||||
| 		return parseCall(std::move(operation)); | ||||
| 	else if (operation.type() == typeid(Instruction)) | ||||
| 	else if (holds_alternative<Instruction>(operation)) | ||||
| 	{ | ||||
| 		solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); | ||||
| 		Instruction const& instr = boost::get<Instruction>(operation); | ||||
| 		yulAssert(m_dialect.flavour == AsmFlavour::Loose, ""); | ||||
| 		Instruction const& instr = std::get<Instruction>(operation); | ||||
| 		// Disallow instructions returning multiple values (and DUP/SWAP) as expression.
 | ||||
| 		if ( | ||||
| 			instructionInfo(instr.instruction).ret != 1 || | ||||
| @ -345,19 +346,19 @@ Expression Parser::parseExpression() | ||||
| 	} | ||||
| 	if (currentToken() == Token::LParen) | ||||
| 		return parseCall(std::move(operation)); | ||||
| 	else if (operation.type() == typeid(Instruction)) | ||||
| 	else if (holds_alternative<Instruction>(operation)) | ||||
| 	{ | ||||
| 		// Instructions not taking arguments are allowed as expressions.
 | ||||
| 		solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); | ||||
| 		Instruction& instr = boost::get<Instruction>(operation); | ||||
| 		yulAssert(m_dialect.flavour == AsmFlavour::Loose, ""); | ||||
| 		Instruction& instr = std::get<Instruction>(operation); | ||||
| 		return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; | ||||
| 	} | ||||
| 	else if (operation.type() == typeid(Identifier)) | ||||
| 		return boost::get<Identifier>(operation); | ||||
| 	else if (holds_alternative<Identifier>(operation)) | ||||
| 		return std::get<Identifier>(operation); | ||||
| 	else | ||||
| 	{ | ||||
| 		solAssert(operation.type() == typeid(Literal), ""); | ||||
| 		return boost::get<Literal>(operation); | ||||
| 		yulAssert(holds_alternative<Literal>(operation), ""); | ||||
| 		return std::get<Literal>(operation); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -537,10 +538,10 @@ FunctionDefinition Parser::parseFunctionDefinition() | ||||
| Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) | ||||
| { | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	if (_initialOp.type() == typeid(Instruction)) | ||||
| 	if (holds_alternative<Instruction>(_initialOp)) | ||||
| 	{ | ||||
| 		solAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); | ||||
| 		Instruction& instruction = boost::get<Instruction>(_initialOp); | ||||
| 		yulAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); | ||||
| 		Instruction& instruction = std::get<Instruction>(_initialOp); | ||||
| 		FunctionalInstruction ret; | ||||
| 		ret.instruction = instruction.instruction; | ||||
| 		ret.location = std::move(instruction.location); | ||||
| @ -591,16 +592,16 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) | ||||
| 		expectToken(Token::RParen); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	else if (_initialOp.type() == typeid(Identifier) || _initialOp.type() == typeid(FunctionCall)) | ||||
| 	else if (holds_alternative<Identifier>(_initialOp) || holds_alternative<FunctionCall>(_initialOp)) | ||||
| 	{ | ||||
| 		FunctionCall ret; | ||||
| 		if (_initialOp.type() == typeid(Identifier)) | ||||
| 		if (holds_alternative<Identifier>(_initialOp)) | ||||
| 		{ | ||||
| 			ret.functionName = std::move(boost::get<Identifier>(_initialOp)); | ||||
| 			ret.functionName = std::move(std::get<Identifier>(_initialOp)); | ||||
| 			ret.location = ret.functionName.location; | ||||
| 		} | ||||
| 		else | ||||
| 			ret = std::move(boost::get<FunctionCall>(_initialOp)); | ||||
| 			ret = std::move(std::get<FunctionCall>(_initialOp)); | ||||
| 		expectToken(Token::LParen); | ||||
| 		if (currentToken() != Token::RParen) | ||||
| 		{ | ||||
|  | ||||
| @ -30,9 +30,9 @@ | ||||
| #include <liblangutil/ParserBase.h> | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <variant> | ||||
| #include <vector> | ||||
| 
 | ||||
| 
 | ||||
| namespace yul | ||||
| { | ||||
| 
 | ||||
| @ -56,7 +56,7 @@ public: | ||||
| 	static std::map<std::string, dev::eth::Instruction> const& instructions(); | ||||
| 
 | ||||
| protected: | ||||
| 	using ElementaryOperation = boost::variant<Instruction, Literal, Identifier, FunctionCall>; | ||||
| 	using ElementaryOperation = std::variant<Instruction, Literal, Identifier, FunctionCall>; | ||||
| 
 | ||||
| 	/// Creates an inline assembly node with the given source location.
 | ||||
| 	template <class T> T createWithLocation(langutil::SourceLocation const& _loc = {}) const | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user