Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
Marenz 2022-01-20 16:38:18 +01:00
commit 7cb129b16b
184 changed files with 3105 additions and 1191 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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})

View File

@ -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:

View File

@ -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``.

View File

@ -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

View File

@ -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``.

View File

@ -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
====================

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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

View File

@ -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>`_.

View File

@ -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;

View File

@ -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.

View File

@ -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``.

View File

@ -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());

View File

@ -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.

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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(

View File

@ -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;

View File

@ -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()
)),
};
}

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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
}

View File

@ -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)
);
}

View File

@ -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

View File

@ -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:

View File

@ -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;
};
/**

View File

@ -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);

View File

@ -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();

View File

@ -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();
});
}

View File

@ -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) ->

View File

@ -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()) + ")";
}

View File

@ -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())

View File

@ -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())

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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&);

View File

@ -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
*

View File

@ -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

View 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);
}

View 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;
};
}

View File

@ -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);
}

View File

@ -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;
};
}

View File

@ -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'},

View File

@ -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>

View File

@ -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);
}

View File

@ -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;
};
}

View File

@ -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"

View File

@ -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,

View File

@ -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

View File

@ -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; \

View File

@ -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; \

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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]

View File

@ -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__":

View File

@ -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)

View File

@ -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.")

View File

@ -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]

View File

@ -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""")

View File

@ -57,7 +57,7 @@ packagename=solc
static_build_distribution=hirsute
DISTRIBUTIONS="focal hirsute impish"
DISTRIBUTIONS="focal hirsute impish jammy"
if is_release
then

View File

@ -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)

View File

@ -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)

View File

@ -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(

View File

@ -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;
};
}

View File

@ -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)

View File

@ -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
View 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

View File

@ -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
}

View File

@ -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"
}

View File

@ -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
View 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

View File

@ -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
}

View File

@ -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
}

View 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
View 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

View 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

View File

@ -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
}

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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