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:
|
||||
ubuntu-2004-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-9
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:3d8a912e8e78e98cd217955d06d98608ad60adc67728d4c3a569991235fa1abb"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-10
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:e61939751ff9777307857361f712b581bfc8a8aaae75fab7b50febc764710587"
|
||||
ubuntu-2004-clang-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-9
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:a1ba002cae17279d1396a898b04e4e9c45602ad881295db3e2f484a7e24f6f43"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-10
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:0de8c68f084120b2a165406e3a0c2aab58b32f5b7182c2322245245f1d243b8d"
|
||||
ubuntu-1604-clang-ossfuzz-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-14
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:f353823cce2f6cd2f9f1459d86cd76fdfc551a0261d87626615ea6c1d8f90587"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-15
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:87f1a57586eec194a6217ab624efc69d3d9af2f7ac8ca36497ad57488c2f08ae"
|
||||
emscripten-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-8
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:842d6074e0e7e5355c89122c1cafc1fdb59696596750e7d56e5f35c0d883ad59"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-9
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d51534dfdd05ece86f69ed7beafd68c15b88606da00a4b7fe2873ccfbd0dce24"
|
||||
evm-version:
|
||||
type: string
|
||||
default: london
|
||||
@ -53,8 +53,11 @@ commands:
|
||||
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")
|
||||
|
||||
[[ "<< parameters.event >>" == "failure" ]] && message=" ❌ [${workflow_name}] Job **${CIRCLE_JOB}** failed on **${CIRCLE_BRANCH}**. Please see [build ${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
|
||||
[[ "<< 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}**"
|
||||
[[ $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" \
|
||||
--request POST \
|
||||
@ -355,7 +358,15 @@ defaults:
|
||||
xcode: "13.2.0"
|
||||
environment:
|
||||
TERM: xterm
|
||||
MAKEFLAGS: -j 5
|
||||
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
|
||||
docker:
|
||||
@ -464,6 +475,98 @@ defaults:
|
||||
requires:
|
||||
- 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:
|
||||
|
||||
@ -791,11 +894,11 @@ jobs:
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_osx:
|
||||
<<: *base_osx
|
||||
<<: *base_osx_large
|
||||
environment:
|
||||
TERM: xterm
|
||||
CMAKE_BUILD_TYPE: Release
|
||||
MAKEFLAGS: -j 5
|
||||
MAKEFLAGS: -j10
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
@ -897,7 +1000,7 @@ jobs:
|
||||
|
||||
t_ubu_soltest_all: &t_ubu_soltest_all
|
||||
<<: *base_ubuntu2004
|
||||
parallelism: 15 # 7 EVM versions, each with/without optimization + 1 ABIv1/@nooptions run
|
||||
parallelism: 50
|
||||
<<: *steps_soltest_all
|
||||
|
||||
t_ubu_lsp: &t_ubu_lsp
|
||||
@ -906,6 +1009,7 @@ jobs:
|
||||
|
||||
t_archlinux_soltest: &t_archlinux_soltest
|
||||
<<: *base_archlinux
|
||||
parallelism: 20
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
@ -924,6 +1028,7 @@ jobs:
|
||||
|
||||
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
|
||||
<<: *base_ubuntu2004
|
||||
parallelism: 20
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
SOLTEST_FLAGS: --enforce-via-yul
|
||||
@ -933,6 +1038,7 @@ jobs:
|
||||
|
||||
t_ubu_clang_soltest: &t_ubu_clang_soltest
|
||||
<<: *base_ubuntu2004_clang
|
||||
parallelism: 20
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
@ -960,6 +1066,7 @@ jobs:
|
||||
|
||||
t_ubu_asan_soltest:
|
||||
<<: *base_ubuntu2004
|
||||
parallelism: 20
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
@ -969,6 +1076,7 @@ jobs:
|
||||
|
||||
t_ubu_asan_clang_soltest:
|
||||
<<: *base_ubuntu2004_clang
|
||||
parallelism: 20
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
@ -978,6 +1086,7 @@ jobs:
|
||||
|
||||
t_ubu_ubsan_clang_soltest:
|
||||
<<: *base_ubuntu2004_clang
|
||||
parallelism: 20
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
<<: *steps_soltest
|
||||
@ -1318,53 +1427,20 @@ workflows:
|
||||
- t_ems_solcjs: *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:
|
||||
<<: *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'
|
||||
- t_ems_ext: *job_ems_compile_ext_colony
|
||||
- t_ems_ext: *job_native_compile_ext_gnosis
|
||||
|
||||
# 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.
|
||||
#- t_ems_ext:
|
||||
# <<: *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'
|
||||
- t_ems_ext:
|
||||
<<: *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'
|
||||
- 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'
|
||||
#-t_ems_ext: *job_native_test_ext_gnosis
|
||||
- t_ems_ext: *job_native_test_ext_gnosis_v2
|
||||
- t_ems_ext: *job_native_test_ext_zeppelin
|
||||
- t_ems_ext: *job_native_test_ext_ens
|
||||
- t_ems_ext: *job_native_test_ext_trident
|
||||
- t_ems_ext: *job_native_test_ext_euler
|
||||
- t_ems_ext: *job_native_test_ext_yield_liquidator
|
||||
- t_ems_ext: *job_native_test_ext_bleeps
|
||||
- t_ems_ext: *job_native_test_ext_pool_together
|
||||
|
||||
# Windows build and tests
|
||||
- b_win: *workflow_trigger_on_tags
|
||||
@ -1374,18 +1450,23 @@ workflows:
|
||||
|
||||
# Bytecode comparison:
|
||||
- b_bytecode_ubu:
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_ubu
|
||||
- b_bytecode_win:
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_win
|
||||
- b_bytecode_osx:
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_osx
|
||||
- b_bytecode_ems:
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_ems
|
||||
- t_bytecode_compare:
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_bytecode_ubu
|
||||
- b_bytecode_win
|
||||
@ -1425,10 +1506,4 @@ workflows:
|
||||
|
||||
# Emscripten build and tests that take more than 15 minutes to execute
|
||||
- b_ems: *workflow_trigger_on_tags
|
||||
- t_ems_ext:
|
||||
<<: *workflow_emscripten
|
||||
name: t_ems_test_ext_colony
|
||||
project: colony
|
||||
binary_type: solcjs
|
||||
nodejs_version: '14'
|
||||
resource_class: medium
|
||||
- t_ems_ext: *job_ems_test_ext_colony
|
||||
|
@ -61,11 +61,11 @@ then
|
||||
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
||||
|
||||
# z3
|
||||
z3_version="4.8.13"
|
||||
z3_version="4.8.14"
|
||||
z3_dir="z3-${z3_version}-x64-osx-10.16"
|
||||
z3_package="${z3_dir}.zip"
|
||||
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"
|
||||
rm "$z3_package"
|
||||
cp "${z3_dir}/bin/libz3.a" /usr/local/lib
|
||||
|
@ -50,19 +50,48 @@ mkdir -p test_results
|
||||
ulimit -s 16384
|
||||
|
||||
get_logfile_basename() {
|
||||
local run="$1"
|
||||
local filename="${EVM}"
|
||||
test "${OPTIMIZE}" = "1" && filename="${filename}_opt"
|
||||
test "${ABI_ENCODER_V1}" = "1" && filename="${filename}_abiv1"
|
||||
filename="${filename}_${run}"
|
||||
|
||||
echo -ne "${filename}"
|
||||
}
|
||||
|
||||
BOOST_TEST_ARGS=("--color_output=no" "--show_progress=yes" "--logger=JUNIT,error,test_results/$(get_logfile_basename).xml" "${BOOST_TEST_ARGS[@]}")
|
||||
SOLTEST_ARGS=("--evm-version=$EVM" "${SOLTEST_FLAGS[@]}")
|
||||
[ -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
|
||||
|
||||
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS+=(--optimize)
|
||||
test "${ABI_ENCODER_V1}" = "1" && SOLTEST_ARGS+=(--abiencoderv1)
|
||||
# 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))
|
||||
|
||||
echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS[*]} -- ${SOLTEST_ARGS[*]}"
|
||||
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[@]}")
|
||||
|
||||
"${REPODIR}/build/test/soltest" "${BOOST_TEST_ARGS[@]}" -- "${SOLTEST_ARGS[@]}"
|
||||
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS+=(--optimize)
|
||||
test "${ABI_ENCODER_V1}" = "1" && SOLTEST_ARGS+=(--abiencoderv1)
|
||||
|
||||
BATCH_ARGS=("--batches" "$((CPUs * CIRCLE_NODE_TOTAL))" "--selected-batch" "$((CPUs * CIRCLE_NODE_INDEX + run))")
|
||||
|
||||
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
|
||||
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)
|
||||
DEFAULT_EVM=london
|
||||
[[ " ${EVM_VALUES[*]} " =~ $DEFAULT_EVM ]]
|
||||
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.
|
||||
if circleci_step_selected "$RUN_STEPS" "$STEP"
|
||||
then
|
||||
EVM="${DEFAULT_EVM}" \
|
||||
OPTIMIZE=1 \
|
||||
ABI_ENCODER_V1=1 \
|
||||
BOOST_TEST_ARGS="-t !smtCheckerTests" \
|
||||
"${REPODIR}/.circleci/soltest.sh"
|
||||
fi
|
||||
((++STEP))
|
||||
EVM="${DEFAULT_EVM}" \
|
||||
OPTIMIZE=1 \
|
||||
ABI_ENCODER_V1=1 \
|
||||
BOOST_TEST_ARGS="-t !smtCheckerTests" \
|
||||
"${REPODIR}/.circleci/soltest.sh"
|
||||
|
||||
# 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[@]}"
|
||||
do
|
||||
for EVM in "${EVM_VALUES[@]}"
|
||||
@ -68,16 +59,13 @@ do
|
||||
DISABLE_SMTCHECKER=""
|
||||
[ "${OPTIMIZE}" != "0" ] && DISABLE_SMTCHECKER="-t !smtCheckerTests"
|
||||
|
||||
if circleci_step_selected "$RUN_STEPS" "$STEP"
|
||||
then
|
||||
EVM="$EVM" \
|
||||
OPTIMIZE="$OPTIMIZE" \
|
||||
SOLTEST_FLAGS="$SOLTEST_FLAGS $ENFORCE_GAS_ARGS $EWASM_ARGS" \
|
||||
BOOST_TEST_ARGS="-t !@nooptions $DISABLE_SMTCHECKER" \
|
||||
"${REPODIR}/.circleci/soltest.sh"
|
||||
fi
|
||||
((++STEP))
|
||||
done
|
||||
done
|
||||
EVM="$EVM" \
|
||||
OPTIMIZE="$OPTIMIZE" \
|
||||
SOLTEST_FLAGS="$SOLTEST_FLAGS $ENFORCE_GAS_ARGS $EWASM_ARGS" \
|
||||
BOOST_TEST_ARGS="-t !@nooptions $DISABLE_SMTCHECKER" \
|
||||
INDEX_SHIFT="$INDEX_SHIFT" \
|
||||
"${REPODIR}/.circleci/soltest.sh"
|
||||
|
||||
((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)
|
||||
configure_project(TESTS)
|
||||
set(LATEST_Z3_VERSION "4.8.13")
|
||||
set(LATEST_Z3_VERSION "4.8.14")
|
||||
set(MINIMUM_Z3_VERSION "4.8.0")
|
||||
find_package(Z3)
|
||||
if (${Z3_FOUND})
|
||||
|
16
Changelog.md
16
Changelog.md
@ -10,10 +10,24 @@ Breaking changes:
|
||||
|
||||
### 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:
|
||||
* Yul Optimizer: Remove ``mstore`` and ``sstore`` operations if the slot already contains the same value.
|
||||
|
||||
|
||||
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:
|
||||
@ -2161,7 +2175,7 @@ Features:
|
||||
* Internal: Inline assembly usable by the code generator.
|
||||
* Commandline interface: Using ``-`` as filename allows reading from stdin.
|
||||
* 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.
|
||||
|
||||
Bugfixes:
|
||||
|
@ -54,7 +54,8 @@
|
||||
- [ ] Run ``./scripts/docker_deploy_manual.sh v$VERSION``).
|
||||
|
||||
### 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).
|
||||
- [ ] 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.
|
||||
project = 'Solidity'
|
||||
copyright = '2016-2021, Ethereum'
|
||||
project_copyright = '2016-2021, Ethereum'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |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:
|
||||
version = re.search('PROJECT_VERSION "([^"]+)"', f.read()).group(1)
|
||||
# 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
|
||||
else:
|
||||
# This is a prerelease version
|
||||
|
@ -34,7 +34,8 @@ contracts (using qualified access like ``L.f()``).
|
||||
Of course, calls to internal functions
|
||||
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.
|
||||
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
|
||||
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.
|
||||
- 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
|
||||
====================
|
||||
|
@ -699,7 +699,7 @@ safest action is to revert all changes and make the whole transaction
|
||||
(or at least call) without effect.
|
||||
|
||||
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::
|
||||
|
||||
@ -895,6 +895,6 @@ in scope in the block that follows.
|
||||
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
|
||||
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
|
||||
has some gas left.
|
||||
|
@ -121,6 +121,9 @@ to receive their money - contracts cannot activate themselves.
|
||||
// before `send` returns.
|
||||
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)) {
|
||||
// No need to call throw here, just reset the amount owing
|
||||
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.
|
||||
In this tutorial, we will sign messages in the browser
|
||||
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.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
@ -564,7 +564,7 @@ yulFunctionDefinition:
|
||||
* While only identifiers without dots can be declared within inline assembly,
|
||||
* 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 variable declaration.
|
||||
|
@ -118,8 +118,17 @@ The nightly version can be installed using these commands:
|
||||
sudo apt-get update
|
||||
sudo apt-get install solc
|
||||
|
||||
We are also releasing a `snap package <https://snapcraft.io/>`_, which is
|
||||
installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To
|
||||
Furthermore, some Linux distributions provide their own packages. These packages are not directly
|
||||
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:
|
||||
|
||||
.. 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.
|
||||
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
|
||||
==============
|
||||
@ -537,8 +534,8 @@ The Solidity version string contains four parts:
|
||||
|
||||
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
|
||||
and the Solidity commit and platform combined make up the Semver build metadata.
|
||||
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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
Example:
|
||||
|
@ -290,6 +290,7 @@ on the individual steps and their sequence below.
|
||||
- :ref:`conditional-unsimplifier`.
|
||||
- :ref:`control-flow-simplifier`.
|
||||
- :ref:`dead-code-eliminator`.
|
||||
- :ref:`equal-store-eliminator`.
|
||||
- :ref:`equivalent-function-combiner`.
|
||||
- :ref:`expression-joiner`.
|
||||
- :ref:`expression-simplifier`.
|
||||
@ -938,6 +939,22 @@ we require ForLoopInitRewriter to run before this step.
|
||||
|
||||
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:
|
||||
|
||||
UnusedPruner
|
||||
|
@ -13,7 +13,7 @@ and :ref:`constant variable<constants>` definitions.
|
||||
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
|
||||
with regards to copyright, the Solidity compiler encourages the use
|
||||
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) {
|
||||
require(_allowances[sender][msg.sender] >= amount, "ERC20: Allowance not high enough.");
|
||||
_allowances[sender][msg.sender] -= amount;
|
||||
_transfer(sender, recipient, amount);
|
||||
approve(sender, msg.sender, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
function approve(address owner, address spender, uint256 amount) public returns (bool) {
|
||||
require(owner != address(0), "ERC20: approve from the zero address");
|
||||
function approve(address spender, uint256 amount) public returns (bool) {
|
||||
require(spender != address(0), "ERC20: approve to the zero address");
|
||||
|
||||
_allowances[owner][spender] = amount;
|
||||
emit Approval(owner, spender, amount);
|
||||
_allowances[msg.sender][spender] = amount;
|
||||
emit Approval(msg.sender, spender, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
function _transfer(address sender, address recipient, uint256 amount) internal {
|
||||
require(sender != address(0), "ERC20: transfer from the zero address");
|
||||
require(recipient != address(0), "ERC20: transfer to the zero address");
|
||||
require(_balances[sender] >= amount, "ERC20: Not enough funds.");
|
||||
|
||||
_balances[sender] -= 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))``.
|
||||
|
||||
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
|
||||
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.
|
||||
|
@ -329,6 +329,10 @@ on ``call``.
|
||||
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.
|
||||
|
||||
* ``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::
|
||||
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
||||
current contract using ``address(this).balance``.
|
||||
|
@ -25,19 +25,21 @@ using namespace solidity::langutil;
|
||||
using namespace solidity::frontend;
|
||||
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_currentNode(_functionFlow.entry),
|
||||
m_returnNode(_functionFlow.exit),
|
||||
m_revertNode(_functionFlow.revert),
|
||||
m_transactionReturnNode(_functionFlow.transactionReturn)
|
||||
m_transactionReturnNode(_functionFlow.transactionReturn),
|
||||
m_contract(_contract)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
||||
CFG::NodeContainer& _nodeContainer,
|
||||
FunctionDefinition const& _function
|
||||
FunctionDefinition const& _function,
|
||||
ContractDefinition const* _contract
|
||||
)
|
||||
{
|
||||
auto functionFlow = make_unique<FunctionFlow>();
|
||||
@ -45,7 +47,7 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
||||
functionFlow->exit = _nodeContainer.newNode();
|
||||
functionFlow->revert = _nodeContainer.newNode();
|
||||
functionFlow->transactionReturn = _nodeContainer.newNode();
|
||||
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
|
||||
ControlFlowBuilder builder(_nodeContainer, *functionFlow, _contract);
|
||||
builder.appendControlFlow(_function);
|
||||
|
||||
return functionFlow;
|
||||
@ -297,7 +299,8 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
||||
_functionCall.expression().accept(*this);
|
||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||
|
||||
m_currentNode->functionCalls.emplace_back(&_functionCall);
|
||||
solAssert(!m_currentNode->functionCall);
|
||||
m_currentNode->functionCall = &_functionCall;
|
||||
|
||||
auto nextNode = newLabel();
|
||||
|
||||
@ -321,8 +324,20 @@ bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
|
||||
auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
|
||||
_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, "");
|
||||
|
||||
m_placeholderEntry = newLabel();
|
||||
@ -355,8 +370,8 @@ bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition)
|
||||
|
||||
}
|
||||
|
||||
for (auto const& modifier: _functionDefinition.modifiers())
|
||||
appendControlFlow(*modifier);
|
||||
for (auto const& modifierInvocation: _functionDefinition.modifiers())
|
||||
appendControlFlow(*modifierInvocation);
|
||||
|
||||
appendControlFlow(_functionDefinition.body());
|
||||
|
||||
|
@ -37,13 +37,15 @@ class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker
|
||||
public:
|
||||
static std::unique_ptr<FunctionFlow> createFunctionFlow(
|
||||
CFG::NodeContainer& _nodeContainer,
|
||||
FunctionDefinition const& _function
|
||||
FunctionDefinition const& _function,
|
||||
ContractDefinition const* _contract = nullptr
|
||||
);
|
||||
|
||||
private:
|
||||
explicit ControlFlowBuilder(
|
||||
CFG::NodeContainer& _nodeContainer,
|
||||
FunctionFlow const& _functionFlow
|
||||
FunctionFlow const& _functionFlow,
|
||||
ContractDefinition const* _contract = nullptr
|
||||
);
|
||||
|
||||
// Visits for constructing the control flow.
|
||||
@ -158,6 +160,8 @@ private:
|
||||
CFGNode* m_revertNode = nullptr;
|
||||
CFGNode* m_transactionReturnNode = nullptr;
|
||||
|
||||
ContractDefinition const* m_contract = nullptr;
|
||||
|
||||
/// The current jump destination of break Statements.
|
||||
CFGNode* m_breakJump = nullptr;
|
||||
/// The current jump destination of continue Statements.
|
||||
|
@ -44,7 +44,7 @@ bool CFG::visit(ContractDefinition const& _contract)
|
||||
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||
if (function->isImplemented())
|
||||
m_functionControlFlow[{&_contract, function}] =
|
||||
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function);
|
||||
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function, &_contract);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -98,8 +98,8 @@ struct CFGNode
|
||||
std::vector<CFGNode*> entries;
|
||||
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
|
||||
std::vector<CFGNode*> exits;
|
||||
/// Function calls done by this node
|
||||
std::vector<FunctionCall const*> functionCalls;
|
||||
/// Function call done by this node
|
||||
FunctionCall const* functionCall = nullptr;
|
||||
|
||||
/// Variable occurrences in the node.
|
||||
std::vector<VariableOccurrence> variableOccurrences;
|
||||
|
@ -81,27 +81,27 @@ void ControlFlowRevertPruner::findRevertStates()
|
||||
if (_node == functionFlow.exit)
|
||||
foundExit = true;
|
||||
|
||||
for (auto const* functionCall: _node->functionCalls)
|
||||
if (auto const* functionCall = _node->functionCall)
|
||||
{
|
||||
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.contract);
|
||||
|
||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
||||
continue;
|
||||
|
||||
CFG::FunctionContractTuple calledFunctionTuple{
|
||||
findScopeContract(*resolvedFunction, item.contract),
|
||||
resolvedFunction
|
||||
};
|
||||
switch (m_functions.at(calledFunctionTuple))
|
||||
if (resolvedFunction && resolvedFunction->isImplemented())
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
wakeUp[calledFunctionTuple].insert(item);
|
||||
foundUnknown = true;
|
||||
return;
|
||||
case RevertState::AllPathsRevert:
|
||||
return;
|
||||
case RevertState::HasNonRevertingPath:
|
||||
break;
|
||||
CFG::FunctionContractTuple calledFunctionTuple{
|
||||
findScopeContract(*resolvedFunction, item.contract),
|
||||
resolvedFunction
|
||||
};
|
||||
switch (m_functions.at(calledFunctionTuple))
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
wakeUp[calledFunctionTuple].insert(item);
|
||||
foundUnknown = true;
|
||||
return;
|
||||
case RevertState::AllPathsRevert:
|
||||
return;
|
||||
case RevertState::HasNonRevertingPath:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,31 +135,29 @@ void ControlFlowRevertPruner::modifyFunctionFlows()
|
||||
FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.first.function, item.first.contract);
|
||||
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
|
||||
[&](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);
|
||||
|
||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
||||
continue;
|
||||
if (resolvedFunction && resolvedFunction->isImplemented())
|
||||
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
[[fallthrough]];
|
||||
case RevertState::AllPathsRevert:
|
||||
// If the revert states of the functions do not
|
||||
// change anymore, we treat all "unknown" states as
|
||||
// "reverting", since they can only be caused by
|
||||
// recursion.
|
||||
for (CFGNode * node: _node->exits)
|
||||
ranges::remove(node->entries, _node);
|
||||
|
||||
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
[[fallthrough]];
|
||||
case RevertState::AllPathsRevert:
|
||||
// If the revert states of the functions do not
|
||||
// change anymore, we treat all "unknown" states as
|
||||
// "reverting", since they can only be caused by
|
||||
// recursion.
|
||||
for (CFGNode * node: _node->exits)
|
||||
ranges::remove(node->entries, _node);
|
||||
|
||||
_node->exits = {functionFlow.revert};
|
||||
functionFlow.revert->entries.push_back(_node);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_node->exits = {functionFlow.revert};
|
||||
functionFlow.revert->entries.push_back(_node);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (CFGNode* exit: _node->exits)
|
||||
|
@ -437,16 +437,14 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
|
||||
}
|
||||
|
||||
if (
|
||||
_variable.isConstant() &&
|
||||
!dynamic_cast<UserDefinedValueType const*>(type) &&
|
||||
type->containsNestedMapping()
|
||||
)
|
||||
m_errorReporter.fatalDeclarationError(
|
||||
3530_error,
|
||||
_variable.location(),
|
||||
"The type contains a (nested) mapping and therefore cannot be a constant."
|
||||
);
|
||||
if (_variable.isConstant() && !type->isValueType())
|
||||
{
|
||||
bool allowed = false;
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
||||
allowed = arrayType->isByteArray();
|
||||
if (!allowed)
|
||||
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented.");
|
||||
}
|
||||
|
||||
_variable.annotation().type = type;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <libsolidity/analysis/DocStringAnalyser.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@ -37,7 +38,7 @@ using namespace solidity::frontend;
|
||||
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.
|
||||
if (_baseFunctions.size() != 1)
|
||||
@ -45,12 +46,6 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
|
||||
|
||||
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());
|
||||
|
||||
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;
|
||||
|
||||
// Update the parameter name for @return tags
|
||||
if (_declaration && tag == "return")
|
||||
if (_functionType && tag == "return")
|
||||
{
|
||||
size_t docParaNameEndPos = content.content.find_first_of(" \t");
|
||||
string const docParameterName = content.content.substr(0, docParaNameEndPos);
|
||||
|
||||
if (
|
||||
hasReturnParameter(*_declaration, n) &&
|
||||
docParameterName != _declaration->returnParameters().at(n)->name()
|
||||
_functionType->returnParameterNames().size() > n &&
|
||||
docParameterName != _functionType->returnParameterNames().at(n)
|
||||
)
|
||||
{
|
||||
bool baseHasNoName =
|
||||
hasReturnParameter(baseFunction, n) &&
|
||||
baseFunction.returnParameterList() &&
|
||||
baseFunction.returnParameters().size() > n &&
|
||||
baseFunction.returnParameters().at(n)->name().empty();
|
||||
|
||||
string paramName = _declaration->returnParameters().at(n)->name();
|
||||
string paramName = _functionType->returnParameterNames().at(n);
|
||||
content.content =
|
||||
(paramName.empty() ? "" : std::move(paramName) + " ") + (
|
||||
string::npos == docParaNameEndPos || baseHasNoName ?
|
||||
@ -127,7 +123,7 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
|
||||
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
if (!_function.isConstructor())
|
||||
handleCallable(_function, _function, _function.annotation());
|
||||
handleCallable(_function, _function, _function.annotation(), TypeProvider::function(_function));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -136,10 +132,12 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
|
||||
if (!_variable.isStateVariable() && !_variable.isFileLevelVariable())
|
||||
return false;
|
||||
|
||||
auto const* getterType = TypeProvider::function(_variable);
|
||||
|
||||
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())
|
||||
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation());
|
||||
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation(), getterType);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -168,17 +166,18 @@ bool DocStringAnalyser::visit(ErrorDefinition const& _error)
|
||||
void DocStringAnalyser::handleCallable(
|
||||
CallableDeclaration const& _callable,
|
||||
StructurallyDocumented const& _node,
|
||||
StructurallyDocumentedAnnotation& _annotation
|
||||
StructurallyDocumentedAnnotation& _annotation,
|
||||
FunctionType const* _functionType
|
||||
)
|
||||
{
|
||||
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation))
|
||||
copyMissingTags({baseFunction}, _annotation, &_callable);
|
||||
copyMissingTags({baseFunction}, _annotation, _functionType);
|
||||
else if (
|
||||
_annotation.docTags.empty() &&
|
||||
_callable.annotation().baseFunctions.size() == 1 &&
|
||||
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
|
||||
)
|
||||
copyMissingTags(_callable.annotation().baseFunctions, _annotation, &_callable);
|
||||
copyMissingTags(_callable.annotation().baseFunctions, _annotation, _functionType);
|
||||
}
|
||||
|
||||
CallableDeclaration const* DocStringAnalyser::resolveInheritDoc(
|
||||
|
@ -54,7 +54,8 @@ private:
|
||||
void handleCallable(
|
||||
CallableDeclaration const& _callable,
|
||||
StructurallyDocumented const& _node,
|
||||
StructurallyDocumentedAnnotation& _annotation
|
||||
StructurallyDocumentedAnnotation& _annotation,
|
||||
FunctionType const* _functionType = nullptr
|
||||
);
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
|
@ -73,24 +73,24 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
|
||||
|
||||
return {
|
||||
magicVarDecl("abi", TypeProvider::magic(MagicType::Kind::ABI)),
|
||||
magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)),
|
||||
magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, 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, StateMutability::Pure)),
|
||||
magicVarDecl("block", TypeProvider::magic(MagicType::Kind::Block)),
|
||||
magicVarDecl("blockhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)),
|
||||
magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
|
||||
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
|
||||
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
||||
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, StateMutability::Pure)),
|
||||
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, StateMutability::View)),
|
||||
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, StateMutability::Pure)),
|
||||
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("require", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
|
||||
magicVarDecl("require", TypeProvider::function(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
|
||||
magicVarDecl("revert", TypeProvider::function(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
|
||||
magicVarDecl("revert", TypeProvider::function(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
|
||||
magicVarDecl("ripemd160", TypeProvider::function(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, 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, StateMutability::Pure)),
|
||||
magicVarDecl("revert", TypeProvider::function(strings(), strings(), FunctionType::Kind::Revert, 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, StateMutability::Pure)),
|
||||
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("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, 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, StateMutability::Pure)),
|
||||
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
|
||||
// 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{},
|
||||
FunctionType::Kind::MetaType,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ void ImmutableValidator::analyze()
|
||||
{
|
||||
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 (VariableDeclaration const* stateVar: contract->stateVariables())
|
||||
@ -62,7 +62,7 @@ void ImmutableValidator::analyze()
|
||||
visitCallableIfNew(*modDef);
|
||||
}
|
||||
|
||||
checkAllVariablesInitialized(m_currentContract.location());
|
||||
checkAllVariablesInitialized(m_mostDerivedContract.location());
|
||||
}
|
||||
|
||||
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))
|
||||
visitCallableIfNew(
|
||||
*_identifierPath.annotation().requiredLookup == VirtualLookup::Virtual ?
|
||||
callableDef->resolveVirtual(m_currentContract) :
|
||||
callableDef->resolveVirtual(m_mostDerivedContract) :
|
||||
*callableDef
|
||||
);
|
||||
|
||||
@ -147,7 +147,7 @@ void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath)
|
||||
void ImmutableValidator::endVisit(Identifier const& _identifier)
|
||||
{
|
||||
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))
|
||||
analyseVariableReference(*varDecl, _identifier);
|
||||
}
|
||||
@ -160,15 +160,18 @@ void ImmutableValidator::endVisit(Return const& _return)
|
||||
|
||||
bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration)
|
||||
{
|
||||
FunctionDefinition const* prevConstructor = m_currentConstructor;
|
||||
m_currentConstructor = nullptr;
|
||||
ScopedSaveAndRestore constructorGuard{m_currentConstructor, {}};
|
||||
ScopedSaveAndRestore constructorContractGuard{m_currentConstructorContract, {}};
|
||||
|
||||
if (FunctionDefinition const* funcDef = dynamic_cast<decltype(funcDef)>(&_callableDeclaration))
|
||||
{
|
||||
ASTNode::listAccept(funcDef->modifiers(), *this);
|
||||
|
||||
if (funcDef->isConstructor())
|
||||
{
|
||||
m_currentConstructorContract = funcDef->annotation().contract;
|
||||
m_currentConstructor = funcDef;
|
||||
}
|
||||
|
||||
if (funcDef->isImplemented())
|
||||
funcDef->body().accept(*this);
|
||||
@ -177,8 +180,6 @@ bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDec
|
||||
if (modDef->isImplemented())
|
||||
modDef->body().accept(*this);
|
||||
|
||||
m_currentConstructor = prevConstructor;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -253,7 +254,8 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
|
||||
|
||||
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())
|
||||
if (varDecl->immutable())
|
||||
if (!util::contains(m_initializedStateVariables, varDecl))
|
||||
@ -263,6 +265,11 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source
|
||||
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
|
||||
"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)
|
||||
|
@ -42,7 +42,7 @@ class ImmutableValidator: private ASTConstVisitor
|
||||
|
||||
public:
|
||||
ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition):
|
||||
m_currentContract(_contractDefinition),
|
||||
m_mostDerivedContract(_contractDefinition),
|
||||
m_errorReporter(_errorReporter)
|
||||
{ }
|
||||
|
||||
@ -66,7 +66,7 @@ private:
|
||||
|
||||
void visitCallableIfNew(Declaration const& _declaration);
|
||||
|
||||
ContractDefinition const& m_currentContract;
|
||||
ContractDefinition const& m_mostDerivedContract;
|
||||
|
||||
CallableDeclarationSet m_visitedCallables;
|
||||
|
||||
@ -74,6 +74,7 @@ private:
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
|
||||
FunctionDefinition const* m_currentConstructor = nullptr;
|
||||
ContractDefinition const* m_currentConstructorContract = nullptr;
|
||||
bool m_inLoop = false;
|
||||
bool m_inBranch = false;
|
||||
bool m_inCreationContext = true;
|
||||
|
@ -530,15 +530,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
}
|
||||
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())
|
||||
m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable.");
|
||||
else if (!*_variable.value()->annotation().isPure)
|
||||
@ -2122,16 +2113,35 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
||||
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{};
|
||||
|
||||
if (functionPointerType->hasDeclaration())
|
||||
{
|
||||
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.\"?";
|
||||
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);
|
||||
@ -2866,7 +2876,6 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
||||
strings(1, ""),
|
||||
strings(1, ""),
|
||||
FunctionType::Kind::ObjectCreation,
|
||||
false,
|
||||
StateMutability::Pure
|
||||
);
|
||||
_newExpression.annotation().isPure = true;
|
||||
|
@ -511,23 +511,20 @@ ModifierDefinition const& ModifierDefinition::resolveVirtual(
|
||||
ContractDefinition const* _searchStart
|
||||
) const
|
||||
{
|
||||
// Super is not possible 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.
|
||||
if (_searchStart == nullptr && !virtualSemantics())
|
||||
// The modifier is not virtual, we can stop here.
|
||||
if (!virtualSemantics())
|
||||
return *this;
|
||||
|
||||
solAssert(!dynamic_cast<ContractDefinition const&>(*scope()).isLibrary(), "");
|
||||
|
||||
for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts)
|
||||
{
|
||||
if (_searchStart != nullptr && c != _searchStart)
|
||||
continue;
|
||||
_searchStart = nullptr;
|
||||
for (ModifierDefinition const* modifier: c->functionModifiers())
|
||||
if (modifier->name() == name())
|
||||
return *modifier;
|
||||
}
|
||||
|
||||
solAssert(false, "Virtual modifier " + name() + " not found.");
|
||||
return *this; // not reached
|
||||
}
|
||||
|
@ -445,13 +445,18 @@ FunctionType const* TypeProvider::function(
|
||||
strings const& _parameterTypes,
|
||||
strings const& _returnParameterTypes,
|
||||
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>(
|
||||
_parameterTypes, _returnParameterTypes,
|
||||
_kind, _arbitraryParameters, _stateMutability
|
||||
_parameterTypes,
|
||||
_returnParameterTypes,
|
||||
_kind,
|
||||
_stateMutability,
|
||||
std::move(_options)
|
||||
);
|
||||
}
|
||||
|
||||
@ -461,13 +466,9 @@ FunctionType const* TypeProvider::function(
|
||||
strings _parameterNames,
|
||||
strings _returnParameterNames,
|
||||
FunctionType::Kind _kind,
|
||||
bool _arbitraryParameters,
|
||||
StateMutability _stateMutability,
|
||||
Declaration const* _declaration,
|
||||
bool _gasSet,
|
||||
bool _valueSet,
|
||||
bool _bound,
|
||||
bool _saltSet
|
||||
FunctionType::Options _options
|
||||
)
|
||||
{
|
||||
return createAndGet<FunctionType>(
|
||||
@ -476,13 +477,9 @@ FunctionType const* TypeProvider::function(
|
||||
_parameterNames,
|
||||
_returnParameterNames,
|
||||
_kind,
|
||||
_arbitraryParameters,
|
||||
_stateMutability,
|
||||
_declaration,
|
||||
_gasSet,
|
||||
_valueSet,
|
||||
_bound,
|
||||
_saltSet
|
||||
std::move(_options)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -149,8 +149,8 @@ public:
|
||||
strings const& _parameterTypes,
|
||||
strings const& _returnParameterTypes,
|
||||
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.
|
||||
@ -160,13 +160,9 @@ public:
|
||||
strings _parameterNames = strings{},
|
||||
strings _returnParameterNames = strings{},
|
||||
FunctionType::Kind _kind = FunctionType::Kind::Internal,
|
||||
bool _arbitraryParameters = false,
|
||||
StateMutability _stateMutability = StateMutability::NonPayable,
|
||||
Declaration const* _declaration = nullptr,
|
||||
bool _gasSet = false,
|
||||
bool _valueSet = false,
|
||||
bool _bound = false,
|
||||
bool _saltSet = false
|
||||
FunctionType::Options _options = {}
|
||||
);
|
||||
|
||||
/// 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()},
|
||||
{"code", TypeProvider::array(DataLocation::Memory)},
|
||||
{"codehash", TypeProvider::fixedBytes(32)},
|
||||
{"call", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
|
||||
{"callcode", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
|
||||
{"delegatecall", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false, StateMutability::NonPayable)},
|
||||
{"staticcall", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)}
|
||||
{"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, StateMutability::Payable)},
|
||||
{"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, StateMutability::View)}
|
||||
};
|
||||
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{"transfer", TypeProvider::function(strings{"uint"}, strings(), FunctionType::Kind::Transfer, 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, StateMutability::NonPayable)});
|
||||
}
|
||||
return members;
|
||||
}
|
||||
@ -2852,7 +2852,6 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
|
||||
parameterNames,
|
||||
strings{""},
|
||||
Kind::Creation,
|
||||
false,
|
||||
stateMutability
|
||||
);
|
||||
}
|
||||
@ -2946,11 +2945,11 @@ string FunctionType::richIdentifier() const
|
||||
}
|
||||
id += "_" + stateMutabilityToString(m_stateMutability);
|
||||
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
|
||||
if (m_gasSet)
|
||||
if (gasSet())
|
||||
id += "gas";
|
||||
if (m_valueSet)
|
||||
if (valueSet())
|
||||
id += "value";
|
||||
if (m_saltSet)
|
||||
if (saltSet())
|
||||
id += "salt";
|
||||
if (bound())
|
||||
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))
|
||||
return nullptr;
|
||||
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);
|
||||
else if (
|
||||
kind() == Kind::External &&
|
||||
sizeOnStack() == 2 &&
|
||||
!bound() &&
|
||||
other.kind() == Kind::External &&
|
||||
other.sizeOnStack() == 2 &&
|
||||
!other.bound()
|
||||
)
|
||||
return commonType(this, _other);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -3098,11 +3107,11 @@ bool FunctionType::nameable() const
|
||||
{
|
||||
return
|
||||
(m_kind == Kind::Internal || m_kind == Kind::External) &&
|
||||
!m_bound &&
|
||||
!m_arbitraryParameters &&
|
||||
!m_gasSet &&
|
||||
!m_valueSet &&
|
||||
!m_saltSet;
|
||||
!bound() &&
|
||||
!takesArbitraryParameters() &&
|
||||
!gasSet() &&
|
||||
!valueSet() &&
|
||||
!saltSet();
|
||||
}
|
||||
|
||||
vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
||||
@ -3144,11 +3153,11 @@ vector<tuple<string, Type const*>> FunctionType::makeStackItems() const
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_gasSet)
|
||||
if (gasSet())
|
||||
slots.emplace_back("gas", TypeProvider::uint256());
|
||||
if (m_valueSet)
|
||||
if (valueSet())
|
||||
slots.emplace_back("value", TypeProvider::uint256());
|
||||
if (m_saltSet)
|
||||
if (saltSet())
|
||||
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
|
||||
if (bound())
|
||||
slots.emplace_back("self", m_parameterTypes.front());
|
||||
@ -3180,13 +3189,13 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
|
||||
if (variable && retParamTypes.get().empty())
|
||||
return FunctionTypePointer();
|
||||
|
||||
solAssert(!takesArbitraryParameters());
|
||||
return TypeProvider::function(
|
||||
paramTypes,
|
||||
retParamTypes,
|
||||
m_parameterNames,
|
||||
m_returnParameterNames,
|
||||
m_kind,
|
||||
m_arbitraryParameters,
|
||||
m_stateMutability,
|
||||
m_declaration
|
||||
);
|
||||
@ -3241,12 +3250,9 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
|
||||
strings(1, ""),
|
||||
strings(1, ""),
|
||||
Kind::SetValue,
|
||||
false,
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
m_gasSet,
|
||||
m_valueSet,
|
||||
m_saltSet
|
||||
Options::fromFunctionType(*this)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -3259,12 +3265,9 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
|
||||
strings(1, ""),
|
||||
strings(1, ""),
|
||||
Kind::SetGas,
|
||||
false,
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
m_gasSet,
|
||||
m_valueSet,
|
||||
m_saltSet
|
||||
Options::fromFunctionType(*this)
|
||||
)
|
||||
);
|
||||
return members;
|
||||
@ -3292,7 +3295,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
|
||||
|
||||
Type const* FunctionType::encodingType() const
|
||||
{
|
||||
if (m_gasSet || m_valueSet)
|
||||
if (gasSet() || valueSet())
|
||||
return nullptr;
|
||||
// Only external functions can be encoded, internal functions cannot leave code boundaries.
|
||||
if (m_kind == Kind::External)
|
||||
@ -3311,7 +3314,7 @@ TypeResult FunctionType::interfaceType(bool /*_inLibrary*/) const
|
||||
|
||||
Type const* FunctionType::mobileType() const
|
||||
{
|
||||
if (m_valueSet || m_gasSet || m_saltSet || m_bound)
|
||||
if (valueSet() || gasSet() || saltSet() || bound())
|
||||
return nullptr;
|
||||
|
||||
// return function without parameter names
|
||||
@ -3321,13 +3324,9 @@ Type const* FunctionType::mobileType() const
|
||||
strings(m_parameterTypes.size()),
|
||||
strings(m_returnParameterNames.size()),
|
||||
m_kind,
|
||||
m_arbitraryParameters,
|
||||
m_stateMutability,
|
||||
m_declaration,
|
||||
m_gasSet,
|
||||
m_valueSet,
|
||||
m_bound,
|
||||
m_saltSet
|
||||
Options::fromFunctionType(*this)
|
||||
);
|
||||
}
|
||||
|
||||
@ -3413,7 +3412,7 @@ bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) con
|
||||
return false;
|
||||
|
||||
//@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;
|
||||
|
||||
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
|
||||
{
|
||||
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(
|
||||
m_parameterTypes,
|
||||
m_returnParameterTypes,
|
||||
m_parameterNames,
|
||||
m_returnParameterNames,
|
||||
m_kind,
|
||||
m_arbitraryParameters,
|
||||
m_stateMutability,
|
||||
m_declaration,
|
||||
m_gasSet || _setGas,
|
||||
m_valueSet || _setValue,
|
||||
m_saltSet || _setSalt,
|
||||
m_bound
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
FunctionTypePointer FunctionType::asBoundFunction() const
|
||||
{
|
||||
solAssert(!m_parameterTypes.empty(), "");
|
||||
solAssert(!m_gasSet, "");
|
||||
solAssert(!m_valueSet, "");
|
||||
solAssert(!m_saltSet, "");
|
||||
solAssert(!gasSet(), "");
|
||||
solAssert(!valueSet(), "");
|
||||
solAssert(!saltSet(), "");
|
||||
Options options = Options::fromFunctionType(*this);
|
||||
options.bound = true;
|
||||
return TypeProvider::function(
|
||||
m_parameterTypes,
|
||||
m_returnParameterTypes,
|
||||
m_parameterNames,
|
||||
m_returnParameterNames,
|
||||
m_kind,
|
||||
m_arbitraryParameters,
|
||||
m_stateMutability,
|
||||
m_declaration,
|
||||
m_gasSet,
|
||||
m_valueSet,
|
||||
m_saltSet,
|
||||
true
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
@ -3596,13 +3593,9 @@ FunctionTypePointer FunctionType::asExternallyCallableFunction(bool _inLibrary)
|
||||
m_parameterNames,
|
||||
m_returnParameterNames,
|
||||
kind,
|
||||
m_arbitraryParameters,
|
||||
m_stateMutability,
|
||||
m_declaration,
|
||||
m_gasSet,
|
||||
m_valueSet,
|
||||
m_saltSet,
|
||||
m_bound
|
||||
Options::fromFunctionType(*this)
|
||||
);
|
||||
}
|
||||
|
||||
@ -3807,7 +3800,6 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
||||
strings{string{}},
|
||||
strings{string{}},
|
||||
FunctionType::Kind::Wrap,
|
||||
false, /*_arbitraryParameters */
|
||||
StateMutability::Pure
|
||||
)
|
||||
);
|
||||
@ -3819,7 +3811,6 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
||||
strings{string{}},
|
||||
strings{string{}},
|
||||
FunctionType::Kind::Unwrap,
|
||||
false, /* _arbitraryParameters */
|
||||
StateMutability::Pure
|
||||
)
|
||||
);
|
||||
@ -3834,8 +3825,9 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
||||
strings{},
|
||||
strings{string()},
|
||||
FunctionType::Kind::BytesConcat,
|
||||
/* _arbitraryParameters */ true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
));
|
||||
|
||||
return members;
|
||||
@ -3958,7 +3950,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
return MemberList::MemberMap({
|
||||
{"coinbase", TypeProvider::payableAddress()},
|
||||
{"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()},
|
||||
{"number", TypeProvider::uint256()},
|
||||
{"gaslimit", TypeProvider::uint256()},
|
||||
@ -3986,8 +3978,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
strings{},
|
||||
strings{1, ""},
|
||||
FunctionType::Kind::ABIEncode,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
)},
|
||||
{"encodePacked", TypeProvider::function(
|
||||
TypePointers{},
|
||||
@ -3995,8 +3988,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
strings{},
|
||||
strings{1, ""},
|
||||
FunctionType::Kind::ABIEncodePacked,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
)},
|
||||
{"encodeWithSelector", TypeProvider::function(
|
||||
TypePointers{TypeProvider::fixedBytes(4)},
|
||||
@ -4004,8 +3998,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
strings{1, ""},
|
||||
strings{1, ""},
|
||||
FunctionType::Kind::ABIEncodeWithSelector,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
)},
|
||||
{"encodeCall", TypeProvider::function(
|
||||
TypePointers{},
|
||||
@ -4013,8 +4008,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
strings{},
|
||||
strings{1, ""},
|
||||
FunctionType::Kind::ABIEncodeCall,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
)},
|
||||
{"encodeWithSignature", TypeProvider::function(
|
||||
TypePointers{TypeProvider::array(DataLocation::Memory, true)},
|
||||
@ -4022,8 +4018,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
strings{1, ""},
|
||||
strings{1, ""},
|
||||
FunctionType::Kind::ABIEncodeWithSignature,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
)},
|
||||
{"decode", TypeProvider::function(
|
||||
TypePointers(),
|
||||
@ -4031,8 +4028,9 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
strings{},
|
||||
strings{},
|
||||
FunctionType::Kind::ABIDecode,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
)}
|
||||
});
|
||||
case Kind::MetaType:
|
||||
|
@ -1110,11 +1110,7 @@ public:
|
||||
u256 storageSize() const override { return underlyingType().storageSize(); }
|
||||
unsigned storageBytes() const override { return underlyingType().storageBytes(); }
|
||||
|
||||
bool isValueType() const override
|
||||
{
|
||||
solAssert(underlyingType().isValueType(), "");
|
||||
return true;
|
||||
}
|
||||
bool isValueType() const override { return true; }
|
||||
bool nameable() const override
|
||||
{
|
||||
solAssert(underlyingType().nameable(), "");
|
||||
@ -1247,6 +1243,38 @@ public:
|
||||
/// Cannot be called.
|
||||
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.
|
||||
/// @arg _kind must be Kind::Internal, Kind::External or Kind::Declaration.
|
||||
@ -1263,18 +1291,21 @@ public:
|
||||
strings const& _parameterTypes,
|
||||
strings const& _returnParameterTypes,
|
||||
Kind _kind,
|
||||
bool _arbitraryParameters = false,
|
||||
StateMutability _stateMutability = StateMutability::NonPayable
|
||||
StateMutability _stateMutability = StateMutability::NonPayable,
|
||||
Options _options = Options{false, false, false, false, false}
|
||||
): FunctionType(
|
||||
parseElementaryTypeVector(_parameterTypes),
|
||||
parseElementaryTypeVector(_returnParameterTypes),
|
||||
strings(_parameterTypes.size(), ""),
|
||||
strings(_returnParameterTypes.size(), ""),
|
||||
_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.
|
||||
@ -1284,13 +1315,9 @@ public:
|
||||
strings _parameterNames = strings(),
|
||||
strings _returnParameterNames = strings(),
|
||||
Kind _kind = Kind::Internal,
|
||||
bool _arbitraryParameters = false,
|
||||
StateMutability _stateMutability = StateMutability::NonPayable,
|
||||
Declaration const* _declaration = nullptr,
|
||||
bool _gasSet = false,
|
||||
bool _valueSet = false,
|
||||
bool _saltSet = false,
|
||||
bool _bound = false
|
||||
Options _options = Options{false, false, false, false, false}
|
||||
):
|
||||
m_parameterTypes(std::move(_parameterTypes)),
|
||||
m_returnParameterTypes(std::move(_returnParameterTypes)),
|
||||
@ -1298,12 +1325,8 @@ public:
|
||||
m_returnParameterNames(std::move(_returnParameterNames)),
|
||||
m_kind(_kind),
|
||||
m_stateMutability(_stateMutability),
|
||||
m_arbitraryParameters(_arbitraryParameters),
|
||||
m_gasSet(_gasSet),
|
||||
m_valueSet(_valueSet),
|
||||
m_bound(_bound),
|
||||
m_declaration(_declaration),
|
||||
m_saltSet(_saltSet)
|
||||
m_options(std::move(_options))
|
||||
{
|
||||
solAssert(
|
||||
m_parameterNames.size() == m_parameterTypes.size(),
|
||||
@ -1314,7 +1337,7 @@ public:
|
||||
"Return parameter names list must match return parameter types list!"
|
||||
);
|
||||
solAssert(
|
||||
!m_bound || !m_parameterTypes.empty(),
|
||||
!bound() || !m_parameterTypes.empty(),
|
||||
"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
|
||||
/// and abi.encodePacked.
|
||||
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.
|
||||
bool takesSinglePackedBytesParameter() const
|
||||
{
|
||||
@ -1427,10 +1450,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool gasSet() const { return m_gasSet; }
|
||||
bool valueSet() const { return m_valueSet; }
|
||||
bool saltSet() const { return m_saltSet; }
|
||||
bool bound() const { return m_bound; }
|
||||
bool gasSet() const { return m_options.gasSet; }
|
||||
bool valueSet() const { return m_options.valueSet; }
|
||||
bool saltSet() const { return m_options.saltSet; }
|
||||
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
|
||||
/// of the parameters to false.
|
||||
@ -1458,15 +1481,8 @@ private:
|
||||
std::vector<std::string> m_returnParameterNames;
|
||||
Kind const m_kind;
|
||||
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;
|
||||
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()
|
||||
{
|
||||
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);
|
||||
storeFreeMemoryPointer();
|
||||
}
|
||||
@ -92,7 +92,7 @@ void CompilerUtils::toSizeAfterFreeMemoryPointer()
|
||||
|
||||
void CompilerUtils::revertWithStringData(Type const& _argumentType)
|
||||
{
|
||||
solAssert(_argumentType.isImplicitlyConvertibleTo(*TypeProvider::fromElementaryTypeName("string memory")), "");
|
||||
solAssert(_argumentType.isImplicitlyConvertibleTo(*TypeProvider::fromElementaryTypeName("string memory")));
|
||||
fetchFreeMemoryPointer();
|
||||
m_context << util::selectorFromSignature("Error(string)");
|
||||
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||
@ -173,9 +173,9 @@ void CompilerUtils::loadFromMemoryDynamic(
|
||||
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
||||
{
|
||||
solAssert(!arrayType->isDynamicallySized(), "");
|
||||
solAssert(!_fromCalldata, "");
|
||||
solAssert(_padToWordBoundaries, "");
|
||||
solAssert(!arrayType->isDynamicallySized());
|
||||
solAssert(!_fromCalldata);
|
||||
solAssert(_padToWordBoundaries);
|
||||
if (_keepUpdatedMemoryOffset)
|
||||
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
|
||||
auto stackHeightBefore = m_context.stackHeight();
|
||||
abiDecodeV2(_typeParameters, _fromMemory);
|
||||
solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, "");
|
||||
solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2);
|
||||
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
|
||||
// we would have to convert it like below.
|
||||
solAssert(arrayType.location() == DataLocation::Memory, "");
|
||||
solAssert(arrayType.location() == DataLocation::Memory);
|
||||
if (arrayType.isDynamicallySized())
|
||||
{
|
||||
// compute data pointer
|
||||
@ -430,7 +430,7 @@ void CompilerUtils::encodeToMemory(
|
||||
// stack: <v1> <v2> ... <vn> <mem>
|
||||
bool const encoderV2 = m_context.useABICoderV2();
|
||||
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
|
||||
solAssert(targetTypes.size() == _givenTypes.size(), "");
|
||||
solAssert(targetTypes.size() == _givenTypes.size());
|
||||
for (Type const*& t: targetTypes)
|
||||
{
|
||||
Type const* tEncoding = t->fullEncodingType(_encodeAsLibraryTypes, encoderV2, !_padToWordBoundaries);
|
||||
@ -449,7 +449,7 @@ void CompilerUtils::encodeToMemory(
|
||||
);
|
||||
auto stackHeightBefore = m_context.stackHeight();
|
||||
abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes, _padToWordBoundaries);
|
||||
solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), "");
|
||||
solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -489,8 +489,8 @@ void CompilerUtils::encodeToMemory(
|
||||
{
|
||||
// special case: convert storage reference type to value type - this is only
|
||||
// possible for library calls where we just forward the storage reference
|
||||
solAssert(_encodeAsLibraryTypes, "");
|
||||
solAssert(_givenTypes[i]->sizeOnStack() == 1, "");
|
||||
solAssert(_encodeAsLibraryTypes);
|
||||
solAssert(_givenTypes[i]->sizeOnStack() == 1);
|
||||
}
|
||||
else if (
|
||||
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
|
||||
@ -638,7 +638,7 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
|
||||
{
|
||||
if (_type.baseType()->hasSimpleZeroValueInMemory())
|
||||
{
|
||||
solAssert(_type.baseType()->isValueType(), "");
|
||||
solAssert(_type.baseType()->isValueType());
|
||||
Whiskers templ(R"({
|
||||
let size := mul(length, <element_size>)
|
||||
// cheap way of zero-initializing a memory range
|
||||
@ -774,9 +774,9 @@ void CompilerUtils::convertType(
|
||||
|
||||
if (stackTypeCategory == Type::Category::UserDefinedValueType)
|
||||
{
|
||||
solAssert(_cleanupNeeded, "");
|
||||
solAssert(_cleanupNeeded);
|
||||
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_typeOnStack);
|
||||
solAssert(_typeOnStack == _targetType || _targetType == userDefined.underlyingType(), "");
|
||||
solAssert(_typeOnStack == _targetType || _targetType == userDefined.underlyingType());
|
||||
return convertType(
|
||||
userDefined.underlyingType(),
|
||||
_targetType,
|
||||
@ -787,9 +787,9 @@ void CompilerUtils::convertType(
|
||||
}
|
||||
if (targetTypeCategory == Type::Category::UserDefinedValueType)
|
||||
{
|
||||
solAssert(_cleanupNeeded, "");
|
||||
solAssert(_cleanupNeeded);
|
||||
auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_targetType);
|
||||
solAssert(_typeOnStack.isImplicitlyConvertibleTo(userDefined.underlyingType()), "");
|
||||
solAssert(_typeOnStack.isImplicitlyConvertibleTo(userDefined.underlyingType()));
|
||||
return convertType(
|
||||
_typeOnStack,
|
||||
userDefined.underlyingType(),
|
||||
@ -829,7 +829,7 @@ void CompilerUtils::convertType(
|
||||
}
|
||||
else if (targetTypeCategory == Type::Category::Address)
|
||||
{
|
||||
solAssert(typeOnStack.numBytes() * 8 == 160, "");
|
||||
solAssert(typeOnStack.numBytes() * 8 == 160);
|
||||
rightShiftNumberOnStack(256 - 160);
|
||||
}
|
||||
else
|
||||
@ -849,7 +849,7 @@ void CompilerUtils::convertType(
|
||||
break;
|
||||
}
|
||||
case Type::Category::Enum:
|
||||
solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer, "");
|
||||
solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer);
|
||||
if (enumOverflowCheckPending)
|
||||
{
|
||||
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack);
|
||||
@ -885,13 +885,13 @@ void CompilerUtils::convertType(
|
||||
cleanHigherOrderBits(*typeOnStack);
|
||||
}
|
||||
else if (stackTypeCategory == Type::Category::Address)
|
||||
solAssert(targetBytesType.numBytes() * 8 == 160, "");
|
||||
solAssert(targetBytesType.numBytes() * 8 == 160);
|
||||
leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8);
|
||||
}
|
||||
else if (targetTypeCategory == Type::Category::Enum)
|
||||
{
|
||||
solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested.");
|
||||
solAssert(_typeOnStack.mobileType(), "");
|
||||
solAssert(_typeOnStack.mobileType());
|
||||
// just clean
|
||||
convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
|
||||
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType);
|
||||
@ -964,13 +964,13 @@ void CompilerUtils::convertType(
|
||||
if (targetTypeCategory == Type::Category::FixedBytes)
|
||||
{
|
||||
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))));
|
||||
}
|
||||
else if (targetTypeCategory == Type::Category::Array)
|
||||
{
|
||||
auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
|
||||
solAssert(arrayType.isByteArray(), "");
|
||||
solAssert(arrayType.isByteArray());
|
||||
size_t storageSize = 32 + ((data.size() + 31) / 32) * 32;
|
||||
allocateMemory(storageSize);
|
||||
// stack: mempos
|
||||
@ -995,10 +995,10 @@ void CompilerUtils::convertType(
|
||||
typeOnStack.isByteArray() && !typeOnStack.isString(),
|
||||
"Array types other than bytes not convertible to bytesNN."
|
||||
);
|
||||
solAssert(typeOnStack.isDynamicallySized(), "");
|
||||
solAssert(typeOnStack.isDynamicallySized());
|
||||
|
||||
bool fromCalldata = typeOnStack.dataStoredIn(DataLocation::CallData);
|
||||
solAssert(typeOnStack.sizeOnStack() == (fromCalldata ? 2 : 1), "");
|
||||
solAssert(typeOnStack.sizeOnStack() == (fromCalldata ? 2 : 1));
|
||||
if (fromCalldata)
|
||||
m_context << Instruction::SWAP1;
|
||||
|
||||
@ -1012,7 +1012,7 @@ void CompilerUtils::convertType(
|
||||
);
|
||||
break;
|
||||
}
|
||||
solAssert(targetTypeCategory == stackTypeCategory, "");
|
||||
solAssert(targetTypeCategory == stackTypeCategory);
|
||||
auto const& targetType = dynamic_cast<ArrayType const&>(_targetType);
|
||||
switch (targetType.location())
|
||||
{
|
||||
@ -1034,9 +1034,9 @@ void CompilerUtils::convertType(
|
||||
typeOnStack.baseType()->isDynamicallyEncoded()
|
||||
)
|
||||
{
|
||||
solAssert(m_context.useABICoderV2(), "");
|
||||
solAssert(m_context.useABICoderV2());
|
||||
// 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())
|
||||
m_context << Instruction::SWAP1;
|
||||
|
||||
@ -1122,9 +1122,9 @@ void CompilerUtils::convertType(
|
||||
typeOnStack.arrayType().isByteArray() && !typeOnStack.arrayType().isString(),
|
||||
"Array types other than bytes not convertible to bytesNN."
|
||||
);
|
||||
solAssert(typeOnStack.isDynamicallySized(), "");
|
||||
solAssert(typeOnStack.dataStoredIn(DataLocation::CallData), "");
|
||||
solAssert(typeOnStack.sizeOnStack() == 2, "");
|
||||
solAssert(typeOnStack.isDynamicallySized());
|
||||
solAssert(typeOnStack.dataStoredIn(DataLocation::CallData));
|
||||
solAssert(typeOnStack.sizeOnStack() == 2);
|
||||
|
||||
m_context << Instruction::SWAP1;
|
||||
m_context.callYulFunction(
|
||||
@ -1138,14 +1138,16 @@ void CompilerUtils::convertType(
|
||||
break;
|
||||
}
|
||||
|
||||
solAssert(_targetType.category() == Type::Category::Array, "");
|
||||
solAssert(_targetType.category() == Type::Category::Array);
|
||||
auto const& targetArrayType = dynamic_cast<ArrayType const&>(_targetType);
|
||||
solAssert(typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType), "");
|
||||
solAssert(
|
||||
typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType) ||
|
||||
(typeOnStack.arrayType().isByteArray() && targetArrayType.isByteArray())
|
||||
);
|
||||
solAssert(
|
||||
typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) &&
|
||||
typeOnStack.arrayType().isDynamicallySized() &&
|
||||
!typeOnStack.arrayType().baseType()->isDynamicallyEncoded(),
|
||||
""
|
||||
!typeOnStack.arrayType().baseType()->isDynamicallyEncoded()
|
||||
);
|
||||
if (!_targetType.dataStoredIn(DataLocation::CallData))
|
||||
return convertType(typeOnStack.arrayType(), _targetType);
|
||||
@ -1153,7 +1155,7 @@ void CompilerUtils::convertType(
|
||||
}
|
||||
case Type::Category::Struct:
|
||||
{
|
||||
solAssert(targetTypeCategory == stackTypeCategory, "");
|
||||
solAssert(targetTypeCategory == stackTypeCategory);
|
||||
auto& targetType = dynamic_cast<StructType const&>(_targetType);
|
||||
auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
|
||||
switch (targetType.location())
|
||||
@ -1182,7 +1184,7 @@ void CompilerUtils::convertType(
|
||||
// stack: <memory ptr> <source ref> <memory ptr>
|
||||
for (auto const& member: typeOnStack->members(nullptr))
|
||||
{
|
||||
solAssert(!member.type->containsNestedMapping(), "");
|
||||
solAssert(!member.type->containsNestedMapping());
|
||||
pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name);
|
||||
_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||
_context << u256(offsets.second);
|
||||
@ -1209,7 +1211,7 @@ void CompilerUtils::convertType(
|
||||
{
|
||||
if (typeOnStack.isDynamicallyEncoded())
|
||||
{
|
||||
solAssert(m_context.useABICoderV2(), "");
|
||||
solAssert(m_context.useABICoderV2());
|
||||
m_context.callYulFunction(
|
||||
m_context.utilFunctions().conversionFunction(typeOnStack, targetType),
|
||||
1,
|
||||
@ -1231,7 +1233,7 @@ void CompilerUtils::convertType(
|
||||
}
|
||||
break;
|
||||
case DataLocation::CallData:
|
||||
solAssert(_typeOnStack == _targetType, "");
|
||||
solAssert(_typeOnStack == _targetType);
|
||||
// nothing to do
|
||||
break;
|
||||
}
|
||||
@ -1241,7 +1243,7 @@ void CompilerUtils::convertType(
|
||||
{
|
||||
TupleType const& sourceTuple = dynamic_cast<TupleType const&>(_typeOnStack);
|
||||
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();
|
||||
for (size_t i = 0; i < sourceTuple.components().size(); ++i)
|
||||
{
|
||||
@ -1249,7 +1251,7 @@ void CompilerUtils::convertType(
|
||||
Type const* targetType = targetTuple.components()[i];
|
||||
if (!sourceType)
|
||||
{
|
||||
solAssert(!targetType, "");
|
||||
solAssert(!targetType);
|
||||
continue;
|
||||
}
|
||||
unsigned sourceSize = sourceType->sizeOnStack();
|
||||
@ -1291,7 +1293,7 @@ void CompilerUtils::convertType(
|
||||
break;
|
||||
default:
|
||||
// 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)
|
||||
{
|
||||
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
|
||||
@ -1348,14 +1350,14 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
||||
}
|
||||
if (referenceType->location() == DataLocation::CallData)
|
||||
{
|
||||
solAssert(referenceType->sizeOnStack() == 1 || referenceType->sizeOnStack() == 2, "");
|
||||
solAssert(referenceType->sizeOnStack() == 1 || referenceType->sizeOnStack() == 2);
|
||||
m_context << Instruction::CALLDATASIZE;
|
||||
if (referenceType->sizeOnStack() == 2)
|
||||
m_context << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
solAssert(referenceType->location() == DataLocation::Memory, "");
|
||||
solAssert(referenceType->location() == DataLocation::Memory);
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
||||
if (arrayType->isDynamicallySized())
|
||||
{
|
||||
@ -1383,7 +1385,7 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
||||
}
|
||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
||||
{
|
||||
solAssert(!arrayType->isDynamicallySized(), "");
|
||||
solAssert(!arrayType->isDynamicallySized());
|
||||
if (arrayType->length() > 0)
|
||||
{
|
||||
_context << arrayType->length() << Instruction::SWAP1;
|
||||
@ -1483,7 +1485,7 @@ void CompilerUtils::popStackSlots(size_t _amount)
|
||||
|
||||
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;
|
||||
popStackSlots(amount);
|
||||
m_context.appendJumpTo(_jumpTo);
|
||||
@ -1551,7 +1553,7 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
|
||||
|
||||
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords)
|
||||
{
|
||||
solAssert(_type.isValueType(), "");
|
||||
solAssert(_type.isValueType());
|
||||
Type const* type = &_type;
|
||||
if (auto const* userDefined = dynamic_cast<UserDefinedValueType const*>(type))
|
||||
type = &userDefined->underlyingType();
|
||||
@ -1603,7 +1605,7 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
|
||||
|
||||
void CompilerUtils::leftShiftNumberOnStack(unsigned _bits)
|
||||
{
|
||||
solAssert(_bits < 256, "");
|
||||
solAssert(_bits < 256);
|
||||
if (m_context.evmVersion().hasBitwiseShifting())
|
||||
m_context << _bits << Instruction::SHL;
|
||||
else
|
||||
@ -1612,7 +1614,7 @@ void CompilerUtils::leftShiftNumberOnStack(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
|
||||
if (m_context.evmVersion().hasBitwiseShifting())
|
||||
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) + ")."
|
||||
);
|
||||
|
||||
solAssert(!_type.isDynamicallyEncoded(), "");
|
||||
solAssert(!_type.isDynamicallyEncoded());
|
||||
|
||||
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
|
||||
|
||||
|
@ -780,6 +780,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
break;
|
||||
case FunctionType::Kind::Send:
|
||||
case FunctionType::Kind::Transfer:
|
||||
{
|
||||
_functionCall.expression().accept(*this);
|
||||
// Provide the gas stipend manually at first because we may send 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
|
||||
m_context << Instruction::SWAP1 << Instruction::DUP2;
|
||||
m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1;
|
||||
FunctionType::Options callOptions;
|
||||
callOptions.valueSet = true;
|
||||
callOptions.gasSet = true;
|
||||
appendExternalFunctionCall(
|
||||
FunctionType(
|
||||
TypePointers{},
|
||||
@ -795,11 +799,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
strings(),
|
||||
strings(),
|
||||
FunctionType::Kind::BareCall,
|
||||
false,
|
||||
StateMutability::NonPayable,
|
||||
nullptr,
|
||||
true,
|
||||
true
|
||||
callOptions
|
||||
),
|
||||
{},
|
||||
false
|
||||
@ -812,6 +814,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
m_context.appendConditionalRevert(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Selfdestruct:
|
||||
acceptAndConvert(*arguments.front(), *function.parameterTypes().front(), true);
|
||||
m_context << Instruction::SELFDESTRUCT;
|
||||
@ -1255,7 +1258,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
|
||||
auto const functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type);
|
||||
solAssert(functionPtr);
|
||||
solAssert(functionPtr->sizeOnStack() == 2);
|
||||
|
||||
// Account for tuples with one component which become that component
|
||||
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)
|
||||
{
|
||||
// stack: <memory pointer> <functionPointer>
|
||||
// Extract selector from the stack
|
||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||
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>
|
||||
// Extract selector from the stack
|
||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||
}
|
||||
// Conversion will be done below
|
||||
dataOnStack = TypeProvider::uint(32);
|
||||
}
|
||||
@ -1747,6 +1760,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
case Type::Category::Function:
|
||||
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;
|
||||
/// need to store it as bytes4
|
||||
utils().leftShiftNumberOnStack(224);
|
||||
@ -1755,8 +1771,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type);
|
||||
solAssert(functionType.kind() == FunctionType::Kind::External, "");
|
||||
// stack: <address> <function_id>
|
||||
m_context << Instruction::POP;
|
||||
CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 1);
|
||||
}
|
||||
else
|
||||
solAssert(
|
||||
@ -2265,12 +2280,29 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO
|
||||
|
||||
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 (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
|
||||
// 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 << ((u256(1) << 32) - 1) << Instruction::AND;
|
||||
}
|
||||
m_context << Instruction::EQ;
|
||||
}
|
||||
m_context << Instruction::EQ;
|
||||
if (_operator == Token::NotEqual)
|
||||
m_context << Instruction::ISZERO;
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types.");
|
||||
bool isSigned = false;
|
||||
if (auto type = dynamic_cast<IntegerType const*>(&_type))
|
||||
isSigned = type->isSigned();
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
#include <libsolutil/Whiskers.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
using namespace std;
|
||||
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.");
|
||||
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);
|
||||
|
||||
solAssert(fromType.arrayType().isImplicitlyConvertibleTo(targetType), "");
|
||||
solAssert(
|
||||
fromType.arrayType().isImplicitlyConvertibleTo(targetType) ||
|
||||
(fromType.arrayType().isByteArray() && targetType.isByteArray())
|
||||
);
|
||||
solAssert(
|
||||
fromType.arrayType().dataStoredIn(DataLocation::CallData) &&
|
||||
fromType.arrayType().isDynamicallySized() &&
|
||||
!fromType.arrayType().baseType()->isDynamicallyEncoded(),
|
||||
""
|
||||
!fromType.arrayType().baseType()->isDynamicallyEncoded()
|
||||
);
|
||||
|
||||
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);
|
||||
t("memberOffset", formatNumber(srcSlotOffset));
|
||||
if (memberType.isValueType())
|
||||
t("read", readFromStorageValueType(memberType, srcOffset, false));
|
||||
t("read", readFromStorageValueType(memberType, srcOffset, true));
|
||||
else
|
||||
solAssert(srcOffset == 0, "");
|
||||
|
||||
@ -4548,3 +4551,31 @@ string YulUtilFunctions::externalCodeFunction()
|
||||
.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
|
||||
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:
|
||||
/// @returns the name of a function that copies a struct from calldata or memory to storage
|
||||
/// signature: (slot, value) ->
|
||||
|
@ -95,7 +95,7 @@ struct CopyTranslate: public yul::ASTCopier
|
||||
return ASTCopier::translate(_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));
|
||||
}
|
||||
|
||||
@ -115,14 +115,14 @@ private:
|
||||
if (suffix.empty() && varDecl->isLocalVariable())
|
||||
{
|
||||
auto const& var = m_context.localVariable(*varDecl);
|
||||
solAssert(var.type().sizeOnStack() == 1, "");
|
||||
solAssert(var.type().sizeOnStack() == 1);
|
||||
|
||||
value = var.commaSeparatedList();
|
||||
}
|
||||
else if (varDecl->isConstant())
|
||||
{
|
||||
VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl);
|
||||
solAssert(variable, "");
|
||||
solAssert(variable);
|
||||
|
||||
if (variable->value()->annotation().type->category() == Type::Category::RationalNumber)
|
||||
{
|
||||
@ -130,7 +130,7 @@ private:
|
||||
if (auto const* bytesType = dynamic_cast<FixedBytesType const*>(variable->type()))
|
||||
intValue <<= 256 - 8 * bytesType->numBytes();
|
||||
else
|
||||
solAssert(variable->type()->category() == Type::Category::Integer, "");
|
||||
solAssert(variable->type()->category() == Type::Category::Integer);
|
||||
value = intValue.str();
|
||||
}
|
||||
else if (auto const* literal = dynamic_cast<Literal const*>(variable->value().get()))
|
||||
@ -141,20 +141,20 @@ private:
|
||||
{
|
||||
case Type::Category::Bool:
|
||||
case Type::Category::Address:
|
||||
solAssert(type->category() == variable->annotation().type->category(), "");
|
||||
solAssert(type->category() == variable->annotation().type->category());
|
||||
value = toCompactHexWithPrefix(type->literalValue(literal));
|
||||
break;
|
||||
case Type::Category::StringLiteral:
|
||||
{
|
||||
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();
|
||||
solAssert(stringLiteral.value().size() <= numBytes, "");
|
||||
solAssert(stringLiteral.value().size() <= numBytes);
|
||||
value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft)));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solAssert(false, "");
|
||||
solAssert(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -167,25 +167,25 @@ private:
|
||||
else if (suffix == "offset")
|
||||
value = to_string(m_context.storageLocationOfStateVariable(*varDecl).second);
|
||||
else
|
||||
solAssert(false, "");
|
||||
solAssert(false);
|
||||
}
|
||||
else if (varDecl->type()->dataStoredIn(DataLocation::Storage))
|
||||
{
|
||||
solAssert(suffix == "slot" || suffix == "offset", "");
|
||||
solAssert(varDecl->isLocalVariable(), "");
|
||||
solAssert(suffix == "slot" || suffix == "offset");
|
||||
solAssert(varDecl->isLocalVariable());
|
||||
if (suffix == "slot")
|
||||
value = IRVariable{*varDecl}.part("slot").name();
|
||||
else if (varDecl->type()->isValueType())
|
||||
value = IRVariable{*varDecl}.part("offset").name();
|
||||
else
|
||||
{
|
||||
solAssert(!IRVariable{*varDecl}.hasPart("offset"), "");
|
||||
solAssert(!IRVariable{*varDecl}.hasPart("offset"));
|
||||
value = "0";
|
||||
}
|
||||
}
|
||||
else if (varDecl->type()->dataStoredIn(DataLocation::CallData))
|
||||
{
|
||||
solAssert(suffix == "offset" || suffix == "length", "");
|
||||
solAssert(suffix == "offset" || suffix == "length");
|
||||
value = IRVariable{*varDecl}.part(suffix).name();
|
||||
}
|
||||
else if (
|
||||
@ -193,15 +193,15 @@ private:
|
||||
functionType && functionType->kind() == FunctionType::Kind::External
|
||||
)
|
||||
{
|
||||
solAssert(suffix == "selector" || suffix == "address", "");
|
||||
solAssert(varDecl->type()->sizeOnStack() == 2, "");
|
||||
solAssert(suffix == "selector" || suffix == "address");
|
||||
solAssert(varDecl->type()->sizeOnStack() == 2);
|
||||
if (suffix == "selector")
|
||||
value = IRVariable{*varDecl}.part("functionSelector").name();
|
||||
else
|
||||
value = IRVariable{*varDecl}.part("address").name();
|
||||
}
|
||||
else
|
||||
solAssert(false, "");
|
||||
solAssert(false);
|
||||
|
||||
if (isdigit(value.front()))
|
||||
return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}};
|
||||
@ -268,7 +268,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va
|
||||
setLocation(_varDecl);
|
||||
|
||||
solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable.");
|
||||
solAssert(!_varDecl.isConstant(), "");
|
||||
solAssert(!_varDecl.isConstant());
|
||||
if (!_varDecl.value())
|
||||
return;
|
||||
|
||||
@ -355,7 +355,7 @@ string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const
|
||||
templ("sourceLocationComment", dispenseLocationComment(_constant, m_context));
|
||||
templ("functionName", functionName);
|
||||
IRGeneratorForStatements generator(m_context, m_utils);
|
||||
solAssert(_constant.value(), "");
|
||||
solAssert(_constant.value());
|
||||
Type const& constantType = *_constant.type();
|
||||
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
|
||||
templ("code", generator.code());
|
||||
@ -386,7 +386,7 @@ void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _var
|
||||
for (size_t i = 0; i < _varDeclStatement.declarations().size(); ++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));
|
||||
}
|
||||
}
|
||||
@ -443,7 +443,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||
TokenTraits::AssignmentToBinaryOp(assignmentOperator);
|
||||
|
||||
if (TokenTraits::isShiftOp(binaryOperator))
|
||||
solAssert(type(_assignment.rightHandSide()).mobileType(), "");
|
||||
solAssert(type(_assignment.rightHandSide()).mobileType());
|
||||
IRVariable value =
|
||||
type(_assignment.leftHandSide()).isValueType() ?
|
||||
convert(
|
||||
@ -460,11 +460,11 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||
if (assignmentOperator != Token::Assign)
|
||||
{
|
||||
solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
|
||||
solAssert(binaryOperator != Token::Exp, "");
|
||||
solAssert(type(_assignment) == type(_assignment.leftHandSide()), "");
|
||||
solAssert(binaryOperator != Token::Exp);
|
||||
solAssert(type(_assignment) == type(_assignment.leftHandSide()));
|
||||
|
||||
IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
|
||||
solAssert(type(_assignment) == leftIntermediate.type(), "");
|
||||
solAssert(type(_assignment) == leftIntermediate.type());
|
||||
|
||||
define(_assignment) << (
|
||||
TokenTraits::isShiftOp(binaryOperator) ?
|
||||
@ -523,14 +523,14 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
||||
{
|
||||
bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo;
|
||||
if (willBeWrittenTo)
|
||||
solAssert(!m_currentLValue, "");
|
||||
solAssert(!m_currentLValue);
|
||||
if (_tuple.components().size() == 1)
|
||||
{
|
||||
solAssert(_tuple.components().front(), "");
|
||||
solAssert(_tuple.components().front());
|
||||
_tuple.components().front()->accept(*this);
|
||||
setLocation(_tuple);
|
||||
if (willBeWrittenTo)
|
||||
solAssert(!!m_currentLValue, "");
|
||||
solAssert(!!m_currentLValue);
|
||||
else
|
||||
define(_tuple, *_tuple.components().front());
|
||||
}
|
||||
@ -544,7 +544,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
||||
setLocation(_tuple);
|
||||
if (willBeWrittenTo)
|
||||
{
|
||||
solAssert(!!m_currentLValue, "");
|
||||
solAssert(!!m_currentLValue);
|
||||
lvalues.emplace_back(std::move(m_currentLValue));
|
||||
m_currentLValue.reset();
|
||||
}
|
||||
@ -568,7 +568,7 @@ bool IRGeneratorForStatements::visit(Block const& _block)
|
||||
{
|
||||
if (_block.unchecked())
|
||||
{
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Checked, "");
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Checked);
|
||||
m_context.setArithmetic(Arithmetic::Wrapping);
|
||||
}
|
||||
return true;
|
||||
@ -578,7 +578,7 @@ void IRGeneratorForStatements::endVisit(Block const& _block)
|
||||
{
|
||||
if (_block.unchecked())
|
||||
{
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Wrapping, "");
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Wrapping);
|
||||
m_context.setArithmetic(Arithmetic::Checked);
|
||||
}
|
||||
}
|
||||
@ -607,7 +607,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
|
||||
|
||||
void IRGeneratorForStatements::endVisit(PlaceholderStatement const& _placeholder)
|
||||
{
|
||||
solAssert(m_placeholderCallback, "");
|
||||
solAssert(m_placeholderCallback);
|
||||
setLocation(_placeholder);
|
||||
appendCode() << m_placeholderCallback();
|
||||
}
|
||||
@ -776,7 +776,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
{
|
||||
setLocation(_binOp);
|
||||
|
||||
solAssert(!!_binOp.annotation().commonType, "");
|
||||
solAssert(!!_binOp.annotation().commonType);
|
||||
Type const* commonType = _binOp.annotation().commonType;
|
||||
langutil::Token op = _binOp.getOperator();
|
||||
|
||||
@ -799,13 +799,8 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
|
||||
if (TokenTraits::isCompareOp(op))
|
||||
{
|
||||
if (auto type = dynamic_cast<FunctionType const*>(commonType))
|
||||
{
|
||||
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());
|
||||
|
||||
solAssert(commonType->isValueType(), "");
|
||||
bool isSigned = false;
|
||||
if (auto type = dynamic_cast<IntegerType const*>(commonType))
|
||||
isSigned = type->isSigned();
|
||||
@ -813,8 +808,25 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
string args = expressionAsType(_binOp.leftExpression(), *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;
|
||||
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) + ")";
|
||||
else if (op == Token::NotEqual)
|
||||
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))
|
||||
{
|
||||
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(
|
||||
*rationalNumberType,
|
||||
@ -939,7 +951,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
{
|
||||
FunctionDefinition const* functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract());
|
||||
|
||||
solAssert(!functionType->takesArbitraryParameters(), "");
|
||||
solAssert(!functionType->takesArbitraryParameters());
|
||||
|
||||
vector<string> args;
|
||||
if (functionType->bound())
|
||||
@ -950,7 +962,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
|
||||
if (functionDef)
|
||||
{
|
||||
solAssert(functionDef->isImplemented(), "");
|
||||
solAssert(functionDef->isImplemented());
|
||||
|
||||
define(_functionCall) <<
|
||||
m_context.enqueueFunctionForCodeGeneration(*functionDef) <<
|
||||
@ -1053,7 +1065,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::Error:
|
||||
{
|
||||
ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(_functionCall.expression()));
|
||||
solAssert(error, "");
|
||||
solAssert(error);
|
||||
revertWithError(
|
||||
error->functionType(true)->externalSignature(),
|
||||
error->functionType(true)->parameterTypes(),
|
||||
@ -1064,7 +1076,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::Wrap:
|
||||
case FunctionType::Kind::Unwrap:
|
||||
{
|
||||
solAssert(arguments.size() == 1, "");
|
||||
solAssert(arguments.size() == 1);
|
||||
FunctionType::Kind kind = functionType->kind();
|
||||
if (kind == FunctionType::Kind::Wrap)
|
||||
solAssert(
|
||||
@ -1074,7 +1086,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
""
|
||||
);
|
||||
else
|
||||
solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType, "");
|
||||
solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType);
|
||||
|
||||
define(_functionCall, *arguments.at(0));
|
||||
break;
|
||||
@ -1108,7 +1120,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||
{
|
||||
bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||
solAssert(functionType->padArguments() != isPacked, "");
|
||||
solAssert(functionType->padArguments() != isPacked);
|
||||
bool const hasSelectorOrSignature =
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeCall ||
|
||||
@ -1122,7 +1134,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
solAssert(arguments.size() == 2, "");
|
||||
solAssert(arguments.size() == 2);
|
||||
// Account for tuples with one component which become that component
|
||||
if (type(*arguments[1]).category() == Type::Category::Tuple)
|
||||
{
|
||||
@ -1150,10 +1162,21 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
selector = convert(
|
||||
IRVariable(*arguments[0]).part("functionSelector"),
|
||||
*TypeProvider::fixedBytes(4)
|
||||
).name();
|
||||
{
|
||||
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(
|
||||
IRVariable(*arguments[0]).part("functionSelector"),
|
||||
*TypeProvider::fixedBytes(4)
|
||||
).name();
|
||||
}
|
||||
}
|
||||
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||
{
|
||||
// hash the signature
|
||||
@ -1234,7 +1257,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
referenceType && referenceType->dataStoredIn(DataLocation::CallData)
|
||||
)
|
||||
{
|
||||
solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()), "");
|
||||
solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()));
|
||||
IRVariable var = convert(*arguments[0], *TypeProvider::bytesCalldata());
|
||||
templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, false));
|
||||
templ("offset", var.part("offset").name());
|
||||
@ -1256,8 +1279,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
case FunctionType::Kind::Revert:
|
||||
{
|
||||
solAssert(arguments.size() == parameterTypes.size(), "");
|
||||
solAssert(arguments.size() <= 1, "");
|
||||
solAssert(arguments.size() == parameterTypes.size());
|
||||
solAssert(arguments.size() <= 1);
|
||||
solAssert(
|
||||
arguments.empty() ||
|
||||
arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),
|
||||
@ -1276,7 +1299,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::ObjectCreation:
|
||||
{
|
||||
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());
|
||||
define(_functionCall) <<
|
||||
@ -1288,7 +1311,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
case FunctionType::Kind::KECCAK256:
|
||||
{
|
||||
solAssert(arguments.size() == 1, "");
|
||||
solAssert(arguments.size() == 1);
|
||||
|
||||
ArrayType const* arrayType = TypeProvider::bytesMemory();
|
||||
|
||||
@ -1316,10 +1339,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
case FunctionType::Kind::ArrayPop:
|
||||
{
|
||||
solAssert(functionType->bound(), "");
|
||||
solAssert(functionType->parameterTypes().empty(), "");
|
||||
solAssert(functionType->bound());
|
||||
solAssert(functionType->parameterTypes().empty());
|
||||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
||||
solAssert(arrayType, "");
|
||||
solAssert(arrayType);
|
||||
define(_functionCall) <<
|
||||
m_utils.storageArrayPopFunction(*arrayType) <<
|
||||
"(" <<
|
||||
@ -1330,7 +1353,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::ArrayPush:
|
||||
{
|
||||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType());
|
||||
solAssert(arrayType, "");
|
||||
solAssert(arrayType);
|
||||
|
||||
if (arguments.empty())
|
||||
{
|
||||
@ -1391,8 +1414,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
{FunctionType::Kind::AddMod, "addmod"},
|
||||
{FunctionType::Kind::MulMod, "mulmod"},
|
||||
};
|
||||
solAssert(functions.find(functionType->kind()) != functions.end(), "");
|
||||
solAssert(arguments.size() == 3 && parameterTypes.size() == 3, "");
|
||||
solAssert(functions.find(functionType->kind()) != functions.end());
|
||||
solAssert(arguments.size() == 3 && parameterTypes.size() == 3);
|
||||
|
||||
IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2]));
|
||||
define(modulus, *arguments[2]);
|
||||
@ -1417,7 +1440,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
{FunctionType::Kind::Selfdestruct, "selfdestruct"},
|
||||
{FunctionType::Kind::BlockHash, "blockhash"},
|
||||
};
|
||||
solAssert(functions.find(functionType->kind()) != functions.end(), "");
|
||||
solAssert(functions.find(functionType->kind()) != functions.end());
|
||||
|
||||
string args;
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
@ -1474,7 +1497,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
t("saltSet", functionType->saltSet());
|
||||
if (functionType->saltSet())
|
||||
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("isTryCall", _functionCall.annotation().tryCall);
|
||||
if (_functionCall.annotation().tryCall)
|
||||
@ -1488,7 +1511,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::Send:
|
||||
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 value{expressionAsType(*arguments[0], *(parameterTypes[0]))};
|
||||
Whiskers templ(R"(
|
||||
@ -1517,10 +1540,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::RIPEMD160:
|
||||
case FunctionType::Kind::SHA256:
|
||||
{
|
||||
solAssert(!_functionCall.annotation().tryCall, "");
|
||||
solAssert(!functionType->valueSet(), "");
|
||||
solAssert(!functionType->gasSet(), "");
|
||||
solAssert(!functionType->bound(), "");
|
||||
solAssert(!_functionCall.annotation().tryCall);
|
||||
solAssert(!functionType->valueSet());
|
||||
solAssert(!functionType->gasSet());
|
||||
solAssert(!functionType->bound());
|
||||
|
||||
static map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = {
|
||||
{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)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
@ -1613,7 +1636,7 @@ bool IRGeneratorForStatements::visit(MemberAccess const& _memberAccess)
|
||||
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
|
||||
innerExpression->expression().accept(*this);
|
||||
|
||||
@ -1634,7 +1657,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
if (memberFunctionType && memberFunctionType->bound())
|
||||
{
|
||||
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)
|
||||
assignInternalFunctionIDIfNotCalledDirectly(
|
||||
_memberAccess,
|
||||
@ -1650,9 +1673,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
else
|
||||
{
|
||||
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());
|
||||
solAssert(contract && contract->isLibrary(), "");
|
||||
solAssert(contract && contract->isLibrary());
|
||||
define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\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);
|
||||
if (type.isSuper())
|
||||
solAssert(false, "");
|
||||
solAssert(false);
|
||||
|
||||
// ordinary contract type
|
||||
else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
|
||||
@ -1713,7 +1736,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
")\n";
|
||||
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());
|
||||
}
|
||||
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
|
||||
)
|
||||
{
|
||||
solAssert(functionType.hasDeclaration(), "");
|
||||
solAssert(functionType.hasDeclaration());
|
||||
solAssert(
|
||||
functionType.kind() == FunctionType::Kind::Error ||
|
||||
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();
|
||||
auto const& contractType = dynamic_cast<ContractType const&>(*arg);
|
||||
solAssert(!contractType.isSuper(), "");
|
||||
solAssert(!contractType.isSuper());
|
||||
ContractDefinition const& contract = contractType.contractDefinition();
|
||||
m_context.subObjectsCreated().insert(&contract);
|
||||
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();
|
||||
auto const& contractType = dynamic_cast<ContractType const&>(*arg);
|
||||
solAssert(!contractType.isSuper(), "");
|
||||
solAssert(!contractType.isSuper());
|
||||
ContractDefinition const& contract = contractType.contractDefinition();
|
||||
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")
|
||||
{
|
||||
solAssert(type.location() == DataLocation::Storage, "");
|
||||
solAssert(type.location() == DataLocation::Storage);
|
||||
define(IRVariable{_memberAccess}.part("slot"), IRVariable{_memberAccess.expression()}.part("slot"));
|
||||
}
|
||||
else
|
||||
@ -1996,8 +2019,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
*_memberAccess.annotation().referencedDeclaration
|
||||
).resolveVirtual(m_context.mostDerivedContract(), super);
|
||||
|
||||
solAssert(resolvedFunctionDef.functionType(true), "");
|
||||
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
||||
solAssert(resolvedFunctionDef.functionType(true));
|
||||
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal);
|
||||
assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, resolvedFunctionDef);
|
||||
}
|
||||
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
|
||||
// without any specific code being generated,
|
||||
// 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))
|
||||
define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n";
|
||||
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))
|
||||
solAssert(arrayType->isByteArray() && member == "concat", "");
|
||||
solAssert(arrayType->isByteArray() && member == "concat");
|
||||
else
|
||||
// The old code generator had a generic "else" case here
|
||||
// without any specific code being generated,
|
||||
// but it would still be better to have an exhaustive list.
|
||||
solAssert(false, "");
|
||||
solAssert(false);
|
||||
break;
|
||||
}
|
||||
case Type::Category::Module:
|
||||
@ -2083,17 +2106,17 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
);
|
||||
if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||
{
|
||||
solAssert(variable->isConstant(), "");
|
||||
solAssert(variable->isConstant());
|
||||
handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess));
|
||||
}
|
||||
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
||||
{
|
||||
auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
||||
solAssert(function && function->isFree(), "");
|
||||
solAssert(function->functionType(true), "");
|
||||
solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
||||
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
|
||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
||||
solAssert(function && function->isFree());
|
||||
solAssert(function->functionType(true));
|
||||
solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal);
|
||||
solAssert(funType->kind() == FunctionType::Kind::Internal);
|
||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static);
|
||||
|
||||
assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function);
|
||||
}
|
||||
@ -2117,7 +2140,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
||||
|
||||
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.
|
||||
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();
|
||||
|
||||
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.");
|
||||
|
||||
@ -2253,8 +2276,8 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
}
|
||||
else if (baseType.category() == Type::Category::TypeType)
|
||||
{
|
||||
solAssert(baseType.sizeOnStack() == 0, "");
|
||||
solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, "");
|
||||
solAssert(baseType.sizeOnStack() == 0);
|
||||
solAssert(_indexAccess.annotation().type->sizeOnStack() == 0);
|
||||
// no-op - this seems to be a lone array type (`structType[];`)
|
||||
}
|
||||
else
|
||||
@ -2279,7 +2302,7 @@ void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAcces
|
||||
{
|
||||
case DataLocation::CallData:
|
||||
{
|
||||
solAssert(baseType.isDynamicallySized(), "");
|
||||
solAssert(baseType.isDynamicallySized());
|
||||
IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()};
|
||||
if (_indexRangeAccess.startExpression())
|
||||
define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()});
|
||||
@ -2317,18 +2340,18 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
switch (magicVar->type()->category())
|
||||
{
|
||||
case Type::Category::Contract:
|
||||
solAssert(_identifier.name() == "this", "");
|
||||
solAssert(_identifier.name() == "this");
|
||||
define(_identifier) << "address()\n";
|
||||
break;
|
||||
case Type::Category::Integer:
|
||||
solAssert(_identifier.name() == "now", "");
|
||||
solAssert(_identifier.name() == "now");
|
||||
define(_identifier) << "timestamp()\n";
|
||||
break;
|
||||
case Type::Category::TypeType:
|
||||
{
|
||||
auto typeType = dynamic_cast<TypeType const*>(magicVar->type());
|
||||
if (auto contractType = dynamic_cast<ContractType const*>(typeType->actualType()))
|
||||
solAssert(!contractType->isSuper() || _identifier.name() == "super", "");
|
||||
solAssert(!contractType->isSuper() || _identifier.name() == "super");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -2338,11 +2361,11 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
}
|
||||
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());
|
||||
|
||||
solAssert(resolvedFunctionDef.functionType(true), "");
|
||||
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
||||
solAssert(resolvedFunctionDef.functionType(true));
|
||||
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal);
|
||||
assignInternalFunctionIDIfNotCalledDirectly(_identifier, resolvedFunctionDef);
|
||||
}
|
||||
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()));
|
||||
solAssert(!funType.takesArbitraryParameters(), "");
|
||||
solAssert(_arguments.size() == funType.parameterTypes().size(), "");
|
||||
solAssert(!funType.isBareCall(), "");
|
||||
solAssert(!funType.takesArbitraryParameters());
|
||||
solAssert(_arguments.size() == funType.parameterTypes().size());
|
||||
solAssert(!funType.isBareCall());
|
||||
FunctionType::Kind const funKind = funType.kind();
|
||||
|
||||
solAssert(
|
||||
@ -2543,7 +2566,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
|
||||
string const retVars = IRVariable(_functionCall).commaSeparatedList();
|
||||
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("dynamicReturnSize", returnInfo.dynamicReturnSize);
|
||||
@ -2552,7 +2575,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
|
||||
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
||||
|
||||
solAssert(funType.padArguments(), "");
|
||||
solAssert(funType.padArguments());
|
||||
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall));
|
||||
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
|
||||
|
||||
@ -2605,7 +2628,7 @@ void IRGeneratorForStatements::appendBareCall(
|
||||
);
|
||||
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::BareCall ||
|
||||
@ -2613,7 +2636,7 @@ void IRGeneratorForStatements::appendBareCall(
|
||||
funKind == FunctionType::Kind::BareStaticCall, ""
|
||||
);
|
||||
|
||||
solAssert(!_functionCall.annotation().tryCall, "");
|
||||
solAssert(!_functionCall.annotation().tryCall);
|
||||
Whiskers templ(R"(
|
||||
<?needsEncoding>
|
||||
let <pos> := <allocateUnbounded>()
|
||||
@ -2825,7 +2848,7 @@ string IRGeneratorForStatements::binaryOperation(
|
||||
"Not yet implemented - FixedPointType."
|
||||
);
|
||||
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
|
||||
solAssert(type, "");
|
||||
solAssert(type);
|
||||
bool checked = m_context.arithmetic() == Arithmetic::Checked;
|
||||
switch (_operator)
|
||||
{
|
||||
@ -2865,9 +2888,9 @@ std::string IRGeneratorForStatements::shiftOperation(
|
||||
"Not yet implemented - FixedPointType."
|
||||
);
|
||||
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
|
||||
Whiskers(R"(
|
||||
@ -2886,7 +2909,7 @@ std::string IRGeneratorForStatements::shiftOperation(
|
||||
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
|
||||
{
|
||||
langutil::Token const op = _binOp.getOperator();
|
||||
solAssert(op == Token::Or || op == Token::And, "");
|
||||
solAssert(op == Token::Or || op == Token::And);
|
||||
|
||||
_binOp.leftExpression().accept(*this);
|
||||
setLocation(_binOp);
|
||||
@ -2933,7 +2956,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
||||
|
||||
if (_memory.byteArrayElement)
|
||||
{
|
||||
solAssert(_lvalue.type == *TypeProvider::byte(), "");
|
||||
solAssert(_lvalue.type == *TypeProvider::byte());
|
||||
appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n";
|
||||
}
|
||||
else
|
||||
@ -2957,9 +2980,9 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(_lvalue.type.sizeOnStack() == 1, "");
|
||||
solAssert(_lvalue.type.sizeOnStack() == 1);
|
||||
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";
|
||||
}
|
||||
},
|
||||
@ -2968,7 +2991,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
||||
{
|
||||
solUnimplementedAssert(_lvalue.type.isValueType());
|
||||
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);
|
||||
|
||||
IRVariable prepared(m_context.newYulVariable(), _lvalue.type);
|
||||
@ -3028,7 +3051,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
||||
[&](IRLValue::Immutable const& _immutable) {
|
||||
solUnimplementedAssert(_lvalue.type.isValueType());
|
||||
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1);
|
||||
solAssert(_lvalue.type == *_immutable.variable->type(), "");
|
||||
solAssert(_lvalue.type == *_immutable.variable->type());
|
||||
if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation)
|
||||
{
|
||||
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)
|
||||
{
|
||||
solAssert(!m_currentLValue, "");
|
||||
solAssert(!m_currentLValue);
|
||||
|
||||
if (_expression.annotation().willBeWrittenTo)
|
||||
{
|
||||
m_currentLValue.emplace(std::move(_lvalue));
|
||||
if (_lvalue.type.dataStoredIn(DataLocation::CallData))
|
||||
solAssert(holds_alternative<IRLValue::Stack>(_lvalue.kind), "");
|
||||
solAssert(holds_alternative<IRLValue::Stack>(_lvalue.kind));
|
||||
}
|
||||
else
|
||||
// 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;
|
||||
for (ASTPointer<VariableDeclaration> const& varDecl: successClause.parameters()->parameters())
|
||||
{
|
||||
solAssert(varDecl, "");
|
||||
solAssert(varDecl);
|
||||
define(m_context.addLocalVariable(*varDecl),
|
||||
successClause.parameters()->parameters().size() == 1 ?
|
||||
IRVariable(externalCall) :
|
||||
@ -3171,7 +3194,7 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
|
||||
appendCode() << runFallback << " := 0\n";
|
||||
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());
|
||||
define(var) << dataVariable << "\n";
|
||||
}
|
||||
@ -3192,7 +3215,7 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
|
||||
appendCode() << runFallback << " := 0\n";
|
||||
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());
|
||||
define(var) << code << "\n";
|
||||
}
|
||||
@ -3218,7 +3241,7 @@ void IRGeneratorForStatements::handleCatchFallback(TryCatchClause const& _fallba
|
||||
setLocation(_fallback);
|
||||
if (_fallback.parameters())
|
||||
{
|
||||
solAssert(m_context.evmVersion().supportsReturndata(), "");
|
||||
solAssert(m_context.evmVersion().supportsReturndata());
|
||||
solAssert(
|
||||
_fallback.parameters()->parameters().size() == 1 &&
|
||||
_fallback.parameters()->parameters().front() &&
|
||||
@ -3254,7 +3277,7 @@ void IRGeneratorForStatements::revertWithError(
|
||||
for (ASTPointer<Expression const> const& arg: _errorArguments)
|
||||
{
|
||||
errorArgumentVars += IRVariable(*arg).stackSlots();
|
||||
solAssert(arg->annotation().type, "");
|
||||
solAssert(arg->annotation().type);
|
||||
errorArgumentTypes.push_back(arg->annotation().type);
|
||||
}
|
||||
templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars));
|
||||
@ -3272,6 +3295,6 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause)
|
||||
|
||||
string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const
|
||||
{
|
||||
solAssert(_library.isLibrary(), "");
|
||||
solAssert(_library.isLibrary());
|
||||
return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")";
|
||||
}
|
||||
|
@ -78,6 +78,9 @@ CHC::CHC(
|
||||
|
||||
void CHC::analyze(SourceUnit const& _source)
|
||||
{
|
||||
if (!shouldAnalyze(_source))
|
||||
return;
|
||||
|
||||
if (!m_settings.solvers.z3 && !m_settings.solvers.smtlib2)
|
||||
{
|
||||
if (!m_noSolverWarning)
|
||||
@ -137,6 +140,9 @@ vector<string> CHC::unhandledQueries() const
|
||||
|
||||
bool CHC::visit(ContractDefinition const& _contract)
|
||||
{
|
||||
if (!shouldAnalyze(_contract))
|
||||
return false;
|
||||
|
||||
resetContractAnalysis();
|
||||
initContract(_contract);
|
||||
clearIndices(&_contract);
|
||||
@ -152,6 +158,9 @@ bool CHC::visit(ContractDefinition const& _contract)
|
||||
|
||||
void CHC::endVisit(ContractDefinition const& _contract)
|
||||
{
|
||||
if (!shouldAnalyze(_contract))
|
||||
return;
|
||||
|
||||
for (auto base: _contract.annotation().linearizedBaseContracts)
|
||||
{
|
||||
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
|
||||
{
|
||||
if (!_contract.canBeDeployed())
|
||||
|
@ -223,6 +223,8 @@ protected:
|
||||
/// @returns true if @param _contract is set for analysis in the settings
|
||||
/// and it is not abstract.
|
||||
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);
|
||||
|
||||
|
@ -44,7 +44,7 @@ struct OptimiserSettings
|
||||
static char constexpr DefaultYulOptimiserSteps[] =
|
||||
"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
|
||||
"Lcul" // Simplify again
|
||||
"Vcul [j]" // Reverse SSA
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::string_literals;
|
||||
using namespace std::placeholders;
|
||||
|
||||
using namespace solidity::lsp;
|
||||
@ -96,10 +97,10 @@ LanguageServer::LanguageServer(Transport& _transport):
|
||||
{"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)},
|
||||
{"initialized", [](auto, auto) {}},
|
||||
{"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }},
|
||||
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _1, _2)},
|
||||
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _1, _2)},
|
||||
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _1, _2)},
|
||||
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _1, _2)},
|
||||
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)},
|
||||
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
|
||||
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
|
||||
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
|
||||
},
|
||||
m_fileRepository("/" /* basePath */),
|
||||
m_compilerStack{m_fileRepository.reader()}
|
||||
@ -260,6 +261,10 @@ bool LanguageServer::run()
|
||||
else
|
||||
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 (...)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool LanguageServer::checkServerInitialized(MessageID _id)
|
||||
void LanguageServer::requireServerInitialized()
|
||||
{
|
||||
if (m_state != State::Initialized)
|
||||
{
|
||||
m_client.error(_id, ErrorCode::ServerNotInitialized, "Server is not properly initialized.");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
lspAssert(
|
||||
m_state == State::Initialized,
|
||||
ErrorCode::ServerNotInitialized,
|
||||
"Server is not properly initialized."
|
||||
);
|
||||
}
|
||||
|
||||
void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
||||
{
|
||||
if (m_state != State::Started)
|
||||
{
|
||||
m_client.error(_id, ErrorCode::RequestFailed, "Initialize called at the wrong time.");
|
||||
return;
|
||||
}
|
||||
lspAssert(
|
||||
m_state == State::Started,
|
||||
ErrorCode::RequestFailed,
|
||||
"Initialize called at the wrong time."
|
||||
);
|
||||
|
||||
m_state = State::Initialized;
|
||||
|
||||
// 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"])
|
||||
{
|
||||
rootPath = uri.asString();
|
||||
if (!boost::starts_with(rootPath, "file://"))
|
||||
{
|
||||
m_client.error(_id, ErrorCode::InvalidParams, "rootUri only supports file URI scheme.");
|
||||
return;
|
||||
}
|
||||
lspAssert(
|
||||
boost::starts_with(rootPath, "file://"),
|
||||
ErrorCode::InvalidParams,
|
||||
"rootUri only supports file URI scheme."
|
||||
);
|
||||
|
||||
rootPath = rootPath.substr(7);
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
void LanguageServer::handleWorkspaceDidChangeConfiguration(MessageID _id, Json::Value const& _args)
|
||||
void LanguageServer::handleWorkspaceDidChangeConfiguration(Json::Value const& _args)
|
||||
{
|
||||
if (!checkServerInitialized(_id))
|
||||
return;
|
||||
requireServerInitialized();
|
||||
|
||||
if (_args["settings"].isObject())
|
||||
changeConfiguration(_args["settings"]);
|
||||
}
|
||||
|
||||
void LanguageServer::handleTextDocumentDidOpen(MessageID _id, Json::Value const& _args)
|
||||
void LanguageServer::handleTextDocumentDidOpen(Json::Value const& _args)
|
||||
{
|
||||
if (!checkServerInitialized(_id))
|
||||
return;
|
||||
requireServerInitialized();
|
||||
|
||||
if (!_args["textDocument"])
|
||||
m_client.error(_id, ErrorCode::RequestFailed, "Text document parameter missing.");
|
||||
lspAssert(
|
||||
_args["textDocument"],
|
||||
ErrorCode::RequestFailed,
|
||||
"Text document parameter missing."
|
||||
);
|
||||
|
||||
string text = _args["textDocument"]["text"].asString();
|
||||
string uri = _args["textDocument"]["uri"].asString();
|
||||
@ -342,41 +347,37 @@ void LanguageServer::handleTextDocumentDidOpen(MessageID _id, Json::Value const&
|
||||
compileAndUpdateDiagnostics();
|
||||
}
|
||||
|
||||
void LanguageServer::handleTextDocumentDidChange(MessageID _id, Json::Value const& _args)
|
||||
void LanguageServer::handleTextDocumentDidChange(Json::Value const& _args)
|
||||
{
|
||||
if (!checkServerInitialized(_id))
|
||||
return;
|
||||
requireServerInitialized();
|
||||
|
||||
string const uri = _args["textDocument"]["uri"].asString();
|
||||
|
||||
for (Json::Value jsonContentChange: _args["contentChanges"])
|
||||
{
|
||||
if (!jsonContentChange.isObject())
|
||||
{
|
||||
m_client.error(_id, ErrorCode::RequestFailed, "Invalid content reference.");
|
||||
return;
|
||||
}
|
||||
lspAssert(
|
||||
jsonContentChange.isObject(),
|
||||
ErrorCode::RequestFailed,
|
||||
"Invalid content reference."
|
||||
);
|
||||
|
||||
string const sourceUnitName = m_fileRepository.clientPathToSourceUnitName(uri);
|
||||
if (!m_fileRepository.sourceUnits().count(sourceUnitName))
|
||||
{
|
||||
m_client.error(_id, ErrorCode::RequestFailed, "Unknown file: " + uri);
|
||||
return;
|
||||
}
|
||||
lspAssert(
|
||||
m_fileRepository.sourceUnits().count(sourceUnitName),
|
||||
ErrorCode::RequestFailed,
|
||||
"Unknown file: " + uri
|
||||
);
|
||||
|
||||
string text = jsonContentChange["text"].asString();
|
||||
if (jsonContentChange["range"].isObject()) // otherwise full content update
|
||||
{
|
||||
optional<SourceLocation> change = parseRange(sourceUnitName, jsonContentChange["range"]);
|
||||
if (!change || !change->hasText())
|
||||
{
|
||||
m_client.error(
|
||||
_id,
|
||||
ErrorCode::RequestFailed,
|
||||
"Invalid source range: " + jsonCompactPrint(jsonContentChange["range"])
|
||||
);
|
||||
return;
|
||||
}
|
||||
lspAssert(
|
||||
change && change->hasText(),
|
||||
ErrorCode::RequestFailed,
|
||||
"Invalid source range: " + jsonCompactPrint(jsonContentChange["range"])
|
||||
);
|
||||
|
||||
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));
|
||||
text = move(buffer);
|
||||
@ -387,13 +388,15 @@ void LanguageServer::handleTextDocumentDidChange(MessageID _id, Json::Value cons
|
||||
compileAndUpdateDiagnostics();
|
||||
}
|
||||
|
||||
void LanguageServer::handleTextDocumentDidClose(MessageID _id, Json::Value const& _args)
|
||||
void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
|
||||
{
|
||||
if (!checkServerInitialized(_id))
|
||||
return;
|
||||
requireServerInitialized();
|
||||
|
||||
if (!_args["textDocument"])
|
||||
m_client.error(_id, ErrorCode::RequestFailed, "Text document parameter missing.");
|
||||
lspAssert(
|
||||
_args["textDocument"],
|
||||
ErrorCode::RequestFailed,
|
||||
"Text document parameter missing."
|
||||
);
|
||||
|
||||
string uri = _args["textDocument"]["uri"].asString();
|
||||
m_openFiles.erase(uri);
|
||||
|
@ -60,12 +60,12 @@ public:
|
||||
private:
|
||||
/// 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.
|
||||
bool checkServerInitialized(MessageID _id);
|
||||
void requireServerInitialized();
|
||||
void handleInitialize(MessageID _id, Json::Value const& _args);
|
||||
void handleWorkspaceDidChangeConfiguration(MessageID _id, Json::Value const& _args);
|
||||
void handleTextDocumentDidOpen(MessageID _id, Json::Value const& _args);
|
||||
void handleTextDocumentDidChange(MessageID _id, Json::Value const& _args);
|
||||
void handleTextDocumentDidClose(MessageID _id, Json::Value const& _args);
|
||||
void handleWorkspaceDidChangeConfiguration(Json::Value const& _args);
|
||||
void handleTextDocumentDidOpen(Json::Value const& _args);
|
||||
void handleTextDocumentDidChange(Json::Value const& _args);
|
||||
void handleTextDocumentDidClose(Json::Value const& _args);
|
||||
|
||||
/// Invoked when the server user-supplied configuration changes (initiated by the client).
|
||||
void changeConfiguration(Json::Value const&);
|
||||
|
@ -17,6 +17,8 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#pragma once
|
||||
|
||||
#include <libsolutil/Exceptions.h>
|
||||
|
||||
#include <json/value.h>
|
||||
|
||||
#include <functional>
|
||||
@ -45,6 +47,32 @@ enum class ErrorCode
|
||||
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
|
||||
*
|
||||
|
@ -120,6 +120,8 @@ add_library(yul
|
||||
optimiser/DeadCodeEliminator.h
|
||||
optimiser/Disambiguator.cpp
|
||||
optimiser/Disambiguator.h
|
||||
optimiser/EqualStoreEliminator.cpp
|
||||
optimiser/EqualStoreEliminator.h
|
||||
optimiser/EquivalentFunctionDetector.cpp
|
||||
optimiser/EquivalentFunctionDetector.h
|
||||
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 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/Dialect.h>
|
||||
#include <libyul/YulString.h>
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
|
||||
#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
|
||||
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/FunctionGrouper.h>
|
||||
#include <libyul/optimiser/FunctionHoister.h>
|
||||
#include <libyul/optimiser/EqualStoreEliminator.h>
|
||||
#include <libyul/optimiser/EquivalentFunctionCombiner.h>
|
||||
#include <libyul/optimiser/ExpressionSplitter.h>
|
||||
#include <libyul/optimiser/ExpressionJoiner.h>
|
||||
@ -204,6 +205,7 @@ map<string, unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps()
|
||||
ConditionalUnsimplifier,
|
||||
ControlFlowSimplifier,
|
||||
DeadCodeEliminator,
|
||||
EqualStoreEliminator,
|
||||
EquivalentFunctionCombiner,
|
||||
ExpressionInliner,
|
||||
ExpressionJoiner,
|
||||
@ -244,6 +246,7 @@ map<string, char> const& OptimiserSuite::stepNameToAbbreviationMap()
|
||||
{ConditionalUnsimplifier::name, 'U'},
|
||||
{ControlFlowSimplifier::name, 'n'},
|
||||
{DeadCodeEliminator::name, 'D'},
|
||||
{EqualStoreEliminator::name, 'E'},
|
||||
{EquivalentFunctionCombiner::name, 'v'},
|
||||
{ExpressionInliner::name, 'e'},
|
||||
{ExpressionJoiner::name, 'j'},
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <libyul/optimiser/UnusedAssignEliminator.h>
|
||||
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
@ -156,18 +156,3 @@ void UnusedStoreBase::merge(TrackedStores& _target, vector<TrackedStores>&& _sou
|
||||
merge(_target, move(ts));
|
||||
_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;
|
||||
};
|
||||
|
||||
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"
|
||||
fi
|
||||
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-8
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-9
|
||||
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"
|
||||
|
@ -187,7 +187,7 @@ def parse_cli_output(source_file_name: Path, cli_output: str) -> FileReport:
|
||||
return file_report
|
||||
|
||||
|
||||
def prepare_compiler_input( # pylint: disable=too-many-arguments
|
||||
def prepare_compiler_input(
|
||||
compiler_path: Path,
|
||||
source_file_name: Path,
|
||||
optimize: bool,
|
||||
@ -256,7 +256,7 @@ def detect_metadata_cli_option_support(compiler_path: Path):
|
||||
return process.returncode == 0
|
||||
|
||||
|
||||
def run_compiler( # pylint: disable=too-many-arguments
|
||||
def run_compiler(
|
||||
compiler_path: Path,
|
||||
source_file_name: Path,
|
||||
optimize: bool,
|
||||
@ -320,7 +320,7 @@ def run_compiler( # pylint: disable=too-many-arguments
|
||||
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],
|
||||
compiler_path: Path,
|
||||
interface: CompilerInterface,
|
||||
|
@ -25,9 +25,9 @@ set -ev
|
||||
keyid=70D110489D66E2F6
|
||||
email=builds@ethereum.org
|
||||
packagename=z3-static
|
||||
version=4.8.13
|
||||
version=4.8.14
|
||||
|
||||
DISTRIBUTIONS="focal groovy hirsute"
|
||||
DISTRIBUTIONS="focal hirsute impish jammy"
|
||||
|
||||
for distribution in $DISTRIBUTIONS
|
||||
do
|
||||
|
@ -33,12 +33,12 @@
|
||||
# Using $(em-config CACHE)/sysroot/usr seems to work, though, and still has cmake find the
|
||||
# dependencies automatically.
|
||||
FROM emscripten/emsdk:2.0.33 AS base
|
||||
LABEL version="8"
|
||||
LABEL version="9"
|
||||
|
||||
ADD emscripten.jam /usr/src
|
||||
RUN set -ex; \
|
||||
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; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
|
@ -22,7 +22,7 @@
|
||||
# (c) 2016-2021 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
FROM gcr.io/oss-fuzz-base/base-clang:latest as base
|
||||
LABEL version="14"
|
||||
LABEL version="15"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@ -61,7 +61,7 @@ RUN set -ex; \
|
||||
|
||||
# Z3
|
||||
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; \
|
||||
cd /usr/src/z3; \
|
||||
mkdir build; \
|
||||
|
@ -22,7 +22,7 @@
|
||||
# (c) 2016-2019 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
FROM buildpack-deps:focal AS base
|
||||
LABEL version="9"
|
||||
LABEL version="10"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
# (c) 2016-2019 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
FROM buildpack-deps:focal AS base
|
||||
LABEL version="9"
|
||||
LABEL version="10"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
@ -33,12 +33,12 @@ def parse_call(call):
|
||||
return function.strip(), arguments.strip(), results.strip()
|
||||
|
||||
|
||||
def colorize(left, right, id):
|
||||
def colorize(left, right, index):
|
||||
red = "\x1b[31m"
|
||||
yellow = "\x1b[33m"
|
||||
reset = "\x1b[0m"
|
||||
colors = [red, yellow]
|
||||
color = colors[id % len(colors)]
|
||||
color = colors[index % len(colors)]
|
||||
function, _arguments, _results = parse_call(right)
|
||||
left = left.replace("compileAndRun", color + "compileAndRun" + 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
|
||||
# identical with the tests specified in SolidityEndToEndTest.cpp.
|
||||
#
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
||||
import re
|
||||
import os
|
||||
@ -32,11 +30,11 @@ class Trace:
|
||||
def get_input(self):
|
||||
return self._input
|
||||
|
||||
def set_input(self, input):
|
||||
def set_input(self, bytecode):
|
||||
if self.kind == "create":
|
||||
# remove cbor encoded metadata from bytecode
|
||||
length = int(input[-4:], 16) * 2
|
||||
self._input = input[:len(input) - length - 4]
|
||||
length = int(bytecode[-4:], 16) * 2
|
||||
self._input = bytecode[:len(bytecode) - length - 4]
|
||||
|
||||
def get_output(self):
|
||||
return self._output
|
||||
@ -110,21 +108,21 @@ class TraceAnalyser:
|
||||
|
||||
@staticmethod
|
||||
def parse_parameters(line, trace):
|
||||
input = re.search(r'\s*in:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if input:
|
||||
trace.input = input.group(1)
|
||||
output = re.search(r'\s*out:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if output:
|
||||
trace.output = output.group(1)
|
||||
result = re.search(r'\s*result:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if result:
|
||||
trace.result = result.group(1)
|
||||
gas_used = re.search(r'\s*gas\sused:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if gas_used:
|
||||
trace.gas = gas_used.group(1)
|
||||
value = re.search(r'\s*value:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if value:
|
||||
trace.value = value.group(1)
|
||||
input_match = re.search(r'\s*in:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if input_match:
|
||||
trace.input = input_match.group(1)
|
||||
output_match = re.search(r'\s*out:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if output_match:
|
||||
trace.output = output_match.group(1)
|
||||
result_match = re.search(r'\s*result:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if result_match:
|
||||
trace.result = result_match.group(1)
|
||||
gas_used_match = re.search(r'\s*gas\sused:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if gas_used_match:
|
||||
trace.gas = gas_used_match.group(1)
|
||||
value_match = re.search(r'\s*value:\s*([a-fA-F0-9]*)', line, re.M | re.I)
|
||||
if value_match:
|
||||
trace.value = value_match.group(1)
|
||||
|
||||
def diff(self, analyser):
|
||||
if not self.ready:
|
||||
@ -154,7 +152,8 @@ class TraceAnalyser:
|
||||
|
||||
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):
|
||||
left_trace = trace
|
||||
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:
|
||||
content = f.read()
|
||||
finally:
|
||||
if content == None:
|
||||
if content is None:
|
||||
print(f"Error reading: {file_name}")
|
||||
return content
|
||||
|
||||
@ -44,11 +44,11 @@ def find_ids_in_source_file(file_name, id_to_file_names):
|
||||
if in_comment(source, m.start()):
|
||||
continue
|
||||
underscore_pos = m.group(0).index("_")
|
||||
id = m.group(0)[0:underscore_pos]
|
||||
if id in id_to_file_names:
|
||||
id_to_file_names[id].append(file_name)
|
||||
error_id = m.group(0)[0:underscore_pos]
|
||||
if error_id in id_to_file_names:
|
||||
id_to_file_names[error_id].append(file_name)
|
||||
else:
|
||||
id_to_file_names[id] = [file_name]
|
||||
id_to_file_names[error_id] = [file_name]
|
||||
|
||||
|
||||
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()])
|
||||
|
||||
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
|
||||
if not in_comment(source, m.start()) and (len(id) != 4 or id[0] == "0" or id_to_count[id] > 1):
|
||||
assert id in id_to_count
|
||||
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 error_id in id_to_count
|
||||
new_id = get_next_id(available_ids)
|
||||
assert new_id not in id_to_count
|
||||
id_to_count[id] -= 1
|
||||
id_to_count[error_id] -= 1
|
||||
else:
|
||||
new_id = id
|
||||
new_id = error_id
|
||||
|
||||
destination.extend(new_id + "_error")
|
||||
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
|
||||
"""
|
||||
|
||||
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:
|
||||
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"""
|
||||
|
||||
source_file_names = []
|
||||
for dir 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 directory in sub_dirs:
|
||||
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:
|
||||
_, ext = path.splitext(file_name)
|
||||
if ext in extensions:
|
||||
@ -145,27 +145,27 @@ def find_ids_in_cmdline_test_err(file_name):
|
||||
|
||||
|
||||
def print_ids(ids):
|
||||
for k, id in enumerate(sorted(ids)):
|
||||
for k, error_id in enumerate(sorted(ids)):
|
||||
if k % 10 > 0:
|
||||
print(" ", end="")
|
||||
elif k > 0:
|
||||
print()
|
||||
print(id, end="")
|
||||
print(error_id, end="")
|
||||
|
||||
|
||||
def print_ids_per_file(ids, id_to_file_names, top_dir):
|
||||
file_name_to_ids = {}
|
||||
for id in ids:
|
||||
for file_name in id_to_file_names[id]:
|
||||
for error_id in ids:
|
||||
for file_name in id_to_file_names[error_id]:
|
||||
relpath = path.relpath(file_name, top_dir)
|
||||
if relpath not in file_name_to_ids:
|
||||
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):
|
||||
print(file_name)
|
||||
for id in sorted(file_name_to_ids[file_name]):
|
||||
print(f" {id}", end="")
|
||||
for error_id in sorted(file_name_to_ids[file_name]):
|
||||
print(f" {error_id}", end="")
|
||||
print()
|
||||
|
||||
|
||||
@ -254,8 +254,6 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False):
|
||||
|
||||
|
||||
def main(argv):
|
||||
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
|
||||
|
||||
check = False
|
||||
fix = False
|
||||
no_confirm = False
|
||||
@ -277,7 +275,7 @@ def main(argv):
|
||||
|
||||
if [check, fix, examine_coverage, next_id].count(True) != 1:
|
||||
print("usage: python error_codes.py --check | --fix [--no-confirm] | --examine-coverage | --next")
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
cwd = os.getcwd()
|
||||
|
||||
@ -289,23 +287,23 @@ def main(argv):
|
||||
source_id_to_file_names = find_ids_in_source_files(source_file_names)
|
||||
|
||||
ok = True
|
||||
for id in sorted(source_id_to_file_names):
|
||||
if len(id) != 4:
|
||||
print(f"ID {id} length != 4")
|
||||
for error_id in sorted(source_id_to_file_names):
|
||||
if len(error_id) != 4:
|
||||
print(f"ID {error_id} length != 4")
|
||||
ok = False
|
||||
if id[0] == "0":
|
||||
print(f"ID {id} starts with zero")
|
||||
if error_id[0] == "0":
|
||||
print(f"ID {error_id} starts with zero")
|
||||
ok = False
|
||||
if len(source_id_to_file_names[id]) > 1:
|
||||
print(f"ID {id} appears {len(source_id_to_file_names[id])} times")
|
||||
if len(source_id_to_file_names[error_id]) > 1:
|
||||
print(f"ID {error_id} appears {len(source_id_to_file_names[error_id])} times")
|
||||
ok = False
|
||||
|
||||
if examine_coverage:
|
||||
if not ok:
|
||||
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
|
||||
exit(res)
|
||||
sys.exit(res)
|
||||
|
||||
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 not ok:
|
||||
print("Incorrect IDs have to be fixed before applying --next")
|
||||
exit(1)
|
||||
available_ids = {str(id) for id in range(1000, 10000)} - source_id_to_file_names.keys()
|
||||
sys.exit(1)
|
||||
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)
|
||||
print(f"Next ID: {next_id}")
|
||||
exit(0)
|
||||
sys.exit(0)
|
||||
|
||||
if ok:
|
||||
print("No incorrect IDs found")
|
||||
exit(0)
|
||||
sys.exit(0)
|
||||
|
||||
if check:
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
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":
|
||||
answer = input("[Y/N]? ")
|
||||
if answer not in "yY":
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
# 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)
|
||||
print("Fixing completed")
|
||||
exit(2)
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
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,
|
||||
# so before checking remove 4 spaces from each line.
|
||||
remainder = dedent(test)
|
||||
hash = hashlib.sha256(test.encode("utf-8")).hexdigest()
|
||||
sol_filename = f'test_{hash}_{cleaned_filename}.{language}'
|
||||
source_code_hash = hashlib.sha256(test.encode("utf-8")).hexdigest()
|
||||
sol_filename = f'test_{source_code_hash}_{cleaned_filename}.{language}'
|
||||
with open(sol_filename, mode='w', encoding='utf8', newline='') as fi:
|
||||
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 os import path, walk
|
||||
from sys import exit
|
||||
from textwrap import dedent
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
PROJECT_ROOT = path.dirname(path.dirname(path.realpath(__file__)))
|
||||
PYLINT_RCFILE = f"{PROJECT_ROOT}/scripts/pylintrc"
|
||||
@ -89,7 +89,7 @@ def main():
|
||||
success = pylint_all_filenames(options.dev_mode, rootdirs)
|
||||
|
||||
if not success:
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("No problems found.")
|
||||
|
||||
@ -98,4 +98,4 @@ if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
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
|
||||
# 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,
|
||||
# undefined-variable, unused-*, useless-object-inheritance.
|
||||
# TODO: What could be eliminated in future PRs: invalid-name, pointless-string-statement, redefined-outer-name.
|
||||
disable=
|
||||
bad-continuation,
|
||||
bad-indentation,
|
||||
bad-whitespace,
|
||||
consider-using-sys-exit,
|
||||
duplicate-code,
|
||||
invalid-name,
|
||||
missing-docstring,
|
||||
no-else-return,
|
||||
no-self-use,
|
||||
pointless-string-statement,
|
||||
redefined-builtin,
|
||||
redefined-outer-name,
|
||||
singleton-comparison,
|
||||
too-few-public-methods,
|
||||
too-many-arguments,
|
||||
too-many-branches,
|
||||
too-many-instance-attributes,
|
||||
too-many-locals,
|
||||
too-many-public-methods,
|
||||
too-many-statements,
|
||||
ungrouped-imports
|
||||
|
||||
[BASIC]
|
||||
|
@ -41,7 +41,8 @@ class regressor:
|
||||
"build/test/tools/ossfuzz")
|
||||
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.add_argument('-o', '--out-dir', required=True, type=str,
|
||||
help="""Directory where test results will be written""")
|
||||
|
@ -57,7 +57,7 @@ packagename=solc
|
||||
|
||||
static_build_distribution=hirsute
|
||||
|
||||
DISTRIBUTIONS="focal hirsute impish"
|
||||
DISTRIBUTIONS="focal hirsute impish jammy"
|
||||
|
||||
if is_release
|
||||
then
|
||||
|
@ -40,7 +40,7 @@ def writeSourceToFile(lines):
|
||||
filePath, srcName = extractSourceName(lines[0])
|
||||
# print("sourceName is ", srcName)
|
||||
# print("filePath is", filePath)
|
||||
if filePath != False:
|
||||
if filePath:
|
||||
os.system("mkdir -p " + filePath)
|
||||
with open(srcName, mode='a+', encoding='utf8', newline='') as f:
|
||||
createdSources.append(srcName)
|
||||
|
@ -47,8 +47,8 @@ def write_cases(f, tests):
|
||||
cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower()
|
||||
for test in tests:
|
||||
remainder = re.sub(r'^ {4}', '', test, 0, re.MULTILINE)
|
||||
hash = hashlib.sha256(test).hexdigest()
|
||||
with open(f'test_{hash}_{cleaned_filename}.sol', 'w', encoding='utf8') as _f:
|
||||
source_code_hash = hashlib.sha256(test).hexdigest()
|
||||
with open(f'test_{source_code_hash}_{cleaned_filename}.sol', 'w', encoding='utf8') as _f:
|
||||
_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")
|
||||
("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.")
|
||||
("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-smt", po::bool_switch(&disableSMT)->default_value(disableSMT), "disable SMT checker")
|
||||
("optimize", po::bool_switch(&optimize)->default_value(optimize), "enables optimization")
|
||||
@ -126,6 +128,17 @@ void CommonOptions::validate() const
|
||||
ConfigException,
|
||||
"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)
|
||||
{
|
||||
assertThrow(
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <libsolutil/Exceptions.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <test/evmc/evmc.h>
|
||||
|
||||
@ -67,6 +68,8 @@ struct CommonOptions
|
||||
bool useABIEncoderV1 = false;
|
||||
bool showMessages = false;
|
||||
bool showMetadata = false;
|
||||
size_t batches = 1;
|
||||
size_t selectedBatch = 0;
|
||||
|
||||
langutil::EVMVersion evmVersion() const;
|
||||
|
||||
@ -96,4 +99,27 @@ bool isValidSemanticTestPath(boost::filesystem::path const& _testPath);
|
||||
|
||||
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
|
||||
#endif
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/tree/traverse.hpp>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
@ -60,6 +61,41 @@ void removeTestSuite(std::string const& _name)
|
||||
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)
|
||||
{
|
||||
try
|
||||
@ -100,7 +136,8 @@ int registerTests(
|
||||
bool _enforceViaYul,
|
||||
bool _enforceCompileToEwasm,
|
||||
vector<string> const& _labels,
|
||||
TestCase::TestCaseCreator _testCaseCreator
|
||||
TestCase::TestCaseCreator _testCaseCreator,
|
||||
solidity::test::Batcher& _batcher
|
||||
)
|
||||
{
|
||||
int numTestsAdded = 0;
|
||||
@ -131,33 +168,38 @@ int registerTests(
|
||||
_enforceViaYul,
|
||||
_enforceCompileToEwasm,
|
||||
_labels,
|
||||
_testCaseCreator
|
||||
_testCaseCreator,
|
||||
_batcher
|
||||
);
|
||||
_suite.add(sub_suite);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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
|
||||
// invalidated on reallocation.
|
||||
static vector<unique_ptr<string const>> filenames;
|
||||
// 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
|
||||
// that is passed in. If the strings were stored directly in the vector, pointers/references to them would be
|
||||
// invalidated on reallocation.
|
||||
static vector<unique_ptr<string const>> filenames;
|
||||
|
||||
filenames.emplace_back(make_unique<string>(_path.string()));
|
||||
auto test_case = make_test_case(
|
||||
[config, _testCaseCreator]
|
||||
{
|
||||
BOOST_REQUIRE_NO_THROW({
|
||||
runTestCase(config, _testCaseCreator);
|
||||
});
|
||||
},
|
||||
_path.stem().string(),
|
||||
*filenames.back(),
|
||||
0
|
||||
);
|
||||
for (auto const& _label: _labels)
|
||||
test_case->add_label(_label);
|
||||
_suite.add(test_case);
|
||||
numTestsAdded = 1;
|
||||
filenames.emplace_back(make_unique<string>(_path.string()));
|
||||
auto test_case = make_test_case(
|
||||
[config, _testCaseCreator]
|
||||
{
|
||||
BOOST_REQUIRE_NO_THROW({
|
||||
runTestCase(config, _testCaseCreator);
|
||||
});
|
||||
},
|
||||
_path.stem().string(),
|
||||
*filenames.back(),
|
||||
0
|
||||
);
|
||||
for (auto const& _label: _labels)
|
||||
test_case->add_label(_label);
|
||||
_suite.add(test_case);
|
||||
numTestsAdded = 1;
|
||||
}
|
||||
}
|
||||
return numTestsAdded;
|
||||
}
|
||||
@ -172,6 +214,7 @@ void initializeOptions()
|
||||
|
||||
solidity::test::CommonOptions::setSingleton(std::move(options));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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*/[] )
|
||||
{
|
||||
using namespace solidity::test;
|
||||
|
||||
master_test_suite_t& master = framework::master_test_suite();
|
||||
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)
|
||||
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
|
||||
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)
|
||||
continue;
|
||||
|
||||
solAssert(registerTests(
|
||||
//TODO
|
||||
//solAssert(
|
||||
registerTests(
|
||||
master,
|
||||
options.testPath / ts.path,
|
||||
ts.subpath,
|
||||
options.enforceViaYul,
|
||||
options.enforceCompileToEwasm,
|
||||
ts.labels,
|
||||
ts.testCaseCreator
|
||||
) > 0, std::string("no ") + ts.title + " tests found");
|
||||
ts.testCaseCreator,
|
||||
batcher
|
||||
);
|
||||
// > 0, std::string("no ") + ts.title + " tests found");
|
||||
}
|
||||
|
||||
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
||||
|
@ -42,3 +42,8 @@ printTask "Running external tests..."
|
||||
"$REPO_ROOT/externalTests/gnosis-v2.sh" "$@"
|
||||
"$REPO_ROOT/externalTests/colony.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 "$@"
|
||||
BINARY_TYPE="$1"
|
||||
BINARY_PATH="$2"
|
||||
SELECTED_PRESETS="$3"
|
||||
|
||||
function compile_fn { yarn run provision:token:contracts; }
|
||||
function test_fn { yarn run test:contracts; }
|
||||
@ -34,7 +35,8 @@ function test_fn { yarn run test:contracts; }
|
||||
function colony_test
|
||||
{
|
||||
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 compile_only_presets=(
|
||||
@ -49,16 +51,15 @@ function colony_test
|
||||
legacy-optimize-evm+yul
|
||||
)
|
||||
|
||||
local selected_optimizer_presets
|
||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
||||
[[ $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" "$branch" "$DIR"
|
||||
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
||||
|
||||
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
|
||||
git submodule update --init
|
||||
|
||||
@ -70,7 +71,7 @@ function colony_test
|
||||
replace_version_pragmas
|
||||
[[ $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
|
||||
done
|
||||
}
|
||||
|
@ -24,7 +24,16 @@ set -e
|
||||
|
||||
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"
|
||||
|
||||
@ -37,10 +46,22 @@ function verify_input
|
||||
{
|
||||
local binary_type="$1"
|
||||
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'."
|
||||
[[ -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
|
||||
@ -77,12 +98,24 @@ function setup_solc
|
||||
function download_project
|
||||
{
|
||||
local repo="$1"
|
||||
local solcjs_branch="$2"
|
||||
local test_dir="$3"
|
||||
local ref_type="$2"
|
||||
local solcjs_ref="$3"
|
||||
local test_dir="$4"
|
||||
|
||||
printLog "Cloning $solcjs_branch of $repo..."
|
||||
git clone --depth 1 "$repo" -b "$solcjs_branch" "$test_dir/ext"
|
||||
cd ext
|
||||
[[ $ref_type == commit || $ref_type == branch || $ref_type == tag ]] || assertFail
|
||||
|
||||
printLog "Cloning ${ref_type} ${solcjs_ref} of ${repo}..."
|
||||
if [[ $ref_type == commit ]]; then
|
||||
mkdir 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)"
|
||||
}
|
||||
|
||||
@ -117,6 +150,19 @@ function neutralize_package_json_hooks
|
||||
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
|
||||
{
|
||||
local custom_solcjs_path="${1:-solc/}"
|
||||
@ -176,14 +222,37 @@ function force_hardhat_compiler_binary
|
||||
echo "Config file: ${config_file}"
|
||||
echo "Binary type: ${binary_type}"
|
||||
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
|
||||
{
|
||||
local config_file="$1"
|
||||
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..."
|
||||
echo "-------------------------------------"
|
||||
@ -195,10 +264,16 @@ function force_hardhat_compiler_settings
|
||||
echo "Compiler version (full): ${SOLCVERSION}"
|
||||
echo "-------------------------------------"
|
||||
|
||||
{
|
||||
echo -n 'module.exports["solidity"] = '
|
||||
hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version"
|
||||
} >> "$config_file"
|
||||
local settings
|
||||
settings=$(hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version")
|
||||
if [[ $config_file == *\.js ]]; then
|
||||
[[ $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
|
||||
@ -216,8 +291,12 @@ function hardhat_verify_compiler_version
|
||||
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..."
|
||||
grep '"solcVersion": "'"${solc_version}"'"' --with-filename artifacts/build-info/*.json || fail "Wrong compiler version detected."
|
||||
grep '"solcLongVersion": "'"${full_solc_version}"'"' --with-filename artifacts/build-info/*.json || fail "Wrong compiler version detected."
|
||||
local build_info_files
|
||||
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
|
||||
@ -249,6 +328,8 @@ function settings_from_preset
|
||||
local preset="$1"
|
||||
local evm_version="$2"
|
||||
|
||||
[[ " ${AVAILABLE_PRESETS[*]} " == *" $preset "* ]] || assertFail
|
||||
|
||||
case "$preset" in
|
||||
# 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}}" ;;
|
||||
@ -292,16 +373,27 @@ function hardhat_solc_build_subtask {
|
||||
local full_solc_version="$2"
|
||||
local binary_type="$3"
|
||||
local solc_path="$4"
|
||||
local language="$5"
|
||||
|
||||
[[ $binary_type == native || $binary_type == solcjs ]] || assertFail
|
||||
|
||||
[[ $binary_type == native ]] && local is_solcjs=false
|
||||
[[ $binary_type == solcjs ]] && local is_solcjs=true
|
||||
|
||||
echo "const {TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD} = require('hardhat/builtin-tasks/task-names');"
|
||||
echo "const assert = require('assert');"
|
||||
echo
|
||||
echo "subtask(TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, async (args, hre, runSuper) => {"
|
||||
if [[ $language == js ]]; then
|
||||
echo "const {TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD} = require('hardhat/builtin-tasks/task-names');"
|
||||
echo "const assert = require('assert');"
|
||||
echo
|
||||
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 " return {"
|
||||
echo " compilerPath: '$(realpath "$solc_path")',"
|
||||
@ -367,9 +459,10 @@ function hardhat_run_test
|
||||
local compile_only_presets="$3"
|
||||
local compile_fn="$4"
|
||||
local test_fn="$5"
|
||||
local config_var_name="$6"
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
|
@ -27,49 +27,48 @@ source test/externalTests/common.sh
|
||||
verify_input "$@"
|
||||
BINARY_TYPE="$1"
|
||||
BINARY_PATH="$2"
|
||||
SELECTED_PRESETS="$3"
|
||||
|
||||
function compile_fn { npx truffle compile; }
|
||||
function test_fn { npm run test; }
|
||||
function compile_fn { yarn build; }
|
||||
function test_fn { yarn test; }
|
||||
|
||||
function ens_test
|
||||
{
|
||||
local repo="https://github.com/ensdomains/ens.git"
|
||||
local branch=master
|
||||
local config_file="truffle.js"
|
||||
local repo="https://github.com/ensdomains/ens-contracts.git"
|
||||
local ref_type=tag
|
||||
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=(
|
||||
"${compile_only_presets[@]}"
|
||||
#ir-no-optimize # "YulException: Variable var_ttl_236 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+yul
|
||||
legacy-no-optimize
|
||||
#ir-no-optimize # Compilation fails with "YulException: Variable var__945 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 # 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-optimize-evm-only
|
||||
legacy-optimize-evm+yul
|
||||
)
|
||||
|
||||
local selected_optimizer_presets
|
||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
||||
[[ $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" "$branch" "$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
|
||||
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||
|
||||
neutralize_package_lock
|
||||
neutralize_package_json_hooks
|
||||
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$selected_optimizer_presets")"
|
||||
npm install
|
||||
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")"
|
||||
yarn install
|
||||
|
||||
replace_version_pragmas
|
||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
||||
neutralize_packaged_contracts
|
||||
|
||||
for preset in $selected_optimizer_presets; do
|
||||
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||
for preset in $SELECTED_PRESETS; do
|
||||
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||
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 "$@"
|
||||
BINARY_TYPE="$1"
|
||||
BINARY_PATH="$2"
|
||||
SELECTED_PRESETS="$3"
|
||||
|
||||
function compile_fn { npx truffle compile; }
|
||||
function test_fn { npm test; }
|
||||
@ -34,7 +35,8 @@ function test_fn { npm test; }
|
||||
function gnosis_safe_test
|
||||
{
|
||||
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 compile_only_presets=(
|
||||
@ -49,12 +51,11 @@ function gnosis_safe_test
|
||||
legacy-optimize-evm+yul
|
||||
)
|
||||
|
||||
local selected_optimizer_presets
|
||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
||||
[[ $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" "$branch" "$DIR"
|
||||
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||
[[ $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
|
||||
@ -62,13 +63,13 @@ function gnosis_safe_test
|
||||
|
||||
neutralize_package_lock
|
||||
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
|
||||
|
||||
replace_version_pragmas
|
||||
[[ $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
|
||||
done
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
||||
verify_input "$@"
|
||||
BINARY_TYPE="$1"
|
||||
BINARY_PATH="$2"
|
||||
SELECTED_PRESETS="$3"
|
||||
|
||||
function compile_fn { npx truffle compile; }
|
||||
function test_fn { npm test; }
|
||||
@ -34,7 +35,8 @@ function test_fn { npm test; }
|
||||
function gnosis_safe_test
|
||||
{
|
||||
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 compile_only_presets=()
|
||||
@ -48,25 +50,24 @@ function gnosis_safe_test
|
||||
legacy-optimize-evm+yul
|
||||
)
|
||||
|
||||
local selected_optimizer_presets
|
||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
||||
[[ $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" "$branch" "$DIR"
|
||||
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||
[[ $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
|
||||
|
||||
neutralize_package_lock
|
||||
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
|
||||
|
||||
replace_version_pragmas
|
||||
[[ $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
|
||||
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 "$@"
|
||||
BINARY_TYPE="$1"
|
||||
BINARY_PATH="$2"
|
||||
SELECTED_PRESETS="$3"
|
||||
|
||||
function compile_fn { npm run compile; }
|
||||
function test_fn { npm test; }
|
||||
@ -34,7 +35,8 @@ function test_fn { npm test; }
|
||||
function zeppelin_test
|
||||
{
|
||||
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 compile_only_presets=(
|
||||
@ -49,21 +51,20 @@ function zeppelin_test
|
||||
legacy-optimize-evm+yul
|
||||
)
|
||||
|
||||
local selected_optimizer_presets
|
||||
selected_optimizer_presets=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||
print_optimizer_presets_or_exit "$selected_optimizer_presets"
|
||||
[[ $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" "$branch" "$DIR"
|
||||
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||
|
||||
neutralize_package_json_hooks
|
||||
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
|
||||
|
||||
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
|
||||
done
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ class Rule:
|
||||
self.error('Rule is incorrect.\nModel: ' + str(m))
|
||||
self.solver.pop()
|
||||
|
||||
def error(self, msg):
|
||||
@classmethod
|
||||
def error(cls, msg):
|
||||
print(msg)
|
||||
sys.exit(1)
|
||||
|
@ -56,7 +56,8 @@ public:
|
||||
else
|
||||
generatedDocumentation = m_compilerStack.natspecDev(_contractName);
|
||||
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["kind"] = Json::Value(_userDocumentation ? "user" : "dev");
|
||||
@ -118,7 +119,8 @@ BOOST_AUTO_TEST_CASE(user_newline_break)
|
||||
|
||||
char const* natspec = R"ABCDEF(
|
||||
{
|
||||
"methods": {
|
||||
"methods":
|
||||
{
|
||||
"f()":
|
||||
{
|
||||
"notice": "world"
|
||||
@ -146,8 +148,10 @@ BOOST_AUTO_TEST_CASE(user_multiline_empty_lines)
|
||||
|
||||
char const* natspec = R"ABCDEF(
|
||||
{
|
||||
"methods": {
|
||||
"f()": {
|
||||
"methods":
|
||||
{
|
||||
"f()":
|
||||
{
|
||||
"notice": "hello world"
|
||||
}
|
||||
}
|
||||
@ -185,18 +189,27 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test)
|
||||
}
|
||||
)";
|
||||
|
||||
char const* devNatspec = "{"
|
||||
"\"methods\":{"
|
||||
" \"mul(uint256)\":{ \n"
|
||||
" \"details\": \"Multiplies a number by 7\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}}";
|
||||
char const* devNatspec = R"R(
|
||||
{
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256)":
|
||||
{
|
||||
"details": "Multiplies a number by 7"
|
||||
}
|
||||
}
|
||||
})R";
|
||||
|
||||
char const* userNatspec = "{"
|
||||
"\"methods\":{"
|
||||
" \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}"
|
||||
"}}";
|
||||
char const* userNatspec = R"R(
|
||||
{
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256)":
|
||||
{
|
||||
"notice": "Multiplies `a` by 7"
|
||||
}
|
||||
}
|
||||
})R";
|
||||
|
||||
checkNatspec(sourceCode, "test", devNatspec, false);
|
||||
checkNatspec(sourceCode, "test", userNatspec, true);
|
||||
@ -297,16 +310,16 @@ BOOST_AUTO_TEST_CASE(public_state_variable)
|
||||
|
||||
char const* devDoc = R"R(
|
||||
{
|
||||
"methods" : {},
|
||||
"stateVariables" :
|
||||
"methods": {},
|
||||
"stateVariables":
|
||||
{
|
||||
"state" :
|
||||
"state":
|
||||
{
|
||||
"details" : "example of dev",
|
||||
"return" : "returns state",
|
||||
"returns" :
|
||||
"details": "example of dev",
|
||||
"return": "returns state",
|
||||
"returns":
|
||||
{
|
||||
"_0" : "returns state"
|
||||
"_0": "returns state"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -316,9 +329,9 @@ BOOST_AUTO_TEST_CASE(public_state_variable)
|
||||
|
||||
char const* userDoc = R"R(
|
||||
{
|
||||
"methods" :
|
||||
"methods":
|
||||
{
|
||||
"state()" :
|
||||
"state()":
|
||||
{
|
||||
"notice": "example of notice"
|
||||
}
|
||||
@ -346,15 +359,15 @@ BOOST_AUTO_TEST_CASE(public_state_variable_struct)
|
||||
|
||||
char const* devDoc = R"R(
|
||||
{
|
||||
"methods" : {},
|
||||
"stateVariables" :
|
||||
"methods": {},
|
||||
"stateVariables":
|
||||
{
|
||||
"coinStack" :
|
||||
"coinStack":
|
||||
{
|
||||
"returns" :
|
||||
"returns":
|
||||
{
|
||||
"observeGraphicURL" : "Front pic",
|
||||
"reverseGraphicURL" : "Back pic"
|
||||
"observeGraphicURL": "Front pic",
|
||||
"reverseGraphicURL": "Back pic"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -364,9 +377,9 @@ BOOST_AUTO_TEST_CASE(public_state_variable_struct)
|
||||
|
||||
char const* userDoc = R"R(
|
||||
{
|
||||
"methods" :
|
||||
"methods":
|
||||
{
|
||||
"coinStack(uint256)" :
|
||||
"coinStack(uint256)":
|
||||
{
|
||||
"notice": "Get the n-th coin I own"
|
||||
}
|
||||
@ -406,12 +419,12 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
|
||||
|
||||
char const* devDoc = R"(
|
||||
{
|
||||
"methods" : {},
|
||||
"stateVariables" :
|
||||
"methods": {},
|
||||
"stateVariables":
|
||||
{
|
||||
"state" :
|
||||
"state":
|
||||
{
|
||||
"details" : "example of dev"
|
||||
"details": "example of dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -634,9 +647,11 @@ BOOST_AUTO_TEST_CASE(dev_return_no_params)
|
||||
|
||||
char const* natspec = R"ABCDEF(
|
||||
{
|
||||
"methods": {
|
||||
"mul(uint256,uint256)": {
|
||||
"returns": { "d": "The result of the multiplication"
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256,uint256)":
|
||||
{
|
||||
"returns": { "d": "The result of the multiplication" }
|
||||
}
|
||||
}
|
||||
})ABCDEF";
|
||||
@ -866,19 +881,24 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return)
|
||||
}
|
||||
)";
|
||||
|
||||
char const* natspec = "{"
|
||||
"\"methods\":{"
|
||||
" \"mul(uint256,uint256)\":{ \n"
|
||||
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
|
||||
" \"params\": {\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"
|
||||
" },\n"
|
||||
" \"returns\": {\n"
|
||||
" \"d\": \"The result of the multiplication and cookies with nutella\",\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}}";
|
||||
char const* natspec = R"R({
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256,uint256)":
|
||||
{
|
||||
"details": "Multiplies a number by 7 and adds second parameter",
|
||||
"params":
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"returns":
|
||||
{
|
||||
"d": "The result of the multiplication and cookies with nutella"
|
||||
}
|
||||
}
|
||||
}
|
||||
})R";
|
||||
|
||||
checkNatspec(sourceCode, "test", natspec, false);
|
||||
}
|
||||
@ -901,19 +921,25 @@ BOOST_AUTO_TEST_CASE(dev_multiline_comment)
|
||||
}
|
||||
)";
|
||||
|
||||
char const* natspec = "{"
|
||||
"\"methods\":{"
|
||||
" \"mul(uint256,uint256)\":{ \n"
|
||||
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
|
||||
" \"params\": {\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"
|
||||
" },\n"
|
||||
" \"returns\": {\n"
|
||||
" \"d\": \"The result of the multiplication and cookies with nutella\",\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}}";
|
||||
char const* natspec = R"R(
|
||||
{
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256,uint256)":
|
||||
{
|
||||
"details": "Multiplies a number by 7 and adds second parameter",
|
||||
"params":
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"returns":
|
||||
{
|
||||
"d": "The result of the multiplication and cookies with nutella"
|
||||
}
|
||||
}
|
||||
}
|
||||
})R";
|
||||
|
||||
checkNatspec(sourceCode, "test", natspec, false);
|
||||
}
|
||||
@ -1004,13 +1030,14 @@ BOOST_AUTO_TEST_CASE(natspec_notice_without_tag)
|
||||
|
||||
char const* natspec = R"ABCDEF(
|
||||
{
|
||||
"methods" : {
|
||||
"mul(uint256)" : {
|
||||
"notice" : "I do something awesome"
|
||||
}
|
||||
}
|
||||
}
|
||||
)ABCDEF";
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256)":
|
||||
{
|
||||
"notice": "I do something awesome"
|
||||
}
|
||||
}
|
||||
})ABCDEF";
|
||||
|
||||
checkNatspec(sourceCode, "test", natspec, true);
|
||||
}
|
||||
@ -1027,11 +1054,13 @@ BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag)
|
||||
|
||||
char const* natspec = R"ABCDEF(
|
||||
{
|
||||
"methods" : {
|
||||
"mul(uint256)" : {
|
||||
"notice" : "I do something awesome which requires two lines to explain"
|
||||
}
|
||||
}
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256)":
|
||||
{
|
||||
"notice": "I do something awesome which requires two lines to explain"
|
||||
}
|
||||
}
|
||||
}
|
||||
)ABCDEF";
|
||||
|
||||
@ -1047,7 +1076,7 @@ BOOST_AUTO_TEST_CASE(empty_comment)
|
||||
)";
|
||||
char const* natspec = R"ABCDEF(
|
||||
{
|
||||
"methods" : {}
|
||||
"methods": {}
|
||||
}
|
||||
)ABCDEF";
|
||||
|
||||
@ -1135,8 +1164,10 @@ BOOST_AUTO_TEST_CASE(user_constructor)
|
||||
)";
|
||||
|
||||
char const* natspec = R"ABCDEF({
|
||||
"methods": {
|
||||
"constructor" : {
|
||||
"methods":
|
||||
{
|
||||
"constructor":
|
||||
{
|
||||
"notice": "this is a really nice constructor"
|
||||
}
|
||||
}
|
||||
@ -1157,12 +1188,15 @@ BOOST_AUTO_TEST_CASE(user_constructor_and_function)
|
||||
)";
|
||||
|
||||
char const* natspec = R"ABCDEF({
|
||||
"methods" : {
|
||||
"mul(uint256,uint256)" : {
|
||||
"notice" : "another multiplier"
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256,uint256)":
|
||||
{
|
||||
"notice": "another multiplier"
|
||||
},
|
||||
"constructor" : {
|
||||
"notice" : "this is a really nice constructor"
|
||||
"constructor":
|
||||
{
|
||||
"notice": "this is a really nice constructor"
|
||||
}
|
||||
}
|
||||
})ABCDEF";
|
||||
@ -1181,11 +1215,14 @@ BOOST_AUTO_TEST_CASE(dev_constructor)
|
||||
)";
|
||||
|
||||
char const *natspec = R"ABCDEF({
|
||||
"methods" : {
|
||||
"constructor" : {
|
||||
"params" : {
|
||||
"a" : "the parameter a is really nice and very useful",
|
||||
"second" : "the second parameter is not very useful, it just provides additional confusion"
|
||||
"methods":
|
||||
{
|
||||
"constructor":
|
||||
{
|
||||
"params":
|
||||
{
|
||||
"a": "the parameter a is really nice and very useful",
|
||||
"second": "the second parameter is not very useful, it just provides additional confusion"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1228,21 +1265,27 @@ BOOST_AUTO_TEST_CASE(dev_constructor_and_function)
|
||||
)";
|
||||
|
||||
char const *natspec = R"ABCDEF({
|
||||
"methods" : {
|
||||
"mul(uint256,uint256)" : {
|
||||
"details" : "Multiplies a number by 7 and adds second parameter",
|
||||
"params" : {
|
||||
"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"
|
||||
"methods":
|
||||
{
|
||||
"mul(uint256,uint256)":
|
||||
{
|
||||
"details": "Multiplies a number by 7 and adds second parameter",
|
||||
"params":
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"returns" : {
|
||||
"returns":
|
||||
{
|
||||
"d": "The result of the multiplication and cookies with nutella"
|
||||
}
|
||||
},
|
||||
"constructor" : {
|
||||
"params" : {
|
||||
"a" : "the parameter a is really nice and very useful",
|
||||
"second" : "the second parameter is not very useful, it just provides additional confusion"
|
||||
"constructor":
|
||||
{
|
||||
"params":
|
||||
{
|
||||
"a": "the parameter a is really nice and very useful",
|
||||
"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({
|
||||
"methods": {
|
||||
"methods":
|
||||
{
|
||||
"f()": { "notice": "lorem ipsum" }
|
||||
}
|
||||
})ABCDEF";
|
||||
@ -1311,7 +1355,8 @@ BOOST_AUTO_TEST_CASE(slash3_slash4)
|
||||
)";
|
||||
|
||||
char const* natspec = R"ABCDEF({
|
||||
"methods": {
|
||||
"methods":
|
||||
{
|
||||
"f()": { "notice": "lorem" }
|
||||
}
|
||||
})ABCDEF";
|
||||
@ -1340,12 +1385,12 @@ BOOST_AUTO_TEST_CASE(dev_default_inherit_variable)
|
||||
})ABCDEF";
|
||||
|
||||
char const *natspec1 = R"ABCDEF({
|
||||
"methods" : {},
|
||||
"stateVariables" :
|
||||
"methods": {},
|
||||
"stateVariables":
|
||||
{
|
||||
"x" :
|
||||
"x":
|
||||
{
|
||||
"details" : "test"
|
||||
"details": "test"
|
||||
}
|
||||
}
|
||||
})ABCDEF";
|
||||
@ -1406,12 +1451,12 @@ BOOST_AUTO_TEST_CASE(dev_explicit_inherit_variable)
|
||||
})ABCDEF";
|
||||
|
||||
char const *natspec1 = R"ABCDEF({
|
||||
"methods" : {},
|
||||
"stateVariables" :
|
||||
"methods": {},
|
||||
"stateVariables":
|
||||
{
|
||||
"x" :
|
||||
"x":
|
||||
{
|
||||
"details" : "test"
|
||||
"details": "test"
|
||||
}
|
||||
}
|
||||
})ABCDEF";
|
||||
@ -1467,7 +1512,7 @@ BOOST_AUTO_TEST_CASE(dev_default_inherit)
|
||||
function transfer(address to, uint amount) virtual override external returns (bool)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contract Token is Middle {
|
||||
@ -1513,8 +1558,8 @@ BOOST_AUTO_TEST_CASE(user_default_inherit)
|
||||
contract Middle is ERC20 {
|
||||
function transfer(address to, uint amount) virtual override external returns (bool)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
contract Token is Middle {
|
||||
@ -2295,7 +2340,7 @@ BOOST_AUTO_TEST_CASE(dev_return_name_no_description)
|
||||
{
|
||||
"returns":
|
||||
{
|
||||
"a": "a",
|
||||
"a": "a"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2308,7 +2353,7 @@ BOOST_AUTO_TEST_CASE(dev_return_name_no_description)
|
||||
{
|
||||
"returns":
|
||||
{
|
||||
"b": "a",
|
||||
"b": "a"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2333,11 +2378,11 @@ BOOST_AUTO_TEST_CASE(error)
|
||||
char const* devdoc = R"X({
|
||||
"errors":{
|
||||
"E(uint256,uint256)": [{
|
||||
"details" : "an error.",
|
||||
"params" :
|
||||
"details": "an error.",
|
||||
"params":
|
||||
{
|
||||
"a" : "first parameter",
|
||||
"b" : "second parameter"
|
||||
"a": "first parameter",
|
||||
"b": "second parameter"
|
||||
}
|
||||
}]
|
||||
},
|
||||
@ -2349,7 +2394,7 @@ BOOST_AUTO_TEST_CASE(error)
|
||||
char const* userdoc = R"X({
|
||||
"errors":{
|
||||
"E(uint256,uint256)": [{
|
||||
"notice" : "Something failed."
|
||||
"notice": "Something failed."
|
||||
}]
|
||||
},
|
||||
"methods": {}
|
||||
@ -2384,22 +2429,23 @@ BOOST_AUTO_TEST_CASE(error_multiple)
|
||||
|
||||
char const* devdoc = R"X({
|
||||
"methods": {},
|
||||
"errors": {
|
||||
"errors":
|
||||
{
|
||||
"E(uint256,uint256)": [
|
||||
{
|
||||
"details" : "an error.",
|
||||
"params" :
|
||||
"details": "an error.",
|
||||
"params":
|
||||
{
|
||||
"x" : "first parameter",
|
||||
"y" : "second parameter"
|
||||
"x": "first parameter",
|
||||
"y": "second parameter"
|
||||
}
|
||||
},
|
||||
{
|
||||
"details" : "X an error.",
|
||||
"params" :
|
||||
"details": "X an error.",
|
||||
"params":
|
||||
{
|
||||
"a" : "X first parameter",
|
||||
"b" : "X second parameter"
|
||||
"a": "X first parameter",
|
||||
"b": "X second parameter"
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -2411,8 +2457,8 @@ BOOST_AUTO_TEST_CASE(error_multiple)
|
||||
char const* userdoc = R"X({
|
||||
"errors":{
|
||||
"E(uint256,uint256)": [
|
||||
{ "notice" : "Something failed." },
|
||||
{ "notice" : "X Something failed." }
|
||||
{ "notice": "Something failed." },
|
||||
{ "notice": "X Something failed." }
|
||||
]
|
||||
},
|
||||
"methods": {}
|
||||
@ -2465,7 +2511,8 @@ BOOST_AUTO_TEST_CASE(custom_inheritance)
|
||||
}
|
||||
)";
|
||||
|
||||
char const* natspecA = R"ABCDEF({
|
||||
char const* natspecA = R"ABCDEF(
|
||||
{
|
||||
"methods":
|
||||
{
|
||||
"g(uint256)":
|
||||
@ -2473,8 +2520,9 @@ BOOST_AUTO_TEST_CASE(custom_inheritance)
|
||||
"custom:since": "2014"
|
||||
}
|
||||
}
|
||||
)ABCDEF";
|
||||
char const* natspecB = R"ABCDEF({
|
||||
})ABCDEF";
|
||||
char const* natspecB = R"ABCDEF(
|
||||
{
|
||||
"methods": {}
|
||||
})ABCDEF";
|
||||
|
||||
@ -2482,7 +2530,7 @@ BOOST_AUTO_TEST_CASE(custom_inheritance)
|
||||
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"(
|
||||
interface IThing {
|
||||
@ -2506,11 +2554,11 @@ BOOST_AUTO_TEST_CASE(dev_different_amount_return_parameters)
|
||||
{
|
||||
"value()":
|
||||
{
|
||||
"returns":
|
||||
{
|
||||
"x": "a number",
|
||||
"y": "another number"
|
||||
}
|
||||
"returns":
|
||||
{
|
||||
"x": "a number",
|
||||
"y": "another number"
|
||||
}
|
||||
}
|
||||
}
|
||||
})ABCDEF";
|
||||
@ -2534,6 +2582,58 @@ BOOST_AUTO_TEST_CASE(dev_different_amount_return_parameters)
|
||||
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()
|
||||
|
@ -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
|
||||
// ----
|
||||
// constructor()
|
||||
// gas irOptimized: 1558001
|
||||
// gas irOptimized: 1557137
|
||||
// gas legacy: 2436584
|
||||
// gas legacyOptimized: 1776483
|
||||
// supportsInterface(bytes4): 0x0 -> 0
|
||||
|
@ -20,7 +20,7 @@ contract test {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// 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 legacyOptimized: 111781
|
||||
// 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
|
||||
// ----
|
||||
// set(uint256): 7 -> true
|
||||
// gas irOptimized: 110011
|
||||
// gas irOptimized: 110119
|
||||
// gas legacy: 110616
|
||||
// gas legacyOptimized: 110006
|
||||
// retrieve(uint256): 7 -> 1, 3, 4, 2
|
||||
// copy(uint256,uint256): 7, 8 -> true
|
||||
// gas irOptimized: 118707
|
||||
// gas irOptimized: 118698
|
||||
// gas legacy: 119166
|
||||
// gas legacyOptimized: 118622
|
||||
// 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