mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7716 from ethereum/develop
Merge develop into release for 0.5.13
This commit is contained in:
commit
5b0b510c02
@ -104,17 +104,6 @@ defaults:
|
|||||||
name: command line tests
|
name: command line tests
|
||||||
command: ./test/cmdlineTests.sh
|
command: ./test/cmdlineTests.sh
|
||||||
|
|
||||||
- test_ubuntu1904: &test_ubuntu1904
|
|
||||||
docker:
|
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- attach_workspace:
|
|
||||||
at: build
|
|
||||||
- run: *run_soltest
|
|
||||||
- store_test_results: *store_test_results
|
|
||||||
- store_artifacts: *artifacts_test_results
|
|
||||||
|
|
||||||
- test_ubuntu1904_clang: &test_ubuntu1904_clang
|
- test_ubuntu1904_clang: &test_ubuntu1904_clang
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
|
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
|
||||||
@ -126,7 +115,7 @@ defaults:
|
|||||||
- store_test_results: *store_test_results
|
- store_test_results: *store_test_results
|
||||||
- store_artifacts: *artifacts_test_results
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
- test_ubuntu1904_all: &test_ubuntu1904
|
- test_ubuntu1904: &test_ubuntu1904
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904
|
- image: ethereum/solidity-buildpack-deps:ubuntu1904
|
||||||
steps:
|
steps:
|
||||||
@ -423,40 +412,61 @@ jobs:
|
|||||||
|
|
||||||
b_osx:
|
b_osx:
|
||||||
macos:
|
macos:
|
||||||
xcode: "10.0.0"
|
xcode: "11.0.0"
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
CMAKE_OPTIONS: -DLLL=ON
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||||
- run:
|
- run:
|
||||||
name: Install build dependencies
|
name: Install build dependencies
|
||||||
command: |
|
command: ./.circleci/osx_install_dependencies.sh
|
||||||
brew unlink python
|
- save_cache:
|
||||||
brew install z3
|
key: dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||||
brew install boost
|
paths:
|
||||||
brew install cmake
|
- /usr/local/bin
|
||||||
brew install wget
|
- /usr/local/sbin
|
||||||
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
- /usr/local/lib
|
||||||
|
- /usr/local/include
|
||||||
|
- /usr/local/Cellar
|
||||||
|
- /usr/local/Homebrew
|
||||||
- run: *run_build
|
- run: *run_build
|
||||||
- store_artifacts: *artifacts_solc
|
- store_artifacts: *artifacts_solc
|
||||||
- persist_to_workspace: *artifacts_executables
|
- persist_to_workspace: *artifacts_build_dir
|
||||||
|
|
||||||
|
t_osx_soltest:
|
||||||
|
macos:
|
||||||
|
xcode: "11.0.0"
|
||||||
|
environment:
|
||||||
|
EVM: constantinople
|
||||||
|
OPTIMIZE: 0
|
||||||
|
TERM: xterm
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||||
|
- attach_workspace:
|
||||||
|
at: build
|
||||||
|
- run: *run_soltest
|
||||||
|
- store_test_results: *store_test_results
|
||||||
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
t_osx_cli:
|
t_osx_cli:
|
||||||
macos:
|
macos:
|
||||||
xcode: "10.0.0"
|
xcode: "11.0.0"
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: build
|
at: build
|
||||||
- run:
|
|
||||||
name: Install dependencies
|
|
||||||
command: |
|
|
||||||
brew unlink python
|
|
||||||
brew install z3
|
|
||||||
- run: *run_cmdline_tests
|
- run: *run_cmdline_tests
|
||||||
- store_artifacts: *artifacts_test_results
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
@ -568,7 +578,7 @@ jobs:
|
|||||||
environment:
|
environment:
|
||||||
EVM: constantinople
|
EVM: constantinople
|
||||||
OPTIMIZE: 0
|
OPTIMIZE: 0
|
||||||
flags: --no-smt
|
SOLTEST_FLAGS: --no-smt
|
||||||
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
|
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
|
||||||
|
|
||||||
t_ems_solcjs:
|
t_ems_solcjs:
|
||||||
@ -660,6 +670,7 @@ workflows:
|
|||||||
# OS/X build and tests
|
# OS/X build and tests
|
||||||
- b_osx: *workflow_trigger_on_tags
|
- b_osx: *workflow_trigger_on_tags
|
||||||
- t_osx_cli: *workflow_osx
|
- t_osx_cli: *workflow_osx
|
||||||
|
- t_osx_soltest: *workflow_osx
|
||||||
|
|
||||||
# Ubuntu build and tests
|
# Ubuntu build and tests
|
||||||
- b_ubu: *workflow_trigger_on_tags
|
- b_ubu: *workflow_trigger_on_tags
|
||||||
|
@ -57,12 +57,12 @@ RUN git clone --recursive -b boost-1.69.0 https://github.com/boostorg/boost.git
|
|||||||
./bootstrap.sh --with-toolset=clang --prefix=/usr; \
|
./bootstrap.sh --with-toolset=clang --prefix=/usr; \
|
||||||
./b2 toolset=clang headers; \
|
./b2 toolset=clang headers; \
|
||||||
./b2 toolset=clang variant=release \
|
./b2 toolset=clang variant=release \
|
||||||
system regex filesystem unit_test_framework program_options \
|
system filesystem unit_test_framework program_options \
|
||||||
install -j $(($(nproc)/2)); \
|
install -j $(($(nproc)/2)); \
|
||||||
rm -rf /usr/src/boost
|
rm -rf /usr/src/boost
|
||||||
|
|
||||||
# Z3
|
# Z3
|
||||||
RUN git clone --depth 1 -b Z3-4.8.5 https://github.com/Z3Prover/z3.git \
|
RUN git clone --depth 1 -b z3-4.8.6 https://github.com/Z3Prover/z3.git \
|
||||||
/usr/src/z3; \
|
/usr/src/z3; \
|
||||||
cd /usr/src/z3; \
|
cd /usr/src/z3; \
|
||||||
python scripts/mk_make.py --prefix=/usr ; \
|
python scripts/mk_make.py --prefix=/usr ; \
|
||||||
|
@ -34,7 +34,7 @@ RUN set -ex; \
|
|||||||
build-essential \
|
build-essential \
|
||||||
software-properties-common \
|
software-properties-common \
|
||||||
cmake ninja-build clang++-8 \
|
cmake ninja-build clang++-8 \
|
||||||
libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev \
|
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
|
||||||
libboost-program-options-dev \
|
libboost-program-options-dev \
|
||||||
libjsoncpp-dev \
|
libjsoncpp-dev \
|
||||||
llvm-8-dev libz3-static-dev \
|
llvm-8-dev libz3-static-dev \
|
||||||
|
@ -34,7 +34,7 @@ RUN set -ex; \
|
|||||||
build-essential \
|
build-essential \
|
||||||
software-properties-common \
|
software-properties-common \
|
||||||
cmake ninja-build clang++-8 libc++-8-dev libc++abi-8-dev \
|
cmake ninja-build clang++-8 libc++-8-dev libc++abi-8-dev \
|
||||||
libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev \
|
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
|
||||||
libboost-program-options-dev \
|
libboost-program-options-dev \
|
||||||
libjsoncpp-dev \
|
libjsoncpp-dev \
|
||||||
llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \
|
llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \
|
||||||
|
59
.circleci/osx_install_dependencies.sh
Executable file
59
.circleci/osx_install_dependencies.sh
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Bash script to install osx dependencies
|
||||||
|
#
|
||||||
|
# The documentation for solidity is hosted at:
|
||||||
|
#
|
||||||
|
# https://solidity.readthedocs.org
|
||||||
|
#
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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/>
|
||||||
|
#
|
||||||
|
# (c) 2016-2019 solidity contributors.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# note that the following directories may be cached by circleci:
|
||||||
|
# - /usr/local/bin
|
||||||
|
# - /usr/local/sbin
|
||||||
|
# - /usr/local/lib
|
||||||
|
# - /usr/local/include
|
||||||
|
# - /usr/local/Cellar
|
||||||
|
# - /usr/local/Homebrew
|
||||||
|
|
||||||
|
if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies
|
||||||
|
then
|
||||||
|
brew unlink python
|
||||||
|
brew install boost
|
||||||
|
brew install cmake
|
||||||
|
brew install wget
|
||||||
|
brew install coreutils
|
||||||
|
./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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
fi
|
||||||
|
|
@ -12,6 +12,7 @@
|
|||||||
# EVM=version_string Specifies EVM version to compile for (such as homestead, etc)
|
# EVM=version_string Specifies EVM version to compile for (such as homestead, etc)
|
||||||
# OPTIMIZE=1 Enables backend optimizer
|
# OPTIMIZE=1 Enables backend optimizer
|
||||||
# ABI_ENCODER_V2=1 Enables ABI encoder version 2
|
# ABI_ENCODER_V2=1 Enables ABI encoder version 2
|
||||||
|
# SOLTEST_FLAGS=<flags> Appends <flags> to default SOLTEST_ARGS
|
||||||
#
|
#
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# This file is part of solidity.
|
# This file is part of solidity.
|
||||||
@ -54,7 +55,7 @@ get_logfile_basename() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml"
|
BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml"
|
||||||
SOLTEST_ARGS="--evm-version=$EVM --evmonepath /usr/lib/libevmone.so $flags"
|
SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS"
|
||||||
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
|
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
|
||||||
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul"
|
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ include(EthPolicy)
|
|||||||
eth_policy()
|
eth_policy()
|
||||||
|
|
||||||
# project name and version should be set after cmake_policy CMP0048
|
# project name and version should be set after cmake_policy CMP0048
|
||||||
set(PROJECT_VERSION "0.5.12")
|
set(PROJECT_VERSION "0.5.13")
|
||||||
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
|
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
|
||||||
|
|
||||||
include(TestBigEndian)
|
include(TestBigEndian)
|
||||||
|
@ -119,7 +119,7 @@ Use `solAssert` and `solUnimplementedAssert` generously to check assumptions tha
|
|||||||
4. Favour declarations close to use; don't habitually declare at top of scope ala C.
|
4. Favour declarations close to use; don't habitually declare at top of scope ala C.
|
||||||
5. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move.
|
5. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move.
|
||||||
6. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments.
|
6. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments.
|
||||||
7. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer.
|
7. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``std::optional`` is better suited than a raw pointer.
|
||||||
8. Never use a macro where adequate non-preprocessor C++ can be written.
|
8. Never use a macro where adequate non-preprocessor C++ can be written.
|
||||||
9. Only use ``auto`` if the type is very long and rather irrelevant.
|
9. Only use ``auto`` if the type is very long and rather irrelevant.
|
||||||
10. Do not pass bools: prefer enumerations instead.
|
10. Do not pass bools: prefer enumerations instead.
|
||||||
@ -135,12 +135,12 @@ enum class Accuracy
|
|||||||
};
|
};
|
||||||
struct MeanSigma
|
struct MeanSigma
|
||||||
{
|
{
|
||||||
float mean;
|
float mean = 0.0f;
|
||||||
float standardDeviation;
|
float standardDeviation = 1.0f;
|
||||||
};
|
};
|
||||||
double const d = 0;
|
double const d = 0;
|
||||||
int i;
|
int i = 0;
|
||||||
int j;
|
int j = 0;
|
||||||
char* s;
|
char* s;
|
||||||
MeanAndSigma ms meanAndSigma(std::vector<float> const& _v, Accuracy _a);
|
MeanAndSigma ms meanAndSigma(std::vector<float> const& _v, Accuracy _a);
|
||||||
Derived* x = dynamic_cast<Derived*>(base);
|
Derived* x = dynamic_cast<Derived*>(base);
|
||||||
|
28
Changelog.md
28
Changelog.md
@ -1,3 +1,30 @@
|
|||||||
|
### 0.5.13 (2019-11-14)
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* Allow to obtain the address of a linked library with ``address(LibraryName)``.
|
||||||
|
|
||||||
|
|
||||||
|
Compiler Features:
|
||||||
|
* Code Generator: Use SELFBALANCE opcode for ``address(this).balance`` if using Istanbul EVM.
|
||||||
|
* EWasm: Experimental EWasm binary output via ``--ewasm`` and as documented in standard-json.
|
||||||
|
* SMTChecker: Add break/continue support to the CHC engine.
|
||||||
|
* SMTChecker: Support assignments to multi-dimensional arrays and mappings.
|
||||||
|
* SMTChecker: Support inheritance and function overriding.
|
||||||
|
* Standard JSON Interface: Output the storage layout of a contract when artifact ``storageLayout`` is requested.
|
||||||
|
* TypeChecker: List possible candidates when overload resolution fails.
|
||||||
|
* TypeChecker: Disallow variables of library types.
|
||||||
|
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer.
|
||||||
|
* SMTChecker: Fix internal error when accessing indices of fixed bytes.
|
||||||
|
* SMTChecker: Fix internal error when using function pointers as arguments.
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 0.5.12 (2019-10-01)
|
### 0.5.12 (2019-10-01)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
@ -10,6 +37,7 @@ Compiler Features:
|
|||||||
* SMTChecker: Add loop support to the CHC engine.
|
* SMTChecker: Add loop support to the CHC engine.
|
||||||
* Yul Optimizer: Take side-effect-freeness of user-defined functions into account.
|
* Yul Optimizer: Take side-effect-freeness of user-defined functions into account.
|
||||||
* Yul Optimizer: Remove redundant mload/sload operations.
|
* Yul Optimizer: Remove redundant mload/sload operations.
|
||||||
|
* Yul Optimizer: Use the fact that branch conditions have certain value inside the branch.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -60,6 +60,8 @@ Please follow the
|
|||||||
[Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html)
|
[Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html)
|
||||||
if you want to help.
|
if you want to help.
|
||||||
|
|
||||||
|
You can find our current feature and bug priorities for forthcoming releases [in the projects section](https://github.com/ethereum/solidity/projects).
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
* [@axic](https://github.com/axic)
|
* [@axic](https://github.com/axic)
|
||||||
* [@chriseth](https://github.com/chriseth)
|
* [@chriseth](https://github.com/chriseth)
|
||||||
|
@ -23,6 +23,9 @@ endif()
|
|||||||
|
|
||||||
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
|
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
|
||||||
|
|
||||||
|
# Prevent the path of the source directory from ending up in the binary via __FILE__ macros.
|
||||||
|
eth_add_cxx_compiler_flag_if_supported("-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=/solidity")
|
||||||
|
|
||||||
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
||||||
# Enables all the warnings about constructions that some users consider questionable,
|
# Enables all the warnings about constructions that some users consider questionable,
|
||||||
# and that are easy to avoid. Also enable some extra warning flags that are not
|
# and that are easy to avoid. Also enable some extra warning flags that are not
|
||||||
@ -130,6 +133,8 @@ elseif (DEFINED MSVC)
|
|||||||
add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement
|
add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement
|
||||||
add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions
|
add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions
|
||||||
add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819)
|
add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819)
|
||||||
|
add_compile_options(-DBOOST_REGEX_NO_LIB) # disable automatic boost::regex library selection
|
||||||
|
add_compile_options(-D_REGEX_MAX_STACK_COUNT=200000L) # increase std::regex recursion depth limit
|
||||||
|
|
||||||
# disable empty object file warning
|
# disable empty object file warning
|
||||||
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
|
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
|
||||||
|
@ -26,7 +26,7 @@ set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts)
|
|||||||
set(Boost_USE_MULTITHREADED ON)
|
set(Boost_USE_MULTITHREADED ON)
|
||||||
option(Boost_USE_STATIC_LIBS "Link Boost statically" ON)
|
option(Boost_USE_STATIC_LIBS "Link Boost statically" ON)
|
||||||
|
|
||||||
set(BOOST_COMPONENTS "regex;filesystem;unit_test_framework;program_options;system")
|
set(BOOST_COMPONENTS "filesystem;unit_test_framework;program_options;system")
|
||||||
|
|
||||||
find_package(Boost 1.65.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
find_package(Boost 1.65.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||||
|
|
||||||
|
@ -34,3 +34,17 @@ macro(eth_default_option O DEF)
|
|||||||
endif()
|
endif()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
function(detect_stray_source_files FILELIST DIRECTORY)
|
||||||
|
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||||
|
file(GLOB sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${DIRECTORY}/*.cpp" "${DIRECTORY}/*.h")
|
||||||
|
else()
|
||||||
|
file(GLOB sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CONFIGURE_DEPENDS "${DIRECTORY}/*.cpp" "${DIRECTORY}/*.h")
|
||||||
|
endif()
|
||||||
|
foreach(path IN LISTS FILELIST)
|
||||||
|
list(REMOVE_ITEM sources ${path})
|
||||||
|
endforeach()
|
||||||
|
list(LENGTH sources leftover_sources)
|
||||||
|
if (leftover_sources)
|
||||||
|
message(SEND_ERROR "The following source files are present but are not compiled: ${sources}")
|
||||||
|
endif()
|
||||||
|
endfunction(detect_stray_source_files)
|
||||||
|
@ -456,7 +456,7 @@ New version:
|
|||||||
// Throw is now disallowed.
|
// Throw is now disallowed.
|
||||||
require(x > 100);
|
require(x > 100);
|
||||||
int y = -3 >> 1;
|
int y = -3 >> 1;
|
||||||
// y == -2 (correct)
|
require(y == -2);
|
||||||
do {
|
do {
|
||||||
x += 1;
|
x += 1;
|
||||||
if (x > 10) continue;
|
if (x > 10) continue;
|
||||||
@ -473,7 +473,7 @@ New version:
|
|||||||
|
|
||||||
using address_make_payable for address;
|
using address_make_payable for address;
|
||||||
// Data location for 'arr' must be specified
|
// Data location for 'arr' must be specified
|
||||||
function g(uint[] memory arr, bytes8 x, OtherContract otherContract, address unknownContract) public payable {
|
function g(uint[] memory /* arr */, bytes8 x, OtherContract otherContract, address unknownContract) public payable {
|
||||||
// 'otherContract.transfer' is not provided.
|
// 'otherContract.transfer' is not provided.
|
||||||
// Since the code of 'OtherContract' is known and has the fallback
|
// Since the code of 'OtherContract' is known and has the fallback
|
||||||
// function, address(otherContract) has type 'address payable'.
|
// function, address(otherContract) has type 'address payable'.
|
||||||
|
@ -292,7 +292,7 @@ In the grammar, opcodes are represented as pre-defined identifiers.
|
|||||||
| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address |
|
| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address |
|
||||||
| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) |
|
| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) |
|
||||||
| | | | and send v wei and return the new address, where ``0xff`` is a |
|
| | | | and send v wei and return the new address, where ``0xff`` is a |
|
||||||
| | | | 8 byte value, ``this`` is the current contract's address |
|
| | | | 1 byte value, ``this`` is the current contract's address |
|
||||||
| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value |
|
| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value |
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
| call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) |
|
| call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) |
|
||||||
|
@ -754,6 +754,10 @@
|
|||||||
"bugs": [],
|
"bugs": [],
|
||||||
"released": "2019-10-01"
|
"released": "2019-10-01"
|
||||||
},
|
},
|
||||||
|
"0.5.13": {
|
||||||
|
"bugs": [],
|
||||||
|
"released": "2019-11-14"
|
||||||
|
},
|
||||||
"0.5.2": {
|
"0.5.2": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
"SignedArrayStorageCopy",
|
"SignedArrayStorageCopy",
|
||||||
|
@ -22,9 +22,8 @@ a contract where the goal is to send the most money to the
|
|||||||
contract in order to become the "richest", inspired by
|
contract in order to become the "richest", inspired by
|
||||||
`King of the Ether <https://www.kingoftheether.com/>`_.
|
`King of the Ether <https://www.kingoftheether.com/>`_.
|
||||||
|
|
||||||
In the following contract, if you are usurped as the richest,
|
In the following contract, if you are no longer the richest,
|
||||||
you will receive the funds of the person who has gone on to
|
you receive the funds of the person who is now the richest.
|
||||||
become the new richest.
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -178,6 +178,9 @@ custom types without the overhead of external function calls:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It is possible to obtain the address of a library by converting
|
||||||
|
the library type to the ``address`` type, i.e. using ``address(LibraryName)``.
|
||||||
|
|
||||||
As the compiler cannot know where the library will be
|
As the compiler cannot know where the library will be
|
||||||
deployed at, these addresses have to be filled into the
|
deployed at, these addresses have to be filled into the
|
||||||
final bytecode by a linker
|
final bytecode by a linker
|
||||||
|
@ -158,13 +158,13 @@ to write a function, for example:
|
|||||||
|
|
||||||
// Getter function generated by the compiler
|
// Getter function generated by the compiler
|
||||||
/*
|
/*
|
||||||
function myArray(uint i) returns (uint) {
|
function myArray(uint i) public view returns (uint) {
|
||||||
return myArray[i];
|
return myArray[i];
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// function that returns entire array
|
// function that returns entire array
|
||||||
function getArray() returns (uint[] memory) {
|
function getArray() public view returns (uint[] memory) {
|
||||||
return myArray;
|
return myArray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ Miscellaneous
|
|||||||
Layout of State Variables in Storage
|
Layout of State Variables in Storage
|
||||||
************************************
|
************************************
|
||||||
|
|
||||||
|
.. _storage-inplace-encoding:
|
||||||
|
|
||||||
Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
|
Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
|
||||||
|
|
||||||
- The first item in a storage slot is stored lower-order aligned.
|
- The first item in a storage slot is stored lower-order aligned.
|
||||||
@ -49,6 +51,8 @@ The elements of structs and arrays are stored after each other, just as if they
|
|||||||
Mappings and Dynamic Arrays
|
Mappings and Dynamic Arrays
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
.. _storage-hashed-encoding:
|
||||||
|
|
||||||
Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash
|
Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash
|
||||||
computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
|
computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
|
||||||
|
|
||||||
@ -88,6 +92,267 @@ by checking if the lowest bit is set: short (not set) and long (set).
|
|||||||
.. note::
|
.. note::
|
||||||
Handling invalidly encoded slots is currently not supported but may be added in the future.
|
Handling invalidly encoded slots is currently not supported but may be added in the future.
|
||||||
|
|
||||||
|
JSON Output
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. _storage-layout-top-level:
|
||||||
|
|
||||||
|
The storage layout of a contract can be requested via the :ref:`standard JSON interface <compiler-api>`. The output is a JSON object containing two keys,
|
||||||
|
``storage`` and ``types``. The ``storage`` object is an array where each
|
||||||
|
element has the following form:
|
||||||
|
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"astId": 2,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "x",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint256"
|
||||||
|
}
|
||||||
|
|
||||||
|
where the example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA``
|
||||||
|
and
|
||||||
|
|
||||||
|
- ``astId`` is the id of the AST node of the state variable's declaration
|
||||||
|
- ``contract`` is the name of the contract including its path as prefix
|
||||||
|
- ``label`` is the name of the state variable
|
||||||
|
- ``offset`` is the offset in bytes within the storage slot according to the encoding
|
||||||
|
- ``slot`` is the storage slot where the state variable resides or starts. This
|
||||||
|
number may be very large and therefore its JSON value is represented as a
|
||||||
|
string.
|
||||||
|
- ``type`` is an identifier used as key to the variable's type information (described in the following)
|
||||||
|
|
||||||
|
The given ``type``, in this case ``t_uint256`` represents an element in
|
||||||
|
``types``, which has the form:
|
||||||
|
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
{
|
||||||
|
"encoding": "inplace",
|
||||||
|
"label": "uint256",
|
||||||
|
"numberOfBytes": "32",
|
||||||
|
}
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
- ``encoding`` how the data is encoded in storage, where the possible values are:
|
||||||
|
|
||||||
|
- ``inplace``: data is laid out contiguously in storage (see :ref:`above <storage-inplace-encoding>`).
|
||||||
|
- ``mapping``: Keccak-256 hash-based method (see :ref:`above <storage-hashed-encoding>`).
|
||||||
|
- ``dynamic_array``: Keccak-256 hash-based method (see :ref:`above <storage-hashed-encoding>`).
|
||||||
|
- ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above <bytes-and-string>`).
|
||||||
|
|
||||||
|
- ``label`` is the canonical type name.
|
||||||
|
- ``numberOfBytes`` is the number of used bytes (as a decimal string). Note that if ``numberOfBytes > 32`` this means that more than one slot is used.
|
||||||
|
|
||||||
|
Some types have extra information besides the four above. Mappings contain
|
||||||
|
its ``key`` and ``value`` types (again referencing an entry in this mapping
|
||||||
|
of types), arrays have its ``base`` type, and structs list their ``members`` in
|
||||||
|
the same format as the top-level ``storage`` (see :ref:`above
|
||||||
|
<storage-layout-top-level>`).
|
||||||
|
|
||||||
|
.. note ::
|
||||||
|
The JSON output format of a contract's storage layout is still considered experimental
|
||||||
|
and is subject to change in non-breaking releases of Solidity.
|
||||||
|
|
||||||
|
The following example shows a contract and its storage layout, containing
|
||||||
|
value and reference types, types that are encoded packed, and nested types.
|
||||||
|
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
contract A {
|
||||||
|
struct S {
|
||||||
|
uint128 a;
|
||||||
|
uint128 b;
|
||||||
|
uint[2] staticArray;
|
||||||
|
uint[] dynArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint x;
|
||||||
|
uint y;
|
||||||
|
S s;
|
||||||
|
address addr;
|
||||||
|
mapping (uint => mapping (address => bool)) map;
|
||||||
|
uint[] array;
|
||||||
|
string s1;
|
||||||
|
bytes b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
"storageLayout": {
|
||||||
|
"storage": [
|
||||||
|
{
|
||||||
|
"astId": 14,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "x",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 16,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "y",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1",
|
||||||
|
"type": "t_uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 18,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "s",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2",
|
||||||
|
"type": "t_struct(S)12_storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 20,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "addr",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 26,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "map",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "7",
|
||||||
|
"type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 29,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "array",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "8",
|
||||||
|
"type": "t_array(t_uint256)dyn_storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 31,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "s1",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "9",
|
||||||
|
"type": "t_string_storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 33,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "b1",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "10",
|
||||||
|
"type": "t_bytes_storage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"types": {
|
||||||
|
"t_address": {
|
||||||
|
"encoding": "inplace",
|
||||||
|
"label": "address",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_array(t_uint256)2_storage": {
|
||||||
|
"base": "t_uint256",
|
||||||
|
"encoding": "inplace",
|
||||||
|
"label": "uint256[2]",
|
||||||
|
"numberOfBytes": "64"
|
||||||
|
},
|
||||||
|
"t_array(t_uint256)dyn_storage": {
|
||||||
|
"base": "t_uint256",
|
||||||
|
"encoding": "dynamic_array",
|
||||||
|
"label": "uint256[]",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_bool": {
|
||||||
|
"encoding": "inplace",
|
||||||
|
"label": "bool",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
},
|
||||||
|
"t_bytes_storage": {
|
||||||
|
"encoding": "bytes",
|
||||||
|
"label": "bytes",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_mapping(t_address,t_bool)": {
|
||||||
|
"encoding": "mapping",
|
||||||
|
"key": "t_address",
|
||||||
|
"label": "mapping(address => bool)",
|
||||||
|
"numberOfBytes": "32",
|
||||||
|
"value": "t_bool"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint256,t_mapping(t_address,t_bool))": {
|
||||||
|
"encoding": "mapping",
|
||||||
|
"key": "t_uint256",
|
||||||
|
"label": "mapping(uint256 => mapping(address => bool))",
|
||||||
|
"numberOfBytes": "32",
|
||||||
|
"value": "t_mapping(t_address,t_bool)"
|
||||||
|
},
|
||||||
|
"t_string_storage": {
|
||||||
|
"encoding": "bytes",
|
||||||
|
"label": "string",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_struct(S)12_storage": {
|
||||||
|
"encoding": "inplace",
|
||||||
|
"label": "struct A.S",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"astId": 2,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "a",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 4,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "b",
|
||||||
|
"offset": 16,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 8,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "staticArray",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1",
|
||||||
|
"type": "t_array(t_uint256)2_storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"astId": 11,
|
||||||
|
"contract": "fileA:A",
|
||||||
|
"label": "dynArray",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "3",
|
||||||
|
"type": "t_array(t_uint256)dyn_storage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"numberOfBytes": "128"
|
||||||
|
},
|
||||||
|
"t_uint128": {
|
||||||
|
"encoding": "inplace",
|
||||||
|
"label": "uint128",
|
||||||
|
"numberOfBytes": "16"
|
||||||
|
},
|
||||||
|
"t_uint256": {
|
||||||
|
"encoding": "inplace",
|
||||||
|
"label": "uint256",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.. index: memory layout
|
.. index: memory layout
|
||||||
|
|
||||||
****************
|
****************
|
||||||
|
@ -127,6 +127,9 @@ Solidity Tools
|
|||||||
* `Universal Mutator <https://github.com/agroce/universalmutator>`_
|
* `Universal Mutator <https://github.com/agroce/universalmutator>`_
|
||||||
A tool for mutation generation, with configurable rules and support for Solidity and Vyper.
|
A tool for mutation generation, with configurable rules and support for Solidity and Vyper.
|
||||||
|
|
||||||
|
* `PIET <https://piet.slock.it/>`_
|
||||||
|
A tool to develop, audit and use solidity smart contracts through a simple graphical interface.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Information like variable names, comments, and source code formatting is lost in the compilation process and it is not possible to completely recover the original source code. Decompiling smart contracts to view the original source code might not be possible, or the end result that useful.
|
Information like variable names, comments, and source code formatting is lost in the compilation process and it is not possible to completely recover the original source code. Decompiling smart contracts to view the original source code might not be possible, or the end result that useful.
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ types.
|
|||||||
|
|
||||||
pragma solidity >=0.5.0;
|
pragma solidity >=0.5.0;
|
||||||
pragma experimental SMTChecker;
|
pragma experimental SMTChecker;
|
||||||
// This will not compile
|
// This will report a warning
|
||||||
contract Aliasing
|
contract Aliasing
|
||||||
{
|
{
|
||||||
uint[] array;
|
uint[] array;
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
Mapping Types
|
Mapping Types
|
||||||
=============
|
=============
|
||||||
|
|
||||||
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
|
Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables
|
||||||
|
are declared as a mapping type using the syntax ``mapping (_KeyType => _ValueType) _VariableModifiers _VariableName``.
|
||||||
The ``_KeyType`` can be any elementary type. This means it can be any of
|
The ``_KeyType`` can be any elementary type. This means it can be any of
|
||||||
the built-in value types plus ``bytes`` and ``string``. User-defined
|
the built-in value types plus ``bytes`` and ``string``. User-defined
|
||||||
or complex types like contract types, enums, mappings, structs and any array type
|
or complex types like contract types, enums, mappings, structs and any array type
|
||||||
@ -27,11 +28,16 @@ They cannot be used as parameters or return parameters
|
|||||||
of contract functions that are publicly visible.
|
of contract functions that are publicly visible.
|
||||||
|
|
||||||
You can mark state variables of mapping type as ``public`` and Solidity creates a
|
You can mark state variables of mapping type as ``public`` and Solidity creates a
|
||||||
:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a
|
:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a parameter for the getter.
|
||||||
parameter for the getter. If ``_ValueType`` is a value type or a struct,
|
If ``_ValueType`` is a value type or a struct, the getter returns ``_ValueType``.
|
||||||
the getter returns ``_ValueType``.
|
|
||||||
If ``_ValueType`` is an array or a mapping, the getter has one parameter for
|
If ``_ValueType`` is an array or a mapping, the getter has one parameter for
|
||||||
each ``_KeyType``, recursively. For example with a mapping:
|
each ``_KeyType``, recursively.
|
||||||
|
|
||||||
|
In the example below, the ``MappingExample`` contract defines a public ``balances``
|
||||||
|
mapping, with the key type an ``address``, and a value type a ``uint``, mapping
|
||||||
|
an Ethereum address to an unsigned integer value. As ``uint`` is a value type, the getter
|
||||||
|
returns a value that matches the type, which you can see in the ``MappingUser``
|
||||||
|
contract that returns the value at the specified address.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -53,7 +59,146 @@ each ``_KeyType``, recursively. For example with a mapping:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
The example below is a simplified version of an `ERC20 token <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol>`_.
|
||||||
|
``_allowances`` is an example of a mapping type inside another mapping type.
|
||||||
|
The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account.
|
||||||
|
|
||||||
.. note::
|
::
|
||||||
Mappings are not iterable, but it is possible to implement a data structure
|
|
||||||
on top of them. For an example, see `iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_.
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
|
contract MappingExample {
|
||||||
|
|
||||||
|
mapping (address => uint256) private _balances;
|
||||||
|
mapping (address => mapping (address => uint256)) private _allowances;
|
||||||
|
|
||||||
|
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||||
|
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||||
|
|
||||||
|
function allowance(address owner, address spender) public view returns (uint256) {
|
||||||
|
return _allowances[owner][spender];
|
||||||
|
}
|
||||||
|
|
||||||
|
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
|
||||||
|
_transfer(sender, recipient, amount);
|
||||||
|
approve(sender, msg.sender, amount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function approve(address owner, address spender, uint256 amount) public returns (bool) {
|
||||||
|
require(owner != address(0), "ERC20: approve from the zero address");
|
||||||
|
require(spender != address(0), "ERC20: approve to the zero address");
|
||||||
|
|
||||||
|
_allowances[owner][spender] = amount;
|
||||||
|
emit Approval(owner, spender, amount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _transfer(address sender, address recipient, uint256 amount) internal {
|
||||||
|
require(sender != address(0), "ERC20: transfer from the zero address");
|
||||||
|
require(recipient != address(0), "ERC20: transfer to the zero address");
|
||||||
|
|
||||||
|
_balances[sender] -= amount;
|
||||||
|
_balances[recipient] += amount;
|
||||||
|
emit Transfer(sender, recipient, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.. index:: !iterable mappings
|
||||||
|
.. _iterable-mappings:
|
||||||
|
|
||||||
|
Iterable Mappings
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Mappings are not iterable, but it is possible to implement a data structure on
|
||||||
|
top of them and iterate over that. For example, the code below implements an
|
||||||
|
``IterableMapping`` library that the ``User`` contract then adds data too, and
|
||||||
|
the ``sum`` function iterates over to sum all the values.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
|
library IterableMapping {
|
||||||
|
|
||||||
|
struct itmap {
|
||||||
|
mapping(uint => IndexValue) data;
|
||||||
|
KeyFlag[] keys;
|
||||||
|
uint size;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IndexValue { uint keyIndex; uint value; }
|
||||||
|
struct KeyFlag { uint key; bool deleted; }
|
||||||
|
|
||||||
|
function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
|
||||||
|
uint keyIndex = self.data[key].keyIndex;
|
||||||
|
self.data[key].value = value;
|
||||||
|
if (keyIndex > 0)
|
||||||
|
return true;
|
||||||
|
else {
|
||||||
|
keyIndex = self.keys.length++;
|
||||||
|
self.data[key].keyIndex = keyIndex + 1;
|
||||||
|
self.keys[keyIndex].key = key;
|
||||||
|
self.size++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(itmap storage self, uint key) internal returns (bool success) {
|
||||||
|
uint keyIndex = self.data[key].keyIndex;
|
||||||
|
if (keyIndex == 0)
|
||||||
|
return false;
|
||||||
|
delete self.data[key];
|
||||||
|
self.keys[keyIndex - 1].deleted = true;
|
||||||
|
self.size --;
|
||||||
|
}
|
||||||
|
|
||||||
|
function contains(itmap storage self, uint key) internal view returns (bool) {
|
||||||
|
return self.data[key].keyIndex > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function iterate_start(itmap storage self) internal view returns (uint keyIndex) {
|
||||||
|
return iterate_next(self, uint(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function iterate_valid(itmap storage self, uint keyIndex) internal view returns (bool) {
|
||||||
|
return keyIndex < self.keys.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function iterate_next(itmap storage self, uint keyIndex) internal view returns (uint r_keyIndex) {
|
||||||
|
keyIndex++;
|
||||||
|
while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
|
||||||
|
keyIndex++;
|
||||||
|
return keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function iterate_get(itmap storage self, uint keyIndex) internal view returns (uint key, uint value) {
|
||||||
|
key = self.keys[keyIndex].key;
|
||||||
|
value = self.data[key].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// How to use it
|
||||||
|
contract User {
|
||||||
|
// Just a struct holding our data.
|
||||||
|
IterableMapping.itmap data;
|
||||||
|
|
||||||
|
// Insert something
|
||||||
|
function insert(uint k, uint v) public returns (uint size) {
|
||||||
|
// Actually calls itmap_impl.insert, auto-supplying the first parameter for us.
|
||||||
|
IterableMapping.insert(data, k, v);
|
||||||
|
// We can still access members of the struct - but we should take care not to mess with them.
|
||||||
|
return data.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the sum of all stored data.
|
||||||
|
function sum() public view returns (uint s) {
|
||||||
|
for (uint i = IterableMapping.iterate_start(data);
|
||||||
|
IterableMapping.iterate_valid(data, i);
|
||||||
|
i = IterableMapping.iterate_next(data, i)) {
|
||||||
|
(, uint value) = IterableMapping.iterate_get(data, i);
|
||||||
|
s += value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a d
|
|||||||
array of length zero or a static array of the same length with all elements set to their
|
array of length zero or a static array of the same length with all elements set to their
|
||||||
initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves
|
initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves
|
||||||
all other elements and the length of the array untouched. This especially means that it leaves
|
all other elements and the length of the array untouched. This especially means that it leaves
|
||||||
a gap in the array. If you plan to remove items, a mapping is probably a better choice.
|
a gap in the array. If you plan to remove items, a :ref:`mapping <mapping-types>` is probably a better choice.
|
||||||
|
|
||||||
For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:
|
For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:
|
||||||
|
|
||||||
|
@ -276,6 +276,7 @@ Input Description
|
|||||||
// metadata - Metadata
|
// metadata - Metadata
|
||||||
// ir - Yul intermediate representation of the code before optimization
|
// ir - Yul intermediate representation of the code before optimization
|
||||||
// irOptimized - Intermediate representation after optimization
|
// irOptimized - Intermediate representation after optimization
|
||||||
|
// storageLayout - Slots, offsets and types of the contract's state variables.
|
||||||
// evm.assembly - New assembly format
|
// evm.assembly - New assembly format
|
||||||
// evm.legacyAssembly - Old-style assembly format in JSON
|
// evm.legacyAssembly - Old-style assembly format in JSON
|
||||||
// evm.bytecode.object - Bytecode object
|
// evm.bytecode.object - Bytecode object
|
||||||
@ -376,6 +377,8 @@ Output Description
|
|||||||
"devdoc": {},
|
"devdoc": {},
|
||||||
// Intermediate representation (string)
|
// Intermediate representation (string)
|
||||||
"ir": "",
|
"ir": "",
|
||||||
|
// See the Storage Layout documentation.
|
||||||
|
"storageLayout": {"storage": [...], "types": {...} },
|
||||||
// EVM-related outputs
|
// EVM-related outputs
|
||||||
"evm": {
|
"evm": {
|
||||||
// Assembly (string)
|
// Assembly (string)
|
||||||
|
@ -34,6 +34,10 @@ set(sources
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(devcore ${sources})
|
add_library(devcore ${sources})
|
||||||
target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::regex Boost::system)
|
target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::system)
|
||||||
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
|
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
|
||||||
add_dependencies(devcore solidity_BuildInfo.h)
|
add_dependencies(devcore solidity_BuildInfo.h)
|
||||||
|
|
||||||
|
if(SOLC_LINK_STATIC)
|
||||||
|
target_link_libraries(devcore PUBLIC Threads::Threads)
|
||||||
|
endif()
|
||||||
|
@ -25,11 +25,10 @@
|
|||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -277,7 +276,7 @@ void iterateReplacing(std::vector<T>& _vector, F const& _f)
|
|||||||
std::vector<T> modifiedVector;
|
std::vector<T> modifiedVector;
|
||||||
for (size_t i = 0; i < _vector.size(); ++i)
|
for (size_t i = 0; i < _vector.size(); ++i)
|
||||||
{
|
{
|
||||||
if (boost::optional<std::vector<T>> r = _f(_vector[i]))
|
if (std::optional<std::vector<T>> r = _f(_vector[i]))
|
||||||
{
|
{
|
||||||
if (!useModified)
|
if (!useModified)
|
||||||
{
|
{
|
||||||
@ -305,7 +304,7 @@ void iterateReplacingWindow(std::vector<T>& _vector, F const& _f, std::index_seq
|
|||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (; i + sizeof...(I) <= _vector.size(); ++i)
|
for (; i + sizeof...(I) <= _vector.size(); ++i)
|
||||||
{
|
{
|
||||||
if (boost::optional<std::vector<T>> r = _f(_vector[i + I]...))
|
if (std::optional<std::vector<T>> r = _f(_vector[i + I]...))
|
||||||
{
|
{
|
||||||
if (!useModified)
|
if (!useModified)
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libdevcore/Assertions.h>
|
||||||
|
|
||||||
#include <boost/regex.hpp>
|
#include <regex>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
@ -72,9 +72,9 @@ string Whiskers::render() const
|
|||||||
|
|
||||||
void Whiskers::checkParameterValid(string const& _parameter) const
|
void Whiskers::checkParameterValid(string const& _parameter) const
|
||||||
{
|
{
|
||||||
static boost::regex validParam("^" + paramRegex() + "$");
|
static regex validParam("^" + paramRegex() + "$");
|
||||||
assertThrow(
|
assertThrow(
|
||||||
boost::regex_match(_parameter, validParam),
|
regex_match(_parameter, validParam),
|
||||||
WhiskersError,
|
WhiskersError,
|
||||||
"Parameter" + _parameter + " contains invalid characters."
|
"Parameter" + _parameter + " contains invalid characters."
|
||||||
);
|
);
|
||||||
@ -99,6 +99,32 @@ void Whiskers::checkParameterUnknown(string const& _parameter) const
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template<class ReplaceCallback>
|
||||||
|
string regex_replace(
|
||||||
|
string const& _source,
|
||||||
|
regex const& _pattern,
|
||||||
|
ReplaceCallback _replace,
|
||||||
|
regex_constants::match_flag_type _flags = regex_constants::match_default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sregex_iterator curMatch(_source.begin(), _source.end(), _pattern, _flags);
|
||||||
|
sregex_iterator matchEnd;
|
||||||
|
string::const_iterator lastMatchedPos(_source.cbegin());
|
||||||
|
string result;
|
||||||
|
while (curMatch != matchEnd)
|
||||||
|
{
|
||||||
|
result.append(curMatch->prefix().first, curMatch->prefix().second);
|
||||||
|
result.append(_replace(*curMatch));
|
||||||
|
lastMatchedPos = (*curMatch)[0].second;
|
||||||
|
++curMatch;
|
||||||
|
}
|
||||||
|
result.append(lastMatchedPos, _source.cend());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string Whiskers::replace(
|
string Whiskers::replace(
|
||||||
string const& _template,
|
string const& _template,
|
||||||
StringMap const& _parameters,
|
StringMap const& _parameters,
|
||||||
@ -106,8 +132,7 @@ string Whiskers::replace(
|
|||||||
map<string, vector<StringMap>> const& _listParameters
|
map<string, vector<StringMap>> const& _listParameters
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using namespace boost;
|
static regex listOrTag("<(" + paramRegex() + ")>|<#(" + paramRegex() + ")>((?:.|\\r|\\n)*?)</\\2>|<\\?(" + paramRegex() + ")>((?:.|\\r|\\n)*?)(<!\\4>((?:.|\\r|\\n)*?))?</\\4>");
|
||||||
static regex listOrTag("<(" + paramRegex() + ")>|<#(" + paramRegex() + ")>(.*?)</\\2>|<\\?(" + paramRegex() + ")>(.*?)(<!\\4>(.*?))?</\\4>");
|
|
||||||
return regex_replace(_template, listOrTag, [&](match_results<string::const_iterator> _match) -> string
|
return regex_replace(_template, listOrTag, [&](match_results<string::const_iterator> _match) -> string
|
||||||
{
|
{
|
||||||
string tagName(_match[1]);
|
string tagName(_match[1]);
|
||||||
|
@ -258,7 +258,7 @@ Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string
|
|||||||
string Assembly::toStringInHex(u256 _value)
|
string Assembly::toStringInHex(u256 _value)
|
||||||
{
|
{
|
||||||
std::stringstream hexStr;
|
std::stringstream hexStr;
|
||||||
hexStr << hex << _value;
|
hexStr << std::uppercase << hex << _value;
|
||||||
return hexStr.str();
|
return hexStr.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,13 +356,13 @@ string dev::eth::disassemble(bytes const& _mem)
|
|||||||
stringstream ret;
|
stringstream ret;
|
||||||
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
|
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
|
||||||
if (!isValidInstruction(_instr))
|
if (!isValidInstruction(_instr))
|
||||||
ret << "0x" << hex << int(_instr) << " ";
|
ret << "0x" << std::uppercase << std::hex << int(_instr) << " ";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InstructionInfo info = instructionInfo(_instr);
|
InstructionInfo info = instructionInfo(_instr);
|
||||||
ret << info.name << " ";
|
ret << info.name << " ";
|
||||||
if (info.additional)
|
if (info.additional)
|
||||||
ret << "0x" << hex << _data << " ";
|
ret << "0x" << std::uppercase << std::hex << _data << " ";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return ret.str();
|
return ret.str();
|
||||||
|
@ -49,7 +49,7 @@ template <class S> S modWorkaround(S const& _a, S const& _b)
|
|||||||
|
|
||||||
// This works around a bug fixed with Boost 1.64.
|
// This works around a bug fixed with Boost 1.64.
|
||||||
// https://www.boost.org/doc/libs/1_68_0/libs/multiprecision/doc/html/boost_multiprecision/map/hist.html#boost_multiprecision.map.hist.multiprecision_2_3_1_boost_1_64
|
// https://www.boost.org/doc/libs/1_68_0/libs/multiprecision/doc/html/boost_multiprecision/map/hist.html#boost_multiprecision.map.hist.multiprecision_2_3_1_boost_1_64
|
||||||
inline u256 shlWorkaround(u256 const& _x, unsigned _amount)
|
template <class S> S shlWorkaround(S const& _x, unsigned _amount)
|
||||||
{
|
{
|
||||||
return u256((bigint(_x) << _amount) & u256(-1));
|
return u256((bigint(_x) << _amount) & u256(-1));
|
||||||
}
|
}
|
||||||
@ -66,44 +66,50 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
|
|||||||
Pattern
|
Pattern
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using Word = typename Pattern::Word;
|
||||||
return std::vector<SimplificationRule<Pattern>> {
|
return std::vector<SimplificationRule<Pattern>> {
|
||||||
// arithmetic on constants
|
// arithmetic on constants
|
||||||
{{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
|
{{Pattern::Builtins::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
|
||||||
{{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
|
{{Pattern::Builtins::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
|
||||||
{{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
|
{{Pattern::Builtins::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
|
||||||
{{Instruction::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
|
{{Pattern::Builtins::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
|
||||||
{{Instruction::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
{{Pattern::Builtins::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
||||||
{{Instruction::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
|
{{Pattern::Builtins::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
|
||||||
{{Instruction::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
{{Pattern::Builtins::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
||||||
{{Instruction::EXP, {A, B}}, [=]{ return u256(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << 256)); }, false},
|
{{Pattern::Builtins::EXP, {A, B}}, [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false},
|
||||||
{{Instruction::NOT, {A}}, [=]{ return ~A.d(); }, false},
|
{{Pattern::Builtins::NOT, {A}}, [=]{ return ~A.d(); }, false},
|
||||||
{{Instruction::LT, {A, B}}, [=]() -> u256 { return A.d() < B.d() ? 1 : 0; }, false},
|
{{Pattern::Builtins::LT, {A, B}}, [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false},
|
||||||
{{Instruction::GT, {A, B}}, [=]() -> u256 { return A.d() > B.d() ? 1 : 0; }, false},
|
{{Pattern::Builtins::GT, {A, B}}, [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false},
|
||||||
{{Instruction::SLT, {A, B}}, [=]() -> u256 { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
|
{{Pattern::Builtins::SLT, {A, B}}, [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
|
||||||
{{Instruction::SGT, {A, B}}, [=]() -> u256 { 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},
|
||||||
{{Instruction::EQ, {A, B}}, [=]() -> u256 { return A.d() == B.d() ? 1 : 0; }, false},
|
{{Pattern::Builtins::EQ, {A, B}}, [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false},
|
||||||
{{Instruction::ISZERO, {A}}, [=]() -> u256 { return A.d() == 0 ? 1 : 0; }, false},
|
{{Pattern::Builtins::ISZERO, {A}}, [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false},
|
||||||
{{Instruction::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false},
|
{{Pattern::Builtins::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false},
|
||||||
{{Instruction::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false},
|
{{Pattern::Builtins::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false},
|
||||||
{{Instruction::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false},
|
{{Pattern::Builtins::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false},
|
||||||
{{Instruction::BYTE, {A, B}}, [=]{ return A.d() >= 32 ? 0 : (B.d() >> unsigned(8 * (31 - A.d()))) & 0xff; }, false},
|
{{Pattern::Builtins::BYTE, {A, B}}, [=]{
|
||||||
{{Instruction::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) + bigint(B.d())) % C.d()); }, false},
|
return
|
||||||
{{Instruction::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) * bigint(B.d())) % C.d()); }, false},
|
A.d() >= Pattern::WordSize / 8 ?
|
||||||
{{Instruction::SIGNEXTEND, {A, B}}, [=]() -> u256 {
|
0 :
|
||||||
if (A.d() >= 31)
|
(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 {
|
||||||
|
if (A.d() >= Pattern::WordSize / 8 - 1)
|
||||||
return B.d();
|
return B.d();
|
||||||
unsigned testBit = unsigned(A.d()) * 8 + 7;
|
unsigned testBit = unsigned(A.d()) * 8 + 7;
|
||||||
u256 mask = (u256(1) << testBit) - 1;
|
Word mask = (Word(1) << testBit) - 1;
|
||||||
return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask;
|
return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask;
|
||||||
}, false},
|
}, false},
|
||||||
{{Instruction::SHL, {A, B}}, [=]{
|
{{Pattern::Builtins::SHL, {A, B}}, [=]{
|
||||||
if (A.d() > 255)
|
if (A.d() >= Pattern::WordSize)
|
||||||
return u256(0);
|
return Word(0);
|
||||||
return shlWorkaround(B.d(), unsigned(A.d()));
|
return shlWorkaround(B.d(), unsigned(A.d()));
|
||||||
}, false},
|
}, false},
|
||||||
{{Instruction::SHR, {A, B}}, [=]{
|
{{Pattern::Builtins::SHR, {A, B}}, [=]{
|
||||||
if (A.d() > 255)
|
if (A.d() >= Pattern::WordSize)
|
||||||
return u256(0);
|
return Word(0);
|
||||||
return B.d() >> unsigned(A.d());
|
return B.d() >> unsigned(A.d());
|
||||||
}, false}
|
}, false}
|
||||||
};
|
};
|
||||||
@ -118,50 +124,51 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
|
|||||||
Pattern Y
|
Pattern Y
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using Word = typename Pattern::Word;
|
||||||
return std::vector<SimplificationRule<Pattern>> {
|
return std::vector<SimplificationRule<Pattern>> {
|
||||||
// invariants involving known constants
|
// invariants involving known constants
|
||||||
{{Instruction::ADD, {X, 0}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::ADD, {X, 0}}, [=]{ return X; }, false},
|
||||||
{{Instruction::ADD, {0, X}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::ADD, {0, X}}, [=]{ return X; }, false},
|
||||||
{{Instruction::SUB, {X, 0}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::SUB, {X, 0}}, [=]{ return X; }, false},
|
||||||
{{Instruction::SUB, {~u256(0), X}}, [=]() -> Pattern { return {Instruction::NOT, {X}}; }, false},
|
{{Pattern::Builtins::SUB, {~Word(0), X}}, [=]() -> Pattern { return {Pattern::Builtins::NOT, {X}}; }, false},
|
||||||
{{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::MUL, {X, 0}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::MUL, {0, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::MUL, {0, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::MUL, {X, 1}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::MUL, {X, 1}}, [=]{ return X; }, false},
|
||||||
{{Instruction::MUL, {1, X}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::MUL, {1, X}}, [=]{ return X; }, false},
|
||||||
{{Instruction::MUL, {X, u256(-1)}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false},
|
{{Pattern::Builtins::MUL, {X, Word(-1)}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false},
|
||||||
{{Instruction::MUL, {u256(-1), X}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false},
|
{{Pattern::Builtins::MUL, {Word(-1), X}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false},
|
||||||
{{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::DIV, {X, 0}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::DIV, {0, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::DIV, {0, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::DIV, {X, 1}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::DIV, {X, 1}}, [=]{ return X; }, false},
|
||||||
{{Instruction::SDIV, {X, 0}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::SDIV, {X, 0}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::SDIV, {0, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::SDIV, {0, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::SDIV, {X, 1}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::SDIV, {X, 1}}, [=]{ return X; }, false},
|
||||||
{{Instruction::AND, {X, ~u256(0)}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::AND, {X, ~Word(0)}}, [=]{ return X; }, false},
|
||||||
{{Instruction::AND, {~u256(0), X}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::AND, {~Word(0), X}}, [=]{ return X; }, false},
|
||||||
{{Instruction::AND, {X, 0}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::AND, {X, 0}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::AND, {0, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::AND, {0, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::OR, {X, 0}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::OR, {X, 0}}, [=]{ return X; }, false},
|
||||||
{{Instruction::OR, {0, X}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::OR, {0, X}}, [=]{ return X; }, false},
|
||||||
{{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }, true},
|
{{Pattern::Builtins::OR, {X, ~Word(0)}}, [=]{ return ~Word(0); }, true},
|
||||||
{{Instruction::OR, {~u256(0), X}}, [=]{ return ~u256(0); }, true},
|
{{Pattern::Builtins::OR, {~Word(0), X}}, [=]{ return ~Word(0); }, true},
|
||||||
{{Instruction::XOR, {X, 0}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::XOR, {X, 0}}, [=]{ return X; }, false},
|
||||||
{{Instruction::XOR, {0, X}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::XOR, {0, X}}, [=]{ return X; }, false},
|
||||||
{{Instruction::MOD, {X, 0}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::MOD, {X, 0}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::MOD, {0, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
|
{{Pattern::Builtins::EQ, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false },
|
||||||
{{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
|
{{Pattern::Builtins::EQ, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false },
|
||||||
{{Instruction::SHL, {0, X}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::SHL, {0, X}}, [=]{ return X; }, false},
|
||||||
{{Instruction::SHR, {0, X}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::SHR, {0, X}}, [=]{ return X; }, false},
|
||||||
{{Instruction::SHL, {X, 0}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::SHL, {X, 0}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::SHR, {X, 0}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::SHR, {X, 0}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::GT, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false},
|
{{Pattern::Builtins::GT, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false},
|
||||||
{{Instruction::LT, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false},
|
{{Pattern::Builtins::LT, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false},
|
||||||
{{Instruction::GT, {X, ~u256(0)}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::GT, {X, ~Word(0)}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::LT, {~u256(0), X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::LT, {~Word(0), X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::GT, {0, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::GT, {0, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::LT, {X, 0}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::LT, {X, 0}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::AND, {{Instruction::BYTE, {X, Y}}, {u256(0xff)}}}, [=]() -> Pattern { return {Instruction::BYTE, {X, Y}}; }, false},
|
{{Pattern::Builtins::AND, {{Pattern::Builtins::BYTE, {X, Y}}, {Word(0xff)}}}, [=]() -> Pattern { return {Pattern::Builtins::BYTE, {X, Y}}; }, false},
|
||||||
{{Instruction::BYTE, {31, X}}, [=]() -> Pattern { return {Instruction::AND, {X, u256(0xff)}}; }, false}
|
{{Pattern::Builtins::BYTE, {Pattern::WordSize / 8 - 1, X}}, [=]() -> Pattern { return {Pattern::Builtins::AND, {X, Word(0xff)}}; }, false}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,18 +181,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3(
|
|||||||
Pattern
|
Pattern
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using Word = typename Pattern::Word;
|
||||||
return std::vector<SimplificationRule<Pattern>> {
|
return std::vector<SimplificationRule<Pattern>> {
|
||||||
// operations involving an expression and itself
|
// operations involving an expression and itself
|
||||||
{{Instruction::AND, {X, X}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::AND, {X, X}}, [=]{ return X; }, true},
|
||||||
{{Instruction::OR, {X, X}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::OR, {X, X}}, [=]{ return X; }, true},
|
||||||
{{Instruction::XOR, {X, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::XOR, {X, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::SUB, {X, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::SUB, {X, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::EQ, {X, X}}, [=]{ return u256(1); }, true},
|
{{Pattern::Builtins::EQ, {X, X}}, [=]{ return Word(1); }, true},
|
||||||
{{Instruction::LT, {X, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::LT, {X, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::SLT, {X, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::SLT, {X, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::GT, {X, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::GT, {X, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::SGT, {X, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::SGT, {X, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true}
|
{{Pattern::Builtins::MOD, {X, X}}, [=]{ return Word(0); }, true}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,25 +206,26 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4(
|
|||||||
Pattern Y
|
Pattern Y
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using Word = typename Pattern::Word;
|
||||||
return std::vector<SimplificationRule<Pattern>> {
|
return std::vector<SimplificationRule<Pattern>> {
|
||||||
// logical instruction combinations
|
// logical instruction combinations
|
||||||
{{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }, false},
|
{{Pattern::Builtins::NOT, {{Pattern::Builtins::NOT, {X}}}}, [=]{ return X; }, false},
|
||||||
{{Instruction::XOR, {X, {Instruction::XOR, {X, Y}}}}, [=]{ return Y; }, true},
|
{{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {X, Y}}}}, [=]{ return Y; }, true},
|
||||||
{{Instruction::XOR, {X, {Instruction::XOR, {Y, X}}}}, [=]{ return Y; }, true},
|
{{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {Y, X}}}}, [=]{ return Y; }, true},
|
||||||
{{Instruction::XOR, {{Instruction::XOR, {X, Y}}, X}}, [=]{ return Y; }, true},
|
{{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {X, Y}}, X}}, [=]{ return Y; }, true},
|
||||||
{{Instruction::XOR, {{Instruction::XOR, {Y, X}}, X}}, [=]{ return Y; }, true},
|
{{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {Y, X}}, X}}, [=]{ return Y; }, true},
|
||||||
{{Instruction::OR, {X, {Instruction::AND, {X, Y}}}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {X, Y}}}}, [=]{ return X; }, true},
|
||||||
{{Instruction::OR, {X, {Instruction::AND, {Y, X}}}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {Y, X}}}}, [=]{ return X; }, true},
|
||||||
{{Instruction::OR, {{Instruction::AND, {X, Y}}, X}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {X, Y}}, X}}, [=]{ return X; }, true},
|
||||||
{{Instruction::OR, {{Instruction::AND, {Y, X}}, X}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {Y, X}}, X}}, [=]{ return X; }, true},
|
||||||
{{Instruction::AND, {X, {Instruction::OR, {X, Y}}}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {X, Y}}}}, [=]{ return X; }, true},
|
||||||
{{Instruction::AND, {X, {Instruction::OR, {Y, X}}}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {Y, X}}}}, [=]{ return X; }, true},
|
||||||
{{Instruction::AND, {{Instruction::OR, {X, Y}}, X}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {X, Y}}, X}}, [=]{ return X; }, true},
|
||||||
{{Instruction::AND, {{Instruction::OR, {Y, X}}, X}}, [=]{ return X; }, true},
|
{{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {Y, X}}, X}}, [=]{ return X; }, true},
|
||||||
{{Instruction::AND, {X, {Instruction::NOT, {X}}}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::AND, {{Instruction::NOT, {X}}, X}}, [=]{ return u256(0); }, true},
|
{{Pattern::Builtins::AND, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return Word(0); }, true},
|
||||||
{{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true},
|
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return ~Word(0); }, true},
|
||||||
{{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true},
|
{{Pattern::Builtins::OR, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return ~Word(0); }, true},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,58 +239,61 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
|
|||||||
Pattern
|
Pattern
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using Word = typename Pattern::Word;
|
||||||
|
|
||||||
std::vector<SimplificationRule<Pattern>> rules;
|
std::vector<SimplificationRule<Pattern>> rules;
|
||||||
|
|
||||||
// Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1
|
// Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1
|
||||||
for (size_t i = 0; i < 256; ++i)
|
for (size_t i = 0; i < Pattern::WordSize; ++i)
|
||||||
{
|
{
|
||||||
u256 value = u256(1) << i;
|
Word value = Word(1) << i;
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::MOD, {X, value}},
|
{Pattern::Builtins::MOD, {X, value}},
|
||||||
[=]() -> Pattern { return {Instruction::AND, {X, value - 1}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace SHL >=256, X with 0
|
// Replace SHL >=256, X with 0
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::SHL, {A, X}},
|
{Pattern::Builtins::SHL, {A, X}},
|
||||||
[=]() -> Pattern { return u256(0); },
|
[=]() -> Pattern { return Word(0); },
|
||||||
true,
|
true,
|
||||||
[=]() { return A.d() >= 256; }
|
[=]() { return A.d() >= Pattern::WordSize; }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Replace SHR >=256, X with 0
|
// Replace SHR >=256, X with 0
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::SHR, {A, X}},
|
{Pattern::Builtins::SHR, {A, X}},
|
||||||
[=]() -> Pattern { return u256(0); },
|
[=]() -> Pattern { return Word(0); },
|
||||||
true,
|
true,
|
||||||
[=]() { return A.d() >= 256; }
|
[=]() { return A.d() >= Pattern::WordSize; }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Replace BYTE(A, X), A >= 32 with 0
|
// Replace BYTE(A, X), A >= 32 with 0
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::BYTE, {A, X}},
|
{Pattern::Builtins::BYTE, {A, X}},
|
||||||
[=]() -> Pattern { return u256(0); },
|
[=]() -> Pattern { return Word(0); },
|
||||||
true,
|
true,
|
||||||
[=]() { return A.d() >= 32; }
|
[=]() { return A.d() >= Pattern::WordSize / 8; }
|
||||||
});
|
});
|
||||||
|
|
||||||
for (auto const& op: std::vector<Instruction>{
|
for (auto const& op: std::vector<Instruction>{
|
||||||
Instruction::ADDRESS,
|
Pattern::Builtins::ADDRESS,
|
||||||
Instruction::CALLER,
|
Pattern::Builtins::CALLER,
|
||||||
Instruction::ORIGIN,
|
Pattern::Builtins::ORIGIN,
|
||||||
Instruction::COINBASE
|
Pattern::Builtins::COINBASE
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
u256 const mask = (u256(1) << 160) - 1;
|
assertThrow(Pattern::WordSize > 160, OptimizerException, "");
|
||||||
|
Word const mask = (Word(1) << 160) - 1;
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::AND, {{op, mask}}},
|
{Pattern::Builtins::AND, {{op, mask}}},
|
||||||
[=]() -> Pattern { return op; },
|
[=]() -> Pattern { return op; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::AND, {{mask, op}}},
|
{Pattern::Builtins::AND, {{mask, op}}},
|
||||||
[=]() -> Pattern { return op; },
|
[=]() -> Pattern { return op; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
@ -302,27 +314,27 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6(
|
|||||||
std::vector<SimplificationRule<Pattern>> rules;
|
std::vector<SimplificationRule<Pattern>> rules;
|
||||||
// Double negation of opcodes with boolean result
|
// Double negation of opcodes with boolean result
|
||||||
for (auto const& op: std::vector<Instruction>{
|
for (auto const& op: std::vector<Instruction>{
|
||||||
Instruction::EQ,
|
Pattern::Builtins::EQ,
|
||||||
Instruction::LT,
|
Pattern::Builtins::LT,
|
||||||
Instruction::SLT,
|
Pattern::Builtins::SLT,
|
||||||
Instruction::GT,
|
Pattern::Builtins::GT,
|
||||||
Instruction::SGT
|
Pattern::Builtins::SGT
|
||||||
})
|
})
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::ISZERO, {{Instruction::ISZERO, {{op, {X, Y}}}}}},
|
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}},
|
||||||
[=]() -> Pattern { return {op, {X, Y}}; },
|
[=]() -> Pattern { return {op, {X, Y}}; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
|
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::ISZERO, {{Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}}},
|
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}},
|
||||||
[=]() -> Pattern { return {Instruction::ISZERO, {X}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
|
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::ISZERO, {{Instruction::XOR, {X, Y}}}},
|
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}},
|
||||||
[=]() -> Pattern { return { Instruction::EQ, {X, Y} }; },
|
[=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -338,14 +350,15 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
|||||||
Pattern Y
|
Pattern Y
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using Word = typename Pattern::Word;
|
||||||
std::vector<SimplificationRule<Pattern>> rules;
|
std::vector<SimplificationRule<Pattern>> rules;
|
||||||
// Associative operations
|
// Associative operations
|
||||||
for (auto const& opFun: std::vector<std::pair<Instruction,std::function<u256(u256 const&,u256 const&)>>>{
|
for (auto const& opFun: std::vector<std::pair<Instruction,std::function<Word(Word const&,Word const&)>>>{
|
||||||
{Instruction::ADD, std::plus<u256>()},
|
{Pattern::Builtins::ADD, std::plus<Word>()},
|
||||||
{Instruction::MUL, std::multiplies<u256>()},
|
{Pattern::Builtins::MUL, std::multiplies<Word>()},
|
||||||
{Instruction::AND, std::bit_and<u256>()},
|
{Pattern::Builtins::AND, std::bit_and<Word>()},
|
||||||
{Instruction::OR, std::bit_or<u256>()},
|
{Pattern::Builtins::OR, std::bit_or<Word>()},
|
||||||
{Instruction::XOR, std::bit_xor<u256>()}
|
{Pattern::Builtins::XOR, std::bit_xor<Word>()}
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
auto op = opFun.first;
|
auto op = opFun.first;
|
||||||
@ -382,13 +395,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
|||||||
// Combine two SHL by constant
|
// Combine two SHL by constant
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X)
|
// SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X)
|
||||||
{Instruction::SHL, {{B}, {Instruction::SHL, {{A}, {X}}}}},
|
{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
|
||||||
[=]() -> Pattern {
|
[=]() -> Pattern {
|
||||||
bigint sum = bigint(A.d()) + B.d();
|
bigint sum = bigint(A.d()) + B.d();
|
||||||
if (sum >= 256)
|
if (sum >= Pattern::WordSize)
|
||||||
return {Instruction::AND, {X, u256(0)}};
|
return {Pattern::Builtins::AND, {X, Word(0)}};
|
||||||
else
|
else
|
||||||
return {Instruction::SHL, {u256(sum), X}};
|
return {Pattern::Builtins::SHL, {Word(sum), X}};
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
@ -396,13 +409,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
|||||||
// Combine two SHR by constant
|
// Combine two SHR by constant
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X)
|
// SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X)
|
||||||
{Instruction::SHR, {{B}, {Instruction::SHR, {{A}, {X}}}}},
|
{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
|
||||||
[=]() -> Pattern {
|
[=]() -> Pattern {
|
||||||
bigint sum = bigint(A.d()) + B.d();
|
bigint sum = bigint(A.d()) + B.d();
|
||||||
if (sum >= 256)
|
if (sum >= Pattern::WordSize)
|
||||||
return {Instruction::AND, {X, u256(0)}};
|
return {Pattern::Builtins::AND, {X, Word(0)}};
|
||||||
else
|
else
|
||||||
return {Instruction::SHR, {u256(sum), X}};
|
return {Pattern::Builtins::SHR, {Word(sum), X}};
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
@ -410,112 +423,112 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
|||||||
// Combine SHL-SHR by constant
|
// Combine SHL-SHR by constant
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
// SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
||||||
{Instruction::SHR, {{B}, {Instruction::SHL, {{A}, {X}}}}},
|
{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
|
||||||
[=]() -> Pattern {
|
[=]() -> Pattern {
|
||||||
u256 mask = shlWorkaround(u256(-1), unsigned(A.d())) >> unsigned(B.d());
|
Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d());
|
||||||
|
|
||||||
if (A.d() > B.d())
|
if (A.d() > B.d())
|
||||||
return {Instruction::AND, {{Instruction::SHL, {A.d() - B.d(), X}}, mask}};
|
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}};
|
||||||
else if (B.d() > A.d())
|
else if (B.d() > A.d())
|
||||||
return {Instruction::AND, {{Instruction::SHR, {B.d() - A.d(), X}}, mask}};
|
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}};
|
||||||
else
|
else
|
||||||
return {Instruction::AND, {X, mask}};
|
return {Pattern::Builtins::AND, {X, mask}};
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
[=] { return A.d() < 256 && B.d() < 256; }
|
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Combine SHR-SHL by constant
|
// Combine SHR-SHL by constant
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
// SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
||||||
{Instruction::SHL, {{B}, {Instruction::SHR, {{A}, {X}}}}},
|
{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
|
||||||
[=]() -> Pattern {
|
[=]() -> Pattern {
|
||||||
u256 mask = shlWorkaround(u256(-1) >> unsigned(A.d()), unsigned(B.d()));
|
Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d()));
|
||||||
|
|
||||||
if (A.d() > B.d())
|
if (A.d() > B.d())
|
||||||
return {Instruction::AND, {{Instruction::SHR, {A.d() - B.d(), X}}, mask}};
|
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}};
|
||||||
else if (B.d() > A.d())
|
else if (B.d() > A.d())
|
||||||
return {Instruction::AND, {{Instruction::SHL, {B.d() - A.d(), X}}, mask}};
|
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}};
|
||||||
else
|
else
|
||||||
return {Instruction::AND, {X, mask}};
|
return {Pattern::Builtins::AND, {X, mask}};
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
[=] { return A.d() < 256 && B.d() < 256; }
|
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Move AND with constant across SHL and SHR by constant
|
// Move AND with constant across SHL and SHR by constant
|
||||||
for (auto shiftOp: {Instruction::SHL, Instruction::SHR})
|
for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR})
|
||||||
{
|
{
|
||||||
auto replacement = [=]() -> Pattern {
|
auto replacement = [=]() -> Pattern {
|
||||||
u256 mask =
|
Word mask =
|
||||||
shiftOp == Instruction::SHL ?
|
shiftOp == Pattern::Builtins::SHL ?
|
||||||
shlWorkaround(A.d(), unsigned(B.d())) :
|
shlWorkaround(A.d(), unsigned(B.d())) :
|
||||||
A.d() >> unsigned(B.d());
|
A.d() >> unsigned(B.d());
|
||||||
return {Instruction::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}};
|
return {Pattern::Builtins::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}};
|
||||||
};
|
};
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
|
// SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
|
||||||
{shiftOp, {{B}, {Instruction::AND, {{X}, {A}}}}},
|
{shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}},
|
||||||
replacement,
|
replacement,
|
||||||
false,
|
false,
|
||||||
[=] { return B.d() < 256; }
|
[=] { return B.d() < Pattern::WordSize; }
|
||||||
});
|
});
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
|
// SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
|
||||||
{shiftOp, {{B}, {Instruction::AND, {{A}, {X}}}}},
|
{shiftOp, {{B}, {Pattern::Builtins::AND, {{A}, {X}}}}},
|
||||||
replacement,
|
replacement,
|
||||||
false,
|
false,
|
||||||
[=] { return B.d() < 256; }
|
[=] { return B.d() < Pattern::WordSize; }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// MUL(X, SHL(Y, 1)) -> SHL(Y, X)
|
// MUL(X, SHL(Y, 1)) -> SHL(Y, X)
|
||||||
{Instruction::MUL, {X, {Instruction::SHL, {Y, u256(1)}}}},
|
{Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
|
||||||
[=]() -> Pattern {
|
[=]() -> Pattern {
|
||||||
return {Instruction::SHL, {Y, X}};
|
return {Pattern::Builtins::SHL, {Y, X}};
|
||||||
},
|
},
|
||||||
// Actually only changes the order, does not remove.
|
// Actually only changes the order, does not remove.
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// MUL(SHL(X, 1), Y) -> SHL(X, Y)
|
// MUL(SHL(X, 1), Y) -> SHL(X, Y)
|
||||||
{Instruction::MUL, {{Instruction::SHL, {X, u256(1)}}, Y}},
|
{Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}},
|
||||||
[=]() -> Pattern {
|
[=]() -> Pattern {
|
||||||
return {Instruction::SHL, {X, Y}};
|
return {Pattern::Builtins::SHL, {X, Y}};
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
|
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// DIV(X, SHL(Y, 1)) -> SHR(Y, X)
|
// DIV(X, SHL(Y, 1)) -> SHR(Y, X)
|
||||||
{Instruction::DIV, {X, {Instruction::SHL, {Y, u256(1)}}}},
|
{Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
|
||||||
[=]() -> Pattern {
|
[=]() -> Pattern {
|
||||||
return {Instruction::SHR, {Y, X}};
|
return {Pattern::Builtins::SHR, {Y, X}};
|
||||||
},
|
},
|
||||||
// Actually only changes the order, does not remove.
|
// Actually only changes the order, does not remove.
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
std::function<bool()> feasibilityFunction = [=]() {
|
std::function<bool()> feasibilityFunction = [=]() {
|
||||||
if (B.d() > 256)
|
if (B.d() > Pattern::WordSize)
|
||||||
return false;
|
return false;
|
||||||
unsigned bAsUint = static_cast<unsigned>(B.d());
|
unsigned bAsUint = static_cast<unsigned>(B.d());
|
||||||
return (A.d() & (u256(-1) >> bAsUint)) == (u256(-1) >> bAsUint);
|
return (A.d() & ((~Word(0)) >> bAsUint)) == ((~Word(0)) >> bAsUint);
|
||||||
};
|
};
|
||||||
|
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B)
|
// AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B)
|
||||||
{Instruction::AND, {A, {Instruction::SHR, {B, X}}}},
|
{Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}},
|
||||||
[=]() -> Pattern { return {Instruction::SHR, {B, X}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
|
||||||
false,
|
false,
|
||||||
feasibilityFunction
|
feasibilityFunction
|
||||||
});
|
});
|
||||||
|
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
|
// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
|
||||||
{Instruction::AND, {{Instruction::SHR, {B, X}}, A}},
|
{Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}},
|
||||||
[=]() -> Pattern { return {Instruction::SHR, {B, X}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
|
||||||
false,
|
false,
|
||||||
feasibilityFunction
|
feasibilityFunction
|
||||||
});
|
});
|
||||||
@ -538,28 +551,28 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8(
|
|||||||
rules += std::vector<SimplificationRule<Pattern>>{
|
rules += std::vector<SimplificationRule<Pattern>>{
|
||||||
{
|
{
|
||||||
// X - A -> X + (-A)
|
// X - A -> X + (-A)
|
||||||
{Instruction::SUB, {X, A}},
|
{Pattern::Builtins::SUB, {X, A}},
|
||||||
[=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; },
|
||||||
false
|
false
|
||||||
}, {
|
}, {
|
||||||
// (X + A) - Y -> (X - Y) + A
|
// (X + A) - Y -> (X - Y) + A
|
||||||
{Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}},
|
{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}},
|
||||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
|
||||||
false
|
false
|
||||||
}, {
|
}, {
|
||||||
// (A + X) - Y -> (X - Y) + A
|
// (A + X) - Y -> (X - Y) + A
|
||||||
{Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}},
|
{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}},
|
||||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
|
||||||
false
|
false
|
||||||
}, {
|
}, {
|
||||||
// X - (Y + A) -> (X - Y) + (-A)
|
// X - (Y + A) -> (X - Y) + (-A)
|
||||||
{Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}},
|
{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}},
|
||||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||||
false
|
false
|
||||||
}, {
|
}, {
|
||||||
// X - (A + Y) -> (X - Y) + (-A)
|
// X - (A + Y) -> (X - Y) + (-A)
|
||||||
{Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}},
|
{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}},
|
||||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -577,29 +590,31 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart9(
|
|||||||
Pattern Z
|
Pattern Z
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using Word = typename Pattern::Word;
|
||||||
std::vector<SimplificationRule<Pattern>> rules;
|
std::vector<SimplificationRule<Pattern>> rules;
|
||||||
|
|
||||||
u256 const mask = (u256(1) << 160) - 1;
|
assertThrow(Pattern::WordSize > 160, OptimizerException, "");
|
||||||
|
Word const mask = (Word(1) << 160) - 1;
|
||||||
// CREATE
|
// CREATE
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::AND, {{Instruction::CREATE, {W, X, Y}}, mask}},
|
{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}},
|
||||||
[=]() -> Pattern { return {Instruction::CREATE, {W, X, Y}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::AND, {{mask, {Instruction::CREATE, {W, X, Y}}}}},
|
{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}},
|
||||||
[=]() -> Pattern { return {Instruction::CREATE, {W, X, Y}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
// CREATE2
|
// CREATE2
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::AND, {{Instruction::CREATE2, {W, X, Y, Z}}, mask}},
|
{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}},
|
||||||
[=]() -> Pattern { return {Instruction::CREATE2, {W, X, Y, Z}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
rules.push_back({
|
rules.push_back({
|
||||||
{Instruction::AND, {{mask, {Instruction::CREATE2, {W, X, Y, Z}}}}},
|
{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}},
|
||||||
[=]() -> Pattern { return {Instruction::CREATE2, {W, X, Y, Z}}; },
|
[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -621,6 +636,14 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
|||||||
Pattern Z
|
Pattern Z
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using Word = typename Pattern::Word;
|
||||||
|
// Some sanity checks
|
||||||
|
assertThrow(Pattern::WordSize % 8 == 0, OptimizerException, "");
|
||||||
|
assertThrow(Pattern::WordSize >= 8, OptimizerException, "");
|
||||||
|
assertThrow(Pattern::WordSize <= 256, OptimizerException, "");
|
||||||
|
assertThrow(Word(-1) == ~Word(0), OptimizerException, "");
|
||||||
|
assertThrow(Word(-1) + 1 == Word(0), OptimizerException, "");
|
||||||
|
|
||||||
std::vector<SimplificationRule<Pattern>> rules;
|
std::vector<SimplificationRule<Pattern>> rules;
|
||||||
rules += simplificationRuleListPart1(A, B, C, W, X);
|
rules += simplificationRuleListPart1(A, B, C, W, X);
|
||||||
rules += simplificationRuleListPart2(A, B, C, W, X);
|
rules += simplificationRuleListPart2(A, B, C, W, X);
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libevmasm/Instruction.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -54,5 +55,85 @@ struct SimplificationRule
|
|||||||
std::function<bool()> feasible;
|
std::function<bool()> feasible;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,10 @@ public:
|
|||||||
using Expression = ExpressionClasses::Expression;
|
using Expression = ExpressionClasses::Expression;
|
||||||
using Id = ExpressionClasses::Id;
|
using Id = ExpressionClasses::Id;
|
||||||
|
|
||||||
|
using Builtins = dev::eth::EVMBuiltins;
|
||||||
|
static constexpr size_t WordSize = 256;
|
||||||
|
using Word = u256;
|
||||||
|
|
||||||
// Matches a specific constant value.
|
// Matches a specific constant value.
|
||||||
Pattern(unsigned _value): Pattern(u256(_value)) {}
|
Pattern(unsigned _value): Pattern(u256(_value)) {}
|
||||||
// Matches a specific constant value.
|
// Matches a specific constant value.
|
||||||
|
@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <boost/operators.hpp>
|
#include <boost/operators.hpp>
|
||||||
|
|
||||||
|
|
||||||
@ -51,12 +51,12 @@ public:
|
|||||||
static EVMVersion istanbul() { return {Version::Istanbul}; }
|
static EVMVersion istanbul() { return {Version::Istanbul}; }
|
||||||
static EVMVersion berlin() { return {Version::Berlin}; }
|
static EVMVersion berlin() { return {Version::Berlin}; }
|
||||||
|
|
||||||
static boost::optional<EVMVersion> fromString(std::string const& _version)
|
static std::optional<EVMVersion> fromString(std::string const& _version)
|
||||||
{
|
{
|
||||||
for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg(), istanbul(), berlin()})
|
for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg(), istanbul(), berlin()})
|
||||||
if (_version == v.name())
|
if (_version == v.name())
|
||||||
return v;
|
return v;
|
||||||
return {};
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; }
|
bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; }
|
||||||
|
@ -53,8 +53,9 @@
|
|||||||
#include <liblangutil/Common.h>
|
#include <liblangutil/Common.h>
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
#include <liblangutil/Scanner.h>
|
#include <liblangutil/Scanner.h>
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
@ -187,7 +188,7 @@ bool Scanner::scanHexByte(char& o_scannedByte)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<unsigned> Scanner::scanUnicode()
|
std::optional<unsigned> Scanner::scanUnicode()
|
||||||
{
|
{
|
||||||
unsigned x = 0;
|
unsigned x = 0;
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
@ -718,7 +719,7 @@ bool Scanner::scanEscape()
|
|||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
{
|
{
|
||||||
if (boost::optional<unsigned> codepoint = scanUnicode())
|
if (auto const codepoint = scanUnicode(); codepoint.has_value())
|
||||||
addUnicodeAsUTF8(*codepoint);
|
addUnicodeAsUTF8(*codepoint);
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@ -57,6 +57,8 @@
|
|||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
@ -207,7 +209,7 @@ private:
|
|||||||
inline Token selectToken(char _next, Token _then, Token _else);
|
inline Token selectToken(char _next, Token _then, Token _else);
|
||||||
|
|
||||||
bool scanHexByte(char& o_scannedByte);
|
bool scanHexByte(char& o_scannedByte);
|
||||||
boost::optional<unsigned> scanUnicode();
|
std::optional<unsigned> scanUnicode();
|
||||||
|
|
||||||
/// Scans a single Solidity token.
|
/// Scans a single Solidity token.
|
||||||
void scanToken();
|
void scanToken();
|
||||||
|
@ -79,6 +79,8 @@ set(sources
|
|||||||
formal/BMC.h
|
formal/BMC.h
|
||||||
formal/CHC.cpp
|
formal/CHC.cpp
|
||||||
formal/CHC.h
|
formal/CHC.h
|
||||||
|
formal/CHCSmtLib2Interface.cpp
|
||||||
|
formal/CHCSmtLib2Interface.h
|
||||||
formal/CHCSolverInterface.h
|
formal/CHCSolverInterface.h
|
||||||
formal/EncodingContext.cpp
|
formal/EncodingContext.cpp
|
||||||
formal/EncodingContext.h
|
formal/EncodingContext.h
|
||||||
@ -111,6 +113,8 @@ set(sources
|
|||||||
interface/ReadFile.h
|
interface/ReadFile.h
|
||||||
interface/StandardCompiler.cpp
|
interface/StandardCompiler.cpp
|
||||||
interface/StandardCompiler.h
|
interface/StandardCompiler.h
|
||||||
|
interface/StorageLayout.cpp
|
||||||
|
interface/StorageLayout.h
|
||||||
interface/Version.cpp
|
interface/Version.cpp
|
||||||
interface/Version.h
|
interface/Version.h
|
||||||
parsing/DocStringParser.cpp
|
parsing/DocStringParser.cpp
|
||||||
|
@ -38,10 +38,12 @@ namespace solidity
|
|||||||
|
|
||||||
NameAndTypeResolver::NameAndTypeResolver(
|
NameAndTypeResolver::NameAndTypeResolver(
|
||||||
GlobalContext& _globalContext,
|
GlobalContext& _globalContext,
|
||||||
|
langutil::EVMVersion _evmVersion,
|
||||||
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
|
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
|
||||||
ErrorReporter& _errorReporter
|
ErrorReporter& _errorReporter
|
||||||
):
|
):
|
||||||
m_scopes(_scopes),
|
m_scopes(_scopes),
|
||||||
|
m_evmVersion(_evmVersion),
|
||||||
m_errorReporter(_errorReporter),
|
m_errorReporter(_errorReporter),
|
||||||
m_globalContext(_globalContext)
|
m_globalContext(_globalContext)
|
||||||
{
|
{
|
||||||
@ -324,7 +326,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
|||||||
{
|
{
|
||||||
if (m_scopes.count(&_node))
|
if (m_scopes.count(&_node))
|
||||||
setScope(&_node);
|
setScope(&_node);
|
||||||
return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
|
return ReferencesResolver(m_errorReporter, *this, m_evmVersion, _resolveInsideCode).resolve(_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
|
||||||
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
@ -55,6 +57,7 @@ public:
|
|||||||
/// are filled during the lifetime of this object.
|
/// are filled during the lifetime of this object.
|
||||||
NameAndTypeResolver(
|
NameAndTypeResolver(
|
||||||
GlobalContext& _globalContext,
|
GlobalContext& _globalContext,
|
||||||
|
langutil::EVMVersion _evmVersion,
|
||||||
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
|
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
|
||||||
langutil::ErrorReporter& _errorReporter
|
langutil::ErrorReporter& _errorReporter
|
||||||
);
|
);
|
||||||
@ -130,6 +133,7 @@ private:
|
|||||||
/// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
|
/// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
|
||||||
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
|
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
|
||||||
|
|
||||||
|
langutil::EVMVersion m_evmVersion;
|
||||||
DeclarationContainer* m_currentScope = nullptr;
|
DeclarationContainer* m_currentScope = nullptr;
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
GlobalContext& m_globalContext;
|
GlobalContext& m_globalContext;
|
||||||
|
@ -122,7 +122,7 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
|
|||||||
if (!_typeName.annotation().type)
|
if (!_typeName.annotation().type)
|
||||||
{
|
{
|
||||||
_typeName.annotation().type = TypeProvider::fromElementaryTypeName(_typeName.typeName());
|
_typeName.annotation().type = TypeProvider::fromElementaryTypeName(_typeName.typeName());
|
||||||
if (_typeName.stateMutability().is_initialized())
|
if (_typeName.stateMutability().has_value())
|
||||||
{
|
{
|
||||||
// for non-address types this was already caught by the parser
|
// for non-address types this was already caught by the parser
|
||||||
solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
|
solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
|
||||||
@ -323,12 +323,12 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
// Will be re-generated later with correct information
|
// Will be re-generated later with correct information
|
||||||
// We use the latest EVM version because we will re-run it anyway.
|
// We use the latest EVM version because we will re-run it anyway.
|
||||||
yul::AsmAnalysisInfo analysisInfo;
|
yul::AsmAnalysisInfo analysisInfo;
|
||||||
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
|
std::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
|
||||||
yul::AsmAnalyzer(
|
yul::AsmAnalyzer(
|
||||||
analysisInfo,
|
analysisInfo,
|
||||||
errorsIgnored,
|
errorsIgnored,
|
||||||
errorTypeForLoose,
|
errorTypeForLoose,
|
||||||
yul::EVMDialect::looseAssemblyForEVM(EVMVersion{}),
|
yul::EVMDialect::looseAssemblyForEVM(m_evmVersion),
|
||||||
resolver
|
resolver
|
||||||
).analyze(_inlineAssembly.operations());
|
).analyze(_inlineAssembly.operations());
|
||||||
return false;
|
return false;
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <list>
|
#include <list>
|
||||||
@ -52,10 +53,12 @@ public:
|
|||||||
ReferencesResolver(
|
ReferencesResolver(
|
||||||
langutil::ErrorReporter& _errorReporter,
|
langutil::ErrorReporter& _errorReporter,
|
||||||
NameAndTypeResolver& _resolver,
|
NameAndTypeResolver& _resolver,
|
||||||
|
langutil::EVMVersion _evmVersion,
|
||||||
bool _resolveInsideCode = false
|
bool _resolveInsideCode = false
|
||||||
):
|
):
|
||||||
m_errorReporter(_errorReporter),
|
m_errorReporter(_errorReporter),
|
||||||
m_resolver(_resolver),
|
m_resolver(_resolver),
|
||||||
|
m_evmVersion(_evmVersion),
|
||||||
m_resolveInsideCode(_resolveInsideCode)
|
m_resolveInsideCode(_resolveInsideCode)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -96,6 +99,7 @@ private:
|
|||||||
|
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
NameAndTypeResolver& m_resolver;
|
NameAndTypeResolver& m_resolver;
|
||||||
|
langutil::EVMVersion m_evmVersion;
|
||||||
/// Stack of return parameters.
|
/// Stack of return parameters.
|
||||||
std::vector<ParameterList const*> m_returnParameters;
|
std::vector<ParameterList const*> m_returnParameters;
|
||||||
bool const m_resolveInsideCode;
|
bool const m_resolveInsideCode;
|
||||||
|
@ -390,11 +390,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
set<Declaration const*> modifiers;
|
set<Declaration const*> modifiers;
|
||||||
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
|
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
|
||||||
{
|
{
|
||||||
|
auto baseContracts = dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts;
|
||||||
|
// Delete first base which is just the main contract itself
|
||||||
|
baseContracts.erase(baseContracts.begin());
|
||||||
|
|
||||||
visitManually(
|
visitManually(
|
||||||
*modifier,
|
*modifier,
|
||||||
_function.isConstructor() ?
|
_function.isConstructor() ? baseContracts : vector<ContractDefinition const*>()
|
||||||
dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts :
|
|
||||||
vector<ContractDefinition const*>()
|
|
||||||
);
|
);
|
||||||
Declaration const* decl = &dereference(*modifier->name());
|
Declaration const* decl = &dereference(*modifier->name());
|
||||||
if (modifiers.count(decl))
|
if (modifiers.count(decl))
|
||||||
@ -448,6 +450,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
TypePointer varType = _variable.annotation().type;
|
TypePointer varType = _variable.annotation().type;
|
||||||
solAssert(!!varType, "Variable type not provided.");
|
solAssert(!!varType, "Variable type not provided.");
|
||||||
|
|
||||||
|
if (auto contractType = dynamic_cast<ContractType const*>(varType))
|
||||||
|
if (contractType->contractDefinition().isLibrary())
|
||||||
|
m_errorReporter.typeError(_variable.location(), "The type of a variable cannot be a library.");
|
||||||
if (_variable.value())
|
if (_variable.value())
|
||||||
expectType(*_variable.value(), *varType);
|
expectType(*_variable.value(), *varType);
|
||||||
if (_variable.isConstant())
|
if (_variable.isConstant())
|
||||||
@ -522,7 +527,12 @@ void TypeChecker::visitManually(
|
|||||||
_modifier.arguments() ? *_modifier.arguments() : std::vector<ASTPointer<Expression>>();
|
_modifier.arguments() ? *_modifier.arguments() : std::vector<ASTPointer<Expression>>();
|
||||||
for (ASTPointer<Expression> const& argument: arguments)
|
for (ASTPointer<Expression> const& argument: arguments)
|
||||||
argument->accept(*this);
|
argument->accept(*this);
|
||||||
|
|
||||||
|
{
|
||||||
|
m_insideModifierInvocation = true;
|
||||||
|
ScopeGuard resetFlag{[&] () { m_insideModifierInvocation = false; }};
|
||||||
_modifier.name()->accept(*this);
|
_modifier.name()->accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
auto const* declaration = &dereference(*_modifier.name());
|
auto const* declaration = &dereference(*_modifier.name());
|
||||||
vector<ASTPointer<VariableDeclaration>> emptyParameterList;
|
vector<ASTPointer<VariableDeclaration>> emptyParameterList;
|
||||||
@ -695,6 +705,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
}
|
}
|
||||||
else if (_context == yul::IdentifierContext::LValue)
|
else if (_context == yul::IdentifierContext::LValue)
|
||||||
{
|
{
|
||||||
|
if (dynamic_cast<MagicVariableDeclaration const*>(declaration))
|
||||||
|
return size_t(-1);
|
||||||
|
|
||||||
m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly.");
|
m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly.");
|
||||||
return size_t(-1);
|
return size_t(-1);
|
||||||
}
|
}
|
||||||
@ -2342,12 +2355,30 @@ bool TypeChecker::visit(Identifier const& _identifier)
|
|||||||
if (functionType->canTakeArguments(*annotation.arguments))
|
if (functionType->canTakeArguments(*annotation.arguments))
|
||||||
candidates.push_back(declaration);
|
candidates.push_back(declaration);
|
||||||
}
|
}
|
||||||
if (candidates.empty())
|
if (candidates.size() == 1)
|
||||||
m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
|
|
||||||
else if (candidates.size() == 1)
|
|
||||||
annotation.referencedDeclaration = candidates.front();
|
annotation.referencedDeclaration = candidates.front();
|
||||||
else
|
else
|
||||||
m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup.");
|
{
|
||||||
|
SecondarySourceLocation ssl;
|
||||||
|
|
||||||
|
for (Declaration const* declaration: annotation.overloadedDeclarations)
|
||||||
|
if (declaration->location().isEmpty())
|
||||||
|
{
|
||||||
|
// Try to re-construct function definition
|
||||||
|
string description;
|
||||||
|
for (auto const& param: declaration->functionType(true)->parameterTypes())
|
||||||
|
description += (description.empty() ? "" : ", ") + param->toString(false);
|
||||||
|
description = "function " + _identifier.name() + "(" + description + ")";
|
||||||
|
|
||||||
|
ssl.append("Candidate: " + description, declaration->location());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ssl.append("Candidate:", declaration->location());
|
||||||
|
if (candidates.empty())
|
||||||
|
m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No matching declaration found after argument-dependent lookup.");
|
||||||
|
else
|
||||||
|
m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No unique declaration found after argument-dependent lookup.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
solAssert(
|
solAssert(
|
||||||
@ -2376,12 +2407,21 @@ bool TypeChecker::visit(Identifier const& _identifier)
|
|||||||
if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256)
|
if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
_identifier.location(),
|
_identifier.location(),
|
||||||
"\"sha3\" has been deprecated in favour of \"keccak256\""
|
"\"sha3\" has been deprecated in favour of \"keccak256\"."
|
||||||
);
|
);
|
||||||
else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct)
|
else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
_identifier.location(),
|
_identifier.location(),
|
||||||
"\"suicide\" has been deprecated in favour of \"selfdestruct\""
|
"\"suicide\" has been deprecated in favour of \"selfdestruct\"."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_insideModifierInvocation)
|
||||||
|
if (ModifierType const* type = dynamic_cast<decltype(type)>(_identifier.annotation().type))
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_identifier.location(),
|
||||||
|
"Modifier can only be referenced in function headers."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +166,9 @@ private:
|
|||||||
/// Flag indicating whether we are currently inside a StructDefinition.
|
/// Flag indicating whether we are currently inside a StructDefinition.
|
||||||
bool m_insideStruct = false;
|
bool m_insideStruct = false;
|
||||||
|
|
||||||
|
/// Flag indicating whether we are currently inside the invocation of a modifier
|
||||||
|
bool m_insideModifierInvocation = false;
|
||||||
|
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
|
|||||||
void ViewPureChecker::reportMutability(
|
void ViewPureChecker::reportMutability(
|
||||||
StateMutability _mutability,
|
StateMutability _mutability,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
boost::optional<SourceLocation> const& _nestedLocation
|
std::optional<SourceLocation> const& _nestedLocation
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (_mutability > m_bestMutabilityAndLocation.mutability)
|
if (_mutability > m_bestMutabilityAndLocation.mutability)
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
@ -67,7 +68,7 @@ private:
|
|||||||
void reportMutability(
|
void reportMutability(
|
||||||
StateMutability _mutability,
|
StateMutability _mutability,
|
||||||
langutil::SourceLocation const& _location,
|
langutil::SourceLocation const& _location,
|
||||||
boost::optional<langutil::SourceLocation> const& _nestedLocation = {}
|
std::optional<langutil::SourceLocation> const& _nestedLocation = {}
|
||||||
);
|
);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ASTNode>> const& m_ast;
|
std::vector<std::shared_ptr<ASTNode>> const& m_ast;
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -912,10 +913,10 @@ public:
|
|||||||
ElementaryTypeName(
|
ElementaryTypeName(
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
ElementaryTypeNameToken const& _elem,
|
ElementaryTypeNameToken const& _elem,
|
||||||
boost::optional<StateMutability> _stateMutability = {}
|
std::optional<StateMutability> _stateMutability = {}
|
||||||
): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability)
|
): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability)
|
||||||
{
|
{
|
||||||
solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, "");
|
solAssert(!_stateMutability.has_value() || _elem.token() == Token::Address, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void accept(ASTVisitor& _visitor) override;
|
void accept(ASTVisitor& _visitor) override;
|
||||||
@ -923,11 +924,11 @@ public:
|
|||||||
|
|
||||||
ElementaryTypeNameToken const& typeName() const { return m_type; }
|
ElementaryTypeNameToken const& typeName() const { return m_type; }
|
||||||
|
|
||||||
boost::optional<StateMutability> const& stateMutability() const { return m_stateMutability; }
|
std::optional<StateMutability> const& stateMutability() const { return m_stateMutability; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ElementaryTypeNameToken m_type;
|
ElementaryTypeNameToken m_type;
|
||||||
boost::optional<StateMutability> m_stateMutability; ///< state mutability for address type
|
std::optional<StateMutability> m_stateMutability; ///< state mutability for address type
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,10 +26,9 @@
|
|||||||
#include <libsolidity/ast/ASTEnums.h>
|
#include <libsolidity/ast/ASTEnums.h>
|
||||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -183,7 +182,7 @@ struct ExpressionAnnotation: ASTAnnotation
|
|||||||
|
|
||||||
/// Types and - if given - names of arguments if the expr. is a function
|
/// Types and - if given - names of arguments if the expr. is a function
|
||||||
/// that is called, used for overload resoultion
|
/// that is called, used for overload resoultion
|
||||||
boost::optional<FuncCallArguments> arguments;
|
std::optional<FuncCallArguments> arguments;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IdentifierAnnotation: ExpressionAnnotation
|
struct IdentifierAnnotation: ExpressionAnnotation
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
#include <libyul/AsmPrinter.h>
|
#include <libyul/AsmPrinter.h>
|
||||||
|
#include <libdevcore/JSON.h>
|
||||||
#include <libdevcore/UTF8.h>
|
#include <libdevcore/UTF8.h>
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
|
||||||
@ -147,7 +148,7 @@ Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp, bool _short)
|
|||||||
return typeDescriptions;
|
return typeDescriptions;
|
||||||
|
|
||||||
}
|
}
|
||||||
Json::Value ASTJsonConverter::typePointerToJson(boost::optional<FuncCallArguments> const& _tps)
|
Json::Value ASTJsonConverter::typePointerToJson(std::optional<FuncCallArguments> const& _tps)
|
||||||
{
|
{
|
||||||
if (_tps)
|
if (_tps)
|
||||||
{
|
{
|
||||||
@ -189,7 +190,7 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifie
|
|||||||
|
|
||||||
void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node)
|
void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node)
|
||||||
{
|
{
|
||||||
_stream << toJson(_node);
|
_stream << jsonPrettyPrint(toJson(_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value&& ASTJsonConverter::toJson(ASTNode const& _node)
|
Json::Value&& ASTJsonConverter::toJson(ASTNode const& _node)
|
||||||
|
@ -27,10 +27,12 @@
|
|||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
@ -169,7 +171,7 @@ private:
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
static Json::Value typePointerToJson(TypePointer _tp, bool _short = false);
|
static Json::Value typePointerToJson(TypePointer _tp, bool _short = false);
|
||||||
static Json::Value typePointerToJson(boost::optional<FuncCallArguments> const& _tps);
|
static Json::Value typePointerToJson(std::optional<FuncCallArguments> const& _tps);
|
||||||
void appendExpressionAttributes(
|
void appendExpressionAttributes(
|
||||||
std::vector<std::pair<std::string, Json::Value>> &_attributes,
|
std::vector<std::pair<std::string, Json::Value>> &_attributes,
|
||||||
ExpressionAnnotation const& _annotation
|
ExpressionAnnotation const& _annotation
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
|
@ -1812,10 +1812,10 @@ TypePointer ArrayType::decodingType() const
|
|||||||
|
|
||||||
TypeResult ArrayType::interfaceType(bool _inLibrary) const
|
TypeResult ArrayType::interfaceType(bool _inLibrary) const
|
||||||
{
|
{
|
||||||
if (_inLibrary && m_interfaceType_library.is_initialized())
|
if (_inLibrary && m_interfaceType_library.has_value())
|
||||||
return *m_interfaceType_library;
|
return *m_interfaceType_library;
|
||||||
|
|
||||||
if (!_inLibrary && m_interfaceType.is_initialized())
|
if (!_inLibrary && m_interfaceType.has_value())
|
||||||
return *m_interfaceType;
|
return *m_interfaceType;
|
||||||
|
|
||||||
TypeResult result{TypePointer{}};
|
TypeResult result{TypePointer{}};
|
||||||
@ -1849,7 +1849,7 @@ u256 ArrayType::memoryDataSize() const
|
|||||||
solAssert(m_location == DataLocation::Memory, "");
|
solAssert(m_location == DataLocation::Memory, "");
|
||||||
solAssert(!isByteArray(), "");
|
solAssert(!isByteArray(), "");
|
||||||
bigint size = bigint(m_length) * m_baseType->memoryHeadSize();
|
bigint size = bigint(m_length) * m_baseType->memoryHeadSize();
|
||||||
solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit u256.");
|
solAssert(size <= numeric_limits<u256>::max(), "Array size does not fit u256.");
|
||||||
return u256(size);
|
return u256(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2106,10 +2106,10 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
|
|||||||
|
|
||||||
TypeResult StructType::interfaceType(bool _inLibrary) const
|
TypeResult StructType::interfaceType(bool _inLibrary) const
|
||||||
{
|
{
|
||||||
if (_inLibrary && m_interfaceType_library.is_initialized())
|
if (_inLibrary && m_interfaceType_library.has_value())
|
||||||
return *m_interfaceType_library;
|
return *m_interfaceType_library;
|
||||||
|
|
||||||
if (!_inLibrary && m_interfaceType.is_initialized())
|
if (!_inLibrary && m_interfaceType.has_value())
|
||||||
return *m_interfaceType;
|
return *m_interfaceType;
|
||||||
|
|
||||||
TypeResult result{TypePointer{}};
|
TypeResult result{TypePointer{}};
|
||||||
@ -2166,7 +2166,7 @@ TypeResult StructType::interfaceType(bool _inLibrary) const
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
m_recursive = m_recursive.get() || (CycleDetector<StructDefinition>(visitor).run(structDefinition()) != nullptr);
|
m_recursive = m_recursive.value() || (CycleDetector<StructDefinition>(visitor).run(structDefinition()) != nullptr);
|
||||||
|
|
||||||
std::string const recursiveErrMsg = "Recursive type not allowed for public or external contract functions.";
|
std::string const recursiveErrMsg = "Recursive type not allowed for public or external contract functions.";
|
||||||
|
|
||||||
@ -2179,13 +2179,13 @@ TypeResult StructType::interfaceType(bool _inLibrary) const
|
|||||||
else
|
else
|
||||||
m_interfaceType_library = TypeProvider::withLocation(this, DataLocation::Memory, true);
|
m_interfaceType_library = TypeProvider::withLocation(this, DataLocation::Memory, true);
|
||||||
|
|
||||||
if (m_recursive.get())
|
if (m_recursive.value())
|
||||||
m_interfaceType = TypeResult::err(recursiveErrMsg);
|
m_interfaceType = TypeResult::err(recursiveErrMsg);
|
||||||
|
|
||||||
return *m_interfaceType_library;
|
return *m_interfaceType_library;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_recursive.get())
|
if (m_recursive.value())
|
||||||
m_interfaceType = TypeResult::err(recursiveErrMsg);
|
m_interfaceType = TypeResult::err(recursiveErrMsg);
|
||||||
else if (!result.message().empty())
|
else if (!result.message().empty())
|
||||||
m_interfaceType = result;
|
m_interfaceType = result;
|
||||||
@ -3373,6 +3373,15 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
|
|||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoolResult TypeType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
|
{
|
||||||
|
if (auto const* address = dynamic_cast<AddressType const*>(&_convertTo))
|
||||||
|
if (address->stateMutability() == StateMutability::NonPayable)
|
||||||
|
if (auto const* contractType = dynamic_cast<ContractType const*>(m_actualType))
|
||||||
|
return contractType->contractDefinition().isLibrary();
|
||||||
|
return isImplicitlyConvertibleTo(_convertTo);
|
||||||
|
}
|
||||||
|
|
||||||
ModifierType::ModifierType(ModifierDefinition const& _modifier)
|
ModifierType::ModifierType(ModifierDefinition const& _modifier)
|
||||||
{
|
{
|
||||||
TypePointers params;
|
TypePointers params;
|
||||||
|
@ -31,11 +31,11 @@
|
|||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
#include <libdevcore/Result.h>
|
#include <libdevcore/Result.h>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <boost/rational.hpp>
|
#include <boost/rational.hpp>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -769,8 +769,8 @@ private:
|
|||||||
Type const* m_baseType;
|
Type const* m_baseType;
|
||||||
bool m_hasDynamicLength = true;
|
bool m_hasDynamicLength = true;
|
||||||
u256 m_length;
|
u256 m_length;
|
||||||
mutable boost::optional<TypeResult> m_interfaceType;
|
mutable std::optional<TypeResult> m_interfaceType;
|
||||||
mutable boost::optional<TypeResult> m_interfaceType_library;
|
mutable std::optional<TypeResult> m_interfaceType_library;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -865,12 +865,12 @@ public:
|
|||||||
|
|
||||||
bool recursive() const
|
bool recursive() const
|
||||||
{
|
{
|
||||||
if (m_recursive.is_initialized())
|
if (m_recursive.has_value())
|
||||||
return m_recursive.get();
|
return m_recursive.value();
|
||||||
|
|
||||||
interfaceType(false);
|
interfaceType(false);
|
||||||
|
|
||||||
return m_recursive.get();
|
return m_recursive.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
|
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
|
||||||
@ -898,9 +898,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
StructDefinition const& m_struct;
|
StructDefinition const& m_struct;
|
||||||
// Caches for interfaceType(bool)
|
// Caches for interfaceType(bool)
|
||||||
mutable boost::optional<TypeResult> m_interfaceType;
|
mutable std::optional<TypeResult> m_interfaceType;
|
||||||
mutable boost::optional<TypeResult> m_interfaceType_library;
|
mutable std::optional<TypeResult> m_interfaceType_library;
|
||||||
mutable boost::optional<bool> m_recursive;
|
mutable std::optional<bool> m_recursive;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1287,6 +1287,7 @@ public:
|
|||||||
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
|
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
|
||||||
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||||
|
|
||||||
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
private:
|
private:
|
||||||
TypePointer m_actualType;
|
TypePointer m_actualType;
|
||||||
};
|
};
|
||||||
|
@ -411,7 +411,7 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
analyzerResult = yul::AsmAnalyzer(
|
analyzerResult = yul::AsmAnalyzer(
|
||||||
analysisInfo,
|
analysisInfo,
|
||||||
errorReporter,
|
errorReporter,
|
||||||
boost::none,
|
std::nullopt,
|
||||||
dialect,
|
dialect,
|
||||||
identifierAccess.resolve
|
identifierAccess.resolve
|
||||||
).analyze(*parserResult);
|
).analyze(*parserResult);
|
||||||
|
@ -475,7 +475,22 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
{
|
{
|
||||||
solAssert(_functionCall.arguments().size() == 1, "");
|
solAssert(_functionCall.arguments().size() == 1, "");
|
||||||
solAssert(_functionCall.names().empty(), "");
|
solAssert(_functionCall.names().empty(), "");
|
||||||
acceptAndConvert(*_functionCall.arguments().front(), *_functionCall.annotation().type);
|
auto const& expression = *_functionCall.arguments().front();
|
||||||
|
auto const& targetType = *_functionCall.annotation().type;
|
||||||
|
if (auto const* typeType = dynamic_cast<TypeType const*>(expression.annotation().type))
|
||||||
|
if (auto const* addressType = dynamic_cast<AddressType const*>(&targetType))
|
||||||
|
{
|
||||||
|
auto const* contractType = dynamic_cast<ContractType const*>(typeType->actualType());
|
||||||
|
solAssert(
|
||||||
|
contractType &&
|
||||||
|
contractType->contractDefinition().isLibrary() &&
|
||||||
|
addressType->stateMutability() == StateMutability::NonPayable,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
m_context.appendLibraryAddress(contractType->contractDefinition().fullyQualifiedName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
acceptAndConvert(expression, targetType);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1229,6 +1244,28 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
utils().leftShiftNumberOnStack(224);
|
utils().leftShiftNumberOnStack(224);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Another special case for `address(this).balance`. Post-Istanbul, we can use the selfbalance
|
||||||
|
// opcode.
|
||||||
|
if (
|
||||||
|
m_context.evmVersion().hasSelfBalance() &&
|
||||||
|
member == "balance" &&
|
||||||
|
_memberAccess.expression().annotation().type->category() == Type::Category::Address
|
||||||
|
)
|
||||||
|
if (FunctionCall const* funCall = dynamic_cast<FunctionCall const*>(&_memberAccess.expression()))
|
||||||
|
if (auto const* addr = dynamic_cast<ElementaryTypeNameExpression const*>(&funCall->expression()))
|
||||||
|
if (
|
||||||
|
addr->typeName().token() == Token::Address &&
|
||||||
|
funCall->arguments().size() == 1
|
||||||
|
)
|
||||||
|
if (auto arg = dynamic_cast<Identifier const*>( funCall->arguments().front().get()))
|
||||||
|
if (
|
||||||
|
arg->name() == "this" &&
|
||||||
|
dynamic_cast<MagicVariableDeclaration const*>(arg->annotation().referencedDeclaration)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_context << Instruction::SELFBALANCE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_memberAccess.expression().accept(*this);
|
_memberAccess.expression().accept(*this);
|
||||||
switch (_memberAccess.expression().annotation().type->category())
|
switch (_memberAccess.expression().annotation().type->category())
|
||||||
|
@ -962,11 +962,11 @@ string YulUtilFunctions::readFromCalldata(Type const& _type)
|
|||||||
return readFromMemoryOrCalldata(_type, true);
|
return readFromMemoryOrCalldata(_type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::updateStorageValueFunction(Type const& _type, boost::optional<unsigned> const& _offset)
|
string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::optional<unsigned> const& _offset)
|
||||||
{
|
{
|
||||||
string const functionName =
|
string const functionName =
|
||||||
"update_storage_value_" +
|
"update_storage_value_" +
|
||||||
(_offset.is_initialized() ? ("offset_" + to_string(*_offset)) : "") +
|
(_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") +
|
||||||
_type.identifier();
|
_type.identifier();
|
||||||
|
|
||||||
return m_functionCollector->createFunction(functionName, [&] {
|
return m_functionCollector->createFunction(functionName, [&] {
|
||||||
@ -983,11 +983,11 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, boost::op
|
|||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("update",
|
("update",
|
||||||
_offset.is_initialized() ?
|
_offset.has_value() ?
|
||||||
updateByteSliceFunction(_type.storageBytes(), *_offset) :
|
updateByteSliceFunction(_type.storageBytes(), *_offset) :
|
||||||
updateByteSliceFunctionDynamic(_type.storageBytes())
|
updateByteSliceFunctionDynamic(_type.storageBytes())
|
||||||
)
|
)
|
||||||
("offset", _offset.is_initialized() ? "" : "offset, ")
|
("offset", _offset.has_value() ? "" : "offset, ")
|
||||||
("prepare", prepareStoreFunction(_type))
|
("prepare", prepareStoreFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ public:
|
|||||||
/// the specified slot and offset. If offset is not given, it is expected as
|
/// the specified slot and offset. If offset is not given, it is expected as
|
||||||
/// runtime parameter.
|
/// runtime parameter.
|
||||||
/// signature: (slot, [offset,] value)
|
/// signature: (slot, [offset,] value)
|
||||||
std::string updateStorageValueFunction(Type const& _type, boost::optional<unsigned> const& _offset = boost::optional<unsigned>());
|
std::string updateStorageValueFunction(Type const& _type, std::optional<unsigned> const& _offset = std::optional<unsigned>());
|
||||||
|
|
||||||
/// Returns the name of a function that will write the given value to
|
/// Returns the name of a function that will write the given value to
|
||||||
/// the specified address.
|
/// the specified address.
|
||||||
|
@ -115,7 +115,7 @@ string IRStorageItem::storeValue(string const& _value, Type const& _sourceType)
|
|||||||
if (m_type->isValueType())
|
if (m_type->isValueType())
|
||||||
solAssert(_sourceType == *m_type, "Different type, but might not be an error.");
|
solAssert(_sourceType == *m_type, "Different type, but might not be an error.");
|
||||||
|
|
||||||
boost::optional<unsigned> offset;
|
std::optional<unsigned> offset;
|
||||||
|
|
||||||
if (m_offset.type() == typeid(unsigned))
|
if (m_offset.type() == typeid(unsigned))
|
||||||
offset = get<unsigned>(m_offset);
|
offset = get<unsigned>(m_offset);
|
||||||
|
@ -108,14 +108,16 @@ bool BMC::shouldInlineFunctionCall(FunctionCall const& _funCall)
|
|||||||
|
|
||||||
bool BMC::visit(ContractDefinition const& _contract)
|
bool BMC::visit(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
SMTEncoder::visit(_contract);
|
initContract(_contract);
|
||||||
|
|
||||||
/// Check targets created by state variable initialization.
|
/// Check targets created by state variable initialization.
|
||||||
smt::Expression constraints = m_context.assertions();
|
smt::Expression constraints = m_context.assertions();
|
||||||
checkVerificationTargets(constraints);
|
checkVerificationTargets(constraints);
|
||||||
m_verificationTargets.clear();
|
m_verificationTargets.clear();
|
||||||
|
|
||||||
return true;
|
SMTEncoder::visit(_contract);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BMC::endVisit(ContractDefinition const& _contract)
|
void BMC::endVisit(ContractDefinition const& _contract)
|
||||||
@ -426,15 +428,20 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall)
|
|||||||
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
|
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
|
||||||
solAssert(funType, "");
|
solAssert(funType, "");
|
||||||
|
|
||||||
|
auto const& functionParams = funDef->parameters();
|
||||||
|
auto const& arguments = _funCall.arguments();
|
||||||
|
unsigned firstParam = 0;
|
||||||
if (funType->bound())
|
if (funType->bound())
|
||||||
{
|
{
|
||||||
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
|
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
|
||||||
solAssert(boundFunction, "");
|
solAssert(boundFunction, "");
|
||||||
funArgs.push_back(expr(boundFunction->expression()));
|
funArgs.push_back(expr(boundFunction->expression(), functionParams.front()->type()));
|
||||||
|
firstParam = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto arg: _funCall.arguments())
|
solAssert((arguments.size() + firstParam) == functionParams.size(), "");
|
||||||
funArgs.push_back(expr(*arg));
|
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||||
|
funArgs.push_back(expr(*arguments.at(i), functionParams.at(i + firstParam)->type()));
|
||||||
initializeFunctionCallParameters(*funDef, funArgs);
|
initializeFunctionCallParameters(*funDef, funArgs);
|
||||||
|
|
||||||
// The reason why we need to pushCallStack here instead of visit(FunctionDefinition)
|
// The reason why we need to pushCallStack here instead of visit(FunctionDefinition)
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <libsolidity/formal/CHC.h>
|
#include <libsolidity/formal/CHC.h>
|
||||||
|
|
||||||
|
#include <libsolidity/formal/CHCSmtLib2Interface.h>
|
||||||
|
|
||||||
#ifdef HAVE_Z3
|
#ifdef HAVE_Z3
|
||||||
#include <libsolidity/formal/Z3CHCInterface.h>
|
#include <libsolidity/formal/Z3CHCInterface.h>
|
||||||
#endif
|
#endif
|
||||||
@ -30,13 +32,20 @@ using namespace dev;
|
|||||||
using namespace langutil;
|
using namespace langutil;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
|
|
||||||
CHC::CHC(smt::EncodingContext& _context, ErrorReporter& _errorReporter):
|
CHC::CHC(
|
||||||
|
smt::EncodingContext& _context,
|
||||||
|
ErrorReporter& _errorReporter,
|
||||||
|
map<h256, string> const& _smtlib2Responses
|
||||||
|
):
|
||||||
SMTEncoder(_context),
|
SMTEncoder(_context),
|
||||||
#ifdef HAVE_Z3
|
#ifdef HAVE_Z3
|
||||||
m_interface(make_shared<smt::Z3CHCInterface>()),
|
m_interface(make_shared<smt::Z3CHCInterface>()),
|
||||||
|
#else
|
||||||
|
m_interface(make_shared<smt::CHCSmtLib2Interface>(_smtlib2Responses)),
|
||||||
#endif
|
#endif
|
||||||
m_outerErrorReporter(_errorReporter)
|
m_outerErrorReporter(_errorReporter)
|
||||||
{
|
{
|
||||||
|
(void)_smtlib2Responses;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::analyze(SourceUnit const& _source)
|
void CHC::analyze(SourceUnit const& _source)
|
||||||
@ -47,12 +56,24 @@ void CHC::analyze(SourceUnit const& _source)
|
|||||||
auto z3Interface = dynamic_pointer_cast<smt::Z3CHCInterface>(m_interface);
|
auto z3Interface = dynamic_pointer_cast<smt::Z3CHCInterface>(m_interface);
|
||||||
solAssert(z3Interface, "");
|
solAssert(z3Interface, "");
|
||||||
m_context.setSolver(z3Interface->z3Interface());
|
m_context.setSolver(z3Interface->z3Interface());
|
||||||
|
#else
|
||||||
|
auto smtlib2Interface = dynamic_pointer_cast<smt::CHCSmtLib2Interface>(m_interface);
|
||||||
|
solAssert(smtlib2Interface, "");
|
||||||
|
m_context.setSolver(smtlib2Interface->smtlib2Interface());
|
||||||
|
#endif
|
||||||
m_context.clear();
|
m_context.clear();
|
||||||
m_context.setAssertionAccumulation(false);
|
m_context.setAssertionAccumulation(false);
|
||||||
m_variableUsage.setFunctionInlining(false);
|
m_variableUsage.setFunctionInlining(false);
|
||||||
|
|
||||||
_source.accept(*this);
|
_source.accept(*this);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
|
vector<string> CHC::unhandledQueries() const
|
||||||
|
{
|
||||||
|
if (auto smtlib2 = dynamic_pointer_cast<smt::CHCSmtLib2Interface>(m_interface))
|
||||||
|
return smtlib2->unhandledQueries();
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CHC::visit(ContractDefinition const& _contract)
|
bool CHC::visit(ContractDefinition const& _contract)
|
||||||
@ -62,8 +83,7 @@ bool CHC::visit(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
if (!SMTEncoder::visit(_contract))
|
initContract(_contract);
|
||||||
return false;
|
|
||||||
|
|
||||||
m_stateVariables = _contract.stateVariablesIncludingInherited();
|
m_stateVariables = _contract.stateVariablesIncludingInherited();
|
||||||
|
|
||||||
@ -89,7 +109,6 @@ bool CHC::visit(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
// If the contract has a constructor it is handled as a function.
|
// If the contract has a constructor it is handled as a function.
|
||||||
// Otherwise we zero-initialize all state vars.
|
// Otherwise we zero-initialize all state vars.
|
||||||
// TODO take into account state vars init values.
|
|
||||||
if (!_contract.constructor())
|
if (!_contract.constructor())
|
||||||
{
|
{
|
||||||
string constructorName = "constructor_" + _contract.name() + "_" + to_string(_contract.id());
|
string constructorName = "constructor_" + _contract.name() + "_" + to_string(_contract.id());
|
||||||
@ -101,14 +120,15 @@ bool CHC::visit(ContractDefinition const& _contract)
|
|||||||
{
|
{
|
||||||
auto const& symbVar = m_context.variable(*var);
|
auto const& symbVar = m_context.variable(*var);
|
||||||
symbVar->increaseIndex();
|
symbVar->increaseIndex();
|
||||||
m_interface->declareVariable(symbVar->currentName(), *symbVar->sort());
|
m_interface->declareVariable(symbVar->currentName(), symbVar->sort());
|
||||||
m_context.setZeroValue(*symbVar);
|
m_context.setZeroValue(*symbVar);
|
||||||
}
|
}
|
||||||
|
|
||||||
connectBlocks(constructorPred, interface());
|
connectBlocks(constructorPred, interface());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
SMTEncoder::visit(_contract);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::endVisit(ContractDefinition const& _contract)
|
void CHC::endVisit(ContractDefinition const& _contract)
|
||||||
@ -137,24 +157,22 @@ bool CHC::visit(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
initFunction(_function);
|
initFunction(_function);
|
||||||
|
|
||||||
|
auto functionEntryBlock = createBlock(m_currentFunction);
|
||||||
|
auto bodyBlock = createBlock(&m_currentFunction->body());
|
||||||
|
|
||||||
|
auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables());
|
||||||
|
auto bodyPred = predicate(*bodyBlock);
|
||||||
|
|
||||||
// Store the constraints related to variable initialization.
|
// Store the constraints related to variable initialization.
|
||||||
smt::Expression const& initAssertions = m_context.assertions();
|
smt::Expression const& initAssertions = m_context.assertions();
|
||||||
m_context.pushSolver();
|
m_context.pushSolver();
|
||||||
|
|
||||||
solAssert(m_functionBlocks == 0, "");
|
|
||||||
|
|
||||||
createBlock(m_currentFunction);
|
|
||||||
createBlock(&m_currentFunction->body(), "block_");
|
|
||||||
|
|
||||||
auto functionPred = predicate(m_currentFunction);
|
|
||||||
auto bodyPred = predicate(&m_currentFunction->body());
|
|
||||||
|
|
||||||
connectBlocks(interface(), functionPred);
|
connectBlocks(interface(), functionPred);
|
||||||
connectBlocks(functionPred, bodyPred);
|
connectBlocks(functionPred, bodyPred);
|
||||||
|
|
||||||
m_context.popSolver();
|
m_context.popSolver();
|
||||||
|
|
||||||
pushBlock(&m_currentFunction->body());
|
setCurrentBlock(*bodyBlock);
|
||||||
|
|
||||||
// We need to re-add the constraints that were created for initialization of variables.
|
// We need to re-add the constraints that were created for initialization of variables.
|
||||||
m_context.addAssertion(initAssertions);
|
m_context.addAssertion(initAssertions);
|
||||||
@ -169,25 +187,10 @@ void CHC::endVisit(FunctionDefinition const& _function)
|
|||||||
if (!shouldVisit(_function))
|
if (!shouldVisit(_function))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
solAssert(m_currentFunction == &_function, "Inlining internal function calls not yet implemented");
|
connectBlocks(m_currentBlock, interface());
|
||||||
|
|
||||||
// Function Exit block.
|
|
||||||
createBlock(m_currentFunction);
|
|
||||||
connectBlocks(m_path.back(), predicate(&_function));
|
|
||||||
|
|
||||||
// Rule FunctionExit -> Interface, uses no constraints.
|
|
||||||
clearIndices();
|
|
||||||
m_context.pushSolver();
|
|
||||||
connectBlocks(predicate(&_function), interface());
|
|
||||||
m_context.popSolver();
|
|
||||||
|
|
||||||
|
solAssert(&_function == m_currentFunction, "");
|
||||||
m_currentFunction = nullptr;
|
m_currentFunction = nullptr;
|
||||||
solAssert(m_path.size() == m_functionBlocks, "");
|
|
||||||
while (m_functionBlocks > 0)
|
|
||||||
popBlock();
|
|
||||||
|
|
||||||
solAssert(m_path.empty(), "");
|
|
||||||
|
|
||||||
SMTEncoder::endVisit(_function);
|
SMTEncoder::endVisit(_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +201,38 @@ bool CHC::visit(IfStatement const& _if)
|
|||||||
bool unknownFunctionCallWasSeen = m_unknownFunctionCallSeen;
|
bool unknownFunctionCallWasSeen = m_unknownFunctionCallSeen;
|
||||||
m_unknownFunctionCallSeen = false;
|
m_unknownFunctionCallSeen = false;
|
||||||
|
|
||||||
SMTEncoder::visit(_if);
|
solAssert(m_currentFunction, "");
|
||||||
|
auto const& functionBody = m_currentFunction->body();
|
||||||
|
|
||||||
|
auto ifHeaderBlock = createBlock(&_if, "if_header_");
|
||||||
|
auto trueBlock = createBlock(&_if.trueStatement(), "if_true_");
|
||||||
|
auto falseBlock = _if.falseStatement() ? createBlock(_if.falseStatement(), "if_false_") : nullptr;
|
||||||
|
auto afterIfBlock = createBlock(&functionBody);
|
||||||
|
|
||||||
|
connectBlocks(m_currentBlock, predicate(*ifHeaderBlock));
|
||||||
|
|
||||||
|
setCurrentBlock(*ifHeaderBlock);
|
||||||
|
_if.condition().accept(*this);
|
||||||
|
auto condition = expr(_if.condition());
|
||||||
|
|
||||||
|
connectBlocks(m_currentBlock, predicate(*trueBlock), condition);
|
||||||
|
if (_if.falseStatement())
|
||||||
|
connectBlocks(m_currentBlock, predicate(*falseBlock), !condition);
|
||||||
|
else
|
||||||
|
connectBlocks(m_currentBlock, predicate(*afterIfBlock), !condition);
|
||||||
|
|
||||||
|
setCurrentBlock(*trueBlock);
|
||||||
|
_if.trueStatement().accept(*this);
|
||||||
|
connectBlocks(m_currentBlock, predicate(*afterIfBlock));
|
||||||
|
|
||||||
|
if (_if.falseStatement())
|
||||||
|
{
|
||||||
|
setCurrentBlock(*falseBlock);
|
||||||
|
_if.falseStatement()->accept(*this);
|
||||||
|
connectBlocks(m_currentBlock, predicate(*afterIfBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentBlock(*afterIfBlock);
|
||||||
|
|
||||||
if (m_unknownFunctionCallSeen)
|
if (m_unknownFunctionCallSeen)
|
||||||
eraseKnowledge();
|
eraseKnowledge();
|
||||||
@ -214,16 +248,41 @@ bool CHC::visit(WhileStatement const& _while)
|
|||||||
m_unknownFunctionCallSeen = false;
|
m_unknownFunctionCallSeen = false;
|
||||||
|
|
||||||
solAssert(m_currentFunction, "");
|
solAssert(m_currentFunction, "");
|
||||||
|
auto const& functionBody = m_currentFunction->body();
|
||||||
|
|
||||||
|
auto namePrefix = string(_while.isDoWhile() ? "do_" : "") + "while";
|
||||||
|
auto loopHeaderBlock = createBlock(&_while, namePrefix + "_header_");
|
||||||
|
auto loopBodyBlock = createBlock(&_while.body(), namePrefix + "_body_");
|
||||||
|
auto afterLoopBlock = createBlock(&functionBody);
|
||||||
|
|
||||||
|
auto outerBreakDest = m_breakDest;
|
||||||
|
auto outerContinueDest = m_continueDest;
|
||||||
|
m_breakDest = afterLoopBlock.get();
|
||||||
|
m_continueDest = loopHeaderBlock.get();
|
||||||
|
|
||||||
if (_while.isDoWhile())
|
if (_while.isDoWhile())
|
||||||
_while.body().accept(*this);
|
_while.body().accept(*this);
|
||||||
|
|
||||||
visitLoop(
|
connectBlocks(m_currentBlock, predicate(*loopHeaderBlock));
|
||||||
_while,
|
|
||||||
&_while.condition(),
|
setCurrentBlock(*loopHeaderBlock);
|
||||||
_while.body(),
|
|
||||||
nullptr
|
_while.condition().accept(*this);
|
||||||
);
|
auto condition = expr(_while.condition());
|
||||||
|
|
||||||
|
connectBlocks(m_currentBlock, predicate(*loopBodyBlock), condition);
|
||||||
|
connectBlocks(m_currentBlock, predicate(*afterLoopBlock), !condition);
|
||||||
|
|
||||||
|
// Loop body visit.
|
||||||
|
setCurrentBlock(*loopBodyBlock);
|
||||||
|
_while.body().accept(*this);
|
||||||
|
|
||||||
|
m_breakDest = outerBreakDest;
|
||||||
|
m_continueDest = outerContinueDest;
|
||||||
|
|
||||||
|
// Back edge.
|
||||||
|
connectBlocks(m_currentBlock, predicate(*loopHeaderBlock));
|
||||||
|
setCurrentBlock(*afterLoopBlock);
|
||||||
|
|
||||||
if (m_unknownFunctionCallSeen)
|
if (m_unknownFunctionCallSeen)
|
||||||
eraseKnowledge();
|
eraseKnowledge();
|
||||||
@ -239,16 +298,52 @@ bool CHC::visit(ForStatement const& _for)
|
|||||||
m_unknownFunctionCallSeen = false;
|
m_unknownFunctionCallSeen = false;
|
||||||
|
|
||||||
solAssert(m_currentFunction, "");
|
solAssert(m_currentFunction, "");
|
||||||
|
auto const& functionBody = m_currentFunction->body();
|
||||||
|
|
||||||
|
auto loopHeaderBlock = createBlock(&_for, "for_header_");
|
||||||
|
auto loopBodyBlock = createBlock(&_for.body(), "for_body_");
|
||||||
|
auto afterLoopBlock = createBlock(&functionBody);
|
||||||
|
auto postLoop = _for.loopExpression();
|
||||||
|
auto postLoopBlock = postLoop ? createBlock(postLoop, "for_post_") : nullptr;
|
||||||
|
|
||||||
|
auto outerBreakDest = m_breakDest;
|
||||||
|
auto outerContinueDest = m_continueDest;
|
||||||
|
m_breakDest = afterLoopBlock.get();
|
||||||
|
m_continueDest = postLoop ? postLoopBlock.get() : loopHeaderBlock.get();
|
||||||
|
|
||||||
if (auto init = _for.initializationExpression())
|
if (auto init = _for.initializationExpression())
|
||||||
init->accept(*this);
|
init->accept(*this);
|
||||||
|
|
||||||
visitLoop(
|
connectBlocks(m_currentBlock, predicate(*loopHeaderBlock));
|
||||||
_for,
|
setCurrentBlock(*loopHeaderBlock);
|
||||||
_for.condition(),
|
|
||||||
_for.body(),
|
auto condition = smt::Expression(true);
|
||||||
_for.loopExpression()
|
if (auto forCondition = _for.condition())
|
||||||
);
|
{
|
||||||
|
forCondition->accept(*this);
|
||||||
|
condition = expr(*forCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectBlocks(m_currentBlock, predicate(*loopBodyBlock), condition);
|
||||||
|
connectBlocks(m_currentBlock, predicate(*afterLoopBlock), !condition);
|
||||||
|
|
||||||
|
// Loop body visit.
|
||||||
|
setCurrentBlock(*loopBodyBlock);
|
||||||
|
_for.body().accept(*this);
|
||||||
|
|
||||||
|
if (postLoop)
|
||||||
|
{
|
||||||
|
connectBlocks(m_currentBlock, predicate(*postLoopBlock));
|
||||||
|
setCurrentBlock(*postLoopBlock);
|
||||||
|
postLoop->accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_breakDest = outerBreakDest;
|
||||||
|
m_continueDest = outerContinueDest;
|
||||||
|
|
||||||
|
// Back edge.
|
||||||
|
connectBlocks(m_currentBlock, predicate(*loopHeaderBlock));
|
||||||
|
setCurrentBlock(*afterLoopBlock);
|
||||||
|
|
||||||
if (m_unknownFunctionCallSeen)
|
if (m_unknownFunctionCallSeen)
|
||||||
eraseKnowledge();
|
eraseKnowledge();
|
||||||
@ -301,16 +396,20 @@ void CHC::endVisit(FunctionCall const& _funCall)
|
|||||||
createReturnedExpressions(_funCall);
|
createReturnedExpressions(_funCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::endVisit(Break const&)
|
void CHC::endVisit(Break const& _break)
|
||||||
{
|
{
|
||||||
solAssert(m_breakDest, "");
|
solAssert(m_breakDest, "");
|
||||||
m_breakSeen = true;
|
connectBlocks(m_currentBlock, predicate(*m_breakDest));
|
||||||
|
auto breakGhost = createBlock(&_break, "break_ghost_");
|
||||||
|
m_currentBlock = predicate(*breakGhost);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::endVisit(Continue const&)
|
void CHC::endVisit(Continue const& _continue)
|
||||||
{
|
{
|
||||||
solAssert(m_continueDest, "");
|
solAssert(m_continueDest, "");
|
||||||
m_continueSeen = true;
|
connectBlocks(m_currentBlock, predicate(*m_continueDest));
|
||||||
|
auto continueGhost = createBlock(&_continue, "continue_ghost_");
|
||||||
|
m_currentBlock = predicate(*continueGhost);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::visitAssert(FunctionCall const& _funCall)
|
void CHC::visitAssert(FunctionCall const& _funCall)
|
||||||
@ -319,12 +418,10 @@ void CHC::visitAssert(FunctionCall const& _funCall)
|
|||||||
solAssert(args.size() == 1, "");
|
solAssert(args.size() == 1, "");
|
||||||
solAssert(args.front()->annotation().type->category() == Type::Category::Bool, "");
|
solAssert(args.front()->annotation().type->category() == Type::Category::Bool, "");
|
||||||
|
|
||||||
solAssert(!m_path.empty(), "");
|
|
||||||
|
|
||||||
createErrorBlock();
|
createErrorBlock();
|
||||||
|
|
||||||
smt::Expression assertNeg = !(m_context.expression(*args.front())->currentValue());
|
smt::Expression assertNeg = !(m_context.expression(*args.front())->currentValue());
|
||||||
connectBlocks(m_path.back(), error(), currentPathConditions() && assertNeg);
|
connectBlocks(m_currentBlock, error(), currentPathConditions() && assertNeg);
|
||||||
|
|
||||||
m_verificationTargets.push_back(&_funCall);
|
m_verificationTargets.push_back(&_funCall);
|
||||||
}
|
}
|
||||||
@ -341,85 +438,6 @@ void CHC::unknownFunctionCall(FunctionCall const&)
|
|||||||
m_unknownFunctionCallSeen = true;
|
m_unknownFunctionCallSeen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::visitLoop(
|
|
||||||
BreakableStatement const& _loop,
|
|
||||||
Expression const* _condition,
|
|
||||||
Statement const& _body,
|
|
||||||
ASTNode const* _postLoop
|
|
||||||
)
|
|
||||||
{
|
|
||||||
bool breakWasSeen = m_breakSeen;
|
|
||||||
bool continueWasSeen = m_continueSeen;
|
|
||||||
m_breakSeen = false;
|
|
||||||
m_continueSeen = false;
|
|
||||||
|
|
||||||
solAssert(m_currentFunction, "");
|
|
||||||
auto const& functionBody = m_currentFunction->body();
|
|
||||||
|
|
||||||
createBlock(&_loop, "loop_header_");
|
|
||||||
createBlock(&_body, "loop_body_");
|
|
||||||
createBlock(&functionBody, "block_");
|
|
||||||
|
|
||||||
connectBlocks(m_path.back(), predicate(&_loop));
|
|
||||||
|
|
||||||
// We need to save the next block here because new blocks
|
|
||||||
// might be created inside the loop body.
|
|
||||||
// This will be m_path.back() in the end of this function.
|
|
||||||
pushBlock(&functionBody);
|
|
||||||
|
|
||||||
smt::Expression loopHeader = predicate(&_loop);
|
|
||||||
pushBlock(&_loop);
|
|
||||||
|
|
||||||
if (_condition)
|
|
||||||
_condition->accept(*this);
|
|
||||||
auto condition = _condition ? expr(*_condition) : smt::Expression(true);
|
|
||||||
|
|
||||||
connectBlocks(loopHeader, predicate(&_body), condition);
|
|
||||||
connectBlocks(loopHeader, predicate(&functionBody), !condition);
|
|
||||||
|
|
||||||
// Loop body visit.
|
|
||||||
pushBlock(&_body);
|
|
||||||
|
|
||||||
m_breakDest = &functionBody;
|
|
||||||
m_continueDest = _postLoop ? _postLoop : &_loop;
|
|
||||||
|
|
||||||
auto functionBlocks = m_functionBlocks;
|
|
||||||
_body.accept(*this);
|
|
||||||
if (_postLoop)
|
|
||||||
{
|
|
||||||
createBlock(_postLoop, "loop_post_");
|
|
||||||
connectBlocks(m_path.back(), predicate(_postLoop));
|
|
||||||
pushBlock(_postLoop);
|
|
||||||
_postLoop->accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back edge.
|
|
||||||
connectBlocks(m_path.back(), predicate(&_loop));
|
|
||||||
|
|
||||||
// Pop all function blocks created by nested inner loops
|
|
||||||
// to adjust the assertion context.
|
|
||||||
for (unsigned i = m_functionBlocks; i > functionBlocks; --i)
|
|
||||||
popBlock();
|
|
||||||
m_functionBlocks = functionBlocks;
|
|
||||||
|
|
||||||
// Loop body
|
|
||||||
popBlock();
|
|
||||||
// Loop header
|
|
||||||
popBlock();
|
|
||||||
|
|
||||||
// New function block starts with indices = 0
|
|
||||||
clearIndices();
|
|
||||||
|
|
||||||
if (m_breakSeen || m_continueSeen)
|
|
||||||
{
|
|
||||||
eraseKnowledge();
|
|
||||||
m_context.resetVariables([](VariableDeclaration const&) { return true; });
|
|
||||||
}
|
|
||||||
|
|
||||||
m_breakSeen = breakWasSeen;
|
|
||||||
m_continueSeen = continueWasSeen;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHC::reset()
|
void CHC::reset()
|
||||||
{
|
{
|
||||||
m_stateSorts.clear();
|
m_stateSorts.clear();
|
||||||
@ -427,8 +445,9 @@ void CHC::reset()
|
|||||||
m_verificationTargets.clear();
|
m_verificationTargets.clear();
|
||||||
m_safeAssertions.clear();
|
m_safeAssertions.clear();
|
||||||
m_unknownFunctionCallSeen = false;
|
m_unknownFunctionCallSeen = false;
|
||||||
m_breakSeen = false;
|
m_blockCounter = 0;
|
||||||
m_continueSeen = false;
|
m_breakDest = nullptr;
|
||||||
|
m_continueDest = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::eraseKnowledge()
|
void CHC::eraseKnowledge()
|
||||||
@ -458,19 +477,12 @@ bool CHC::shouldVisit(FunctionDefinition const& _function) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::pushBlock(ASTNode const* _node)
|
void CHC::setCurrentBlock(smt::SymbolicFunctionVariable const& _block)
|
||||||
{
|
|
||||||
clearIndices();
|
|
||||||
m_context.pushSolver();
|
|
||||||
m_path.push_back(predicate(_node));
|
|
||||||
++m_functionBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHC::popBlock()
|
|
||||||
{
|
{
|
||||||
m_context.popSolver();
|
m_context.popSolver();
|
||||||
m_path.pop_back();
|
clearIndices();
|
||||||
--m_functionBlocks;
|
m_context.pushSolver();
|
||||||
|
m_currentBlock = predicate(_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::SortPointer CHC::constructorSort()
|
smt::SortPointer CHC::constructorSort()
|
||||||
@ -493,25 +505,24 @@ smt::SortPointer CHC::interfaceSort()
|
|||||||
|
|
||||||
smt::SortPointer CHC::sort(FunctionDefinition const& _function)
|
smt::SortPointer CHC::sort(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
if (m_nodeSorts.count(&_function))
|
|
||||||
return m_nodeSorts.at(&_function);
|
|
||||||
|
|
||||||
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
|
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
|
||||||
vector<smt::SortPointer> varSorts;
|
vector<smt::SortPointer> varSorts;
|
||||||
for (auto const& var: _function.parameters() + _function.returnParameters())
|
for (auto const& var: _function.parameters() + _function.returnParameters())
|
||||||
|
{
|
||||||
|
// SMT solvers do not support function types as arguments.
|
||||||
|
if (var->type()->category() == Type::Category::Function)
|
||||||
|
varSorts.push_back(make_shared<smt::Sort>(smt::Kind::Int));
|
||||||
|
else
|
||||||
varSorts.push_back(smt::smtSort(*var->type()));
|
varSorts.push_back(smt::smtSort(*var->type()));
|
||||||
auto sort = make_shared<smt::FunctionSort>(
|
}
|
||||||
|
return make_shared<smt::FunctionSort>(
|
||||||
m_stateSorts + varSorts,
|
m_stateSorts + varSorts,
|
||||||
boolSort
|
boolSort
|
||||||
);
|
);
|
||||||
return m_nodeSorts[&_function] = move(sort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::SortPointer CHC::sort(ASTNode const* _node)
|
smt::SortPointer CHC::sort(ASTNode const* _node)
|
||||||
{
|
{
|
||||||
if (m_nodeSorts.count(_node))
|
|
||||||
return m_nodeSorts.at(_node);
|
|
||||||
|
|
||||||
if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node))
|
if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node))
|
||||||
return sort(*funDef);
|
return sort(*funDef);
|
||||||
|
|
||||||
@ -521,12 +532,17 @@ smt::SortPointer CHC::sort(ASTNode const* _node)
|
|||||||
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
|
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
|
||||||
vector<smt::SortPointer> varSorts;
|
vector<smt::SortPointer> varSorts;
|
||||||
for (auto const& var: m_currentFunction->localVariables())
|
for (auto const& var: m_currentFunction->localVariables())
|
||||||
|
{
|
||||||
|
// SMT solvers do not support function types as arguments.
|
||||||
|
if (var->type()->category() == Type::Category::Function)
|
||||||
|
varSorts.push_back(make_shared<smt::Sort>(smt::Kind::Int));
|
||||||
|
else
|
||||||
varSorts.push_back(smt::smtSort(*var->type()));
|
varSorts.push_back(smt::smtSort(*var->type()));
|
||||||
auto functionBodySort = make_shared<smt::FunctionSort>(
|
}
|
||||||
|
return make_shared<smt::FunctionSort>(
|
||||||
fSort->domain + varSorts,
|
fSort->domain + varSorts,
|
||||||
boolSort
|
boolSort
|
||||||
);
|
);
|
||||||
return m_nodeSorts[_node] = move(functionBodySort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPointer _sort, string const& _name)
|
unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPointer _sort, string const& _name)
|
||||||
@ -536,7 +552,7 @@ unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPoin
|
|||||||
_name,
|
_name,
|
||||||
m_context
|
m_context
|
||||||
);
|
);
|
||||||
m_interface->registerRelation(block->currentValue());
|
m_interface->registerRelation(block->currentFunctionValue());
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,25 +584,24 @@ smt::Expression CHC::error()
|
|||||||
|
|
||||||
smt::Expression CHC::error(unsigned _idx)
|
smt::Expression CHC::error(unsigned _idx)
|
||||||
{
|
{
|
||||||
return m_errorPredicate->valueAtIndex(_idx)({});
|
return m_errorPredicate->functionValueAtIndex(_idx)({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::createBlock(ASTNode const* _node, string const& _prefix)
|
unique_ptr<smt::SymbolicFunctionVariable> CHC::createBlock(ASTNode const* _node, string const& _prefix)
|
||||||
{
|
{
|
||||||
if (m_predicates.count(_node))
|
return createSymbolicBlock(sort(_node),
|
||||||
{
|
"block_" +
|
||||||
m_predicates.at(_node)->increaseIndex();
|
uniquePrefix() +
|
||||||
m_interface->registerRelation(m_predicates.at(_node)->currentValue());
|
"_" +
|
||||||
}
|
_prefix +
|
||||||
else
|
predicateName(_node));
|
||||||
m_predicates[_node] = createSymbolicBlock(sort(_node), _prefix + predicateName(_node));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::createErrorBlock()
|
void CHC::createErrorBlock()
|
||||||
{
|
{
|
||||||
solAssert(m_errorPredicate, "");
|
solAssert(m_errorPredicate, "");
|
||||||
m_errorPredicate->increaseIndex();
|
m_errorPredicate->increaseIndex();
|
||||||
m_interface->registerRelation(m_errorPredicate->currentValue());
|
m_interface->registerRelation(m_errorPredicate->currentFunctionValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints)
|
void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints)
|
||||||
@ -646,13 +661,20 @@ string CHC::predicateName(ASTNode const* _node)
|
|||||||
return prefix + to_string(_node->id());
|
return prefix + to_string(_node->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::Expression CHC::predicate(ASTNode const* _node)
|
smt::Expression CHC::predicate(smt::SymbolicFunctionVariable const& _block)
|
||||||
{
|
{
|
||||||
if (dynamic_cast<FunctionDefinition const*>(_node))
|
return _block(currentBlockVariables());
|
||||||
return (*m_predicates.at(_node))(currentFunctionVariables());
|
|
||||||
return (*m_predicates.at(_node))(currentBlockVariables());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smt::Expression CHC::predicate(
|
||||||
|
smt::SymbolicFunctionVariable const& _block,
|
||||||
|
vector<smt::Expression> const& _arguments
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return _block(_arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CHC::addRule(smt::Expression const& _rule, string const& _ruleName)
|
void CHC::addRule(smt::Expression const& _rule, string const& _ruleName)
|
||||||
{
|
{
|
||||||
m_interface->addRule(_rule, _ruleName);
|
m_interface->addRule(_rule, _ruleName);
|
||||||
@ -680,3 +702,8 @@ bool CHC::query(smt::Expression const& _query, langutil::SourceLocation const& _
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string CHC::uniquePrefix()
|
||||||
|
{
|
||||||
|
return to_string(m_blockCounter++);
|
||||||
|
}
|
||||||
|
@ -44,12 +44,21 @@ namespace solidity
|
|||||||
class CHC: public SMTEncoder
|
class CHC: public SMTEncoder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CHC(smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter);
|
CHC(
|
||||||
|
smt::EncodingContext& _context,
|
||||||
|
langutil::ErrorReporter& _errorReporter,
|
||||||
|
std::map<h256, std::string> const& _smtlib2Responses
|
||||||
|
);
|
||||||
|
|
||||||
void analyze(SourceUnit const& _sources);
|
void analyze(SourceUnit const& _sources);
|
||||||
|
|
||||||
std::set<Expression const*> const& safeAssertions() const { return m_safeAssertions; }
|
std::set<Expression const*> const& safeAssertions() const { return m_safeAssertions; }
|
||||||
|
|
||||||
|
/// This is used if the Horn solver is not directly linked into this binary.
|
||||||
|
/// @returns a list of inputs to the Horn solver that were not part of the argument to
|
||||||
|
/// the constructor.
|
||||||
|
std::vector<std::string> unhandledQueries() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Visitor functions.
|
/// Visitor functions.
|
||||||
//@{
|
//@{
|
||||||
@ -66,12 +75,6 @@ private:
|
|||||||
|
|
||||||
void visitAssert(FunctionCall const& _funCall);
|
void visitAssert(FunctionCall const& _funCall);
|
||||||
void unknownFunctionCall(FunctionCall const& _funCall);
|
void unknownFunctionCall(FunctionCall const& _funCall);
|
||||||
void visitLoop(
|
|
||||||
BreakableStatement const& _loop,
|
|
||||||
Expression const* _condition,
|
|
||||||
Statement const& _body,
|
|
||||||
ASTNode const* _postLoop
|
|
||||||
);
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Helpers.
|
/// Helpers.
|
||||||
@ -80,8 +83,7 @@ private:
|
|||||||
void eraseKnowledge();
|
void eraseKnowledge();
|
||||||
bool shouldVisit(ContractDefinition const& _contract) const;
|
bool shouldVisit(ContractDefinition const& _contract) const;
|
||||||
bool shouldVisit(FunctionDefinition const& _function) const;
|
bool shouldVisit(FunctionDefinition const& _function) const;
|
||||||
void pushBlock(ASTNode const* _node);
|
void setCurrentBlock(smt::SymbolicFunctionVariable const& _block);
|
||||||
void popBlock();
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Sort helpers.
|
/// Sort helpers.
|
||||||
@ -105,9 +107,8 @@ private:
|
|||||||
smt::Expression error();
|
smt::Expression error();
|
||||||
smt::Expression error(unsigned _idx);
|
smt::Expression error(unsigned _idx);
|
||||||
|
|
||||||
/// Creates a block for the given _node or increases its SSA index
|
/// Creates a block for the given _node.
|
||||||
/// if the block already exists which in practice creates a new function.
|
std::unique_ptr<smt::SymbolicFunctionVariable> createBlock(ASTNode const* _node, std::string const& _prefix = "");
|
||||||
void createBlock(ASTNode const* _node, std::string const& _prefix = "");
|
|
||||||
|
|
||||||
/// Creates a new error block to be used by an assertion.
|
/// Creates a new error block to be used by an assertion.
|
||||||
/// Also registers the predicate.
|
/// Also registers the predicate.
|
||||||
@ -129,7 +130,9 @@ private:
|
|||||||
/// @returns the predicate name for a given node.
|
/// @returns the predicate name for a given node.
|
||||||
std::string predicateName(ASTNode const* _node);
|
std::string predicateName(ASTNode const* _node);
|
||||||
/// @returns a predicate application over the current scoped variables.
|
/// @returns a predicate application over the current scoped variables.
|
||||||
smt::Expression predicate(ASTNode const* _node);
|
smt::Expression predicate(smt::SymbolicFunctionVariable const& _block);
|
||||||
|
/// @returns a predicate application over @param _arguments.
|
||||||
|
smt::Expression predicate(smt::SymbolicFunctionVariable const& _block, std::vector<smt::Expression> const& _arguments);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Solver related.
|
/// Solver related.
|
||||||
@ -140,22 +143,26 @@ private:
|
|||||||
bool query(smt::Expression const& _query, langutil::SourceLocation const& _location);
|
bool query(smt::Expression const& _query, langutil::SourceLocation const& _location);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
/// Misc.
|
||||||
|
//@{
|
||||||
|
/// Returns a prefix to be used in a new unique block name
|
||||||
|
/// and increases the block counter.
|
||||||
|
std::string uniquePrefix();
|
||||||
|
//@}
|
||||||
|
|
||||||
/// Predicates.
|
/// Predicates.
|
||||||
//@{
|
//@{
|
||||||
/// Constructor predicate.
|
/// Constructor predicate.
|
||||||
/// Default constructor sets state vars to 0.
|
/// Default constructor sets state vars to 0.
|
||||||
std::unique_ptr<smt::SymbolicVariable> m_constructorPredicate;
|
std::unique_ptr<smt::SymbolicFunctionVariable> m_constructorPredicate;
|
||||||
|
|
||||||
/// Artificial Interface predicate.
|
/// Artificial Interface predicate.
|
||||||
/// Single entry block for all functions.
|
/// Single entry block for all functions.
|
||||||
std::unique_ptr<smt::SymbolicVariable> m_interfacePredicate;
|
std::unique_ptr<smt::SymbolicFunctionVariable> m_interfacePredicate;
|
||||||
|
|
||||||
/// Artificial Error predicate.
|
/// Artificial Error predicate.
|
||||||
/// Single error block for all assertions.
|
/// Single error block for all assertions.
|
||||||
std::unique_ptr<smt::SymbolicVariable> m_errorPredicate;
|
std::unique_ptr<smt::SymbolicFunctionVariable> m_errorPredicate;
|
||||||
|
|
||||||
/// Maps AST nodes to their predicates.
|
|
||||||
std::unordered_map<ASTNode const*, std::shared_ptr<smt::SymbolicVariable>> m_predicates;
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Variables.
|
/// Variables.
|
||||||
@ -166,9 +173,6 @@ private:
|
|||||||
/// State variables.
|
/// State variables.
|
||||||
/// Used to create all predicates.
|
/// Used to create all predicates.
|
||||||
std::vector<VariableDeclaration const*> m_stateVariables;
|
std::vector<VariableDeclaration const*> m_stateVariables;
|
||||||
|
|
||||||
/// Input sorts for AST nodes.
|
|
||||||
std::map<ASTNode const*, smt::SortPointer> m_nodeSorts;
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Verification targets.
|
/// Verification targets.
|
||||||
@ -183,21 +187,19 @@ private:
|
|||||||
//@{
|
//@{
|
||||||
FunctionDefinition const* m_currentFunction = nullptr;
|
FunctionDefinition const* m_currentFunction = nullptr;
|
||||||
|
|
||||||
/// Number of basic blocks created for the body of the current function.
|
/// The current block.
|
||||||
unsigned m_functionBlocks = 0;
|
smt::Expression m_currentBlock = smt::Expression(true);
|
||||||
/// The current control flow path.
|
|
||||||
std::vector<smt::Expression> m_path;
|
/// Counter to generate unique block names.
|
||||||
|
unsigned m_blockCounter = 0;
|
||||||
|
|
||||||
/// Whether a function call was seen in the current scope.
|
/// Whether a function call was seen in the current scope.
|
||||||
bool m_unknownFunctionCallSeen = false;
|
bool m_unknownFunctionCallSeen = false;
|
||||||
/// Whether a break statement was seen in the current scope.
|
|
||||||
bool m_breakSeen = false;
|
|
||||||
/// Whether a continue statement was seen in the current scope.
|
|
||||||
bool m_continueSeen = false;
|
|
||||||
|
|
||||||
/// Block where a loop break should go to.
|
/// Block where a loop break should go to.
|
||||||
ASTNode const* m_breakDest;
|
smt::SymbolicFunctionVariable const* m_breakDest = nullptr;
|
||||||
/// Block where a loop continue should go to.
|
/// Block where a loop continue should go to.
|
||||||
ASTNode const* m_continueDest;
|
smt::SymbolicFunctionVariable const* m_continueDest = nullptr;
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// CHC solver.
|
/// CHC solver.
|
||||||
|
160
libsolidity/formal/CHCSmtLib2Interface.cpp
Normal file
160
libsolidity/formal/CHCSmtLib2Interface.cpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/formal/CHCSmtLib2Interface.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Keccak256.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
using namespace dev::solidity::smt;
|
||||||
|
|
||||||
|
CHCSmtLib2Interface::CHCSmtLib2Interface(map<h256, string> const& _queryResponses):
|
||||||
|
m_smtlib2(make_shared<SMTLib2Interface>(_queryResponses)),
|
||||||
|
m_queryResponses(_queryResponses)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHCSmtLib2Interface::reset()
|
||||||
|
{
|
||||||
|
m_accumulatedOutput.clear();
|
||||||
|
m_variables.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHCSmtLib2Interface::registerRelation(smt::Expression const& _expr)
|
||||||
|
{
|
||||||
|
solAssert(_expr.sort, "");
|
||||||
|
solAssert(_expr.sort->kind == smt::Kind::Function, "");
|
||||||
|
if (!m_variables.count(_expr.name))
|
||||||
|
{
|
||||||
|
auto fSort = dynamic_pointer_cast<FunctionSort>(_expr.sort);
|
||||||
|
string domain = m_smtlib2->toSmtLibSort(fSort->domain);
|
||||||
|
// Relations are predicates which have implicit codomain Bool.
|
||||||
|
m_variables.insert(_expr.name);
|
||||||
|
write(
|
||||||
|
"(declare-rel |" +
|
||||||
|
_expr.name +
|
||||||
|
"| " +
|
||||||
|
domain +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHCSmtLib2Interface::addRule(smt::Expression const& _expr, std::string const& _name)
|
||||||
|
{
|
||||||
|
write(
|
||||||
|
"(rule (! " +
|
||||||
|
m_smtlib2->toSExpr(_expr) +
|
||||||
|
" :named " +
|
||||||
|
_name +
|
||||||
|
"))"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pair<CheckResult, vector<string>> CHCSmtLib2Interface::query(smt::Expression const& _block)
|
||||||
|
{
|
||||||
|
string accumulated{};
|
||||||
|
swap(m_accumulatedOutput, accumulated);
|
||||||
|
for (auto const& var: m_smtlib2->variables())
|
||||||
|
declareVariable(var.first, var.second);
|
||||||
|
m_accumulatedOutput += accumulated;
|
||||||
|
|
||||||
|
string response = querySolver(
|
||||||
|
m_accumulatedOutput +
|
||||||
|
"\n(query " + _block.name + " :print-certificate true)"
|
||||||
|
);
|
||||||
|
|
||||||
|
CheckResult result;
|
||||||
|
// TODO proper parsing
|
||||||
|
if (boost::starts_with(response, "sat\n"))
|
||||||
|
result = CheckResult::SATISFIABLE;
|
||||||
|
else if (boost::starts_with(response, "unsat\n"))
|
||||||
|
result = CheckResult::UNSATISFIABLE;
|
||||||
|
else if (boost::starts_with(response, "unknown\n"))
|
||||||
|
result = CheckResult::UNKNOWN;
|
||||||
|
else
|
||||||
|
result = CheckResult::ERROR;
|
||||||
|
|
||||||
|
// TODO collect invariants or counterexamples.
|
||||||
|
return make_pair(result, vector<string>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||||
|
{
|
||||||
|
solAssert(_sort, "");
|
||||||
|
if (_sort->kind == Kind::Function)
|
||||||
|
declareFunction(_name, _sort);
|
||||||
|
else if (!m_variables.count(_name))
|
||||||
|
{
|
||||||
|
m_variables.insert(_name);
|
||||||
|
write("(declare-var |" + _name + "| " + m_smtlib2->toSmtLibSort(*_sort) + ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
|
||||||
|
{
|
||||||
|
solAssert(_sort, "");
|
||||||
|
solAssert(_sort->kind == smt::Kind::Function, "");
|
||||||
|
// TODO Use domain and codomain as key as well
|
||||||
|
if (!m_variables.count(_name))
|
||||||
|
{
|
||||||
|
auto fSort = dynamic_pointer_cast<FunctionSort>(_sort);
|
||||||
|
solAssert(fSort->codomain, "");
|
||||||
|
string domain = m_smtlib2->toSmtLibSort(fSort->domain);
|
||||||
|
string codomain = m_smtlib2->toSmtLibSort(*fSort->codomain);
|
||||||
|
m_variables.insert(_name);
|
||||||
|
write(
|
||||||
|
"(declare-fun |" +
|
||||||
|
_name +
|
||||||
|
"| " +
|
||||||
|
domain +
|
||||||
|
" " +
|
||||||
|
codomain +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHCSmtLib2Interface::write(string _data)
|
||||||
|
{
|
||||||
|
m_accumulatedOutput += move(_data) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
string CHCSmtLib2Interface::querySolver(string const& _input)
|
||||||
|
{
|
||||||
|
h256 inputHash = dev::keccak256(_input);
|
||||||
|
if (m_queryResponses.count(inputHash))
|
||||||
|
return m_queryResponses.at(inputHash);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_unhandledQueries.push_back(_input);
|
||||||
|
return "unknown\n";
|
||||||
|
}
|
||||||
|
}
|
75
libsolidity/formal/CHCSmtLib2Interface.h
Normal file
75
libsolidity/formal/CHCSmtLib2Interface.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for solving Horn systems via smtlib2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/formal/CHCSolverInterface.h>
|
||||||
|
|
||||||
|
#include <libsolidity/formal/SMTLib2Interface.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
namespace smt
|
||||||
|
{
|
||||||
|
|
||||||
|
class CHCSmtLib2Interface: public CHCSolverInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CHCSmtLib2Interface(std::map<h256, std::string> const& _queryResponses);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void registerRelation(Expression const& _expr) override;
|
||||||
|
|
||||||
|
void addRule(Expression const& _expr, std::string const& _name) override;
|
||||||
|
|
||||||
|
std::pair<CheckResult, std::vector<std::string>> query(Expression const& _expr) override;
|
||||||
|
|
||||||
|
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||||
|
|
||||||
|
std::vector<std::string> unhandledQueries() const { return m_unhandledQueries; }
|
||||||
|
|
||||||
|
std::shared_ptr<SMTLib2Interface> smtlib2Interface() { return m_smtlib2; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void declareFunction(std::string const& _name, SortPointer const& _sort);
|
||||||
|
|
||||||
|
void write(std::string _data);
|
||||||
|
|
||||||
|
/// Communicates with the solver via the callback. Throws SMTSolverError on error.
|
||||||
|
std::string querySolver(std::string const& _input);
|
||||||
|
|
||||||
|
/// Used to access toSmtLibSort, SExpr, and handle variables.
|
||||||
|
/// Needs to be a shared_ptr since it's also passed to EncodingContext.
|
||||||
|
std::shared_ptr<SMTLib2Interface> m_smtlib2;
|
||||||
|
|
||||||
|
std::string m_accumulatedOutput;
|
||||||
|
std::set<std::string> m_variables;
|
||||||
|
|
||||||
|
std::map<h256, std::string> const& m_queryResponses;
|
||||||
|
std::vector<std::string> m_unhandledQueries;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ class CHCSolverInterface
|
|||||||
public:
|
public:
|
||||||
virtual ~CHCSolverInterface() = default;
|
virtual ~CHCSolverInterface() = default;
|
||||||
|
|
||||||
virtual void declareVariable(std::string const& _name, Sort const& _sort) = 0;
|
virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0;
|
||||||
|
|
||||||
/// Takes a function declaration as a relation.
|
/// Takes a function declaration as a relation.
|
||||||
virtual void registerRelation(Expression const& _expr) = 0;
|
virtual void registerRelation(Expression const& _expr) = 0;
|
||||||
|
@ -48,10 +48,10 @@ void CVC4Interface::pop()
|
|||||||
m_solver.pop();
|
m_solver.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVC4Interface::declareVariable(string const& _name, Sort const& _sort)
|
void CVC4Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||||
{
|
{
|
||||||
if (!m_variables.count(_name))
|
solAssert(_sort, "");
|
||||||
m_variables.insert({_name, m_context.mkVar(_name.c_str(), cvc4Sort(_sort))});
|
m_variables[_name] = m_context.mkVar(_name.c_str(), cvc4Sort(*_sort));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVC4Interface::addAssertion(Expression const& _expr)
|
void CVC4Interface::addAssertion(Expression const& _expr)
|
||||||
|
@ -50,7 +50,7 @@ public:
|
|||||||
void push() override;
|
void push() override;
|
||||||
void pop() override;
|
void pop() override;
|
||||||
|
|
||||||
void declareVariable(std::string const&, Sort const&) override;
|
void declareVariable(std::string const&, SortPointer const&) override;
|
||||||
|
|
||||||
void addAssertion(Expression const& _expr) override;
|
void addAssertion(Expression const& _expr) override;
|
||||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
||||||
|
@ -24,7 +24,7 @@ using namespace dev::solidity;
|
|||||||
|
|
||||||
ModelChecker::ModelChecker(ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses):
|
ModelChecker::ModelChecker(ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses):
|
||||||
m_bmc(m_context, _errorReporter, _smtlib2Responses),
|
m_bmc(m_context, _errorReporter, _smtlib2Responses),
|
||||||
m_chc(m_context, _errorReporter),
|
m_chc(m_context, _errorReporter, _smtlib2Responses),
|
||||||
m_context()
|
m_context()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -40,5 +40,5 @@ void ModelChecker::analyze(SourceUnit const& _source)
|
|||||||
|
|
||||||
vector<string> ModelChecker::unhandledQueries()
|
vector<string> ModelChecker::unhandledQueries()
|
||||||
{
|
{
|
||||||
return m_bmc.unhandledQueries();
|
return m_bmc.unhandledQueries() + m_chc.unhandledQueries();
|
||||||
}
|
}
|
||||||
|
@ -36,12 +36,37 @@ SMTEncoder::SMTEncoder(smt::EncodingContext& _context):
|
|||||||
|
|
||||||
bool SMTEncoder::visit(ContractDefinition const& _contract)
|
bool SMTEncoder::visit(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
solAssert(m_currentContract == nullptr, "");
|
solAssert(m_currentContract, "");
|
||||||
m_currentContract = &_contract;
|
|
||||||
|
|
||||||
initializeStateVariables(_contract);
|
for (auto const& node: _contract.subNodes())
|
||||||
|
if (!dynamic_pointer_cast<FunctionDefinition>(node))
|
||||||
|
node->accept(*this);
|
||||||
|
|
||||||
return true;
|
vector<FunctionDefinition const*> resolvedFunctions = _contract.definedFunctions();
|
||||||
|
for (auto const& base: _contract.annotation().linearizedBaseContracts)
|
||||||
|
for (auto const& baseFunction: base->definedFunctions())
|
||||||
|
{
|
||||||
|
if (baseFunction->isConstructor())
|
||||||
|
continue;
|
||||||
|
bool overridden = false;
|
||||||
|
for (auto const& function: resolvedFunctions)
|
||||||
|
if (
|
||||||
|
function->name() == baseFunction->name() &&
|
||||||
|
FunctionType(*function).asCallableFunction(false)->
|
||||||
|
hasEqualParameterTypes(*FunctionType(*baseFunction).asCallableFunction(false))
|
||||||
|
)
|
||||||
|
{
|
||||||
|
overridden = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!overridden)
|
||||||
|
resolvedFunctions.push_back(baseFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& function: resolvedFunctions)
|
||||||
|
function->accept(*this);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTEncoder::endVisit(ContractDefinition const& _contract)
|
void SMTEncoder::endVisit(ContractDefinition const& _contract)
|
||||||
@ -110,9 +135,13 @@ void SMTEncoder::visitFunctionOrModifier()
|
|||||||
*modifierInvocation->name()->annotation().referencedDeclaration
|
*modifierInvocation->name()->annotation().referencedDeclaration
|
||||||
);
|
);
|
||||||
vector<smt::Expression> modifierArgsExpr;
|
vector<smt::Expression> modifierArgsExpr;
|
||||||
if (modifierInvocation->arguments())
|
if (auto const* arguments = modifierInvocation->arguments())
|
||||||
for (auto arg: *modifierInvocation->arguments())
|
{
|
||||||
modifierArgsExpr.push_back(expr(*arg));
|
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);
|
initializeFunctionCallParameters(modifierDef, modifierArgsExpr);
|
||||||
pushCallStack({&modifierDef, modifierInvocation.get()});
|
pushCallStack({&modifierDef, modifierInvocation.get()});
|
||||||
modifierDef.body().accept(*this);
|
modifierDef.body().accept(*this);
|
||||||
@ -512,6 +541,14 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMTEncoder::initContract(ContractDefinition const& _contract)
|
||||||
|
{
|
||||||
|
solAssert(m_currentContract == nullptr, "");
|
||||||
|
m_currentContract = &_contract;
|
||||||
|
|
||||||
|
initializeStateVariables(_contract);
|
||||||
|
}
|
||||||
|
|
||||||
void SMTEncoder::initFunction(FunctionDefinition const& _function)
|
void SMTEncoder::initFunction(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
solAssert(m_callStack.empty(), "");
|
solAssert(m_callStack.empty(), "");
|
||||||
@ -561,10 +598,10 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
|
|||||||
{
|
{
|
||||||
// Will be translated as part of the node that requested the lvalue.
|
// Will be translated as part of the node that requested the lvalue.
|
||||||
}
|
}
|
||||||
else if (_identifier.annotation().type->category() == Type::Category::Function)
|
|
||||||
visitFunctionIdentifier(_identifier);
|
|
||||||
else if (auto decl = identifierToVariable(_identifier))
|
else if (auto decl = identifierToVariable(_identifier))
|
||||||
defineExpr(_identifier, currentValue(*decl));
|
defineExpr(_identifier, currentValue(*decl));
|
||||||
|
else if (_identifier.annotation().type->category() == Type::Category::Function)
|
||||||
|
visitFunctionIdentifier(_identifier);
|
||||||
else if (_identifier.name() == "now")
|
else if (_identifier.name() == "now")
|
||||||
defineGlobalVariable(_identifier.name(), _identifier);
|
defineGlobalVariable(_identifier.name(), _identifier);
|
||||||
else if (_identifier.name() == "this")
|
else if (_identifier.name() == "this")
|
||||||
@ -665,7 +702,7 @@ void SMTEncoder::endVisit(Return const& _return)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (returnParams.size() == 1)
|
else if (returnParams.size() == 1)
|
||||||
m_context.addAssertion(expr(*_return.expression()) == m_context.newValue(*returnParams.front()));
|
m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,6 +770,15 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
|||||||
auto varDecl = identifierToVariable(*id);
|
auto varDecl = identifierToVariable(*id);
|
||||||
solAssert(varDecl, "");
|
solAssert(varDecl, "");
|
||||||
array = m_context.variable(*varDecl);
|
array = m_context.variable(*varDecl);
|
||||||
|
|
||||||
|
if (varDecl->type()->category() == Type::Category::FixedBytes)
|
||||||
|
{
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_indexAccess.location(),
|
||||||
|
"Assertion checker does not yet support index accessing fixed bytes."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (auto const& innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression()))
|
else if (auto const& innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression()))
|
||||||
{
|
{
|
||||||
@ -774,8 +820,12 @@ void SMTEncoder::arrayAssignment()
|
|||||||
|
|
||||||
void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression const& _rightHandSide)
|
void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression const& _rightHandSide)
|
||||||
{
|
{
|
||||||
auto const& indexAccess = dynamic_cast<IndexAccess const&>(_expr);
|
auto toStore = _rightHandSide;
|
||||||
if (auto const& id = dynamic_cast<Identifier const*>(&indexAccess.baseExpression()))
|
auto indexAccess = dynamic_cast<IndexAccess const*>(&_expr);
|
||||||
|
solAssert(indexAccess, "");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (auto const& id = dynamic_cast<Identifier const*>(&indexAccess->baseExpression()))
|
||||||
{
|
{
|
||||||
auto varDecl = identifierToVariable(*id);
|
auto varDecl = identifierToVariable(*id);
|
||||||
solAssert(varDecl, "");
|
solAssert(varDecl, "");
|
||||||
@ -816,36 +866,33 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c
|
|||||||
|
|
||||||
smt::Expression store = smt::Expression::store(
|
smt::Expression store = smt::Expression::store(
|
||||||
m_context.variable(*varDecl)->currentValue(),
|
m_context.variable(*varDecl)->currentValue(),
|
||||||
expr(*indexAccess.indexExpression()),
|
expr(*indexAccess->indexExpression()),
|
||||||
_rightHandSide
|
toStore
|
||||||
);
|
);
|
||||||
m_context.addAssertion(m_context.newValue(*varDecl) == store);
|
m_context.addAssertion(m_context.newValue(*varDecl) == store);
|
||||||
// Update the SMT select value after the assignment,
|
// Update the SMT select value after the assignment,
|
||||||
// necessary for sound models.
|
// necessary for sound models.
|
||||||
defineExpr(indexAccess, smt::Expression::select(
|
defineExpr(*indexAccess, smt::Expression::select(
|
||||||
m_context.variable(*varDecl)->currentValue(),
|
m_context.variable(*varDecl)->currentValue(),
|
||||||
expr(*indexAccess.indexExpression())
|
expr(*indexAccess->indexExpression())
|
||||||
));
|
));
|
||||||
}
|
|
||||||
else if (dynamic_cast<IndexAccess const*>(&indexAccess.baseExpression()))
|
|
||||||
{
|
|
||||||
auto identifier = dynamic_cast<Identifier const*>(leftmostBase(indexAccess));
|
|
||||||
if (identifier)
|
|
||||||
{
|
|
||||||
auto varDecl = identifierToVariable(*identifier);
|
|
||||||
m_context.newValue(*varDecl);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_errorReporter.warning(
|
break;
|
||||||
indexAccess.location(),
|
}
|
||||||
"Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays."
|
else if (auto base = dynamic_cast<IndexAccess const*>(&indexAccess->baseExpression()))
|
||||||
);
|
{
|
||||||
|
toStore = smt::Expression::store(expr(*base), expr(*indexAccess->indexExpression()), toStore);
|
||||||
|
indexAccess = base;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
_expr.location(),
|
_expr.location(),
|
||||||
"Assertion checker does not yet implement this expression."
|
"Assertion checker does not yet implement this expression."
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _expr, bool _increaseIndex)
|
void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _expr, bool _increaseIndex)
|
||||||
@ -1300,7 +1347,7 @@ void SMTEncoder::createExpr(Expression const& _e)
|
|||||||
void SMTEncoder::defineExpr(Expression const& _e, smt::Expression _value)
|
void SMTEncoder::defineExpr(Expression const& _e, smt::Expression _value)
|
||||||
{
|
{
|
||||||
createExpr(_e);
|
createExpr(_e);
|
||||||
solAssert(smt::smtKind(_e.annotation().type->category()) != smt::Kind::Function, "Equality operator applied to type that is not fully supported");
|
solAssert(_value.sort->kind != smt::Kind::Function, "Equality operator applied to type that is not fully supported");
|
||||||
m_context.addAssertion(expr(_e) == _value);
|
m_context.addAssertion(expr(_e) == _value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +107,7 @@ protected:
|
|||||||
void compareOperation(BinaryOperation const& _op);
|
void compareOperation(BinaryOperation const& _op);
|
||||||
void booleanOperation(BinaryOperation const& _op);
|
void booleanOperation(BinaryOperation const& _op);
|
||||||
|
|
||||||
|
void initContract(ContractDefinition const& _contract);
|
||||||
void initFunction(FunctionDefinition const& _function);
|
void initFunction(FunctionDefinition const& _function);
|
||||||
void visitAssert(FunctionCall const& _funCall);
|
void visitAssert(FunctionCall const& _funCall);
|
||||||
void visitRequire(FunctionCall const& _funCall);
|
void visitRequire(FunctionCall const& _funCall);
|
||||||
|
@ -60,27 +60,29 @@ void SMTLib2Interface::pop()
|
|||||||
m_accumulatedOutput.pop_back();
|
m_accumulatedOutput.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTLib2Interface::declareVariable(string const& _name, Sort const& _sort)
|
void SMTLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||||
{
|
{
|
||||||
if (_sort.kind == Kind::Function)
|
solAssert(_sort, "");
|
||||||
|
if (_sort->kind == Kind::Function)
|
||||||
declareFunction(_name, _sort);
|
declareFunction(_name, _sort);
|
||||||
else if (!m_variables.count(_name))
|
else if (!m_variables.count(_name))
|
||||||
{
|
{
|
||||||
m_variables.insert(_name);
|
m_variables.emplace(_name, _sort);
|
||||||
write("(declare-fun |" + _name + "| () " + toSmtLibSort(_sort) + ')');
|
write("(declare-fun |" + _name + "| () " + toSmtLibSort(*_sort) + ')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTLib2Interface::declareFunction(string const& _name, Sort const& _sort)
|
void SMTLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
|
||||||
{
|
{
|
||||||
solAssert(_sort.kind == smt::Kind::Function, "");
|
solAssert(_sort, "");
|
||||||
|
solAssert(_sort->kind == smt::Kind::Function, "");
|
||||||
// TODO Use domain and codomain as key as well
|
// TODO Use domain and codomain as key as well
|
||||||
if (!m_variables.count(_name))
|
if (!m_variables.count(_name))
|
||||||
{
|
{
|
||||||
FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort);
|
auto const& fSort = dynamic_pointer_cast<FunctionSort>(_sort);
|
||||||
string domain = toSmtLibSort(fSort.domain);
|
string domain = toSmtLibSort(fSort->domain);
|
||||||
string codomain = toSmtLibSort(*fSort.codomain);
|
string codomain = toSmtLibSort(*fSort->codomain);
|
||||||
m_variables.insert(_name);
|
m_variables.emplace(_name, _sort);
|
||||||
write(
|
write(
|
||||||
"(declare-fun |" +
|
"(declare-fun |" +
|
||||||
_name +
|
_name +
|
||||||
@ -159,6 +161,7 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
|
|||||||
case Kind::Array:
|
case Kind::Array:
|
||||||
{
|
{
|
||||||
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
|
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
|
||||||
|
solAssert(arraySort.domain && arraySort.range, "");
|
||||||
return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')';
|
return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')';
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -48,20 +48,23 @@ public:
|
|||||||
void push() override;
|
void push() override;
|
||||||
void pop() override;
|
void pop() override;
|
||||||
|
|
||||||
void declareVariable(std::string const&, Sort const&) override;
|
void declareVariable(std::string const&, SortPointer const&) override;
|
||||||
|
|
||||||
void addAssertion(smt::Expression const& _expr) override;
|
void addAssertion(smt::Expression const& _expr) override;
|
||||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<smt::Expression> const& _expressionsToEvaluate) override;
|
std::pair<CheckResult, std::vector<std::string>> check(std::vector<smt::Expression> const& _expressionsToEvaluate) override;
|
||||||
|
|
||||||
std::vector<std::string> unhandledQueries() override { return m_unhandledQueries; }
|
std::vector<std::string> unhandledQueries() override { return m_unhandledQueries; }
|
||||||
|
|
||||||
private:
|
// Used by CHCSmtLib2Interface
|
||||||
void declareFunction(std::string const&, Sort const&);
|
|
||||||
|
|
||||||
std::string toSExpr(smt::Expression const& _expr);
|
std::string toSExpr(smt::Expression const& _expr);
|
||||||
std::string toSmtLibSort(Sort const& _sort);
|
std::string toSmtLibSort(Sort const& _sort);
|
||||||
std::string toSmtLibSort(std::vector<SortPointer> const& _sort);
|
std::string toSmtLibSort(std::vector<SortPointer> const& _sort);
|
||||||
|
|
||||||
|
std::map<std::string, SortPointer> variables() { return m_variables; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void declareFunction(std::string const& _name, SortPointer const& _sort);
|
||||||
|
|
||||||
void write(std::string _data);
|
void write(std::string _data);
|
||||||
|
|
||||||
std::string checkSatAndGetValuesCommand(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
std::string checkSatAndGetValuesCommand(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
||||||
@ -71,7 +74,7 @@ private:
|
|||||||
std::string querySolver(std::string const& _input);
|
std::string querySolver(std::string const& _input);
|
||||||
|
|
||||||
std::vector<std::string> m_accumulatedOutput;
|
std::vector<std::string> m_accumulatedOutput;
|
||||||
std::set<std::string> m_variables;
|
std::map<std::string, SortPointer> m_variables;
|
||||||
|
|
||||||
std::map<h256, std::string> const& m_queryResponses;
|
std::map<h256, std::string> const& m_queryResponses;
|
||||||
std::vector<std::string> m_unhandledQueries;
|
std::vector<std::string> m_unhandledQueries;
|
||||||
|
@ -59,8 +59,9 @@ void SMTPortfolio::pop()
|
|||||||
s->pop();
|
s->pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTPortfolio::declareVariable(string const& _name, Sort const& _sort)
|
void SMTPortfolio::declareVariable(string const& _name, SortPointer const& _sort)
|
||||||
{
|
{
|
||||||
|
solAssert(_sort, "");
|
||||||
for (auto const& s: m_solvers)
|
for (auto const& s: m_solvers)
|
||||||
s->declareVariable(_name, _sort);
|
s->declareVariable(_name, _sort);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ public:
|
|||||||
void push() override;
|
void push() override;
|
||||||
void pop() override;
|
void pop() override;
|
||||||
|
|
||||||
void declareVariable(std::string const&, Sort const&) override;
|
void declareVariable(std::string const&, SortPointer const&) override;
|
||||||
|
|
||||||
void addAssertion(smt::Expression const& _expr) override;
|
void addAssertion(smt::Expression const& _expr) override;
|
||||||
|
|
||||||
|
@ -338,13 +338,13 @@ public:
|
|||||||
virtual void push() = 0;
|
virtual void push() = 0;
|
||||||
virtual void pop() = 0;
|
virtual void pop() = 0;
|
||||||
|
|
||||||
virtual void declareVariable(std::string const& _name, Sort const& _sort) = 0;
|
virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0;
|
||||||
Expression newVariable(std::string _name, SortPointer _sort)
|
Expression newVariable(std::string _name, SortPointer const& _sort)
|
||||||
{
|
{
|
||||||
// Subclasses should do something here
|
// Subclasses should do something here
|
||||||
solAssert(_sort, "");
|
solAssert(_sort, "");
|
||||||
declareVariable(_name, *_sort);
|
declareVariable(_name, _sort);
|
||||||
return Expression(std::move(_name), {}, std::move(_sort));
|
return Expression(std::move(_name), {}, _sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void addAssertion(Expression const& _expr) = 0;
|
virtual void addAssertion(Expression const& _expr) = 0;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/ast/TypeProvider.h>
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
|
#include <libdevcore/CommonData.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -139,7 +140,28 @@ pair<bool, shared_ptr<SymbolicVariable>> newSymbolicVariable(
|
|||||||
else if (isBool(_type.category()))
|
else if (isBool(_type.category()))
|
||||||
var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _context);
|
var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _context);
|
||||||
else if (isFunction(_type.category()))
|
else if (isFunction(_type.category()))
|
||||||
|
{
|
||||||
|
auto const& fType = dynamic_cast<FunctionType const*>(type);
|
||||||
|
auto const& paramsIn = fType->parameterTypes();
|
||||||
|
auto const& paramsOut = fType->returnParameterTypes();
|
||||||
|
auto findFunctionParam = [&](auto&& params) {
|
||||||
|
return find_if(
|
||||||
|
begin(params),
|
||||||
|
end(params),
|
||||||
|
[&](TypePointer _paramType) { return _paramType->category() == solidity::Type::Category::Function; }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if (
|
||||||
|
findFunctionParam(paramsIn) != end(paramsIn) ||
|
||||||
|
findFunctionParam(paramsOut) != end(paramsOut)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
abstract = true;
|
||||||
|
var = make_shared<SymbolicIntVariable>(TypeProvider::uint256(), type, _uniqueName, _context);
|
||||||
|
}
|
||||||
|
else
|
||||||
var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _context);
|
var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _context);
|
||||||
|
}
|
||||||
else if (isInteger(_type.category()))
|
else if (isInteger(_type.category()))
|
||||||
var = make_shared<SymbolicIntVariable>(type, type, _uniqueName, _context);
|
var = make_shared<SymbolicIntVariable>(type, type, _uniqueName, _context);
|
||||||
else if (isFixedBytes(_type.category()))
|
else if (isFixedBytes(_type.category()))
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include <libsolidity/formal/SymbolicTypes.h>
|
#include <libsolidity/formal/SymbolicTypes.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/ast/TypeProvider.h>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
@ -153,16 +152,38 @@ SymbolicFunctionVariable::SymbolicFunctionVariable(
|
|||||||
solAssert(m_sort->kind == Kind::Function, "");
|
solAssert(m_sort->kind == Kind::Function, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolicFunctionVariable::resetDeclaration()
|
Expression SymbolicFunctionVariable::currentValue(solidity::TypePointer const& _targetType) const
|
||||||
{
|
{
|
||||||
m_declaration = m_context.newVariable(currentName(), m_sort);
|
return m_abstract.currentValue(_targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression SymbolicFunctionVariable::currentFunctionValue() const
|
||||||
|
{
|
||||||
|
return m_declaration;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression SymbolicFunctionVariable::valueAtIndex(int _index) const
|
||||||
|
{
|
||||||
|
return m_abstract.valueAtIndex(_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression SymbolicFunctionVariable::functionValueAtIndex(int _index) const
|
||||||
|
{
|
||||||
|
return SymbolicVariable::valueAtIndex(_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression SymbolicFunctionVariable::resetIndex()
|
||||||
|
{
|
||||||
|
SymbolicVariable::resetIndex();
|
||||||
|
return m_abstract.resetIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression SymbolicFunctionVariable::increaseIndex()
|
Expression SymbolicFunctionVariable::increaseIndex()
|
||||||
{
|
{
|
||||||
++(*m_ssa);
|
++(*m_ssa);
|
||||||
resetDeclaration();
|
resetDeclaration();
|
||||||
return currentValue();
|
m_abstract.increaseIndex();
|
||||||
|
return m_abstract.currentValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) const
|
Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) const
|
||||||
@ -170,6 +191,11 @@ Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) c
|
|||||||
return m_declaration(_arguments);
|
return m_declaration(_arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SymbolicFunctionVariable::resetDeclaration()
|
||||||
|
{
|
||||||
|
m_declaration = m_context.newVariable(currentName(), m_sort);
|
||||||
|
}
|
||||||
|
|
||||||
SymbolicMappingVariable::SymbolicMappingVariable(
|
SymbolicMappingVariable::SymbolicMappingVariable(
|
||||||
solidity::TypePointer _type,
|
solidity::TypePointer _type,
|
||||||
string _uniqueName,
|
string _uniqueName,
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <libsolidity/formal/SolverInterface.h>
|
#include <libsolidity/formal/SolverInterface.h>
|
||||||
#include <libsolidity/formal/SSAVariable.h>
|
#include <libsolidity/formal/SSAVariable.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -138,7 +139,12 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialization of SymbolicVariable for FunctionType
|
* Specialization of SymbolicVariable for FunctionType.
|
||||||
|
* Besides containing a symbolic function declaration,
|
||||||
|
* it also has an integer used as abstraction.
|
||||||
|
* By default, the abstract representation is used when
|
||||||
|
* values are requested, and the function declaration is
|
||||||
|
* used when operator() is applied over arguments.
|
||||||
*/
|
*/
|
||||||
class SymbolicFunctionVariable: public SymbolicVariable
|
class SymbolicFunctionVariable: public SymbolicVariable
|
||||||
{
|
{
|
||||||
@ -154,8 +160,20 @@ public:
|
|||||||
EncodingContext& _context
|
EncodingContext& _context
|
||||||
);
|
);
|
||||||
|
|
||||||
Expression increaseIndex();
|
Expression currentValue(solidity::TypePointer const& _targetType = TypePointer{}) const override;
|
||||||
Expression operator()(std::vector<Expression> _arguments) const;
|
|
||||||
|
// Explicit request the function declaration.
|
||||||
|
Expression currentFunctionValue() const;
|
||||||
|
|
||||||
|
Expression valueAtIndex(int _index) const override;
|
||||||
|
|
||||||
|
// Explicit request the function declaration.
|
||||||
|
Expression functionValueAtIndex(int _index) const;
|
||||||
|
|
||||||
|
Expression resetIndex() override;
|
||||||
|
Expression increaseIndex() override;
|
||||||
|
|
||||||
|
Expression operator()(std::vector<Expression> _arguments) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Creates a new function declaration.
|
/// Creates a new function declaration.
|
||||||
@ -163,6 +181,14 @@ private:
|
|||||||
|
|
||||||
/// Stores the current function declaration.
|
/// Stores the current function declaration.
|
||||||
Expression m_declaration;
|
Expression m_declaration;
|
||||||
|
|
||||||
|
/// Abstract representation.
|
||||||
|
SymbolicIntVariable m_abstract{
|
||||||
|
TypeProvider::uint256(),
|
||||||
|
TypeProvider::uint256(),
|
||||||
|
m_uniqueName + "_abstract",
|
||||||
|
m_context
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,8 +47,9 @@ Z3CHCInterface::Z3CHCInterface():
|
|||||||
m_solver.set(p);
|
m_solver.set(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Z3CHCInterface::declareVariable(string const& _name, Sort const& _sort)
|
void Z3CHCInterface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||||
{
|
{
|
||||||
|
solAssert(_sort, "");
|
||||||
m_z3Interface->declareVariable(_name, _sort);
|
m_z3Interface->declareVariable(_name, _sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ public:
|
|||||||
Z3CHCInterface();
|
Z3CHCInterface();
|
||||||
|
|
||||||
/// Forwards variable declaration to Z3Interface.
|
/// Forwards variable declaration to Z3Interface.
|
||||||
void declareVariable(std::string const& _name, Sort const& _sort) override;
|
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||||
|
|
||||||
void registerRelation(Expression const& _expr) override;
|
void registerRelation(Expression const& _expr) override;
|
||||||
|
|
||||||
|
@ -50,22 +50,25 @@ void Z3Interface::pop()
|
|||||||
m_solver.pop();
|
m_solver.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Z3Interface::declareVariable(string const& _name, Sort const& _sort)
|
void Z3Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||||
{
|
{
|
||||||
if (_sort.kind == Kind::Function)
|
solAssert(_sort, "");
|
||||||
declareFunction(_name, _sort);
|
if (_sort->kind == Kind::Function)
|
||||||
else if (!m_constants.count(_name))
|
declareFunction(_name, *_sort);
|
||||||
m_constants.insert({_name, m_context.constant(_name.c_str(), z3Sort(_sort))});
|
else if (m_constants.count(_name))
|
||||||
|
m_constants.at(_name) = m_context.constant(_name.c_str(), z3Sort(*_sort));
|
||||||
|
else
|
||||||
|
m_constants.emplace(_name, m_context.constant(_name.c_str(), z3Sort(*_sort)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Z3Interface::declareFunction(string const& _name, Sort const& _sort)
|
void Z3Interface::declareFunction(string const& _name, Sort const& _sort)
|
||||||
{
|
{
|
||||||
solAssert(_sort.kind == smt::Kind::Function, "");
|
solAssert(_sort.kind == smt::Kind::Function, "");
|
||||||
if (!m_functions.count(_name))
|
|
||||||
{
|
|
||||||
FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort);
|
FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort);
|
||||||
m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain))});
|
if (m_functions.count(_name))
|
||||||
}
|
m_functions.at(_name) = m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain));
|
||||||
|
else
|
||||||
|
m_functions.emplace(_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Z3Interface::addAssertion(Expression const& _expr)
|
void Z3Interface::addAssertion(Expression const& _expr)
|
||||||
|
@ -38,7 +38,7 @@ public:
|
|||||||
void push() override;
|
void push() override;
|
||||||
void pop() override;
|
void pop() override;
|
||||||
|
|
||||||
void declareVariable(std::string const& _name, Sort const& _sort) override;
|
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||||
|
|
||||||
void addAssertion(Expression const& _expr) override;
|
void addAssertion(Expression const& _expr) override;
|
||||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include <libsolidity/interface/ABI.h>
|
#include <libsolidity/interface/ABI.h>
|
||||||
#include <libsolidity/interface/Natspec.h>
|
#include <libsolidity/interface/Natspec.h>
|
||||||
#include <libsolidity/interface/GasEstimator.h>
|
#include <libsolidity/interface/GasEstimator.h>
|
||||||
|
#include <libsolidity/interface/StorageLayout.h>
|
||||||
#include <libsolidity/interface/Version.h>
|
#include <libsolidity/interface/Version.h>
|
||||||
#include <libsolidity/parsing/Parser.h>
|
#include <libsolidity/parsing/Parser.h>
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ CompilerStack::~CompilerStack()
|
|||||||
TypeProvider::reset();
|
TypeProvider::reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping)
|
std::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping)
|
||||||
{
|
{
|
||||||
auto eq = find(_remapping.begin(), _remapping.end(), '=');
|
auto eq = find(_remapping.begin(), _remapping.end(), '=');
|
||||||
if (eq == _remapping.end())
|
if (eq == _remapping.end())
|
||||||
@ -269,7 +270,7 @@ bool CompilerStack::analyze()
|
|||||||
noErrors = false;
|
noErrors = false;
|
||||||
|
|
||||||
m_globalContext = make_shared<GlobalContext>();
|
m_globalContext = make_shared<GlobalContext>();
|
||||||
NameAndTypeResolver resolver(*m_globalContext, m_scopes, m_errorReporter);
|
NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_scopes, m_errorReporter);
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
if (!resolver.registerDeclarations(*source->ast))
|
if (!resolver.registerDeclarations(*source->ast))
|
||||||
return false;
|
return false;
|
||||||
@ -578,6 +579,14 @@ string const& CompilerStack::eWasm(string const& _contractName) const
|
|||||||
return contract(_contractName).eWasm;
|
return contract(_contractName).eWasm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eth::LinkerObject const& CompilerStack::eWasmObject(string const& _contractName) const
|
||||||
|
{
|
||||||
|
if (m_stackState != CompilationSuccessful)
|
||||||
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||||
|
|
||||||
|
return contract(_contractName).eWasmObject;
|
||||||
|
}
|
||||||
|
|
||||||
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
|
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
@ -659,6 +668,28 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
|||||||
return *_contract.abi;
|
return *_contract.abi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Json::Value const& CompilerStack::storageLayout(string const& _contractName) const
|
||||||
|
{
|
||||||
|
if (m_stackState < AnalysisPerformed)
|
||||||
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
|
return storageLayout(contract(_contractName));
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
|
||||||
|
{
|
||||||
|
if (m_stackState < AnalysisPerformed)
|
||||||
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
|
// caches the result
|
||||||
|
if (!_contract.storageLayout)
|
||||||
|
_contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract)));
|
||||||
|
|
||||||
|
return *_contract.storageLayout;
|
||||||
|
}
|
||||||
|
|
||||||
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
@ -1047,7 +1078,9 @@ void CompilerStack::generateEWasm(ContractDefinition const& _contract)
|
|||||||
//cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl;
|
//cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl;
|
||||||
|
|
||||||
// Turn into eWasm text representation.
|
// Turn into eWasm text representation.
|
||||||
compiledContract.eWasm = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm).assembly;
|
auto result = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm);
|
||||||
|
compiledContract.eWasm = std::move(result.assembly);
|
||||||
|
compiledContract.eWasmObject = std::move(*result.bytecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
||||||
|
@ -121,7 +121,7 @@ public:
|
|||||||
void reset(bool _keepSettings = false);
|
void reset(bool _keepSettings = false);
|
||||||
|
|
||||||
// Parses a remapping of the format "context:prefix=target".
|
// Parses a remapping of the format "context:prefix=target".
|
||||||
static boost::optional<Remapping> parseRemapping(std::string const& _remapping);
|
static std::optional<Remapping> parseRemapping(std::string const& _remapping);
|
||||||
|
|
||||||
/// Sets path remappings.
|
/// Sets path remappings.
|
||||||
/// Must be set before parsing.
|
/// Must be set before parsing.
|
||||||
@ -232,9 +232,12 @@ public:
|
|||||||
/// @returns the optimized IR representation of a contract.
|
/// @returns the optimized IR representation of a contract.
|
||||||
std::string const& yulIROptimized(std::string const& _contractName) const;
|
std::string const& yulIROptimized(std::string const& _contractName) const;
|
||||||
|
|
||||||
/// @returns the eWasm (text) representation of a contract.
|
/// @returns the eWasm text representation of a contract.
|
||||||
std::string const& eWasm(std::string const& _contractName) const;
|
std::string const& eWasm(std::string const& _contractName) const;
|
||||||
|
|
||||||
|
/// @returns the eWasm representation of a contract.
|
||||||
|
eth::LinkerObject const& eWasmObject(std::string const& _contractName) const;
|
||||||
|
|
||||||
/// @returns the assembled object for a contract.
|
/// @returns the assembled object for a contract.
|
||||||
eth::LinkerObject const& object(std::string const& _contractName) const;
|
eth::LinkerObject const& object(std::string const& _contractName) const;
|
||||||
|
|
||||||
@ -269,6 +272,10 @@ public:
|
|||||||
/// Prerequisite: Successful call to parse or compile.
|
/// Prerequisite: Successful call to parse or compile.
|
||||||
Json::Value const& contractABI(std::string const& _contractName) const;
|
Json::Value const& contractABI(std::string const& _contractName) const;
|
||||||
|
|
||||||
|
/// @returns a JSON representing the storage layout of the contract.
|
||||||
|
/// Prerequisite: Successful call to parse or compile.
|
||||||
|
Json::Value const& storageLayout(std::string const& _contractName) const;
|
||||||
|
|
||||||
/// @returns a JSON representing the contract's user documentation.
|
/// @returns a JSON representing the contract's user documentation.
|
||||||
/// Prerequisite: Successful call to parse or compile.
|
/// Prerequisite: Successful call to parse or compile.
|
||||||
Json::Value const& natspecUser(std::string const& _contractName) const;
|
Json::Value const& natspecUser(std::string const& _contractName) const;
|
||||||
@ -312,9 +319,11 @@ private:
|
|||||||
eth::LinkerObject runtimeObject; ///< Runtime object.
|
eth::LinkerObject runtimeObject; ///< Runtime object.
|
||||||
std::string yulIR; ///< Experimental Yul IR code.
|
std::string yulIR; ///< Experimental Yul IR code.
|
||||||
std::string yulIROptimized; ///< Optimized experimental Yul IR code.
|
std::string yulIROptimized; ///< Optimized experimental Yul IR code.
|
||||||
std::string eWasm; ///< Experimental eWasm code (text representation).
|
std::string eWasm; ///< Experimental eWasm text representation
|
||||||
|
eth::LinkerObject eWasmObject; ///< Experimental eWasm code
|
||||||
mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
|
mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
|
||||||
mutable std::unique_ptr<Json::Value const> abi;
|
mutable std::unique_ptr<Json::Value const> abi;
|
||||||
|
mutable std::unique_ptr<Json::Value const> storageLayout;
|
||||||
mutable std::unique_ptr<Json::Value const> userDocumentation;
|
mutable std::unique_ptr<Json::Value const> userDocumentation;
|
||||||
mutable std::unique_ptr<Json::Value const> devDocumentation;
|
mutable std::unique_ptr<Json::Value const> devDocumentation;
|
||||||
mutable std::unique_ptr<std::string const> sourceMapping;
|
mutable std::unique_ptr<std::string const> sourceMapping;
|
||||||
@ -346,7 +355,7 @@ private:
|
|||||||
/// The IR is stored but otherwise unused.
|
/// The IR is stored but otherwise unused.
|
||||||
void generateIR(ContractDefinition const& _contract);
|
void generateIR(ContractDefinition const& _contract);
|
||||||
|
|
||||||
/// Generate eWasm text representation for a single contract.
|
/// Generate eWasm representation for a single contract.
|
||||||
void generateEWasm(ContractDefinition const& _contract);
|
void generateEWasm(ContractDefinition const& _contract);
|
||||||
|
|
||||||
/// Links all the known library addresses in the available objects. Any unknown
|
/// Links all the known library addresses in the available objects. Any unknown
|
||||||
@ -378,6 +387,10 @@ private:
|
|||||||
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
|
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
|
||||||
Json::Value const& contractABI(Contract const&) const;
|
Json::Value const& contractABI(Contract const&) const;
|
||||||
|
|
||||||
|
/// @returns the storage layout of the contract as a JSON object.
|
||||||
|
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
|
||||||
|
Json::Value const& storageLayout(Contract const&) const;
|
||||||
|
|
||||||
/// @returns the Natspec User documentation as a JSON object.
|
/// @returns the Natspec User documentation as a JSON object.
|
||||||
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
|
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
|
||||||
Json::Value const& natspecUser(Contract const&) const;
|
Json::Value const& natspecUser(Contract const&) const;
|
||||||
|
@ -31,8 +31,9 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
@ -317,7 +318,7 @@ Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _so
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys, string const& _name)
|
std::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys, string const& _name)
|
||||||
{
|
{
|
||||||
if (!!_input && !_input.isObject())
|
if (!!_input && !_input.isObject())
|
||||||
return formatFatalError("JSONError", "\"" + _name + "\" must be an object");
|
return formatFatalError("JSONError", "\"" + _name + "\" must be an object");
|
||||||
@ -326,46 +327,46 @@ boost::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> co
|
|||||||
if (!_keys.count(member))
|
if (!_keys.count(member))
|
||||||
return formatFatalError("JSONError", "Unknown key \"" + member + "\"");
|
return formatFatalError("JSONError", "Unknown key \"" + member + "\"");
|
||||||
|
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkRootKeys(Json::Value const& _input)
|
std::optional<Json::Value> checkRootKeys(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
static set<string> keys{"auxiliaryInput", "language", "settings", "sources"};
|
static set<string> keys{"auxiliaryInput", "language", "settings", "sources"};
|
||||||
return checkKeys(_input, keys, "root");
|
return checkKeys(_input, keys, "root");
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkSourceKeys(Json::Value const& _input, string const& _name)
|
std::optional<Json::Value> checkSourceKeys(Json::Value const& _input, string const& _name)
|
||||||
{
|
{
|
||||||
static set<string> keys{"content", "keccak256", "urls"};
|
static set<string> keys{"content", "keccak256", "urls"};
|
||||||
return checkKeys(_input, keys, "sources." + _name);
|
return checkKeys(_input, keys, "sources." + _name);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
|
std::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
static set<string> keys{"smtlib2responses"};
|
static set<string> keys{"smtlib2responses"};
|
||||||
return checkKeys(_input, keys, "auxiliaryInput");
|
return checkKeys(_input, keys, "auxiliaryInput");
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
|
std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
static set<string> keys{"parserErrorRecovery", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"};
|
static set<string> keys{"parserErrorRecovery", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"};
|
||||||
return checkKeys(_input, keys, "settings");
|
return checkKeys(_input, keys, "settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
|
std::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
static set<string> keys{"details", "enabled", "runs"};
|
static set<string> keys{"details", "enabled", "runs"};
|
||||||
return checkKeys(_input, keys, "settings.optimizer");
|
return checkKeys(_input, keys, "settings.optimizer");
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkOptimizerDetailsKeys(Json::Value const& _input)
|
std::optional<Json::Value> checkOptimizerDetailsKeys(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
static set<string> keys{"peephole", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails"};
|
static set<string> keys{"peephole", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails"};
|
||||||
return checkKeys(_input, keys, "settings.optimizer.details");
|
return checkKeys(_input, keys, "settings.optimizer.details");
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting)
|
std::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting)
|
||||||
{
|
{
|
||||||
if (_details.isMember(_name))
|
if (_details.isMember(_name))
|
||||||
{
|
{
|
||||||
@ -376,7 +377,7 @@ boost::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, s
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
|
std::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool())
|
if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool())
|
||||||
return formatFatalError("JSONError", "\"settings.metadata.useLiteralContent\" must be Boolean");
|
return formatFatalError("JSONError", "\"settings.metadata.useLiteralContent\" must be Boolean");
|
||||||
@ -384,7 +385,7 @@ boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
|
|||||||
return checkKeys(_input, keys, "settings.metadata");
|
return checkKeys(_input, keys, "settings.metadata");
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelection)
|
std::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelection)
|
||||||
{
|
{
|
||||||
if (!!_outputSelection && !_outputSelection.isObject())
|
if (!!_outputSelection && !_outputSelection.isObject())
|
||||||
return formatFatalError("JSONError", "\"settings.outputSelection\" must be an object");
|
return formatFatalError("JSONError", "\"settings.outputSelection\" must be an object");
|
||||||
@ -426,7 +427,7 @@ boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSele
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
/// Validates the optimizer settings and returns them in a parsed object.
|
/// Validates the optimizer settings and returns them in a parsed object.
|
||||||
/// On error returns the json-formatted error message.
|
/// On error returns the json-formatted error message.
|
||||||
@ -635,7 +636,7 @@ boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompile
|
|||||||
{
|
{
|
||||||
if (!settings["evmVersion"].isString())
|
if (!settings["evmVersion"].isString())
|
||||||
return formatFatalError("JSONError", "evmVersion must be a string.");
|
return formatFatalError("JSONError", "evmVersion must be a string.");
|
||||||
boost::optional<langutil::EVMVersion> version = langutil::EVMVersion::fromString(settings["evmVersion"].asString());
|
std::optional<langutil::EVMVersion> version = langutil::EVMVersion::fromString(settings["evmVersion"].asString());
|
||||||
if (!version)
|
if (!version)
|
||||||
return formatFatalError("JSONError", "Invalid EVM version requested.");
|
return formatFatalError("JSONError", "Invalid EVM version requested.");
|
||||||
ret.evmVersion = *version;
|
ret.evmVersion = *version;
|
||||||
@ -886,10 +887,12 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
|||||||
string file = contractName.substr(0, colon);
|
string file = contractName.substr(0, colon);
|
||||||
string name = contractName.substr(colon + 1);
|
string name = contractName.substr(colon + 1);
|
||||||
|
|
||||||
// ABI, documentation and metadata
|
// ABI, storage layout, documentation and metadata
|
||||||
Json::Value contractData(Json::objectValue);
|
Json::Value contractData(Json::objectValue);
|
||||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental))
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental))
|
||||||
contractData["abi"] = compilerStack.contractABI(contractName);
|
contractData["abi"] = compilerStack.contractABI(contractName);
|
||||||
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false))
|
||||||
|
contractData["storageLayout"] = compilerStack.storageLayout(contractName);
|
||||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental))
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental))
|
||||||
contractData["metadata"] = compilerStack.metadata(contractName);
|
contractData["metadata"] = compilerStack.metadata(contractName);
|
||||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental))
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental))
|
||||||
@ -906,6 +909,8 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
|||||||
// eWasm
|
// eWasm
|
||||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental))
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental))
|
||||||
contractData["ewasm"]["wast"] = compilerStack.eWasm(contractName);
|
contractData["ewasm"]["wast"] = compilerStack.eWasm(contractName);
|
||||||
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wasm", wildcardMatchesExperimental))
|
||||||
|
contractData["ewasm"]["wasm"] = compilerStack.eWasmObject(contractName).toHex();
|
||||||
|
|
||||||
// EVM
|
// EVM
|
||||||
Json::Value evmData(Json::objectValue);
|
Json::Value evmData(Json::objectValue);
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/interface/CompilerStack.h>
|
#include <libsolidity/interface/CompilerStack.h>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <optional>
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
|
119
libsolidity/interface/StorageLayout.cpp
Normal file
119
libsolidity/interface/StorageLayout.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
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/interface/StorageLayout.h>
|
||||||
|
|
||||||
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
Json::Value StorageLayout::generate(ContractDefinition const& _contractDef)
|
||||||
|
{
|
||||||
|
solAssert(!m_contract, "");
|
||||||
|
m_contract = &_contractDef;
|
||||||
|
m_types.clear();
|
||||||
|
|
||||||
|
auto typeType = dynamic_cast<TypeType const*>(_contractDef.type());
|
||||||
|
solAssert(typeType, "");
|
||||||
|
auto contractType = dynamic_cast<ContractType const*>(typeType->actualType());
|
||||||
|
solAssert(contractType, "");
|
||||||
|
|
||||||
|
Json::Value variables(Json::arrayValue);
|
||||||
|
for (auto [var, slot, offset]: contractType->stateVariables())
|
||||||
|
variables.append(generate(*var, slot, offset));
|
||||||
|
|
||||||
|
Json::Value layout;
|
||||||
|
layout["storage"] = move(variables);
|
||||||
|
layout["types"] = move(m_types);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value StorageLayout::generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset)
|
||||||
|
{
|
||||||
|
Json::Value varEntry;
|
||||||
|
TypePointer varType = _var.type();
|
||||||
|
|
||||||
|
varEntry["label"] = _var.name();
|
||||||
|
varEntry["astId"] = int(_var.id());
|
||||||
|
varEntry["contract"] = m_contract->fullyQualifiedName();
|
||||||
|
varEntry["slot"] = _slot.str();
|
||||||
|
varEntry["offset"] = _offset;
|
||||||
|
varEntry["type"] = typeKeyName(varType);
|
||||||
|
|
||||||
|
generate(varType);
|
||||||
|
|
||||||
|
return varEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StorageLayout::generate(TypePointer _type)
|
||||||
|
{
|
||||||
|
if (m_types.isMember(typeKeyName(_type)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Register it now to cut recursive visits.
|
||||||
|
Json::Value& typeInfo = m_types[typeKeyName(_type)];
|
||||||
|
typeInfo["label"] = _type->toString(true);
|
||||||
|
typeInfo["numberOfBytes"] = u256(_type->storageBytes() * _type->storageSize()).str();
|
||||||
|
|
||||||
|
if (auto structType = dynamic_cast<StructType const*>(_type))
|
||||||
|
{
|
||||||
|
Json::Value members(Json::arrayValue);
|
||||||
|
auto const& structDef = structType->structDefinition();
|
||||||
|
for (auto const& member: structDef.members())
|
||||||
|
{
|
||||||
|
auto const& offsets = structType->storageOffsetsOfMember(member->name());
|
||||||
|
members.append(generate(*member, offsets.first, offsets.second));
|
||||||
|
}
|
||||||
|
typeInfo["members"] = move(members);
|
||||||
|
typeInfo["encoding"] = "inplace";
|
||||||
|
}
|
||||||
|
else if (auto mappingType = dynamic_cast<MappingType const*>(_type))
|
||||||
|
{
|
||||||
|
typeInfo["key"] = typeKeyName(mappingType->keyType());
|
||||||
|
typeInfo["value"] = typeKeyName(mappingType->valueType());
|
||||||
|
generate(mappingType->keyType());
|
||||||
|
generate(mappingType->valueType());
|
||||||
|
typeInfo["encoding"] = "mapping";
|
||||||
|
}
|
||||||
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
|
||||||
|
{
|
||||||
|
if (arrayType->isByteArray())
|
||||||
|
typeInfo["encoding"] = "bytes";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
typeInfo["base"] = typeKeyName(arrayType->baseType());
|
||||||
|
generate(arrayType->baseType());
|
||||||
|
typeInfo["encoding"] = arrayType->isDynamicallySized() ? "dynamic_array" : "inplace";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(_type->isValueType(), "");
|
||||||
|
typeInfo["encoding"] = "inplace";
|
||||||
|
}
|
||||||
|
|
||||||
|
solAssert(typeInfo.isMember("encoding"), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
string StorageLayout::typeKeyName(TypePointer _type)
|
||||||
|
{
|
||||||
|
if (auto refType = dynamic_cast<ReferenceType const*>(_type))
|
||||||
|
return TypeProvider::withLocationIfReference(refType->location(), _type)->richIdentifier();
|
||||||
|
return _type->richIdentifier();
|
||||||
|
}
|
58
libsolidity/interface/StorageLayout.h
Normal file
58
libsolidity/interface/StorageLayout.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Generates the storage layout of a contract.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <libsolidity/ast/Types.h>
|
||||||
|
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
class StorageLayout
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Generates the storage layout of the contract
|
||||||
|
/// @param _contractDef The contract definition
|
||||||
|
/// @return A JSON representation of the contract's storage layout.
|
||||||
|
Json::Value generate(ContractDefinition const& _contractDef);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Generates the JSON information for a variable and its storage location.
|
||||||
|
Json::Value generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset);
|
||||||
|
|
||||||
|
/// Generates the JSON information for @param _type
|
||||||
|
void generate(TypePointer _type);
|
||||||
|
|
||||||
|
/// The key for the JSON object describing a type.
|
||||||
|
std::string typeKeyName(TypePointer _type);
|
||||||
|
|
||||||
|
Json::Value m_types;
|
||||||
|
|
||||||
|
/// Current analyzed contract
|
||||||
|
ContractDefinition const* m_contract = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -874,7 +874,9 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
|||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable);
|
auto stateMutability = elemTypeName.token() == Token::Address
|
||||||
|
? optional<StateMutability>{StateMutability::NonPayable}
|
||||||
|
: nullopt;
|
||||||
if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false))
|
if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false))
|
||||||
{
|
{
|
||||||
if (elemTypeName.token() == Token::Address)
|
if (elemTypeName.token() == Token::Address)
|
||||||
|
@ -31,11 +31,11 @@
|
|||||||
#include <libyul/backends/evm/EVMDialect.h>
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
@ -59,7 +59,7 @@ public:
|
|||||||
explicit AsmAnalyzer(
|
explicit AsmAnalyzer(
|
||||||
AsmAnalysisInfo& _analysisInfo,
|
AsmAnalysisInfo& _analysisInfo,
|
||||||
langutil::ErrorReporter& _errorReporter,
|
langutil::ErrorReporter& _errorReporter,
|
||||||
boost::optional<langutil::Error::Type> _errorTypeForLoose,
|
std::optional<langutil::Error::Type> _errorTypeForLoose,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver(),
|
ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver(),
|
||||||
std::set<YulString> const& _dataNames = {}
|
std::set<YulString> const& _dataNames = {}
|
||||||
@ -127,7 +127,7 @@ private:
|
|||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
|
std::optional<langutil::Error::Type> m_errorTypeForLoose;
|
||||||
/// Names of data objects to be referenced by builtin functions with literal arguments.
|
/// Names of data objects to be referenced by builtin functions with literal arguments.
|
||||||
std::set<YulString> m_dataNames;
|
std::set<YulString> m_dataNames;
|
||||||
ForLoop const* m_currentForLoop = nullptr;
|
ForLoop const* m_currentForLoop = nullptr;
|
||||||
|
@ -27,10 +27,10 @@
|
|||||||
#include <libdevcore/Visitor.h>
|
#include <libdevcore/Visitor.h>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace yul
|
namespace yul
|
||||||
{
|
{
|
||||||
|
@ -117,7 +117,7 @@ bool AssemblyStack::analyzeParsed(Object& _object)
|
|||||||
AsmAnalyzer analyzer(
|
AsmAnalyzer analyzer(
|
||||||
*_object.analysisInfo,
|
*_object.analysisInfo,
|
||||||
m_errorReporter,
|
m_errorReporter,
|
||||||
boost::none,
|
std::nullopt,
|
||||||
languageToDialect(m_language, m_evmVersion),
|
languageToDialect(m_language, m_evmVersion),
|
||||||
{},
|
{},
|
||||||
_object.dataNames()
|
_object.dataNames()
|
||||||
@ -200,7 +200,10 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
|||||||
Dialect const& dialect = languageToDialect(m_language, EVMVersion{});
|
Dialect const& dialect = languageToDialect(m_language, EVMVersion{});
|
||||||
|
|
||||||
MachineAssemblyObject object;
|
MachineAssemblyObject object;
|
||||||
object.assembly = EWasmObjectCompiler::compile(*m_parserResult, dialect);
|
auto result = EWasmObjectCompiler::compile(*m_parserResult, dialect);
|
||||||
|
object.assembly = std::move(result.first);
|
||||||
|
object.bytecode = make_shared<dev::eth::LinkerObject>();
|
||||||
|
object.bytecode->bytecode = std::move(result.second);
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,8 @@ add_library(yul
|
|||||||
backends/wasm/EWasmObjectCompiler.h
|
backends/wasm/EWasmObjectCompiler.h
|
||||||
backends/wasm/EWasmToText.cpp
|
backends/wasm/EWasmToText.cpp
|
||||||
backends/wasm/EWasmToText.h
|
backends/wasm/EWasmToText.h
|
||||||
|
backends/wasm/BinaryTransform.cpp
|
||||||
|
backends/wasm/BinaryTransform.h
|
||||||
backends/wasm/WasmDialect.cpp
|
backends/wasm/WasmDialect.cpp
|
||||||
backends/wasm/WasmDialect.h
|
backends/wasm/WasmDialect.h
|
||||||
backends/wasm/WordSizeTransform.cpp
|
backends/wasm/WordSizeTransform.cpp
|
||||||
@ -66,6 +68,10 @@ add_library(yul
|
|||||||
optimiser/CallGraphGenerator.h
|
optimiser/CallGraphGenerator.h
|
||||||
optimiser/CommonSubexpressionEliminator.cpp
|
optimiser/CommonSubexpressionEliminator.cpp
|
||||||
optimiser/CommonSubexpressionEliminator.h
|
optimiser/CommonSubexpressionEliminator.h
|
||||||
|
optimiser/ConditionalSimplifier.cpp
|
||||||
|
optimiser/ConditionalSimplifier.h
|
||||||
|
optimiser/ConditionalUnsimplifier.cpp
|
||||||
|
optimiser/ConditionalUnsimplifier.h
|
||||||
optimiser/ControlFlowSimplifier.cpp
|
optimiser/ControlFlowSimplifier.cpp
|
||||||
optimiser/ControlFlowSimplifier.h
|
optimiser/ControlFlowSimplifier.h
|
||||||
optimiser/DataFlowAnalyzer.cpp
|
optimiser/DataFlowAnalyzer.cpp
|
||||||
|
@ -723,9 +723,7 @@ void CodeTransform::visitExpression(Expression const& _expression)
|
|||||||
|
|
||||||
void CodeTransform::visitStatements(vector<Statement> const& _statements)
|
void CodeTransform::visitStatements(vector<Statement> const& _statements)
|
||||||
{
|
{
|
||||||
// Workaround boost bug:
|
std::optional<AbstractAssembly::LabelID> jumpTarget = std::nullopt;
|
||||||
// https://www.boost.org/doc/libs/1_63_0/libs/optional/doc/html/boost_optional/tutorial/gotchas/false_positive_with__wmaybe_uninitialized.html
|
|
||||||
boost::optional<AbstractAssembly::LabelID> jumpTarget = boost::make_optional(false, AbstractAssembly::LabelID());
|
|
||||||
|
|
||||||
for (auto const& statement: _statements)
|
for (auto const& statement: _statements)
|
||||||
{
|
{
|
||||||
@ -740,7 +738,7 @@ void CodeTransform::visitStatements(vector<Statement> const& _statements)
|
|||||||
else if (!functionDefinition && jumpTarget)
|
else if (!functionDefinition && jumpTarget)
|
||||||
{
|
{
|
||||||
m_assembly.appendLabel(*jumpTarget);
|
m_assembly.appendLabel(*jumpTarget);
|
||||||
jumpTarget = boost::none;
|
jumpTarget = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::apply_visitor(*this, statement);
|
boost::apply_visitor(*this, statement);
|
||||||
|
@ -28,8 +28,8 @@
|
|||||||
#include <libyul/AsmScope.h>
|
#include <libyul/AsmScope.h>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
|
@ -47,7 +47,7 @@ struct BuiltinContext
|
|||||||
|
|
||||||
struct BuiltinFunctionForEVM: BuiltinFunction
|
struct BuiltinFunctionForEVM: BuiltinFunction
|
||||||
{
|
{
|
||||||
boost::optional<dev::eth::Instruction> instruction;
|
std::optional<dev::eth::Instruction> instruction;
|
||||||
/// Function to generate code for the given function call and append it to the abstract
|
/// Function to generate code for the given function call and append it to the abstract
|
||||||
/// assembly. The fourth parameter is called to visit (and generate code for) the arguments
|
/// assembly. The fourth parameter is called to visit (and generate code for) the arguments
|
||||||
/// from right to left.
|
/// from right to left.
|
||||||
|
582
libyul/backends/wasm/BinaryTransform.cpp
Normal file
582
libyul/backends/wasm/BinaryTransform.cpp
Normal file
@ -0,0 +1,582 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* EWasm to binary encoder.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libyul/backends/wasm/BinaryTransform.h>
|
||||||
|
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
#include <libdevcore/CommonData.h>
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace yul;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace yul::wasm;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
bytes toBytes(uint8_t _b)
|
||||||
|
{
|
||||||
|
return bytes(1, _b);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Section: uint8_t
|
||||||
|
{
|
||||||
|
CUSTOM = 0x00,
|
||||||
|
TYPE = 0x01,
|
||||||
|
IMPORT = 0x02,
|
||||||
|
FUNCTION = 0x03,
|
||||||
|
MEMORY = 0x05,
|
||||||
|
GLOBAL = 0x06,
|
||||||
|
EXPORT = 0x07,
|
||||||
|
CODE = 0x0a
|
||||||
|
};
|
||||||
|
|
||||||
|
bytes toBytes(Section _s)
|
||||||
|
{
|
||||||
|
return toBytes(uint8_t(_s));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ValueType: uint8_t
|
||||||
|
{
|
||||||
|
Void = 0x40,
|
||||||
|
Function = 0x60,
|
||||||
|
I64 = 0x7e,
|
||||||
|
I32 = 0x7f
|
||||||
|
};
|
||||||
|
|
||||||
|
bytes toBytes(ValueType _vt)
|
||||||
|
{
|
||||||
|
return toBytes(uint8_t(_vt));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Export: uint8_t
|
||||||
|
{
|
||||||
|
Function = 0x0,
|
||||||
|
Memory = 0x2
|
||||||
|
};
|
||||||
|
|
||||||
|
bytes toBytes(Export _export)
|
||||||
|
{
|
||||||
|
return toBytes(uint8_t(_export));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Opcode: uint8_t
|
||||||
|
{
|
||||||
|
Unreachable = 0x00,
|
||||||
|
Nop = 0x01,
|
||||||
|
Block = 0x02,
|
||||||
|
Loop = 0x03,
|
||||||
|
If = 0x04,
|
||||||
|
Else = 0x05,
|
||||||
|
Try = 0x06,
|
||||||
|
Catch = 0x07,
|
||||||
|
Throw = 0x08,
|
||||||
|
Rethrow = 0x09,
|
||||||
|
BrOnExn = 0x0a,
|
||||||
|
End = 0x0b,
|
||||||
|
Br = 0x0c,
|
||||||
|
BrIf = 0x0d,
|
||||||
|
BrTable = 0x0e,
|
||||||
|
Return = 0x0f,
|
||||||
|
Call = 0x10,
|
||||||
|
CallIndirect = 0x11,
|
||||||
|
ReturnCall = 0x12,
|
||||||
|
ReturnCallIndirect = 0x13,
|
||||||
|
Drop = 0x1a,
|
||||||
|
Select = 0x1b,
|
||||||
|
LocalGet = 0x20,
|
||||||
|
LocalSet = 0x21,
|
||||||
|
LocalTee = 0x22,
|
||||||
|
GlobalGet = 0x23,
|
||||||
|
GlobalSet = 0x24,
|
||||||
|
I32Const = 0x41,
|
||||||
|
I64Const = 0x42,
|
||||||
|
};
|
||||||
|
|
||||||
|
bytes toBytes(Opcode _o)
|
||||||
|
{
|
||||||
|
return toBytes(uint8_t(_o));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::map<string, uint8_t> const builtins = {
|
||||||
|
{"i32.load", 0x28},
|
||||||
|
{"i64.load", 0x29},
|
||||||
|
{"i32.load8_s", 0x2c},
|
||||||
|
{"i32.load8_u", 0x2d},
|
||||||
|
{"i32.load16_s", 0x2e},
|
||||||
|
{"i32.load16_u", 0x2f},
|
||||||
|
{"i64.load8_s", 0x30},
|
||||||
|
{"i64.load8_u", 0x31},
|
||||||
|
{"i64.load16_s", 0x32},
|
||||||
|
{"i64.load16_u", 0x33},
|
||||||
|
{"i64.load32_s", 0x34},
|
||||||
|
{"i64.load32_u", 0x35},
|
||||||
|
{"i32.store", 0x36},
|
||||||
|
{"i64.store", 0x37},
|
||||||
|
{"i32.store8", 0x3a},
|
||||||
|
{"i32.store16", 0x3b},
|
||||||
|
{"i64.store8", 0x3c},
|
||||||
|
{"i64.store16", 0x3d},
|
||||||
|
{"i64.store32", 0x3e},
|
||||||
|
{"memory.size", 0x3f},
|
||||||
|
{"memory.grow", 0x40},
|
||||||
|
{"i32.eqz", 0x45},
|
||||||
|
{"i32.eq", 0x46},
|
||||||
|
{"i32.ne", 0x47},
|
||||||
|
{"i32.lt_s", 0x48},
|
||||||
|
{"i32.lt_u", 0x49},
|
||||||
|
{"i32.gt_s", 0x4a},
|
||||||
|
{"i32.gt_u", 0x4b},
|
||||||
|
{"i32.le_s", 0x4c},
|
||||||
|
{"i32.le_u", 0x4d},
|
||||||
|
{"i32.ge_s", 0x4e},
|
||||||
|
{"i32.ge_u", 0x4f},
|
||||||
|
{"i64.eqz", 0x50},
|
||||||
|
{"i64.eq", 0x51},
|
||||||
|
{"i64.ne", 0x52},
|
||||||
|
{"i64.lt_s", 0x53},
|
||||||
|
{"i64.lt_u", 0x54},
|
||||||
|
{"i64.gt_s", 0x55},
|
||||||
|
{"i64.gt_u", 0x56},
|
||||||
|
{"i64.le_s", 0x57},
|
||||||
|
{"i64.le_u", 0x58},
|
||||||
|
{"i64.ge_s", 0x59},
|
||||||
|
{"i64.ge_u", 0x5a},
|
||||||
|
{"i32.clz", 0x67},
|
||||||
|
{"i32.ctz", 0x68},
|
||||||
|
{"i32.popcnt", 0x69},
|
||||||
|
{"i32.add", 0x6a},
|
||||||
|
{"i32.sub", 0x6b},
|
||||||
|
{"i32.mul", 0x6c},
|
||||||
|
{"i32.div_s", 0x6d},
|
||||||
|
{"i32.div_u", 0x6e},
|
||||||
|
{"i32.rem_s", 0x6f},
|
||||||
|
{"i32.rem_u", 0x70},
|
||||||
|
{"i32.and", 0x71},
|
||||||
|
{"i32.or", 0x72},
|
||||||
|
{"i32.xor", 0x73},
|
||||||
|
{"i32.shl", 0x74},
|
||||||
|
{"i32.shr_s", 0x75},
|
||||||
|
{"i32.shr_u", 0x76},
|
||||||
|
{"i32.rotl", 0x77},
|
||||||
|
{"i32.rotr", 0x78},
|
||||||
|
{"i64.clz", 0x79},
|
||||||
|
{"i64.ctz", 0x7a},
|
||||||
|
{"i64.popcnt", 0x7b},
|
||||||
|
{"i64.add", 0x7c},
|
||||||
|
{"i64.sub", 0x7d},
|
||||||
|
{"i64.mul", 0x7e},
|
||||||
|
{"i64.div_s", 0x7f},
|
||||||
|
{"i64.div_u", 0x80},
|
||||||
|
{"i64.rem_s", 0x81},
|
||||||
|
{"i64.rem_u", 0x82},
|
||||||
|
{"i64.and", 0x83},
|
||||||
|
{"i64.or", 0x84},
|
||||||
|
{"i64.xor", 0x85},
|
||||||
|
{"i64.shl", 0x86},
|
||||||
|
{"i64.shr_s", 0x87},
|
||||||
|
{"i64.shr_u", 0x88},
|
||||||
|
{"i64.rotl", 0x89},
|
||||||
|
{"i64.rotr", 0x8a},
|
||||||
|
{"i32.wrap_i64", 0xa7},
|
||||||
|
{"i64.extend_i32_s", 0xac},
|
||||||
|
{"i64.extend_i32_u", 0xad},
|
||||||
|
};
|
||||||
|
|
||||||
|
bytes lebEncode(uint64_t _n)
|
||||||
|
{
|
||||||
|
bytes encoded;
|
||||||
|
while (_n > 0x7f)
|
||||||
|
{
|
||||||
|
encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f)));
|
||||||
|
_n >>= 7;
|
||||||
|
}
|
||||||
|
encoded.emplace_back(_n);
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes lebEncodeSigned(int64_t _n)
|
||||||
|
{
|
||||||
|
if (_n >= 0 && _n < 0x40)
|
||||||
|
return toBytes(uint8_t(uint64_t(_n) & 0xff));
|
||||||
|
else if (-_n > 0 && -_n < 0x40)
|
||||||
|
return toBytes(uint8_t(uint64_t(_n + 0x80) & 0xff));
|
||||||
|
else
|
||||||
|
return toBytes(uint8_t(0x80 | uint8_t(_n & 0x7f))) + lebEncodeSigned(_n / 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes prefixSize(bytes _data)
|
||||||
|
{
|
||||||
|
size_t size = _data.size();
|
||||||
|
return lebEncode(size) + std::move(_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes makeSection(Section _section, bytes _data)
|
||||||
|
{
|
||||||
|
return toBytes(_section) + prefixSize(std::move(_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::run(Module const& _module)
|
||||||
|
{
|
||||||
|
BinaryTransform bt;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _module.globals.size(); ++i)
|
||||||
|
bt.m_globals[_module.globals[i].variableName] = i;
|
||||||
|
|
||||||
|
size_t funID = 0;
|
||||||
|
for (FunctionImport const& fun: _module.imports)
|
||||||
|
bt.m_functions[fun.internalName] = funID++;
|
||||||
|
for (FunctionDefinition const& fun: _module.functions)
|
||||||
|
bt.m_functions[fun.name] = funID++;
|
||||||
|
|
||||||
|
bytes ret{0, 'a', 's', 'm'};
|
||||||
|
// version
|
||||||
|
ret += bytes{1, 0, 0, 0};
|
||||||
|
ret += bt.typeSection(_module.imports, _module.functions);
|
||||||
|
ret += bt.importSection(_module.imports);
|
||||||
|
ret += bt.functionSection(_module.functions);
|
||||||
|
ret += bt.memorySection();
|
||||||
|
ret += bt.globalSection();
|
||||||
|
ret += bt.exportSection();
|
||||||
|
for (auto const& sub: _module.subModules)
|
||||||
|
{
|
||||||
|
// TODO should we prefix and / or shorten the name?
|
||||||
|
bytes data = BinaryTransform::run(sub.second);
|
||||||
|
size_t length = data.size();
|
||||||
|
ret += bt.customSection(sub.first, std::move(data));
|
||||||
|
bt.m_subModulePosAndSize[sub.first] = {ret.size() - length, length};
|
||||||
|
}
|
||||||
|
ret += bt.codeSection(_module.functions);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(Literal const& _literal)
|
||||||
|
{
|
||||||
|
return toBytes(Opcode::I64Const) + lebEncodeSigned(_literal.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(StringLiteral const&)
|
||||||
|
{
|
||||||
|
// TODO is this used?
|
||||||
|
yulAssert(false, "String literals not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(LocalVariable const& _variable)
|
||||||
|
{
|
||||||
|
return toBytes(Opcode::LocalGet) + lebEncode(m_locals.at(_variable.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(GlobalVariable const& _variable)
|
||||||
|
{
|
||||||
|
return toBytes(Opcode::GlobalGet) + lebEncode(m_globals.at(_variable.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(BuiltinCall const& _call)
|
||||||
|
{
|
||||||
|
// We need to avoid visiting the arguments of `dataoffset` and `datasize` because
|
||||||
|
// they are references to object names that should not end up in the code.
|
||||||
|
if (_call.functionName == "dataoffset")
|
||||||
|
{
|
||||||
|
string name = boost::get<StringLiteral>(_call.arguments.at(0)).value;
|
||||||
|
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first);
|
||||||
|
}
|
||||||
|
else if (_call.functionName == "datasize")
|
||||||
|
{
|
||||||
|
string name = boost::get<StringLiteral>(_call.arguments.at(0)).value;
|
||||||
|
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes args = visit(_call.arguments);
|
||||||
|
|
||||||
|
if (_call.functionName == "unreachable")
|
||||||
|
return toBytes(Opcode::Unreachable);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found");
|
||||||
|
bytes ret = std::move(args) + toBytes(builtins.at(_call.functionName));
|
||||||
|
if (
|
||||||
|
_call.functionName.find(".load") != string::npos ||
|
||||||
|
_call.functionName.find(".store") != string::npos
|
||||||
|
)
|
||||||
|
// alignment and offset
|
||||||
|
ret += bytes{{3, 0}};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(FunctionCall const& _call)
|
||||||
|
{
|
||||||
|
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functions.at(_call.functionName));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(LocalAssignment const& _assignment)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
boost::apply_visitor(*this, *_assignment.value) +
|
||||||
|
toBytes(Opcode::LocalSet) +
|
||||||
|
lebEncode(m_locals.at(_assignment.variableName));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(GlobalAssignment const& _assignment)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
boost::apply_visitor(*this, *_assignment.value) +
|
||||||
|
toBytes(Opcode::GlobalSet) +
|
||||||
|
lebEncode(m_globals.at(_assignment.variableName));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(If const& _if)
|
||||||
|
{
|
||||||
|
bytes result =
|
||||||
|
boost::apply_visitor(*this, *_if.condition) +
|
||||||
|
toBytes(Opcode::If) +
|
||||||
|
toBytes(ValueType::Void);
|
||||||
|
|
||||||
|
m_labels.push({});
|
||||||
|
|
||||||
|
result += visit(_if.statements);
|
||||||
|
if (_if.elseStatements)
|
||||||
|
result += toBytes(Opcode::Else) + visit(*_if.elseStatements);
|
||||||
|
|
||||||
|
m_labels.pop();
|
||||||
|
|
||||||
|
result += toBytes(Opcode::End);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(Loop const& _loop)
|
||||||
|
{
|
||||||
|
bytes result = toBytes(Opcode::Loop) + toBytes(ValueType::Void);
|
||||||
|
|
||||||
|
m_labels.push(_loop.labelName);
|
||||||
|
result += visit(_loop.statements);
|
||||||
|
m_labels.pop();
|
||||||
|
|
||||||
|
result += toBytes(Opcode::End);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(Break const&)
|
||||||
|
{
|
||||||
|
yulAssert(false, "br not yet implemented.");
|
||||||
|
// TODO the index is just the nesting depth.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(BreakIf const&)
|
||||||
|
{
|
||||||
|
yulAssert(false, "br_if not yet implemented.");
|
||||||
|
// TODO the index is just the nesting depth.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(Block const& _block)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
toBytes(Opcode::Block) +
|
||||||
|
toBytes(ValueType::Void) +
|
||||||
|
visit(_block.statements) +
|
||||||
|
toBytes(Opcode::End);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::operator()(FunctionDefinition const& _function)
|
||||||
|
{
|
||||||
|
bytes ret;
|
||||||
|
|
||||||
|
// This is a kind of run-length-encoding of local types. Has to be adapted once
|
||||||
|
// we have locals of different types.
|
||||||
|
ret += lebEncode(1); // number of locals groups
|
||||||
|
ret += lebEncode(_function.locals.size());
|
||||||
|
ret += toBytes(ValueType::I64);
|
||||||
|
|
||||||
|
m_locals.clear();
|
||||||
|
size_t varIdx = 0;
|
||||||
|
for (size_t i = 0; i < _function.parameterNames.size(); ++i)
|
||||||
|
m_locals[_function.parameterNames[i]] = varIdx++;
|
||||||
|
for (size_t i = 0; i < _function.locals.size(); ++i)
|
||||||
|
m_locals[_function.locals[i].variableName] = varIdx++;
|
||||||
|
|
||||||
|
ret += visit(_function.body);
|
||||||
|
ret += toBytes(Opcode::End);
|
||||||
|
|
||||||
|
return prefixSize(std::move(ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
encodeTypes(_import.paramTypes),
|
||||||
|
encodeTypes(_import.returnType ? vector<string>(1, *_import.returnType) : vector<string>())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryTransform::Type BinaryTransform::typeOf(FunctionDefinition const& _funDef)
|
||||||
|
{
|
||||||
|
|
||||||
|
return {
|
||||||
|
encodeTypes(vector<string>(_funDef.parameterNames.size(), "i64")),
|
||||||
|
encodeTypes(vector<string>(_funDef.returns ? 1 : 0, "i64"))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t BinaryTransform::encodeType(string const& _typeName)
|
||||||
|
{
|
||||||
|
if (_typeName == "i32")
|
||||||
|
return uint8_t(ValueType::I32);
|
||||||
|
else if (_typeName == "i64")
|
||||||
|
return uint8_t(ValueType::I64);
|
||||||
|
else
|
||||||
|
yulAssert(false, "");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<uint8_t> BinaryTransform::encodeTypes(vector<string> const& _typeNames)
|
||||||
|
{
|
||||||
|
vector<uint8_t> result;
|
||||||
|
for (auto const& t: _typeNames)
|
||||||
|
result.emplace_back(encodeType(t));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::typeSection(
|
||||||
|
vector<FunctionImport> const& _imports,
|
||||||
|
vector<FunctionDefinition> const& _functions
|
||||||
|
)
|
||||||
|
{
|
||||||
|
map<Type, vector<string>> types;
|
||||||
|
for (auto const& import: _imports)
|
||||||
|
types[typeOf(import)].emplace_back(import.internalName);
|
||||||
|
for (auto const& fun: _functions)
|
||||||
|
types[typeOf(fun)].emplace_back(fun.name);
|
||||||
|
|
||||||
|
bytes result;
|
||||||
|
size_t index = 0;
|
||||||
|
for (auto const& [type, funNames]: types)
|
||||||
|
{
|
||||||
|
for (string const& name: funNames)
|
||||||
|
m_functionTypes[name] = index;
|
||||||
|
result += toBytes(ValueType::Function);
|
||||||
|
result += lebEncode(type.first.size()) + type.first;
|
||||||
|
result += lebEncode(type.second.size()) + type.second;
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeSection(Section::TYPE, lebEncode(index) + std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::importSection(
|
||||||
|
vector<FunctionImport> const& _imports
|
||||||
|
)
|
||||||
|
{
|
||||||
|
bytes result = lebEncode(_imports.size());
|
||||||
|
for (FunctionImport const& import: _imports)
|
||||||
|
{
|
||||||
|
uint8_t importKind = 0; // function
|
||||||
|
result +=
|
||||||
|
encodeName(import.module) +
|
||||||
|
encodeName(import.externalName) +
|
||||||
|
toBytes(importKind) +
|
||||||
|
lebEncode(m_functionTypes[import.internalName]);
|
||||||
|
}
|
||||||
|
return makeSection(Section::IMPORT, std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::functionSection(vector<FunctionDefinition> const& _functions)
|
||||||
|
{
|
||||||
|
bytes result = lebEncode(_functions.size());
|
||||||
|
for (auto const& fun: _functions)
|
||||||
|
result += lebEncode(m_functionTypes.at(fun.name));
|
||||||
|
return makeSection(Section::FUNCTION, std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::memorySection()
|
||||||
|
{
|
||||||
|
bytes result = lebEncode(1);
|
||||||
|
result.push_back(0); // flags
|
||||||
|
result.push_back(1); // initial
|
||||||
|
return makeSection(Section::MEMORY, std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::globalSection()
|
||||||
|
{
|
||||||
|
bytes result = lebEncode(m_globals.size());
|
||||||
|
for (size_t i = 0; i < m_globals.size(); ++i)
|
||||||
|
result +=
|
||||||
|
// mutable i64
|
||||||
|
bytes{uint8_t(ValueType::I64), 1} +
|
||||||
|
toBytes(Opcode::I64Const) +
|
||||||
|
lebEncodeSigned(0) +
|
||||||
|
toBytes(Opcode::End);
|
||||||
|
|
||||||
|
return makeSection(Section::GLOBAL, std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::exportSection()
|
||||||
|
{
|
||||||
|
bytes result = lebEncode(2);
|
||||||
|
result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0);
|
||||||
|
result += encodeName("main") + toBytes(Export::Function) + lebEncode(m_functions.at("main"));
|
||||||
|
return makeSection(Section::EXPORT, std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::customSection(string const& _name, bytes _data)
|
||||||
|
{
|
||||||
|
bytes result = encodeName(_name) + std::move(_data);
|
||||||
|
return makeSection(Section::CUSTOM, std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions)
|
||||||
|
{
|
||||||
|
bytes result = lebEncode(_functions.size());
|
||||||
|
for (FunctionDefinition const& fun: _functions)
|
||||||
|
result += (*this)(fun);
|
||||||
|
return makeSection(Section::CODE, std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::visit(vector<Expression> const& _expressions)
|
||||||
|
{
|
||||||
|
bytes result;
|
||||||
|
for (auto const& expr: _expressions)
|
||||||
|
result += boost::apply_visitor(*this, expr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::visitReversed(vector<Expression> const& _expressions)
|
||||||
|
{
|
||||||
|
bytes result;
|
||||||
|
for (auto const& expr: _expressions | boost::adaptors::reversed)
|
||||||
|
result += boost::apply_visitor(*this, expr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BinaryTransform::encodeName(std::string const& _name)
|
||||||
|
{
|
||||||
|
// UTF-8 is allowed here by the Wasm spec, but since all names here should stem from
|
||||||
|
// Solidity or Yul identifiers or similar, non-ascii characters ending up here
|
||||||
|
// is a very bad sign.
|
||||||
|
for (char c: _name)
|
||||||
|
yulAssert(uint8_t(c) <= 0x7f, "Non-ascii character found.");
|
||||||
|
return lebEncode(_name.size()) + asBytes(_name);
|
||||||
|
}
|
94
libyul/backends/wasm/BinaryTransform.h
Normal file
94
libyul/backends/wasm/BinaryTransform.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* EWasm to binary encoder.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/backends/wasm/EWasmAST.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
namespace yul
|
||||||
|
{
|
||||||
|
namespace wasm
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web assembly to binary transform.
|
||||||
|
*/
|
||||||
|
class BinaryTransform: public boost::static_visitor<dev::bytes>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static dev::bytes run(Module const& _module);
|
||||||
|
|
||||||
|
dev::bytes operator()(wasm::Literal const& _literal);
|
||||||
|
dev::bytes operator()(wasm::StringLiteral const& _literal);
|
||||||
|
dev::bytes operator()(wasm::LocalVariable const& _identifier);
|
||||||
|
dev::bytes operator()(wasm::GlobalVariable const& _identifier);
|
||||||
|
dev::bytes operator()(wasm::BuiltinCall const& _builinCall);
|
||||||
|
dev::bytes operator()(wasm::FunctionCall const& _functionCall);
|
||||||
|
dev::bytes operator()(wasm::LocalAssignment const& _assignment);
|
||||||
|
dev::bytes operator()(wasm::GlobalAssignment const& _assignment);
|
||||||
|
dev::bytes operator()(wasm::If const& _if);
|
||||||
|
dev::bytes operator()(wasm::Loop const& _loop);
|
||||||
|
dev::bytes operator()(wasm::Break const& _break);
|
||||||
|
dev::bytes operator()(wasm::BreakIf const& _break);
|
||||||
|
dev::bytes operator()(wasm::Block const& _block);
|
||||||
|
dev::bytes operator()(wasm::FunctionDefinition const& _function);
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>;
|
||||||
|
static Type typeOf(wasm::FunctionImport const& _import);
|
||||||
|
static Type typeOf(wasm::FunctionDefinition const& _funDef);
|
||||||
|
|
||||||
|
static uint8_t encodeType(std::string const& _typeName);
|
||||||
|
static std::vector<uint8_t> encodeTypes(std::vector<std::string> const& _typeNames);
|
||||||
|
dev::bytes typeSection(
|
||||||
|
std::vector<wasm::FunctionImport> const& _imports,
|
||||||
|
std::vector<wasm::FunctionDefinition> const& _functions
|
||||||
|
);
|
||||||
|
|
||||||
|
dev::bytes importSection(std::vector<wasm::FunctionImport> const& _imports);
|
||||||
|
dev::bytes functionSection(std::vector<wasm::FunctionDefinition> const& _functions);
|
||||||
|
dev::bytes memorySection();
|
||||||
|
dev::bytes globalSection();
|
||||||
|
dev::bytes exportSection();
|
||||||
|
dev::bytes customSection(std::string const& _name, dev::bytes _data);
|
||||||
|
dev::bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions);
|
||||||
|
|
||||||
|
dev::bytes visit(std::vector<wasm::Expression> const& _expressions);
|
||||||
|
dev::bytes visitReversed(std::vector<wasm::Expression> const& _expressions);
|
||||||
|
|
||||||
|
static dev::bytes encodeName(std::string const& _name);
|
||||||
|
|
||||||
|
std::map<std::string, size_t> m_locals;
|
||||||
|
std::map<std::string, size_t> m_globals;
|
||||||
|
std::map<std::string, size_t> m_functions;
|
||||||
|
std::map<std::string, size_t> m_functionTypes;
|
||||||
|
std::stack<std::string> m_labels;
|
||||||
|
std::map<std::string, std::pair<size_t, size_t>> m_subModulePosAndSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ namespace
|
|||||||
{
|
{
|
||||||
static string const polyfill{R"({
|
static string const polyfill{R"({
|
||||||
function or_bool(a, b, c, d) -> r {
|
function or_bool(a, b, c, d) -> r {
|
||||||
r := i64.ne(0, i64.or(i64.or(a, b), i64.or(c, d)))
|
r := i64.or(i64.or(a, b), i64.or(c, d))
|
||||||
}
|
}
|
||||||
// returns a + y + c plus carry value on 64 bit values.
|
// returns a + y + c plus carry value on 64 bit values.
|
||||||
// c should be at most 1
|
// c should be at most 1
|
||||||
@ -205,9 +205,7 @@ function cmp(a, b) -> r {
|
|||||||
switch i64.lt_u(a, b)
|
switch i64.lt_u(a, b)
|
||||||
case 1 { r := 0xffffffffffffffff }
|
case 1 { r := 0xffffffffffffffff }
|
||||||
default {
|
default {
|
||||||
switch i64.gt_u(a, b)
|
r := i64.ne(a, b)
|
||||||
case 1 { r := 1 }
|
|
||||||
default { r := 0 }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,10 +216,7 @@ function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
|||||||
case 0 {
|
case 0 {
|
||||||
switch cmp(x3, y3)
|
switch cmp(x3, y3)
|
||||||
case 0 {
|
case 0 {
|
||||||
switch cmp(x4, y4)
|
z4 := i64.lt_u(x4, y4)
|
||||||
case 0 { z4 := 0 }
|
|
||||||
case 1 { z4 := 0 }
|
|
||||||
default { z4 := 1 }
|
|
||||||
}
|
}
|
||||||
case 1 { z4 := 0 }
|
case 1 { z4 := 0 }
|
||||||
default { z4 := 1 }
|
default { z4 := 1 }
|
||||||
@ -245,13 +240,78 @@ function sgt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
|||||||
z1, z2, z3, z4 := slt(y1, y2, y3, y4, x1, x2, x3, x4)
|
z1, z2, z3, z4 := slt(y1, y2, y3, y4, x1, x2, x3, x4)
|
||||||
}
|
}
|
||||||
|
|
||||||
function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
function shl_single(a, amount) -> x, y {
|
||||||
// TODO implement
|
// amount < 64
|
||||||
unreachable()
|
x := i64.shr_u(a, i64.sub(64, amount))
|
||||||
|
y := i64.shl(a, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
||||||
|
if i64.and(i64.eqz(x1), i64.eqz(x2)) {
|
||||||
|
if i64.eqz(x3) {
|
||||||
|
if i64.lt_u(x4, 256) {
|
||||||
|
if i64.ge_u(x4, 128) {
|
||||||
|
y1 := y3
|
||||||
|
y2 := y4
|
||||||
|
y3 := 0
|
||||||
|
y4 := 0
|
||||||
|
x4 := i64.sub(x4, 128)
|
||||||
|
}
|
||||||
|
if i64.ge_u(x4, 64) {
|
||||||
|
y1 := y2
|
||||||
|
y2 := y3
|
||||||
|
y3 := y4
|
||||||
|
y4 := 0
|
||||||
|
x4 := i64.sub(x4, 64)
|
||||||
|
}
|
||||||
|
let t, r
|
||||||
|
t, z4 := shl_single(y4, x4)
|
||||||
|
r, z3 := shl_single(y3, x4)
|
||||||
|
z3 := i64.or(z3, t)
|
||||||
|
t, z2 := shl_single(y2, x4)
|
||||||
|
z2 := i64.or(z2, r)
|
||||||
|
r, z1 := shl_single(y1, x4)
|
||||||
|
z1 := i64.or(z1, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shr_single(a, amount) -> x, y {
|
||||||
|
// amount < 64
|
||||||
|
y := i64.shl(a, i64.sub(64, amount))
|
||||||
|
x := i64.shr_u(a, amount)
|
||||||
|
}
|
||||||
|
|
||||||
function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
||||||
// TODO implement
|
if i64.and(i64.eqz(x1), i64.eqz(x2)) {
|
||||||
unreachable()
|
if i64.eqz(x3) {
|
||||||
|
if i64.lt_u(x4, 256) {
|
||||||
|
if i64.ge_u(x4, 128) {
|
||||||
|
y4 := y2
|
||||||
|
y3 := y1
|
||||||
|
y2 := 0
|
||||||
|
y1 := 0
|
||||||
|
x4 := i64.sub(x4, 128)
|
||||||
|
}
|
||||||
|
if i64.ge_u(x4, 64) {
|
||||||
|
y4 := y3
|
||||||
|
y3 := y2
|
||||||
|
y2 := y1
|
||||||
|
y1 := 0
|
||||||
|
x4 := i64.sub(x4, 64)
|
||||||
|
}
|
||||||
|
let t
|
||||||
|
z4, t := shr_single(y4, x4)
|
||||||
|
z3, t := shr_single(y3, x4)
|
||||||
|
z4 := i64.or(z4, t)
|
||||||
|
z2, t := shr_single(y2, x4)
|
||||||
|
z3 := i64.or(z3, t)
|
||||||
|
z1, t := shr_single(y1, x4)
|
||||||
|
z2 := i64.or(z2, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
||||||
// TODO implement
|
// TODO implement
|
||||||
@ -291,45 +351,36 @@ function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function address() -> z1, z2, z3, z4 {
|
function address() -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
|
||||||
eth.getAddress(0)
|
eth.getAddress(0)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
z1, z2, z3, z4 := mload_internal(0)
|
||||||
restore_temp_mem_32(t1, t2, t3, t4)
|
|
||||||
}
|
}
|
||||||
function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||||
// TODO implement
|
// TODO implement
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
function origin() -> z1, z2, z3, z4 {
|
function origin() -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
|
||||||
eth.getTxOrigin(0)
|
eth.getTxOrigin(0)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
z1, z2, z3, z4 := mload_internal(0)
|
||||||
restore_temp_mem_32(t1, t2, t3, t4)
|
|
||||||
}
|
}
|
||||||
function caller() -> z1, z2, z3, z4 {
|
function caller() -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
|
||||||
eth.getCaller(0)
|
eth.getCaller(0)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
z1, z2, z3, z4 := mload_internal(0)
|
||||||
restore_temp_mem_32(t1, t2, t3, t4)
|
|
||||||
}
|
}
|
||||||
function callvalue() -> z1, z2, z3, z4 {
|
function callvalue() -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
|
||||||
eth.getCallValue(0)
|
eth.getCallValue(0)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
z1, z2, z3, z4 := mload_internal(0)
|
||||||
restore_temp_mem_32(t1, t2, t3, t4)
|
|
||||||
}
|
}
|
||||||
function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
|
||||||
eth.callDataCopy(0, u256_to_i32(x1, x2, x3, x4), 32)
|
eth.callDataCopy(0, u256_to_i32(x1, x2, x3, x4), 32)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
z1, z2, z3, z4 := mload_internal(0)
|
||||||
restore_temp_mem_32(t1, t2, t3, t4)
|
|
||||||
}
|
}
|
||||||
function calldatasize() -> z1, z2, z3, z4 {
|
function calldatasize() -> z1, z2, z3, z4 {
|
||||||
z4 := eth.getCallDataSize()
|
z4 := eth.getCallDataSize()
|
||||||
}
|
}
|
||||||
function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||||
eth.callDataCopy(
|
eth.callDataCopy(
|
||||||
u256_to_i32ptr(x1, x2, x3, x4),
|
// scratch - TODO: overflow check
|
||||||
|
i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64),
|
||||||
u256_to_i32(y1, y2, y3, y4),
|
u256_to_i32(y1, y2, y3, y4),
|
||||||
u256_to_i32(z1, z2, z3, z4)
|
u256_to_i32(z1, z2, z3, z4)
|
||||||
)
|
)
|
||||||
@ -337,14 +388,13 @@ function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
|||||||
|
|
||||||
// Needed?
|
// Needed?
|
||||||
function codesize() -> z1, z2, z3, z4 {
|
function codesize() -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
|
||||||
eth.getCodeSize(0)
|
eth.getCodeSize(0)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
z1, z2, z3, z4 := mload_internal(0)
|
||||||
restore_temp_mem_32(t1, t2, t3, t4)
|
|
||||||
}
|
}
|
||||||
function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||||
eth.codeCopy(
|
eth.codeCopy(
|
||||||
u256_to_i32ptr(x1, x2, x3, x4),
|
// scratch - TODO: overflow check
|
||||||
|
i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64),
|
||||||
u256_to_i32(y1, y2, y3, y4),
|
u256_to_i32(y1, y2, y3, y4),
|
||||||
u256_to_i32(z1, z2, z3, z4)
|
u256_to_i32(z1, z2, z3, z4)
|
||||||
)
|
)
|
||||||
@ -355,10 +405,8 @@ function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function gasprice() -> z1, z2, z3, z4 {
|
function gasprice() -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
|
||||||
eth.getTxGasPrice(0)
|
eth.getTxGasPrice(0)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
z1, z2, z3, z4 := mload_internal(0)
|
||||||
restore_temp_mem_32(t1, t2, t3, t4)
|
|
||||||
}
|
}
|
||||||
function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||||
// TODO implement
|
// TODO implement
|
||||||
@ -378,7 +426,8 @@ function returndatasize() -> z1, z2, z3, z4 {
|
|||||||
}
|
}
|
||||||
function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
|
||||||
eth.returnDataCopy(
|
eth.returnDataCopy(
|
||||||
u256_to_i32ptr(x1, x2, x3, x4),
|
// scratch - TODO: overflow check
|
||||||
|
i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64),
|
||||||
u256_to_i32(y1, y2, y3, y4),
|
u256_to_i32(y1, y2, y3, y4),
|
||||||
u256_to_i32(z1, z2, z3, z4)
|
u256_to_i32(z1, z2, z3, z4)
|
||||||
)
|
)
|
||||||
@ -399,10 +448,8 @@ function number() -> z1, z2, z3, z4 {
|
|||||||
z4 := eth.getBlockNumber()
|
z4 := eth.getBlockNumber()
|
||||||
}
|
}
|
||||||
function difficulty() -> z1, z2, z3, z4 {
|
function difficulty() -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4 := save_temp_mem_32()
|
|
||||||
eth.getBlockDifficulty(0)
|
eth.getBlockDifficulty(0)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 0)
|
z1, z2, z3, z4 := mload_internal(0)
|
||||||
restore_temp_mem_32(t1, t2, t3, t4)
|
|
||||||
}
|
}
|
||||||
function gaslimit() -> z1, z2, z3, z4 {
|
function gaslimit() -> z1, z2, z3, z4 {
|
||||||
z4 := eth.getBlockGasLimit()
|
z4 := eth.getBlockGasLimit()
|
||||||
@ -463,6 +510,12 @@ function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) {
|
|||||||
}
|
}
|
||||||
function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||||
let pos := u256_to_i32ptr(x1, x2, x3, x4)
|
let pos := u256_to_i32ptr(x1, x2, x3, x4)
|
||||||
|
// Make room for the scratch space
|
||||||
|
// TODO do we need to check for overflow?
|
||||||
|
pos := i64.add(pos, 64)
|
||||||
|
z1, z2, z3, z4 := mload_internal(pos)
|
||||||
|
}
|
||||||
|
function mload_internal(pos) -> z1, z2, z3, z4 {
|
||||||
z1 := endian_swap(i64.load(pos))
|
z1 := endian_swap(i64.load(pos))
|
||||||
z2 := endian_swap(i64.load(i64.add(pos, 8)))
|
z2 := endian_swap(i64.load(i64.add(pos, 8)))
|
||||||
z3 := endian_swap(i64.load(i64.add(pos, 16)))
|
z3 := endian_swap(i64.load(i64.add(pos, 16)))
|
||||||
@ -470,10 +523,16 @@ function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
|||||||
}
|
}
|
||||||
function mstore(x1, x2, x3, x4, y1, y2, y3, y4) {
|
function mstore(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||||
let pos := u256_to_i32ptr(x1, x2, x3, x4)
|
let pos := u256_to_i32ptr(x1, x2, x3, x4)
|
||||||
i64.store(pos, endian_swap(x1))
|
// Make room for the scratch space
|
||||||
i64.store(i64.add(pos, 8), endian_swap(x2))
|
// TODO do we need to check for overflow?
|
||||||
i64.store(i64.add(pos, 16), endian_swap(x3))
|
pos := i64.add(pos, 64)
|
||||||
i64.store(i64.add(pos, 24), endian_swap(x4))
|
mstore_internal(pos, y1, y2, y3, y4)
|
||||||
|
}
|
||||||
|
function mstore_internal(pos, y1, y2, y3, y4) {
|
||||||
|
i64.store(pos, endian_swap(y1))
|
||||||
|
i64.store(i64.add(pos, 8), endian_swap(y2))
|
||||||
|
i64.store(i64.add(pos, 16), endian_swap(y3))
|
||||||
|
i64.store(i64.add(pos, 24), endian_swap(y4))
|
||||||
}
|
}
|
||||||
function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) {
|
function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||||
// TODO implement
|
// TODO implement
|
||||||
@ -485,19 +544,15 @@ function msize() -> z1, z2, z3, z4 {
|
|||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
|
||||||
let t1, t2, t3, t4, t5, t6, t7, t8 := save_temp_mem_64()
|
mstore_internal(0, x1, x2, x3, x4)
|
||||||
mstore(0, 0, 0, 0, x1, x2, x3, x4)
|
eth.storageLoad(0, 32)
|
||||||
eth.storageLoad(0, 16)
|
z1, z2, z3, z4 := mload_internal(32)
|
||||||
z1, z2, z3, z4 := mload(0, 0, 0, 16)
|
|
||||||
restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sstore(x1, x2, x3, x4, y1, y2, y3, y4) {
|
function sstore(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||||
let t1, t2, t3, t4, t5, t6, t7, t8 := save_temp_mem_64()
|
mstore_internal(0, x1, x2, x3, x4)
|
||||||
mstore(0, 0, 0, 0, x1, x2, x3, x4)
|
mstore_internal(32, y1, y2, y3, y4)
|
||||||
mstore(0, 0, 0, 32, y1, y2, y3, y4)
|
|
||||||
eth.storageStore(0, 32)
|
eth.storageStore(0, 32)
|
||||||
restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needed?
|
// Needed?
|
||||||
@ -615,13 +670,15 @@ function selfdestruct(a1, a2, a3, a4) {
|
|||||||
|
|
||||||
function return(x1, x2, x3, x4, y1, y2, y3, y4) {
|
function return(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||||
eth.finish(
|
eth.finish(
|
||||||
u256_to_i32ptr(x1, x2, x3, x4),
|
// scratch - TODO: overflow check
|
||||||
|
i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64),
|
||||||
u256_to_i32(y1, y2, y3, y4)
|
u256_to_i32(y1, y2, y3, y4)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
function revert(x1, x2, x3, x4, y1, y2, y3, y4) {
|
function revert(x1, x2, x3, x4, y1, y2, y3, y4) {
|
||||||
eth.revert(
|
eth.revert(
|
||||||
u256_to_i32ptr(x1, x2, x3, x4),
|
// scratch - TODO: overflow check
|
||||||
|
i64.add(u256_to_i32ptr(x1, x2, x3, x4), 64),
|
||||||
u256_to_i32(y1, y2, y3, y4)
|
u256_to_i32(y1, y2, y3, y4)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -653,12 +710,13 @@ Object EVMToEWasmTranslator::run(Object const& _object)
|
|||||||
ast.statements.emplace_back(ASTCopier{}.translate(st));
|
ast.statements.emplace_back(ASTCopier{}.translate(st));
|
||||||
|
|
||||||
Object ret;
|
Object ret;
|
||||||
|
ret.name = _object.name;
|
||||||
ret.code = make_shared<Block>(move(ast));
|
ret.code = make_shared<Block>(move(ast));
|
||||||
ret.analysisInfo = make_shared<AsmAnalysisInfo>();
|
ret.analysisInfo = make_shared<AsmAnalysisInfo>();
|
||||||
|
|
||||||
ErrorList errors;
|
ErrorList errors;
|
||||||
ErrorReporter errorReporter(errors);
|
ErrorReporter errorReporter(errors);
|
||||||
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, boost::none, WasmDialect::instance(), {}, _object.dataNames());
|
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, std::nullopt, WasmDialect::instance(), {}, _object.dataNames());
|
||||||
if (!analyzer.analyze(*ret.code))
|
if (!analyzer.analyze(*ret.code))
|
||||||
{
|
{
|
||||||
// TODO the errors here are "wrong" because they have invalid source references!
|
// TODO the errors here are "wrong" because they have invalid source references!
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace yul
|
namespace yul
|
||||||
{
|
{
|
||||||
@ -33,7 +35,6 @@ struct Literal;
|
|||||||
struct StringLiteral;
|
struct StringLiteral;
|
||||||
struct LocalVariable;
|
struct LocalVariable;
|
||||||
struct GlobalVariable;
|
struct GlobalVariable;
|
||||||
struct Label;
|
|
||||||
struct FunctionCall;
|
struct FunctionCall;
|
||||||
struct BuiltinCall;
|
struct BuiltinCall;
|
||||||
struct LocalAssignment;
|
struct LocalAssignment;
|
||||||
@ -42,11 +43,11 @@ struct Block;
|
|||||||
struct If;
|
struct If;
|
||||||
struct Loop;
|
struct Loop;
|
||||||
struct Break;
|
struct Break;
|
||||||
struct Continue;
|
struct BreakIf;
|
||||||
using Expression = boost::variant<
|
using Expression = boost::variant<
|
||||||
Literal, StringLiteral, LocalVariable, GlobalVariable, Label,
|
Literal, StringLiteral, LocalVariable, GlobalVariable,
|
||||||
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
|
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
|
||||||
Block, If, Loop, Break, Continue
|
Block, If, Loop, Break, BreakIf
|
||||||
>;
|
>;
|
||||||
|
|
||||||
struct Literal { uint64_t value; };
|
struct Literal { uint64_t value; };
|
||||||
@ -66,7 +67,7 @@ struct If {
|
|||||||
};
|
};
|
||||||
struct Loop { std::string labelName; std::vector<Expression> statements; };
|
struct Loop { std::string labelName; std::vector<Expression> statements; };
|
||||||
struct Break { Label label; };
|
struct Break { Label label; };
|
||||||
struct Continue { Label label; };
|
struct BreakIf { Label label; std::unique_ptr<Expression> condition; };
|
||||||
|
|
||||||
struct VariableDeclaration { std::string variableName; };
|
struct VariableDeclaration { std::string variableName; };
|
||||||
struct GlobalVariableDeclaration { std::string variableName; };
|
struct GlobalVariableDeclaration { std::string variableName; };
|
||||||
@ -87,6 +88,16 @@ struct FunctionDefinition
|
|||||||
std::vector<Expression> body;
|
std::vector<Expression> body;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract representation of a wasm module.
|
||||||
|
*/
|
||||||
|
struct Module
|
||||||
|
{
|
||||||
|
std::vector<GlobalVariableDeclaration> globals;
|
||||||
|
std::vector<FunctionImport> imports;
|
||||||
|
std::vector<FunctionDefinition> functions;
|
||||||
|
std::map<std::string, Module> subModules;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
#include <libyul/backends/wasm/EWasmCodeTransform.h>
|
#include <libyul/backends/wasm/EWasmCodeTransform.h>
|
||||||
|
|
||||||
#include <libyul/backends/wasm/EWasmToText.h>
|
|
||||||
#include <libyul/optimiser/NameCollector.h>
|
#include <libyul/optimiser/NameCollector.h>
|
||||||
|
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
@ -37,10 +36,11 @@ using namespace std;
|
|||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace yul;
|
using namespace yul;
|
||||||
|
|
||||||
string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
|
wasm::Module EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
|
||||||
{
|
{
|
||||||
|
wasm::Module module;
|
||||||
|
|
||||||
EWasmCodeTransform transform(_dialect, _ast);
|
EWasmCodeTransform transform(_dialect, _ast);
|
||||||
vector<wasm::FunctionDefinition> functions;
|
|
||||||
|
|
||||||
for (auto const& statement: _ast.statements)
|
for (auto const& statement: _ast.statements)
|
||||||
{
|
{
|
||||||
@ -49,17 +49,14 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
|
|||||||
"Expected only function definitions at the highest level."
|
"Expected only function definitions at the highest level."
|
||||||
);
|
);
|
||||||
if (statement.type() == typeid(yul::FunctionDefinition))
|
if (statement.type() == typeid(yul::FunctionDefinition))
|
||||||
functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement)));
|
module.functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<wasm::FunctionImport> imports;
|
|
||||||
for (auto& imp: transform.m_functionsToImport)
|
for (auto& imp: transform.m_functionsToImport)
|
||||||
imports.emplace_back(std::move(imp.second));
|
module.imports.emplace_back(std::move(imp.second));
|
||||||
return EWasmToText().run(
|
module.globals = transform.m_globalVariables;
|
||||||
transform.m_globalVariables,
|
|
||||||
imports,
|
return module;
|
||||||
functions
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::generateMultiAssignment(
|
wasm::Expression EWasmCodeTransform::generateMultiAssignment(
|
||||||
@ -133,6 +130,8 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionalInstruction const& _f)
|
|||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
|
wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
|
||||||
{
|
{
|
||||||
|
bool typeConversionNeeded = false;
|
||||||
|
|
||||||
if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name))
|
if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name))
|
||||||
{
|
{
|
||||||
if (_call.functionName.name.str().substr(0, 4) == "eth.")
|
if (_call.functionName.name.str().substr(0, 4) == "eth.")
|
||||||
@ -152,6 +151,7 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
|
|||||||
imp.paramTypes.emplace_back(param.str());
|
imp.paramTypes.emplace_back(param.str());
|
||||||
m_functionsToImport[builtin->name] = std::move(imp);
|
m_functionsToImport[builtin->name] = std::move(imp);
|
||||||
}
|
}
|
||||||
|
typeConversionNeeded = true;
|
||||||
}
|
}
|
||||||
else if (builtin->literalArguments)
|
else if (builtin->literalArguments)
|
||||||
{
|
{
|
||||||
@ -161,14 +161,32 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
|
|||||||
return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)};
|
return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return wasm::BuiltinCall{_call.functionName.name.str(), visit(_call.arguments)};
|
{
|
||||||
|
wasm::BuiltinCall call{
|
||||||
|
_call.functionName.name.str(),
|
||||||
|
injectTypeConversionIfNeeded(visit(_call.arguments), builtin->parameters)
|
||||||
|
};
|
||||||
|
if (!builtin->returns.empty() && !builtin->returns.front().empty() && builtin->returns.front() != "i64"_yulstring)
|
||||||
|
{
|
||||||
|
yulAssert(builtin->returns.front() == "i32"_yulstring, "Invalid type " + builtin->returns.front().str());
|
||||||
|
call = wasm::BuiltinCall{"i64.extend_i32_u", make_vector<wasm::Expression>(std::move(call))};
|
||||||
|
}
|
||||||
|
return {std::move(call)};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this function returns multiple values, then the first one will
|
// If this function returns multiple values, then the first one will
|
||||||
// be returned in the expression itself and the others in global variables.
|
// be returned in the expression itself and the others in global variables.
|
||||||
// The values have to be used right away in an assignment or variable declaration,
|
// The values have to be used right away in an assignment or variable declaration,
|
||||||
// so it is handled there.
|
// so it is handled there.
|
||||||
return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)};
|
|
||||||
|
wasm::FunctionCall funCall{_call.functionName.name.str(), visit(_call.arguments)};
|
||||||
|
if (typeConversionNeeded)
|
||||||
|
// Inject type conversion if needed on the fly. This is just a temporary measure
|
||||||
|
// and can be removed once we have proper types in Yul.
|
||||||
|
return injectTypeConversionIfNeeded(std::move(funCall));
|
||||||
|
else
|
||||||
|
return {std::move(funCall)};
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(Identifier const& _identifier)
|
wasm::Expression EWasmCodeTransform::operator()(Identifier const& _identifier)
|
||||||
@ -191,7 +209,16 @@ wasm::Expression EWasmCodeTransform::operator()(yul::Instruction const&)
|
|||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(If const& _if)
|
wasm::Expression EWasmCodeTransform::operator()(If const& _if)
|
||||||
{
|
{
|
||||||
return wasm::If{visit(*_if.condition), visit(_if.body.statements), {}};
|
// TODO converting i64 to i32 might not always be needed.
|
||||||
|
|
||||||
|
vector<wasm::Expression> args;
|
||||||
|
args.emplace_back(visitReturnByValue(*_if.condition));
|
||||||
|
args.emplace_back(wasm::Literal{0});
|
||||||
|
return wasm::If{
|
||||||
|
make_unique<wasm::Expression>(wasm::BuiltinCall{"i64.ne", std::move(args)}),
|
||||||
|
visit(_if.body.statements),
|
||||||
|
{}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(Switch const& _switch)
|
wasm::Expression EWasmCodeTransform::operator()(Switch const& _switch)
|
||||||
@ -248,8 +275,7 @@ wasm::Expression EWasmCodeTransform::operator()(ForLoop const& _for)
|
|||||||
|
|
||||||
wasm::Loop loop;
|
wasm::Loop loop;
|
||||||
loop.statements = visit(_for.pre.statements);
|
loop.statements = visit(_for.pre.statements);
|
||||||
loop.statements.emplace_back(wasm::BuiltinCall{"br_if", make_vector<wasm::Expression>(
|
loop.statements.emplace_back(wasm::BreakIf{wasm::Label{breakLabel}, make_unique<wasm::Expression>(
|
||||||
wasm::Label{breakLabel},
|
|
||||||
wasm::BuiltinCall{"i64.eqz", make_vector<wasm::Expression>(
|
wasm::BuiltinCall{"i64.eqz", make_vector<wasm::Expression>(
|
||||||
visitReturnByValue(*_for.condition)
|
visitReturnByValue(*_for.condition)
|
||||||
)}
|
)}
|
||||||
@ -267,7 +293,7 @@ wasm::Expression EWasmCodeTransform::operator()(Break const&)
|
|||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(Continue const&)
|
wasm::Expression EWasmCodeTransform::operator()(Continue const&)
|
||||||
{
|
{
|
||||||
return wasm::Continue{wasm::Label{m_breakContinueLabelNames.top().second}};
|
return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().second}};
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(Block const& _block)
|
wasm::Expression EWasmCodeTransform::operator()(Block const& _block)
|
||||||
@ -337,6 +363,40 @@ wasm::FunctionDefinition EWasmCodeTransform::translateFunction(yul::FunctionDefi
|
|||||||
return fun;
|
return fun;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wasm::Expression EWasmCodeTransform::injectTypeConversionIfNeeded(wasm::FunctionCall _call) const
|
||||||
|
{
|
||||||
|
wasm::FunctionImport const& import = m_functionsToImport.at(YulString{_call.functionName});
|
||||||
|
for (size_t i = 0; i < _call.arguments.size(); ++i)
|
||||||
|
if (import.paramTypes.at(i) == "i32")
|
||||||
|
_call.arguments[i] = wasm::BuiltinCall{"i32.wrap_i64", make_vector<wasm::Expression>(std::move(_call.arguments[i]))};
|
||||||
|
else
|
||||||
|
yulAssert(import.paramTypes.at(i) == "i64", "Unknown type " + import.paramTypes.at(i));
|
||||||
|
|
||||||
|
if (import.returnType && *import.returnType != "i64")
|
||||||
|
{
|
||||||
|
yulAssert(*import.returnType == "i32", "Invalid type " + *import.returnType);
|
||||||
|
return wasm::BuiltinCall{"i64.extend_i32_u", make_vector<wasm::Expression>(std::move(_call))};
|
||||||
|
}
|
||||||
|
return {std::move(_call)};
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<wasm::Expression> EWasmCodeTransform::injectTypeConversionIfNeeded(
|
||||||
|
vector<wasm::Expression> _arguments,
|
||||||
|
vector<Type> const& _parameterTypes
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < _arguments.size(); ++i)
|
||||||
|
if (_parameterTypes.at(i) == "i32"_yulstring)
|
||||||
|
_arguments[i] = wasm::BuiltinCall{"i32.wrap_i64", make_vector<wasm::Expression>(std::move(_arguments[i]))};
|
||||||
|
else
|
||||||
|
yulAssert(
|
||||||
|
_parameterTypes.at(i).empty() || _parameterTypes.at(i) == "i64"_yulstring,
|
||||||
|
"Unknown type " + _parameterTypes.at(i).str()
|
||||||
|
);
|
||||||
|
|
||||||
|
return _arguments;
|
||||||
|
}
|
||||||
|
|
||||||
string EWasmCodeTransform::newLabel()
|
string EWasmCodeTransform::newLabel()
|
||||||
{
|
{
|
||||||
return m_nameDispenser.newName("label_"_yulstring).str();
|
return m_nameDispenser.newName("label_"_yulstring).str();
|
||||||
|
@ -35,7 +35,7 @@ struct AsmAnalysisInfo;
|
|||||||
class EWasmCodeTransform: public boost::static_visitor<wasm::Expression>
|
class EWasmCodeTransform: public boost::static_visitor<wasm::Expression>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::string run(Dialect const& _dialect, yul::Block const& _ast);
|
static wasm::Module run(Dialect const& _dialect, yul::Block const& _ast);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
wasm::Expression operator()(yul::Instruction const& _instruction);
|
wasm::Expression operator()(yul::Instruction const& _instruction);
|
||||||
@ -82,6 +82,12 @@ private:
|
|||||||
|
|
||||||
wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef);
|
wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef);
|
||||||
|
|
||||||
|
wasm::Expression injectTypeConversionIfNeeded(wasm::FunctionCall _call) const;
|
||||||
|
std::vector<wasm::Expression> injectTypeConversionIfNeeded(
|
||||||
|
std::vector<wasm::Expression> _arguments,
|
||||||
|
std::vector<yul::Type> const& _parameterTypes
|
||||||
|
) const;
|
||||||
|
|
||||||
std::string newLabel();
|
std::string newLabel();
|
||||||
/// Makes sure that there are at least @a _amount global variables.
|
/// Makes sure that there are at least @a _amount global variables.
|
||||||
void allocateGlobals(size_t _amount);
|
void allocateGlobals(size_t _amount);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user