mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
commit
7cb129b16b
@ -9,20 +9,20 @@ version: 2.1
|
|||||||
parameters:
|
parameters:
|
||||||
ubuntu-2004-docker-image:
|
ubuntu-2004-docker-image:
|
||||||
type: string
|
type: string
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-9
|
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-10
|
||||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:3d8a912e8e78e98cd217955d06d98608ad60adc67728d4c3a569991235fa1abb"
|
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:e61939751ff9777307857361f712b581bfc8a8aaae75fab7b50febc764710587"
|
||||||
ubuntu-2004-clang-docker-image:
|
ubuntu-2004-clang-docker-image:
|
||||||
type: string
|
type: string
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-9
|
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-10
|
||||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:a1ba002cae17279d1396a898b04e4e9c45602ad881295db3e2f484a7e24f6f43"
|
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:0de8c68f084120b2a165406e3a0c2aab58b32f5b7182c2322245245f1d243b8d"
|
||||||
ubuntu-1604-clang-ossfuzz-docker-image:
|
ubuntu-1604-clang-ossfuzz-docker-image:
|
||||||
type: string
|
type: string
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-14
|
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-15
|
||||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:f353823cce2f6cd2f9f1459d86cd76fdfc551a0261d87626615ea6c1d8f90587"
|
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:87f1a57586eec194a6217ab624efc69d3d9af2f7ac8ca36497ad57488c2f08ae"
|
||||||
emscripten-docker-image:
|
emscripten-docker-image:
|
||||||
type: string
|
type: string
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-8
|
# solbuildpackpusher/solidity-buildpack-deps:emscripten-9
|
||||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:842d6074e0e7e5355c89122c1cafc1fdb59696596750e7d56e5f35c0d883ad59"
|
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d51534dfdd05ece86f69ed7beafd68c15b88606da00a4b7fe2873ccfbd0dce24"
|
||||||
evm-version:
|
evm-version:
|
||||||
type: string
|
type: string
|
||||||
default: london
|
default: london
|
||||||
@ -53,8 +53,11 @@ commands:
|
|||||||
workflow_info=$(curl --silent "https://circleci.com/api/v2/workflow/${CIRCLE_WORKFLOW_ID}") || true
|
workflow_info=$(curl --silent "https://circleci.com/api/v2/workflow/${CIRCLE_WORKFLOW_ID}") || true
|
||||||
workflow_name=$(echo "$workflow_info" | grep -E '"\s*name"\s*:\s*".*"' | cut -d \" -f 4 || echo "$CIRCLE_WORKFLOW_ID")
|
workflow_name=$(echo "$workflow_info" | grep -E '"\s*name"\s*:\s*".*"' | cut -d \" -f 4 || echo "$CIRCLE_WORKFLOW_ID")
|
||||||
|
|
||||||
[[ "<< parameters.event >>" == "failure" ]] && message=" ❌ [${workflow_name}] Job **${CIRCLE_JOB}** failed on **${CIRCLE_BRANCH}**. Please see [build ${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
|
[[ $CIRCLE_NODE_TOTAL == 1 ]] && job="**${CIRCLE_JOB}**"
|
||||||
[[ "<< parameters.event >>" == "success" ]] && message=" ✅ [${workflow_name}] Job **${CIRCLE_JOB}** succeeded on **${CIRCLE_BRANCH}**. Please see [build ${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
|
[[ $CIRCLE_NODE_TOTAL != 1 ]] && job="**${CIRCLE_JOB}** (run $((CIRCLE_NODE_INDEX + 1))/${CIRCLE_NODE_TOTAL})"
|
||||||
|
|
||||||
|
[[ "<< parameters.event >>" == "failure" ]] && message=" ❌ [${workflow_name}] Job ${job} failed on **${CIRCLE_BRANCH}**. Please see [build ${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
|
||||||
|
[[ "<< parameters.event >>" == "success" ]] && message=" ✅ [${workflow_name}] Job ${job} succeeded on **${CIRCLE_BRANCH}**. Please see [build ${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
|
||||||
|
|
||||||
curl "https://api.gitter.im/v1/rooms/${GITTER_NOTIFY_ROOM_ID}/chatMessages" \
|
curl "https://api.gitter.im/v1/rooms/${GITTER_NOTIFY_ROOM_ID}/chatMessages" \
|
||||||
--request POST \
|
--request POST \
|
||||||
@ -357,6 +360,14 @@ defaults:
|
|||||||
TERM: xterm
|
TERM: xterm
|
||||||
MAKEFLAGS: -j5
|
MAKEFLAGS: -j5
|
||||||
|
|
||||||
|
- base_osx_large: &base_osx_large
|
||||||
|
macos:
|
||||||
|
xcode: "13.2.0"
|
||||||
|
resource_class: large
|
||||||
|
environment:
|
||||||
|
TERM: xterm
|
||||||
|
MAKEFLAGS: -j10
|
||||||
|
|
||||||
- base_ems_large: &base_ems_large
|
- base_ems_large: &base_ems_large
|
||||||
docker:
|
docker:
|
||||||
- image: << pipeline.parameters.emscripten-docker-image >>
|
- image: << pipeline.parameters.emscripten-docker-image >>
|
||||||
@ -464,6 +475,98 @@ defaults:
|
|||||||
requires:
|
requires:
|
||||||
- b_win_release
|
- b_win_release
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Parameterized Job Templates
|
||||||
|
|
||||||
|
# Separate compile-only runs of those external tests where a full run takes much longer.
|
||||||
|
- job_ems_compile_ext_colony: &job_ems_compile_ext_colony
|
||||||
|
<<: *workflow_emscripten
|
||||||
|
name: t_ems_compile_ext_colony
|
||||||
|
project: colony
|
||||||
|
binary_type: solcjs
|
||||||
|
compile_only: 1
|
||||||
|
nodejs_version: '14'
|
||||||
|
- job_native_compile_ext_gnosis: &job_native_compile_ext_gnosis
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_compile_ext_gnosis
|
||||||
|
project: gnosis
|
||||||
|
binary_type: native
|
||||||
|
compile_only: 1
|
||||||
|
nodejs_version: '14'
|
||||||
|
|
||||||
|
- job_native_test_ext_gnosis: &job_native_test_ext_gnosis
|
||||||
|
<<: *workflow_emscripten
|
||||||
|
name: t_native_test_ext_gnosis
|
||||||
|
project: gnosis
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests do not start on node.js 14 ("ganache-cli exited early with code 1").
|
||||||
|
nodejs_version: '12'
|
||||||
|
- job_native_test_ext_gnosis_v2: &job_native_test_ext_gnosis_v2
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_test_ext_gnosis_v2
|
||||||
|
project: gnosis-v2
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests do not start on node.js 14 ("ganache-cli exited early with code 1").
|
||||||
|
nodejs_version: '12'
|
||||||
|
- job_native_test_ext_zeppelin: &job_native_test_ext_zeppelin
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_test_ext_zeppelin
|
||||||
|
project: zeppelin
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
||||||
|
nodejs_version: '16'
|
||||||
|
resource_class: large
|
||||||
|
- job_native_test_ext_ens: &job_native_test_ext_ens
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_test_ext_ens
|
||||||
|
project: ens
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
||||||
|
nodejs_version: '16'
|
||||||
|
- job_native_test_ext_trident: &job_native_test_ext_trident
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_test_ext_trident
|
||||||
|
project: trident
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
||||||
|
nodejs_version: '16'
|
||||||
|
- job_native_test_ext_euler: &job_native_test_ext_euler
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_test_ext_euler
|
||||||
|
project: euler
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
||||||
|
nodejs_version: '16'
|
||||||
|
- job_native_test_ext_yield_liquidator: &job_native_test_ext_yield_liquidator
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_test_ext_yield_liquidator
|
||||||
|
project: yield-liquidator
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
||||||
|
nodejs_version: '16'
|
||||||
|
- job_native_test_ext_bleeps: &job_native_test_ext_bleeps
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_test_ext_bleeps
|
||||||
|
project: bleeps
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
||||||
|
nodejs_version: '16'
|
||||||
|
resource_class: medium
|
||||||
|
- job_native_test_ext_pool_together: &job_native_test_ext_pool_together
|
||||||
|
<<: *workflow_ubuntu2004_static
|
||||||
|
name: t_native_test_ext_pool_together
|
||||||
|
project: pool-together
|
||||||
|
binary_type: native
|
||||||
|
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
||||||
|
nodejs_version: '16'
|
||||||
|
- job_ems_test_ext_colony: &job_ems_test_ext_colony
|
||||||
|
<<: *workflow_emscripten
|
||||||
|
name: t_ems_test_ext_colony
|
||||||
|
project: colony
|
||||||
|
binary_type: solcjs
|
||||||
|
nodejs_version: '14'
|
||||||
|
resource_class: medium
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
@ -791,11 +894,11 @@ jobs:
|
|||||||
- gitter_notify_failure_unless_pr
|
- gitter_notify_failure_unless_pr
|
||||||
|
|
||||||
b_osx:
|
b_osx:
|
||||||
<<: *base_osx
|
<<: *base_osx_large
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
CMAKE_BUILD_TYPE: Release
|
CMAKE_BUILD_TYPE: Release
|
||||||
MAKEFLAGS: -j 5
|
MAKEFLAGS: -j10
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@ -897,7 +1000,7 @@ jobs:
|
|||||||
|
|
||||||
t_ubu_soltest_all: &t_ubu_soltest_all
|
t_ubu_soltest_all: &t_ubu_soltest_all
|
||||||
<<: *base_ubuntu2004
|
<<: *base_ubuntu2004
|
||||||
parallelism: 15 # 7 EVM versions, each with/without optimization + 1 ABIv1/@nooptions run
|
parallelism: 50
|
||||||
<<: *steps_soltest_all
|
<<: *steps_soltest_all
|
||||||
|
|
||||||
t_ubu_lsp: &t_ubu_lsp
|
t_ubu_lsp: &t_ubu_lsp
|
||||||
@ -906,6 +1009,7 @@ jobs:
|
|||||||
|
|
||||||
t_archlinux_soltest: &t_archlinux_soltest
|
t_archlinux_soltest: &t_archlinux_soltest
|
||||||
<<: *base_archlinux
|
<<: *base_archlinux
|
||||||
|
parallelism: 20
|
||||||
environment:
|
environment:
|
||||||
EVM: << pipeline.parameters.evm-version >>
|
EVM: << pipeline.parameters.evm-version >>
|
||||||
OPTIMIZE: 0
|
OPTIMIZE: 0
|
||||||
@ -924,6 +1028,7 @@ jobs:
|
|||||||
|
|
||||||
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
|
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
|
||||||
<<: *base_ubuntu2004
|
<<: *base_ubuntu2004
|
||||||
|
parallelism: 20
|
||||||
environment:
|
environment:
|
||||||
EVM: << pipeline.parameters.evm-version >>
|
EVM: << pipeline.parameters.evm-version >>
|
||||||
SOLTEST_FLAGS: --enforce-via-yul
|
SOLTEST_FLAGS: --enforce-via-yul
|
||||||
@ -933,6 +1038,7 @@ jobs:
|
|||||||
|
|
||||||
t_ubu_clang_soltest: &t_ubu_clang_soltest
|
t_ubu_clang_soltest: &t_ubu_clang_soltest
|
||||||
<<: *base_ubuntu2004_clang
|
<<: *base_ubuntu2004_clang
|
||||||
|
parallelism: 20
|
||||||
environment:
|
environment:
|
||||||
EVM: << pipeline.parameters.evm-version >>
|
EVM: << pipeline.parameters.evm-version >>
|
||||||
OPTIMIZE: 0
|
OPTIMIZE: 0
|
||||||
@ -960,6 +1066,7 @@ jobs:
|
|||||||
|
|
||||||
t_ubu_asan_soltest:
|
t_ubu_asan_soltest:
|
||||||
<<: *base_ubuntu2004
|
<<: *base_ubuntu2004
|
||||||
|
parallelism: 20
|
||||||
environment:
|
environment:
|
||||||
EVM: << pipeline.parameters.evm-version >>
|
EVM: << pipeline.parameters.evm-version >>
|
||||||
OPTIMIZE: 0
|
OPTIMIZE: 0
|
||||||
@ -969,6 +1076,7 @@ jobs:
|
|||||||
|
|
||||||
t_ubu_asan_clang_soltest:
|
t_ubu_asan_clang_soltest:
|
||||||
<<: *base_ubuntu2004_clang
|
<<: *base_ubuntu2004_clang
|
||||||
|
parallelism: 20
|
||||||
environment:
|
environment:
|
||||||
EVM: << pipeline.parameters.evm-version >>
|
EVM: << pipeline.parameters.evm-version >>
|
||||||
OPTIMIZE: 0
|
OPTIMIZE: 0
|
||||||
@ -978,6 +1086,7 @@ jobs:
|
|||||||
|
|
||||||
t_ubu_ubsan_clang_soltest:
|
t_ubu_ubsan_clang_soltest:
|
||||||
<<: *base_ubuntu2004_clang
|
<<: *base_ubuntu2004_clang
|
||||||
|
parallelism: 20
|
||||||
environment:
|
environment:
|
||||||
EVM: << pipeline.parameters.evm-version >>
|
EVM: << pipeline.parameters.evm-version >>
|
||||||
<<: *steps_soltest
|
<<: *steps_soltest
|
||||||
@ -1318,53 +1427,20 @@ workflows:
|
|||||||
- t_ems_solcjs: *workflow_emscripten
|
- t_ems_solcjs: *workflow_emscripten
|
||||||
- t_ems_ext_hardhat: *workflow_emscripten
|
- t_ems_ext_hardhat: *workflow_emscripten
|
||||||
|
|
||||||
# Separate compile-only runs of those external tests where a full run takes much longer.
|
- t_ems_ext: *job_ems_compile_ext_colony
|
||||||
- t_ems_ext:
|
- t_ems_ext: *job_native_compile_ext_gnosis
|
||||||
<<: *workflow_emscripten
|
|
||||||
name: t_ems_compile_ext_colony
|
|
||||||
project: colony
|
|
||||||
binary_type: solcjs
|
|
||||||
compile_only: 1
|
|
||||||
nodejs_version: '14'
|
|
||||||
- t_ems_ext:
|
|
||||||
<<: *workflow_ubuntu2004_static
|
|
||||||
name: t_native_compile_ext_gnosis
|
|
||||||
project: gnosis
|
|
||||||
binary_type: native
|
|
||||||
compile_only: 1
|
|
||||||
nodejs_version: '14'
|
|
||||||
|
|
||||||
# FIXME: Gnosis tests are pretty flaky right now. They often fail on CircleCI due to random ProviderError
|
# FIXME: Gnosis tests are pretty flaky right now. They often fail on CircleCI due to random ProviderError
|
||||||
# and there are also other less frequent problems. See https://github.com/gnosis/safe-contracts/issues/216.
|
# and there are also other less frequent problems. See https://github.com/gnosis/safe-contracts/issues/216.
|
||||||
#- t_ems_ext:
|
#-t_ems_ext: *job_native_test_ext_gnosis
|
||||||
# <<: *workflow_emscripten
|
- t_ems_ext: *job_native_test_ext_gnosis_v2
|
||||||
# name: t_native_test_ext_gnosis
|
- t_ems_ext: *job_native_test_ext_zeppelin
|
||||||
# project: gnosis
|
- t_ems_ext: *job_native_test_ext_ens
|
||||||
# binary_type: native
|
- t_ems_ext: *job_native_test_ext_trident
|
||||||
# # NOTE: Tests do not start on node.js 14 ("ganache-cli exited early with code 1").
|
- t_ems_ext: *job_native_test_ext_euler
|
||||||
# nodejs_version: '12'
|
- t_ems_ext: *job_native_test_ext_yield_liquidator
|
||||||
- t_ems_ext:
|
- t_ems_ext: *job_native_test_ext_bleeps
|
||||||
<<: *workflow_ubuntu2004_static
|
- t_ems_ext: *job_native_test_ext_pool_together
|
||||||
name: t_native_test_ext_gnosis_v2
|
|
||||||
project: gnosis-v2
|
|
||||||
binary_type: native
|
|
||||||
# NOTE: Tests do not start on node.js 14 ("ganache-cli exited early with code 1").
|
|
||||||
nodejs_version: '12'
|
|
||||||
- t_ems_ext:
|
|
||||||
<<: *workflow_ubuntu2004_static
|
|
||||||
name: t_native_test_ext_zeppelin
|
|
||||||
project: zeppelin
|
|
||||||
binary_type: native
|
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
|
||||||
resource_class: large
|
|
||||||
- t_ems_ext:
|
|
||||||
<<: *workflow_ubuntu2004_static
|
|
||||||
name: t_native_test_ext_ens
|
|
||||||
project: ens
|
|
||||||
binary_type: native
|
|
||||||
# NOTE: One of the dependencies (fsevents) fails to build its native extension on node.js 12+.
|
|
||||||
nodejs_version: '10'
|
|
||||||
|
|
||||||
# Windows build and tests
|
# Windows build and tests
|
||||||
- b_win: *workflow_trigger_on_tags
|
- b_win: *workflow_trigger_on_tags
|
||||||
@ -1374,18 +1450,23 @@ workflows:
|
|||||||
|
|
||||||
# Bytecode comparison:
|
# Bytecode comparison:
|
||||||
- b_bytecode_ubu:
|
- b_bytecode_ubu:
|
||||||
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_ubu
|
- b_ubu
|
||||||
- b_bytecode_win:
|
- b_bytecode_win:
|
||||||
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_win
|
- b_win
|
||||||
- b_bytecode_osx:
|
- b_bytecode_osx:
|
||||||
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_osx
|
- b_osx
|
||||||
- b_bytecode_ems:
|
- b_bytecode_ems:
|
||||||
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_ems
|
- b_ems
|
||||||
- t_bytecode_compare:
|
- t_bytecode_compare:
|
||||||
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_bytecode_ubu
|
- b_bytecode_ubu
|
||||||
- b_bytecode_win
|
- b_bytecode_win
|
||||||
@ -1425,10 +1506,4 @@ workflows:
|
|||||||
|
|
||||||
# Emscripten build and tests that take more than 15 minutes to execute
|
# Emscripten build and tests that take more than 15 minutes to execute
|
||||||
- b_ems: *workflow_trigger_on_tags
|
- b_ems: *workflow_trigger_on_tags
|
||||||
- t_ems_ext:
|
- t_ems_ext: *job_ems_test_ext_colony
|
||||||
<<: *workflow_emscripten
|
|
||||||
name: t_ems_test_ext_colony
|
|
||||||
project: colony
|
|
||||||
binary_type: solcjs
|
|
||||||
nodejs_version: '14'
|
|
||||||
resource_class: medium
|
|
||||||
|
@ -61,11 +61,11 @@ then
|
|||||||
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
||||||
|
|
||||||
# z3
|
# z3
|
||||||
z3_version="4.8.13"
|
z3_version="4.8.14"
|
||||||
z3_dir="z3-${z3_version}-x64-osx-10.16"
|
z3_dir="z3-${z3_version}-x64-osx-10.16"
|
||||||
z3_package="${z3_dir}.zip"
|
z3_package="${z3_dir}.zip"
|
||||||
wget "https://github.com/Z3Prover/z3/releases/download/z3-${z3_version}/${z3_package}"
|
wget "https://github.com/Z3Prover/z3/releases/download/z3-${z3_version}/${z3_package}"
|
||||||
validate_checksum "$z3_package" 191b26be2b617b2dffffce139d77abcd7e584859efbc10a58d01a1d7830697a4
|
validate_checksum "$z3_package" 1341671670e0c4e72da80815128a68975ee90816d50ceaf6bd820f06babe2cfd
|
||||||
unzip "$z3_package"
|
unzip "$z3_package"
|
||||||
rm "$z3_package"
|
rm "$z3_package"
|
||||||
cp "${z3_dir}/bin/libz3.a" /usr/local/lib
|
cp "${z3_dir}/bin/libz3.a" /usr/local/lib
|
||||||
|
@ -50,19 +50,48 @@ mkdir -p test_results
|
|||||||
ulimit -s 16384
|
ulimit -s 16384
|
||||||
|
|
||||||
get_logfile_basename() {
|
get_logfile_basename() {
|
||||||
|
local run="$1"
|
||||||
local filename="${EVM}"
|
local filename="${EVM}"
|
||||||
test "${OPTIMIZE}" = "1" && filename="${filename}_opt"
|
test "${OPTIMIZE}" = "1" && filename="${filename}_opt"
|
||||||
test "${ABI_ENCODER_V1}" = "1" && filename="${filename}_abiv1"
|
test "${ABI_ENCODER_V1}" = "1" && filename="${filename}_abiv1"
|
||||||
|
filename="${filename}_${run}"
|
||||||
|
|
||||||
echo -ne "${filename}"
|
echo -ne "${filename}"
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_TEST_ARGS=("--color_output=no" "--show_progress=yes" "--logger=JUNIT,error,test_results/$(get_logfile_basename).xml" "${BOOST_TEST_ARGS[@]}")
|
[ -z "$CIRCLE_NODE_TOTAL" ] || [ "$CIRCLE_NODE_TOTAL" = 0 ] && CIRCLE_NODE_TOTAL=1
|
||||||
|
[ -z "$CIRCLE_NODE_INDEX" ] && CIRCLE_NODE_INDEX=0
|
||||||
|
[ -z "$INDEX_SHIFT" ] && INDEX_SHIFT=0
|
||||||
|
|
||||||
|
# Multiply by a prime number to get better spread, just in case
|
||||||
|
# long-running test cases are next to each other.
|
||||||
|
CIRCLE_NODE_INDEX=$(((CIRCLE_NODE_INDEX + 23 * INDEX_SHIFT) % CIRCLE_NODE_TOTAL))
|
||||||
|
|
||||||
|
CPUs=3
|
||||||
|
PIDs=()
|
||||||
|
for run in $(seq 0 $((CPUs - 1)))
|
||||||
|
do
|
||||||
|
BOOST_TEST_ARGS_RUN=(
|
||||||
|
"--color_output=no"
|
||||||
|
"--show_progress=yes"
|
||||||
|
"--logger=JUNIT,error,test_results/$(get_logfile_basename "$run").xml"
|
||||||
|
"${BOOST_TEST_ARGS[@]}"
|
||||||
|
)
|
||||||
SOLTEST_ARGS=("--evm-version=$EVM" "${SOLTEST_FLAGS[@]}")
|
SOLTEST_ARGS=("--evm-version=$EVM" "${SOLTEST_FLAGS[@]}")
|
||||||
|
|
||||||
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS+=(--optimize)
|
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS+=(--optimize)
|
||||||
test "${ABI_ENCODER_V1}" = "1" && SOLTEST_ARGS+=(--abiencoderv1)
|
test "${ABI_ENCODER_V1}" = "1" && SOLTEST_ARGS+=(--abiencoderv1)
|
||||||
|
|
||||||
echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS[*]} -- ${SOLTEST_ARGS[*]}"
|
BATCH_ARGS=("--batches" "$((CPUs * CIRCLE_NODE_TOTAL))" "--selected-batch" "$((CPUs * CIRCLE_NODE_INDEX + run))")
|
||||||
|
|
||||||
"${REPODIR}/build/test/soltest" "${BOOST_TEST_ARGS[@]}" -- "${SOLTEST_ARGS[@]}"
|
echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS_RUN[*]} -- ${SOLTEST_ARGS[*]}"
|
||||||
|
|
||||||
|
"${REPODIR}/build/test/soltest" -l test_suite "${BOOST_TEST_ARGS_RUN[@]}" -- "${SOLTEST_ARGS[@]}" "${BATCH_ARGS[@]}" &
|
||||||
|
PIDs+=($!)
|
||||||
|
done
|
||||||
|
|
||||||
|
# wait for individual processes to get their exit status
|
||||||
|
for pid in ${PIDs[*]}
|
||||||
|
do
|
||||||
|
wait "$pid"
|
||||||
|
done
|
||||||
|
@ -31,30 +31,21 @@ REPODIR="$(realpath "$(dirname "$0")"/..)"
|
|||||||
# shellcheck source=scripts/common.sh
|
# shellcheck source=scripts/common.sh
|
||||||
source "${REPODIR}/scripts/common.sh"
|
source "${REPODIR}/scripts/common.sh"
|
||||||
|
|
||||||
# NOTE: If you add/remove values, remember to update `parallelism` setting in CircleCI config.
|
|
||||||
EVM_VALUES=(homestead byzantium constantinople petersburg istanbul berlin london)
|
EVM_VALUES=(homestead byzantium constantinople petersburg istanbul berlin london)
|
||||||
DEFAULT_EVM=london
|
DEFAULT_EVM=london
|
||||||
[[ " ${EVM_VALUES[*]} " =~ $DEFAULT_EVM ]]
|
[[ " ${EVM_VALUES[*]} " =~ $DEFAULT_EVM ]]
|
||||||
OPTIMIZE_VALUES=(0 1)
|
OPTIMIZE_VALUES=(0 1)
|
||||||
STEPS=$(( 1 + ${#EVM_VALUES[@]} * ${#OPTIMIZE_VALUES[@]} ))
|
|
||||||
|
|
||||||
RUN_STEPS=$(circleci_select_steps "$(seq "$STEPS")")
|
|
||||||
printTask "Running steps $RUN_STEPS..."
|
|
||||||
|
|
||||||
STEP=1
|
|
||||||
|
|
||||||
|
|
||||||
# Run for ABI encoder v1, without SMTChecker tests.
|
# Run for ABI encoder v1, without SMTChecker tests.
|
||||||
if circleci_step_selected "$RUN_STEPS" "$STEP"
|
|
||||||
then
|
|
||||||
EVM="${DEFAULT_EVM}" \
|
EVM="${DEFAULT_EVM}" \
|
||||||
OPTIMIZE=1 \
|
OPTIMIZE=1 \
|
||||||
ABI_ENCODER_V1=1 \
|
ABI_ENCODER_V1=1 \
|
||||||
BOOST_TEST_ARGS="-t !smtCheckerTests" \
|
BOOST_TEST_ARGS="-t !smtCheckerTests" \
|
||||||
"${REPODIR}/.circleci/soltest.sh"
|
"${REPODIR}/.circleci/soltest.sh"
|
||||||
fi
|
|
||||||
((++STEP))
|
|
||||||
|
|
||||||
|
# We shift the batch index so that long-running tests
|
||||||
|
# do not always run in the same executor for all EVM versions
|
||||||
|
INDEX_SHIFT=0
|
||||||
for OPTIMIZE in "${OPTIMIZE_VALUES[@]}"
|
for OPTIMIZE in "${OPTIMIZE_VALUES[@]}"
|
||||||
do
|
do
|
||||||
for EVM in "${EVM_VALUES[@]}"
|
for EVM in "${EVM_VALUES[@]}"
|
||||||
@ -68,16 +59,13 @@ do
|
|||||||
DISABLE_SMTCHECKER=""
|
DISABLE_SMTCHECKER=""
|
||||||
[ "${OPTIMIZE}" != "0" ] && DISABLE_SMTCHECKER="-t !smtCheckerTests"
|
[ "${OPTIMIZE}" != "0" ] && DISABLE_SMTCHECKER="-t !smtCheckerTests"
|
||||||
|
|
||||||
if circleci_step_selected "$RUN_STEPS" "$STEP"
|
|
||||||
then
|
|
||||||
EVM="$EVM" \
|
EVM="$EVM" \
|
||||||
OPTIMIZE="$OPTIMIZE" \
|
OPTIMIZE="$OPTIMIZE" \
|
||||||
SOLTEST_FLAGS="$SOLTEST_FLAGS $ENFORCE_GAS_ARGS $EWASM_ARGS" \
|
SOLTEST_FLAGS="$SOLTEST_FLAGS $ENFORCE_GAS_ARGS $EWASM_ARGS" \
|
||||||
BOOST_TEST_ARGS="-t !@nooptions $DISABLE_SMTCHECKER" \
|
BOOST_TEST_ARGS="-t !@nooptions $DISABLE_SMTCHECKER" \
|
||||||
|
INDEX_SHIFT="$INDEX_SHIFT" \
|
||||||
"${REPODIR}/.circleci/soltest.sh"
|
"${REPODIR}/.circleci/soltest.sh"
|
||||||
fi
|
|
||||||
((++STEP))
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
((STEP == STEPS + 1)) || assertFail "Step counter not properly adjusted!"
|
INDEX_SHIFT=$((INDEX_SHIFT + 1))
|
||||||
|
done
|
||||||
|
done
|
@ -65,7 +65,7 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/licens
|
|||||||
|
|
||||||
include(EthOptions)
|
include(EthOptions)
|
||||||
configure_project(TESTS)
|
configure_project(TESTS)
|
||||||
set(LATEST_Z3_VERSION "4.8.13")
|
set(LATEST_Z3_VERSION "4.8.14")
|
||||||
set(MINIMUM_Z3_VERSION "4.8.0")
|
set(MINIMUM_Z3_VERSION "4.8.0")
|
||||||
find_package(Z3)
|
find_package(Z3)
|
||||||
if (${Z3_FOUND})
|
if (${Z3_FOUND})
|
||||||
|
16
Changelog.md
16
Changelog.md
@ -10,10 +10,24 @@ Breaking changes:
|
|||||||
|
|
||||||
### 0.8.12 (unreleased)
|
### 0.8.12 (unreleased)
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* General: Support ``ContractName.functionName`` for ``abi.encodeCall``, in addition to external function pointers.
|
||||||
|
* General: Add equality-comparison operators for external function types.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* Yul Optimizer: Remove ``mstore`` and ``sstore`` operations if the slot already contains the same value.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
* Antlr Grammar: Allow builtin names in ``yulPath`` to support ``.address`` in function pointers.
|
||||||
|
* Code Generator: Fix ICE when accessing the members of external functions occupying more than two stack slots.
|
||||||
|
* Code Generator: Fix ICE when doing an explicit conversion from ``string calldata`` to ``bytes``.
|
||||||
|
* Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis.
|
||||||
|
* Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the parent contract contains immutable variables.
|
||||||
|
* IR Generator: Fix IR syntax error when copying storage arrays of structs containing functions.
|
||||||
|
* Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different.
|
||||||
|
* TypeChecker: Fix ICE when a constant variable declaration forward references a struct.
|
||||||
|
|
||||||
|
|
||||||
Solc-Js:
|
Solc-Js:
|
||||||
@ -2161,7 +2175,7 @@ Features:
|
|||||||
* Internal: Inline assembly usable by the code generator.
|
* Internal: Inline assembly usable by the code generator.
|
||||||
* Commandline interface: Using ``-`` as filename allows reading from stdin.
|
* Commandline interface: Using ``-`` as filename allows reading from stdin.
|
||||||
* Interface JSON: Fallback function is now part of the ABI.
|
* Interface JSON: Fallback function is now part of the ABI.
|
||||||
* Interface: Version string now *semver* compatible.
|
* Interface: Version string now *SemVer* compatible.
|
||||||
* Code generator: Do not provide "new account gas" if we know the called account exists.
|
* Code generator: Do not provide "new account gas" if we know the called account exists.
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -54,7 +54,8 @@
|
|||||||
- [ ] Run ``./scripts/docker_deploy_manual.sh v$VERSION``).
|
- [ ] Run ``./scripts/docker_deploy_manual.sh v$VERSION``).
|
||||||
|
|
||||||
### PPA
|
### PPA
|
||||||
- [ ] Change ``scripts/release_ppa.sh`` to match your key's email and key id.
|
- [ ] Make sure the ``ethereum/cpp-build-deps`` PPA repository contains libz3-static-dev builds for all current versions of ubuntu. If not run ``scripts/deps-ppa/static-z3.sh`` (after changing email address and key id and adding the missing ubuntu version) and wait for the builds to succeed before continuing.
|
||||||
|
- [ ] Change ``scripts/release_ppa.sh`` to match your key's email and key id; double-check that ``DISTRIBUTIONS`` contains the most recent versions.
|
||||||
- [ ] Run ``scripts/release_ppa.sh v$VERSION`` to create the PPA release (you need the relevant openssl key).
|
- [ ] Run ``scripts/release_ppa.sh v$VERSION`` to create the PPA release (you need the relevant openssl key).
|
||||||
- [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty``, ``Xenial`` and ``Bionic`` while selecting ``Copy existing binaries``.
|
- [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty``, ``Xenial`` and ``Bionic`` while selecting ``Copy existing binaries``.
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ master_doc = 'index'
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'Solidity'
|
project = 'Solidity'
|
||||||
copyright = '2016-2021, Ethereum'
|
project_copyright = '2016-2021, Ethereum'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
@ -73,7 +73,7 @@ copyright = '2016-2021, Ethereum'
|
|||||||
with open('../CMakeLists.txt', 'r', encoding='utf8') as f:
|
with open('../CMakeLists.txt', 'r', encoding='utf8') as f:
|
||||||
version = re.search('PROJECT_VERSION "([^"]+)"', f.read()).group(1)
|
version = re.search('PROJECT_VERSION "([^"]+)"', f.read()).group(1)
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
if os.path.isfile('../prerelease.txt') != True or os.path.getsize('../prerelease.txt') == 0:
|
if not os.path.isfile('../prerelease.txt') or os.path.getsize('../prerelease.txt') == 0:
|
||||||
release = version
|
release = version
|
||||||
else:
|
else:
|
||||||
# This is a prerelease version
|
# This is a prerelease version
|
||||||
|
@ -34,7 +34,8 @@ contracts (using qualified access like ``L.f()``).
|
|||||||
Of course, calls to internal functions
|
Of course, calls to internal functions
|
||||||
use the internal calling convention, which means that all internal types
|
use the internal calling convention, which means that all internal types
|
||||||
can be passed and types :ref:`stored in memory <data-location>` will be passed by reference and not copied.
|
can be passed and types :ref:`stored in memory <data-location>` will be passed by reference and not copied.
|
||||||
To realize this in the EVM, code of internal library functions
|
To realize this in the EVM, the code of internal library functions
|
||||||
|
that are called from a contract
|
||||||
and all functions called from therein will at compile time be included in the calling
|
and all functions called from therein will at compile time be included in the calling
|
||||||
contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``.
|
contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``.
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ the team and contributors are working on, you can join our public team calls:
|
|||||||
- Mondays at 3pm CET/CEST.
|
- Mondays at 3pm CET/CEST.
|
||||||
- Wednesdays at 2pm CET/CEST.
|
- Wednesdays at 2pm CET/CEST.
|
||||||
|
|
||||||
Both calls take place on `Jitsi <https://meet.komputing.org/solidity>`_.
|
Both calls take place on `Jitsi <https://meet.ethereum.org/solidity>`_.
|
||||||
|
|
||||||
How to Report Issues
|
How to Report Issues
|
||||||
====================
|
====================
|
||||||
|
@ -699,7 +699,7 @@ safest action is to revert all changes and make the whole transaction
|
|||||||
(or at least call) without effect.
|
(or at least call) without effect.
|
||||||
|
|
||||||
In both cases, the caller can react on such failures using ``try``/``catch``, but
|
In both cases, the caller can react on such failures using ``try``/``catch``, but
|
||||||
the changes in the caller will always be reverted.
|
the changes in the callee will always be reverted.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -895,6 +895,6 @@ in scope in the block that follows.
|
|||||||
The error might have happened deeper down in the call chain and the
|
The error might have happened deeper down in the call chain and the
|
||||||
called contract just forwarded it. Also, it could be due to an
|
called contract just forwarded it. Also, it could be due to an
|
||||||
out-of-gas situation and not a deliberate error condition:
|
out-of-gas situation and not a deliberate error condition:
|
||||||
The caller always retains 63/64th of the gas in a call and thus
|
The caller always retains at least 1/64th of the gas in a call and thus
|
||||||
even if the called contract goes out of gas, the caller still
|
even if the called contract goes out of gas, the caller still
|
||||||
has some gas left.
|
has some gas left.
|
||||||
|
@ -121,6 +121,9 @@ to receive their money - contracts cannot activate themselves.
|
|||||||
// before `send` returns.
|
// before `send` returns.
|
||||||
pendingReturns[msg.sender] = 0;
|
pendingReturns[msg.sender] = 0;
|
||||||
|
|
||||||
|
// msg.sender is not of type `address payable` and must be
|
||||||
|
// explicitly converted using `payable(msg.sender)` in order
|
||||||
|
// use the member function `send()`.
|
||||||
if (!payable(msg.sender).send(amount)) {
|
if (!payable(msg.sender).send(amount)) {
|
||||||
// No need to call throw here, just reset the amount owing
|
// No need to call throw here, just reset the amount owing
|
||||||
pendingReturns[msg.sender] = amount;
|
pendingReturns[msg.sender] = amount;
|
||||||
|
@ -37,7 +37,7 @@ Alice does not need to interact with the Ethereum network
|
|||||||
to sign the transaction, the process is completely offline.
|
to sign the transaction, the process is completely offline.
|
||||||
In this tutorial, we will sign messages in the browser
|
In this tutorial, we will sign messages in the browser
|
||||||
using `web3.js <https://github.com/ethereum/web3.js>`_ and
|
using `web3.js <https://github.com/ethereum/web3.js>`_ and
|
||||||
`MetaMask <https://metamask.io>`_, using the method described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_,
|
`MetaMask <https://metamask.io>`_, using the method described in `EIP-712 <https://github.com/ethereum/EIPs/pull/712>`_,
|
||||||
as it provides a number of other security benefits.
|
as it provides a number of other security benefits.
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
@ -564,7 +564,7 @@ yulFunctionDefinition:
|
|||||||
* While only identifiers without dots can be declared within inline assembly,
|
* While only identifiers without dots can be declared within inline assembly,
|
||||||
* paths containing dots can refer to declarations outside the inline assembly block.
|
* paths containing dots can refer to declarations outside the inline assembly block.
|
||||||
*/
|
*/
|
||||||
yulPath: YulIdentifier (YulPeriod YulIdentifier)*;
|
yulPath: YulIdentifier (YulPeriod (YulIdentifier | YulEVMBuiltin))*;
|
||||||
/**
|
/**
|
||||||
* A call to a function with return values can only occur as right-hand side of an assignment or
|
* A call to a function with return values can only occur as right-hand side of an assignment or
|
||||||
* a variable declaration.
|
* a variable declaration.
|
||||||
|
@ -118,8 +118,17 @@ The nightly version can be installed using these commands:
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install solc
|
sudo apt-get install solc
|
||||||
|
|
||||||
We are also releasing a `snap package <https://snapcraft.io/>`_, which is
|
Furthermore, some Linux distributions provide their own packages. These packages are not directly
|
||||||
installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To
|
maintained by us, but usually kept up-to-date by the respective package maintainers.
|
||||||
|
|
||||||
|
For example, Arch Linux has packages for the latest development version:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
pacman -S solidity
|
||||||
|
|
||||||
|
There is also a `snap package <https://snapcraft.io/solc>`_, however, it is **currently unmaintained**.
|
||||||
|
It is installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To
|
||||||
install the latest stable version of solc:
|
install the latest stable version of solc:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
@ -139,18 +148,6 @@ with the most recent changes, please use the following:
|
|||||||
but it comes with limitations, like accessing only the files in your ``/home`` and ``/media`` directories.
|
but it comes with limitations, like accessing only the files in your ``/home`` and ``/media`` directories.
|
||||||
For more information, go to `Demystifying Snap Confinement <https://snapcraft.io/blog/demystifying-snap-confinement>`_.
|
For more information, go to `Demystifying Snap Confinement <https://snapcraft.io/blog/demystifying-snap-confinement>`_.
|
||||||
|
|
||||||
Arch Linux also has packages, albeit limited to the latest development version:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
pacman -S solidity
|
|
||||||
|
|
||||||
Gentoo Linux has an `Ethereum overlay <https://overlays.gentoo.org/#ethereum>`_ that contains a Solidity package.
|
|
||||||
After the overlay is setup, ``solc`` can be installed in x86_64 architectures by:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
emerge dev-lang/solidity
|
|
||||||
|
|
||||||
macOS Packages
|
macOS Packages
|
||||||
==============
|
==============
|
||||||
@ -537,8 +534,8 @@ The Solidity version string contains four parts:
|
|||||||
|
|
||||||
If there are local modifications, the commit will be postfixed with ``.mod``.
|
If there are local modifications, the commit will be postfixed with ``.mod``.
|
||||||
|
|
||||||
These parts are combined as required by Semver, where the Solidity pre-release tag equals to the Semver pre-release
|
These parts are combined as required by SemVer, where the Solidity pre-release tag equals to the SemVer pre-release
|
||||||
and the Solidity commit and platform combined make up the Semver build metadata.
|
and the Solidity commit and platform combined make up the SemVer build metadata.
|
||||||
|
|
||||||
A release example: ``0.4.8+commit.60cc1668.Emscripten.clang``.
|
A release example: ``0.4.8+commit.60cc1668.Emscripten.clang``.
|
||||||
|
|
||||||
@ -549,7 +546,7 @@ Important Information About Versioning
|
|||||||
|
|
||||||
After a release is made, the patch version level is bumped, because we assume that only
|
After a release is made, the patch version level is bumped, because we assume that only
|
||||||
patch level changes follow. When changes are merged, the version should be bumped according
|
patch level changes follow. When changes are merged, the version should be bumped according
|
||||||
to semver and the severity of the change. Finally, a release is always made with the version
|
to SemVer and the severity of the change. Finally, a release is always made with the version
|
||||||
of the current nightly build, but without the ``prerelease`` specifier.
|
of the current nightly build, but without the ``prerelease`` specifier.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -290,6 +290,7 @@ on the individual steps and their sequence below.
|
|||||||
- :ref:`conditional-unsimplifier`.
|
- :ref:`conditional-unsimplifier`.
|
||||||
- :ref:`control-flow-simplifier`.
|
- :ref:`control-flow-simplifier`.
|
||||||
- :ref:`dead-code-eliminator`.
|
- :ref:`dead-code-eliminator`.
|
||||||
|
- :ref:`equal-store-eliminator`.
|
||||||
- :ref:`equivalent-function-combiner`.
|
- :ref:`equivalent-function-combiner`.
|
||||||
- :ref:`expression-joiner`.
|
- :ref:`expression-joiner`.
|
||||||
- :ref:`expression-simplifier`.
|
- :ref:`expression-simplifier`.
|
||||||
@ -938,6 +939,22 @@ we require ForLoopInitRewriter to run before this step.
|
|||||||
|
|
||||||
Prerequisite: ForLoopInitRewriter, Function Hoister, Function Grouper
|
Prerequisite: ForLoopInitRewriter, Function Hoister, Function Grouper
|
||||||
|
|
||||||
|
.. _equal-store-eliminator:
|
||||||
|
|
||||||
|
EqualStoreEliminator
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This steps removes ``mstore(k, v)`` and ``sstore(k, v)`` calls if
|
||||||
|
there was a previous call to ``mstore(k, v)`` / ``sstore(k, v)``,
|
||||||
|
no other store in between and the values of ``k`` and ``v`` did not change.
|
||||||
|
|
||||||
|
This simple step is effective if run after the SSA transform and the
|
||||||
|
Common Subexpression Eliminator, because SSA will make sure that the variables
|
||||||
|
will not change and the Common Subexpression Eliminator re-uses exactly the same
|
||||||
|
variable if the value is known to be the same.
|
||||||
|
|
||||||
|
Prerequisites: Disambiguator, ForLoopInitRewriter
|
||||||
|
|
||||||
.. _unused-pruner:
|
.. _unused-pruner:
|
||||||
|
|
||||||
UnusedPruner
|
UnusedPruner
|
||||||
|
@ -13,7 +13,7 @@ and :ref:`constant variable<constants>` definitions.
|
|||||||
SPDX License Identifier
|
SPDX License Identifier
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
Trust in smart contract can be better established if their source code
|
Trust in smart contracts can be better established if their source code
|
||||||
is available. Since making source code available always touches on legal problems
|
is available. Since making source code available always touches on legal problems
|
||||||
with regards to copyright, the Solidity compiler encourages the use
|
with regards to copyright, the Solidity compiler encourages the use
|
||||||
of machine-readable `SPDX license identifiers <https://spdx.org>`_.
|
of machine-readable `SPDX license identifiers <https://spdx.org>`_.
|
||||||
|
@ -84,23 +84,24 @@ The example below uses ``_allowances`` to record the amount someone else is allo
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
|
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
|
||||||
|
require(_allowances[sender][msg.sender] >= amount, "ERC20: Allowance not high enough.");
|
||||||
|
_allowances[sender][msg.sender] -= amount;
|
||||||
_transfer(sender, recipient, amount);
|
_transfer(sender, recipient, amount);
|
||||||
approve(sender, msg.sender, amount);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function approve(address owner, address spender, uint256 amount) public returns (bool) {
|
function approve(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");
|
require(spender != address(0), "ERC20: approve to the zero address");
|
||||||
|
|
||||||
_allowances[owner][spender] = amount;
|
_allowances[msg.sender][spender] = amount;
|
||||||
emit Approval(owner, spender, amount);
|
emit Approval(msg.sender, spender, amount);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _transfer(address sender, address recipient, uint256 amount) internal {
|
function _transfer(address sender, address recipient, uint256 amount) internal {
|
||||||
require(sender != address(0), "ERC20: transfer from the zero address");
|
require(sender != address(0), "ERC20: transfer from the zero address");
|
||||||
require(recipient != address(0), "ERC20: transfer to the zero address");
|
require(recipient != address(0), "ERC20: transfer to the zero address");
|
||||||
|
require(_balances[sender] >= amount, "ERC20: Not enough funds.");
|
||||||
|
|
||||||
_balances[sender] -= amount;
|
_balances[sender] -= amount;
|
||||||
_balances[recipient] += amount;
|
_balances[recipient] += amount;
|
||||||
|
@ -153,7 +153,8 @@ third-party string libraries. You can also compare two strings by their keccak25
|
|||||||
concatenate two strings using ``bytes.concat(bytes(s1), bytes(s2))``.
|
concatenate two strings using ``bytes.concat(bytes(s1), bytes(s2))``.
|
||||||
|
|
||||||
You should use ``bytes`` over ``bytes1[]`` because it is cheaper,
|
You should use ``bytes`` over ``bytes1[]`` because it is cheaper,
|
||||||
since ``bytes1[]`` adds 31 padding bytes between the elements. As a general rule,
|
since using ``bytes1[]`` in ``memory`` adds 31 padding bytes between the elements. Note that in ``storage``, the
|
||||||
|
padding is absent due to tight packing, see :ref:`bytes and string <bytes-and-string>`. As a general rule,
|
||||||
use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length
|
use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length
|
||||||
string (UTF-8) data. If you can limit the length to a certain number of bytes,
|
string (UTF-8) data. If you can limit the length to a certain number of bytes,
|
||||||
always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper.
|
always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper.
|
||||||
|
@ -329,6 +329,10 @@ on ``call``.
|
|||||||
regardless of whether state is read from or written to, as this can have many pitfalls.
|
regardless of whether state is read from or written to, as this can have many pitfalls.
|
||||||
Also, access to gas might change in the future.
|
Also, access to gas might change in the future.
|
||||||
|
|
||||||
|
* ``code`` and ``codehash``
|
||||||
|
|
||||||
|
You can query the deployed code for any smart contract. Use ``code`` to get the EVM bytecode as a string, which might be empty. Use ``codehash`` get the Keccak-256 hash of that code.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
||||||
current contract using ``address(this).balance``.
|
current contract using ``address(this).balance``.
|
||||||
|
@ -25,19 +25,21 @@ using namespace solidity::langutil;
|
|||||||
using namespace solidity::frontend;
|
using namespace solidity::frontend;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
|
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow, ContractDefinition const* _contract):
|
||||||
m_nodeContainer(_nodeContainer),
|
m_nodeContainer(_nodeContainer),
|
||||||
m_currentNode(_functionFlow.entry),
|
m_currentNode(_functionFlow.entry),
|
||||||
m_returnNode(_functionFlow.exit),
|
m_returnNode(_functionFlow.exit),
|
||||||
m_revertNode(_functionFlow.revert),
|
m_revertNode(_functionFlow.revert),
|
||||||
m_transactionReturnNode(_functionFlow.transactionReturn)
|
m_transactionReturnNode(_functionFlow.transactionReturn),
|
||||||
|
m_contract(_contract)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
||||||
CFG::NodeContainer& _nodeContainer,
|
CFG::NodeContainer& _nodeContainer,
|
||||||
FunctionDefinition const& _function
|
FunctionDefinition const& _function,
|
||||||
|
ContractDefinition const* _contract
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto functionFlow = make_unique<FunctionFlow>();
|
auto functionFlow = make_unique<FunctionFlow>();
|
||||||
@ -45,7 +47,7 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
|||||||
functionFlow->exit = _nodeContainer.newNode();
|
functionFlow->exit = _nodeContainer.newNode();
|
||||||
functionFlow->revert = _nodeContainer.newNode();
|
functionFlow->revert = _nodeContainer.newNode();
|
||||||
functionFlow->transactionReturn = _nodeContainer.newNode();
|
functionFlow->transactionReturn = _nodeContainer.newNode();
|
||||||
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
|
ControlFlowBuilder builder(_nodeContainer, *functionFlow, _contract);
|
||||||
builder.appendControlFlow(_function);
|
builder.appendControlFlow(_function);
|
||||||
|
|
||||||
return functionFlow;
|
return functionFlow;
|
||||||
@ -297,7 +299,8 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
|||||||
_functionCall.expression().accept(*this);
|
_functionCall.expression().accept(*this);
|
||||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||||
|
|
||||||
m_currentNode->functionCalls.emplace_back(&_functionCall);
|
solAssert(!m_currentNode->functionCall);
|
||||||
|
m_currentNode->functionCall = &_functionCall;
|
||||||
|
|
||||||
auto nextNode = newLabel();
|
auto nextNode = newLabel();
|
||||||
|
|
||||||
@ -321,8 +324,20 @@ bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
|
|||||||
auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
|
auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
|
||||||
_modifierInvocation.name().annotation().referencedDeclaration
|
_modifierInvocation.name().annotation().referencedDeclaration
|
||||||
);
|
);
|
||||||
if (!modifierDefinition) return false;
|
|
||||||
if (!modifierDefinition->isImplemented()) return false;
|
if (!modifierDefinition)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
VirtualLookup const& requiredLookup = *_modifierInvocation.name().annotation().requiredLookup;
|
||||||
|
|
||||||
|
if (requiredLookup == VirtualLookup::Virtual)
|
||||||
|
modifierDefinition = &modifierDefinition->resolveVirtual(*m_contract);
|
||||||
|
else
|
||||||
|
solAssert(requiredLookup == VirtualLookup::Static);
|
||||||
|
|
||||||
|
if (!modifierDefinition->isImplemented())
|
||||||
|
return false;
|
||||||
|
|
||||||
solAssert(!!m_returnNode, "");
|
solAssert(!!m_returnNode, "");
|
||||||
|
|
||||||
m_placeholderEntry = newLabel();
|
m_placeholderEntry = newLabel();
|
||||||
@ -355,8 +370,8 @@ bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& modifier: _functionDefinition.modifiers())
|
for (auto const& modifierInvocation: _functionDefinition.modifiers())
|
||||||
appendControlFlow(*modifier);
|
appendControlFlow(*modifierInvocation);
|
||||||
|
|
||||||
appendControlFlow(_functionDefinition.body());
|
appendControlFlow(_functionDefinition.body());
|
||||||
|
|
||||||
|
@ -37,13 +37,15 @@ class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker
|
|||||||
public:
|
public:
|
||||||
static std::unique_ptr<FunctionFlow> createFunctionFlow(
|
static std::unique_ptr<FunctionFlow> createFunctionFlow(
|
||||||
CFG::NodeContainer& _nodeContainer,
|
CFG::NodeContainer& _nodeContainer,
|
||||||
FunctionDefinition const& _function
|
FunctionDefinition const& _function,
|
||||||
|
ContractDefinition const* _contract = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ControlFlowBuilder(
|
explicit ControlFlowBuilder(
|
||||||
CFG::NodeContainer& _nodeContainer,
|
CFG::NodeContainer& _nodeContainer,
|
||||||
FunctionFlow const& _functionFlow
|
FunctionFlow const& _functionFlow,
|
||||||
|
ContractDefinition const* _contract = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
// Visits for constructing the control flow.
|
// Visits for constructing the control flow.
|
||||||
@ -158,6 +160,8 @@ private:
|
|||||||
CFGNode* m_revertNode = nullptr;
|
CFGNode* m_revertNode = nullptr;
|
||||||
CFGNode* m_transactionReturnNode = nullptr;
|
CFGNode* m_transactionReturnNode = nullptr;
|
||||||
|
|
||||||
|
ContractDefinition const* m_contract = nullptr;
|
||||||
|
|
||||||
/// The current jump destination of break Statements.
|
/// The current jump destination of break Statements.
|
||||||
CFGNode* m_breakJump = nullptr;
|
CFGNode* m_breakJump = nullptr;
|
||||||
/// The current jump destination of continue Statements.
|
/// The current jump destination of continue Statements.
|
||||||
|
@ -44,7 +44,7 @@ bool CFG::visit(ContractDefinition const& _contract)
|
|||||||
for (FunctionDefinition const* function: contract->definedFunctions())
|
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||||
if (function->isImplemented())
|
if (function->isImplemented())
|
||||||
m_functionControlFlow[{&_contract, function}] =
|
m_functionControlFlow[{&_contract, function}] =
|
||||||
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function);
|
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function, &_contract);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -98,8 +98,8 @@ struct CFGNode
|
|||||||
std::vector<CFGNode*> entries;
|
std::vector<CFGNode*> entries;
|
||||||
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
|
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
|
||||||
std::vector<CFGNode*> exits;
|
std::vector<CFGNode*> exits;
|
||||||
/// Function calls done by this node
|
/// Function call done by this node
|
||||||
std::vector<FunctionCall const*> functionCalls;
|
FunctionCall const* functionCall = nullptr;
|
||||||
|
|
||||||
/// Variable occurrences in the node.
|
/// Variable occurrences in the node.
|
||||||
std::vector<VariableOccurrence> variableOccurrences;
|
std::vector<VariableOccurrence> variableOccurrences;
|
||||||
|
@ -81,13 +81,12 @@ void ControlFlowRevertPruner::findRevertStates()
|
|||||||
if (_node == functionFlow.exit)
|
if (_node == functionFlow.exit)
|
||||||
foundExit = true;
|
foundExit = true;
|
||||||
|
|
||||||
for (auto const* functionCall: _node->functionCalls)
|
if (auto const* functionCall = _node->functionCall)
|
||||||
{
|
{
|
||||||
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.contract);
|
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.contract);
|
||||||
|
|
||||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
if (resolvedFunction && resolvedFunction->isImplemented())
|
||||||
continue;
|
{
|
||||||
|
|
||||||
CFG::FunctionContractTuple calledFunctionTuple{
|
CFG::FunctionContractTuple calledFunctionTuple{
|
||||||
findScopeContract(*resolvedFunction, item.contract),
|
findScopeContract(*resolvedFunction, item.contract),
|
||||||
resolvedFunction
|
resolvedFunction
|
||||||
@ -104,6 +103,7 @@ void ControlFlowRevertPruner::findRevertStates()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (CFGNode* exit: _node->exits)
|
for (CFGNode* exit: _node->exits)
|
||||||
_addChild(exit);
|
_addChild(exit);
|
||||||
@ -135,13 +135,11 @@ void ControlFlowRevertPruner::modifyFunctionFlows()
|
|||||||
FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.first.function, item.first.contract);
|
FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.first.function, item.first.contract);
|
||||||
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
|
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
|
||||||
[&](CFGNode* _node, auto&& _addChild) {
|
[&](CFGNode* _node, auto&& _addChild) {
|
||||||
for (auto const* functionCall: _node->functionCalls)
|
if (auto const* functionCall = _node->functionCall)
|
||||||
{
|
{
|
||||||
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.first.contract);
|
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.first.contract);
|
||||||
|
|
||||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
if (resolvedFunction && resolvedFunction->isImplemented())
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
|
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
|
||||||
{
|
{
|
||||||
case RevertState::Unknown:
|
case RevertState::Unknown:
|
||||||
|
@ -437,16 +437,14 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
|||||||
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
|
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (_variable.isConstant() && !type->isValueType())
|
||||||
_variable.isConstant() &&
|
{
|
||||||
!dynamic_cast<UserDefinedValueType const*>(type) &&
|
bool allowed = false;
|
||||||
type->containsNestedMapping()
|
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
||||||
)
|
allowed = arrayType->isByteArray();
|
||||||
m_errorReporter.fatalDeclarationError(
|
if (!allowed)
|
||||||
3530_error,
|
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented.");
|
||||||
_variable.location(),
|
}
|
||||||
"The type contains a (nested) mapping and therefore cannot be a constant."
|
|
||||||
);
|
|
||||||
|
|
||||||
_variable.annotation().type = type;
|
_variable.annotation().type = type;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <libsolidity/analysis/DocStringAnalyser.h>
|
#include <libsolidity/analysis/DocStringAnalyser.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
@ -37,7 +38,7 @@ using namespace solidity::frontend;
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, CallableDeclaration const* _declaration = nullptr)
|
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, FunctionType const* _functionType = nullptr)
|
||||||
{
|
{
|
||||||
// Only copy if there is exactly one direct base function.
|
// Only copy if there is exactly one direct base function.
|
||||||
if (_baseFunctions.size() != 1)
|
if (_baseFunctions.size() != 1)
|
||||||
@ -45,12 +46,6 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
|
|||||||
|
|
||||||
CallableDeclaration const& baseFunction = **_baseFunctions.begin();
|
CallableDeclaration const& baseFunction = **_baseFunctions.begin();
|
||||||
|
|
||||||
auto hasReturnParameter = [](CallableDeclaration const& declaration, size_t _n)
|
|
||||||
{
|
|
||||||
return declaration.returnParameterList() &&
|
|
||||||
declaration.returnParameters().size() > _n;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto& sourceDoc = dynamic_cast<StructurallyDocumentedAnnotation const&>(baseFunction.annotation());
|
auto& sourceDoc = dynamic_cast<StructurallyDocumentedAnnotation const&>(baseFunction.annotation());
|
||||||
|
|
||||||
for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();)
|
for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();)
|
||||||
@ -70,21 +65,22 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
|
|||||||
DocTag content = it->second;
|
DocTag content = it->second;
|
||||||
|
|
||||||
// Update the parameter name for @return tags
|
// Update the parameter name for @return tags
|
||||||
if (_declaration && tag == "return")
|
if (_functionType && tag == "return")
|
||||||
{
|
{
|
||||||
size_t docParaNameEndPos = content.content.find_first_of(" \t");
|
size_t docParaNameEndPos = content.content.find_first_of(" \t");
|
||||||
string const docParameterName = content.content.substr(0, docParaNameEndPos);
|
string const docParameterName = content.content.substr(0, docParaNameEndPos);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
hasReturnParameter(*_declaration, n) &&
|
_functionType->returnParameterNames().size() > n &&
|
||||||
docParameterName != _declaration->returnParameters().at(n)->name()
|
docParameterName != _functionType->returnParameterNames().at(n)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
bool baseHasNoName =
|
bool baseHasNoName =
|
||||||
hasReturnParameter(baseFunction, n) &&
|
baseFunction.returnParameterList() &&
|
||||||
|
baseFunction.returnParameters().size() > n &&
|
||||||
baseFunction.returnParameters().at(n)->name().empty();
|
baseFunction.returnParameters().at(n)->name().empty();
|
||||||
|
|
||||||
string paramName = _declaration->returnParameters().at(n)->name();
|
string paramName = _functionType->returnParameterNames().at(n);
|
||||||
content.content =
|
content.content =
|
||||||
(paramName.empty() ? "" : std::move(paramName) + " ") + (
|
(paramName.empty() ? "" : std::move(paramName) + " ") + (
|
||||||
string::npos == docParaNameEndPos || baseHasNoName ?
|
string::npos == docParaNameEndPos || baseHasNoName ?
|
||||||
@ -127,7 +123,7 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
|
|||||||
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
|
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
if (!_function.isConstructor())
|
if (!_function.isConstructor())
|
||||||
handleCallable(_function, _function, _function.annotation());
|
handleCallable(_function, _function, _function.annotation(), TypeProvider::function(_function));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,10 +132,12 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
|
|||||||
if (!_variable.isStateVariable() && !_variable.isFileLevelVariable())
|
if (!_variable.isStateVariable() && !_variable.isFileLevelVariable())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
auto const* getterType = TypeProvider::function(_variable);
|
||||||
|
|
||||||
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
|
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
|
||||||
copyMissingTags({baseFunction}, _variable.annotation());
|
copyMissingTags({baseFunction}, _variable.annotation(), getterType);
|
||||||
else if (_variable.annotation().docTags.empty())
|
else if (_variable.annotation().docTags.empty())
|
||||||
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation());
|
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation(), getterType);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -168,17 +166,18 @@ bool DocStringAnalyser::visit(ErrorDefinition const& _error)
|
|||||||
void DocStringAnalyser::handleCallable(
|
void DocStringAnalyser::handleCallable(
|
||||||
CallableDeclaration const& _callable,
|
CallableDeclaration const& _callable,
|
||||||
StructurallyDocumented const& _node,
|
StructurallyDocumented const& _node,
|
||||||
StructurallyDocumentedAnnotation& _annotation
|
StructurallyDocumentedAnnotation& _annotation,
|
||||||
|
FunctionType const* _functionType
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation))
|
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation))
|
||||||
copyMissingTags({baseFunction}, _annotation, &_callable);
|
copyMissingTags({baseFunction}, _annotation, _functionType);
|
||||||
else if (
|
else if (
|
||||||
_annotation.docTags.empty() &&
|
_annotation.docTags.empty() &&
|
||||||
_callable.annotation().baseFunctions.size() == 1 &&
|
_callable.annotation().baseFunctions.size() == 1 &&
|
||||||
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
|
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
|
||||||
)
|
)
|
||||||
copyMissingTags(_callable.annotation().baseFunctions, _annotation, &_callable);
|
copyMissingTags(_callable.annotation().baseFunctions, _annotation, _functionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
CallableDeclaration const* DocStringAnalyser::resolveInheritDoc(
|
CallableDeclaration const* DocStringAnalyser::resolveInheritDoc(
|
||||||
|
@ -54,7 +54,8 @@ private:
|
|||||||
void handleCallable(
|
void handleCallable(
|
||||||
CallableDeclaration const& _callable,
|
CallableDeclaration const& _callable,
|
||||||
StructurallyDocumented const& _node,
|
StructurallyDocumented const& _node,
|
||||||
StructurallyDocumentedAnnotation& _annotation
|
StructurallyDocumentedAnnotation& _annotation,
|
||||||
|
FunctionType const* _functionType = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
|
@ -73,24 +73,24 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
magicVarDecl("abi", TypeProvider::magic(MagicType::Kind::ABI)),
|
magicVarDecl("abi", TypeProvider::magic(MagicType::Kind::ABI)),
|
||||||
magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)),
|
magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, StateMutability::Pure)),
|
||||||
magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)),
|
magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, StateMutability::Pure)),
|
||||||
magicVarDecl("block", TypeProvider::magic(MagicType::Kind::Block)),
|
magicVarDecl("block", TypeProvider::magic(MagicType::Kind::Block)),
|
||||||
magicVarDecl("blockhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)),
|
magicVarDecl("blockhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, StateMutability::View)),
|
||||||
magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
|
magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, StateMutability::Pure)),
|
||||||
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
|
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, StateMutability::View)),
|
||||||
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, StateMutability::Pure)),
|
||||||
magicVarDecl("msg", TypeProvider::magic(MagicType::Kind::Message)),
|
magicVarDecl("msg", TypeProvider::magic(MagicType::Kind::Message)),
|
||||||
magicVarDecl("mulmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)),
|
magicVarDecl("mulmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, StateMutability::Pure)),
|
||||||
magicVarDecl("now", TypeProvider::uint256()),
|
magicVarDecl("now", TypeProvider::uint256()),
|
||||||
magicVarDecl("require", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
|
magicVarDecl("require", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Require, StateMutability::Pure)),
|
||||||
magicVarDecl("require", TypeProvider::function(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
|
magicVarDecl("require", TypeProvider::function(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, StateMutability::Pure)),
|
||||||
magicVarDecl("revert", TypeProvider::function(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
|
magicVarDecl("revert", TypeProvider::function(strings(), strings(), FunctionType::Kind::Revert, StateMutability::Pure)),
|
||||||
magicVarDecl("revert", TypeProvider::function(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
|
magicVarDecl("revert", TypeProvider::function(strings{"string memory"}, strings(), FunctionType::Kind::Revert, StateMutability::Pure)),
|
||||||
magicVarDecl("ripemd160", TypeProvider::function(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)),
|
magicVarDecl("ripemd160", TypeProvider::function(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, StateMutability::Pure)),
|
||||||
magicVarDecl("selfdestruct", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
magicVarDecl("selfdestruct", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||||
magicVarDecl("sha256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
|
magicVarDecl("sha256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, StateMutability::Pure)),
|
||||||
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, StateMutability::Pure)),
|
||||||
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||||
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
|
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
|
||||||
// Accepts a MagicType that can be any contract type or an Integer type and returns a
|
// Accepts a MagicType that can be any contract type or an Integer type and returns a
|
||||||
@ -99,8 +99,8 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
|
|||||||
strings{},
|
strings{},
|
||||||
strings{},
|
strings{},
|
||||||
FunctionType::Kind::MetaType,
|
FunctionType::Kind::MetaType,
|
||||||
true,
|
StateMutability::Pure,
|
||||||
StateMutability::Pure
|
FunctionType::Options::withArbitraryParameters()
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ void ImmutableValidator::analyze()
|
|||||||
{
|
{
|
||||||
m_inCreationContext = true;
|
m_inCreationContext = true;
|
||||||
|
|
||||||
auto linearizedContracts = m_currentContract.annotation().linearizedBaseContracts | ranges::views::reverse;
|
auto linearizedContracts = m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse;
|
||||||
|
|
||||||
for (ContractDefinition const* contract: linearizedContracts)
|
for (ContractDefinition const* contract: linearizedContracts)
|
||||||
for (VariableDeclaration const* stateVar: contract->stateVariables())
|
for (VariableDeclaration const* stateVar: contract->stateVariables())
|
||||||
@ -62,7 +62,7 @@ void ImmutableValidator::analyze()
|
|||||||
visitCallableIfNew(*modDef);
|
visitCallableIfNew(*modDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAllVariablesInitialized(m_currentContract.location());
|
checkAllVariablesInitialized(m_mostDerivedContract.location());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImmutableValidator::visit(Assignment const& _assignment)
|
bool ImmutableValidator::visit(Assignment const& _assignment)
|
||||||
@ -137,7 +137,7 @@ void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath)
|
|||||||
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifierPath.annotation().referencedDeclaration))
|
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifierPath.annotation().referencedDeclaration))
|
||||||
visitCallableIfNew(
|
visitCallableIfNew(
|
||||||
*_identifierPath.annotation().requiredLookup == VirtualLookup::Virtual ?
|
*_identifierPath.annotation().requiredLookup == VirtualLookup::Virtual ?
|
||||||
callableDef->resolveVirtual(m_currentContract) :
|
callableDef->resolveVirtual(m_mostDerivedContract) :
|
||||||
*callableDef
|
*callableDef
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath)
|
|||||||
void ImmutableValidator::endVisit(Identifier const& _identifier)
|
void ImmutableValidator::endVisit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||||
visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_currentContract) : *callableDef);
|
visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_mostDerivedContract) : *callableDef);
|
||||||
if (auto const varDecl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
if (auto const varDecl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||||
analyseVariableReference(*varDecl, _identifier);
|
analyseVariableReference(*varDecl, _identifier);
|
||||||
}
|
}
|
||||||
@ -160,15 +160,18 @@ void ImmutableValidator::endVisit(Return const& _return)
|
|||||||
|
|
||||||
bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration)
|
bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration)
|
||||||
{
|
{
|
||||||
FunctionDefinition const* prevConstructor = m_currentConstructor;
|
ScopedSaveAndRestore constructorGuard{m_currentConstructor, {}};
|
||||||
m_currentConstructor = nullptr;
|
ScopedSaveAndRestore constructorContractGuard{m_currentConstructorContract, {}};
|
||||||
|
|
||||||
if (FunctionDefinition const* funcDef = dynamic_cast<decltype(funcDef)>(&_callableDeclaration))
|
if (FunctionDefinition const* funcDef = dynamic_cast<decltype(funcDef)>(&_callableDeclaration))
|
||||||
{
|
{
|
||||||
ASTNode::listAccept(funcDef->modifiers(), *this);
|
ASTNode::listAccept(funcDef->modifiers(), *this);
|
||||||
|
|
||||||
if (funcDef->isConstructor())
|
if (funcDef->isConstructor())
|
||||||
|
{
|
||||||
|
m_currentConstructorContract = funcDef->annotation().contract;
|
||||||
m_currentConstructor = funcDef;
|
m_currentConstructor = funcDef;
|
||||||
|
}
|
||||||
|
|
||||||
if (funcDef->isImplemented())
|
if (funcDef->isImplemented())
|
||||||
funcDef->body().accept(*this);
|
funcDef->body().accept(*this);
|
||||||
@ -177,8 +180,6 @@ bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDec
|
|||||||
if (modDef->isImplemented())
|
if (modDef->isImplemented())
|
||||||
modDef->body().accept(*this);
|
modDef->body().accept(*this);
|
||||||
|
|
||||||
m_currentConstructor = prevConstructor;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +254,8 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
|
|||||||
|
|
||||||
void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location)
|
void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
for (ContractDefinition const* contract: m_currentContract.annotation().linearizedBaseContracts)
|
for (ContractDefinition const* contract: m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse)
|
||||||
|
{
|
||||||
for (VariableDeclaration const* varDecl: contract->stateVariables())
|
for (VariableDeclaration const* varDecl: contract->stateVariables())
|
||||||
if (varDecl->immutable())
|
if (varDecl->immutable())
|
||||||
if (!util::contains(m_initializedStateVariables, varDecl))
|
if (!util::contains(m_initializedStateVariables, varDecl))
|
||||||
@ -263,6 +265,11 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source
|
|||||||
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
|
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
|
||||||
"Construction control flow ends without initializing all immutable state variables."
|
"Construction control flow ends without initializing all immutable state variables."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Don't check further than the current c'tors contract
|
||||||
|
if (contract == m_currentConstructorContract)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration)
|
void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration)
|
||||||
|
@ -42,7 +42,7 @@ class ImmutableValidator: private ASTConstVisitor
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition):
|
ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition):
|
||||||
m_currentContract(_contractDefinition),
|
m_mostDerivedContract(_contractDefinition),
|
||||||
m_errorReporter(_errorReporter)
|
m_errorReporter(_errorReporter)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ private:
|
|||||||
|
|
||||||
void visitCallableIfNew(Declaration const& _declaration);
|
void visitCallableIfNew(Declaration const& _declaration);
|
||||||
|
|
||||||
ContractDefinition const& m_currentContract;
|
ContractDefinition const& m_mostDerivedContract;
|
||||||
|
|
||||||
CallableDeclarationSet m_visitedCallables;
|
CallableDeclarationSet m_visitedCallables;
|
||||||
|
|
||||||
@ -74,6 +74,7 @@ private:
|
|||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
|
|
||||||
FunctionDefinition const* m_currentConstructor = nullptr;
|
FunctionDefinition const* m_currentConstructor = nullptr;
|
||||||
|
ContractDefinition const* m_currentConstructorContract = nullptr;
|
||||||
bool m_inLoop = false;
|
bool m_inLoop = false;
|
||||||
bool m_inBranch = false;
|
bool m_inBranch = false;
|
||||||
bool m_inCreationContext = true;
|
bool m_inCreationContext = true;
|
||||||
|
@ -530,15 +530,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
}
|
}
|
||||||
if (_variable.isConstant())
|
if (_variable.isConstant())
|
||||||
{
|
{
|
||||||
if (!varType->isValueType())
|
|
||||||
{
|
|
||||||
bool allowed = false;
|
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(varType))
|
|
||||||
allowed = arrayType->isByteArray();
|
|
||||||
if (!allowed)
|
|
||||||
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Constants of non-value type not yet implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_variable.value())
|
if (!_variable.value())
|
||||||
m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable.");
|
m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable.");
|
||||||
else if (!*_variable.value()->annotation().isPure)
|
else if (!*_variable.value()->annotation().isPure)
|
||||||
@ -2122,16 +2113,35 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionPointerType->kind() != FunctionType::Kind::External)
|
if (
|
||||||
|
functionPointerType->kind() != FunctionType::Kind::External &&
|
||||||
|
functionPointerType->kind() != FunctionType::Kind::Declaration
|
||||||
|
)
|
||||||
{
|
{
|
||||||
string msg = "Function must be \"public\" or \"external\".";
|
string msg = "Expected regular external function type, or external view on public function.";
|
||||||
|
if (functionPointerType->kind() == FunctionType::Kind::Internal)
|
||||||
|
msg += " Provided internal function.";
|
||||||
|
else if (functionPointerType->kind() == FunctionType::Kind::DelegateCall)
|
||||||
|
msg += " Cannot use library functions for abi.encodeCall.";
|
||||||
|
else if (functionPointerType->kind() == FunctionType::Kind::Creation)
|
||||||
|
msg += " Provided creation function.";
|
||||||
|
else
|
||||||
|
msg += " Cannot use special function.";
|
||||||
SecondarySourceLocation ssl{};
|
SecondarySourceLocation ssl{};
|
||||||
|
|
||||||
if (functionPointerType->hasDeclaration())
|
if (functionPointerType->hasDeclaration())
|
||||||
{
|
{
|
||||||
ssl.append("Function is declared here:", functionPointerType->declaration().location());
|
ssl.append("Function is declared here:", functionPointerType->declaration().location());
|
||||||
if (functionPointerType->declaration().scope() == m_currentContract)
|
if (
|
||||||
|
functionPointerType->declaration().visibility() == Visibility::Public &&
|
||||||
|
functionPointerType->declaration().scope() == m_currentContract
|
||||||
|
)
|
||||||
msg += " Did you forget to prefix \"this.\"?";
|
msg += " Did you forget to prefix \"this.\"?";
|
||||||
|
else if (contains(
|
||||||
|
m_currentContract->annotation().linearizedBaseContracts,
|
||||||
|
functionPointerType->declaration().scope()
|
||||||
|
) && functionPointerType->declaration().scope() != m_currentContract)
|
||||||
|
msg += " Functions from base contracts have to be external.";
|
||||||
}
|
}
|
||||||
|
|
||||||
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
|
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
|
||||||
@ -2866,7 +2876,6 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
|||||||
strings(1, ""),
|
strings(1, ""),
|
||||||
strings(1, ""),
|
strings(1, ""),
|
||||||
FunctionType::Kind::ObjectCreation,
|
FunctionType::Kind::ObjectCreation,
|
||||||
false,
|
|
||||||
StateMutability::Pure
|
StateMutability::Pure
|
||||||
);
|
);
|
||||||
_newExpression.annotation().isPure = true;
|
_newExpression.annotation().isPure = true;
|
||||||
|
@ -511,23 +511,20 @@ ModifierDefinition const& ModifierDefinition::resolveVirtual(
|
|||||||
ContractDefinition const* _searchStart
|
ContractDefinition const* _searchStart
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
|
// Super is not possible with modifiers
|
||||||
solAssert(_searchStart == nullptr, "Used super in connection with modifiers.");
|
solAssert(_searchStart == nullptr, "Used super in connection with modifiers.");
|
||||||
|
|
||||||
// If we are not doing super-lookup and the modifier is not virtual, we can stop here.
|
// The modifier is not virtual, we can stop here.
|
||||||
if (_searchStart == nullptr && !virtualSemantics())
|
if (!virtualSemantics())
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
solAssert(!dynamic_cast<ContractDefinition const&>(*scope()).isLibrary(), "");
|
solAssert(!dynamic_cast<ContractDefinition const&>(*scope()).isLibrary(), "");
|
||||||
|
|
||||||
for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts)
|
for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts)
|
||||||
{
|
|
||||||
if (_searchStart != nullptr && c != _searchStart)
|
|
||||||
continue;
|
|
||||||
_searchStart = nullptr;
|
|
||||||
for (ModifierDefinition const* modifier: c->functionModifiers())
|
for (ModifierDefinition const* modifier: c->functionModifiers())
|
||||||
if (modifier->name() == name())
|
if (modifier->name() == name())
|
||||||
return *modifier;
|
return *modifier;
|
||||||
}
|
|
||||||
solAssert(false, "Virtual modifier " + name() + " not found.");
|
solAssert(false, "Virtual modifier " + name() + " not found.");
|
||||||
return *this; // not reached
|
return *this; // not reached
|
||||||
}
|
}
|
||||||
|
@ -445,13 +445,18 @@ FunctionType const* TypeProvider::function(
|
|||||||
strings const& _parameterTypes,
|
strings const& _parameterTypes,
|
||||||
strings const& _returnParameterTypes,
|
strings const& _returnParameterTypes,
|
||||||
FunctionType::Kind _kind,
|
FunctionType::Kind _kind,
|
||||||
bool _arbitraryParameters,
|
StateMutability _stateMutability,
|
||||||
StateMutability _stateMutability
|
FunctionType::Options _options
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
// Can only use this constructor for "arbitraryParameters".
|
||||||
|
solAssert(!_options.valueSet && !_options.gasSet && !_options.saltSet && !_options.bound);
|
||||||
return createAndGet<FunctionType>(
|
return createAndGet<FunctionType>(
|
||||||
_parameterTypes, _returnParameterTypes,
|
_parameterTypes,
|
||||||
_kind, _arbitraryParameters, _stateMutability
|
_returnParameterTypes,
|
||||||
|
_kind,
|
||||||
|
_stateMutability,
|
||||||
|
std::move(_options)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,13 +466,9 @@ FunctionType const* TypeProvider::function(
|
|||||||
strings _parameterNames,
|
strings _parameterNames,
|
||||||
strings _returnParameterNames,
|
strings _returnParameterNames,
|
||||||
FunctionType::Kind _kind,
|
FunctionType::Kind _kind,
|
||||||
bool _arbitraryParameters,
|
|
||||||
StateMutability _stateMutability,
|
StateMutability _stateMutability,
|
||||||
Declaration const* _declaration,
|
Declaration const* _declaration,
|
||||||
bool _gasSet,
|
FunctionType::Options _options
|
||||||
bool _valueSet,
|
|
||||||
bool _bound,
|
|
||||||
bool _saltSet
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return createAndGet<FunctionType>(
|
return createAndGet<FunctionType>(
|
||||||
@ -476,13 +477,9 @@ FunctionType const* TypeProvider::function(
|
|||||||
_parameterNames,
|
_parameterNames,
|
||||||
_returnParameterNames,
|
_returnParameterNames,
|
||||||
_kind,
|
_kind,
|
||||||
_arbitraryParameters,
|
|
||||||
_stateMutability,
|
_stateMutability,
|
||||||
_declaration,
|
_declaration,
|
||||||
_gasSet,
|
std::move(_options)
|
||||||
_valueSet,
|
|
||||||
_bound,
|
|
||||||
_saltSet
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,8 +149,8 @@ public:
|
|||||||
strings const& _parameterTypes,
|
strings const& _parameterTypes,
|
||||||
strings const& _returnParameterTypes,
|
strings const& _returnParameterTypes,
|
||||||
FunctionType::Kind _kind = FunctionType::Kind::Internal,
|
FunctionType::Kind _kind = FunctionType::Kind::Internal,
|
||||||
bool _arbitraryParameters = false,
|
StateMutability _stateMutability = StateMutability::NonPayable,
|
||||||
StateMutability _stateMutability = StateMutability::NonPayable
|
FunctionType::Options _options = {}
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @returns a highly customized FunctionType, use with care.
|
/// @returns a highly customized FunctionType, use with care.
|
||||||
@ -160,13 +160,9 @@ public:
|
|||||||
strings _parameterNames = strings{},
|
strings _parameterNames = strings{},
|
||||||
strings _returnParameterNames = strings{},
|
strings _returnParameterNames = strings{},
|
||||||
FunctionType::Kind _kind = FunctionType::Kind::Internal,
|
FunctionType::Kind _kind = FunctionType::Kind::Internal,
|
||||||
bool _arbitraryParameters = false,
|
|
||||||
StateMutability _stateMutability = StateMutability::NonPayable,
|
StateMutability _stateMutability = StateMutability::NonPayable,
|
||||||
Declaration const* _declaration = nullptr,
|
Declaration const* _declaration = nullptr,
|
||||||
bool _gasSet = false,
|
FunctionType::Options _options = {}
|
||||||
bool _valueSet = false,
|
|
||||||
bool _bound = false,
|
|
||||||
bool _saltSet = false
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
||||||
|
@ -470,15 +470,15 @@ MemberList::MemberMap AddressType::nativeMembers(ASTNode const*) const
|
|||||||
{"balance", TypeProvider::uint256()},
|
{"balance", TypeProvider::uint256()},
|
||||||
{"code", TypeProvider::array(DataLocation::Memory)},
|
{"code", TypeProvider::array(DataLocation::Memory)},
|
||||||
{"codehash", TypeProvider::fixedBytes(32)},
|
{"codehash", TypeProvider::fixedBytes(32)},
|
||||||
{"call", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
|
{"call", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, StateMutability::Payable)},
|
||||||
{"callcode", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
|
{"callcode", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, StateMutability::Payable)},
|
||||||
{"delegatecall", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false, StateMutability::NonPayable)},
|
{"delegatecall", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, StateMutability::NonPayable)},
|
||||||
{"staticcall", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)}
|
{"staticcall", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, StateMutability::View)}
|
||||||
};
|
};
|
||||||
if (m_stateMutability == StateMutability::Payable)
|
if (m_stateMutability == StateMutability::Payable)
|
||||||
{
|
{
|
||||||
members.emplace_back(MemberList::Member{"send", TypeProvider::function(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send, false, StateMutability::NonPayable)});
|
members.emplace_back(MemberList::Member{"send", TypeProvider::function(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send, StateMutability::NonPayable)});
|
||||||
members.emplace_back(MemberList::Member{"transfer", TypeProvider::function(strings{"uint"}, strings(), FunctionType::Kind::Transfer, false, StateMutability::NonPayable)});
|
members.emplace_back(MemberList::Member{"transfer", TypeProvider::function(strings{"uint"}, strings(), FunctionType::Kind::Transfer, StateMutability::NonPayable)});
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
@ -2852,7 +2852,6 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
|
|||||||
parameterNames,
|
parameterNames,
|
||||||
strings{""},
|
strings{""},
|
||||||
Kind::Creation,
|
Kind::Creation,
|
||||||
false,
|
|
||||||
stateMutability
|
stateMutability
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -2946,11 +2945,11 @@ string FunctionType::richIdentifier() const
|
|||||||
}
|
}
|
||||||
id += "_" + stateMutabilityToString(m_stateMutability);
|
id += "_" + stateMutabilityToString(m_stateMutability);
|
||||||
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
|
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
|
||||||
if (m_gasSet)
|
if (gasSet())
|
||||||
id += "gas";
|
id += "gas";
|
||||||
if (m_valueSet)
|
if (valueSet())
|
||||||
id += "value";
|
id += "value";
|
||||||
if (m_saltSet)
|
if (saltSet())
|
||||||
id += "salt";
|
id += "salt";
|
||||||
if (bound())
|
if (bound())
|
||||||
id += "bound_to" + identifierList(selfType());
|
id += "bound_to" + identifierList(selfType());
|
||||||
@ -3023,8 +3022,18 @@ TypeResult FunctionType::binaryOperatorResult(Token _operator, Type const* _othe
|
|||||||
if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
|
if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
FunctionType const& other = dynamic_cast<FunctionType const&>(*_other);
|
FunctionType const& other = dynamic_cast<FunctionType const&>(*_other);
|
||||||
if (kind() == Kind::Internal && other.kind() == Kind::Internal && sizeOnStack() == 1 && other.sizeOnStack() == 1)
|
if (kind() == Kind::Internal && sizeOnStack() == 1 && other.kind() == Kind::Internal && other.sizeOnStack() == 1)
|
||||||
return commonType(this, _other);
|
return commonType(this, _other);
|
||||||
|
else if (
|
||||||
|
kind() == Kind::External &&
|
||||||
|
sizeOnStack() == 2 &&
|
||||||
|
!bound() &&
|
||||||
|
other.kind() == Kind::External &&
|
||||||
|
other.sizeOnStack() == 2 &&
|
||||||
|
!other.bound()
|
||||||
|
)
|
||||||
|
return commonType(this, _other);
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3098,11 +3107,11 @@ bool FunctionType::nameable() const
|
|||||||
{
|
{
|
||||||
return
|
return
|
||||||
(m_kind == Kind::Internal || m_kind == Kind::External) &&
|
(m_kind == Kind::Internal || m_kind == Kind::External) &&
|
||||||
!m_bound &&
|
!bound() &&
|
||||||
!m_arbitraryParameters &&
|
!takesArbitraryParameters() &&
|
||||||
!m_gasSet &&
|
!gasSet() &&
|
||||||
!m_valueSet &&
|
!valueSet() &&
|
||||||
!m_saltSet;
|
!saltSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
||||||
@ -3144,11 +3153,11 @@ vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_gasSet)
|
if (gasSet())
|
||||||
slots.emplace_back("gas", TypeProvider::uint256());
|
slots.emplace_back("gas", TypeProvider::uint256());
|
||||||
if (m_valueSet)
|
if (valueSet())
|
||||||
slots.emplace_back("value", TypeProvider::uint256());
|
slots.emplace_back("value", TypeProvider::uint256());
|
||||||
if (m_saltSet)
|
if (saltSet())
|
||||||
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
|
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
|
||||||
if (bound())
|
if (bound())
|
||||||
slots.emplace_back("self", m_parameterTypes.front());
|
slots.emplace_back("self", m_parameterTypes.front());
|
||||||
@ -3180,13 +3189,13 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
|
|||||||
if (variable && retParamTypes.get().empty())
|
if (variable && retParamTypes.get().empty())
|
||||||
return FunctionTypePointer();
|
return FunctionTypePointer();
|
||||||
|
|
||||||
|
solAssert(!takesArbitraryParameters());
|
||||||
return TypeProvider::function(
|
return TypeProvider::function(
|
||||||
paramTypes,
|
paramTypes,
|
||||||
retParamTypes,
|
retParamTypes,
|
||||||
m_parameterNames,
|
m_parameterNames,
|
||||||
m_returnParameterNames,
|
m_returnParameterNames,
|
||||||
m_kind,
|
m_kind,
|
||||||
m_arbitraryParameters,
|
|
||||||
m_stateMutability,
|
m_stateMutability,
|
||||||
m_declaration
|
m_declaration
|
||||||
);
|
);
|
||||||
@ -3241,12 +3250,9 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
|
|||||||
strings(1, ""),
|
strings(1, ""),
|
||||||
strings(1, ""),
|
strings(1, ""),
|
||||||
Kind::SetValue,
|
Kind::SetValue,
|
||||||
false,
|
|
||||||
StateMutability::Pure,
|
StateMutability::Pure,
|
||||||
nullptr,
|
nullptr,
|
||||||
m_gasSet,
|
Options::fromFunctionType(*this)
|
||||||
m_valueSet,
|
|
||||||
m_saltSet
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -3259,12 +3265,9 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
|
|||||||
strings(1, ""),
|
strings(1, ""),
|
||||||
strings(1, ""),
|
strings(1, ""),
|
||||||
Kind::SetGas,
|
Kind::SetGas,
|
||||||
false,
|
|
||||||
StateMutability::Pure,
|
StateMutability::Pure,
|
||||||
nullptr,
|
nullptr,
|
||||||
m_gasSet,
|
Options::fromFunctionType(*this)
|
||||||
m_valueSet,
|
|
||||||
m_saltSet
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return members;
|
return members;
|
||||||
@ -3292,7 +3295,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
|
|||||||
|
|
||||||
Type const* FunctionType::encodingType() const
|
Type const* FunctionType::encodingType() const
|
||||||
{
|
{
|
||||||
if (m_gasSet || m_valueSet)
|
if (gasSet() || valueSet())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
// Only external functions can be encoded, internal functions cannot leave code boundaries.
|
// Only external functions can be encoded, internal functions cannot leave code boundaries.
|
||||||
if (m_kind == Kind::External)
|
if (m_kind == Kind::External)
|
||||||
@ -3311,7 +3314,7 @@ TypeResult FunctionType::interfaceType(bool /*_inLibrary*/) const
|
|||||||
|
|
||||||
Type const* FunctionType::mobileType() const
|
Type const* FunctionType::mobileType() const
|
||||||
{
|
{
|
||||||
if (m_valueSet || m_gasSet || m_saltSet || m_bound)
|
if (valueSet() || gasSet() || saltSet() || bound())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// return function without parameter names
|
// return function without parameter names
|
||||||
@ -3321,13 +3324,9 @@ Type const* FunctionType::mobileType() const
|
|||||||
strings(m_parameterTypes.size()),
|
strings(m_parameterTypes.size()),
|
||||||
strings(m_returnParameterNames.size()),
|
strings(m_returnParameterNames.size()),
|
||||||
m_kind,
|
m_kind,
|
||||||
m_arbitraryParameters,
|
|
||||||
m_stateMutability,
|
m_stateMutability,
|
||||||
m_declaration,
|
m_declaration,
|
||||||
m_gasSet,
|
Options::fromFunctionType(*this)
|
||||||
m_valueSet,
|
|
||||||
m_bound,
|
|
||||||
m_saltSet
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3413,7 +3412,7 @@ bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) con
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
//@todo this is ugly, but cannot be prevented right now
|
//@todo this is ugly, but cannot be prevented right now
|
||||||
if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet || m_saltSet != _other.m_saltSet)
|
if (gasSet() != _other.gasSet() || valueSet() != _other.valueSet() || saltSet() != _other.saltSet())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bound() != _other.bound())
|
if (bound() != _other.bound())
|
||||||
@ -3524,41 +3523,39 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
|||||||
Type const* FunctionType::copyAndSetCallOptions(bool _setGas, bool _setValue, bool _setSalt) const
|
Type const* FunctionType::copyAndSetCallOptions(bool _setGas, bool _setValue, bool _setSalt) const
|
||||||
{
|
{
|
||||||
solAssert(m_kind != Kind::Declaration, "");
|
solAssert(m_kind != Kind::Declaration, "");
|
||||||
|
Options options = Options::fromFunctionType(*this);
|
||||||
|
if (_setGas) options.gasSet = true;
|
||||||
|
if (_setValue) options.valueSet = true;
|
||||||
|
if (_setSalt) options.saltSet = true;
|
||||||
return TypeProvider::function(
|
return TypeProvider::function(
|
||||||
m_parameterTypes,
|
m_parameterTypes,
|
||||||
m_returnParameterTypes,
|
m_returnParameterTypes,
|
||||||
m_parameterNames,
|
m_parameterNames,
|
||||||
m_returnParameterNames,
|
m_returnParameterNames,
|
||||||
m_kind,
|
m_kind,
|
||||||
m_arbitraryParameters,
|
|
||||||
m_stateMutability,
|
m_stateMutability,
|
||||||
m_declaration,
|
m_declaration,
|
||||||
m_gasSet || _setGas,
|
options
|
||||||
m_valueSet || _setValue,
|
|
||||||
m_saltSet || _setSalt,
|
|
||||||
m_bound
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionTypePointer FunctionType::asBoundFunction() const
|
FunctionTypePointer FunctionType::asBoundFunction() const
|
||||||
{
|
{
|
||||||
solAssert(!m_parameterTypes.empty(), "");
|
solAssert(!m_parameterTypes.empty(), "");
|
||||||
solAssert(!m_gasSet, "");
|
solAssert(!gasSet(), "");
|
||||||
solAssert(!m_valueSet, "");
|
solAssert(!valueSet(), "");
|
||||||
solAssert(!m_saltSet, "");
|
solAssert(!saltSet(), "");
|
||||||
|
Options options = Options::fromFunctionType(*this);
|
||||||
|
options.bound = true;
|
||||||
return TypeProvider::function(
|
return TypeProvider::function(
|
||||||
m_parameterTypes,
|
m_parameterTypes,
|
||||||
m_returnParameterTypes,
|
m_returnParameterTypes,
|
||||||
m_parameterNames,
|
m_parameterNames,
|
||||||
m_returnParameterNames,
|
m_returnParameterNames,
|
||||||
m_kind,
|
m_kind,
|
||||||
m_arbitraryParameters,
|
|
||||||
m_stateMutability,
|
m_stateMutability,
|
||||||
m_declaration,
|
m_declaration,
|
||||||
m_gasSet,
|
options
|
||||||
m_valueSet,
|
|
||||||
m_saltSet,
|
|
||||||
true
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3596,13 +3593,9 @@ FunctionTypePointer FunctionType::asExternallyCallableFunction(bool _inLibrary)
|
|||||||
m_parameterNames,
|
m_parameterNames,
|
||||||
m_returnParameterNames,
|
m_returnParameterNames,
|
||||||
kind,
|
kind,
|
||||||
m_arbitraryParameters,
|
|
||||||
m_stateMutability,
|
m_stateMutability,
|
||||||
m_declaration,
|
m_declaration,
|
||||||
m_gasSet,
|
Options::fromFunctionType(*this)
|
||||||
m_valueSet,
|
|
||||||
m_saltSet,
|
|
||||||
m_bound
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3807,7 +3800,6 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
|||||||
strings{string{}},
|
strings{string{}},
|
||||||
strings{string{}},
|
strings{string{}},
|
||||||
FunctionType::Kind::Wrap,
|
FunctionType::Kind::Wrap,
|
||||||
false, /*_arbitraryParameters */
|
|
||||||
StateMutability::Pure
|
StateMutability::Pure
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -3819,7 +3811,6 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
|||||||
strings{string{}},
|
strings{string{}},
|
||||||
strings{string{}},
|
strings{string{}},
|
||||||
FunctionType::Kind::Unwrap,
|
FunctionType::Kind::Unwrap,
|
||||||
false, /* _arbitraryParameters */
|
|
||||||
StateMutability::Pure
|
StateMutability::Pure
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -3834,8 +3825,9 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
|||||||
strings{},
|
strings{},
|
||||||
strings{string()},
|
strings{string()},
|
||||||
FunctionType::Kind::BytesConcat,
|
FunctionType::Kind::BytesConcat,
|
||||||
/* _arbitraryParameters */ true,
|
StateMutability::Pure,
|
||||||
StateMutability::Pure
|
nullptr,
|
||||||
|
FunctionType::Options::withArbitraryParameters()
|
||||||
));
|
));
|
||||||
|
|
||||||
return members;
|
return members;
|
||||||
@ -3958,7 +3950,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
return MemberList::MemberMap({
|
return MemberList::MemberMap({
|
||||||
{"coinbase", TypeProvider::payableAddress()},
|
{"coinbase", TypeProvider::payableAddress()},
|
||||||
{"timestamp", TypeProvider::uint256()},
|
{"timestamp", TypeProvider::uint256()},
|
||||||
{"blockhash", TypeProvider::function(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)},
|
{"blockhash", TypeProvider::function(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, StateMutability::View)},
|
||||||
{"difficulty", TypeProvider::uint256()},
|
{"difficulty", TypeProvider::uint256()},
|
||||||
{"number", TypeProvider::uint256()},
|
{"number", TypeProvider::uint256()},
|
||||||
{"gaslimit", TypeProvider::uint256()},
|
{"gaslimit", TypeProvider::uint256()},
|
||||||
@ -3986,8 +3978,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
strings{},
|
strings{},
|
||||||
strings{1, ""},
|
strings{1, ""},
|
||||||
FunctionType::Kind::ABIEncode,
|
FunctionType::Kind::ABIEncode,
|
||||||
true,
|
StateMutability::Pure,
|
||||||
StateMutability::Pure
|
nullptr,
|
||||||
|
FunctionType::Options::withArbitraryParameters()
|
||||||
)},
|
)},
|
||||||
{"encodePacked", TypeProvider::function(
|
{"encodePacked", TypeProvider::function(
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
@ -3995,8 +3988,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
strings{},
|
strings{},
|
||||||
strings{1, ""},
|
strings{1, ""},
|
||||||
FunctionType::Kind::ABIEncodePacked,
|
FunctionType::Kind::ABIEncodePacked,
|
||||||
true,
|
StateMutability::Pure,
|
||||||
StateMutability::Pure
|
nullptr,
|
||||||
|
FunctionType::Options::withArbitraryParameters()
|
||||||
)},
|
)},
|
||||||
{"encodeWithSelector", TypeProvider::function(
|
{"encodeWithSelector", TypeProvider::function(
|
||||||
TypePointers{TypeProvider::fixedBytes(4)},
|
TypePointers{TypeProvider::fixedBytes(4)},
|
||||||
@ -4004,8 +3998,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
strings{1, ""},
|
strings{1, ""},
|
||||||
strings{1, ""},
|
strings{1, ""},
|
||||||
FunctionType::Kind::ABIEncodeWithSelector,
|
FunctionType::Kind::ABIEncodeWithSelector,
|
||||||
true,
|
StateMutability::Pure,
|
||||||
StateMutability::Pure
|
nullptr,
|
||||||
|
FunctionType::Options::withArbitraryParameters()
|
||||||
)},
|
)},
|
||||||
{"encodeCall", TypeProvider::function(
|
{"encodeCall", TypeProvider::function(
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
@ -4013,8 +4008,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
strings{},
|
strings{},
|
||||||
strings{1, ""},
|
strings{1, ""},
|
||||||
FunctionType::Kind::ABIEncodeCall,
|
FunctionType::Kind::ABIEncodeCall,
|
||||||
true,
|
StateMutability::Pure,
|
||||||
StateMutability::Pure
|
nullptr,
|
||||||
|
FunctionType::Options::withArbitraryParameters()
|
||||||
)},
|
)},
|
||||||
{"encodeWithSignature", TypeProvider::function(
|
{"encodeWithSignature", TypeProvider::function(
|
||||||
TypePointers{TypeProvider::array(DataLocation::Memory, true)},
|
TypePointers{TypeProvider::array(DataLocation::Memory, true)},
|
||||||
@ -4022,8 +4018,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
strings{1, ""},
|
strings{1, ""},
|
||||||
strings{1, ""},
|
strings{1, ""},
|
||||||
FunctionType::Kind::ABIEncodeWithSignature,
|
FunctionType::Kind::ABIEncodeWithSignature,
|
||||||
true,
|
StateMutability::Pure,
|
||||||
StateMutability::Pure
|
nullptr,
|
||||||
|
FunctionType::Options::withArbitraryParameters()
|
||||||
)},
|
)},
|
||||||
{"decode", TypeProvider::function(
|
{"decode", TypeProvider::function(
|
||||||
TypePointers(),
|
TypePointers(),
|
||||||
@ -4031,8 +4028,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
strings{},
|
strings{},
|
||||||
strings{},
|
strings{},
|
||||||
FunctionType::Kind::ABIDecode,
|
FunctionType::Kind::ABIDecode,
|
||||||
true,
|
StateMutability::Pure,
|
||||||
StateMutability::Pure
|
nullptr,
|
||||||
|
FunctionType::Options::withArbitraryParameters()
|
||||||
)}
|
)}
|
||||||
});
|
});
|
||||||
case Kind::MetaType:
|
case Kind::MetaType:
|
||||||
|
@ -1110,11 +1110,7 @@ public:
|
|||||||
u256 storageSize() const override { return underlyingType().storageSize(); }
|
u256 storageSize() const override { return underlyingType().storageSize(); }
|
||||||
unsigned storageBytes() const override { return underlyingType().storageBytes(); }
|
unsigned storageBytes() const override { return underlyingType().storageBytes(); }
|
||||||
|
|
||||||
bool isValueType() const override
|
bool isValueType() const override { return true; }
|
||||||
{
|
|
||||||
solAssert(underlyingType().isValueType(), "");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool nameable() const override
|
bool nameable() const override
|
||||||
{
|
{
|
||||||
solAssert(underlyingType().nameable(), "");
|
solAssert(underlyingType().nameable(), "");
|
||||||
@ -1247,6 +1243,38 @@ public:
|
|||||||
/// Cannot be called.
|
/// Cannot be called.
|
||||||
Declaration,
|
Declaration,
|
||||||
};
|
};
|
||||||
|
struct Options
|
||||||
|
{
|
||||||
|
/// true iff the function takes an arbitrary number of arguments of arbitrary types
|
||||||
|
bool arbitraryParameters = false;
|
||||||
|
/// true iff the gas value to be used is on the stack
|
||||||
|
bool gasSet = false;
|
||||||
|
/// true iff the value to be sent is on the stack
|
||||||
|
bool valueSet = false;
|
||||||
|
/// iff the salt value (for create2) to be used is on the stack
|
||||||
|
bool saltSet = false;
|
||||||
|
/// true iff the function is called as arg1.fun(arg2, ..., argn).
|
||||||
|
/// This is achieved through the "using for" directive.
|
||||||
|
bool bound = false;
|
||||||
|
|
||||||
|
static Options withArbitraryParameters()
|
||||||
|
{
|
||||||
|
Options result;
|
||||||
|
result.arbitraryParameters = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static Options fromFunctionType(FunctionType const& _type)
|
||||||
|
{
|
||||||
|
Options result;
|
||||||
|
result.arbitraryParameters = _type.takesArbitraryParameters();
|
||||||
|
result.gasSet = _type.gasSet();
|
||||||
|
result.valueSet = _type.valueSet();
|
||||||
|
result.saltSet = _type.saltSet();
|
||||||
|
result.bound = _type.bound();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Creates the type of a function.
|
/// Creates the type of a function.
|
||||||
/// @arg _kind must be Kind::Internal, Kind::External or Kind::Declaration.
|
/// @arg _kind must be Kind::Internal, Kind::External or Kind::Declaration.
|
||||||
@ -1263,18 +1291,21 @@ public:
|
|||||||
strings const& _parameterTypes,
|
strings const& _parameterTypes,
|
||||||
strings const& _returnParameterTypes,
|
strings const& _returnParameterTypes,
|
||||||
Kind _kind,
|
Kind _kind,
|
||||||
bool _arbitraryParameters = false,
|
StateMutability _stateMutability = StateMutability::NonPayable,
|
||||||
StateMutability _stateMutability = StateMutability::NonPayable
|
Options _options = Options{false, false, false, false, false}
|
||||||
): FunctionType(
|
): FunctionType(
|
||||||
parseElementaryTypeVector(_parameterTypes),
|
parseElementaryTypeVector(_parameterTypes),
|
||||||
parseElementaryTypeVector(_returnParameterTypes),
|
parseElementaryTypeVector(_returnParameterTypes),
|
||||||
strings(_parameterTypes.size(), ""),
|
strings(_parameterTypes.size(), ""),
|
||||||
strings(_returnParameterTypes.size(), ""),
|
strings(_returnParameterTypes.size(), ""),
|
||||||
_kind,
|
_kind,
|
||||||
_arbitraryParameters,
|
_stateMutability,
|
||||||
_stateMutability
|
nullptr,
|
||||||
|
std::move(_options)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
// In this constructor, only the "arbitrary Parameters" option should be used.
|
||||||
|
solAssert(!bound() && !gasSet() && !valueSet() && !saltSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detailed constructor, use with care.
|
/// Detailed constructor, use with care.
|
||||||
@ -1284,13 +1315,9 @@ public:
|
|||||||
strings _parameterNames = strings(),
|
strings _parameterNames = strings(),
|
||||||
strings _returnParameterNames = strings(),
|
strings _returnParameterNames = strings(),
|
||||||
Kind _kind = Kind::Internal,
|
Kind _kind = Kind::Internal,
|
||||||
bool _arbitraryParameters = false,
|
|
||||||
StateMutability _stateMutability = StateMutability::NonPayable,
|
StateMutability _stateMutability = StateMutability::NonPayable,
|
||||||
Declaration const* _declaration = nullptr,
|
Declaration const* _declaration = nullptr,
|
||||||
bool _gasSet = false,
|
Options _options = Options{false, false, false, false, false}
|
||||||
bool _valueSet = false,
|
|
||||||
bool _saltSet = false,
|
|
||||||
bool _bound = false
|
|
||||||
):
|
):
|
||||||
m_parameterTypes(std::move(_parameterTypes)),
|
m_parameterTypes(std::move(_parameterTypes)),
|
||||||
m_returnParameterTypes(std::move(_returnParameterTypes)),
|
m_returnParameterTypes(std::move(_returnParameterTypes)),
|
||||||
@ -1298,12 +1325,8 @@ public:
|
|||||||
m_returnParameterNames(std::move(_returnParameterNames)),
|
m_returnParameterNames(std::move(_returnParameterNames)),
|
||||||
m_kind(_kind),
|
m_kind(_kind),
|
||||||
m_stateMutability(_stateMutability),
|
m_stateMutability(_stateMutability),
|
||||||
m_arbitraryParameters(_arbitraryParameters),
|
|
||||||
m_gasSet(_gasSet),
|
|
||||||
m_valueSet(_valueSet),
|
|
||||||
m_bound(_bound),
|
|
||||||
m_declaration(_declaration),
|
m_declaration(_declaration),
|
||||||
m_saltSet(_saltSet)
|
m_options(std::move(_options))
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
m_parameterNames.size() == m_parameterTypes.size(),
|
m_parameterNames.size() == m_parameterTypes.size(),
|
||||||
@ -1314,7 +1337,7 @@ public:
|
|||||||
"Return parameter names list must match return parameter types list!"
|
"Return parameter names list must match return parameter types list!"
|
||||||
);
|
);
|
||||||
solAssert(
|
solAssert(
|
||||||
!m_bound || !m_parameterTypes.empty(),
|
!bound() || !m_parameterTypes.empty(),
|
||||||
"Attempted construction of bound function without self type"
|
"Attempted construction of bound function without self type"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1408,7 +1431,7 @@ public:
|
|||||||
/// The only functions that do not pad are hash functions, the low-level call functions
|
/// The only functions that do not pad are hash functions, the low-level call functions
|
||||||
/// and abi.encodePacked.
|
/// and abi.encodePacked.
|
||||||
bool padArguments() const;
|
bool padArguments() const;
|
||||||
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
|
bool takesArbitraryParameters() const { return m_options.arbitraryParameters; }
|
||||||
/// true iff the function takes a single bytes parameter and it is passed on without padding.
|
/// true iff the function takes a single bytes parameter and it is passed on without padding.
|
||||||
bool takesSinglePackedBytesParameter() const
|
bool takesSinglePackedBytesParameter() const
|
||||||
{
|
{
|
||||||
@ -1427,10 +1450,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gasSet() const { return m_gasSet; }
|
bool gasSet() const { return m_options.gasSet; }
|
||||||
bool valueSet() const { return m_valueSet; }
|
bool valueSet() const { return m_options.valueSet; }
|
||||||
bool saltSet() const { return m_saltSet; }
|
bool saltSet() const { return m_options.saltSet; }
|
||||||
bool bound() const { return m_bound; }
|
bool bound() const { return m_options.bound; }
|
||||||
|
|
||||||
/// @returns a copy of this type, where gas or value are set manually. This will never set one
|
/// @returns a copy of this type, where gas or value are set manually. This will never set one
|
||||||
/// of the parameters to false.
|
/// of the parameters to false.
|
||||||
@ -1458,15 +1481,8 @@ private:
|
|||||||
std::vector<std::string> m_returnParameterNames;
|
std::vector<std::string> m_returnParameterNames;
|
||||||
Kind const m_kind;
|
Kind const m_kind;
|
||||||
StateMutability m_stateMutability = StateMutability::NonPayable;
|
StateMutability m_stateMutability = StateMutability::NonPayable;
|
||||||
/// true if the function takes an arbitrary number of arguments of arbitrary types
|
|
||||||
bool const m_arbitraryParameters = false;
|
|
||||||
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
|
|
||||||
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
|
|
||||||
/// true iff the function is called as arg1.fun(arg2, ..., argn).
|
|
||||||
/// This is achieved through the "using for" directive.
|
|
||||||
bool const m_bound = false;
|
|
||||||
Declaration const* m_declaration = nullptr;
|
Declaration const* m_declaration = nullptr;
|
||||||
bool m_saltSet = false; ///< true iff the salt value to be used is on the stack
|
Options const m_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +54,7 @@ static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPoi
|
|||||||
void CompilerUtils::initialiseFreeMemoryPointer()
|
void CompilerUtils::initialiseFreeMemoryPointer()
|
||||||
{
|
{
|
||||||
size_t reservedMemory = m_context.reservedMemory();
|
size_t reservedMemory = m_context.reservedMemory();
|
||||||
solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63, "");
|
solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63);
|
||||||
m_context << (u256(generalPurposeMemoryStart) + reservedMemory);
|
m_context << (u256(generalPurposeMemoryStart) + reservedMemory);
|
||||||
storeFreeMemoryPointer();
|
storeFreeMemoryPointer();
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ void CompilerUtils::toSizeAfterFreeMemoryPointer()
|
|||||||
|
|
||||||
void CompilerUtils::revertWithStringData(Type const& _argumentType)
|
void CompilerUtils::revertWithStringData(Type const& _argumentType)
|
||||||
{
|
{
|
||||||
solAssert(_argumentType.isImplicitlyConvertibleTo(*TypeProvider::fromElementaryTypeName("string memory")), "");
|
solAssert(_argumentType.isImplicitlyConvertibleTo(*TypeProvider::fromElementaryTypeName("string memory")));
|
||||||
fetchFreeMemoryPointer();
|
fetchFreeMemoryPointer();
|
||||||
m_context << util::selectorFromSignature("Error(string)");
|
m_context << util::selectorFromSignature("Error(string)");
|
||||||
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||||
@ -173,9 +173,9 @@ void CompilerUtils::loadFromMemoryDynamic(
|
|||||||
|
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
||||||
{
|
{
|
||||||
solAssert(!arrayType->isDynamicallySized(), "");
|
solAssert(!arrayType->isDynamicallySized());
|
||||||
solAssert(!_fromCalldata, "");
|
solAssert(!_fromCalldata);
|
||||||
solAssert(_padToWordBoundaries, "");
|
solAssert(_padToWordBoundaries);
|
||||||
if (_keepUpdatedMemoryOffset)
|
if (_keepUpdatedMemoryOffset)
|
||||||
m_context << arrayType->memoryDataSize() << Instruction::ADD;
|
m_context << arrayType->memoryDataSize() << Instruction::ADD;
|
||||||
}
|
}
|
||||||
@ -251,7 +251,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem
|
|||||||
// Use the new Yul-based decoding function
|
// Use the new Yul-based decoding function
|
||||||
auto stackHeightBefore = m_context.stackHeight();
|
auto stackHeightBefore = m_context.stackHeight();
|
||||||
abiDecodeV2(_typeParameters, _fromMemory);
|
abiDecodeV2(_typeParameters, _fromMemory);
|
||||||
solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, "");
|
solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +290,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem
|
|||||||
);
|
);
|
||||||
// @todo If base type is an array or struct, it is still calldata-style encoded, so
|
// @todo If base type is an array or struct, it is still calldata-style encoded, so
|
||||||
// we would have to convert it like below.
|
// we would have to convert it like below.
|
||||||
solAssert(arrayType.location() == DataLocation::Memory, "");
|
solAssert(arrayType.location() == DataLocation::Memory);
|
||||||
if (arrayType.isDynamicallySized())
|
if (arrayType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
// compute data pointer
|
// compute data pointer
|
||||||
@ -430,7 +430,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
// stack: <v1> <v2> ... <vn> <mem>
|
// stack: <v1> <v2> ... <vn> <mem>
|
||||||
bool const encoderV2 = m_context.useABICoderV2();
|
bool const encoderV2 = m_context.useABICoderV2();
|
||||||
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
|
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
|
||||||
solAssert(targetTypes.size() == _givenTypes.size(), "");
|
solAssert(targetTypes.size() == _givenTypes.size());
|
||||||
for (Type const*& t: targetTypes)
|
for (Type const*& t: targetTypes)
|
||||||
{
|
{
|
||||||
Type const* tEncoding = t->fullEncodingType(_encodeAsLibraryTypes, encoderV2, !_padToWordBoundaries);
|
Type const* tEncoding = t->fullEncodingType(_encodeAsLibraryTypes, encoderV2, !_padToWordBoundaries);
|
||||||
@ -449,7 +449,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
);
|
);
|
||||||
auto stackHeightBefore = m_context.stackHeight();
|
auto stackHeightBefore = m_context.stackHeight();
|
||||||
abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes, _padToWordBoundaries);
|
abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes, _padToWordBoundaries);
|
||||||
solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), "");
|
solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,8 +489,8 @@ void CompilerUtils::encodeToMemory(
|
|||||||
{
|
{
|
||||||
// special case: convert storage reference type to value type - this is only
|
// special case: convert storage reference type to value type - this is only
|
||||||
// possible for library calls where we just forward the storage reference
|
// possible for library calls where we just forward the storage reference
|
||||||
solAssert(_encodeAsLibraryTypes, "");
|
solAssert(_encodeAsLibraryTypes);
|
||||||
solAssert(_givenTypes[i]->sizeOnStack() == 1, "");
|
solAssert(_givenTypes[i]->sizeOnStack() == 1);
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
|
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
|
||||||
@ -638,7 +638,7 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
|
|||||||
{
|
{
|
||||||
if (_type.baseType()->hasSimpleZeroValueInMemory())
|
if (_type.baseType()->hasSimpleZeroValueInMemory())
|
||||||
{
|
{
|
||||||
solAssert(_type.baseType()->isValueType(), "");
|
solAssert(_type.baseType()->isValueType());
|
||||||
Whiskers templ(R"({
|
Whiskers templ(R"({
|
||||||
let size := mul(length, <element_size>)
|
let size := mul(length, <element_size>)
|
||||||
// cheap way of zero-initializing a memory range
|
// cheap way of zero-initializing a memory range
|
||||||
@ -774,9 +774,9 @@ void CompilerUtils::convertType(
|
|||||||
|
|
||||||
if (stackTypeCategory == Type::Category::UserDefinedValueType)
|
if (stackTypeCategory == Type::Category::UserDefinedValueType)
|
||||||
{
|
{
|
||||||
solAssert(_cleanupNeeded, "");
|
solAssert(_cleanupNeeded);
|
||||||
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_typeOnStack);
|
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_typeOnStack);
|
||||||
solAssert(_typeOnStack == _targetType || _targetType == userDefined.underlyingType(), "");
|
solAssert(_typeOnStack == _targetType || _targetType == userDefined.underlyingType());
|
||||||
return convertType(
|
return convertType(
|
||||||
userDefined.underlyingType(),
|
userDefined.underlyingType(),
|
||||||
_targetType,
|
_targetType,
|
||||||
@ -787,9 +787,9 @@ void CompilerUtils::convertType(
|
|||||||
}
|
}
|
||||||
if (targetTypeCategory == Type::Category::UserDefinedValueType)
|
if (targetTypeCategory == Type::Category::UserDefinedValueType)
|
||||||
{
|
{
|
||||||
solAssert(_cleanupNeeded, "");
|
solAssert(_cleanupNeeded);
|
||||||
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_targetType);
|
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_targetType);
|
||||||
solAssert(_typeOnStack.isImplicitlyConvertibleTo(userDefined.underlyingType()), "");
|
solAssert(_typeOnStack.isImplicitlyConvertibleTo(userDefined.underlyingType()));
|
||||||
return convertType(
|
return convertType(
|
||||||
_typeOnStack,
|
_typeOnStack,
|
||||||
userDefined.underlyingType(),
|
userDefined.underlyingType(),
|
||||||
@ -829,7 +829,7 @@ void CompilerUtils::convertType(
|
|||||||
}
|
}
|
||||||
else if (targetTypeCategory == Type::Category::Address)
|
else if (targetTypeCategory == Type::Category::Address)
|
||||||
{
|
{
|
||||||
solAssert(typeOnStack.numBytes() * 8 == 160, "");
|
solAssert(typeOnStack.numBytes() * 8 == 160);
|
||||||
rightShiftNumberOnStack(256 - 160);
|
rightShiftNumberOnStack(256 - 160);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -849,7 +849,7 @@ void CompilerUtils::convertType(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::Category::Enum:
|
case Type::Category::Enum:
|
||||||
solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer, "");
|
solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer);
|
||||||
if (enumOverflowCheckPending)
|
if (enumOverflowCheckPending)
|
||||||
{
|
{
|
||||||
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack);
|
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack);
|
||||||
@ -885,13 +885,13 @@ void CompilerUtils::convertType(
|
|||||||
cleanHigherOrderBits(*typeOnStack);
|
cleanHigherOrderBits(*typeOnStack);
|
||||||
}
|
}
|
||||||
else if (stackTypeCategory == Type::Category::Address)
|
else if (stackTypeCategory == Type::Category::Address)
|
||||||
solAssert(targetBytesType.numBytes() * 8 == 160, "");
|
solAssert(targetBytesType.numBytes() * 8 == 160);
|
||||||
leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8);
|
leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8);
|
||||||
}
|
}
|
||||||
else if (targetTypeCategory == Type::Category::Enum)
|
else if (targetTypeCategory == Type::Category::Enum)
|
||||||
{
|
{
|
||||||
solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested.");
|
solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested.");
|
||||||
solAssert(_typeOnStack.mobileType(), "");
|
solAssert(_typeOnStack.mobileType());
|
||||||
// just clean
|
// just clean
|
||||||
convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
|
convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
|
||||||
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType);
|
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType);
|
||||||
@ -964,13 +964,13 @@ void CompilerUtils::convertType(
|
|||||||
if (targetTypeCategory == Type::Category::FixedBytes)
|
if (targetTypeCategory == Type::Category::FixedBytes)
|
||||||
{
|
{
|
||||||
unsigned const numBytes = dynamic_cast<FixedBytesType const&>(_targetType).numBytes();
|
unsigned const numBytes = dynamic_cast<FixedBytesType const&>(_targetType).numBytes();
|
||||||
solAssert(data.size() <= 32, "");
|
solAssert(data.size() <= 32);
|
||||||
m_context << (u256(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes))));
|
m_context << (u256(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes))));
|
||||||
}
|
}
|
||||||
else if (targetTypeCategory == Type::Category::Array)
|
else if (targetTypeCategory == Type::Category::Array)
|
||||||
{
|
{
|
||||||
auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
|
auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
|
||||||
solAssert(arrayType.isByteArray(), "");
|
solAssert(arrayType.isByteArray());
|
||||||
size_t storageSize = 32 + ((data.size() + 31) / 32) * 32;
|
size_t storageSize = 32 + ((data.size() + 31) / 32) * 32;
|
||||||
allocateMemory(storageSize);
|
allocateMemory(storageSize);
|
||||||
// stack: mempos
|
// stack: mempos
|
||||||
@ -995,10 +995,10 @@ void CompilerUtils::convertType(
|
|||||||
typeOnStack.isByteArray() && !typeOnStack.isString(),
|
typeOnStack.isByteArray() && !typeOnStack.isString(),
|
||||||
"Array types other than bytes not convertible to bytesNN."
|
"Array types other than bytes not convertible to bytesNN."
|
||||||
);
|
);
|
||||||
solAssert(typeOnStack.isDynamicallySized(), "");
|
solAssert(typeOnStack.isDynamicallySized());
|
||||||
|
|
||||||
bool fromCalldata = typeOnStack.dataStoredIn(DataLocation::CallData);
|
bool fromCalldata = typeOnStack.dataStoredIn(DataLocation::CallData);
|
||||||
solAssert(typeOnStack.sizeOnStack() == (fromCalldata ? 2 : 1), "");
|
solAssert(typeOnStack.sizeOnStack() == (fromCalldata ? 2 : 1));
|
||||||
if (fromCalldata)
|
if (fromCalldata)
|
||||||
m_context << Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
|
|
||||||
@ -1012,7 +1012,7 @@ void CompilerUtils::convertType(
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
solAssert(targetTypeCategory == stackTypeCategory, "");
|
solAssert(targetTypeCategory == stackTypeCategory);
|
||||||
auto const& targetType = dynamic_cast<ArrayType const&>(_targetType);
|
auto const& targetType = dynamic_cast<ArrayType const&>(_targetType);
|
||||||
switch (targetType.location())
|
switch (targetType.location())
|
||||||
{
|
{
|
||||||
@ -1034,9 +1034,9 @@ void CompilerUtils::convertType(
|
|||||||
typeOnStack.baseType()->isDynamicallyEncoded()
|
typeOnStack.baseType()->isDynamicallyEncoded()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(m_context.useABICoderV2(), "");
|
solAssert(m_context.useABICoderV2());
|
||||||
// stack: offset length(optional in case of dynamically sized array)
|
// stack: offset length(optional in case of dynamically sized array)
|
||||||
solAssert(typeOnStack.sizeOnStack() == (typeOnStack.isDynamicallySized() ? 2 : 1), "");
|
solAssert(typeOnStack.sizeOnStack() == (typeOnStack.isDynamicallySized() ? 2 : 1));
|
||||||
if (typeOnStack.isDynamicallySized())
|
if (typeOnStack.isDynamicallySized())
|
||||||
m_context << Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
|
|
||||||
@ -1122,9 +1122,9 @@ void CompilerUtils::convertType(
|
|||||||
typeOnStack.arrayType().isByteArray() && !typeOnStack.arrayType().isString(),
|
typeOnStack.arrayType().isByteArray() && !typeOnStack.arrayType().isString(),
|
||||||
"Array types other than bytes not convertible to bytesNN."
|
"Array types other than bytes not convertible to bytesNN."
|
||||||
);
|
);
|
||||||
solAssert(typeOnStack.isDynamicallySized(), "");
|
solAssert(typeOnStack.isDynamicallySized());
|
||||||
solAssert(typeOnStack.dataStoredIn(DataLocation::CallData), "");
|
solAssert(typeOnStack.dataStoredIn(DataLocation::CallData));
|
||||||
solAssert(typeOnStack.sizeOnStack() == 2, "");
|
solAssert(typeOnStack.sizeOnStack() == 2);
|
||||||
|
|
||||||
m_context << Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
m_context.callYulFunction(
|
m_context.callYulFunction(
|
||||||
@ -1138,14 +1138,16 @@ void CompilerUtils::convertType(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
solAssert(_targetType.category() == Type::Category::Array, "");
|
solAssert(_targetType.category() == Type::Category::Array);
|
||||||
auto const& targetArrayType = dynamic_cast<ArrayType const&>(_targetType);
|
auto const& targetArrayType = dynamic_cast<ArrayType const&>(_targetType);
|
||||||
solAssert(typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType), "");
|
solAssert(
|
||||||
|
typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType) ||
|
||||||
|
(typeOnStack.arrayType().isByteArray() && targetArrayType.isByteArray())
|
||||||
|
);
|
||||||
solAssert(
|
solAssert(
|
||||||
typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) &&
|
typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) &&
|
||||||
typeOnStack.arrayType().isDynamicallySized() &&
|
typeOnStack.arrayType().isDynamicallySized() &&
|
||||||
!typeOnStack.arrayType().baseType()->isDynamicallyEncoded(),
|
!typeOnStack.arrayType().baseType()->isDynamicallyEncoded()
|
||||||
""
|
|
||||||
);
|
);
|
||||||
if (!_targetType.dataStoredIn(DataLocation::CallData))
|
if (!_targetType.dataStoredIn(DataLocation::CallData))
|
||||||
return convertType(typeOnStack.arrayType(), _targetType);
|
return convertType(typeOnStack.arrayType(), _targetType);
|
||||||
@ -1153,7 +1155,7 @@ void CompilerUtils::convertType(
|
|||||||
}
|
}
|
||||||
case Type::Category::Struct:
|
case Type::Category::Struct:
|
||||||
{
|
{
|
||||||
solAssert(targetTypeCategory == stackTypeCategory, "");
|
solAssert(targetTypeCategory == stackTypeCategory);
|
||||||
auto& targetType = dynamic_cast<StructType const&>(_targetType);
|
auto& targetType = dynamic_cast<StructType const&>(_targetType);
|
||||||
auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
|
auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
|
||||||
switch (targetType.location())
|
switch (targetType.location())
|
||||||
@ -1182,7 +1184,7 @@ void CompilerUtils::convertType(
|
|||||||
// stack: <memory ptr> <source ref> <memory ptr>
|
// stack: <memory ptr> <source ref> <memory ptr>
|
||||||
for (auto const& member: typeOnStack->members(nullptr))
|
for (auto const& member: typeOnStack->members(nullptr))
|
||||||
{
|
{
|
||||||
solAssert(!member.type->containsNestedMapping(), "");
|
solAssert(!member.type->containsNestedMapping());
|
||||||
pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name);
|
pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name);
|
||||||
_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||||
_context << u256(offsets.second);
|
_context << u256(offsets.second);
|
||||||
@ -1209,7 +1211,7 @@ void CompilerUtils::convertType(
|
|||||||
{
|
{
|
||||||
if (typeOnStack.isDynamicallyEncoded())
|
if (typeOnStack.isDynamicallyEncoded())
|
||||||
{
|
{
|
||||||
solAssert(m_context.useABICoderV2(), "");
|
solAssert(m_context.useABICoderV2());
|
||||||
m_context.callYulFunction(
|
m_context.callYulFunction(
|
||||||
m_context.utilFunctions().conversionFunction(typeOnStack, targetType),
|
m_context.utilFunctions().conversionFunction(typeOnStack, targetType),
|
||||||
1,
|
1,
|
||||||
@ -1231,7 +1233,7 @@ void CompilerUtils::convertType(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
solAssert(_typeOnStack == _targetType, "");
|
solAssert(_typeOnStack == _targetType);
|
||||||
// nothing to do
|
// nothing to do
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1241,7 +1243,7 @@ void CompilerUtils::convertType(
|
|||||||
{
|
{
|
||||||
TupleType const& sourceTuple = dynamic_cast<TupleType const&>(_typeOnStack);
|
TupleType const& sourceTuple = dynamic_cast<TupleType const&>(_typeOnStack);
|
||||||
TupleType const& targetTuple = dynamic_cast<TupleType const&>(_targetType);
|
TupleType const& targetTuple = dynamic_cast<TupleType const&>(_targetType);
|
||||||
solAssert(targetTuple.components().size() == sourceTuple.components().size(), "");
|
solAssert(targetTuple.components().size() == sourceTuple.components().size());
|
||||||
unsigned depth = sourceTuple.sizeOnStack();
|
unsigned depth = sourceTuple.sizeOnStack();
|
||||||
for (size_t i = 0; i < sourceTuple.components().size(); ++i)
|
for (size_t i = 0; i < sourceTuple.components().size(); ++i)
|
||||||
{
|
{
|
||||||
@ -1249,7 +1251,7 @@ void CompilerUtils::convertType(
|
|||||||
Type const* targetType = targetTuple.components()[i];
|
Type const* targetType = targetTuple.components()[i];
|
||||||
if (!sourceType)
|
if (!sourceType)
|
||||||
{
|
{
|
||||||
solAssert(!targetType, "");
|
solAssert(!targetType);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unsigned sourceSize = sourceType->sizeOnStack();
|
unsigned sourceSize = sourceType->sizeOnStack();
|
||||||
@ -1291,7 +1293,7 @@ void CompilerUtils::convertType(
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// we used to allow conversions from function to address
|
// we used to allow conversions from function to address
|
||||||
solAssert(!(stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address), "");
|
solAssert(!(stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address));
|
||||||
if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function)
|
if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function)
|
||||||
{
|
{
|
||||||
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
|
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
|
||||||
@ -1348,14 +1350,14 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
|||||||
}
|
}
|
||||||
if (referenceType->location() == DataLocation::CallData)
|
if (referenceType->location() == DataLocation::CallData)
|
||||||
{
|
{
|
||||||
solAssert(referenceType->sizeOnStack() == 1 || referenceType->sizeOnStack() == 2, "");
|
solAssert(referenceType->sizeOnStack() == 1 || referenceType->sizeOnStack() == 2);
|
||||||
m_context << Instruction::CALLDATASIZE;
|
m_context << Instruction::CALLDATASIZE;
|
||||||
if (referenceType->sizeOnStack() == 2)
|
if (referenceType->sizeOnStack() == 2)
|
||||||
m_context << 0;
|
m_context << 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
solAssert(referenceType->location() == DataLocation::Memory, "");
|
solAssert(referenceType->location() == DataLocation::Memory);
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
||||||
if (arrayType->isDynamicallySized())
|
if (arrayType->isDynamicallySized())
|
||||||
{
|
{
|
||||||
@ -1383,7 +1385,7 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
|||||||
}
|
}
|
||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
||||||
{
|
{
|
||||||
solAssert(!arrayType->isDynamicallySized(), "");
|
solAssert(!arrayType->isDynamicallySized());
|
||||||
if (arrayType->length() > 0)
|
if (arrayType->length() > 0)
|
||||||
{
|
{
|
||||||
_context << arrayType->length() << Instruction::SWAP1;
|
_context << arrayType->length() << Instruction::SWAP1;
|
||||||
@ -1483,7 +1485,7 @@ void CompilerUtils::popStackSlots(size_t _amount)
|
|||||||
|
|
||||||
void CompilerUtils::popAndJump(unsigned _toHeight, evmasm::AssemblyItem const& _jumpTo)
|
void CompilerUtils::popAndJump(unsigned _toHeight, evmasm::AssemblyItem const& _jumpTo)
|
||||||
{
|
{
|
||||||
solAssert(m_context.stackHeight() >= _toHeight, "");
|
solAssert(m_context.stackHeight() >= _toHeight);
|
||||||
unsigned amount = m_context.stackHeight() - _toHeight;
|
unsigned amount = m_context.stackHeight() - _toHeight;
|
||||||
popStackSlots(amount);
|
popStackSlots(amount);
|
||||||
m_context.appendJumpTo(_jumpTo);
|
m_context.appendJumpTo(_jumpTo);
|
||||||
@ -1551,7 +1553,7 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
|
|||||||
|
|
||||||
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords)
|
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords)
|
||||||
{
|
{
|
||||||
solAssert(_type.isValueType(), "");
|
solAssert(_type.isValueType());
|
||||||
Type const* type = &_type;
|
Type const* type = &_type;
|
||||||
if (auto const* userDefined = dynamic_cast<UserDefinedValueType const*>(type))
|
if (auto const* userDefined = dynamic_cast<UserDefinedValueType const*>(type))
|
||||||
type = &userDefined->underlyingType();
|
type = &userDefined->underlyingType();
|
||||||
@ -1603,7 +1605,7 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
|
|||||||
|
|
||||||
void CompilerUtils::leftShiftNumberOnStack(unsigned _bits)
|
void CompilerUtils::leftShiftNumberOnStack(unsigned _bits)
|
||||||
{
|
{
|
||||||
solAssert(_bits < 256, "");
|
solAssert(_bits < 256);
|
||||||
if (m_context.evmVersion().hasBitwiseShifting())
|
if (m_context.evmVersion().hasBitwiseShifting())
|
||||||
m_context << _bits << Instruction::SHL;
|
m_context << _bits << Instruction::SHL;
|
||||||
else
|
else
|
||||||
@ -1612,7 +1614,7 @@ void CompilerUtils::leftShiftNumberOnStack(unsigned _bits)
|
|||||||
|
|
||||||
void CompilerUtils::rightShiftNumberOnStack(unsigned _bits)
|
void CompilerUtils::rightShiftNumberOnStack(unsigned _bits)
|
||||||
{
|
{
|
||||||
solAssert(_bits < 256, "");
|
solAssert(_bits < 256);
|
||||||
// NOTE: If we add signed right shift, SAR rounds differently than SDIV
|
// NOTE: If we add signed right shift, SAR rounds differently than SDIV
|
||||||
if (m_context.evmVersion().hasBitwiseShifting())
|
if (m_context.evmVersion().hasBitwiseShifting())
|
||||||
m_context << _bits << Instruction::SHR;
|
m_context << _bits << Instruction::SHR;
|
||||||
@ -1627,7 +1629,7 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords,
|
|||||||
"Memory store of types with stack size != 1 not allowed (Type: " + _type.toString(true) + ")."
|
"Memory store of types with stack size != 1 not allowed (Type: " + _type.toString(true) + ")."
|
||||||
);
|
);
|
||||||
|
|
||||||
solAssert(!_type.isDynamicallyEncoded(), "");
|
solAssert(!_type.isDynamicallyEncoded());
|
||||||
|
|
||||||
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
|
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
|
||||||
|
|
||||||
|
@ -780,6 +780,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
break;
|
break;
|
||||||
case FunctionType::Kind::Send:
|
case FunctionType::Kind::Send:
|
||||||
case FunctionType::Kind::Transfer:
|
case FunctionType::Kind::Transfer:
|
||||||
|
{
|
||||||
_functionCall.expression().accept(*this);
|
_functionCall.expression().accept(*this);
|
||||||
// Provide the gas stipend manually at first because we may send zero ether.
|
// Provide the gas stipend manually at first because we may send zero ether.
|
||||||
// Will be zeroed if we send more than zero ether.
|
// Will be zeroed if we send more than zero ether.
|
||||||
@ -788,6 +789,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
// gas <- gas * !value
|
// gas <- gas * !value
|
||||||
m_context << Instruction::SWAP1 << Instruction::DUP2;
|
m_context << Instruction::SWAP1 << Instruction::DUP2;
|
||||||
m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1;
|
m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1;
|
||||||
|
FunctionType::Options callOptions;
|
||||||
|
callOptions.valueSet = true;
|
||||||
|
callOptions.gasSet = true;
|
||||||
appendExternalFunctionCall(
|
appendExternalFunctionCall(
|
||||||
FunctionType(
|
FunctionType(
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
@ -795,11 +799,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
strings(),
|
strings(),
|
||||||
strings(),
|
strings(),
|
||||||
FunctionType::Kind::BareCall,
|
FunctionType::Kind::BareCall,
|
||||||
false,
|
|
||||||
StateMutability::NonPayable,
|
StateMutability::NonPayable,
|
||||||
nullptr,
|
nullptr,
|
||||||
true,
|
callOptions
|
||||||
true
|
|
||||||
),
|
),
|
||||||
{},
|
{},
|
||||||
false
|
false
|
||||||
@ -812,6 +814,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
m_context.appendConditionalRevert(true);
|
m_context.appendConditionalRevert(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case FunctionType::Kind::Selfdestruct:
|
case FunctionType::Kind::Selfdestruct:
|
||||||
acceptAndConvert(*arguments.front(), *function.parameterTypes().front(), true);
|
acceptAndConvert(*arguments.front(), *function.parameterTypes().front(), true);
|
||||||
m_context << Instruction::SELFDESTRUCT;
|
m_context << Instruction::SELFDESTRUCT;
|
||||||
@ -1255,7 +1258,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
auto const functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type);
|
auto const functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type);
|
||||||
solAssert(functionPtr);
|
solAssert(functionPtr);
|
||||||
solAssert(functionPtr->sizeOnStack() == 2);
|
|
||||||
|
|
||||||
// Account for tuples with one component which become that component
|
// Account for tuples with one component which become that component
|
||||||
if (auto const tupleType = dynamic_cast<TupleType const*>(arguments[1]->annotation().type))
|
if (auto const tupleType = dynamic_cast<TupleType const*>(arguments[1]->annotation().type))
|
||||||
@ -1330,9 +1332,20 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
else if (function.kind() == FunctionType::Kind::ABIEncodeCall)
|
else if (function.kind() == FunctionType::Kind::ABIEncodeCall)
|
||||||
{
|
{
|
||||||
|
auto const& funType = dynamic_cast<FunctionType const&>(*selectorType);
|
||||||
|
if (funType.kind() == FunctionType::Kind::Declaration)
|
||||||
|
{
|
||||||
|
solAssert(funType.hasDeclaration());
|
||||||
|
solAssert(selectorType->sizeOnStack() == 0);
|
||||||
|
m_context << funType.externalIdentifier();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(selectorType->sizeOnStack() == 2);
|
||||||
// stack: <memory pointer> <functionPointer>
|
// stack: <memory pointer> <functionPointer>
|
||||||
// Extract selector from the stack
|
// Extract selector from the stack
|
||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
|
}
|
||||||
// Conversion will be done below
|
// Conversion will be done below
|
||||||
dataOnStack = TypeProvider::uint(32);
|
dataOnStack = TypeProvider::uint(32);
|
||||||
}
|
}
|
||||||
@ -1747,6 +1760,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
case Type::Category::Function:
|
case Type::Category::Function:
|
||||||
if (member == "selector")
|
if (member == "selector")
|
||||||
{
|
{
|
||||||
|
auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type);
|
||||||
|
if (functionType.kind() == FunctionType::Kind::External)
|
||||||
|
CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 2);
|
||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
/// need to store it as bytes4
|
/// need to store it as bytes4
|
||||||
utils().leftShiftNumberOnStack(224);
|
utils().leftShiftNumberOnStack(224);
|
||||||
@ -1755,8 +1771,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type);
|
auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type);
|
||||||
solAssert(functionType.kind() == FunctionType::Kind::External, "");
|
solAssert(functionType.kind() == FunctionType::Kind::External, "");
|
||||||
// stack: <address> <function_id>
|
CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 1);
|
||||||
m_context << Instruction::POP;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
solAssert(
|
solAssert(
|
||||||
@ -2265,12 +2280,29 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO
|
|||||||
|
|
||||||
void ExpressionCompiler::appendCompareOperatorCode(Token _operator, Type const& _type)
|
void ExpressionCompiler::appendCompareOperatorCode(Token _operator, Type const& _type)
|
||||||
{
|
{
|
||||||
solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types.");
|
|
||||||
if (_operator == Token::Equal || _operator == Token::NotEqual)
|
if (_operator == Token::Equal || _operator == Token::NotEqual)
|
||||||
{
|
{
|
||||||
if (FunctionType const* funType = dynamic_cast<decltype(funType)>(&_type))
|
FunctionType const* functionType = dynamic_cast<decltype(functionType)>(&_type);
|
||||||
|
if (functionType && functionType->kind() == FunctionType::Kind::External)
|
||||||
{
|
{
|
||||||
if (funType->kind() == FunctionType::Kind::Internal)
|
solUnimplementedAssert(functionType->sizeOnStack() == 2, "");
|
||||||
|
m_context << Instruction::SWAP3;
|
||||||
|
|
||||||
|
m_context << ((u256(1) << 160) - 1) << Instruction::AND;
|
||||||
|
m_context << Instruction::SWAP1;
|
||||||
|
m_context << ((u256(1) << 160) - 1) << Instruction::AND;
|
||||||
|
m_context << Instruction::EQ;
|
||||||
|
m_context << Instruction::SWAP2;
|
||||||
|
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
|
||||||
|
m_context << Instruction::SWAP1;
|
||||||
|
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
|
||||||
|
m_context << Instruction::EQ;
|
||||||
|
m_context << Instruction::AND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types.");
|
||||||
|
if (functionType && functionType->kind() == FunctionType::Kind::Internal)
|
||||||
{
|
{
|
||||||
// We have to remove the upper bits (construction time value) because they might
|
// We have to remove the upper bits (construction time value) because they might
|
||||||
// be "unknown" in one of the operands and not in the other.
|
// be "unknown" in one of the operands and not in the other.
|
||||||
@ -2278,13 +2310,14 @@ void ExpressionCompiler::appendCompareOperatorCode(Token _operator, Type const&
|
|||||||
m_context << Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
|
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
m_context << Instruction::EQ;
|
m_context << Instruction::EQ;
|
||||||
|
}
|
||||||
if (_operator == Token::NotEqual)
|
if (_operator == Token::NotEqual)
|
||||||
m_context << Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types.");
|
||||||
bool isSigned = false;
|
bool isSigned = false;
|
||||||
if (auto type = dynamic_cast<IntegerType const*>(&_type))
|
if (auto type = dynamic_cast<IntegerType const*>(&_type))
|
||||||
isSigned = type->isSigned();
|
isSigned = type->isSigned();
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <libsolutil/FunctionSelector.h>
|
#include <libsolutil/FunctionSelector.h>
|
||||||
#include <libsolutil/Whiskers.h>
|
#include <libsolutil/Whiskers.h>
|
||||||
#include <libsolutil/StringUtils.h>
|
#include <libsolutil/StringUtils.h>
|
||||||
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -3218,15 +3219,17 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
solAssert(fromType.arrayType().isByteArray(), "Array types other than bytes not convertible to bytesNN.");
|
solAssert(fromType.arrayType().isByteArray(), "Array types other than bytes not convertible to bytesNN.");
|
||||||
return bytesToFixedBytesConversionFunction(fromType.arrayType(), dynamic_cast<FixedBytesType const &>(_to));
|
return bytesToFixedBytesConversionFunction(fromType.arrayType(), dynamic_cast<FixedBytesType const &>(_to));
|
||||||
}
|
}
|
||||||
solAssert(_to.category() == Type::Category::Array, "");
|
solAssert(_to.category() == Type::Category::Array);
|
||||||
auto const& targetType = dynamic_cast<ArrayType const&>(_to);
|
auto const& targetType = dynamic_cast<ArrayType const&>(_to);
|
||||||
|
|
||||||
solAssert(fromType.arrayType().isImplicitlyConvertibleTo(targetType), "");
|
solAssert(
|
||||||
|
fromType.arrayType().isImplicitlyConvertibleTo(targetType) ||
|
||||||
|
(fromType.arrayType().isByteArray() && targetType.isByteArray())
|
||||||
|
);
|
||||||
solAssert(
|
solAssert(
|
||||||
fromType.arrayType().dataStoredIn(DataLocation::CallData) &&
|
fromType.arrayType().dataStoredIn(DataLocation::CallData) &&
|
||||||
fromType.arrayType().isDynamicallySized() &&
|
fromType.arrayType().isDynamicallySized() &&
|
||||||
!fromType.arrayType().baseType()->isDynamicallyEncoded(),
|
!fromType.arrayType().baseType()->isDynamicallyEncoded()
|
||||||
""
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!targetType.dataStoredIn(DataLocation::CallData))
|
if (!targetType.dataStoredIn(DataLocation::CallData))
|
||||||
@ -3608,7 +3611,7 @@ string YulUtilFunctions::copyStructToStorageFunction(StructType const& _from, St
|
|||||||
auto const& [srcSlotOffset, srcOffset] = _from.storageOffsetsOfMember(structMembers[i].name);
|
auto const& [srcSlotOffset, srcOffset] = _from.storageOffsetsOfMember(structMembers[i].name);
|
||||||
t("memberOffset", formatNumber(srcSlotOffset));
|
t("memberOffset", formatNumber(srcSlotOffset));
|
||||||
if (memberType.isValueType())
|
if (memberType.isValueType())
|
||||||
t("read", readFromStorageValueType(memberType, srcOffset, false));
|
t("read", readFromStorageValueType(memberType, srcOffset, true));
|
||||||
else
|
else
|
||||||
solAssert(srcOffset == 0, "");
|
solAssert(srcOffset == 0, "");
|
||||||
|
|
||||||
@ -4548,3 +4551,31 @@ string YulUtilFunctions::externalCodeFunction()
|
|||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string YulUtilFunctions::externalFunctionPointersEqualFunction()
|
||||||
|
{
|
||||||
|
std::string const functionName = "externalFunctionPointersEqualFunction";
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
return util::Whiskers(R"(
|
||||||
|
function <functionName>(
|
||||||
|
leftAddress,
|
||||||
|
leftSelector,
|
||||||
|
rightAddress,
|
||||||
|
rightSelector
|
||||||
|
) -> result {
|
||||||
|
result := and(
|
||||||
|
eq(
|
||||||
|
<addressCleanUpFunction>(leftAddress), <addressCleanUpFunction>(rightAddress)
|
||||||
|
),
|
||||||
|
eq(
|
||||||
|
<selectorCleanUpFunction>(leftSelector), <selectorCleanUpFunction>(rightSelector)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("addressCleanUpFunction", cleanupFunction(*TypeProvider::address()))
|
||||||
|
("selectorCleanUpFunction", cleanupFunction(*TypeProvider::uint(32)))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -522,6 +522,9 @@ public:
|
|||||||
/// Signature: (address) -> mpos
|
/// Signature: (address) -> mpos
|
||||||
std::string externalCodeFunction();
|
std::string externalCodeFunction();
|
||||||
|
|
||||||
|
/// @return the name of a function that that checks if two external functions pointers are equal or not
|
||||||
|
std::string externalFunctionPointersEqualFunction();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @returns the name of a function that copies a struct from calldata or memory to storage
|
/// @returns the name of a function that copies a struct from calldata or memory to storage
|
||||||
/// signature: (slot, value) ->
|
/// signature: (slot, value) ->
|
||||||
|
@ -95,7 +95,7 @@ struct CopyTranslate: public yul::ASTCopier
|
|||||||
return ASTCopier::translate(_identifier);
|
return ASTCopier::translate(_identifier);
|
||||||
|
|
||||||
yul::Expression translated = translateReference(_identifier);
|
yul::Expression translated = translateReference(_identifier);
|
||||||
solAssert(holds_alternative<yul::Identifier>(translated), "");
|
solAssert(holds_alternative<yul::Identifier>(translated));
|
||||||
return get<yul::Identifier>(std::move(translated));
|
return get<yul::Identifier>(std::move(translated));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,14 +115,14 @@ private:
|
|||||||
if (suffix.empty() && varDecl->isLocalVariable())
|
if (suffix.empty() && varDecl->isLocalVariable())
|
||||||
{
|
{
|
||||||
auto const& var = m_context.localVariable(*varDecl);
|
auto const& var = m_context.localVariable(*varDecl);
|
||||||
solAssert(var.type().sizeOnStack() == 1, "");
|
solAssert(var.type().sizeOnStack() == 1);
|
||||||
|
|
||||||
value = var.commaSeparatedList();
|
value = var.commaSeparatedList();
|
||||||
}
|
}
|
||||||
else if (varDecl->isConstant())
|
else if (varDecl->isConstant())
|
||||||
{
|
{
|
||||||
VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl);
|
VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl);
|
||||||
solAssert(variable, "");
|
solAssert(variable);
|
||||||
|
|
||||||
if (variable->value()->annotation().type->category() == Type::Category::RationalNumber)
|
if (variable->value()->annotation().type->category() == Type::Category::RationalNumber)
|
||||||
{
|
{
|
||||||
@ -130,7 +130,7 @@ private:
|
|||||||
if (auto const* bytesType = dynamic_cast<FixedBytesType const*>(variable->type()))
|
if (auto const* bytesType = dynamic_cast<FixedBytesType const*>(variable->type()))
|
||||||
intValue <<= 256 - 8 * bytesType->numBytes();
|
intValue <<= 256 - 8 * bytesType->numBytes();
|
||||||
else
|
else
|
||||||
solAssert(variable->type()->category() == Type::Category::Integer, "");
|
solAssert(variable->type()->category() == Type::Category::Integer);
|
||||||
value = intValue.str();
|
value = intValue.str();
|
||||||
}
|
}
|
||||||
else if (auto const* literal = dynamic_cast<Literal const*>(variable->value().get()))
|
else if (auto const* literal = dynamic_cast<Literal const*>(variable->value().get()))
|
||||||
@ -141,20 +141,20 @@ private:
|
|||||||
{
|
{
|
||||||
case Type::Category::Bool:
|
case Type::Category::Bool:
|
||||||
case Type::Category::Address:
|
case Type::Category::Address:
|
||||||
solAssert(type->category() == variable->annotation().type->category(), "");
|
solAssert(type->category() == variable->annotation().type->category());
|
||||||
value = toCompactHexWithPrefix(type->literalValue(literal));
|
value = toCompactHexWithPrefix(type->literalValue(literal));
|
||||||
break;
|
break;
|
||||||
case Type::Category::StringLiteral:
|
case Type::Category::StringLiteral:
|
||||||
{
|
{
|
||||||
auto const& stringLiteral = dynamic_cast<StringLiteralType const&>(*type);
|
auto const& stringLiteral = dynamic_cast<StringLiteralType const&>(*type);
|
||||||
solAssert(variable->type()->category() == Type::Category::FixedBytes, "");
|
solAssert(variable->type()->category() == Type::Category::FixedBytes);
|
||||||
unsigned const numBytes = dynamic_cast<FixedBytesType const&>(*variable->type()).numBytes();
|
unsigned const numBytes = dynamic_cast<FixedBytesType const&>(*variable->type()).numBytes();
|
||||||
solAssert(stringLiteral.value().size() <= numBytes, "");
|
solAssert(stringLiteral.value().size() <= numBytes);
|
||||||
value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft)));
|
value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
solAssert(false, "");
|
solAssert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -167,25 +167,25 @@ private:
|
|||||||
else if (suffix == "offset")
|
else if (suffix == "offset")
|
||||||
value = to_string(m_context.storageLocationOfStateVariable(*varDecl).second);
|
value = to_string(m_context.storageLocationOfStateVariable(*varDecl).second);
|
||||||
else
|
else
|
||||||
solAssert(false, "");
|
solAssert(false);
|
||||||
}
|
}
|
||||||
else if (varDecl->type()->dataStoredIn(DataLocation::Storage))
|
else if (varDecl->type()->dataStoredIn(DataLocation::Storage))
|
||||||
{
|
{
|
||||||
solAssert(suffix == "slot" || suffix == "offset", "");
|
solAssert(suffix == "slot" || suffix == "offset");
|
||||||
solAssert(varDecl->isLocalVariable(), "");
|
solAssert(varDecl->isLocalVariable());
|
||||||
if (suffix == "slot")
|
if (suffix == "slot")
|
||||||
value = IRVariable{*varDecl}.part("slot").name();
|
value = IRVariable{*varDecl}.part("slot").name();
|
||||||
else if (varDecl->type()->isValueType())
|
else if (varDecl->type()->isValueType())
|
||||||
value = IRVariable{*varDecl}.part("offset").name();
|
value = IRVariable{*varDecl}.part("offset").name();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(!IRVariable{*varDecl}.hasPart("offset"), "");
|
solAssert(!IRVariable{*varDecl}.hasPart("offset"));
|
||||||
value = "0";
|
value = "0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (varDecl->type()->dataStoredIn(DataLocation::CallData))
|
else if (varDecl->type()->dataStoredIn(DataLocation::CallData))
|
||||||
{
|
{
|
||||||
solAssert(suffix == "offset" || suffix == "length", "");
|
solAssert(suffix == "offset" || suffix == "length");
|
||||||
value = IRVariable{*varDecl}.part(suffix).name();
|
value = IRVariable{*varDecl}.part(suffix).name();
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
@ -193,15 +193,15 @@ private:
|
|||||||
functionType && functionType->kind() == FunctionType::Kind::External
|
functionType && functionType->kind() == FunctionType::Kind::External
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(suffix == "selector" || suffix == "address", "");
|
solAssert(suffix == "selector" || suffix == "address");
|
||||||
solAssert(varDecl->type()->sizeOnStack() == 2, "");
|
solAssert(varDecl->type()->sizeOnStack() == 2);
|
||||||
if (suffix == "selector")
|
if (suffix == "selector")
|
||||||
value = IRVariable{*varDecl}.part("functionSelector").name();
|
value = IRVariable{*varDecl}.part("functionSelector").name();
|
||||||
else
|
else
|
||||||
value = IRVariable{*varDecl}.part("address").name();
|
value = IRVariable{*varDecl}.part("address").name();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
solAssert(false, "");
|
solAssert(false);
|
||||||
|
|
||||||
if (isdigit(value.front()))
|
if (isdigit(value.front()))
|
||||||
return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}};
|
return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}};
|
||||||
@ -268,7 +268,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va
|
|||||||
setLocation(_varDecl);
|
setLocation(_varDecl);
|
||||||
|
|
||||||
solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable.");
|
solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable.");
|
||||||
solAssert(!_varDecl.isConstant(), "");
|
solAssert(!_varDecl.isConstant());
|
||||||
if (!_varDecl.value())
|
if (!_varDecl.value())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -355,7 +355,7 @@ string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const
|
|||||||
templ("sourceLocationComment", dispenseLocationComment(_constant, m_context));
|
templ("sourceLocationComment", dispenseLocationComment(_constant, m_context));
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
IRGeneratorForStatements generator(m_context, m_utils);
|
IRGeneratorForStatements generator(m_context, m_utils);
|
||||||
solAssert(_constant.value(), "");
|
solAssert(_constant.value());
|
||||||
Type const& constantType = *_constant.type();
|
Type const& constantType = *_constant.type();
|
||||||
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
|
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
|
||||||
templ("code", generator.code());
|
templ("code", generator.code());
|
||||||
@ -386,7 +386,7 @@ void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _var
|
|||||||
for (size_t i = 0; i < _varDeclStatement.declarations().size(); ++i)
|
for (size_t i = 0; i < _varDeclStatement.declarations().size(); ++i)
|
||||||
if (auto const& decl = _varDeclStatement.declarations()[i])
|
if (auto const& decl = _varDeclStatement.declarations()[i])
|
||||||
{
|
{
|
||||||
solAssert(tupleType->components()[i], "");
|
solAssert(tupleType->components()[i]);
|
||||||
define(m_context.addLocalVariable(*decl), IRVariable(*expression).tupleComponent(i));
|
define(m_context.addLocalVariable(*decl), IRVariable(*expression).tupleComponent(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -443,7 +443,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
|||||||
TokenTraits::AssignmentToBinaryOp(assignmentOperator);
|
TokenTraits::AssignmentToBinaryOp(assignmentOperator);
|
||||||
|
|
||||||
if (TokenTraits::isShiftOp(binaryOperator))
|
if (TokenTraits::isShiftOp(binaryOperator))
|
||||||
solAssert(type(_assignment.rightHandSide()).mobileType(), "");
|
solAssert(type(_assignment.rightHandSide()).mobileType());
|
||||||
IRVariable value =
|
IRVariable value =
|
||||||
type(_assignment.leftHandSide()).isValueType() ?
|
type(_assignment.leftHandSide()).isValueType() ?
|
||||||
convert(
|
convert(
|
||||||
@ -460,11 +460,11 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
|||||||
if (assignmentOperator != Token::Assign)
|
if (assignmentOperator != Token::Assign)
|
||||||
{
|
{
|
||||||
solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
|
solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
|
||||||
solAssert(binaryOperator != Token::Exp, "");
|
solAssert(binaryOperator != Token::Exp);
|
||||||
solAssert(type(_assignment) == type(_assignment.leftHandSide()), "");
|
solAssert(type(_assignment) == type(_assignment.leftHandSide()));
|
||||||
|
|
||||||
IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
|
IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
|
||||||
solAssert(type(_assignment) == leftIntermediate.type(), "");
|
solAssert(type(_assignment) == leftIntermediate.type());
|
||||||
|
|
||||||
define(_assignment) << (
|
define(_assignment) << (
|
||||||
TokenTraits::isShiftOp(binaryOperator) ?
|
TokenTraits::isShiftOp(binaryOperator) ?
|
||||||
@ -523,14 +523,14 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
|||||||
{
|
{
|
||||||
bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo;
|
bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo;
|
||||||
if (willBeWrittenTo)
|
if (willBeWrittenTo)
|
||||||
solAssert(!m_currentLValue, "");
|
solAssert(!m_currentLValue);
|
||||||
if (_tuple.components().size() == 1)
|
if (_tuple.components().size() == 1)
|
||||||
{
|
{
|
||||||
solAssert(_tuple.components().front(), "");
|
solAssert(_tuple.components().front());
|
||||||
_tuple.components().front()->accept(*this);
|
_tuple.components().front()->accept(*this);
|
||||||
setLocation(_tuple);
|
setLocation(_tuple);
|
||||||
if (willBeWrittenTo)
|
if (willBeWrittenTo)
|
||||||
solAssert(!!m_currentLValue, "");
|
solAssert(!!m_currentLValue);
|
||||||
else
|
else
|
||||||
define(_tuple, *_tuple.components().front());
|
define(_tuple, *_tuple.components().front());
|
||||||
}
|
}
|
||||||
@ -544,7 +544,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
|||||||
setLocation(_tuple);
|
setLocation(_tuple);
|
||||||
if (willBeWrittenTo)
|
if (willBeWrittenTo)
|
||||||
{
|
{
|
||||||
solAssert(!!m_currentLValue, "");
|
solAssert(!!m_currentLValue);
|
||||||
lvalues.emplace_back(std::move(m_currentLValue));
|
lvalues.emplace_back(std::move(m_currentLValue));
|
||||||
m_currentLValue.reset();
|
m_currentLValue.reset();
|
||||||
}
|
}
|
||||||
@ -568,7 +568,7 @@ bool IRGeneratorForStatements::visit(Block const& _block)
|
|||||||
{
|
{
|
||||||
if (_block.unchecked())
|
if (_block.unchecked())
|
||||||
{
|
{
|
||||||
solAssert(m_context.arithmetic() == Arithmetic::Checked, "");
|
solAssert(m_context.arithmetic() == Arithmetic::Checked);
|
||||||
m_context.setArithmetic(Arithmetic::Wrapping);
|
m_context.setArithmetic(Arithmetic::Wrapping);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -578,7 +578,7 @@ void IRGeneratorForStatements::endVisit(Block const& _block)
|
|||||||
{
|
{
|
||||||
if (_block.unchecked())
|
if (_block.unchecked())
|
||||||
{
|
{
|
||||||
solAssert(m_context.arithmetic() == Arithmetic::Wrapping, "");
|
solAssert(m_context.arithmetic() == Arithmetic::Wrapping);
|
||||||
m_context.setArithmetic(Arithmetic::Checked);
|
m_context.setArithmetic(Arithmetic::Checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -607,7 +607,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
|
|||||||
|
|
||||||
void IRGeneratorForStatements::endVisit(PlaceholderStatement const& _placeholder)
|
void IRGeneratorForStatements::endVisit(PlaceholderStatement const& _placeholder)
|
||||||
{
|
{
|
||||||
solAssert(m_placeholderCallback, "");
|
solAssert(m_placeholderCallback);
|
||||||
setLocation(_placeholder);
|
setLocation(_placeholder);
|
||||||
appendCode() << m_placeholderCallback();
|
appendCode() << m_placeholderCallback();
|
||||||
}
|
}
|
||||||
@ -776,7 +776,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
{
|
{
|
||||||
setLocation(_binOp);
|
setLocation(_binOp);
|
||||||
|
|
||||||
solAssert(!!_binOp.annotation().commonType, "");
|
solAssert(!!_binOp.annotation().commonType);
|
||||||
Type const* commonType = _binOp.annotation().commonType;
|
Type const* commonType = _binOp.annotation().commonType;
|
||||||
langutil::Token op = _binOp.getOperator();
|
langutil::Token op = _binOp.getOperator();
|
||||||
|
|
||||||
@ -799,13 +799,8 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
|
|
||||||
if (TokenTraits::isCompareOp(op))
|
if (TokenTraits::isCompareOp(op))
|
||||||
{
|
{
|
||||||
if (auto type = dynamic_cast<FunctionType const*>(commonType))
|
solAssert(commonType->isValueType());
|
||||||
{
|
|
||||||
solAssert(op == Token::Equal || op == Token::NotEqual, "Invalid function pointer comparison!");
|
|
||||||
solAssert(type->kind() != FunctionType::Kind::External, "External function comparison not allowed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
solAssert(commonType->isValueType(), "");
|
|
||||||
bool isSigned = false;
|
bool isSigned = false;
|
||||||
if (auto type = dynamic_cast<IntegerType const*>(commonType))
|
if (auto type = dynamic_cast<IntegerType const*>(commonType))
|
||||||
isSigned = type->isSigned();
|
isSigned = type->isSigned();
|
||||||
@ -813,8 +808,25 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
string args = expressionAsType(_binOp.leftExpression(), *commonType, true);
|
string args = expressionAsType(_binOp.leftExpression(), *commonType, true);
|
||||||
args += ", " + expressionAsType(_binOp.rightExpression(), *commonType, true);
|
args += ", " + expressionAsType(_binOp.rightExpression(), *commonType, true);
|
||||||
|
|
||||||
|
auto functionType = dynamic_cast<FunctionType const*>(commonType);
|
||||||
|
solAssert(functionType ? (op == Token::Equal || op == Token::NotEqual) : true, "Invalid function pointer comparison!");
|
||||||
|
|
||||||
string expr;
|
string expr;
|
||||||
if (op == Token::Equal)
|
|
||||||
|
if (functionType && functionType->kind() == FunctionType::Kind::External)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(functionType->sizeOnStack() == 2, "");
|
||||||
|
expr = m_utils.externalFunctionPointersEqualFunction() +
|
||||||
|
"(" +
|
||||||
|
IRVariable{_binOp.leftExpression()}.part("address").name() + "," +
|
||||||
|
IRVariable{_binOp.leftExpression()}.part("functionSelector").name() + "," +
|
||||||
|
IRVariable{_binOp.rightExpression()}.part("address").name() + "," +
|
||||||
|
IRVariable{_binOp.rightExpression()}.part("functionSelector").name() +
|
||||||
|
")";
|
||||||
|
if (op == Token::NotEqual)
|
||||||
|
expr = "iszero(" + expr + ")";
|
||||||
|
}
|
||||||
|
else if (op == Token::Equal)
|
||||||
expr = "eq(" + move(args) + ")";
|
expr = "eq(" + move(args) + ")";
|
||||||
else if (op == Token::NotEqual)
|
else if (op == Token::NotEqual)
|
||||||
expr = "iszero(eq(" + move(args) + "))";
|
expr = "iszero(eq(" + move(args) + "))";
|
||||||
@ -843,7 +855,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
else if (auto rationalNumberType = dynamic_cast<RationalNumberType const*>(_binOp.leftExpression().annotation().type))
|
else if (auto rationalNumberType = dynamic_cast<RationalNumberType const*>(_binOp.leftExpression().annotation().type))
|
||||||
{
|
{
|
||||||
solAssert(rationalNumberType->integerType(), "Invalid literal as the base for exponentiation.");
|
solAssert(rationalNumberType->integerType(), "Invalid literal as the base for exponentiation.");
|
||||||
solAssert(dynamic_cast<IntegerType const*>(commonType), "");
|
solAssert(dynamic_cast<IntegerType const*>(commonType));
|
||||||
|
|
||||||
define(_binOp) << m_utils.overflowCheckedIntLiteralExpFunction(
|
define(_binOp) << m_utils.overflowCheckedIntLiteralExpFunction(
|
||||||
*rationalNumberType,
|
*rationalNumberType,
|
||||||
@ -939,7 +951,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
{
|
{
|
||||||
FunctionDefinition const* functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract());
|
FunctionDefinition const* functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract());
|
||||||
|
|
||||||
solAssert(!functionType->takesArbitraryParameters(), "");
|
solAssert(!functionType->takesArbitraryParameters());
|
||||||
|
|
||||||
vector<string> args;
|
vector<string> args;
|
||||||
if (functionType->bound())
|
if (functionType->bound())
|
||||||
@ -950,7 +962,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
if (functionDef)
|
if (functionDef)
|
||||||
{
|
{
|
||||||
solAssert(functionDef->isImplemented(), "");
|
solAssert(functionDef->isImplemented());
|
||||||
|
|
||||||
define(_functionCall) <<
|
define(_functionCall) <<
|
||||||
m_context.enqueueFunctionForCodeGeneration(*functionDef) <<
|
m_context.enqueueFunctionForCodeGeneration(*functionDef) <<
|
||||||
@ -1053,7 +1065,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::Error:
|
case FunctionType::Kind::Error:
|
||||||
{
|
{
|
||||||
ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(_functionCall.expression()));
|
ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(_functionCall.expression()));
|
||||||
solAssert(error, "");
|
solAssert(error);
|
||||||
revertWithError(
|
revertWithError(
|
||||||
error->functionType(true)->externalSignature(),
|
error->functionType(true)->externalSignature(),
|
||||||
error->functionType(true)->parameterTypes(),
|
error->functionType(true)->parameterTypes(),
|
||||||
@ -1064,7 +1076,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::Wrap:
|
case FunctionType::Kind::Wrap:
|
||||||
case FunctionType::Kind::Unwrap:
|
case FunctionType::Kind::Unwrap:
|
||||||
{
|
{
|
||||||
solAssert(arguments.size() == 1, "");
|
solAssert(arguments.size() == 1);
|
||||||
FunctionType::Kind kind = functionType->kind();
|
FunctionType::Kind kind = functionType->kind();
|
||||||
if (kind == FunctionType::Kind::Wrap)
|
if (kind == FunctionType::Kind::Wrap)
|
||||||
solAssert(
|
solAssert(
|
||||||
@ -1074,7 +1086,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
""
|
""
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType, "");
|
solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType);
|
||||||
|
|
||||||
define(_functionCall, *arguments.at(0));
|
define(_functionCall, *arguments.at(0));
|
||||||
break;
|
break;
|
||||||
@ -1108,7 +1120,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||||
{
|
{
|
||||||
bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||||
solAssert(functionType->padArguments() != isPacked, "");
|
solAssert(functionType->padArguments() != isPacked);
|
||||||
bool const hasSelectorOrSignature =
|
bool const hasSelectorOrSignature =
|
||||||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
||||||
functionType->kind() == FunctionType::Kind::ABIEncodeCall ||
|
functionType->kind() == FunctionType::Kind::ABIEncodeCall ||
|
||||||
@ -1122,7 +1134,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||||
{
|
{
|
||||||
solAssert(arguments.size() == 2, "");
|
solAssert(arguments.size() == 2);
|
||||||
// Account for tuples with one component which become that component
|
// Account for tuples with one component which become that component
|
||||||
if (type(*arguments[1]).category() == Type::Category::Tuple)
|
if (type(*arguments[1]).category() == Type::Category::Tuple)
|
||||||
{
|
{
|
||||||
@ -1150,10 +1162,21 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||||
|
{
|
||||||
|
auto const& selectorType = dynamic_cast<FunctionType const&>(type(*arguments.front()));
|
||||||
|
if (selectorType.kind() == FunctionType::Kind::Declaration)
|
||||||
|
{
|
||||||
|
solAssert(selectorType.hasDeclaration());
|
||||||
|
selector = formatNumber(selectorType.externalIdentifier() << (256 - 32));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
selector = convert(
|
selector = convert(
|
||||||
IRVariable(*arguments[0]).part("functionSelector"),
|
IRVariable(*arguments[0]).part("functionSelector"),
|
||||||
*TypeProvider::fixedBytes(4)
|
*TypeProvider::fixedBytes(4)
|
||||||
).name();
|
).name();
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||||
{
|
{
|
||||||
// hash the signature
|
// hash the signature
|
||||||
@ -1234,7 +1257,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
referenceType && referenceType->dataStoredIn(DataLocation::CallData)
|
referenceType && referenceType->dataStoredIn(DataLocation::CallData)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()), "");
|
solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()));
|
||||||
IRVariable var = convert(*arguments[0], *TypeProvider::bytesCalldata());
|
IRVariable var = convert(*arguments[0], *TypeProvider::bytesCalldata());
|
||||||
templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, false));
|
templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, false));
|
||||||
templ("offset", var.part("offset").name());
|
templ("offset", var.part("offset").name());
|
||||||
@ -1256,8 +1279,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
case FunctionType::Kind::Revert:
|
case FunctionType::Kind::Revert:
|
||||||
{
|
{
|
||||||
solAssert(arguments.size() == parameterTypes.size(), "");
|
solAssert(arguments.size() == parameterTypes.size());
|
||||||
solAssert(arguments.size() <= 1, "");
|
solAssert(arguments.size() <= 1);
|
||||||
solAssert(
|
solAssert(
|
||||||
arguments.empty() ||
|
arguments.empty() ||
|
||||||
arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),
|
arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),
|
||||||
@ -1276,7 +1299,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::ObjectCreation:
|
case FunctionType::Kind::ObjectCreation:
|
||||||
{
|
{
|
||||||
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type);
|
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type);
|
||||||
solAssert(arguments.size() == 1, "");
|
solAssert(arguments.size() == 1);
|
||||||
|
|
||||||
IRVariable value = convert(*arguments[0], *TypeProvider::uint256());
|
IRVariable value = convert(*arguments[0], *TypeProvider::uint256());
|
||||||
define(_functionCall) <<
|
define(_functionCall) <<
|
||||||
@ -1288,7 +1311,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
case FunctionType::Kind::KECCAK256:
|
case FunctionType::Kind::KECCAK256:
|
||||||
{
|
{
|
||||||
solAssert(arguments.size() == 1, "");
|
solAssert(arguments.size() == 1);
|
||||||
|
|
||||||
ArrayType const* arrayType = TypeProvider::bytesMemory();
|
ArrayType const* arrayType = TypeProvider::bytesMemory();
|
||||||
|
|
||||||
@ -1316,10 +1339,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
case FunctionType::Kind::ArrayPop:
|
case FunctionType::Kind::ArrayPop:
|
||||||
{
|
{
|
||||||
solAssert(functionType->bound(), "");
|
solAssert(functionType->bound());
|
||||||
solAssert(functionType->parameterTypes().empty(), "");
|
solAssert(functionType->parameterTypes().empty());
|
||||||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
||||||
solAssert(arrayType, "");
|
solAssert(arrayType);
|
||||||
define(_functionCall) <<
|
define(_functionCall) <<
|
||||||
m_utils.storageArrayPopFunction(*arrayType) <<
|
m_utils.storageArrayPopFunction(*arrayType) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
@ -1330,7 +1353,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::ArrayPush:
|
case FunctionType::Kind::ArrayPush:
|
||||||
{
|
{
|
||||||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
||||||
solAssert(arrayType, "");
|
solAssert(arrayType);
|
||||||
|
|
||||||
if (arguments.empty())
|
if (arguments.empty())
|
||||||
{
|
{
|
||||||
@ -1391,8 +1414,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
{FunctionType::Kind::AddMod, "addmod"},
|
{FunctionType::Kind::AddMod, "addmod"},
|
||||||
{FunctionType::Kind::MulMod, "mulmod"},
|
{FunctionType::Kind::MulMod, "mulmod"},
|
||||||
};
|
};
|
||||||
solAssert(functions.find(functionType->kind()) != functions.end(), "");
|
solAssert(functions.find(functionType->kind()) != functions.end());
|
||||||
solAssert(arguments.size() == 3 && parameterTypes.size() == 3, "");
|
solAssert(arguments.size() == 3 && parameterTypes.size() == 3);
|
||||||
|
|
||||||
IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2]));
|
IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2]));
|
||||||
define(modulus, *arguments[2]);
|
define(modulus, *arguments[2]);
|
||||||
@ -1417,7 +1440,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
{FunctionType::Kind::Selfdestruct, "selfdestruct"},
|
{FunctionType::Kind::Selfdestruct, "selfdestruct"},
|
||||||
{FunctionType::Kind::BlockHash, "blockhash"},
|
{FunctionType::Kind::BlockHash, "blockhash"},
|
||||||
};
|
};
|
||||||
solAssert(functions.find(functionType->kind()) != functions.end(), "");
|
solAssert(functions.find(functionType->kind()) != functions.end());
|
||||||
|
|
||||||
string args;
|
string args;
|
||||||
for (size_t i = 0; i < arguments.size(); ++i)
|
for (size_t i = 0; i < arguments.size(); ++i)
|
||||||
@ -1474,7 +1497,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
t("saltSet", functionType->saltSet());
|
t("saltSet", functionType->saltSet());
|
||||||
if (functionType->saltSet())
|
if (functionType->saltSet())
|
||||||
t("salt", IRVariable(_functionCall.expression()).part("salt").name());
|
t("salt", IRVariable(_functionCall.expression()).part("salt").name());
|
||||||
solAssert(IRVariable(_functionCall).stackSlots().size() == 1, "");
|
solAssert(IRVariable(_functionCall).stackSlots().size() == 1);
|
||||||
t("address", IRVariable(_functionCall).commaSeparatedList());
|
t("address", IRVariable(_functionCall).commaSeparatedList());
|
||||||
t("isTryCall", _functionCall.annotation().tryCall);
|
t("isTryCall", _functionCall.annotation().tryCall);
|
||||||
if (_functionCall.annotation().tryCall)
|
if (_functionCall.annotation().tryCall)
|
||||||
@ -1488,7 +1511,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::Send:
|
case FunctionType::Kind::Send:
|
||||||
case FunctionType::Kind::Transfer:
|
case FunctionType::Kind::Transfer:
|
||||||
{
|
{
|
||||||
solAssert(arguments.size() == 1 && parameterTypes.size() == 1, "");
|
solAssert(arguments.size() == 1 && parameterTypes.size() == 1);
|
||||||
string address{IRVariable(_functionCall.expression()).part("address").name()};
|
string address{IRVariable(_functionCall.expression()).part("address").name()};
|
||||||
string value{expressionAsType(*arguments[0], *(parameterTypes[0]))};
|
string value{expressionAsType(*arguments[0], *(parameterTypes[0]))};
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
@ -1517,10 +1540,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::RIPEMD160:
|
case FunctionType::Kind::RIPEMD160:
|
||||||
case FunctionType::Kind::SHA256:
|
case FunctionType::Kind::SHA256:
|
||||||
{
|
{
|
||||||
solAssert(!_functionCall.annotation().tryCall, "");
|
solAssert(!_functionCall.annotation().tryCall);
|
||||||
solAssert(!functionType->valueSet(), "");
|
solAssert(!functionType->valueSet());
|
||||||
solAssert(!functionType->gasSet(), "");
|
solAssert(!functionType->gasSet());
|
||||||
solAssert(!functionType->bound(), "");
|
solAssert(!functionType->bound());
|
||||||
|
|
||||||
static map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = {
|
static map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = {
|
||||||
{FunctionType::Kind::ECRecover, std::make_tuple(1, 0)},
|
{FunctionType::Kind::ECRecover, std::make_tuple(1, 0)},
|
||||||
@ -1595,7 +1618,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
|
|||||||
for (size_t i = 0; i < _options.names().size(); ++i)
|
for (size_t i = 0; i < _options.names().size(); ++i)
|
||||||
{
|
{
|
||||||
string const& name = *_options.names()[i];
|
string const& name = *_options.names()[i];
|
||||||
solAssert(name == "salt" || name == "gas" || name == "value", "");
|
solAssert(name == "salt" || name == "gas" || name == "value");
|
||||||
|
|
||||||
define(IRVariable(_options).part(name), *_options.options()[i]);
|
define(IRVariable(_options).part(name), *_options.options()[i]);
|
||||||
}
|
}
|
||||||
@ -1613,7 +1636,7 @@ bool IRGeneratorForStatements::visit(MemberAccess const& _memberAccess)
|
|||||||
innerExpression->expression().annotation().type->category() == Type::Category::Address
|
innerExpression->expression().annotation().type->category() == Type::Category::Address
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(innerExpression->annotation().type->category() == Type::Category::Array, "");
|
solAssert(innerExpression->annotation().type->category() == Type::Category::Array);
|
||||||
// Skip visiting <address>.code
|
// Skip visiting <address>.code
|
||||||
innerExpression->expression().accept(*this);
|
innerExpression->expression().accept(*this);
|
||||||
|
|
||||||
@ -1634,7 +1657,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
if (memberFunctionType && memberFunctionType->bound())
|
if (memberFunctionType && memberFunctionType->bound())
|
||||||
{
|
{
|
||||||
define(IRVariable(_memberAccess).part("self"), _memberAccess.expression());
|
define(IRVariable(_memberAccess).part("self"), _memberAccess.expression());
|
||||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static);
|
||||||
if (memberFunctionType->kind() == FunctionType::Kind::Internal)
|
if (memberFunctionType->kind() == FunctionType::Kind::Internal)
|
||||||
assignInternalFunctionIDIfNotCalledDirectly(
|
assignInternalFunctionIDIfNotCalledDirectly(
|
||||||
_memberAccess,
|
_memberAccess,
|
||||||
@ -1650,9 +1673,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration());
|
auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration());
|
||||||
solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall, "");
|
solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall);
|
||||||
auto contract = dynamic_cast<ContractDefinition const*>(functionDefinition.scope());
|
auto contract = dynamic_cast<ContractDefinition const*>(functionDefinition.scope());
|
||||||
solAssert(contract && contract->isLibrary(), "");
|
solAssert(contract && contract->isLibrary());
|
||||||
define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n";
|
define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n";
|
||||||
define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier() << "\n";
|
define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier() << "\n";
|
||||||
}
|
}
|
||||||
@ -1665,7 +1688,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
|
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
|
||||||
if (type.isSuper())
|
if (type.isSuper())
|
||||||
solAssert(false, "");
|
solAssert(false);
|
||||||
|
|
||||||
// ordinary contract type
|
// ordinary contract type
|
||||||
else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
|
else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
|
||||||
@ -1713,7 +1736,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
")\n";
|
")\n";
|
||||||
else if (set<string>{"send", "transfer"}.count(member))
|
else if (set<string>{"send", "transfer"}.count(member))
|
||||||
{
|
{
|
||||||
solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, "");
|
solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable);
|
||||||
define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression());
|
define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression());
|
||||||
}
|
}
|
||||||
else if (set<string>{"call", "callcode", "delegatecall", "staticcall"}.count(member))
|
else if (set<string>{"call", "callcode", "delegatecall", "staticcall"}.count(member))
|
||||||
@ -1741,7 +1764,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
functionType.kind() == FunctionType::Kind::Internal
|
functionType.kind() == FunctionType::Kind::Internal
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(functionType.hasDeclaration(), "");
|
solAssert(functionType.hasDeclaration());
|
||||||
solAssert(
|
solAssert(
|
||||||
functionType.kind() == FunctionType::Kind::Error ||
|
functionType.kind() == FunctionType::Kind::Error ||
|
||||||
functionType.declaration().isPartOfExternalInterface(),
|
functionType.declaration().isPartOfExternalInterface(),
|
||||||
@ -1809,7 +1832,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||||
auto const& contractType = dynamic_cast<ContractType const&>(*arg);
|
auto const& contractType = dynamic_cast<ContractType const&>(*arg);
|
||||||
solAssert(!contractType.isSuper(), "");
|
solAssert(!contractType.isSuper());
|
||||||
ContractDefinition const& contract = contractType.contractDefinition();
|
ContractDefinition const& contract = contractType.contractDefinition();
|
||||||
m_context.subObjectsCreated().insert(&contract);
|
m_context.subObjectsCreated().insert(&contract);
|
||||||
appendCode() << Whiskers(R"(
|
appendCode() << Whiskers(R"(
|
||||||
@ -1833,7 +1856,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||||
auto const& contractType = dynamic_cast<ContractType const&>(*arg);
|
auto const& contractType = dynamic_cast<ContractType const&>(*arg);
|
||||||
solAssert(!contractType.isSuper(), "");
|
solAssert(!contractType.isSuper());
|
||||||
ContractDefinition const& contract = contractType.contractDefinition();
|
ContractDefinition const& contract = contractType.contractDefinition();
|
||||||
define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n";
|
define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n";
|
||||||
}
|
}
|
||||||
@ -1960,7 +1983,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
}
|
}
|
||||||
else if (member == "pop" || member == "push")
|
else if (member == "pop" || member == "push")
|
||||||
{
|
{
|
||||||
solAssert(type.location() == DataLocation::Storage, "");
|
solAssert(type.location() == DataLocation::Storage);
|
||||||
define(IRVariable{_memberAccess}.part("slot"), IRVariable{_memberAccess.expression()}.part("slot"));
|
define(IRVariable{_memberAccess}.part("slot"), IRVariable{_memberAccess.expression()}.part("slot"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1996,8 +2019,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
*_memberAccess.annotation().referencedDeclaration
|
*_memberAccess.annotation().referencedDeclaration
|
||||||
).resolveVirtual(m_context.mostDerivedContract(), super);
|
).resolveVirtual(m_context.mostDerivedContract(), super);
|
||||||
|
|
||||||
solAssert(resolvedFunctionDef.functionType(true), "");
|
solAssert(resolvedFunctionDef.functionType(true));
|
||||||
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal);
|
||||||
assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, resolvedFunctionDef);
|
assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, resolvedFunctionDef);
|
||||||
}
|
}
|
||||||
else if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
else if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
@ -2055,19 +2078,19 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
// The old code generator had a generic "else" case here
|
// The old code generator had a generic "else" case here
|
||||||
// without any specific code being generated,
|
// without any specific code being generated,
|
||||||
// but it would still be better to have an exhaustive list.
|
// but it would still be better to have an exhaustive list.
|
||||||
solAssert(false, "");
|
solAssert(false);
|
||||||
}
|
}
|
||||||
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType))
|
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType))
|
||||||
define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n";
|
define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n";
|
||||||
else if (dynamic_cast<UserDefinedValueType const*>(&actualType))
|
else if (dynamic_cast<UserDefinedValueType const*>(&actualType))
|
||||||
solAssert(member == "wrap" || member == "unwrap", "");
|
solAssert(member == "wrap" || member == "unwrap");
|
||||||
else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType))
|
else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType))
|
||||||
solAssert(arrayType->isByteArray() && member == "concat", "");
|
solAssert(arrayType->isByteArray() && member == "concat");
|
||||||
else
|
else
|
||||||
// The old code generator had a generic "else" case here
|
// The old code generator had a generic "else" case here
|
||||||
// without any specific code being generated,
|
// without any specific code being generated,
|
||||||
// but it would still be better to have an exhaustive list.
|
// but it would still be better to have an exhaustive list.
|
||||||
solAssert(false, "");
|
solAssert(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::Category::Module:
|
case Type::Category::Module:
|
||||||
@ -2083,17 +2106,17 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
);
|
);
|
||||||
if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
{
|
{
|
||||||
solAssert(variable->isConstant(), "");
|
solAssert(variable->isConstant());
|
||||||
handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess));
|
handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess));
|
||||||
}
|
}
|
||||||
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
{
|
{
|
||||||
auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
||||||
solAssert(function && function->isFree(), "");
|
solAssert(function && function->isFree());
|
||||||
solAssert(function->functionType(true), "");
|
solAssert(function->functionType(true));
|
||||||
solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal);
|
||||||
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
|
solAssert(funType->kind() == FunctionType::Kind::Internal);
|
||||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static);
|
||||||
|
|
||||||
assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function);
|
assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function);
|
||||||
}
|
}
|
||||||
@ -2117,7 +2140,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
|||||||
|
|
||||||
yul::Statement modified = bodyCopier(_inlineAsm.operations());
|
yul::Statement modified = bodyCopier(_inlineAsm.operations());
|
||||||
|
|
||||||
solAssert(holds_alternative<yul::Block>(modified), "");
|
solAssert(holds_alternative<yul::Block>(modified));
|
||||||
|
|
||||||
// Do not provide dialect so that we get the full type information.
|
// Do not provide dialect so that we get the full type information.
|
||||||
appendCode() << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n";
|
appendCode() << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n";
|
||||||
@ -2160,7 +2183,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
dynamic_cast<ArraySliceType const&>(baseType).arrayType();
|
dynamic_cast<ArraySliceType const&>(baseType).arrayType();
|
||||||
|
|
||||||
if (baseType.category() == Type::Category::ArraySlice)
|
if (baseType.category() == Type::Category::ArraySlice)
|
||||||
solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized(), "");
|
solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized());
|
||||||
|
|
||||||
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||||
|
|
||||||
@ -2253,8 +2276,8 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
}
|
}
|
||||||
else if (baseType.category() == Type::Category::TypeType)
|
else if (baseType.category() == Type::Category::TypeType)
|
||||||
{
|
{
|
||||||
solAssert(baseType.sizeOnStack() == 0, "");
|
solAssert(baseType.sizeOnStack() == 0);
|
||||||
solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, "");
|
solAssert(_indexAccess.annotation().type->sizeOnStack() == 0);
|
||||||
// no-op - this seems to be a lone array type (`structType[];`)
|
// no-op - this seems to be a lone array type (`structType[];`)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2279,7 +2302,7 @@ void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAcces
|
|||||||
{
|
{
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
{
|
{
|
||||||
solAssert(baseType.isDynamicallySized(), "");
|
solAssert(baseType.isDynamicallySized());
|
||||||
IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()};
|
IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()};
|
||||||
if (_indexRangeAccess.startExpression())
|
if (_indexRangeAccess.startExpression())
|
||||||
define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()});
|
define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()});
|
||||||
@ -2317,18 +2340,18 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
|||||||
switch (magicVar->type()->category())
|
switch (magicVar->type()->category())
|
||||||
{
|
{
|
||||||
case Type::Category::Contract:
|
case Type::Category::Contract:
|
||||||
solAssert(_identifier.name() == "this", "");
|
solAssert(_identifier.name() == "this");
|
||||||
define(_identifier) << "address()\n";
|
define(_identifier) << "address()\n";
|
||||||
break;
|
break;
|
||||||
case Type::Category::Integer:
|
case Type::Category::Integer:
|
||||||
solAssert(_identifier.name() == "now", "");
|
solAssert(_identifier.name() == "now");
|
||||||
define(_identifier) << "timestamp()\n";
|
define(_identifier) << "timestamp()\n";
|
||||||
break;
|
break;
|
||||||
case Type::Category::TypeType:
|
case Type::Category::TypeType:
|
||||||
{
|
{
|
||||||
auto typeType = dynamic_cast<TypeType const*>(magicVar->type());
|
auto typeType = dynamic_cast<TypeType const*>(magicVar->type());
|
||||||
if (auto contractType = dynamic_cast<ContractType const*>(typeType->actualType()))
|
if (auto contractType = dynamic_cast<ContractType const*>(typeType->actualType()))
|
||||||
solAssert(!contractType->isSuper() || _identifier.name() == "super", "");
|
solAssert(!contractType->isSuper() || _identifier.name() == "super");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -2338,11 +2361,11 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
|||||||
}
|
}
|
||||||
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||||
{
|
{
|
||||||
solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, "");
|
solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual);
|
||||||
FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract());
|
FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract());
|
||||||
|
|
||||||
solAssert(resolvedFunctionDef.functionType(true), "");
|
solAssert(resolvedFunctionDef.functionType(true));
|
||||||
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal);
|
||||||
assignInternalFunctionIDIfNotCalledDirectly(_identifier, resolvedFunctionDef);
|
assignInternalFunctionIDIfNotCalledDirectly(_identifier, resolvedFunctionDef);
|
||||||
}
|
}
|
||||||
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
@ -2437,9 +2460,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||||
solAssert(!funType.takesArbitraryParameters(), "");
|
solAssert(!funType.takesArbitraryParameters());
|
||||||
solAssert(_arguments.size() == funType.parameterTypes().size(), "");
|
solAssert(_arguments.size() == funType.parameterTypes().size());
|
||||||
solAssert(!funType.isBareCall(), "");
|
solAssert(!funType.isBareCall());
|
||||||
FunctionType::Kind const funKind = funType.kind();
|
FunctionType::Kind const funKind = funType.kind();
|
||||||
|
|
||||||
solAssert(
|
solAssert(
|
||||||
@ -2543,7 +2566,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
|
|
||||||
string const retVars = IRVariable(_functionCall).commaSeparatedList();
|
string const retVars = IRVariable(_functionCall).commaSeparatedList();
|
||||||
templ("retVars", retVars);
|
templ("retVars", retVars);
|
||||||
solAssert(retVars.empty() == returnInfo.returnTypes.empty(), "");
|
solAssert(retVars.empty() == returnInfo.returnTypes.empty());
|
||||||
|
|
||||||
templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true));
|
templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true));
|
||||||
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
|
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
|
||||||
@ -2552,7 +2575,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
|
|
||||||
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
||||||
|
|
||||||
solAssert(funType.padArguments(), "");
|
solAssert(funType.padArguments());
|
||||||
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall));
|
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall));
|
||||||
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
|
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
|
||||||
|
|
||||||
@ -2605,7 +2628,7 @@ void IRGeneratorForStatements::appendBareCall(
|
|||||||
);
|
);
|
||||||
FunctionType::Kind const funKind = funType.kind();
|
FunctionType::Kind const funKind = funType.kind();
|
||||||
|
|
||||||
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
|
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall());
|
||||||
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
|
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
|
||||||
solAssert(
|
solAssert(
|
||||||
funKind == FunctionType::Kind::BareCall ||
|
funKind == FunctionType::Kind::BareCall ||
|
||||||
@ -2613,7 +2636,7 @@ void IRGeneratorForStatements::appendBareCall(
|
|||||||
funKind == FunctionType::Kind::BareStaticCall, ""
|
funKind == FunctionType::Kind::BareStaticCall, ""
|
||||||
);
|
);
|
||||||
|
|
||||||
solAssert(!_functionCall.annotation().tryCall, "");
|
solAssert(!_functionCall.annotation().tryCall);
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
<?needsEncoding>
|
<?needsEncoding>
|
||||||
let <pos> := <allocateUnbounded>()
|
let <pos> := <allocateUnbounded>()
|
||||||
@ -2825,7 +2848,7 @@ string IRGeneratorForStatements::binaryOperation(
|
|||||||
"Not yet implemented - FixedPointType."
|
"Not yet implemented - FixedPointType."
|
||||||
);
|
);
|
||||||
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
|
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
|
||||||
solAssert(type, "");
|
solAssert(type);
|
||||||
bool checked = m_context.arithmetic() == Arithmetic::Checked;
|
bool checked = m_context.arithmetic() == Arithmetic::Checked;
|
||||||
switch (_operator)
|
switch (_operator)
|
||||||
{
|
{
|
||||||
@ -2865,9 +2888,9 @@ std::string IRGeneratorForStatements::shiftOperation(
|
|||||||
"Not yet implemented - FixedPointType."
|
"Not yet implemented - FixedPointType."
|
||||||
);
|
);
|
||||||
IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type());
|
IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type());
|
||||||
solAssert(amountType, "");
|
solAssert(amountType);
|
||||||
|
|
||||||
solAssert(_operator == Token::SHL || _operator == Token::SAR, "");
|
solAssert(_operator == Token::SHL || _operator == Token::SAR);
|
||||||
|
|
||||||
return
|
return
|
||||||
Whiskers(R"(
|
Whiskers(R"(
|
||||||
@ -2886,7 +2909,7 @@ std::string IRGeneratorForStatements::shiftOperation(
|
|||||||
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
|
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
|
||||||
{
|
{
|
||||||
langutil::Token const op = _binOp.getOperator();
|
langutil::Token const op = _binOp.getOperator();
|
||||||
solAssert(op == Token::Or || op == Token::And, "");
|
solAssert(op == Token::Or || op == Token::And);
|
||||||
|
|
||||||
_binOp.leftExpression().accept(*this);
|
_binOp.leftExpression().accept(*this);
|
||||||
setLocation(_binOp);
|
setLocation(_binOp);
|
||||||
@ -2933,7 +2956,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
|||||||
|
|
||||||
if (_memory.byteArrayElement)
|
if (_memory.byteArrayElement)
|
||||||
{
|
{
|
||||||
solAssert(_lvalue.type == *TypeProvider::byte(), "");
|
solAssert(_lvalue.type == *TypeProvider::byte());
|
||||||
appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n";
|
appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2957,9 +2980,9 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(_lvalue.type.sizeOnStack() == 1, "");
|
solAssert(_lvalue.type.sizeOnStack() == 1);
|
||||||
auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
|
auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
|
||||||
solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), "");
|
solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory));
|
||||||
appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
|
appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2968,7 +2991,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
|||||||
{
|
{
|
||||||
solUnimplementedAssert(_lvalue.type.isValueType());
|
solUnimplementedAssert(_lvalue.type.isValueType());
|
||||||
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
|
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
|
||||||
solAssert(_lvalue.type == *_immutable.variable->type(), "");
|
solAssert(_lvalue.type == *_immutable.variable->type());
|
||||||
size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable);
|
size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable);
|
||||||
|
|
||||||
IRVariable prepared(m_context.newYulVariable(), _lvalue.type);
|
IRVariable prepared(m_context.newYulVariable(), _lvalue.type);
|
||||||
@ -3028,7 +3051,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
|||||||
[&](IRLValue::Immutable const& _immutable) {
|
[&](IRLValue::Immutable const& _immutable) {
|
||||||
solUnimplementedAssert(_lvalue.type.isValueType());
|
solUnimplementedAssert(_lvalue.type.isValueType());
|
||||||
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
|
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
|
||||||
solAssert(_lvalue.type == *_immutable.variable->type(), "");
|
solAssert(_lvalue.type == *_immutable.variable->type());
|
||||||
if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation)
|
if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation)
|
||||||
{
|
{
|
||||||
string readFunction = m_utils.readFromMemory(*_immutable.variable->type());
|
string readFunction = m_utils.readFromMemory(*_immutable.variable->type());
|
||||||
@ -3050,13 +3073,13 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
|||||||
|
|
||||||
void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue _lvalue)
|
void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue _lvalue)
|
||||||
{
|
{
|
||||||
solAssert(!m_currentLValue, "");
|
solAssert(!m_currentLValue);
|
||||||
|
|
||||||
if (_expression.annotation().willBeWrittenTo)
|
if (_expression.annotation().willBeWrittenTo)
|
||||||
{
|
{
|
||||||
m_currentLValue.emplace(std::move(_lvalue));
|
m_currentLValue.emplace(std::move(_lvalue));
|
||||||
if (_lvalue.type.dataStoredIn(DataLocation::CallData))
|
if (_lvalue.type.dataStoredIn(DataLocation::CallData))
|
||||||
solAssert(holds_alternative<IRLValue::Stack>(_lvalue.kind), "");
|
solAssert(holds_alternative<IRLValue::Stack>(_lvalue.kind));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// Only define the expression, if it will not be written to.
|
// Only define the expression, if it will not be written to.
|
||||||
@ -3130,7 +3153,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
|
|||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (ASTPointer<VariableDeclaration> const& varDecl: successClause.parameters()->parameters())
|
for (ASTPointer<VariableDeclaration> const& varDecl: successClause.parameters()->parameters())
|
||||||
{
|
{
|
||||||
solAssert(varDecl, "");
|
solAssert(varDecl);
|
||||||
define(m_context.addLocalVariable(*varDecl),
|
define(m_context.addLocalVariable(*varDecl),
|
||||||
successClause.parameters()->parameters().size() == 1 ?
|
successClause.parameters()->parameters().size() == 1 ?
|
||||||
IRVariable(externalCall) :
|
IRVariable(externalCall) :
|
||||||
@ -3171,7 +3194,7 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
|
|||||||
appendCode() << runFallback << " := 0\n";
|
appendCode() << runFallback << " := 0\n";
|
||||||
if (errorClause->parameters())
|
if (errorClause->parameters())
|
||||||
{
|
{
|
||||||
solAssert(errorClause->parameters()->parameters().size() == 1, "");
|
solAssert(errorClause->parameters()->parameters().size() == 1);
|
||||||
IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front());
|
IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front());
|
||||||
define(var) << dataVariable << "\n";
|
define(var) << dataVariable << "\n";
|
||||||
}
|
}
|
||||||
@ -3192,7 +3215,7 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
|
|||||||
appendCode() << runFallback << " := 0\n";
|
appendCode() << runFallback << " := 0\n";
|
||||||
if (panicClause->parameters())
|
if (panicClause->parameters())
|
||||||
{
|
{
|
||||||
solAssert(panicClause->parameters()->parameters().size() == 1, "");
|
solAssert(panicClause->parameters()->parameters().size() == 1);
|
||||||
IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front());
|
IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front());
|
||||||
define(var) << code << "\n";
|
define(var) << code << "\n";
|
||||||
}
|
}
|
||||||
@ -3218,7 +3241,7 @@ void IRGeneratorForStatements::handleCatchFallback(TryCatchClause const& _fallba
|
|||||||
setLocation(_fallback);
|
setLocation(_fallback);
|
||||||
if (_fallback.parameters())
|
if (_fallback.parameters())
|
||||||
{
|
{
|
||||||
solAssert(m_context.evmVersion().supportsReturndata(), "");
|
solAssert(m_context.evmVersion().supportsReturndata());
|
||||||
solAssert(
|
solAssert(
|
||||||
_fallback.parameters()->parameters().size() == 1 &&
|
_fallback.parameters()->parameters().size() == 1 &&
|
||||||
_fallback.parameters()->parameters().front() &&
|
_fallback.parameters()->parameters().front() &&
|
||||||
@ -3254,7 +3277,7 @@ void IRGeneratorForStatements::revertWithError(
|
|||||||
for (ASTPointer<Expression const> const& arg: _errorArguments)
|
for (ASTPointer<Expression const> const& arg: _errorArguments)
|
||||||
{
|
{
|
||||||
errorArgumentVars += IRVariable(*arg).stackSlots();
|
errorArgumentVars += IRVariable(*arg).stackSlots();
|
||||||
solAssert(arg->annotation().type, "");
|
solAssert(arg->annotation().type);
|
||||||
errorArgumentTypes.push_back(arg->annotation().type);
|
errorArgumentTypes.push_back(arg->annotation().type);
|
||||||
}
|
}
|
||||||
templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars));
|
templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars));
|
||||||
@ -3272,6 +3295,6 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause)
|
|||||||
|
|
||||||
string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const
|
string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const
|
||||||
{
|
{
|
||||||
solAssert(_library.isLibrary(), "");
|
solAssert(_library.isLibrary());
|
||||||
return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")";
|
return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")";
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,9 @@ CHC::CHC(
|
|||||||
|
|
||||||
void CHC::analyze(SourceUnit const& _source)
|
void CHC::analyze(SourceUnit const& _source)
|
||||||
{
|
{
|
||||||
|
if (!shouldAnalyze(_source))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!m_settings.solvers.z3 && !m_settings.solvers.smtlib2)
|
if (!m_settings.solvers.z3 && !m_settings.solvers.smtlib2)
|
||||||
{
|
{
|
||||||
if (!m_noSolverWarning)
|
if (!m_noSolverWarning)
|
||||||
@ -137,6 +140,9 @@ vector<string> CHC::unhandledQueries() const
|
|||||||
|
|
||||||
bool CHC::visit(ContractDefinition const& _contract)
|
bool CHC::visit(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
|
if (!shouldAnalyze(_contract))
|
||||||
|
return false;
|
||||||
|
|
||||||
resetContractAnalysis();
|
resetContractAnalysis();
|
||||||
initContract(_contract);
|
initContract(_contract);
|
||||||
clearIndices(&_contract);
|
clearIndices(&_contract);
|
||||||
@ -152,6 +158,9 @@ bool CHC::visit(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
void CHC::endVisit(ContractDefinition const& _contract)
|
void CHC::endVisit(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
|
if (!shouldAnalyze(_contract))
|
||||||
|
return;
|
||||||
|
|
||||||
for (auto base: _contract.annotation().linearizedBaseContracts)
|
for (auto base: _contract.annotation().linearizedBaseContracts)
|
||||||
{
|
{
|
||||||
if (auto constructor = base->constructor())
|
if (auto constructor = base->constructor())
|
||||||
|
@ -1035,6 +1035,12 @@ void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SMTEncoder::shouldAnalyze(SourceUnit const& _source) const
|
||||||
|
{
|
||||||
|
return m_settings.contracts.isDefault() ||
|
||||||
|
m_settings.contracts.has(*_source.annotation().path);
|
||||||
|
}
|
||||||
|
|
||||||
bool SMTEncoder::shouldAnalyze(ContractDefinition const& _contract) const
|
bool SMTEncoder::shouldAnalyze(ContractDefinition const& _contract) const
|
||||||
{
|
{
|
||||||
if (!_contract.canBeDeployed())
|
if (!_contract.canBeDeployed())
|
||||||
|
@ -223,6 +223,8 @@ protected:
|
|||||||
/// @returns true if @param _contract is set for analysis in the settings
|
/// @returns true if @param _contract is set for analysis in the settings
|
||||||
/// and it is not abstract.
|
/// and it is not abstract.
|
||||||
bool shouldAnalyze(ContractDefinition const& _contract) const;
|
bool shouldAnalyze(ContractDefinition const& _contract) const;
|
||||||
|
/// @returns true if @param _source is set for analysis in the settings.
|
||||||
|
bool shouldAnalyze(SourceUnit const& _source) const;
|
||||||
|
|
||||||
bool isPublicGetter(Expression const& _expr);
|
bool isPublicGetter(Expression const& _expr);
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ struct OptimiserSettings
|
|||||||
static char constexpr DefaultYulOptimiserSteps[] =
|
static char constexpr DefaultYulOptimiserSteps[] =
|
||||||
"dhfoDgvulfnTUtnIf" // None of these can make stack problems worse
|
"dhfoDgvulfnTUtnIf" // None of these can make stack problems worse
|
||||||
"["
|
"["
|
||||||
"xa[r]scLM" // Turn into SSA and simplify
|
"xa[r]EscLM" // Turn into SSA and simplify
|
||||||
"cCTUtTOntnfDIul" // Perform structural simplification
|
"cCTUtTOntnfDIul" // Perform structural simplification
|
||||||
"Lcul" // Simplify again
|
"Lcul" // Simplify again
|
||||||
"Vcul [j]" // Reverse SSA
|
"Vcul [j]" // Reverse SSA
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace std::string_literals;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
using namespace solidity::lsp;
|
using namespace solidity::lsp;
|
||||||
@ -96,10 +97,10 @@ LanguageServer::LanguageServer(Transport& _transport):
|
|||||||
{"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)},
|
{"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)},
|
||||||
{"initialized", [](auto, auto) {}},
|
{"initialized", [](auto, auto) {}},
|
||||||
{"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }},
|
{"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }},
|
||||||
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _1, _2)},
|
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)},
|
||||||
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _1, _2)},
|
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
|
||||||
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _1, _2)},
|
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
|
||||||
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _1, _2)},
|
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
|
||||||
},
|
},
|
||||||
m_fileRepository("/" /* basePath */),
|
m_fileRepository("/" /* basePath */),
|
||||||
m_compilerStack{m_fileRepository.reader()}
|
m_compilerStack{m_fileRepository.reader()}
|
||||||
@ -260,6 +261,10 @@ bool LanguageServer::run()
|
|||||||
else
|
else
|
||||||
m_client.error({}, ErrorCode::ParseError, "\"method\" has to be a string.");
|
m_client.error({}, ErrorCode::ParseError, "\"method\" has to be a string.");
|
||||||
}
|
}
|
||||||
|
catch (RequestError const& error)
|
||||||
|
{
|
||||||
|
m_client.error(id, error.code(), error.comment() ? *error.comment() : ""s);
|
||||||
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
m_client.error(id, ErrorCode::InternalError, "Unhandled exception: "s + boost::current_exception_diagnostic_information());
|
m_client.error(id, ErrorCode::InternalError, "Unhandled exception: "s + boost::current_exception_diagnostic_information());
|
||||||
@ -268,24 +273,23 @@ bool LanguageServer::run()
|
|||||||
return m_state == State::ExitRequested;
|
return m_state == State::ExitRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LanguageServer::checkServerInitialized(MessageID _id)
|
void LanguageServer::requireServerInitialized()
|
||||||
{
|
{
|
||||||
if (m_state != State::Initialized)
|
lspAssert(
|
||||||
{
|
m_state == State::Initialized,
|
||||||
m_client.error(_id, ErrorCode::ServerNotInitialized, "Server is not properly initialized.");
|
ErrorCode::ServerNotInitialized,
|
||||||
return false;
|
"Server is not properly initialized."
|
||||||
}
|
);
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
||||||
{
|
{
|
||||||
if (m_state != State::Started)
|
lspAssert(
|
||||||
{
|
m_state == State::Started,
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Initialize called at the wrong time.");
|
ErrorCode::RequestFailed,
|
||||||
return;
|
"Initialize called at the wrong time."
|
||||||
}
|
);
|
||||||
|
|
||||||
m_state = State::Initialized;
|
m_state = State::Initialized;
|
||||||
|
|
||||||
// The default of FileReader is to use `.`, but the path from where the LSP was started
|
// The default of FileReader is to use `.`, but the path from where the LSP was started
|
||||||
@ -294,11 +298,12 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
|||||||
if (Json::Value uri = _args["rootUri"])
|
if (Json::Value uri = _args["rootUri"])
|
||||||
{
|
{
|
||||||
rootPath = uri.asString();
|
rootPath = uri.asString();
|
||||||
if (!boost::starts_with(rootPath, "file://"))
|
lspAssert(
|
||||||
{
|
boost::starts_with(rootPath, "file://"),
|
||||||
m_client.error(_id, ErrorCode::InvalidParams, "rootUri only supports file URI scheme.");
|
ErrorCode::InvalidParams,
|
||||||
return;
|
"rootUri only supports file URI scheme."
|
||||||
}
|
);
|
||||||
|
|
||||||
rootPath = rootPath.substr(7);
|
rootPath = rootPath.substr(7);
|
||||||
}
|
}
|
||||||
else if (Json::Value rootPath = _args["rootPath"])
|
else if (Json::Value rootPath = _args["rootPath"])
|
||||||
@ -317,23 +322,23 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
|||||||
m_client.reply(_id, move(replyArgs));
|
m_client.reply(_id, move(replyArgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LanguageServer::handleWorkspaceDidChangeConfiguration(Json::Value const& _args)
|
||||||
void LanguageServer::handleWorkspaceDidChangeConfiguration(MessageID _id, Json::Value const& _args)
|
|
||||||
{
|
{
|
||||||
if (!checkServerInitialized(_id))
|
requireServerInitialized();
|
||||||
return;
|
|
||||||
|
|
||||||
if (_args["settings"].isObject())
|
if (_args["settings"].isObject())
|
||||||
changeConfiguration(_args["settings"]);
|
changeConfiguration(_args["settings"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::handleTextDocumentDidOpen(MessageID _id, Json::Value const& _args)
|
void LanguageServer::handleTextDocumentDidOpen(Json::Value const& _args)
|
||||||
{
|
{
|
||||||
if (!checkServerInitialized(_id))
|
requireServerInitialized();
|
||||||
return;
|
|
||||||
|
|
||||||
if (!_args["textDocument"])
|
lspAssert(
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Text document parameter missing.");
|
_args["textDocument"],
|
||||||
|
ErrorCode::RequestFailed,
|
||||||
|
"Text document parameter missing."
|
||||||
|
);
|
||||||
|
|
||||||
string text = _args["textDocument"]["text"].asString();
|
string text = _args["textDocument"]["text"].asString();
|
||||||
string uri = _args["textDocument"]["uri"].asString();
|
string uri = _args["textDocument"]["uri"].asString();
|
||||||
@ -342,41 +347,37 @@ void LanguageServer::handleTextDocumentDidOpen(MessageID _id, Json::Value const&
|
|||||||
compileAndUpdateDiagnostics();
|
compileAndUpdateDiagnostics();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::handleTextDocumentDidChange(MessageID _id, Json::Value const& _args)
|
void LanguageServer::handleTextDocumentDidChange(Json::Value const& _args)
|
||||||
{
|
{
|
||||||
if (!checkServerInitialized(_id))
|
requireServerInitialized();
|
||||||
return;
|
|
||||||
|
|
||||||
string const uri = _args["textDocument"]["uri"].asString();
|
string const uri = _args["textDocument"]["uri"].asString();
|
||||||
|
|
||||||
for (Json::Value jsonContentChange: _args["contentChanges"])
|
for (Json::Value jsonContentChange: _args["contentChanges"])
|
||||||
{
|
{
|
||||||
if (!jsonContentChange.isObject())
|
lspAssert(
|
||||||
{
|
jsonContentChange.isObject(),
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Invalid content reference.");
|
ErrorCode::RequestFailed,
|
||||||
return;
|
"Invalid content reference."
|
||||||
}
|
);
|
||||||
|
|
||||||
string const sourceUnitName = m_fileRepository.clientPathToSourceUnitName(uri);
|
string const sourceUnitName = m_fileRepository.clientPathToSourceUnitName(uri);
|
||||||
if (!m_fileRepository.sourceUnits().count(sourceUnitName))
|
lspAssert(
|
||||||
{
|
m_fileRepository.sourceUnits().count(sourceUnitName),
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Unknown file: " + uri);
|
ErrorCode::RequestFailed,
|
||||||
return;
|
"Unknown file: " + uri
|
||||||
}
|
);
|
||||||
|
|
||||||
string text = jsonContentChange["text"].asString();
|
string text = jsonContentChange["text"].asString();
|
||||||
if (jsonContentChange["range"].isObject()) // otherwise full content update
|
if (jsonContentChange["range"].isObject()) // otherwise full content update
|
||||||
{
|
{
|
||||||
optional<SourceLocation> change = parseRange(sourceUnitName, jsonContentChange["range"]);
|
optional<SourceLocation> change = parseRange(sourceUnitName, jsonContentChange["range"]);
|
||||||
if (!change || !change->hasText())
|
lspAssert(
|
||||||
{
|
change && change->hasText(),
|
||||||
m_client.error(
|
|
||||||
_id,
|
|
||||||
ErrorCode::RequestFailed,
|
ErrorCode::RequestFailed,
|
||||||
"Invalid source range: " + jsonCompactPrint(jsonContentChange["range"])
|
"Invalid source range: " + jsonCompactPrint(jsonContentChange["range"])
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
|
||||||
string buffer = m_fileRepository.sourceUnits().at(sourceUnitName);
|
string buffer = m_fileRepository.sourceUnits().at(sourceUnitName);
|
||||||
buffer.replace(static_cast<size_t>(change->start), static_cast<size_t>(change->end - change->start), move(text));
|
buffer.replace(static_cast<size_t>(change->start), static_cast<size_t>(change->end - change->start), move(text));
|
||||||
text = move(buffer);
|
text = move(buffer);
|
||||||
@ -387,13 +388,15 @@ void LanguageServer::handleTextDocumentDidChange(MessageID _id, Json::Value cons
|
|||||||
compileAndUpdateDiagnostics();
|
compileAndUpdateDiagnostics();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageServer::handleTextDocumentDidClose(MessageID _id, Json::Value const& _args)
|
void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
|
||||||
{
|
{
|
||||||
if (!checkServerInitialized(_id))
|
requireServerInitialized();
|
||||||
return;
|
|
||||||
|
|
||||||
if (!_args["textDocument"])
|
lspAssert(
|
||||||
m_client.error(_id, ErrorCode::RequestFailed, "Text document parameter missing.");
|
_args["textDocument"],
|
||||||
|
ErrorCode::RequestFailed,
|
||||||
|
"Text document parameter missing."
|
||||||
|
);
|
||||||
|
|
||||||
string uri = _args["textDocument"]["uri"].asString();
|
string uri = _args["textDocument"]["uri"].asString();
|
||||||
m_openFiles.erase(uri);
|
m_openFiles.erase(uri);
|
||||||
|
@ -60,12 +60,12 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Checks if the server is initialized (to be used by messages that need it to be initialized).
|
/// Checks if the server is initialized (to be used by messages that need it to be initialized).
|
||||||
/// Reports an error and returns false if not.
|
/// Reports an error and returns false if not.
|
||||||
bool checkServerInitialized(MessageID _id);
|
void requireServerInitialized();
|
||||||
void handleInitialize(MessageID _id, Json::Value const& _args);
|
void handleInitialize(MessageID _id, Json::Value const& _args);
|
||||||
void handleWorkspaceDidChangeConfiguration(MessageID _id, Json::Value const& _args);
|
void handleWorkspaceDidChangeConfiguration(Json::Value const& _args);
|
||||||
void handleTextDocumentDidOpen(MessageID _id, Json::Value const& _args);
|
void handleTextDocumentDidOpen(Json::Value const& _args);
|
||||||
void handleTextDocumentDidChange(MessageID _id, Json::Value const& _args);
|
void handleTextDocumentDidChange(Json::Value const& _args);
|
||||||
void handleTextDocumentDidClose(MessageID _id, Json::Value const& _args);
|
void handleTextDocumentDidClose(Json::Value const& _args);
|
||||||
|
|
||||||
/// Invoked when the server user-supplied configuration changes (initiated by the client).
|
/// Invoked when the server user-supplied configuration changes (initiated by the client).
|
||||||
void changeConfiguration(Json::Value const&);
|
void changeConfiguration(Json::Value const&);
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolutil/Exceptions.h>
|
||||||
|
|
||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -45,6 +47,32 @@ enum class ErrorCode
|
|||||||
RequestFailed = -32803
|
RequestFailed = -32803
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error exception used to bail out on errors in the LSP function-call handlers.
|
||||||
|
*/
|
||||||
|
class RequestError: public util::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit RequestError(ErrorCode _code):
|
||||||
|
m_code{_code}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode code() const noexcept { return m_code; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorCode m_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define lspAssert(condition, errorCode, errorMessage) \
|
||||||
|
if (!(condition)) \
|
||||||
|
{ \
|
||||||
|
BOOST_THROW_EXCEPTION( \
|
||||||
|
RequestError(errorCode) << \
|
||||||
|
errinfo_comment(errorMessage) \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transport layer API
|
* Transport layer API
|
||||||
*
|
*
|
||||||
|
@ -120,6 +120,8 @@ add_library(yul
|
|||||||
optimiser/DeadCodeEliminator.h
|
optimiser/DeadCodeEliminator.h
|
||||||
optimiser/Disambiguator.cpp
|
optimiser/Disambiguator.cpp
|
||||||
optimiser/Disambiguator.h
|
optimiser/Disambiguator.h
|
||||||
|
optimiser/EqualStoreEliminator.cpp
|
||||||
|
optimiser/EqualStoreEliminator.h
|
||||||
optimiser/EquivalentFunctionDetector.cpp
|
optimiser/EquivalentFunctionDetector.cpp
|
||||||
optimiser/EquivalentFunctionDetector.h
|
optimiser/EquivalentFunctionDetector.h
|
||||||
optimiser/EquivalentFunctionCombiner.cpp
|
optimiser/EquivalentFunctionCombiner.cpp
|
||||||
|
70
libyul/optimiser/EqualStoreEliminator.cpp
Normal file
70
libyul/optimiser/EqualStoreEliminator.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
/**
|
||||||
|
* Optimisation stage that removes mstore and sstore operations if they store the same
|
||||||
|
* value that is already known to be in that slot.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libyul/optimiser/EqualStoreEliminator.h>
|
||||||
|
|
||||||
|
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||||
|
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||||
|
#include <libyul/optimiser/Semantics.h>
|
||||||
|
#include <libyul/AST.h>
|
||||||
|
#include <libyul/Utilities.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::evmasm;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
|
||||||
|
void EqualStoreEliminator::run(OptimiserStepContext const& _context, Block& _ast)
|
||||||
|
{
|
||||||
|
EqualStoreEliminator eliminator{
|
||||||
|
_context.dialect,
|
||||||
|
SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast))
|
||||||
|
};
|
||||||
|
eliminator(_ast);
|
||||||
|
|
||||||
|
StatementRemover remover{eliminator.m_pendingRemovals};
|
||||||
|
remover(_ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EqualStoreEliminator::visit(Statement& _statement)
|
||||||
|
{
|
||||||
|
// No need to consider potential changes through complex arguments since
|
||||||
|
// isSimpleStore only returns something if the arguments are identifiers.
|
||||||
|
if (ExpressionStatement const* expression = get_if<ExpressionStatement>(&_statement))
|
||||||
|
{
|
||||||
|
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, *expression))
|
||||||
|
{
|
||||||
|
if (auto const* currentValue = valueOrNullptr(m_storage, vars->first))
|
||||||
|
if (*currentValue == vars->second)
|
||||||
|
m_pendingRemovals.insert(&_statement);
|
||||||
|
}
|
||||||
|
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, *expression))
|
||||||
|
{
|
||||||
|
if (auto const* currentValue = valueOrNullptr(m_memory, vars->first))
|
||||||
|
if (*currentValue == vars->second)
|
||||||
|
m_pendingRemovals.insert(&_statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFlowAnalyzer::visit(_statement);
|
||||||
|
}
|
60
libyul/optimiser/EqualStoreEliminator.h
Normal file
60
libyul/optimiser/EqualStoreEliminator.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
/**
|
||||||
|
* Optimisation stage that removes mstore and sstore operations if they store the same
|
||||||
|
* value that is already known to be in that slot.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/optimiser/DataFlowAnalyzer.h>
|
||||||
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
|
||||||
|
namespace solidity::yul
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimisation stage that removes mstore and sstore operations if they store the same
|
||||||
|
* value that is already known to be in that slot.
|
||||||
|
*
|
||||||
|
* Works best if the code is in SSA form - without literal arguments.
|
||||||
|
*
|
||||||
|
* Prerequisite: Disambiguator, ForLoopInitRewriter.
|
||||||
|
*/
|
||||||
|
class EqualStoreEliminator: public DataFlowAnalyzer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr char const* name{"EqualStoreEliminator"};
|
||||||
|
static void run(OptimiserStepContext const&, Block& _ast);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EqualStoreEliminator(
|
||||||
|
Dialect const& _dialect,
|
||||||
|
std::map<YulString, SideEffects> _functionSideEffects
|
||||||
|
):
|
||||||
|
DataFlowAnalyzer(_dialect, std::move(_functionSideEffects))
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using ASTModifier::visit;
|
||||||
|
void visit(Statement& _statement) override;
|
||||||
|
|
||||||
|
std::set<Statement const*> m_pendingRemovals;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -57,3 +57,18 @@ optional<evmasm::Instruction> yul::toEVMInstruction(Dialect const& _dialect, Yul
|
|||||||
return builtin->instruction;
|
return builtin->instruction;
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StatementRemover::operator()(Block& _block)
|
||||||
|
{
|
||||||
|
util::iterateReplacing(
|
||||||
|
_block.statements,
|
||||||
|
[&](Statement& _statement) -> std::optional<vector<Statement>>
|
||||||
|
{
|
||||||
|
if (m_toRemove.count(&_statement))
|
||||||
|
return {vector<Statement>{}};
|
||||||
|
else
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ASTModifier::operator()(_block);
|
||||||
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <libyul/ASTForward.h>
|
#include <libyul/ASTForward.h>
|
||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
#include <libyul/YulString.h>
|
#include <libyul/YulString.h>
|
||||||
|
#include <libyul/optimiser/ASTWalker.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
@ -48,4 +49,14 @@ bool isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifie
|
|||||||
/// Helper function that returns the instruction, if the `_name` is a BuiltinFunction
|
/// Helper function that returns the instruction, if the `_name` is a BuiltinFunction
|
||||||
std::optional<evmasm::Instruction> toEVMInstruction(Dialect const& _dialect, YulString const& _name);
|
std::optional<evmasm::Instruction> toEVMInstruction(Dialect const& _dialect, YulString const& _name);
|
||||||
|
|
||||||
|
class StatementRemover: public ASTModifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit StatementRemover(std::set<Statement const*> const& _toRemove): m_toRemove(_toRemove) {}
|
||||||
|
|
||||||
|
void operator()(Block& _block) override;
|
||||||
|
private:
|
||||||
|
std::set<Statement const*> const& m_toRemove;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||||
#include <libyul/optimiser/FunctionGrouper.h>
|
#include <libyul/optimiser/FunctionGrouper.h>
|
||||||
#include <libyul/optimiser/FunctionHoister.h>
|
#include <libyul/optimiser/FunctionHoister.h>
|
||||||
|
#include <libyul/optimiser/EqualStoreEliminator.h>
|
||||||
#include <libyul/optimiser/EquivalentFunctionCombiner.h>
|
#include <libyul/optimiser/EquivalentFunctionCombiner.h>
|
||||||
#include <libyul/optimiser/ExpressionSplitter.h>
|
#include <libyul/optimiser/ExpressionSplitter.h>
|
||||||
#include <libyul/optimiser/ExpressionJoiner.h>
|
#include <libyul/optimiser/ExpressionJoiner.h>
|
||||||
@ -204,6 +205,7 @@ map<string, unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps()
|
|||||||
ConditionalUnsimplifier,
|
ConditionalUnsimplifier,
|
||||||
ControlFlowSimplifier,
|
ControlFlowSimplifier,
|
||||||
DeadCodeEliminator,
|
DeadCodeEliminator,
|
||||||
|
EqualStoreEliminator,
|
||||||
EquivalentFunctionCombiner,
|
EquivalentFunctionCombiner,
|
||||||
ExpressionInliner,
|
ExpressionInliner,
|
||||||
ExpressionJoiner,
|
ExpressionJoiner,
|
||||||
@ -244,6 +246,7 @@ map<string, char> const& OptimiserSuite::stepNameToAbbreviationMap()
|
|||||||
{ConditionalUnsimplifier::name, 'U'},
|
{ConditionalUnsimplifier::name, 'U'},
|
||||||
{ControlFlowSimplifier::name, 'n'},
|
{ControlFlowSimplifier::name, 'n'},
|
||||||
{DeadCodeEliminator::name, 'D'},
|
{DeadCodeEliminator::name, 'D'},
|
||||||
|
{EqualStoreEliminator::name, 'E'},
|
||||||
{EquivalentFunctionCombiner::name, 'v'},
|
{EquivalentFunctionCombiner::name, 'v'},
|
||||||
{ExpressionInliner::name, 'e'},
|
{ExpressionInliner::name, 'e'},
|
||||||
{ExpressionJoiner::name, 'j'},
|
{ExpressionJoiner::name, 'j'},
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <libyul/optimiser/UnusedAssignEliminator.h>
|
#include <libyul/optimiser/UnusedAssignEliminator.h>
|
||||||
|
|
||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
|
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
|
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
@ -156,18 +156,3 @@ void UnusedStoreBase::merge(TrackedStores& _target, vector<TrackedStores>&& _sou
|
|||||||
merge(_target, move(ts));
|
merge(_target, move(ts));
|
||||||
_source.clear();
|
_source.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatementRemover::operator()(Block& _block)
|
|
||||||
{
|
|
||||||
util::iterateReplacing(
|
|
||||||
_block.statements,
|
|
||||||
[&](Statement& _statement) -> std::optional<vector<Statement>>
|
|
||||||
{
|
|
||||||
if (m_toRemove.count(&_statement))
|
|
||||||
return {vector<Statement>{}};
|
|
||||||
else
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
ASTModifier::operator()(_block);
|
|
||||||
}
|
|
||||||
|
@ -105,14 +105,4 @@ protected:
|
|||||||
size_t m_forLoopNestingDepth = 0;
|
size_t m_forLoopNestingDepth = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatementRemover: public ASTModifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit StatementRemover(std::set<Statement const*> const& _toRemove): m_toRemove(_toRemove) {}
|
|
||||||
|
|
||||||
void operator()(Block& _block) override;
|
|
||||||
private:
|
|
||||||
std::set<Statement const*> const& m_toRemove;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ else
|
|||||||
BUILD_DIR="$1"
|
BUILD_DIR="$1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-8
|
# solbuildpackpusher/solidity-buildpack-deps:emscripten-9
|
||||||
docker run -v "$(pwd):/root/project" -w /root/project \
|
docker run -v "$(pwd):/root/project" -w /root/project \
|
||||||
solbuildpackpusher/solidity-buildpack-deps@sha256:842d6074e0e7e5355c89122c1cafc1fdb59696596750e7d56e5f35c0d883ad59 \
|
solbuildpackpusher/solidity-buildpack-deps@sha256:d51534dfdd05ece86f69ed7beafd68c15b88606da00a4b7fe2873ccfbd0dce24\
|
||||||
./scripts/ci/build_emscripten.sh "$BUILD_DIR"
|
./scripts/ci/build_emscripten.sh "$BUILD_DIR"
|
||||||
|
@ -187,7 +187,7 @@ def parse_cli_output(source_file_name: Path, cli_output: str) -> FileReport:
|
|||||||
return file_report
|
return file_report
|
||||||
|
|
||||||
|
|
||||||
def prepare_compiler_input( # pylint: disable=too-many-arguments
|
def prepare_compiler_input(
|
||||||
compiler_path: Path,
|
compiler_path: Path,
|
||||||
source_file_name: Path,
|
source_file_name: Path,
|
||||||
optimize: bool,
|
optimize: bool,
|
||||||
@ -256,7 +256,7 @@ def detect_metadata_cli_option_support(compiler_path: Path):
|
|||||||
return process.returncode == 0
|
return process.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def run_compiler( # pylint: disable=too-many-arguments
|
def run_compiler(
|
||||||
compiler_path: Path,
|
compiler_path: Path,
|
||||||
source_file_name: Path,
|
source_file_name: Path,
|
||||||
optimize: bool,
|
optimize: bool,
|
||||||
@ -320,7 +320,7 @@ def run_compiler( # pylint: disable=too-many-arguments
|
|||||||
return parse_cli_output(Path(source_file_name), process.stdout)
|
return parse_cli_output(Path(source_file_name), process.stdout)
|
||||||
|
|
||||||
|
|
||||||
def generate_report( # pylint: disable=too-many-arguments,too-many-locals
|
def generate_report(
|
||||||
source_file_names: List[str],
|
source_file_names: List[str],
|
||||||
compiler_path: Path,
|
compiler_path: Path,
|
||||||
interface: CompilerInterface,
|
interface: CompilerInterface,
|
||||||
|
@ -25,9 +25,9 @@ set -ev
|
|||||||
keyid=70D110489D66E2F6
|
keyid=70D110489D66E2F6
|
||||||
email=builds@ethereum.org
|
email=builds@ethereum.org
|
||||||
packagename=z3-static
|
packagename=z3-static
|
||||||
version=4.8.13
|
version=4.8.14
|
||||||
|
|
||||||
DISTRIBUTIONS="focal groovy hirsute"
|
DISTRIBUTIONS="focal hirsute impish jammy"
|
||||||
|
|
||||||
for distribution in $DISTRIBUTIONS
|
for distribution in $DISTRIBUTIONS
|
||||||
do
|
do
|
||||||
|
@ -33,12 +33,12 @@
|
|||||||
# Using $(em-config CACHE)/sysroot/usr seems to work, though, and still has cmake find the
|
# Using $(em-config CACHE)/sysroot/usr seems to work, though, and still has cmake find the
|
||||||
# dependencies automatically.
|
# dependencies automatically.
|
||||||
FROM emscripten/emsdk:2.0.33 AS base
|
FROM emscripten/emsdk:2.0.33 AS base
|
||||||
LABEL version="8"
|
LABEL version="9"
|
||||||
|
|
||||||
ADD emscripten.jam /usr/src
|
ADD emscripten.jam /usr/src
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
cd /usr/src; \
|
cd /usr/src; \
|
||||||
git clone https://github.com/Z3Prover/z3.git -b z3-4.8.13 --depth 1 ; \
|
git clone https://github.com/Z3Prover/z3.git -b z3-4.8.14 --depth 1 ; \
|
||||||
cd z3; \
|
cd z3; \
|
||||||
mkdir build; \
|
mkdir build; \
|
||||||
cd build; \
|
cd build; \
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
# (c) 2016-2021 solidity contributors.
|
# (c) 2016-2021 solidity contributors.
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
FROM gcr.io/oss-fuzz-base/base-clang:latest as base
|
FROM gcr.io/oss-fuzz-base/base-clang:latest as base
|
||||||
LABEL version="14"
|
LABEL version="15"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ RUN set -ex; \
|
|||||||
|
|
||||||
# Z3
|
# Z3
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
git clone --depth 1 -b z3-4.8.13 https://github.com/Z3Prover/z3.git \
|
git clone --depth 1 -b z3-4.8.14 https://github.com/Z3Prover/z3.git \
|
||||||
/usr/src/z3; \
|
/usr/src/z3; \
|
||||||
cd /usr/src/z3; \
|
cd /usr/src/z3; \
|
||||||
mkdir build; \
|
mkdir build; \
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
# (c) 2016-2019 solidity contributors.
|
# (c) 2016-2019 solidity contributors.
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
FROM buildpack-deps:focal AS base
|
FROM buildpack-deps:focal AS base
|
||||||
LABEL version="9"
|
LABEL version="10"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
# (c) 2016-2019 solidity contributors.
|
# (c) 2016-2019 solidity contributors.
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
FROM buildpack-deps:focal AS base
|
FROM buildpack-deps:focal AS base
|
||||||
LABEL version="9"
|
LABEL version="10"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
@ -33,12 +33,12 @@ def parse_call(call):
|
|||||||
return function.strip(), arguments.strip(), results.strip()
|
return function.strip(), arguments.strip(), results.strip()
|
||||||
|
|
||||||
|
|
||||||
def colorize(left, right, id):
|
def colorize(left, right, index):
|
||||||
red = "\x1b[31m"
|
red = "\x1b[31m"
|
||||||
yellow = "\x1b[33m"
|
yellow = "\x1b[33m"
|
||||||
reset = "\x1b[0m"
|
reset = "\x1b[0m"
|
||||||
colors = [red, yellow]
|
colors = [red, yellow]
|
||||||
color = colors[id % len(colors)]
|
color = colors[index % len(colors)]
|
||||||
function, _arguments, _results = parse_call(right)
|
function, _arguments, _results = parse_call(right)
|
||||||
left = left.replace("compileAndRun", color + "compileAndRun" + reset)
|
left = left.replace("compileAndRun", color + "compileAndRun" + reset)
|
||||||
right = right.replace("constructor", color + "constructor" + reset)
|
right = right.replace("constructor", color + "constructor" + reset)
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
#
|
#
|
||||||
# verify-testcases.py will compare both traces. If these traces are identical, the extracted tests were
|
# verify-testcases.py will compare both traces. If these traces are identical, the extracted tests were
|
||||||
# identical with the tests specified in SolidityEndToEndTest.cpp.
|
# identical with the tests specified in SolidityEndToEndTest.cpp.
|
||||||
#
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
@ -32,11 +30,11 @@ class Trace:
|
|||||||
def get_input(self):
|
def get_input(self):
|
||||||
return self._input
|
return self._input
|
||||||
|
|
||||||
def set_input(self, input):
|
def set_input(self, bytecode):
|
||||||
if self.kind == "create":
|
if self.kind == "create":
|
||||||
# remove cbor encoded metadata from bytecode
|
# remove cbor encoded metadata from bytecode
|
||||||
length = int(input[-4:], 16) * 2
|
length = int(bytecode[-4:], 16) * 2
|
||||||
self._input = input[:len(input) - length - 4]
|
self._input = bytecode[:len(bytecode) - length - 4]
|
||||||
|
|
||||||
def get_output(self):
|
def get_output(self):
|
||||||
return self._output
|
return self._output
|
||||||
@ -110,21 +108,21 @@ class TraceAnalyser:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_parameters(line, trace):
|
def parse_parameters(line, trace):
|
||||||
input = re.search(r'\s*in:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
input_match = re.search(r'\s*in:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||||
if input:
|
if input_match:
|
||||||
trace.input = input.group(1)
|
trace.input = input_match.group(1)
|
||||||
output = re.search(r'\s*out:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
output_match = re.search(r'\s*out:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||||
if output:
|
if output_match:
|
||||||
trace.output = output.group(1)
|
trace.output = output_match.group(1)
|
||||||
result = re.search(r'\s*result:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
result_match = re.search(r'\s*result:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||||
if result:
|
if result_match:
|
||||||
trace.result = result.group(1)
|
trace.result = result_match.group(1)
|
||||||
gas_used = re.search(r'\s*gas\sused:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
gas_used_match = re.search(r'\s*gas\sused:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||||
if gas_used:
|
if gas_used_match:
|
||||||
trace.gas = gas_used.group(1)
|
trace.gas = gas_used_match.group(1)
|
||||||
value = re.search(r'\s*value:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
value_match = re.search(r'\s*value:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||||
if value:
|
if value_match:
|
||||||
trace.value = value.group(1)
|
trace.value = value_match.group(1)
|
||||||
|
|
||||||
def diff(self, analyser):
|
def diff(self, analyser):
|
||||||
if not self.ready:
|
if not self.ready:
|
||||||
@ -154,7 +152,8 @@ class TraceAnalyser:
|
|||||||
|
|
||||||
print(len(intersection), "test-cases - ", len(mismatches), " mismatche(s)")
|
print(len(intersection), "test-cases - ", len(mismatches), " mismatche(s)")
|
||||||
|
|
||||||
def check_traces(self, test_name, left, right, mismatches):
|
@classmethod
|
||||||
|
def check_traces(cls, test_name, left, right, mismatches):
|
||||||
for trace_id, trace in enumerate(left.traces):
|
for trace_id, trace in enumerate(left.traces):
|
||||||
left_trace = trace
|
left_trace = trace
|
||||||
right_trace = right.traces[trace_id]
|
right_trace = right.traces[trace_id]
|
||||||
|
@ -18,7 +18,7 @@ def read_file(file_name):
|
|||||||
with open(file_name, "r", encoding="latin-1" if is_latin else ENCODING) as f:
|
with open(file_name, "r", encoding="latin-1" if is_latin else ENCODING) as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
finally:
|
finally:
|
||||||
if content == None:
|
if content is None:
|
||||||
print(f"Error reading: {file_name}")
|
print(f"Error reading: {file_name}")
|
||||||
return content
|
return content
|
||||||
|
|
||||||
@ -44,11 +44,11 @@ def find_ids_in_source_file(file_name, id_to_file_names):
|
|||||||
if in_comment(source, m.start()):
|
if in_comment(source, m.start()):
|
||||||
continue
|
continue
|
||||||
underscore_pos = m.group(0).index("_")
|
underscore_pos = m.group(0).index("_")
|
||||||
id = m.group(0)[0:underscore_pos]
|
error_id = m.group(0)[0:underscore_pos]
|
||||||
if id in id_to_file_names:
|
if error_id in id_to_file_names:
|
||||||
id_to_file_names[id].append(file_name)
|
id_to_file_names[error_id].append(file_name)
|
||||||
else:
|
else:
|
||||||
id_to_file_names[id] = [file_name]
|
id_to_file_names[error_id] = [file_name]
|
||||||
|
|
||||||
|
|
||||||
def find_ids_in_source_files(file_names):
|
def find_ids_in_source_files(file_names):
|
||||||
@ -76,16 +76,16 @@ def fix_ids_in_source_file(file_name, id_to_count, available_ids):
|
|||||||
destination.extend(source[k:m.start()])
|
destination.extend(source[k:m.start()])
|
||||||
|
|
||||||
underscore_pos = m.group(0).index("_")
|
underscore_pos = m.group(0).index("_")
|
||||||
id = m.group(0)[0:underscore_pos]
|
error_id = m.group(0)[0:underscore_pos]
|
||||||
|
|
||||||
# incorrect id or id has a duplicate somewhere
|
# incorrect id or id has a duplicate somewhere
|
||||||
if not in_comment(source, m.start()) and (len(id) != 4 or id[0] == "0" or id_to_count[id] > 1):
|
if not in_comment(source, m.start()) and (len(error_id) != 4 or error_id[0] == "0" or id_to_count[error_id] > 1):
|
||||||
assert id in id_to_count
|
assert error_id in id_to_count
|
||||||
new_id = get_next_id(available_ids)
|
new_id = get_next_id(available_ids)
|
||||||
assert new_id not in id_to_count
|
assert new_id not in id_to_count
|
||||||
id_to_count[id] -= 1
|
id_to_count[error_id] -= 1
|
||||||
else:
|
else:
|
||||||
new_id = id
|
new_id = error_id
|
||||||
|
|
||||||
destination.extend(new_id + "_error")
|
destination.extend(new_id + "_error")
|
||||||
k = m.end()
|
k = m.end()
|
||||||
@ -104,7 +104,7 @@ def fix_ids_in_source_files(file_names, id_to_count):
|
|||||||
id_to_count contains number of appearances of every id in sources
|
id_to_count contains number of appearances of every id in sources
|
||||||
"""
|
"""
|
||||||
|
|
||||||
available_ids = {str(id) for id in range(1000, 10000)} - id_to_count.keys()
|
available_ids = {str(error_id) for error_id in range(1000, 10000)} - id_to_count.keys()
|
||||||
for file_name in file_names:
|
for file_name in file_names:
|
||||||
fix_ids_in_source_file(file_name, id_to_count, available_ids)
|
fix_ids_in_source_file(file_name, id_to_count, available_ids)
|
||||||
|
|
||||||
@ -113,8 +113,8 @@ def find_files(top_dir, sub_dirs, extensions):
|
|||||||
"""Builds a list of files with given extensions in specified subdirectories"""
|
"""Builds a list of files with given extensions in specified subdirectories"""
|
||||||
|
|
||||||
source_file_names = []
|
source_file_names = []
|
||||||
for dir in sub_dirs:
|
for directory in sub_dirs:
|
||||||
for root, _, file_names in os.walk(os.path.join(top_dir, dir), onerror=lambda e: exit(f"Walk error: {e}")):
|
for root, _, file_names in os.walk(os.path.join(top_dir, directory), onerror=lambda e: sys.exit(f"Walk error: {e}")):
|
||||||
for file_name in file_names:
|
for file_name in file_names:
|
||||||
_, ext = path.splitext(file_name)
|
_, ext = path.splitext(file_name)
|
||||||
if ext in extensions:
|
if ext in extensions:
|
||||||
@ -145,27 +145,27 @@ def find_ids_in_cmdline_test_err(file_name):
|
|||||||
|
|
||||||
|
|
||||||
def print_ids(ids):
|
def print_ids(ids):
|
||||||
for k, id in enumerate(sorted(ids)):
|
for k, error_id in enumerate(sorted(ids)):
|
||||||
if k % 10 > 0:
|
if k % 10 > 0:
|
||||||
print(" ", end="")
|
print(" ", end="")
|
||||||
elif k > 0:
|
elif k > 0:
|
||||||
print()
|
print()
|
||||||
print(id, end="")
|
print(error_id, end="")
|
||||||
|
|
||||||
|
|
||||||
def print_ids_per_file(ids, id_to_file_names, top_dir):
|
def print_ids_per_file(ids, id_to_file_names, top_dir):
|
||||||
file_name_to_ids = {}
|
file_name_to_ids = {}
|
||||||
for id in ids:
|
for error_id in ids:
|
||||||
for file_name in id_to_file_names[id]:
|
for file_name in id_to_file_names[error_id]:
|
||||||
relpath = path.relpath(file_name, top_dir)
|
relpath = path.relpath(file_name, top_dir)
|
||||||
if relpath not in file_name_to_ids:
|
if relpath not in file_name_to_ids:
|
||||||
file_name_to_ids[relpath] = []
|
file_name_to_ids[relpath] = []
|
||||||
file_name_to_ids[relpath].append(id)
|
file_name_to_ids[relpath].append(error_id)
|
||||||
|
|
||||||
for file_name in sorted(file_name_to_ids):
|
for file_name in sorted(file_name_to_ids):
|
||||||
print(file_name)
|
print(file_name)
|
||||||
for id in sorted(file_name_to_ids[file_name]):
|
for error_id in sorted(file_name_to_ids[file_name]):
|
||||||
print(f" {id}", end="")
|
print(f" {error_id}", end="")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
@ -254,8 +254,6 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False):
|
|||||||
|
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
|
|
||||||
|
|
||||||
check = False
|
check = False
|
||||||
fix = False
|
fix = False
|
||||||
no_confirm = False
|
no_confirm = False
|
||||||
@ -277,7 +275,7 @@ def main(argv):
|
|||||||
|
|
||||||
if [check, fix, examine_coverage, next_id].count(True) != 1:
|
if [check, fix, examine_coverage, next_id].count(True) != 1:
|
||||||
print("usage: python error_codes.py --check | --fix [--no-confirm] | --examine-coverage | --next")
|
print("usage: python error_codes.py --check | --fix [--no-confirm] | --examine-coverage | --next")
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
|
|
||||||
@ -289,23 +287,23 @@ def main(argv):
|
|||||||
source_id_to_file_names = find_ids_in_source_files(source_file_names)
|
source_id_to_file_names = find_ids_in_source_files(source_file_names)
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
for id in sorted(source_id_to_file_names):
|
for error_id in sorted(source_id_to_file_names):
|
||||||
if len(id) != 4:
|
if len(error_id) != 4:
|
||||||
print(f"ID {id} length != 4")
|
print(f"ID {error_id} length != 4")
|
||||||
ok = False
|
ok = False
|
||||||
if id[0] == "0":
|
if error_id[0] == "0":
|
||||||
print(f"ID {id} starts with zero")
|
print(f"ID {error_id} starts with zero")
|
||||||
ok = False
|
ok = False
|
||||||
if len(source_id_to_file_names[id]) > 1:
|
if len(source_id_to_file_names[error_id]) > 1:
|
||||||
print(f"ID {id} appears {len(source_id_to_file_names[id])} times")
|
print(f"ID {error_id} appears {len(source_id_to_file_names[error_id])} times")
|
||||||
ok = False
|
ok = False
|
||||||
|
|
||||||
if examine_coverage:
|
if examine_coverage:
|
||||||
if not ok:
|
if not ok:
|
||||||
print("Incorrect IDs have to be fixed before applying --examine-coverage")
|
print("Incorrect IDs have to be fixed before applying --examine-coverage")
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
res = 0 if examine_id_coverage(cwd, source_id_to_file_names) else 1
|
res = 0 if examine_id_coverage(cwd, source_id_to_file_names) else 1
|
||||||
exit(res)
|
sys.exit(res)
|
||||||
|
|
||||||
ok &= examine_id_coverage(cwd, source_id_to_file_names, new_ids_only=True)
|
ok &= examine_id_coverage(cwd, source_id_to_file_names, new_ids_only=True)
|
||||||
|
|
||||||
@ -314,18 +312,18 @@ def main(argv):
|
|||||||
if next_id:
|
if next_id:
|
||||||
if not ok:
|
if not ok:
|
||||||
print("Incorrect IDs have to be fixed before applying --next")
|
print("Incorrect IDs have to be fixed before applying --next")
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
available_ids = {str(id) for id in range(1000, 10000)} - source_id_to_file_names.keys()
|
available_ids = {str(error_id) for error_id in range(1000, 10000)} - source_id_to_file_names.keys()
|
||||||
next_id = get_next_id(available_ids)
|
next_id = get_next_id(available_ids)
|
||||||
print(f"Next ID: {next_id}")
|
print(f"Next ID: {next_id}")
|
||||||
exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if ok:
|
if ok:
|
||||||
print("No incorrect IDs found")
|
print("No incorrect IDs found")
|
||||||
exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if check:
|
if check:
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
assert fix, "Unexpected state, should not come here without --fix"
|
assert fix, "Unexpected state, should not come here without --fix"
|
||||||
|
|
||||||
@ -338,14 +336,14 @@ def main(argv):
|
|||||||
while len(answer) == 0 or answer not in "YNyn":
|
while len(answer) == 0 or answer not in "YNyn":
|
||||||
answer = input("[Y/N]? ")
|
answer = input("[Y/N]? ")
|
||||||
if answer not in "yY":
|
if answer not in "yY":
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# number of appearances for every id
|
# number of appearances for every id
|
||||||
source_id_to_count = { id: len(file_names) for id, file_names in source_id_to_file_names.items() }
|
source_id_to_count = { error_id: len(file_names) for error_id, file_names in source_id_to_file_names.items() }
|
||||||
|
|
||||||
fix_ids_in_source_files(source_file_names, source_id_to_count)
|
fix_ids_in_source_files(source_file_names, source_id_to_count)
|
||||||
print("Fixing completed")
|
print("Fixing completed")
|
||||||
exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -106,8 +106,8 @@ def write_cases(f, solidityTests, yulTests):
|
|||||||
# When code examples are extracted they are indented by 8 spaces, which violates the style guide,
|
# When code examples are extracted they are indented by 8 spaces, which violates the style guide,
|
||||||
# so before checking remove 4 spaces from each line.
|
# so before checking remove 4 spaces from each line.
|
||||||
remainder = dedent(test)
|
remainder = dedent(test)
|
||||||
hash = hashlib.sha256(test.encode("utf-8")).hexdigest()
|
source_code_hash = hashlib.sha256(test.encode("utf-8")).hexdigest()
|
||||||
sol_filename = f'test_{hash}_{cleaned_filename}.{language}'
|
sol_filename = f'test_{source_code_hash}_{cleaned_filename}.{language}'
|
||||||
with open(sol_filename, mode='w', encoding='utf8', newline='') as fi:
|
with open(sol_filename, mode='w', encoding='utf8', newline='') as fi:
|
||||||
fi.write(remainder)
|
fi.write(remainder)
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ Runs pylint on all Python files in project directories known to contain Python s
|
|||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from os import path, walk
|
from os import path, walk
|
||||||
from sys import exit
|
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
PROJECT_ROOT = path.dirname(path.dirname(path.realpath(__file__)))
|
PROJECT_ROOT = path.dirname(path.dirname(path.realpath(__file__)))
|
||||||
PYLINT_RCFILE = f"{PROJECT_ROOT}/scripts/pylintrc"
|
PYLINT_RCFILE = f"{PROJECT_ROOT}/scripts/pylintrc"
|
||||||
@ -89,7 +89,7 @@ def main():
|
|||||||
success = pylint_all_filenames(options.dev_mode, rootdirs)
|
success = pylint_all_filenames(options.dev_mode, rootdirs)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print("No problems found.")
|
print("No problems found.")
|
||||||
|
|
||||||
@ -98,4 +98,4 @@ if __name__ == "__main__":
|
|||||||
try:
|
try:
|
||||||
main()
|
main()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
exit("Interrupted by user. Exiting.")
|
sys.exit("Interrupted by user. Exiting.")
|
||||||
|
@ -14,24 +14,23 @@
|
|||||||
# ATTENTION: This list should be extended with care, consider using NOLINT comments inside your
|
# ATTENTION: This list should be extended with care, consider using NOLINT comments inside your
|
||||||
# python files instead, as the goal is actually to reduce the list of globally disabled checks.
|
# python files instead, as the goal is actually to reduce the list of globally disabled checks.
|
||||||
#
|
#
|
||||||
# TODO: What could be eliminated in future PRs: bad-continuation, invalid-name, redefined-builtin,
|
# TODO: What could be eliminated in future PRs: invalid-name, pointless-string-statement, redefined-outer-name.
|
||||||
# undefined-variable, unused-*, useless-object-inheritance.
|
|
||||||
disable=
|
disable=
|
||||||
bad-continuation,
|
|
||||||
bad-indentation,
|
bad-indentation,
|
||||||
bad-whitespace,
|
bad-whitespace,
|
||||||
consider-using-sys-exit,
|
|
||||||
duplicate-code,
|
duplicate-code,
|
||||||
invalid-name,
|
invalid-name,
|
||||||
missing-docstring,
|
missing-docstring,
|
||||||
no-else-return,
|
no-else-return,
|
||||||
no-self-use,
|
|
||||||
pointless-string-statement,
|
pointless-string-statement,
|
||||||
redefined-builtin,
|
|
||||||
redefined-outer-name,
|
redefined-outer-name,
|
||||||
singleton-comparison,
|
|
||||||
too-few-public-methods,
|
too-few-public-methods,
|
||||||
|
too-many-arguments,
|
||||||
|
too-many-branches,
|
||||||
|
too-many-instance-attributes,
|
||||||
|
too-many-locals,
|
||||||
too-many-public-methods,
|
too-many-public-methods,
|
||||||
|
too-many-statements,
|
||||||
ungrouped-imports
|
ungrouped-imports
|
||||||
|
|
||||||
[BASIC]
|
[BASIC]
|
||||||
|
@ -41,7 +41,8 @@ class regressor:
|
|||||||
"build/test/tools/ossfuzz")
|
"build/test/tools/ossfuzz")
|
||||||
self._logpath = os.path.join(self._repo_root, "test_results")
|
self._logpath = os.path.join(self._repo_root, "test_results")
|
||||||
|
|
||||||
def parseCmdLine(self, description, args):
|
@classmethod
|
||||||
|
def parseCmdLine(cls, description, args):
|
||||||
argParser = ArgumentParser(description)
|
argParser = ArgumentParser(description)
|
||||||
argParser.add_argument('-o', '--out-dir', required=True, type=str,
|
argParser.add_argument('-o', '--out-dir', required=True, type=str,
|
||||||
help="""Directory where test results will be written""")
|
help="""Directory where test results will be written""")
|
||||||
|
@ -57,7 +57,7 @@ packagename=solc
|
|||||||
|
|
||||||
static_build_distribution=hirsute
|
static_build_distribution=hirsute
|
||||||
|
|
||||||
DISTRIBUTIONS="focal hirsute impish"
|
DISTRIBUTIONS="focal hirsute impish jammy"
|
||||||
|
|
||||||
if is_release
|
if is_release
|
||||||
then
|
then
|
||||||
|
@ -40,7 +40,7 @@ def writeSourceToFile(lines):
|
|||||||
filePath, srcName = extractSourceName(lines[0])
|
filePath, srcName = extractSourceName(lines[0])
|
||||||
# print("sourceName is ", srcName)
|
# print("sourceName is ", srcName)
|
||||||
# print("filePath is", filePath)
|
# print("filePath is", filePath)
|
||||||
if filePath != False:
|
if filePath:
|
||||||
os.system("mkdir -p " + filePath)
|
os.system("mkdir -p " + filePath)
|
||||||
with open(srcName, mode='a+', encoding='utf8', newline='') as f:
|
with open(srcName, mode='a+', encoding='utf8', newline='') as f:
|
||||||
createdSources.append(srcName)
|
createdSources.append(srcName)
|
||||||
|
@ -47,8 +47,8 @@ def write_cases(f, tests):
|
|||||||
cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower()
|
cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower()
|
||||||
for test in tests:
|
for test in tests:
|
||||||
remainder = re.sub(r'^ {4}', '', test, 0, re.MULTILINE)
|
remainder = re.sub(r'^ {4}', '', test, 0, re.MULTILINE)
|
||||||
hash = hashlib.sha256(test).hexdigest()
|
source_code_hash = hashlib.sha256(test).hexdigest()
|
||||||
with open(f'test_{hash}_{cleaned_filename}.sol', 'w', encoding='utf8') as _f:
|
with open(f'test_{source_code_hash}_{cleaned_filename}.sol', 'w', encoding='utf8') as _f:
|
||||||
_f.write(remainder)
|
_f.write(remainder)
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,6 +102,8 @@ void CommonOptions::addOptions()
|
|||||||
("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files")
|
("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files")
|
||||||
("vm", po::value<std::vector<fs::path>>(&vmPaths), "path to evmc library, can be supplied multiple times.")
|
("vm", po::value<std::vector<fs::path>>(&vmPaths), "path to evmc library, can be supplied multiple times.")
|
||||||
("ewasm", po::bool_switch(&ewasm)->default_value(ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.")
|
("ewasm", po::bool_switch(&ewasm)->default_value(ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.")
|
||||||
|
("batches", po::value<size_t>(&this->batches)->default_value(1), "set number of batches to split the tests into")
|
||||||
|
("selected-batch", po::value<size_t>(&this->selectedBatch)->default_value(0), "zero-based number of batch to execute")
|
||||||
("no-semantic-tests", po::bool_switch(&disableSemanticTests)->default_value(disableSemanticTests), "disable semantic tests")
|
("no-semantic-tests", po::bool_switch(&disableSemanticTests)->default_value(disableSemanticTests), "disable semantic tests")
|
||||||
("no-smt", po::bool_switch(&disableSMT)->default_value(disableSMT), "disable SMT checker")
|
("no-smt", po::bool_switch(&disableSMT)->default_value(disableSMT), "disable SMT checker")
|
||||||
("optimize", po::bool_switch(&optimize)->default_value(optimize), "enables optimization")
|
("optimize", po::bool_switch(&optimize)->default_value(optimize), "enables optimization")
|
||||||
@ -126,6 +128,17 @@ void CommonOptions::validate() const
|
|||||||
ConfigException,
|
ConfigException,
|
||||||
"Invalid test path specified."
|
"Invalid test path specified."
|
||||||
);
|
);
|
||||||
|
assertThrow(
|
||||||
|
batches > 0,
|
||||||
|
ConfigException,
|
||||||
|
"Batches needs to be at least 1."
|
||||||
|
);
|
||||||
|
assertThrow(
|
||||||
|
selectedBatch < batches,
|
||||||
|
ConfigException,
|
||||||
|
"Selected batch has to be less than number of batches."
|
||||||
|
);
|
||||||
|
|
||||||
if (enforceGasTest)
|
if (enforceGasTest)
|
||||||
{
|
{
|
||||||
assertThrow(
|
assertThrow(
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <libsolutil/Exceptions.h>
|
#include <libsolutil/Exceptions.h>
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <test/evmc/evmc.h>
|
#include <test/evmc/evmc.h>
|
||||||
|
|
||||||
@ -67,6 +68,8 @@ struct CommonOptions
|
|||||||
bool useABIEncoderV1 = false;
|
bool useABIEncoderV1 = false;
|
||||||
bool showMessages = false;
|
bool showMessages = false;
|
||||||
bool showMetadata = false;
|
bool showMetadata = false;
|
||||||
|
size_t batches = 1;
|
||||||
|
size_t selectedBatch = 0;
|
||||||
|
|
||||||
langutil::EVMVersion evmVersion() const;
|
langutil::EVMVersion evmVersion() const;
|
||||||
|
|
||||||
@ -96,4 +99,27 @@ bool isValidSemanticTestPath(boost::filesystem::path const& _testPath);
|
|||||||
|
|
||||||
bool loadVMs(CommonOptions const& _options);
|
bool loadVMs(CommonOptions const& _options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to help with splitting up all tests into batches.
|
||||||
|
*/
|
||||||
|
class Batcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Batcher(size_t _offset, size_t _batches):
|
||||||
|
m_offset(_offset),
|
||||||
|
m_batches(_batches)
|
||||||
|
{
|
||||||
|
solAssert(m_batches > 0 && m_offset < m_batches);
|
||||||
|
}
|
||||||
|
Batcher(Batcher const&) = delete;
|
||||||
|
Batcher& operator=(Batcher const&) = delete;
|
||||||
|
|
||||||
|
bool checkAndAdvance() { return (m_counter++) % m_batches == m_offset; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t const m_offset;
|
||||||
|
size_t const m_batches;
|
||||||
|
size_t m_counter = 0;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#pragma warning(disable:4535) // calling _set_se_translator requires /EHa
|
#pragma warning(disable:4535) // calling _set_se_translator requires /EHa
|
||||||
#endif
|
#endif
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
#include <boost/test/tree/traverse.hpp>
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
@ -60,6 +61,41 @@ void removeTestSuite(std::string const& _name)
|
|||||||
master.remove(id);
|
master.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that traverses the boost test tree and removes unit tests that are
|
||||||
|
* not in the current batch.
|
||||||
|
*/
|
||||||
|
class BoostBatcher: public test_tree_visitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BoostBatcher(solidity::test::Batcher& _batcher):
|
||||||
|
m_batcher(_batcher)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void visit(test_case const& _testCase) override
|
||||||
|
{
|
||||||
|
if (!m_batcher.checkAndAdvance())
|
||||||
|
// disabling them would be nicer, but it does not work like this:
|
||||||
|
// const_cast<test_case&>(_testCase).p_run_status.value = test_unit::RS_DISABLED;
|
||||||
|
m_path.back()->remove(_testCase.p_id);
|
||||||
|
}
|
||||||
|
bool test_suite_start(test_suite const& _testSuite) override
|
||||||
|
{
|
||||||
|
m_path.push_back(&const_cast<test_suite&>(_testSuite));
|
||||||
|
return test_tree_visitor::test_suite_start(_testSuite);
|
||||||
|
}
|
||||||
|
void test_suite_finish(test_suite const& _testSuite) override
|
||||||
|
{
|
||||||
|
m_path.pop_back();
|
||||||
|
test_tree_visitor::test_suite_finish(_testSuite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
solidity::test::Batcher& m_batcher;
|
||||||
|
std::vector<test_suite*> m_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
void runTestCase(TestCase::Config const& _config, TestCase::TestCaseCreator const& _testCaseCreator)
|
void runTestCase(TestCase::Config const& _config, TestCase::TestCaseCreator const& _testCaseCreator)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -100,7 +136,8 @@ int registerTests(
|
|||||||
bool _enforceViaYul,
|
bool _enforceViaYul,
|
||||||
bool _enforceCompileToEwasm,
|
bool _enforceCompileToEwasm,
|
||||||
vector<string> const& _labels,
|
vector<string> const& _labels,
|
||||||
TestCase::TestCaseCreator _testCaseCreator
|
TestCase::TestCaseCreator _testCaseCreator,
|
||||||
|
solidity::test::Batcher& _batcher
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int numTestsAdded = 0;
|
int numTestsAdded = 0;
|
||||||
@ -131,11 +168,15 @@ int registerTests(
|
|||||||
_enforceViaYul,
|
_enforceViaYul,
|
||||||
_enforceCompileToEwasm,
|
_enforceCompileToEwasm,
|
||||||
_labels,
|
_labels,
|
||||||
_testCaseCreator
|
_testCaseCreator,
|
||||||
|
_batcher
|
||||||
);
|
);
|
||||||
_suite.add(sub_suite);
|
_suite.add(sub_suite);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// TODO would be better to set the test to disabled.
|
||||||
|
if (_batcher.checkAndAdvance())
|
||||||
{
|
{
|
||||||
// This must be a vector of unique_ptrs because Boost.Test keeps the equivalent of a string_view to the filename
|
// This must be a vector of unique_ptrs because Boost.Test keeps the equivalent of a string_view to the filename
|
||||||
// that is passed in. If the strings were stored directly in the vector, pointers/references to them would be
|
// that is passed in. If the strings were stored directly in the vector, pointers/references to them would be
|
||||||
@ -159,6 +200,7 @@ int registerTests(
|
|||||||
_suite.add(test_case);
|
_suite.add(test_case);
|
||||||
numTestsAdded = 1;
|
numTestsAdded = 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return numTestsAdded;
|
return numTestsAdded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,6 +214,7 @@ void initializeOptions()
|
|||||||
|
|
||||||
solidity::test::CommonOptions::setSingleton(std::move(options));
|
solidity::test::CommonOptions::setSingleton(std::move(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Prototype -- why isn't this declared in the boost headers?
|
// TODO: Prototype -- why isn't this declared in the boost headers?
|
||||||
@ -180,6 +223,8 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] );
|
|||||||
|
|
||||||
test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
||||||
{
|
{
|
||||||
|
using namespace solidity::test;
|
||||||
|
|
||||||
master_test_suite_t& master = framework::master_test_suite();
|
master_test_suite_t& master = framework::master_test_suite();
|
||||||
master.p_name.value = "SolidityTests";
|
master.p_name.value = "SolidityTests";
|
||||||
|
|
||||||
@ -191,6 +236,17 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
|||||||
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
||||||
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
|
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
|
||||||
|
|
||||||
|
if (!solidity::test::CommonOptions::get().enforceGasTest)
|
||||||
|
cout << endl << "WARNING :: Gas Cost Expectations are not being enforced" << endl << endl;
|
||||||
|
|
||||||
|
Batcher batcher(CommonOptions::get().selectedBatch, CommonOptions::get().batches);
|
||||||
|
if (CommonOptions::get().batches > 1)
|
||||||
|
cout << "Batch " << CommonOptions::get().selectedBatch << " out of " << CommonOptions::get().batches << endl;
|
||||||
|
|
||||||
|
// Batch the boost tests
|
||||||
|
BoostBatcher boostBatcher(batcher);
|
||||||
|
traverse_test_tree(master, boostBatcher, true);
|
||||||
|
|
||||||
// Include the interactive tests in the automatic tests as well
|
// Include the interactive tests in the automatic tests as well
|
||||||
for (auto const& ts: g_interactiveTestsuites)
|
for (auto const& ts: g_interactiveTestsuites)
|
||||||
{
|
{
|
||||||
@ -202,15 +258,19 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
|||||||
if (ts.needsVM && solidity::test::CommonOptions::get().disableSemanticTests)
|
if (ts.needsVM && solidity::test::CommonOptions::get().disableSemanticTests)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
solAssert(registerTests(
|
//TODO
|
||||||
|
//solAssert(
|
||||||
|
registerTests(
|
||||||
master,
|
master,
|
||||||
options.testPath / ts.path,
|
options.testPath / ts.path,
|
||||||
ts.subpath,
|
ts.subpath,
|
||||||
options.enforceViaYul,
|
options.enforceViaYul,
|
||||||
options.enforceCompileToEwasm,
|
options.enforceCompileToEwasm,
|
||||||
ts.labels,
|
ts.labels,
|
||||||
ts.testCaseCreator
|
ts.testCaseCreator,
|
||||||
) > 0, std::string("no ") + ts.title + " tests found");
|
batcher
|
||||||
|
);
|
||||||
|
// > 0, std::string("no ") + ts.title + " tests found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
||||||
|
@ -42,3 +42,8 @@ printTask "Running external tests..."
|
|||||||
"$REPO_ROOT/externalTests/gnosis-v2.sh" "$@"
|
"$REPO_ROOT/externalTests/gnosis-v2.sh" "$@"
|
||||||
"$REPO_ROOT/externalTests/colony.sh" "$@"
|
"$REPO_ROOT/externalTests/colony.sh" "$@"
|
||||||
"$REPO_ROOT/externalTests/ens.sh" "$@"
|
"$REPO_ROOT/externalTests/ens.sh" "$@"
|
||||||
|
"$REPO_ROOT/externalTests/trident.sh" "$@"
|
||||||
|
"$REPO_ROOT/externalTests/euler.sh" "$@"
|
||||||
|
"$REPO_ROOT/externalTests/yield-liquidator.sh" "$@"
|
||||||
|
"$REPO_ROOT/externalTests/bleeps.sh" "$@"
|
||||||
|
"$REPO_ROOT/externalTests/pool-together.sh" "$@"
|
||||||
|
84
test/externalTests/bleeps.sh
Executable file
84
test/externalTests/bleeps.sh
Executable file
@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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) 2022 solidity contributors.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source scripts/common.sh
|
||||||
|
source test/externalTests/common.sh
|
||||||
|
|
||||||
|
verify_input "$@"
|
||||||
|
BINARY_TYPE="$1"
|
||||||
|
BINARY_PATH="$2"
|
||||||
|
|
||||||
|
function compile_fn { npm run compile; }
|
||||||
|
function test_fn { npm run test; }
|
||||||
|
|
||||||
|
function bleeps_test
|
||||||
|
{
|
||||||
|
local repo="https://github.com/wighawag/bleeps"
|
||||||
|
local ref_type=tag
|
||||||
|
local ref=bleeps_migrations # TODO: There's a 0.4.19 contract in 'main' that would need patching for the latest compiler.
|
||||||
|
local config_file="hardhat.config.ts"
|
||||||
|
local config_var=config
|
||||||
|
|
||||||
|
local compile_only_presets=()
|
||||||
|
local settings_presets=(
|
||||||
|
"${compile_only_presets[@]}"
|
||||||
|
#ir-no-optimize # Compilation fails with: "YulException: Variable param_0 is 2 slot(s) too deep inside the stack."
|
||||||
|
#ir-optimize-evm-only # Compilation fails with: "YulException: Variable param_0 is 2 slot(s) too deep inside the stack."
|
||||||
|
ir-optimize-evm+yul
|
||||||
|
#legacy-no-optimize # Compilation fails with: "CompilerError: Stack too deep, try removing local variables."
|
||||||
|
#legacy-optimize-evm-only # Compilation fails with: "CompilerError: Stack too deep, try removing local variables."
|
||||||
|
legacy-optimize-evm+yul
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
|
|
||||||
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
|
|
||||||
|
pushd "common-lib/"
|
||||||
|
neutralize_package_json_hooks
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd "contracts/"
|
||||||
|
sed -i 's|"bleeps-common": "workspace:\*",|"bleeps-common": "file:../common-lib/",|g' package.json
|
||||||
|
|
||||||
|
neutralize_package_lock
|
||||||
|
neutralize_package_json_hooks
|
||||||
|
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var"
|
||||||
|
npm install npm-run-all
|
||||||
|
npm install
|
||||||
|
|
||||||
|
replace_version_pragmas
|
||||||
|
|
||||||
|
for preset in $SELECTED_PRESETS; do
|
||||||
|
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
|
||||||
|
done
|
||||||
|
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
external_test Bleeps bleeps_test
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { yarn run provision:token:contracts; }
|
function compile_fn { yarn run provision:token:contracts; }
|
||||||
function test_fn { yarn run test:contracts; }
|
function test_fn { yarn run test:contracts; }
|
||||||
@ -34,7 +35,8 @@ function test_fn { yarn run test:contracts; }
|
|||||||
function colony_test
|
function colony_test
|
||||||
{
|
{
|
||||||
local repo="https://github.com/solidity-external-tests/colonyNetwork.git"
|
local repo="https://github.com/solidity-external-tests/colonyNetwork.git"
|
||||||
local branch=develop_080
|
local ref_type=branch
|
||||||
|
local ref="develop_080"
|
||||||
local config_file="truffle.js"
|
local config_file="truffle.js"
|
||||||
|
|
||||||
local compile_only_presets=(
|
local compile_only_presets=(
|
||||||
@ -49,16 +51,15 @@ function colony_test
|
|||||||
legacy-optimize-evm+yul
|
legacy-optimize-evm+yul
|
||||||
)
|
)
|
||||||
|
|
||||||
local selected_optimizer_presets
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
|
||||||
|
|
||||||
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
download_project "$repo" "$branch" "$DIR"
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
||||||
|
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$selected_optimizer_presets")"
|
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$SELECTED_PRESETS")"
|
||||||
yarn install
|
yarn install
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ function colony_test
|
|||||||
replace_version_pragmas
|
replace_version_pragmas
|
||||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
||||||
|
|
||||||
for preset in $selected_optimizer_presets; do
|
for preset in $SELECTED_PRESETS; do
|
||||||
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,16 @@ set -e
|
|||||||
|
|
||||||
CURRENT_EVM_VERSION=london
|
CURRENT_EVM_VERSION=london
|
||||||
|
|
||||||
function print_optimizer_presets_or_exit
|
AVAILABLE_PRESETS=(
|
||||||
|
legacy-no-optimize
|
||||||
|
ir-no-optimize
|
||||||
|
legacy-optimize-evm-only
|
||||||
|
ir-optimize-evm-only
|
||||||
|
legacy-optimize-evm+yul
|
||||||
|
ir-optimize-evm+yul
|
||||||
|
)
|
||||||
|
|
||||||
|
function print_presets_or_exit
|
||||||
{
|
{
|
||||||
local selected_presets="$1"
|
local selected_presets="$1"
|
||||||
|
|
||||||
@ -37,10 +46,22 @@ function verify_input
|
|||||||
{
|
{
|
||||||
local binary_type="$1"
|
local binary_type="$1"
|
||||||
local binary_path="$2"
|
local binary_path="$2"
|
||||||
|
local selected_presets="$3"
|
||||||
|
|
||||||
(( $# == 2 )) || fail "Usage: $0 native|solcjs <path to solc or soljson.js>"
|
(( $# >= 2 && $# <= 3 )) || fail "Usage: $0 native|solcjs <path to solc or soljson.js> [preset]"
|
||||||
[[ $binary_type == native || $binary_type == solcjs ]] || fail "Invalid binary type: '${binary_type}'. Must be either 'native' or 'solcjs'."
|
[[ $binary_type == native || $binary_type == solcjs ]] || fail "Invalid binary type: '${binary_type}'. Must be either 'native' or 'solcjs'."
|
||||||
[[ -f "$binary_path" ]] || fail "The compiler binary does not exist at '${binary_path}'"
|
[[ -f "$binary_path" ]] || fail "The compiler binary does not exist at '${binary_path}'"
|
||||||
|
|
||||||
|
if [[ $selected_presets != "" ]]
|
||||||
|
then
|
||||||
|
for preset in $selected_presets
|
||||||
|
do
|
||||||
|
if [[ " ${AVAILABLE_PRESETS[*]} " != *" $preset "* ]]
|
||||||
|
then
|
||||||
|
fail "Preset '${preset}' does not exist. Available presets: ${AVAILABLE_PRESETS[*]}."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup_solc
|
function setup_solc
|
||||||
@ -77,12 +98,24 @@ function setup_solc
|
|||||||
function download_project
|
function download_project
|
||||||
{
|
{
|
||||||
local repo="$1"
|
local repo="$1"
|
||||||
local solcjs_branch="$2"
|
local ref_type="$2"
|
||||||
local test_dir="$3"
|
local solcjs_ref="$3"
|
||||||
|
local test_dir="$4"
|
||||||
|
|
||||||
printLog "Cloning $solcjs_branch of $repo..."
|
[[ $ref_type == commit || $ref_type == branch || $ref_type == tag ]] || assertFail
|
||||||
git clone --depth 1 "$repo" -b "$solcjs_branch" "$test_dir/ext"
|
|
||||||
|
printLog "Cloning ${ref_type} ${solcjs_ref} of ${repo}..."
|
||||||
|
if [[ $ref_type == commit ]]; then
|
||||||
|
mkdir ext
|
||||||
cd ext
|
cd ext
|
||||||
|
git init
|
||||||
|
git remote add origin "$repo"
|
||||||
|
git fetch --depth 1 origin "$solcjs_ref"
|
||||||
|
git reset --hard FETCH_HEAD
|
||||||
|
else
|
||||||
|
git clone --depth 1 "$repo" -b "$solcjs_ref" "$test_dir/ext"
|
||||||
|
cd ext
|
||||||
|
fi
|
||||||
echo "Current commit hash: $(git rev-parse HEAD)"
|
echo "Current commit hash: $(git rev-parse HEAD)"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +150,19 @@ function neutralize_package_json_hooks
|
|||||||
sed -i 's|"prepare": *".*"|"prepare": ""|g' package.json
|
sed -i 's|"prepare": *".*"|"prepare": ""|g' package.json
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function neutralize_packaged_contracts
|
||||||
|
{
|
||||||
|
# Frameworks will build contracts from any package that contains a configuration file.
|
||||||
|
# This is both unnecessary (any files imported from these packages will get compiled again as a
|
||||||
|
# part of the main project anyway) and trips up our version check because it won't use our
|
||||||
|
# custom compiler binary.
|
||||||
|
printLog "Removing framework config and artifacts from npm packages..."
|
||||||
|
find node_modules/ -type f '(' -name 'hardhat.config.*' -o -name 'truffle-config.*' ')' -delete
|
||||||
|
|
||||||
|
# Some npm packages also come packaged with pre-built artifacts.
|
||||||
|
find node_modules/ -path '*artifacts/build-info/*.json' -delete
|
||||||
|
}
|
||||||
|
|
||||||
function force_solc_modules
|
function force_solc_modules
|
||||||
{
|
{
|
||||||
local custom_solcjs_path="${1:-solc/}"
|
local custom_solcjs_path="${1:-solc/}"
|
||||||
@ -176,14 +222,37 @@ function force_hardhat_compiler_binary
|
|||||||
echo "Config file: ${config_file}"
|
echo "Config file: ${config_file}"
|
||||||
echo "Binary type: ${binary_type}"
|
echo "Binary type: ${binary_type}"
|
||||||
echo "Compiler path: ${solc_path}"
|
echo "Compiler path: ${solc_path}"
|
||||||
hardhat_solc_build_subtask "$SOLCVERSION_SHORT" "$SOLCVERSION" "$binary_type" "$solc_path" >> "$config_file"
|
|
||||||
|
local language="${config_file##*.}"
|
||||||
|
hardhat_solc_build_subtask "$SOLCVERSION_SHORT" "$SOLCVERSION" "$binary_type" "$solc_path" "$language" >> "$config_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
function force_hardhat_unlimited_contract_size
|
||||||
|
{
|
||||||
|
local config_file="$1"
|
||||||
|
local config_var_name="$2"
|
||||||
|
|
||||||
|
printLog "Configuring Hardhat..."
|
||||||
|
echo "-------------------------------------"
|
||||||
|
echo "Allow unlimited contract size: true"
|
||||||
|
echo "-------------------------------------"
|
||||||
|
|
||||||
|
if [[ $config_file == *\.js ]]; then
|
||||||
|
[[ $config_var_name == "" ]] || assertFail
|
||||||
|
echo "module.exports.networks.hardhat.allowUnlimitedContractSize = true" >> "$config_file"
|
||||||
|
else
|
||||||
|
[[ $config_file == *\.ts ]] || assertFail
|
||||||
|
[[ $config_var_name != "" ]] || assertFail
|
||||||
|
echo "${config_var_name}.networks!.hardhat!.allowUnlimitedContractSize = true" >> "$config_file"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function force_hardhat_compiler_settings
|
function force_hardhat_compiler_settings
|
||||||
{
|
{
|
||||||
local config_file="$1"
|
local config_file="$1"
|
||||||
local preset="$2"
|
local preset="$2"
|
||||||
local evm_version="${3:-"$CURRENT_EVM_VERSION"}"
|
local config_var_name="$3"
|
||||||
|
local evm_version="${4:-"$CURRENT_EVM_VERSION"}"
|
||||||
|
|
||||||
printLog "Configuring Hardhat..."
|
printLog "Configuring Hardhat..."
|
||||||
echo "-------------------------------------"
|
echo "-------------------------------------"
|
||||||
@ -195,10 +264,16 @@ function force_hardhat_compiler_settings
|
|||||||
echo "Compiler version (full): ${SOLCVERSION}"
|
echo "Compiler version (full): ${SOLCVERSION}"
|
||||||
echo "-------------------------------------"
|
echo "-------------------------------------"
|
||||||
|
|
||||||
{
|
local settings
|
||||||
echo -n 'module.exports["solidity"] = '
|
settings=$(hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version")
|
||||||
hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version"
|
if [[ $config_file == *\.js ]]; then
|
||||||
} >> "$config_file"
|
[[ $config_var_name == "" ]] || assertFail
|
||||||
|
echo "module.exports['solidity'] = ${settings}" >> "$config_file"
|
||||||
|
else
|
||||||
|
[[ $config_file == *\.ts ]] || assertFail
|
||||||
|
[[ $config_var_name != "" ]] || assertFail
|
||||||
|
echo "${config_var_name}.solidity = {compilers: [${settings}]}" >> "$config_file"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function truffle_verify_compiler_version
|
function truffle_verify_compiler_version
|
||||||
@ -216,8 +291,12 @@ function hardhat_verify_compiler_version
|
|||||||
local full_solc_version="$2"
|
local full_solc_version="$2"
|
||||||
|
|
||||||
printLog "Verify that the correct version (${solc_version}/${full_solc_version}) of the compiler was used to compile the contracts..."
|
printLog "Verify that the correct version (${solc_version}/${full_solc_version}) of the compiler was used to compile the contracts..."
|
||||||
grep '"solcVersion": "'"${solc_version}"'"' --with-filename artifacts/build-info/*.json || fail "Wrong compiler version detected."
|
local build_info_files
|
||||||
grep '"solcLongVersion": "'"${full_solc_version}"'"' --with-filename artifacts/build-info/*.json || fail "Wrong compiler version detected."
|
build_info_files=$(find . -path '*artifacts/build-info/*.json')
|
||||||
|
for build_info_file in $build_info_files; do
|
||||||
|
grep '"solcVersion": "'"${solc_version}"'"' --with-filename "$build_info_file" || fail "Wrong compiler version detected in ${build_info_file}."
|
||||||
|
grep '"solcLongVersion": "'"${full_solc_version}"'"' --with-filename "$build_info_file" || fail "Wrong compiler version detected in ${build_info_file}."
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
function truffle_clean
|
function truffle_clean
|
||||||
@ -249,6 +328,8 @@ function settings_from_preset
|
|||||||
local preset="$1"
|
local preset="$1"
|
||||||
local evm_version="$2"
|
local evm_version="$2"
|
||||||
|
|
||||||
|
[[ " ${AVAILABLE_PRESETS[*]} " == *" $preset "* ]] || assertFail
|
||||||
|
|
||||||
case "$preset" in
|
case "$preset" in
|
||||||
# NOTE: Remember to update `parallelism` of `t_ems_ext` job in CI config if you add/remove presets
|
# NOTE: Remember to update `parallelism` of `t_ems_ext` job in CI config if you add/remove presets
|
||||||
legacy-no-optimize) echo "{evmVersion: '${evm_version}', viaIR: false, optimizer: {enabled: false}}" ;;
|
legacy-no-optimize) echo "{evmVersion: '${evm_version}', viaIR: false, optimizer: {enabled: false}}" ;;
|
||||||
@ -292,16 +373,27 @@ function hardhat_solc_build_subtask {
|
|||||||
local full_solc_version="$2"
|
local full_solc_version="$2"
|
||||||
local binary_type="$3"
|
local binary_type="$3"
|
||||||
local solc_path="$4"
|
local solc_path="$4"
|
||||||
|
local language="$5"
|
||||||
|
|
||||||
[[ $binary_type == native || $binary_type == solcjs ]] || assertFail
|
[[ $binary_type == native || $binary_type == solcjs ]] || assertFail
|
||||||
|
|
||||||
[[ $binary_type == native ]] && local is_solcjs=false
|
[[ $binary_type == native ]] && local is_solcjs=false
|
||||||
[[ $binary_type == solcjs ]] && local is_solcjs=true
|
[[ $binary_type == solcjs ]] && local is_solcjs=true
|
||||||
|
|
||||||
|
if [[ $language == js ]]; then
|
||||||
echo "const {TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD} = require('hardhat/builtin-tasks/task-names');"
|
echo "const {TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD} = require('hardhat/builtin-tasks/task-names');"
|
||||||
echo "const assert = require('assert');"
|
echo "const assert = require('assert');"
|
||||||
echo
|
echo
|
||||||
echo "subtask(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, async (args, hre, runSuper) => {"
|
echo "subtask(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, async (args, hre, runSuper) => {"
|
||||||
|
else
|
||||||
|
[[ $language == ts ]] || assertFail
|
||||||
|
echo "import {TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD} from 'hardhat/builtin-tasks/task-names';"
|
||||||
|
echo "import assert = require('assert');"
|
||||||
|
echo "import {subtask} from 'hardhat/config';"
|
||||||
|
echo
|
||||||
|
echo "subtask(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, async (args: any, _hre: any, _runSuper: any) => {"
|
||||||
|
fi
|
||||||
|
|
||||||
echo " assert(args.solcVersion == '${solc_version}', 'Unexpected solc version: ' + args.solcVersion)"
|
echo " assert(args.solcVersion == '${solc_version}', 'Unexpected solc version: ' + args.solcVersion)"
|
||||||
echo " return {"
|
echo " return {"
|
||||||
echo " compilerPath: '$(realpath "$solc_path")',"
|
echo " compilerPath: '$(realpath "$solc_path")',"
|
||||||
@ -367,9 +459,10 @@ function hardhat_run_test
|
|||||||
local compile_only_presets="$3"
|
local compile_only_presets="$3"
|
||||||
local compile_fn="$4"
|
local compile_fn="$4"
|
||||||
local test_fn="$5"
|
local test_fn="$5"
|
||||||
|
local config_var_name="$6"
|
||||||
|
|
||||||
hardhat_clean
|
hardhat_clean
|
||||||
force_hardhat_compiler_settings "$config_file" "$preset"
|
force_hardhat_compiler_settings "$config_file" "$preset" "$config_var_name"
|
||||||
compile_and_run_test compile_fn test_fn hardhat_verify_compiler_version "$preset" "$compile_only_presets"
|
compile_and_run_test compile_fn test_fn hardhat_verify_compiler_version "$preset" "$compile_only_presets"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,49 +27,48 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { npx truffle compile; }
|
function compile_fn { yarn build; }
|
||||||
function test_fn { npm run test; }
|
function test_fn { yarn test; }
|
||||||
|
|
||||||
function ens_test
|
function ens_test
|
||||||
{
|
{
|
||||||
local repo="https://github.com/ensdomains/ens.git"
|
local repo="https://github.com/ensdomains/ens-contracts.git"
|
||||||
local branch=master
|
local ref_type=tag
|
||||||
local config_file="truffle.js"
|
local ref="v0.0.8" # The project is in flux right now and master might be too unstable for us
|
||||||
|
local config_file="hardhat.config.js"
|
||||||
|
|
||||||
local compile_only_presets=()
|
local compile_only_presets=(
|
||||||
|
legacy-no-optimize # Compiles but tests fail to deploy GovernorCompatibilityBravo (code too large).
|
||||||
|
)
|
||||||
local settings_presets=(
|
local settings_presets=(
|
||||||
"${compile_only_presets[@]}"
|
"${compile_only_presets[@]}"
|
||||||
#ir-no-optimize # "YulException: Variable var_ttl_236 is 1 slot(s) too deep inside the stack."
|
#ir-no-optimize # Compilation fails with "YulException: Variable var__945 is 1 slot(s) too deep inside the stack."
|
||||||
#ir-optimize-evm-only # "YulException: Variable var_ttl_236 is 1 slot(s) too deep inside the stack."
|
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var__945 is 1 slot(s) too deep inside the stack."
|
||||||
ir-optimize-evm+yul
|
#ir-optimize-evm+yul # Compilation fails with "YulException: Variable _5 is 1 too deep in the stack [ _5 usr$i usr$h _7 usr$scratch usr$k usr$f _4 usr$len usr$j_2 RET _2 _1 var_data_mpos usr$totallen usr$x _12 ]"
|
||||||
legacy-no-optimize
|
|
||||||
legacy-optimize-evm-only
|
legacy-optimize-evm-only
|
||||||
legacy-optimize-evm+yul
|
legacy-optimize-evm+yul
|
||||||
)
|
)
|
||||||
|
|
||||||
local selected_optimizer_presets
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
|
||||||
|
|
||||||
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
download_project "$repo" "$branch" "$DIR"
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
|
||||||
|
|
||||||
# Use latest Truffle. Older versions crash on the output from 0.8.0.
|
|
||||||
force_truffle_version ^5.1.55
|
|
||||||
|
|
||||||
neutralize_package_lock
|
neutralize_package_lock
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$selected_optimizer_presets")"
|
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
npm install
|
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")"
|
||||||
|
yarn install
|
||||||
|
|
||||||
replace_version_pragmas
|
replace_version_pragmas
|
||||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
neutralize_packaged_contracts
|
||||||
|
|
||||||
for preset in $selected_optimizer_presets; do
|
for preset in $SELECTED_PRESETS; do
|
||||||
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
external_test Ens ens_test
|
external_test ENS ens_test
|
||||||
|
74
test/externalTests/euler.sh
Executable file
74
test/externalTests/euler.sh
Executable file
@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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) 2022 solidity contributors.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source scripts/common.sh
|
||||||
|
source test/externalTests/common.sh
|
||||||
|
|
||||||
|
verify_input "$@"
|
||||||
|
BINARY_TYPE="$1"
|
||||||
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
|
function compile_fn { npm run compile; }
|
||||||
|
function test_fn { npx --no hardhat --no-compile test; }
|
||||||
|
|
||||||
|
function euler_test
|
||||||
|
{
|
||||||
|
local repo="https://github.com/euler-xyz/euler-contracts"
|
||||||
|
local ref_type=branch
|
||||||
|
local ref="master"
|
||||||
|
local config_file="hardhat.config.js"
|
||||||
|
|
||||||
|
local compile_only_presets=()
|
||||||
|
local settings_presets=(
|
||||||
|
"${compile_only_presets[@]}"
|
||||||
|
#ir-no-optimize # Compilation fails with "YulException: Variable var_utilisation_307 is 6 slot(s) too deep inside the stack."
|
||||||
|
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_utilisation_307 is 6 slot(s) too deep inside the stack."
|
||||||
|
#ir-optimize-evm+yul # Compilation fails with "YulException: Variable var_status_mpos is 3 too deep in the stack"
|
||||||
|
legacy-optimize-evm-only
|
||||||
|
legacy-optimize-evm+yul
|
||||||
|
legacy-no-optimize
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
|
|
||||||
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
|
|
||||||
|
neutralize_package_lock
|
||||||
|
neutralize_package_json_hooks
|
||||||
|
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")"
|
||||||
|
force_hardhat_unlimited_contract_size "$config_file"
|
||||||
|
npm install
|
||||||
|
|
||||||
|
replace_version_pragmas
|
||||||
|
neutralize_packaged_contracts
|
||||||
|
|
||||||
|
for preset in $SELECTED_PRESETS; do
|
||||||
|
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
external_test Euler euler_test
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { npx truffle compile; }
|
function compile_fn { npx truffle compile; }
|
||||||
function test_fn { npm test; }
|
function test_fn { npm test; }
|
||||||
@ -34,7 +35,8 @@ function test_fn { npm test; }
|
|||||||
function gnosis_safe_test
|
function gnosis_safe_test
|
||||||
{
|
{
|
||||||
local repo="https://github.com/solidity-external-tests/safe-contracts.git"
|
local repo="https://github.com/solidity-external-tests/safe-contracts.git"
|
||||||
local branch=v2_080
|
local ref_type=branch
|
||||||
|
local ref="v2_080"
|
||||||
local config_file="truffle-config.js"
|
local config_file="truffle-config.js"
|
||||||
|
|
||||||
local compile_only_presets=(
|
local compile_only_presets=(
|
||||||
@ -49,12 +51,11 @@ function gnosis_safe_test
|
|||||||
legacy-optimize-evm+yul
|
legacy-optimize-evm+yul
|
||||||
)
|
)
|
||||||
|
|
||||||
local selected_optimizer_presets
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
|
||||||
|
|
||||||
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
download_project "$repo" "$branch" "$DIR"
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
||||||
|
|
||||||
sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:solidity-external-tests/mock-contract#master_080|g' package.json
|
sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:solidity-external-tests/mock-contract#master_080|g' package.json
|
||||||
@ -62,13 +63,13 @@ function gnosis_safe_test
|
|||||||
|
|
||||||
neutralize_package_lock
|
neutralize_package_lock
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$selected_optimizer_presets")"
|
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$SELECTED_PRESETS")"
|
||||||
npm install --package-lock
|
npm install --package-lock
|
||||||
|
|
||||||
replace_version_pragmas
|
replace_version_pragmas
|
||||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
||||||
|
|
||||||
for preset in $selected_optimizer_presets; do
|
for preset in $SELECTED_PRESETS; do
|
||||||
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { npx truffle compile; }
|
function compile_fn { npx truffle compile; }
|
||||||
function test_fn { npm test; }
|
function test_fn { npm test; }
|
||||||
@ -34,7 +35,8 @@ function test_fn { npm test; }
|
|||||||
function gnosis_safe_test
|
function gnosis_safe_test
|
||||||
{
|
{
|
||||||
local repo="https://github.com/solidity-external-tests/safe-contracts.git"
|
local repo="https://github.com/solidity-external-tests/safe-contracts.git"
|
||||||
local branch=development_080
|
local ref_type=branch
|
||||||
|
local ref="development_080"
|
||||||
local config_file="truffle-config.js"
|
local config_file="truffle-config.js"
|
||||||
|
|
||||||
local compile_only_presets=()
|
local compile_only_presets=()
|
||||||
@ -48,25 +50,24 @@ function gnosis_safe_test
|
|||||||
legacy-optimize-evm+yul
|
legacy-optimize-evm+yul
|
||||||
)
|
)
|
||||||
|
|
||||||
local selected_optimizer_presets
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
|
||||||
|
|
||||||
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
download_project "$repo" "$branch" "$DIR"
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
||||||
|
|
||||||
sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:solidity-external-tests/mock-contract#master_080|g' package.json
|
sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:solidity-external-tests/mock-contract#master_080|g' package.json
|
||||||
|
|
||||||
neutralize_package_lock
|
neutralize_package_lock
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$selected_optimizer_presets")"
|
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$SELECTED_PRESETS")"
|
||||||
npm install --package-lock
|
npm install --package-lock
|
||||||
|
|
||||||
replace_version_pragmas
|
replace_version_pragmas
|
||||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
||||||
|
|
||||||
for preset in $selected_optimizer_presets; do
|
for preset in $SELECTED_PRESETS; do
|
||||||
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
78
test/externalTests/pool-together.sh
Executable file
78
test/externalTests/pool-together.sh
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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) 2022 solidity contributors.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source scripts/common.sh
|
||||||
|
source test/externalTests/common.sh
|
||||||
|
|
||||||
|
verify_input "$@"
|
||||||
|
BINARY_TYPE="$1"
|
||||||
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
|
function compile_fn { yarn compile; }
|
||||||
|
function test_fn { yarn test; }
|
||||||
|
|
||||||
|
function pool_together_test
|
||||||
|
{
|
||||||
|
local repo="https://github.com/pooltogether/v4-core"
|
||||||
|
local ref_type=branch
|
||||||
|
local ref=master
|
||||||
|
local config_file="hardhat.config.ts"
|
||||||
|
local config_var="config"
|
||||||
|
|
||||||
|
local compile_only_presets=()
|
||||||
|
local settings_presets=(
|
||||||
|
"${compile_only_presets[@]}"
|
||||||
|
#ir-no-optimize # Compilation fails with "YulException: Variable var_amount_205 is 9 slot(s) too deep inside the stack."
|
||||||
|
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_amount_205 is 9 slot(s) too deep inside the stack."
|
||||||
|
#ir-optimize-evm+yul # FIXME: ICE due to https://github.com/ethereum/solidity/issues/12558
|
||||||
|
legacy-no-optimize
|
||||||
|
legacy-optimize-evm-only
|
||||||
|
legacy-optimize-evm+yul
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
|
|
||||||
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
|
|
||||||
|
neutralize_package_lock
|
||||||
|
neutralize_package_json_hooks
|
||||||
|
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var"
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
# These come with already compiled artifacts. We want them recompiled with latest compiler.
|
||||||
|
rm -r node_modules/@pooltogether/yield-source-interface/artifacts/
|
||||||
|
rm -r node_modules/@pooltogether/uniform-random-number/artifacts/
|
||||||
|
rm -r node_modules/@pooltogether/owner-manager-contracts/artifacts/
|
||||||
|
|
||||||
|
replace_version_pragmas
|
||||||
|
|
||||||
|
for preset in $SELECTED_PRESETS; do
|
||||||
|
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
external_test Pool-Together-V4 pool_together_test
|
97
test/externalTests/trident.sh
Executable file
97
test/externalTests/trident.sh
Executable file
@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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) 2021 solidity contributors.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source scripts/common.sh
|
||||||
|
source test/externalTests/common.sh
|
||||||
|
|
||||||
|
verify_input "$@"
|
||||||
|
BINARY_TYPE="$1"
|
||||||
|
BINARY_PATH="$2"
|
||||||
|
|
||||||
|
function compile_fn { yarn build; }
|
||||||
|
|
||||||
|
function test_fn {
|
||||||
|
# shellcheck disable=SC2046
|
||||||
|
TS_NODE_TRANSPILE_ONLY=1 npx hardhat test --no-compile $(
|
||||||
|
# TODO: We need to skip Migration.test.ts because it fails and makes other tests fail too.
|
||||||
|
# Replace this with `yarn test` once https://github.com/sushiswap/trident/issues/283 is fixed.
|
||||||
|
find test/ -name "*.test.ts" ! -path "test/Migration.test.ts" | LC_ALL=C sort
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function trident_test
|
||||||
|
{
|
||||||
|
local repo="https://github.com/sushiswap/trident"
|
||||||
|
local ref_type=commit
|
||||||
|
# FIXME: Switch back to master branch when https://github.com/sushiswap/trident/issues/303 gets fixed.
|
||||||
|
local ref="0cab5ae884cc9a41223d52791be775c3a053cb26" # master as of 2021-12-16
|
||||||
|
local config_file="hardhat.config.ts"
|
||||||
|
local config_var=config
|
||||||
|
|
||||||
|
local compile_only_presets=()
|
||||||
|
local settings_presets=(
|
||||||
|
"${compile_only_presets[@]}"
|
||||||
|
#ir-no-optimize # Compilation fails with: "YulException: Variable var_amount_165 is 9 slot(s) too deep inside the stack."
|
||||||
|
#ir-optimize-evm-only # Compilation fails with: "YulException: Variable var_amount_165 is 9 slot(s) too deep inside the stack."
|
||||||
|
#ir-optimize-evm+yul # Compilation fails with: "YulException: Cannot swap Variable var_nearestTick with Variable _4: too deep in the stack by 4 slots"
|
||||||
|
legacy-no-optimize
|
||||||
|
legacy-optimize-evm-only
|
||||||
|
legacy-optimize-evm+yul
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
|
|
||||||
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
|
|
||||||
|
# TODO: Currently tests work only with the exact versions from yarn.lock.
|
||||||
|
# Re-enable this when https://github.com/sushiswap/trident/issues/284 is fixed.
|
||||||
|
#neutralize_package_lock
|
||||||
|
|
||||||
|
neutralize_package_json_hooks
|
||||||
|
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var"
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
replace_version_pragmas
|
||||||
|
force_solc_modules "${DIR}/solc"
|
||||||
|
|
||||||
|
# BentoBoxV1Flat.sol requires a few small tweaks to compile on 0.8.x.
|
||||||
|
# TODO: Remove once https://github.com/sushiswap/trident/pull/282 gets merged.
|
||||||
|
sed -i 's|uint128(-1)|type(uint128).max|g' contracts/flat/BentoBoxV1Flat.sol
|
||||||
|
sed -i 's|uint64(-1)|type(uint64).max|g' contracts/flat/BentoBoxV1Flat.sol
|
||||||
|
sed -i 's|uint32(-1)|type(uint32).max|g' contracts/flat/BentoBoxV1Flat.sol
|
||||||
|
sed -i 's|IERC20(0)|IERC20(address(0))|g' contracts/flat/BentoBoxV1Flat.sol
|
||||||
|
sed -i 's|IStrategy(0)|IStrategy(address(0))|g' contracts/flat/BentoBoxV1Flat.sol
|
||||||
|
|
||||||
|
# @sushiswap/core package contains contracts that get built with 0.6.12 and fail our compiler
|
||||||
|
# version check. It's not used by tests so we can remove it.
|
||||||
|
rm -r node_modules/@sushiswap/core/
|
||||||
|
|
||||||
|
for preset in $SELECTED_PRESETS; do
|
||||||
|
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
external_test Trident trident_test
|
74
test/externalTests/yield-liquidator.sh
Executable file
74
test/externalTests/yield-liquidator.sh
Executable file
@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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) 2022 solidity contributors.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source scripts/common.sh
|
||||||
|
source test/externalTests/common.sh
|
||||||
|
|
||||||
|
verify_input "$@"
|
||||||
|
BINARY_TYPE="$1"
|
||||||
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
|
function compile_fn { npm run build; }
|
||||||
|
function test_fn { npm run test; }
|
||||||
|
|
||||||
|
function yield_liquidator_test
|
||||||
|
{
|
||||||
|
local repo="https://github.com/yieldprotocol/yield-liquidator-v2"
|
||||||
|
local ref_type=branch
|
||||||
|
local ref="master"
|
||||||
|
local config_file="hardhat.config.ts"
|
||||||
|
local config_var="module.exports"
|
||||||
|
|
||||||
|
local compile_only_presets=()
|
||||||
|
local settings_presets=(
|
||||||
|
"${compile_only_presets[@]}"
|
||||||
|
#ir-no-optimize # Compilation fails with "YulException: Variable var_roles_168_mpos is 2 slot(s) too deep inside the stack."
|
||||||
|
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var__33 is 6 slot(s) too deep inside the stack."
|
||||||
|
ir-optimize-evm+yul
|
||||||
|
legacy-optimize-evm-only
|
||||||
|
legacy-optimize-evm+yul
|
||||||
|
legacy-no-optimize
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
|
|
||||||
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
|
|
||||||
|
neutralize_package_lock
|
||||||
|
neutralize_package_json_hooks
|
||||||
|
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var"
|
||||||
|
force_hardhat_unlimited_contract_size "$config_file" "$config_var"
|
||||||
|
npm install
|
||||||
|
|
||||||
|
replace_version_pragmas
|
||||||
|
|
||||||
|
for preset in $SELECTED_PRESETS; do
|
||||||
|
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
external_test Yield-Liquidator-V2 yield_liquidator_test
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { npm run compile; }
|
function compile_fn { npm run compile; }
|
||||||
function test_fn { npm test; }
|
function test_fn { npm test; }
|
||||||
@ -34,7 +35,8 @@ function test_fn { npm test; }
|
|||||||
function zeppelin_test
|
function zeppelin_test
|
||||||
{
|
{
|
||||||
local repo="https://github.com/OpenZeppelin/openzeppelin-contracts.git"
|
local repo="https://github.com/OpenZeppelin/openzeppelin-contracts.git"
|
||||||
local branch=master
|
local ref_type=branch
|
||||||
|
local ref="master"
|
||||||
local config_file="hardhat.config.js"
|
local config_file="hardhat.config.js"
|
||||||
|
|
||||||
local compile_only_presets=(
|
local compile_only_presets=(
|
||||||
@ -49,21 +51,20 @@ function zeppelin_test
|
|||||||
legacy-optimize-evm+yul
|
legacy-optimize-evm+yul
|
||||||
)
|
)
|
||||||
|
|
||||||
local selected_optimizer_presets
|
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
print_presets_or_exit "$SELECTED_PRESETS"
|
||||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
|
||||||
|
|
||||||
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
download_project "$repo" "$branch" "$DIR"
|
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||||
|
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
force_hardhat_compiler_settings "$config_file" "$(first_word "$selected_optimizer_presets")"
|
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")"
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
replace_version_pragmas
|
replace_version_pragmas
|
||||||
|
|
||||||
for preset in $selected_optimizer_presets; do
|
for preset in $SELECTED_PRESETS; do
|
||||||
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ class Rule:
|
|||||||
self.error('Rule is incorrect.\nModel: ' + str(m))
|
self.error('Rule is incorrect.\nModel: ' + str(m))
|
||||||
self.solver.pop()
|
self.solver.pop()
|
||||||
|
|
||||||
def error(self, msg):
|
@classmethod
|
||||||
|
def error(cls, msg):
|
||||||
print(msg)
|
print(msg)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -56,7 +56,8 @@ public:
|
|||||||
else
|
else
|
||||||
generatedDocumentation = m_compilerStack.natspecDev(_contractName);
|
generatedDocumentation = m_compilerStack.natspecDev(_contractName);
|
||||||
Json::Value expectedDocumentation;
|
Json::Value expectedDocumentation;
|
||||||
util::jsonParseStrict(_expectedDocumentationString, expectedDocumentation);
|
std::string parseError;
|
||||||
|
BOOST_REQUIRE_MESSAGE(util::jsonParseStrict(_expectedDocumentationString, expectedDocumentation, &parseError), parseError);
|
||||||
|
|
||||||
expectedDocumentation["version"] = Json::Value(Natspec::c_natspecVersion);
|
expectedDocumentation["version"] = Json::Value(Natspec::c_natspecVersion);
|
||||||
expectedDocumentation["kind"] = Json::Value(_userDocumentation ? "user" : "dev");
|
expectedDocumentation["kind"] = Json::Value(_userDocumentation ? "user" : "dev");
|
||||||
@ -118,7 +119,8 @@ BOOST_AUTO_TEST_CASE(user_newline_break)
|
|||||||
|
|
||||||
char const* natspec = R"ABCDEF(
|
char const* natspec = R"ABCDEF(
|
||||||
{
|
{
|
||||||
"methods": {
|
"methods":
|
||||||
|
{
|
||||||
"f()":
|
"f()":
|
||||||
{
|
{
|
||||||
"notice": "world"
|
"notice": "world"
|
||||||
@ -146,8 +148,10 @@ BOOST_AUTO_TEST_CASE(user_multiline_empty_lines)
|
|||||||
|
|
||||||
char const* natspec = R"ABCDEF(
|
char const* natspec = R"ABCDEF(
|
||||||
{
|
{
|
||||||
"methods": {
|
"methods":
|
||||||
"f()": {
|
{
|
||||||
|
"f()":
|
||||||
|
{
|
||||||
"notice": "hello world"
|
"notice": "hello world"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,18 +189,27 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test)
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
char const* devNatspec = "{"
|
char const* devNatspec = R"R(
|
||||||
"\"methods\":{"
|
{
|
||||||
" \"mul(uint256)\":{ \n"
|
"methods":
|
||||||
" \"details\": \"Multiplies a number by 7\"\n"
|
{
|
||||||
" }\n"
|
"mul(uint256)":
|
||||||
" }\n"
|
{
|
||||||
"}}";
|
"details": "Multiplies a number by 7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})R";
|
||||||
|
|
||||||
char const* userNatspec = "{"
|
char const* userNatspec = R"R(
|
||||||
"\"methods\":{"
|
{
|
||||||
" \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}"
|
"methods":
|
||||||
"}}";
|
{
|
||||||
|
"mul(uint256)":
|
||||||
|
{
|
||||||
|
"notice": "Multiplies `a` by 7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})R";
|
||||||
|
|
||||||
checkNatspec(sourceCode, "test", devNatspec, false);
|
checkNatspec(sourceCode, "test", devNatspec, false);
|
||||||
checkNatspec(sourceCode, "test", userNatspec, true);
|
checkNatspec(sourceCode, "test", userNatspec, true);
|
||||||
@ -634,9 +647,11 @@ BOOST_AUTO_TEST_CASE(dev_return_no_params)
|
|||||||
|
|
||||||
char const* natspec = R"ABCDEF(
|
char const* natspec = R"ABCDEF(
|
||||||
{
|
{
|
||||||
"methods": {
|
"methods":
|
||||||
"mul(uint256,uint256)": {
|
{
|
||||||
"returns": { "d": "The result of the multiplication"
|
"mul(uint256,uint256)":
|
||||||
|
{
|
||||||
|
"returns": { "d": "The result of the multiplication" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})ABCDEF";
|
})ABCDEF";
|
||||||
@ -866,19 +881,24 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return)
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
char const* natspec = "{"
|
char const* natspec = R"R({
|
||||||
"\"methods\":{"
|
"methods":
|
||||||
" \"mul(uint256,uint256)\":{ \n"
|
{
|
||||||
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
|
"mul(uint256,uint256)":
|
||||||
" \"params\": {\n"
|
{
|
||||||
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
|
"details": "Multiplies a number by 7 and adds second parameter",
|
||||||
" \"second\": \"Documentation for the second parameter\"\n"
|
"params":
|
||||||
" },\n"
|
{
|
||||||
" \"returns\": {\n"
|
"a": "Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines",
|
||||||
" \"d\": \"The result of the multiplication and cookies with nutella\",\n"
|
"second": "Documentation for the second parameter"
|
||||||
" }\n"
|
},
|
||||||
" }\n"
|
"returns":
|
||||||
"}}";
|
{
|
||||||
|
"d": "The result of the multiplication and cookies with nutella"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})R";
|
||||||
|
|
||||||
checkNatspec(sourceCode, "test", natspec, false);
|
checkNatspec(sourceCode, "test", natspec, false);
|
||||||
}
|
}
|
||||||
@ -901,19 +921,25 @@ BOOST_AUTO_TEST_CASE(dev_multiline_comment)
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
char const* natspec = "{"
|
char const* natspec = R"R(
|
||||||
"\"methods\":{"
|
{
|
||||||
" \"mul(uint256,uint256)\":{ \n"
|
"methods":
|
||||||
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
|
{
|
||||||
" \"params\": {\n"
|
"mul(uint256,uint256)":
|
||||||
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
|
{
|
||||||
" \"second\": \"Documentation for the second parameter\"\n"
|
"details": "Multiplies a number by 7 and adds second parameter",
|
||||||
" },\n"
|
"params":
|
||||||
" \"returns\": {\n"
|
{
|
||||||
" \"d\": \"The result of the multiplication and cookies with nutella\",\n"
|
"a": "Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines",
|
||||||
" }\n"
|
"second": "Documentation for the second parameter"
|
||||||
" }\n"
|
},
|
||||||
"}}";
|
"returns":
|
||||||
|
{
|
||||||
|
"d": "The result of the multiplication and cookies with nutella"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})R";
|
||||||
|
|
||||||
checkNatspec(sourceCode, "test", natspec, false);
|
checkNatspec(sourceCode, "test", natspec, false);
|
||||||
}
|
}
|
||||||
@ -1004,13 +1030,14 @@ BOOST_AUTO_TEST_CASE(natspec_notice_without_tag)
|
|||||||
|
|
||||||
char const* natspec = R"ABCDEF(
|
char const* natspec = R"ABCDEF(
|
||||||
{
|
{
|
||||||
"methods" : {
|
"methods":
|
||||||
"mul(uint256)" : {
|
{
|
||||||
|
"mul(uint256)":
|
||||||
|
{
|
||||||
"notice": "I do something awesome"
|
"notice": "I do something awesome"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})ABCDEF";
|
||||||
)ABCDEF";
|
|
||||||
|
|
||||||
checkNatspec(sourceCode, "test", natspec, true);
|
checkNatspec(sourceCode, "test", natspec, true);
|
||||||
}
|
}
|
||||||
@ -1027,8 +1054,10 @@ BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag)
|
|||||||
|
|
||||||
char const* natspec = R"ABCDEF(
|
char const* natspec = R"ABCDEF(
|
||||||
{
|
{
|
||||||
"methods" : {
|
"methods":
|
||||||
"mul(uint256)" : {
|
{
|
||||||
|
"mul(uint256)":
|
||||||
|
{
|
||||||
"notice": "I do something awesome which requires two lines to explain"
|
"notice": "I do something awesome which requires two lines to explain"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1135,8 +1164,10 @@ BOOST_AUTO_TEST_CASE(user_constructor)
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
char const* natspec = R"ABCDEF({
|
char const* natspec = R"ABCDEF({
|
||||||
"methods": {
|
"methods":
|
||||||
"constructor" : {
|
{
|
||||||
|
"constructor":
|
||||||
|
{
|
||||||
"notice": "this is a really nice constructor"
|
"notice": "this is a really nice constructor"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1157,11 +1188,14 @@ BOOST_AUTO_TEST_CASE(user_constructor_and_function)
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
char const* natspec = R"ABCDEF({
|
char const* natspec = R"ABCDEF({
|
||||||
"methods" : {
|
"methods":
|
||||||
"mul(uint256,uint256)" : {
|
{
|
||||||
|
"mul(uint256,uint256)":
|
||||||
|
{
|
||||||
"notice": "another multiplier"
|
"notice": "another multiplier"
|
||||||
},
|
},
|
||||||
"constructor" : {
|
"constructor":
|
||||||
|
{
|
||||||
"notice": "this is a really nice constructor"
|
"notice": "this is a really nice constructor"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1181,9 +1215,12 @@ BOOST_AUTO_TEST_CASE(dev_constructor)
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
char const *natspec = R"ABCDEF({
|
char const *natspec = R"ABCDEF({
|
||||||
"methods" : {
|
"methods":
|
||||||
"constructor" : {
|
{
|
||||||
"params" : {
|
"constructor":
|
||||||
|
{
|
||||||
|
"params":
|
||||||
|
{
|
||||||
"a": "the parameter a is really nice and very useful",
|
"a": "the parameter a is really nice and very useful",
|
||||||
"second": "the second parameter is not very useful, it just provides additional confusion"
|
"second": "the second parameter is not very useful, it just provides additional confusion"
|
||||||
}
|
}
|
||||||
@ -1228,19 +1265,25 @@ BOOST_AUTO_TEST_CASE(dev_constructor_and_function)
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
char const *natspec = R"ABCDEF({
|
char const *natspec = R"ABCDEF({
|
||||||
"methods" : {
|
"methods":
|
||||||
"mul(uint256,uint256)" : {
|
{
|
||||||
|
"mul(uint256,uint256)":
|
||||||
|
{
|
||||||
"details": "Multiplies a number by 7 and adds second parameter",
|
"details": "Multiplies a number by 7 and adds second parameter",
|
||||||
"params" : {
|
"params":
|
||||||
|
{
|
||||||
"a": "Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines",
|
"a": "Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines",
|
||||||
"second": "Documentation for the second parameter"
|
"second": "Documentation for the second parameter"
|
||||||
},
|
},
|
||||||
"returns" : {
|
"returns":
|
||||||
|
{
|
||||||
"d": "The result of the multiplication and cookies with nutella"
|
"d": "The result of the multiplication and cookies with nutella"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"constructor" : {
|
"constructor":
|
||||||
"params" : {
|
{
|
||||||
|
"params":
|
||||||
|
{
|
||||||
"a": "the parameter a is really nice and very useful",
|
"a": "the parameter a is really nice and very useful",
|
||||||
"second": "the second parameter is not very useful, it just provides additional confusion"
|
"second": "the second parameter is not very useful, it just provides additional confusion"
|
||||||
}
|
}
|
||||||
@ -1292,7 +1335,8 @@ BOOST_AUTO_TEST_CASE(slash3_slash3)
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
char const* natspec = R"ABCDEF({
|
char const* natspec = R"ABCDEF({
|
||||||
"methods": {
|
"methods":
|
||||||
|
{
|
||||||
"f()": { "notice": "lorem ipsum" }
|
"f()": { "notice": "lorem ipsum" }
|
||||||
}
|
}
|
||||||
})ABCDEF";
|
})ABCDEF";
|
||||||
@ -1311,7 +1355,8 @@ BOOST_AUTO_TEST_CASE(slash3_slash4)
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
char const* natspec = R"ABCDEF({
|
char const* natspec = R"ABCDEF({
|
||||||
"methods": {
|
"methods":
|
||||||
|
{
|
||||||
"f()": { "notice": "lorem" }
|
"f()": { "notice": "lorem" }
|
||||||
}
|
}
|
||||||
})ABCDEF";
|
})ABCDEF";
|
||||||
@ -2295,7 +2340,7 @@ BOOST_AUTO_TEST_CASE(dev_return_name_no_description)
|
|||||||
{
|
{
|
||||||
"returns":
|
"returns":
|
||||||
{
|
{
|
||||||
"a": "a",
|
"a": "a"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2308,7 +2353,7 @@ BOOST_AUTO_TEST_CASE(dev_return_name_no_description)
|
|||||||
{
|
{
|
||||||
"returns":
|
"returns":
|
||||||
{
|
{
|
||||||
"b": "a",
|
"b": "a"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2384,7 +2429,8 @@ BOOST_AUTO_TEST_CASE(error_multiple)
|
|||||||
|
|
||||||
char const* devdoc = R"X({
|
char const* devdoc = R"X({
|
||||||
"methods": {},
|
"methods": {},
|
||||||
"errors": {
|
"errors":
|
||||||
|
{
|
||||||
"E(uint256,uint256)": [
|
"E(uint256,uint256)": [
|
||||||
{
|
{
|
||||||
"details": "an error.",
|
"details": "an error.",
|
||||||
@ -2465,7 +2511,8 @@ BOOST_AUTO_TEST_CASE(custom_inheritance)
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
char const* natspecA = R"ABCDEF({
|
char const* natspecA = R"ABCDEF(
|
||||||
|
{
|
||||||
"methods":
|
"methods":
|
||||||
{
|
{
|
||||||
"g(uint256)":
|
"g(uint256)":
|
||||||
@ -2473,8 +2520,9 @@ BOOST_AUTO_TEST_CASE(custom_inheritance)
|
|||||||
"custom:since": "2014"
|
"custom:since": "2014"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)ABCDEF";
|
})ABCDEF";
|
||||||
char const* natspecB = R"ABCDEF({
|
char const* natspecB = R"ABCDEF(
|
||||||
|
{
|
||||||
"methods": {}
|
"methods": {}
|
||||||
})ABCDEF";
|
})ABCDEF";
|
||||||
|
|
||||||
@ -2482,7 +2530,7 @@ BOOST_AUTO_TEST_CASE(custom_inheritance)
|
|||||||
checkNatspec(sourceCode, "B", natspecB, false);
|
checkNatspec(sourceCode, "B", natspecB, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(dev_different_amount_return_parameters)
|
BOOST_AUTO_TEST_CASE(dev_struct_getter_override)
|
||||||
{
|
{
|
||||||
char const *sourceCode = R"(
|
char const *sourceCode = R"(
|
||||||
interface IThing {
|
interface IThing {
|
||||||
@ -2534,6 +2582,58 @@ BOOST_AUTO_TEST_CASE(dev_different_amount_return_parameters)
|
|||||||
checkNatspec(sourceCode, "Thing", natspec2, false);
|
checkNatspec(sourceCode, "Thing", natspec2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(dev_struct_getter_override_different_return_parameter_names)
|
||||||
|
{
|
||||||
|
char const *sourceCode = R"(
|
||||||
|
interface IThing {
|
||||||
|
/// @return x a number
|
||||||
|
/// @return y another number
|
||||||
|
function value() external view returns (uint128 x, uint128 y);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Thing is IThing {
|
||||||
|
struct Value {
|
||||||
|
uint128 a;
|
||||||
|
uint128 b;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value public override value;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
char const *natspec = R"ABCDEF({
|
||||||
|
"methods":
|
||||||
|
{
|
||||||
|
"value()":
|
||||||
|
{
|
||||||
|
"returns":
|
||||||
|
{
|
||||||
|
"x": "a number",
|
||||||
|
"y": "another number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})ABCDEF";
|
||||||
|
|
||||||
|
char const *natspec2 = R"ABCDEF({
|
||||||
|
"methods": {},
|
||||||
|
"stateVariables":
|
||||||
|
{
|
||||||
|
"value":
|
||||||
|
{
|
||||||
|
"returns":
|
||||||
|
{
|
||||||
|
"a": "a number",
|
||||||
|
"b": "another number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})ABCDEF";
|
||||||
|
|
||||||
|
checkNatspec(sourceCode, "IThing", natspec, false);
|
||||||
|
checkNatspec(sourceCode, "Thing", natspec2, false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
pragma abicoder v2;
|
||||||
|
|
||||||
|
contract X {
|
||||||
|
// no "returns" on purpose
|
||||||
|
function a(uint) public pure {}
|
||||||
|
function b(uint) external pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Base {
|
||||||
|
function a(uint x) external pure returns (uint) { return x + 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C is Base {
|
||||||
|
function test() public view returns (uint r) {
|
||||||
|
bool success;
|
||||||
|
bytes memory result;
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(X.a, 1));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 2);
|
||||||
|
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(X.b, 10));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 13);
|
||||||
|
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(Base.a, 100));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 114);
|
||||||
|
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(this.a, 1000));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 1115);
|
||||||
|
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(C.b, 10000));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 11116);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
function b(uint x) external view returns (uint) {
|
||||||
|
return this.a(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// EVMVersion: >=byzantium
|
||||||
|
// ----
|
||||||
|
// test() -> 11116
|
@ -178,7 +178,7 @@ contract DepositContract is IDepositContract, ERC165 {
|
|||||||
// compileViaYul: also
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// constructor()
|
// constructor()
|
||||||
// gas irOptimized: 1558001
|
// gas irOptimized: 1557137
|
||||||
// gas legacy: 2436584
|
// gas legacy: 2436584
|
||||||
// gas legacyOptimized: 1776483
|
// gas legacyOptimized: 1776483
|
||||||
// supportsInterface(bytes4): 0x0 -> 0
|
// supportsInterface(bytes4): 0x0 -> 0
|
||||||
|
@ -20,7 +20,7 @@ contract test {
|
|||||||
// compileViaYul: also
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// set(uint8,uint8,uint8,uint8,uint8): 1, 21, 22, 42, 43 -> 0, 0, 0, 0
|
// set(uint8,uint8,uint8,uint8,uint8): 1, 21, 22, 42, 43 -> 0, 0, 0, 0
|
||||||
// gas irOptimized: 111965
|
// gas irOptimized: 111896
|
||||||
// gas legacy: 113806
|
// gas legacy: 113806
|
||||||
// gas legacyOptimized: 111781
|
// gas legacyOptimized: 111781
|
||||||
// get(uint8): 1 -> 21, 22, 42, 43
|
// get(uint8): 1 -> 21, 22, 42, 43
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
contract C {
|
||||||
|
function g() external {}
|
||||||
|
function comparison_operators_for_external_function_pointers_with_dirty_bits() external returns (bool) {
|
||||||
|
function() external g_ptr_dirty = this.g;
|
||||||
|
assembly {
|
||||||
|
g_ptr_dirty.address := or(g_ptr_dirty.address, shl(160, sub(0,1)))
|
||||||
|
g_ptr_dirty.selector := or(g_ptr_dirty.selector, shl(32, sub(0,1)))
|
||||||
|
}
|
||||||
|
function() external g_ptr = this.g;
|
||||||
|
return g_ptr == g_ptr_dirty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// EVMVersion: >=constantinople
|
||||||
|
// ----
|
||||||
|
// comparison_operators_for_external_function_pointers_with_dirty_bits() -> true
|
@ -0,0 +1,81 @@
|
|||||||
|
contract C {
|
||||||
|
function f() external {}
|
||||||
|
function g() external {}
|
||||||
|
function h() pure external {}
|
||||||
|
function i() view external {}
|
||||||
|
|
||||||
|
function comparison_operators_for_external_functions() public returns (bool) {
|
||||||
|
assert(
|
||||||
|
this.f != this.g &&
|
||||||
|
this.f != this.h &&
|
||||||
|
this.f != this.i &&
|
||||||
|
|
||||||
|
this.g != this.h &&
|
||||||
|
this.g != this.i &&
|
||||||
|
|
||||||
|
this.h != this.i &&
|
||||||
|
|
||||||
|
this.f == this.f &&
|
||||||
|
this.g == this.g &&
|
||||||
|
this.h == this.h &&
|
||||||
|
this.i == this.i
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function comparison_operators_for_local_external_function_pointers() public returns (bool) {
|
||||||
|
function () external f_local = this.f;
|
||||||
|
function () external g_local = this.g;
|
||||||
|
function () external pure h_local = this.h;
|
||||||
|
function () external view i_local = this.i;
|
||||||
|
|
||||||
|
assert(
|
||||||
|
f_local == this.f &&
|
||||||
|
g_local == this.g &&
|
||||||
|
h_local == this.h &&
|
||||||
|
i_local == this.i &&
|
||||||
|
|
||||||
|
f_local != this.g &&
|
||||||
|
f_local != this.h &&
|
||||||
|
f_local != this.i &&
|
||||||
|
|
||||||
|
g_local != this.f &&
|
||||||
|
g_local != this.h &&
|
||||||
|
g_local != this.i &&
|
||||||
|
|
||||||
|
h_local != this.f &&
|
||||||
|
h_local != this.g &&
|
||||||
|
h_local != this.i &&
|
||||||
|
|
||||||
|
i_local != this.f &&
|
||||||
|
i_local != this.g &&
|
||||||
|
i_local != this.h
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
f_local == f_local &&
|
||||||
|
f_local != g_local &&
|
||||||
|
f_local != h_local &&
|
||||||
|
f_local != i_local
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
g_local == g_local &&
|
||||||
|
g_local != h_local &&
|
||||||
|
g_local != i_local
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
h_local == h_local &&
|
||||||
|
i_local == i_local &&
|
||||||
|
h_local != i_local
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// comparison_operators_for_external_functions() -> true
|
||||||
|
// comparison_operators_for_local_external_function_pointers() -> true
|
@ -0,0 +1,25 @@
|
|||||||
|
contract C {
|
||||||
|
function g() external {}
|
||||||
|
function h() external payable {}
|
||||||
|
function test_function() external returns (bool){
|
||||||
|
assert (
|
||||||
|
this.g.address == this.g.address &&
|
||||||
|
this.g{gas: 42}.address == this.g.address &&
|
||||||
|
this.g{gas: 42}.selector == this.g.selector
|
||||||
|
);
|
||||||
|
assert (
|
||||||
|
this.h.address == this.h.address &&
|
||||||
|
this.h{gas: 42}.address == this.h.address &&
|
||||||
|
this.h{gas: 42}.selector == this.h.selector
|
||||||
|
);
|
||||||
|
assert (
|
||||||
|
this.h{gas: 42, value: 5}.address == this.h.address &&
|
||||||
|
this.h{gas: 42, value: 5}.selector == this.h.selector
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test_function() -> true
|
@ -38,12 +38,12 @@ contract c {
|
|||||||
// compileViaYul: also
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// set(uint256): 7 -> true
|
// set(uint256): 7 -> true
|
||||||
// gas irOptimized: 110011
|
// gas irOptimized: 110119
|
||||||
// gas legacy: 110616
|
// gas legacy: 110616
|
||||||
// gas legacyOptimized: 110006
|
// gas legacyOptimized: 110006
|
||||||
// retrieve(uint256): 7 -> 1, 3, 4, 2
|
// retrieve(uint256): 7 -> 1, 3, 4, 2
|
||||||
// copy(uint256,uint256): 7, 8 -> true
|
// copy(uint256,uint256): 7, 8 -> true
|
||||||
// gas irOptimized: 118707
|
// gas irOptimized: 118698
|
||||||
// gas legacy: 119166
|
// gas legacy: 119166
|
||||||
// gas legacyOptimized: 118622
|
// gas legacyOptimized: 118622
|
||||||
// retrieve(uint256): 7 -> 1, 3, 4, 2
|
// retrieve(uint256): 7 -> 1, 3, 4, 2
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
// Triggered ICE before
|
||||||
|
contract C {
|
||||||
|
function f(string calldata data) external pure returns(string memory) {
|
||||||
|
bytes calldata test = bytes(data[:3]);
|
||||||
|
return string(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(string): 0x20, 3, "123" -> 0x20, 3, "123"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user