From 22466acf39c41c168a9a24b05b2f00f48cbfa30e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 9 Jan 2020 00:20:27 +0100 Subject: [PATCH 001/160] Experimental wasm rebuild scripts --- .../docker-scripts/genbytecode.sh | 98 ++++++++ .../docker-scripts/isolate_tests.py | 55 ++++ scripts/wasm-rebuild/docker-scripts/patch.sh | 7 + .../docker-scripts/rebuild_current.sh | 67 +++++ .../docker-scripts/rebuild_tags.sh | 238 ++++++++++++++++++ scripts/wasm-rebuild/rebuild.sh | 28 +++ 6 files changed, 493 insertions(+) create mode 100755 scripts/wasm-rebuild/docker-scripts/genbytecode.sh create mode 100755 scripts/wasm-rebuild/docker-scripts/isolate_tests.py create mode 100755 scripts/wasm-rebuild/docker-scripts/patch.sh create mode 100755 scripts/wasm-rebuild/docker-scripts/rebuild_current.sh create mode 100755 scripts/wasm-rebuild/docker-scripts/rebuild_tags.sh create mode 100755 scripts/wasm-rebuild/rebuild.sh diff --git a/scripts/wasm-rebuild/docker-scripts/genbytecode.sh b/scripts/wasm-rebuild/docker-scripts/genbytecode.sh new file mode 100755 index 000000000000..ef28d59a4172 --- /dev/null +++ b/scripts/wasm-rebuild/docker-scripts/genbytecode.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Script used for cross-platform comparison as part of the travis automation. +# Splits all test source code into multiple files, generates bytecode and +# uploads the bytecode into github.com/ethereum/solidity-test-bytecode where +# another travis job is triggered to do the actual comparison. +# +# ------------------------------------------------------------------------------ +# 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 +# +# (c) 2017 solidity contributors. +#------------------------------------------------------------------------------ + +set -e + +SCRIPTDIR=$(dirname "$0") +SCRIPTDIR=$(realpath "${SCRIPTDIR}") + + +echo "Compiling all test contracts into bytecode..." +TMPDIR=$(mktemp -d) +( + cd "${TMPDIR}" + "${SCRIPTDIR}/isolate_tests.py" /src/test/ + + cat > solc < /tmp/report.txt +) +rm -rf "$TMPDIR" diff --git a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py new file mode 100755 index 000000000000..eab461bffe82 --- /dev/null +++ b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python2 + +import sys +import re +import os +import hashlib +from os.path import join, isfile + + +def extract_test_cases(path): + lines = open(path, 'rb').read().splitlines() + + inside = False + delimiter = '' + tests = [] + + for l in lines: + if inside: + if l.strip().endswith(')' + delimiter + '";'): + tests[-1] += l.strip()[:-(3 + len(delimiter))] + inside = False + else: + tests[-1] += l + '\n' + else: + m = re.search(r'R"([^(]*)\((.*)$', l.strip()) + if m: + inside = True + delimiter = m.group(1) + tests += [m.group(2)] + + return tests + +def extract_and_write(f, path): + if f.endswith('.sol'): + cases = [open(path, 'r').read()] + else: + cases = extract_test_cases(path) + write_cases(f, cases) + +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) + open('test_%s_%s.sol' % (hashlib.sha256(test).hexdigest(), cleaned_filename), 'w').write(remainder) + + +if __name__ == '__main__': + path = sys.argv[1] + + for root, subdirs, files in os.walk(path): + if '_build' in subdirs: + subdirs.remove('_build') + for f in files: + path = join(root, f) + extract_and_write(f, path) diff --git a/scripts/wasm-rebuild/docker-scripts/patch.sh b/scripts/wasm-rebuild/docker-scripts/patch.sh new file mode 100755 index 000000000000..a03fc73d0efe --- /dev/null +++ b/scripts/wasm-rebuild/docker-scripts/patch.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +TAG="$1" +SOLJSON_JS="$2" + +# If we ever want to patch the binaries e.g. for compatibility with older solc-js versions, +# we can do that here. diff --git a/scripts/wasm-rebuild/docker-scripts/rebuild_current.sh b/scripts/wasm-rebuild/docker-scripts/rebuild_current.sh new file mode 100755 index 000000000000..b48b37fc589e --- /dev/null +++ b/scripts/wasm-rebuild/docker-scripts/rebuild_current.sh @@ -0,0 +1,67 @@ +#!/bin/bash -e + +# Do not call this script directly. + +# This script is expected to be run inside the docker image trzeci/emscripten:sdk-tag-1.39.3-64bit and +# be called by ./rebuild_tags.sh. + +echo "========== STAGE 1: PREPARE ========== ($(date))" +COMMIT_DATE="$(git show -s --format=%cI HEAD)" +git rev-parse --short=8 HEAD >commit_hash.txt +echo -e "" >prerelease.txt +sed -i -e 's/-Wl,--gc-sections//' cmake/EthCompilerSettings.cmake +echo "set(CMAKE_CXX_FLAGS \"\${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','_malloc','stringToUTF8','setValue'] -s WASM=1 -s WASM_ASYNC_COMPILATION=0 -s SINGLE_FILE=1 -Wno-almost-asm\")" >>cmake/EthCompilerSettings.cmake +# Needed for < 0.5.0. +sed -i -e 's/-Werror/-Wno-error/' cmake/EthCompilerSettings.cmake + +echo "========== STAGE 2: BUILD ========== ($(date))" +scripts/travis-emscripten/install_deps.sh +if [ -d cryptopp ]; then + # Needed for < 0.4.4. Will not affect >= 0.4.5. + # Unfortunately we need to update to the latest + # release in the 5.6 series for it to build. + # Hopefully we don't miss any bugs. + rm -rf cryptopp + git clone https://github.com/weidai11/cryptopp/ + ( + set -e + cd cryptopp + git checkout CRYPTOPP_5_6_5 + ln -s . src + ) +fi +if [ -d jsoncpp ]; then + # Needed for < 0.4.4. Will not affect >= 0.4.5. + ( + set -e + cd jsoncpp + # Checkout the latest commit at the time of our release. + git checkout $(git rev-list -1 --before=$COMMIT_DATE master) + ) +fi + +set +e +scripts/travis-emscripten/build_emscripten.sh +set -e + +mkdir -p upload + +if [ ! -f upload/soljson.js ]; then + if [ -f build/solc/soljson.js ]; then + cp build/solc/soljson.js upload + elif [ -f build/libsolc/soljson.js ]; then + cp build/libsolc/soljson.js upload + elif [ -f emscripten_build/solc/soljson.js ]; then + cp emscripten_build/solc/soljson.js upload + elif [ -f emscripten_build/libsolc/soljson.js ]; then + cp emscripten_build/libsolc/soljson.js upload + fi +fi + +if [ -f upload/soljson.js ]; then + echo "========== SUCCESS ========== ($(date))" + exit 0 +else + echo "========== FAILURE ========== ($(date))" + exit 1 +fi diff --git a/scripts/wasm-rebuild/docker-scripts/rebuild_tags.sh b/scripts/wasm-rebuild/docker-scripts/rebuild_tags.sh new file mode 100755 index 000000000000..6a3065bca44c --- /dev/null +++ b/scripts/wasm-rebuild/docker-scripts/rebuild_tags.sh @@ -0,0 +1,238 @@ +#!/bin/bash -e + +# This script is expected to be run inside the docker image trzeci/emscripten:sdk-tag-1.39.3-64bit. +# Its main purpose is to be called by ../rebuild.sh. + +# Usage: $0 [tagFilter] [outputDirectory] + +# The output directory must be outside the repository, +# since the script will prune the repository directory after +# each build. + +TAG_FILTER="$1" +OUTPUTDIR="$2" +RETEST=0 +shift +shift +while (( "$#" )); do + if [[ "$1" == "--retest" ]]; then + RETEST=1 + else + echo "Unrecognized option: $1" + exit 1 + fi + shift +done + +SOLIDITY_REPO_URL="https://github.com/ethereum/solidity" +SOLC_JS_REPO_URL="https://github.com/ethereum/solc-js" +SOLC_JS_BRANCH=wasmRebuildTests +RELEASE_URL="https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin" +RELEASE_COMMIT_LIST_URL="$RELEASE_URL/list.txt" + +SCRIPTDIR=$(dirname "$0") +SCRIPTDIR=$(realpath "${SCRIPTDIR}") +RED='\033[0;31m' +GREEN='\033[0;32m' +ORANGE='\033[0;33m' +CYAN='\033[0;36m' +RESET='\033[0m' + +function generate_bytecode_report() { + rm -rf /tmp/report.txt + + local EXIT_STATUS + + if semver -r "<0.4.12" $3 > /dev/null; then + set +e + "${SCRIPTDIR}/genbytecode.sh" "$1" >/dev/null 2>&1 + EXIT_STATUS=$? + set -e + else + set +e + ( + set -e + + git reset --hard HEAD --quiet + git clean -f -d -x --quiet + + for dir in build/solc build/libsolc emscripten_build/libsolc; do + mkdir -p $dir + rm -rf $dir/soljson.js + ln -sf "$1" $dir/soljson.js + done + + /tmp/storebytecode.sh >/dev/null 2>&1 + ) + EXIT_STATUS=$? + fi + + if [ $EXIT_STATUS -eq 0 ] && [ -f /tmp/report.txt ] && grep -q -v -c -e "ERROR" -e "NO BYTECODE" /tmp/report.txt; then + mv /tmp/report.txt "$2" + echo -e "${GREEN}SUCCESS${RESET}" + else + echo -e "${RED}FAILURE${RESET}" + fi +} +function clean_git_checkout() { + git submodule deinit --all -q + git reset --hard HEAD --quiet + git clean -f -d -x --quiet + git checkout "$1" --quiet + git submodule init -q + git submodule update -q +} +function process_tag() { + local TAG=$1 + cd /src + # Checkout the historic commit instead of the tag directly. + local HISTORIC_COMMIT_HASH="$(grep "${TAG}+" /tmp/release_commit_list.txt | cut -d '+' -f 2 | cut -d '.' -f 2)" + if [ "$(git cat-file -t ${HISTORIC_COMMIT_HASH} 2>/dev/null)" == "commit" ]; then + clean_git_checkout "$HISTORIC_COMMIT_HASH" + else + clean_git_checkout "${TAG}" + fi + + # compatibility symlink + ln -s . solidity + + local VERSION + if [ -f ./scripts/get_version.sh ]; then + VERSION=$(./scripts/get_version.sh) + else + VERSION=$(echo "$TAG" | cut -d v -f 2) + fi + + local COMMIT_HASH=$(git rev-parse --short=8 HEAD) + local FULL_VERSION_SUFFIX="${TAG}+commit.${COMMIT_HASH}" + local HISTORIC_VERSION_SUFFIX="${TAG}+commit.${HISTORIC_COMMIT_HASH}" + + if [ ! -f "${OUTPUTDIR}/bin/soljson-${FULL_VERSION_SUFFIX}.js" ]; then + echo -ne "BUILDING ${CYAN}${TAG}${RESET}... " + set +e + ( + set -e + "${SCRIPTDIR}/rebuild_current.sh" "${VERSION}" >"${OUTPUTDIR}/log/running/build-$TAG.txt" 2>&1 + "${SCRIPTDIR}/patch.sh" "$TAG" upload/soljson.js + cp upload/soljson.js "${OUTPUTDIR}/bin/soljson-${FULL_VERSION_SUFFIX}.js" + rm upload/soljson.js + ) + local EXIT_STATUS=$? + set -e + rm -f "${OUTPUTDIR}/log/success/build-$TAG.txt" + rm -f "${OUTPUTDIR}/log/fail/build-$TAG.txt" + if [ $EXIT_STATUS -eq 0 ]; then + mv "${OUTPUTDIR}/log/running/build-$TAG.txt" "${OUTPUTDIR}/log/success" + echo -e "${GREEN}SUCCESS${RESET}" + else + mv "${OUTPUTDIR}/log/running/build-$TAG.txt" "${OUTPUTDIR}/log/fail" + echo -e "${RED}FAIL${RESET}" + fi + else + echo -e "${CYAN}${TAG}${RESET} ALREADY EXISTS." + if [ $RETEST -eq 0 ]; then + return 0 + fi + fi + + if [ -f "${OUTPUTDIR}/bin/soljson-${FULL_VERSION_SUFFIX}.js" ]; then + + echo -ne "GENERATE BYTECODE REPORT FOR ${CYAN}${TAG}${RESET}... " + generate_bytecode_report "${OUTPUTDIR}/bin/soljson-${FULL_VERSION_SUFFIX}.js" "${OUTPUTDIR}"/log/reports/report-${TAG}.txt "${TAG}" + echo -ne "GENERATE BYTECODE REPORT FOR HISTORIC ${CYAN}${TAG}${RESET}... " + rm -rf /tmp/soljson.js + if wget -q "$RELEASE_URL/soljson-${HISTORIC_VERSION_SUFFIX}.js" -O /tmp/soljson.js; then + generate_bytecode_report /tmp/soljson.js "${OUTPUTDIR}"/log/reports/report-historic-${TAG}.txt "${TAG}" + else + echo -e "${ORANGE}CANNOT FETCH RELEASE${RESET}" + fi + rm -rf /tmp/soljson.js + + if [ -f "${OUTPUTDIR}/log/reports/report-${TAG}.txt" ] && [ -f "${OUTPUTDIR}/log/reports/report-historic-${TAG}.txt" ]; then + rm -rf "${OUTPUTDIR}/log/success/bytecode-${TAG}.txt" + rm -rf "${OUTPUTDIR}/log/fail/bytecode-${TAG}.txt" + if diff -q "${OUTPUTDIR}/log/reports/report-${TAG}.txt" "${OUTPUTDIR}/log/reports/report-historic-${TAG}.txt" >/dev/null 2>&1; then + echo -e "${GREEN}BYTECODE MATCHES FOR ${CYAN}${TAG}${RESET}" + grep -v -c -e "ERROR" -e "NO BYTECODE" "${OUTPUTDIR}/log/reports/report-${TAG}.txt" >"${OUTPUTDIR}/log/success/bytecode-${TAG}.txt" + else + echo -e "${RED}BYTECODE DOES NOT MATCH FOR ${CYAN}${TAG}${RESET}" + echo "MISMATCH" >"${OUTPUTDIR}/log/fail/bytecode-${TAG}.txt" + fi + fi + + echo -ne "TESTING ${CYAN}${TAG}${RESET}... " + cd /root/solc-js + npm version --allow-same-version --no-git-tag-version "${VERSION}" >/dev/null + sed -i -e "s/runTests(solc, .*)/runTests(solc, '${FULL_VERSION_SUFFIX}')/" test/compiler.js + ln -sf "${OUTPUTDIR}/bin/soljson-${FULL_VERSION_SUFFIX}.js" soljson.js + rm -f "${OUTPUTDIR}/log/success/test-$TAG.txt" + rm -f "${OUTPUTDIR}/log/fail/test-$TAG.txt" + if npm test >"${OUTPUTDIR}/log/running/test-$TAG.txt" 2>&1; then + mv "${OUTPUTDIR}/log/running/test-$TAG.txt" "${OUTPUTDIR}/log/success" + echo -e "${GREEN}SUCCESS${RESET}" + else + mv "${OUTPUTDIR}/log/running/test-$TAG.txt" "${OUTPUTDIR}/log/fail" + echo -e "${RED}FAIL${RESET}" + fi + fi +} + +cd /tmp + +echo "Check out solidity repository..." +if [ -d /root/project ]; then + echo "Solidity repo checkout already exists." +else + git clone "${SOLIDITY_REPO_URL}" /root/project --quiet +fi + +echo "Extract bytecode comparison scripts from v0.6.1..." +cd /root/project +git checkout v0.6.1 --quiet +cp scripts/bytecodecompare/storebytecode.sh /tmp +sed -i -e 's/rm -rf "\$TMPDIR"/cp "\$TMPDIR"\/report.txt \/tmp\/report.txt ; rm -rf "\$TMPDIR"/' /tmp/storebytecode.sh +sed -i -e 's/REPO_ROOT=.*/REPO_ROOT=\/src/' /tmp/storebytecode.sh +export SOLC_EMSCRIPTEN="On" + +echo "Check out solc-js repository..." +if [ -d /root/solc-js ]; then + echo "solc-js repo checkout already exists." +else + git clone --branch "${SOLC_JS_BRANCH}" "${SOLC_JS_REPO_URL}" /root/solc-js --quiet +fi + +echo "Create symbolic links for backwards compatibility with older emscripten docker images." +ln -sf /emsdk_portable/node/current/* /emsdk_portable/node/ +ln -sf /emsdk_portable/emscripten/sdk/ /emsdk_portable/ +ln -sf sdk /emsdk_portable/emscripten/bin +ln -sf /emsdk_portable/emscripten/bin/* /usr/local/bin +rm -rf /src +ln -sf /root/project /src + +apt-get -qq update >/dev/null 2>&1 +apt-get -qq install cmake >/dev/null 2>&1 + +echo "Create output directories." +mkdir -p "${OUTPUTDIR}" +mkdir -p "${OUTPUTDIR}"/log +mkdir -p "${OUTPUTDIR}"/log/success +mkdir -p "${OUTPUTDIR}"/log/fail +mkdir -p "${OUTPUTDIR}"/log/running +mkdir -p "${OUTPUTDIR}"/log/reports +mkdir -p "${OUTPUTDIR}"/bin + +echo "Prepare solc-js." +cd /root/solc-js +npm install >/dev/null 2>&1 + +echo "Install semver helper." +npm install -g semver >/dev/null 2>&1 + +echo "Fetching release commit list." +wget -q "${RELEASE_COMMIT_LIST_URL}" -O /tmp/release_commit_list.txt + +cd /src +TAGS=$(git tag --list "${TAG_FILTER}" | tac) +for TAG in ${TAGS}; do + process_tag "${TAG}" +done diff --git a/scripts/wasm-rebuild/rebuild.sh b/scripts/wasm-rebuild/rebuild.sh new file mode 100755 index 000000000000..6ac2757645bb --- /dev/null +++ b/scripts/wasm-rebuild/rebuild.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# This script is expected to produce working builds for all compiler versions >= 0.3.6 and +# succeeding solc-js test runs for all compiler versions >= 0.5.0. + +if [ $# -lt 2 ]; then + echo "Usage: $0 [tagFilter] [outputDirectory] [options...]" + echo + echo " [tagFilter] will be passed to "git tag --list" to filter the tags to be built." + echo " [outputDirectory] will contain log files and the resulting soljson.js builds." + echo " --retest will re-run tests and bytecode comparisons, even if soljson.js is already built." + exit 1 +fi + +TAGS="$1" +OUTPUTDIR="$2" +shift +shift +SCRIPTDIR=$(dirname "$0") +SCRIPTDIR=$(realpath "${SCRIPTDIR}") + +if [ ! -d "${OUTPUTDIR}" ]; then + echo "Output directory ${OUTPUTDIR} does not exist!." + exit 1 +fi +OUTPUTDIR=$(realpath "${OUTPUTDIR}") + +docker run --rm -v "${OUTPUTDIR}":/tmp/output -v "${SCRIPTDIR}":/tmp/scripts:ro -it trzeci/emscripten:sdk-tag-1.39.3-64bit /tmp/scripts/docker-scripts/rebuild_tags.sh "${TAGS}" /tmp/output $@ From bb6fb675e0f7f31c6880226186605d5d6f49b880 Mon Sep 17 00:00:00 2001 From: pinkiebell <40266861+pinkiebell@users.noreply.github.com> Date: Wed, 22 Jan 2020 17:49:15 +0100 Subject: [PATCH 002/160] libsolidity/codegen: Use calldatacopy to cheaply zero memory instead of codecopy. Motiviation: Zero'ing memory is commonplace in contracts, but with the upcoming Layer-2 EVM translation layers and other on-chain verification mechanisms, using `codecopy` becomes a `costly` operation in those sandboxes. Using `calldatacopy` achieves the same thing, gas costs are also the same as codecopy, and is significantly cheaper in the `sandbox` situation. --- libsolidity/codegen/CompilerUtils.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index f9a0fbcec527..c15d2537e5a4 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -605,7 +605,7 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) Whiskers templ(R"({ let size := mul(length, ) // cheap way of zero-initializing a memory range - codecopy(memptr, codesize(), size) + calldatacopy(memptr, calldatasize(), size) memptr := add(memptr, size) })"); templ("element_size", to_string(_type.memoryStride())); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index df89fb3f989b..ee20e5b378f8 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10179,7 +10179,7 @@ BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor) { - // Memory arrays are initialized using codecopy past the size of the code. + // Memory arrays are initialized using calldatacopy past the size of the calldata. // This test checks that it also works in the constructor context. char const* sourceCode = R"( contract C { From a335fed189a1901024cf5498a5028094407e578c Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 18 Nov 2019 12:12:30 +0100 Subject: [PATCH 003/160] yul proto fuzzer: Add EVM version field --- test/tools/ossfuzz/protoToYul.cpp | 100 ++++++++++++++++++- test/tools/ossfuzz/protoToYul.h | 14 +++ test/tools/ossfuzz/yulProto.proto | 11 ++ test/tools/ossfuzz/yulProtoFuzzer.cpp | 4 +- test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp | 10 +- 5 files changed, 129 insertions(+), 10 deletions(-) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index f5f67fc02049..06f55f74bdac 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -30,6 +30,7 @@ using namespace std; using namespace solidity::yul::test::yul_fuzzer; using namespace solidity::yul::test; +using namespace solidity::langutil; using namespace solidity::util; using namespace solidity; @@ -86,6 +87,29 @@ string ProtoConverter::createAlphaNum(string const& _strBytes) return tmp; } +EVMVersion ProtoConverter::evmVersionMapping(Program_Version const& _ver) +{ + switch (_ver) + { + case Program::HOMESTEAD: + return EVMVersion::homestead(); + case Program::TANGERINE: + return EVMVersion::tangerineWhistle(); + case Program::SPURIOUS: + return EVMVersion::spuriousDragon(); + case Program::BYZANTIUM: + return EVMVersion::byzantium(); + case Program::CONSTANTINOPLE: + return EVMVersion::constantinople(); + case Program::PETERSBURG: + return EVMVersion::petersburg(); + case Program::ISTANBUL: + return EVMVersion::istanbul(); + case Program::BERLIN: + return EVMVersion::berlin(); + } +} + string ProtoConverter::visit(Literal const& _x) { switch (_x.literal_oneof_case()) @@ -230,7 +254,16 @@ void ProtoConverter::visit(Expression const& _x) void ProtoConverter::visit(BinaryOp const& _x) { - switch (_x.op()) + BinaryOp_BOp op = _x.op(); + + if ((op == BinaryOp::SHL || op == BinaryOp::SHR || op == BinaryOp::SAR) && + !m_evmVersion.hasBitwiseShifting()) + { + m_output << dictionaryToken(); + return; + } + + switch (op) { case BinaryOp::ADD: m_output << "add"; @@ -266,12 +299,15 @@ void ProtoConverter::visit(BinaryOp const& _x) m_output << "gt"; break; case BinaryOp::SHR: + yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version"); m_output << "shr"; break; case BinaryOp::SHL: + yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version"); m_output << "shl"; break; case BinaryOp::SAR: + yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version"); m_output << "sar"; break; case BinaryOp::SDIV: @@ -475,7 +511,17 @@ void ProtoConverter::visit(TypedVarDecl const& _x) void ProtoConverter::visit(UnaryOp const& _x) { - switch (_x.op()) + UnaryOp_UOp op = _x.op(); + + // Replace calls to extcodehash on unsupported EVMs with a dictionary + // token. + if (op == UnaryOp::EXTCODEHASH && !m_evmVersion.hasExtCodeHash()) + { + m_output << dictionaryToken(); + return; + } + + switch (op) { case UnaryOp::NOT: m_output << "not"; @@ -550,7 +596,12 @@ void ProtoConverter::visit(NullaryOp const& _x) m_output << "codesize()"; break; case NullaryOp::RETURNDATASIZE: - m_output << "returndatasize()"; + // If evm supports returndatasize, we generate it. Otherwise, + // we output a dictionary token. + if (m_evmVersion.supportsReturndata()) + m_output << "returndatasize()"; + else + m_output << dictionaryToken(); break; case NullaryOp::ADDRESS: m_output << "address()"; @@ -583,10 +634,20 @@ void ProtoConverter::visit(NullaryOp const& _x) m_output << "gaslimit()"; break; case NullaryOp::SELFBALANCE: - m_output << "selfbalance()"; + // Replace calls to selfbalance() on unsupported EVMs with a dictionary + // token. + if (m_evmVersion.hasSelfBalance()) + m_output << "selfbalance()"; + else + m_output << dictionaryToken(); break; case NullaryOp::CHAINID: - m_output << "chainid()"; + // Replace calls to chainid() on unsupported EVMs with a dictionary + // token. + if (m_evmVersion.hasChainID()) + m_output << "chainid()"; + else + m_output << dictionaryToken(); break; } } @@ -600,6 +661,11 @@ void ProtoConverter::visit(CopyFunc const& _x) if (type == CopyFunc::DATA && !m_isObject) return; + // We don't generate code if the copy function is returndatacopy + // and the underlying evm does not support it. + if (type == CopyFunc::RETURNDATA && !m_evmVersion.supportsReturndata()) + return; + switch (type) { case CopyFunc::CALLDATA: @@ -609,6 +675,7 @@ void ProtoConverter::visit(CopyFunc const& _x) m_output << "codecopy"; break; case CopyFunc::RETURNDATA: + yulAssert(m_evmVersion.supportsReturndata(), "Proto fuzzer: Invalid evm version"); m_output << "returndatacopy"; break; case CopyFunc::DATA: @@ -890,6 +957,16 @@ void ProtoConverter::visit(FunctionCall const& _x) void ProtoConverter::visit(LowLevelCall const& _x) { LowLevelCall_Type type = _x.callty(); + + // Generate staticcall if it is supported by the underlying evm + if (type == LowLevelCall::STATICCALL && !m_evmVersion.hasStaticCall()) + { + // Since staticcall is supposed to return 0 on success and 1 on + // failure, we can use counter value to emulate it + m_output << ((counter() % 2) ? "0" : "1"); + return; + } + switch (type) { case LowLevelCall::CALL: @@ -902,6 +979,7 @@ void ProtoConverter::visit(LowLevelCall const& _x) m_output << "delegatecall("; break; case LowLevelCall::STATICCALL: + yulAssert(m_evmVersion.hasStaticCall(), "Proto fuzzer: Invalid evm version"); m_output << "staticcall("; break; } @@ -927,6 +1005,15 @@ void ProtoConverter::visit(LowLevelCall const& _x) void ProtoConverter::visit(Create const& _x) { Create_Type type = _x.createty(); + + // Replace a call to create2 on unsupported EVMs with a dictionary + // token. + if (type == Create::CREATE2 && !m_evmVersion.hasCreate2()) + { + m_output << dictionaryToken(); + return; + } + switch (type) { case Create::CREATE: @@ -1737,6 +1824,9 @@ void ProtoConverter::visit(Program const& _x) // Initialize input size m_inputSize = _x.ByteSizeLong(); + // Record EVM Version + m_evmVersion = evmVersionMapping(_x.ver()); + // Program is either a yul object or a block of // statements. switch (_x.program_oneof_case()) diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index 37321c78c845..a85dbc909fa5 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -31,6 +31,8 @@ #include #include +#include + namespace solidity::yul::test::yul_fuzzer { class ProtoConverter @@ -56,6 +58,12 @@ class ProtoConverter ProtoConverter(ProtoConverter&&) = delete; std::string programToString(Program const& _input); + /// Returns evm version + solidity::langutil::EVMVersion version() + { + return m_evmVersion; + } + private: void visit(BinaryOp const&); @@ -270,6 +278,10 @@ class ProtoConverter /// dictionarySize is the total number of entries in the dictionary. std::string dictionaryToken(util::HexPrefix _p = util::HexPrefix::Add); + /// Returns an EVMVersion object corresponding to the protobuf + /// enum of type Program_Version + solidity::langutil::EVMVersion evmVersionMapping(Program_Version const& _x); + /// Returns a monotonically increasing counter that starts from zero. unsigned counter() { @@ -367,5 +379,7 @@ class ProtoConverter /// Flag to track whether scope extension of variables defined in for-init /// block is enabled. bool m_forInitScopeExtEnabled; + /// Object that holds the targeted evm version specified by protobuf input + solidity::langutil::EVMVersion m_evmVersion; }; } diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index d998da474762..cc9d33833456 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -405,10 +405,21 @@ message Data { } message Program { + enum Version { + HOMESTEAD = 0; + TANGERINE = 1; + SPURIOUS = 2; + BYZANTIUM = 3; + CONSTANTINOPLE = 4; + PETERSBURG = 5; + ISTANBUL = 6; + BERLIN = 7; + } oneof program_oneof { Block block = 1; Object obj = 2; } + required Version ver = 3; } package solidity.yul.test.yul_fuzzer; diff --git a/test/tools/ossfuzz/yulProtoFuzzer.cpp b/test/tools/ossfuzz/yulProtoFuzzer.cpp index 411198d3655d..b834ddc92a7b 100644 --- a/test/tools/ossfuzz/yulProtoFuzzer.cpp +++ b/test/tools/ossfuzz/yulProtoFuzzer.cpp @@ -29,12 +29,14 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::yul::test::yul_fuzzer; +using namespace solidity::langutil; using namespace std; DEFINE_PROTO_FUZZER(Program const& _input) { ProtoConverter converter; string yul_source = converter.programToString(_input); + EVMVersion version = converter.version(); if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) { @@ -51,7 +53,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) // AssemblyStack entry point AssemblyStack stack( - langutil::EVMVersion(), + version, AssemblyStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full() ); diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index f3f65bb31577..9dbc518bef57 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -55,7 +55,9 @@ void printErrors(ostream& _stream, ErrorList const& _errors) DEFINE_PROTO_FUZZER(Program const& _input) { - string yul_source = ProtoConverter().programToString(_input); + ProtoConverter converter; + string yul_source = converter.programToString(_input); + EVMVersion version = converter.version(); if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) { @@ -69,7 +71,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) // AssemblyStack entry point AssemblyStack stack( - langutil::EVMVersion::berlin(), + version, AssemblyStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full() ); @@ -87,7 +89,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( os1, stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion::berlin()) + EVMDialect::strictAssemblyForEVMObjects(version) ); if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached) @@ -97,7 +99,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) termReason = yulFuzzerUtil::interpret( os2, stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion::berlin()), + EVMDialect::strictAssemblyForEVMObjects(version), (yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4) ); From 8cbe1d4b1d2b0289b01a0de122edb186ba28fd97 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 18 Nov 2019 12:12:30 +0100 Subject: [PATCH 004/160] yul proto fuzzer: Make function call generation optional --- test/tools/ossfuzz/protoToYul.cpp | 3 ++- test/tools/ossfuzz/yulProto.proto | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 06f55f74bdac..86a053e1de2d 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -1740,7 +1740,8 @@ void ProtoConverter::createFunctionDefAndCall( !m_inForInitScope, "Proto fuzzer: Trying to create function call inside for-init block" ); - createFunctionCall(funcName, _numInParams, _numOutParams); + if (_x.force_call()) + createFunctionCall(funcName, _numInParams, _numOutParams); } void ProtoConverter::visit(FunctionDef const& _x) diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index cc9d33833456..129638e278a0 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -355,6 +355,7 @@ message FunctionDef { required uint32 num_input_params = 1; required uint32 num_output_params = 2; required Block block = 3; + required bool force_call = 4; } message PopStmt { From 7eb5fd1ca91ab3cf0908220b366538a2ea2f0ba5 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Mon, 27 Jan 2020 18:28:23 +0100 Subject: [PATCH 005/160] Appveyor: Prevent git from warning on std out --- scripts/bytecodecompare/storebytecode.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bytecodecompare/storebytecode.bat b/scripts/bytecodecompare/storebytecode.bat index 996b0e72a3fd..5aad801c4280 100644 --- a/scripts/bytecodecompare/storebytecode.bat +++ b/scripts/bytecodecompare/storebytecode.bat @@ -37,7 +37,7 @@ git clean -f -d -x 2>&1 if not exist %DIRECTORY% mkdir %DIRECTORY% set REPORT=%DIRECTORY%/windows.txt cp ../report.txt %REPORT% -git add %REPORT% +git add %REPORT% 2>$1 git commit -a -m "Added report." git pull --rebase 2>&1 git push origin 2>&1 From c8994d9ee2f45ddae2fe0df03822f5c8c047763f Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 27 Jan 2020 18:57:33 +0100 Subject: [PATCH 006/160] Set version to 0.6.3. --- CMakeLists.txt | 2 +- Changelog.md | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f64c7bd94280..408b42024438 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.6.2") +set(PROJECT_VERSION "0.6.3") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) include(TestBigEndian) diff --git a/Changelog.md b/Changelog.md index 26492e79b344..e503591274b9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,16 @@ +### 0.6.3 (unreleased) + +Language Features: + + + +Compiler Features: + + +Bugfixes: + + + ### 0.6.2 (2020-01-27) Language Features: From 1027f6f78fc32aec60617046ba0b25b9b11f8207 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 28 Jan 2020 11:59:44 +0100 Subject: [PATCH 007/160] Extract some semantic tests --- test/libsolidity/SolidityEndToEndTest.cpp | 82 ------------------- .../functionCall/member_accessors.sol | 22 +++++ .../semanticTests/types/mapping_simple.sol | 27 ++++++ .../semanticTests/types/strings.sol | 15 ++++ 4 files changed, 64 insertions(+), 82 deletions(-) create mode 100644 test/libsolidity/semanticTests/functionCall/member_accessors.sol create mode 100644 test/libsolidity/semanticTests/types/mapping_simple.sol create mode 100644 test/libsolidity/semanticTests/types/strings.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 42bdf8589379..a1d200f897b1 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -784,26 +784,6 @@ BOOST_AUTO_TEST_CASE(small_signed_types) testContractAgainstCpp("run()", small_signed_types_cpp); } -BOOST_AUTO_TEST_CASE(strings) -{ - char const* sourceCode = R"( - contract test { - function fixedBytes() public returns(bytes32 ret) { - return "abc\x00\xff__"; - } - function pipeThrough(bytes2 small, bool one) public returns(bytes16 large, bool oneRet) { - oneRet = one; - large = small; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("fixedBytes()"), encodeArgs(string("abc\0\xff__", 7))); - ABI_CHECK(callContractFunction("pipeThrough(bytes2,bool)", string("\0\x02", 2), true), encodeArgs(string("\0\x2", 2), true)); - ) -} - BOOST_AUTO_TEST_CASE(compound_assign) { char const* sourceCode = R"( @@ -843,40 +823,6 @@ BOOST_AUTO_TEST_CASE(compound_assign) ) } -BOOST_AUTO_TEST_CASE(simple_mapping) -{ - char const* sourceCode = R"( - contract test { - mapping(uint8 => uint8) table; - function get(uint8 k) public returns (uint8 v) { - return table[k]; - } - function set(uint8 k, uint8 v) public { - table[k] = v; - } - } - )"; - - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0)), encodeArgs(uint8_t(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); - callContractFunction("set(uint8,uint8)", uint8_t(0x01), uint8_t(0xa1)); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0xa1))); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); - callContractFunction("set(uint8,uint8)", uint8_t(0x00), uint8_t(0xef)); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0xef))); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0xa1))); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); - callContractFunction("set(uint8,uint8)", uint8_t(0x01), uint8_t(0x05)); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0xef))); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0x05))); - ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); - ) -} - BOOST_AUTO_TEST_CASE(mapping_state) { char const* sourceCode = R"( @@ -1057,34 +1003,6 @@ BOOST_AUTO_TEST_CASE(constructor) ) } -BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) -{ - char const* sourceCode = R"( - contract test { - uint256 public data; - bytes6 public name; - bytes32 public a_hash; - address public an_address; - constructor() public { - data = 8; - name = "Celina"; - a_hash = keccak256("\x7b"); - an_address = address(0x1337); - super_secret_data = 42; - } - uint256 super_secret_data; - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("data()"), encodeArgs(8)); - ABI_CHECK(callContractFunction("name()"), encodeArgs("Celina")); - ABI_CHECK(callContractFunction("a_hash()"), encodeArgs(util::keccak256(bytes(1, 0x7b)))); - ABI_CHECK(callContractFunction("an_address()"), encodeArgs(util::toBigEndian(u160(0x1337)))); - ABI_CHECK(callContractFunction("super_secret_data()"), bytes()); - ); -} - BOOST_AUTO_TEST_CASE(balance) { char const* sourceCode = R"( diff --git a/test/libsolidity/semanticTests/functionCall/member_accessors.sol b/test/libsolidity/semanticTests/functionCall/member_accessors.sol new file mode 100644 index 000000000000..67cb767db03f --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/member_accessors.sol @@ -0,0 +1,22 @@ +contract test { + uint256 public data; + bytes6 public name; + bytes32 public a_hash; + address public an_address; + constructor() public { + data = 8; + name = "Celina"; + a_hash = keccak256("\x7b"); + an_address = address(0x1337); + super_secret_data = 42; + } + uint256 super_secret_data; +} +// ==== +// compileViaYul: also +// ---- +// data() -> 8 +// name() -> "Celina" +// a_hash() -> 0xa91eddf639b0b768929589c1a9fd21dcb0107199bdd82e55c5348018a1572f52 +// an_address() -> 0x1337 +// super_secret_data() -> FAILURE diff --git a/test/libsolidity/semanticTests/types/mapping_simple.sol b/test/libsolidity/semanticTests/types/mapping_simple.sol new file mode 100644 index 000000000000..edee1d855786 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_simple.sol @@ -0,0 +1,27 @@ +contract test { + mapping(uint8 => uint8) table; + function get(uint8 k) public returns (uint8 v) { + return table[k]; + } + function set(uint8 k, uint8 v) public { + table[k] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0xa7 -> 0 +// set(uint8,uint8): 0x01, 0xa1 -> +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> 0 +// set(uint8,uint8): 0x00, 0xef -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> 0 +// set(uint8,uint8): 0x01, 0x05 -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> 0 diff --git a/test/libsolidity/semanticTests/types/strings.sol b/test/libsolidity/semanticTests/types/strings.sol new file mode 100644 index 000000000000..8e6f0943023a --- /dev/null +++ b/test/libsolidity/semanticTests/types/strings.sol @@ -0,0 +1,15 @@ +contract test { + function fixedBytes() public returns(bytes32 ret) { + return "abc\x00\xff__"; + } + function pipeThrough(bytes2 small, bool one) public returns(bytes16 large, bool oneRet) { + oneRet = one; + large = small; + } +} + +// ==== +// compileViaYul: also +// ---- +// fixedBytes() -> "abc\0\xff__" +// pipeThrough(bytes2, bool): "\0\x02", true -> "\0\x2", true From f1004e712c9fe45c012875086181ca9d680f5d06 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Tue, 28 Jan 2020 13:32:43 +0100 Subject: [PATCH 008/160] [WASM] Add div, sdiv, mod, smod, exp, lt, sar, addmod, mulmod, signextend --- libyul/backends/wasm/EVMToEwasmTranslator.cpp | 482 ++++++++++++++++-- .../arithmetic_addmod.yul | 7 +- .../ewasmTranslationTests/arithmetic_div.yul | 13 +- .../ewasmTranslationTests/arithmetic_exp.yul | 17 +- .../ewasmTranslationTests/arithmetic_mod.yul | 8 +- .../arithmetic_mulmod.yul | 7 +- .../ewasmTranslationTests/arithmetic_sdiv.yul | 10 +- .../ewasmTranslationTests/arithmetic_smod.yul | 10 +- test/libyul/ewasmTranslationTests/shifts.yul | 7 +- .../ewasmTranslationTests/signextend.yul | 7 +- 10 files changed, 525 insertions(+), 43 deletions(-) diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index 7f01253c927a..004f29092e65 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -52,6 +52,12 @@ static string const polyfill{R"({ function or_bool(a, b, c, d) -> r { r := i64.or(i64.or(a, b), i64.or(c, d)) } +function or_bool_320(a, b, c, d, e) -> r { + r := i64.or(or_bool(a, b, c, d), e) +} +function or_bool_512(a, b, c, d, e, f, g, h) -> r { + r := i64.or(or_bool(a, b, c, d), or_bool(e, f, g, h)) +} // returns a + y + c plus carry value on 64 bit values. // c should be at most 1 function add_carry(x, y, c) -> r, r_c { @@ -80,6 +86,27 @@ function sub(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { r2, carry := add_carry(x2, bit_negate(y2), carry) r1, carry := add_carry(x1, bit_negate(y1), carry) } +function sub320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { + // x - y = x + (~y + 1) + let carry + r5, carry := add_carry(x5, bit_negate(y5), 1) + r4, carry := add_carry(x4, bit_negate(y4), carry) + r3, carry := add_carry(x3, bit_negate(y3), carry) + r2, carry := add_carry(x2, bit_negate(y2), carry) + r1, carry := add_carry(x1, bit_negate(y1), carry) +} +function sub512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // x - y = x + (~y + 1) + let carry + r8, carry := add_carry(x8, bit_negate(y8), 1) + r7, carry := add_carry(x7, bit_negate(y7), carry) + r6, carry := add_carry(x6, bit_negate(y6), carry) + r5, carry := add_carry(x5, bit_negate(y5), carry) + r4, carry := add_carry(x4, bit_negate(y4), carry) + r3, carry := add_carry(x3, bit_negate(y3), carry) + r2, carry := add_carry(x2, bit_negate(y2), carry) + r1, carry := add_carry(x1, bit_negate(y1), carry) +} function split(x) -> hi, lo { hi := i64.shr_u(x, 32) lo := i64.and(x, 0xffffffff) @@ -124,36 +151,317 @@ function mul_128x128_256(x1, x2, y1, y2) -> r1, r2, r3, r4 { r1 := i64.add(i64.add(ah, carry1), carry2) } +// Multiplies two 256 bit values resulting in a 512 bit +// value split into eight 64 bit values. +function mul_256x256_512(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4, r5, r6, r7, r8 { + let a1, a2, a3, a4 := mul_128x128_256(x1, x2, y1, y2) + let b1, b2, b3, b4 := mul_128x128_256(x1, x2, y3, y4) + let c1, c2, c3, c4 := mul_128x128_256(x3, x4, y1, y2) + let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) + + r8 := d4 + r7 := d3 + + let carry1, carry2 + let t1, t2 + + r6, carry1 := add_carry(b4, c4, 0) + r6, carry2 := add_carry(r6, d2, 0) + + r5, carry1 := add_carry(b3, c3, carry1) + r5, carry2 := add_carry(r5, d1, carry2) + + r4, carry1 := add_carry(a4, b2, carry1) + r4, carry2 := add_carry(r4, c2, carry2) + + r3, carry1 := add_carry(a3, b1, carry1) + r3, carry2 := add_carry(r3, c1, carry2) + + r2, carry1 := add_carry(a2, carry1, carry2) + r1 := i64.add(a1, carry1) +} function mul(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { // TODO it would actually suffice to have mul_128x128_128 for the first two. let b1, b2, b3, b4 := mul_128x128_256(x3, x4, y1, y2) let c1, c2, c3, c4 := mul_128x128_256(x1, x2, y3, y4) - let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) + let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) r4 := d4 r3 := d3 let t1, t2 t1, t2, r1, r2 := add(0, 0, b3, b4, 0, 0, c3, c4) t1, t2, r1, r2 := add(0, 0, r1, r2, 0, 0, d1, d2) } +function shl_internal(amount, x1, x2, x3, x4) -> r1, r2, r3, r4 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.shl(x4, amount) +} +function shr_internal(amount, x1, x2, x3, x4) -> r1, r2, r3, r4 { + // amount < 64 + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, amount) +} +function shl320_internal(amount, x1, x2, x3, x4, x5) -> r1, r2, r3, r4, r5 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shl(x4, amount), i64.shr_u(x5, i64.sub(64, amount))) + r5 := i64.shl(x5, 1) +} +function shr320_internal(amount, x1, x2, x3, x4, x5) -> r1, r2, r3, r4, r5 { + // amount < 64 + r5 := i64.add(i64.shr_u(x5, amount), i64.shl(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, 1) +} +function shl512_internal(amount, x1, x2, x3, x4, x5, x6, x7, x8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shl(x4, amount), i64.shr_u(x5, i64.sub(64, amount))) + r5 := i64.add(i64.shl(x5, amount), i64.shr_u(x6, i64.sub(64, amount))) + r6 := i64.add(i64.shl(x6, amount), i64.shr_u(x7, i64.sub(64, amount))) + r7 := i64.add(i64.shl(x7, amount), i64.shr_u(x8, i64.sub(64, amount))) + r8 := i64.shl(x8, amount) +} +function shr512_internal(amount, x1, x2, x3, x4, x5, x6, x7, x8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // amount < 64 + r8 := i64.add(i64.shr_u(x8, amount), i64.shl(x7, i64.sub(64, amount))) + r7 := i64.add(i64.shr_u(x7, amount), i64.shl(x6, i64.sub(64, amount))) + r6 := i64.add(i64.shr_u(x6, amount), i64.shl(x5, i64.sub(64, amount))) + r5 := i64.add(i64.shr_u(x5, amount), i64.shl(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, amount) +} function div(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - r4 := i64.div_u(x4, y4) + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/DIV.wast + if iszero256(y1, y2, y3, y4) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + for {} 1 {} { + if i64.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, x1, x2, x3, x4)) { + break + } + y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shl_internal(1, m1, m2, m3, m4) + } + + for {} or_bool(m1, m2, m3, m4) {} { + if gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) { + x1, x2, x3, x4 := sub(x1, x2, x3, x4, y1, y2, y3, y4) + r1, r2, r3, r4 := add(r1, r2, r3, r4, m1, m2, m3, m4) + } + + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shr_internal(1, m1, m2, m3, m4) + } + } function sdiv(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - unreachable() + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SDIV.wast + + let sign := i64.shr_u(i64.xor(x1, y1), 63) + + if i64.eqz(i64.clz(x1)) { + x1, x2, x3, x4 := xor( + x1, x2, x3, x4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + x1, x2, x3, x4 := add(x1, x2, x3, x4, 0, 0, 0, 1) + } + + if i64.eqz(i64.clz(y1)) { + y1, y2, y3, y4 := xor( + y1, y2, y3, y4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + y1, y2, y3, y4 := add(y1, y2, y3, y4, 0, 0, 0, 1) + } + + r1, r2, r3, r4 := div(x1, x2, x3, x4, y1, y2, y3, y4) + + if sign { + r1, r2, r3, r4 := xor( + r1, r2, r3, r4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + r1, r2, r3, r4 := add(r1, r2, r3, r4, 0, 0, 0, 1) + } } function mod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - r4 := i64.rem_u(x4, y4) + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/MOD.wast + if iszero256(y1, y2, y3, y4) { + leave + } + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + for {} 1 {} { + if i64.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, r1, r2, r3, r4)) { + break + } + + y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shl_internal(1, m1, m2, m3, m4) + } + + for {} or_bool(m1, m2, m3, m4) {} { + if gte_256x256_64(r1, r2, r3, r4, y1, y2, y3, y4) { + r1, r2, r3, r4 := sub(r1, r2, r3, r4, y1, y2, y3, y4) + } + + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shr_internal(1, m1, m2, m3, m4) + } +} +function mod320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/mod_320.wast + if iszero320(y1, y2, y3, y4, y5) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 0 + let m5 := 1 + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + r5 := x5 + + for {} 1 {} { + if i64.or(i64.eqz(i64.clz(y1)), gte_320x320_64(y1, y2, y3, y4, y5, r1, r2, r3, r4, r5)) { + break + } + y1, y2, y3, y4, y5 := shl320_internal(1, y1, y2, y3, y4, y5) + m1, m2, m3, m4, m5 := shl320_internal(1, m1, m2, m3, m4, m5) + } + + for {} or_bool_320(m1, m2, m3, m4, m5) {} { + if gte_320x320_64(r1, r2, r3, r4, r5, y1, y2, y3, y4, y5) { + r1, r2, r3, r4, r5 := sub320(r1, r2, r3, r4, r5, y1, y2, y3, y4, y5) + } + + y1, y2, y3, y4, y5 := shr320_internal(1, y1, y2, y3, y4, y5) + m1, m2, m3, m4, m5 := shr320_internal(1, m1, m2, m3, m4, m5) + } +} +function mod512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/mod_512.wast + if iszero512(y1, y2, y3, y4, y5, y6, y7, y8) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 0 + let m5 := 0 + let m6 := 0 + let m7 := 0 + let m8 := 1 + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + r5 := x5 + r6 := x6 + r7 := x7 + r8 := x8 + + for {} 1 {} { + if i64.or( + i64.eqz(i64.clz(y1)), + gte_512x512_64(y1, y2, y3, y4, y5, y6, y7, y8, r1, r2, r3, r4, r5, r6, r7, r8) + ) + { + break + } + y1, y2, y3, y4, y5, y6, y7, y8 := shl512_internal(1, y1, y2, y3, y4, y5, y6, y7, y8) + m1, m2, m3, m4, m5, m6, m7, m8 := shl512_internal(1, m1, m2, m3, m4, m5, m6, m7, m8) + } + + for {} or_bool_512(m1, m2, m3, m4, m5, m6, m7, m8) {} { + if gte_512x512_64(r1, r2, r3, r4, r5, r6, r7, r8, y1, y2, y3, y4, y5, y6, y7, y8) { + r1, r2, r3, r4, r5, r6, r7, r8 := sub512(r1, r2, r3, r4, r5, r6, r7, r8, y1, y2, y3, y4, y5, y6, y7, y8) + } + + y1, y2, y3, y4, y5, y6, y7, y8 := shr512_internal(1, y1, y2, y3, y4, y5, y6, y7, y8) + m1, m2, m3, m4, m5, m6, m7, m8 := shr512_internal(1, m1, m2, m3, m4, m5, m6, m7, m8) + } } function smod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - r4 := i64.rem_u(x4, y4) + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SMOD.wast + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + let sign := i64.shr_u(x1, 63) + + if i64.eqz(i64.clz(x1)) { + x1, x2, x3, x4 := xor( + x1, x2, x3, x4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + x1, x2, x3, x4 := add(x1, x2, x3, x4, 0, 0, 0, 1) + } + + if i64.eqz(i64.clz(y1)) { + y1, y2, y3, y4 := xor( + y1, y2, y3, y4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + y1, y2, y3, y4 := add(y1, y2, y3, y4, 0, 0, 0, 1) + } + + r1, r2, r3, r4 := mod(x1, x2, x3, x4, y1, y2, y3, y4) + + if sign { + r1, r2, r3, r4 := xor( + r1, r2, r3, r4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + r1, r2, r3, r4 := add(r1, r2, r3, r4, 0, 0, 0, 1) + } } function exp(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - unreachable() + r4 := 1 + for {} or_bool(y1, y2, y3, y4) {} { + if i64.and(y4, 1) { + r1, r2, r3, r4 := mul(r1, r2, r3, r4, x1, x2, x3, x4) + } + x1, x2, x3, x4 := mul(x1, x2, x3, x4, x1, x2, x3, x4) + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + } } function byte(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { @@ -191,8 +499,17 @@ function not(x1, x2, x3, x4) -> r1, r2, r3, r4 { let mask := 0xffffffffffffffff r1, r2, r3, r4 := xor(x1, x2, x3, x4, mask, mask, mask, mask) } -function iszero(x1, x2, x3, x4) -> r1, r2, r3, r4 { - r4 := i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4))) +function iszero(x1, x2, x3, x4) -> r1, r2, r3 ,r4 { + r4 := iszero256(x1, x2, x3, x4) +} +function iszero256(x1, x2, x3, x4) -> r { + r := i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4))) +} +function iszero320(x1, x2, x3, x4, x5) -> r { + r := i64.eqz(i64.or(i64.or(i64.or(x1, x2), i64.or(x3, x4)), x5)) +} +function iszero512(x1, x2, x3, x4, x5, x6, x7, x8) -> r { + r := i64.and(iszero256(x1, x2, x3, x4), iszero256(x5, x6, x7, x8)) } function eq(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { if i64.eq(x1, y1) { @@ -214,27 +531,100 @@ function cmp(a, b) -> r { r := i64.ne(a, b) } } +function lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z { + switch cmp(x1, y1) + case 0 { + switch cmp(x2, y2) + case 0 { + switch cmp(x3, y3) + case 0 { + switch cmp(x4, y4) + case 0 { + z := i64.lt_u(x5, y5) + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } +} +function lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z { + switch cmp(x1, y1) + case 0 { + switch cmp(x2, y2) + case 0 { + switch cmp(x3, y3) + case 0 { + switch cmp(x4, y4) + case 0 { + switch cmp(x5, y5) + case 0 { + switch cmp(x6, y6) + case 0 { + switch cmp(x7, y7) + case 0 { + z := i64.lt_u(x8, y8) + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } + } + case 1 { z := 0 } + default { z := 1 } +} )" // Split long string to make it compilable on msvc // https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?view=vs-2019 R"( -function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { +function lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z { switch cmp(x1, y1) case 0 { switch cmp(x2, y2) case 0 { switch cmp(x3, y3) case 0 { - z4 := i64.lt_u(x4, y4) + z := i64.lt_u(x4, y4) } - case 1 { z4 := 0 } - default { z4 := 1 } + case 1 { z := 0 } + default { z := 1 } } - case 1 { z4 := 0 } - default { z4 := 1 } + case 1 { z := 0 } + default { z := 1 } } - case 1 { z4 := 0 } - default { z4 := 1 } + case 1 { z := 0 } + default { z := 1 } +} +function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z4 := lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) +} +function gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z { + z := i64.eqz(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) +} +function gte_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z { + z := i64.eqz(lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5)) +} +function gte_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z { + z := i64.eqz(lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8)) } function gt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { z1, z2, z3, z4 := lt(y1, y2, y3, y4, x1, x2, x3, x4) @@ -323,20 +713,54 @@ function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { } } function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() + if i64.clz(y1) { + z1, z2, z3, z4 := shr(x1, x2, x3, x4, y1, y2, y3, y4) + leave + } + + if gte_256x256_64(x1, x2, x3, x4, 0, 0, 0, 256) { + z1 := 0xffffffffffffffff + z2 := 0xffffffffffffffff + z3 := 0xffffffffffffffff + z4 := 0xffffffffffffffff + } + if lt_256x256_64(x1, x2, x3, x4, 0, 0, 0, 256) { + y1, y2, y3, y4 := shr(0, 0, 0, x4, y1, y2, y3, y4) + z1, z2, z3, z4 := shl( + 0, 0, 0, i64.sub(256, x4), + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + z1, z2, z3, z4 := or(y1, y2, y3, y4, z1, z2, z3, z4) + } } function addmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() + let carry + z4, carry := add_carry(x4, y4, 0) + z3, carry := add_carry(x3, y3, carry) + z2, carry := add_carry(x2, y2, carry) + z1, carry := add_carry(x1, y1, carry) + + let z0 + z0, z1, z2, z3, z4 := mod320(carry, z1, z2, z3, z4, 0, m1, m2, m3, m4) } function mulmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() + let r1, r2, r3, r4, r5, r6, r7, r8 := mul_256x256_512(x1, x2, x3, x4, y1, y2, y3, y4) + let t1 + let t2 + let t3 + let t4 + t1, t2, t3, t4, z1, z2, z3, z4 := mod512(r1, r2, r3, r4, r5, r6, r7, r8, 0, 0, 0, 0, m1, m2, m3, m4) } function signextend(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() + z1 := y1 + z2 := y2 + z3 := y3 + z4 := y4 + if lt_256x256_64(x1, x2, x3, x4, 0, 0, 0, 32) { + let d := i64.mul(i64.sub(31, x4), 8) + z1, z2, z3, z4 := shl(0, 0, 0, d, z1, z2, z3, z4) + z1, z2, z3, z4 := sar(0, 0, 0, d, z1, z2, z3, z4) + } } function u256_to_u128(x1, x2, x3, x4) -> v1, v2 { if i64.ne(0, i64.or(x1, x2)) { invalid() } diff --git a/test/libyul/ewasmTranslationTests/arithmetic_addmod.yul b/test/libyul/ewasmTranslationTests/arithmetic_addmod.yul index c1d43a731048..40b84764ede8 100644 --- a/test/libyul/ewasmTranslationTests/arithmetic_addmod.yul +++ b/test/libyul/ewasmTranslationTests/arithmetic_addmod.yul @@ -15,6 +15,11 @@ } // ---- // Trace: -// INVALID() // Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000008 +// 20: 0000000000000000000000000000000000000000000000000000000000000004 // Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000003: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000007: 0000000000000000000000000000000000000000000000000000000000000004 +// 0000000000000000000000000000000000000000000000000000000000000008: 0000000000000000000000000000000000000000000000000000000000000004 diff --git a/test/libyul/ewasmTranslationTests/arithmetic_div.yul b/test/libyul/ewasmTranslationTests/arithmetic_div.yul index 3d97f464cc38..f32c244a9f03 100644 --- a/test/libyul/ewasmTranslationTests/arithmetic_div.yul +++ b/test/libyul/ewasmTranslationTests/arithmetic_div.yul @@ -19,5 +19,16 @@ // ---- // Trace: // Memory dump: -// 0: 0000000000000000000000000000000000000000000000000000000000000001 +// 0: 000000000000000000000000000000000000000000000000000000000000000d +// 20: 0000000000000000555555555555555555555555555555550000000000000000 // Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000004: fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// 0000000000000000000000000000000000000000000000000000000000000005: 8000000000000000000000000000000000000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000006: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// 0000000000000000000000000000000000000000000000000000000000000007: 00000000000000000000000000000000ffffffffffffffffffffffffffffffff +// 0000000000000000000000000000000000000000000000000000000000000008: 000000000000000000000000000000000000000000000000ffffffffffffffff +// 0000000000000000000000000000000000000000000000000000000000000009: 0000000000000000ffffffffffffffffffffffffffffffff0000000000000000 +// 000000000000000000000000000000000000000000000000000000000000000a: 0000000000000000555555555555555555555555555555555555555555555555 +// 000000000000000000000000000000000000000000000000000000000000000b: 0000000000000000000000000000000055555555555555555555555555555555 +// 000000000000000000000000000000000000000000000000000000000000000c: 0000000000000000000000000000000000000000000000005555555555555555 +// 000000000000000000000000000000000000000000000000000000000000000d: 0000000000000000555555555555555555555555555555550000000000000000 diff --git a/test/libyul/ewasmTranslationTests/arithmetic_exp.yul b/test/libyul/ewasmTranslationTests/arithmetic_exp.yul index 328ac9c3be41..6428e73437bf 100644 --- a/test/libyul/ewasmTranslationTests/arithmetic_exp.yul +++ b/test/libyul/ewasmTranslationTests/arithmetic_exp.yul @@ -20,6 +20,21 @@ } // ---- // Trace: -// INVALID() // Memory dump: +// 0: 000000000000000000000000000000000000000000000000000000000000000f +// 20: 8000000000000000000000000000000000000000000000000000000000000000 // Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000002: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000003: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000004: fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// 0000000000000000000000000000000000000000000000000000000000000005: 8000000000000000000000000000000000000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000006: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// 0000000000000000000000000000000000000000000000000000000000000007: fffffffffffffffffffffffffffffffe00000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000008: 00000000000000000000000000000000fffffffffffffffe0000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000009: 0000000000000000000000000000000100000000000000000000000000000000 +// 000000000000000000000000000000000000000000000000000000000000000a: 0000000000000002ffffffffffffffffffffffffffffffffffffffffffffffff +// 000000000000000000000000000000000000000000000000000000000000000b: 00000000000000000000000000000002ffffffffffffffffffffffffffffffff +// 000000000000000000000000000000000000000000000000000000000000000c: 0000000000000000fffffffffffffffd0000000000000002ffffffffffffffff +// 000000000000000000000000000000000000000000000000000000000000000d: ffffffffffffffff000000000000000000000000000000000000000000000000 +// 000000000000000000000000000000000000000000000000000000000000000f: 8000000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libyul/ewasmTranslationTests/arithmetic_mod.yul b/test/libyul/ewasmTranslationTests/arithmetic_mod.yul index 4aa5c6997b45..38cc808bf4a2 100644 --- a/test/libyul/ewasmTranslationTests/arithmetic_mod.yul +++ b/test/libyul/ewasmTranslationTests/arithmetic_mod.yul @@ -19,7 +19,11 @@ // ---- // Trace: // Memory dump: -// 0: 0000000000000000000000000000000000000000000000000000000000000001 -// 20: 0000000000000000000000000000000000000000000000000000000000000001 +// 0: 000000000000000000000000000000000000000000000000000000000000000d +// 20: 0000000000000000000000000000000000000aacffffffff8b3c03a314db9000 // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000003: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000008: 000000000000000000000000000000000000000000000001ffffffffffffffff +// 000000000000000000000000000000000000000000000000000000000000000a: 0000000000000000000000000000000000000000000000000000000000000003 +// 000000000000000000000000000000000000000000000000000000000000000d: 0000000000000000000000000000000000000aacffffffff8b3c03a314db9000 diff --git a/test/libyul/ewasmTranslationTests/arithmetic_mulmod.yul b/test/libyul/ewasmTranslationTests/arithmetic_mulmod.yul index 094723ae02c7..ef6efafd4214 100644 --- a/test/libyul/ewasmTranslationTests/arithmetic_mulmod.yul +++ b/test/libyul/ewasmTranslationTests/arithmetic_mulmod.yul @@ -17,6 +17,11 @@ } // ---- // Trace: -// INVALID() // Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000008 +// 20: 000000000000000000000000000000000000000000000000000000000000000f // Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000003: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000004: fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// 0000000000000000000000000000000000000000000000000000000000000007: 0000000000000000000000000000000000000000000000000000000000000003 +// 0000000000000000000000000000000000000000000000000000000000000008: 000000000000000000000000000000000000000000000000000000000000000f diff --git a/test/libyul/ewasmTranslationTests/arithmetic_sdiv.yul b/test/libyul/ewasmTranslationTests/arithmetic_sdiv.yul index fd4364185328..0cefb340586c 100644 --- a/test/libyul/ewasmTranslationTests/arithmetic_sdiv.yul +++ b/test/libyul/ewasmTranslationTests/arithmetic_sdiv.yul @@ -20,6 +20,14 @@ } // ---- // Trace: -// INVALID() // Memory dump: +// 0: 000000000000000000000000000000000000000000000000000000000000000e +// 20: 7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000005: 8000000000000000000000000000000000000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000007: 7000000000000000000000000000000000000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000009: 8000000000000000000000000000000000000000000000000000000000000001 +// 000000000000000000000000000000000000000000000000000000000000000b: 7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// 000000000000000000000000000000000000000000000000000000000000000c: 0000000000000000000000000000000000000000000000000000000000000001 +// 000000000000000000000000000000000000000000000000000000000000000d: 0000000000000000000000000000000000000000000000000000000000000001 +// 000000000000000000000000000000000000000000000000000000000000000e: 7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libyul/ewasmTranslationTests/arithmetic_smod.yul b/test/libyul/ewasmTranslationTests/arithmetic_smod.yul index 97983a463c42..cffb8f73d3c7 100644 --- a/test/libyul/ewasmTranslationTests/arithmetic_smod.yul +++ b/test/libyul/ewasmTranslationTests/arithmetic_smod.yul @@ -23,7 +23,11 @@ // ---- // Trace: // Memory dump: -// 0: 0000000000000000000000000000000000000000000000000000000000000001 -// 20: 0000000000000000000000000000000000000000000000000000000000000001 +// 0: 000000000000000000000000000000000000000000000000000000000000000e +// 20: fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd // Storage dump: -// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000003: 0000000000000000000000000000000000000000000000000000000000000001 +// 0000000000000000000000000000000000000000000000000000000000000008: 000000000000000000000000000000000000000000000001ffffffffffffffff +// 000000000000000000000000000000000000000000000000000000000000000a: 0000000000000000000000000000000000000000000000000000000000000003 +// 000000000000000000000000000000000000000000000000000000000000000d: 0000000000000000000000000000000000000aacffffffff8b3c03a314db9000 +// 000000000000000000000000000000000000000000000000000000000000000e: fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd diff --git a/test/libyul/ewasmTranslationTests/shifts.yul b/test/libyul/ewasmTranslationTests/shifts.yul index c3e9b6036a44..cf900dae2350 100644 --- a/test/libyul/ewasmTranslationTests/shifts.yul +++ b/test/libyul/ewasmTranslationTests/shifts.yul @@ -13,10 +13,11 @@ // EVMVersion: >=constantinople // ---- // Trace: -// INVALID() // Memory dump: -// 0: 0000000000000000000000000000000000000000000000000000000000000001 -// 20: 0000000000000000000000000000000000101112131415161718191a1b1c1d1e +// 0: 0000000000000000000000000000000000000000000000000000000000000005 // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 101112131415161718191a1b1c1d1e1f20000000000000000000000000000000 // 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000101112131415161718191a1b1c1d1e +// 0000000000000000000000000000000000000000000000000000000000000002: ffffffffffffffffffffffffffffffffff801112131415161718191a1b1c1d1e +// 0000000000000000000000000000000000000000000000000000000000000003: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// 0000000000000000000000000000000000000000000000000000000000000004: 0000000000000000000000000000000000701112131415161718191a1b1c1d1e diff --git a/test/libyul/ewasmTranslationTests/signextend.yul b/test/libyul/ewasmTranslationTests/signextend.yul index 4b54332193a4..f39d5926b943 100644 --- a/test/libyul/ewasmTranslationTests/signextend.yul +++ b/test/libyul/ewasmTranslationTests/signextend.yul @@ -6,6 +6,11 @@ } // ---- // Trace: -// INVALID() // Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000003 +// 20: ffffffffffffffffffffffffffffffffffffffffffffffffffffff8844553322 // Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86 +// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000076 +// 0000000000000000000000000000000000000000000000000000000000000002: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// 0000000000000000000000000000000000000000000000000000000000000003: ffffffffffffffffffffffffffffffffffffffffffffffffffffff8844553322 From f0afb0aeff1eb0735cdbc1b2d04cd7bd7dda6879 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 15 Jan 2020 23:23:32 +0100 Subject: [PATCH 009/160] Remove stack height checks. --- libyul/AsmAnalysis.cpp | 30 -------------- libyul/AsmAnalysis.h | 6 +-- libyul/AsmAnalysisInfo.h | 1 - libyul/backends/evm/EVMCodeTransform.cpp | 52 ++---------------------- libyul/backends/evm/EVMCodeTransform.h | 9 ---- 5 files changed, 7 insertions(+), 91 deletions(-) diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 148eeabfe7c6..bd51a3cf5516 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -101,7 +101,6 @@ bool AsmAnalyzer::operator()(Literal const& _literal) } else if (_literal.kind == LiteralKind::Boolean) yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); - m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; } @@ -151,7 +150,6 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) } m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize; } - m_info.stackHeightInfo[&_identifier] = m_stackHeight; return success; } @@ -170,7 +168,6 @@ bool AsmAnalyzer::operator()(ExpressionStatement const& _statement) m_errorReporter.error(Error::Type::TypeError, _statement.location, msg); success = false; } - m_info.stackHeightInfo[&_statement] = m_stackHeight; return success; } @@ -196,7 +193,6 @@ bool AsmAnalyzer::operator()(Assignment const& _assignment) for (auto const& variableName: _assignment.variableNames) if (!checkAssignment(variableName, 1)) success = false; - m_info.stackHeightInfo[&_assignment] = m_stackHeight; return success; } @@ -239,7 +235,6 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) expectValidType(variable.type, variable.location); m_activeVariables.insert(&std::get(m_currentScope->identifiers.at(variable.name))); } - m_info.stackHeightInfo[&_varDecl] = m_stackHeight; return success; } @@ -261,7 +256,6 @@ bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) bool success = (*this)(_funDef.body); m_stackHeight = stackHeight; - m_info.stackHeightInfo[&_funDef] = m_stackHeight; return success; } @@ -334,7 +328,6 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) } // Use argument size instead of parameter count to avoid misleading errors. m_stackHeight += int(returns) - int(_funCall.arguments.size()); - m_info.stackHeightInfo[&_funCall] = m_stackHeight; return success; } @@ -351,8 +344,6 @@ bool AsmAnalyzer::operator()(If const& _if) if (!(*this)(_if.body)) success = false; - m_info.stackHeightInfo[&_if] = m_stackHeight; - return success; } @@ -421,7 +412,6 @@ bool AsmAnalyzer::operator()(Switch const& _switch) } m_stackHeight = initialHeight; - m_info.stackHeightInfo[&_switch] = m_stackHeight; return success; } @@ -458,31 +448,12 @@ bool AsmAnalyzer::operator()(ForLoop const& _for) success = false; m_stackHeight = initialHeight; - m_info.stackHeightInfo[&_for] = m_stackHeight; m_currentScope = outerScope; m_currentForLoop = outerForLoop; return success; } -bool AsmAnalyzer::operator()(Break const& _break) -{ - m_info.stackHeightInfo[&_break] = m_stackHeight; - return true; -} - -bool AsmAnalyzer::operator()(Continue const& _continue) -{ - m_info.stackHeightInfo[&_continue] = m_stackHeight; - return true; -} - -bool AsmAnalyzer::operator()(Leave const& _leaveStatement) -{ - m_info.stackHeightInfo[&_leaveStatement] = m_stackHeight; - return true; -} - bool AsmAnalyzer::operator()(Block const& _block) { bool success = true; @@ -512,7 +483,6 @@ bool AsmAnalyzer::operator()(Block const& _block) success = false; } - m_info.stackHeightInfo[&_block] = m_stackHeight; m_currentScope = previousScope; return success; } diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index 0bc1884dfab7..bd99b50c210a 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -87,9 +87,9 @@ class AsmAnalyzer bool operator()(If const& _if); bool operator()(Switch const& _switch); bool operator()(ForLoop const& _forLoop); - bool operator()(Break const&); - bool operator()(Continue const&); - bool operator()(Leave const&); + bool operator()(Break const&) { return true; } + bool operator()(Continue const&) { return true; } + bool operator()(Leave const&) { return true; } bool operator()(Block const& _block); private: diff --git a/libyul/AsmAnalysisInfo.h b/libyul/AsmAnalysisInfo.h index 8110608f964b..d094ee510582 100644 --- a/libyul/AsmAnalysisInfo.h +++ b/libyul/AsmAnalysisInfo.h @@ -36,7 +36,6 @@ struct AsmAnalysisInfo using StackHeightInfo = std::map; using Scopes = std::map>; Scopes scopes; - StackHeightInfo stackHeightInfo; /// Virtual blocks which will be used for scopes for function arguments and return values. std::map> virtualBlocks; }; diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index f8ce12017b4b..cd24db738fa0 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -102,7 +102,6 @@ CodeTransform::CodeTransform( bool _evm15, ExternalIdentifierAccess const& _identifierAccess, bool _useNamedLabelsForFunctions, - int _stackAdjustment, shared_ptr _context ): m_assembly(_assembly), @@ -113,7 +112,6 @@ CodeTransform::CodeTransform( m_evm15(_evm15), m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions), m_identifierAccess(_identifierAccess), - m_stackAdjustment(_stackAdjustment), m_context(_context) { if (!m_context) @@ -159,7 +157,6 @@ void CodeTransform::freeUnusedVariables() { yulAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), ""); m_assembly.appendInstruction(evmasm::Instruction::POP); - --m_stackAdjustment; } } @@ -178,11 +175,11 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) yulAssert(m_scope, ""); int const numVariables = _varDecl.variables.size(); - int height = m_assembly.stackHeight(); + int heightAtStart = m_assembly.stackHeight(); if (_varDecl.value) { std::visit(*this, *_varDecl.value); - expectDeposit(numVariables, height); + expectDeposit(numVariables, heightAtStart); } else { @@ -196,7 +193,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) { YulString varName = _varDecl.variables[varIndex].name; auto& var = std::get(m_scope->identifiers.at(varName)); - m_context->variableStackHeights[&var] = height + varIndex; + m_context->variableStackHeights[&var] = heightAtStart + varIndex; if (!m_allowStackOpt) continue; @@ -207,7 +204,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) m_context->variableStackHeights.erase(&var); m_assembly.setSourceLocation(_varDecl.location); m_assembly.appendInstruction(evmasm::Instruction::POP); - --m_stackAdjustment; } else m_variablesScheduledForDeletion.insert(&var); @@ -223,10 +219,8 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) if (int heightDiff = variableHeightDiff(var, varName, true)) m_assembly.appendInstruction(evmasm::swapInstruction(heightDiff - 1)); m_assembly.appendInstruction(evmasm::Instruction::POP); - --m_stackAdjustment; } } - checkStackHeight(&_varDecl); } void CodeTransform::stackError(StackTooDeepError _error, int _targetStackHeight) @@ -249,14 +243,12 @@ void CodeTransform::operator()(Assignment const& _assignment) m_assembly.setSourceLocation(_assignment.location); generateMultiAssignment(_assignment.variableNames); - checkStackHeight(&_assignment); } void CodeTransform::operator()(ExpressionStatement const& _statement) { m_assembly.setSourceLocation(_statement.location); std::visit(*this, _statement.expression); - checkStackHeight(&_statement); } void CodeTransform::operator()(FunctionCall const& _call) @@ -279,7 +271,6 @@ void CodeTransform::operator()(FunctionCall const& _call) { returnLabel = m_assembly.newLabelId(); m_assembly.appendLabelReference(returnLabel); - m_stackAdjustment++; } Scope::Function* function = nullptr; @@ -298,9 +289,7 @@ void CodeTransform::operator()(FunctionCall const& _call) { m_assembly.appendJumpTo(functionEntryID(_call.functionName.name, *function), function->returns.size() - function->arguments.size() - 1); m_assembly.appendLabel(returnLabel); - m_stackAdjustment--; } - checkStackHeight(&_call); } } @@ -334,15 +323,12 @@ void CodeTransform::operator()(Identifier const& _identifier) "Identifier not found and no external access available." ); m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_assembly); - checkStackHeight(&_identifier); } void CodeTransform::operator()(Literal const& _literal) { m_assembly.setSourceLocation(_literal.location); m_assembly.appendConstant(valueOfLiteral(_literal)); - - checkStackHeight(&_literal); } void CodeTransform::operator()(If const& _if) @@ -355,7 +341,6 @@ void CodeTransform::operator()(If const& _if) (*this)(_if.body); m_assembly.setSourceLocation(_if.location); m_assembly.appendLabel(end); - checkStackHeight(&_if); } void CodeTransform::operator()(Switch const& _switch) @@ -403,7 +388,6 @@ void CodeTransform::operator()(Switch const& _switch) m_assembly.setSourceLocation(_switch.location); m_assembly.appendLabel(end); m_assembly.appendInstruction(evmasm::Instruction::POP); - checkStackHeight(&_switch); } void CodeTransform::operator()(FunctionDefinition const& _function) @@ -412,8 +396,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) yulAssert(m_scope->identifiers.count(_function.name), ""); Scope::Function& function = std::get(m_scope->identifiers.at(_function.name)); - int const localStackAdjustment = m_evm15 ? 0 : 1; - int height = localStackAdjustment; + int height = m_evm15 ? 0 : 1; yulAssert(m_info.scopes.at(&_function.body), ""); Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); yulAssert(varScope, ""); @@ -433,8 +416,6 @@ void CodeTransform::operator()(FunctionDefinition const& _function) m_assembly.setStackHeight(height); - m_stackAdjustment += localStackAdjustment; - for (auto const& v: _function.returnVariables) { auto& var = std::get(varScope->identifiers.at(v.name)); @@ -458,7 +439,6 @@ void CodeTransform::operator()(FunctionDefinition const& _function) m_evm15, m_identifierAccess, m_useNamedLabelsForFunctions, - localStackAdjustment, m_context )(_function.body); } @@ -527,8 +507,6 @@ void CodeTransform::operator()(FunctionDefinition const& _function) m_assembly.appendReturnsub(_function.returnVariables.size(), stackHeightBefore); else m_assembly.appendJump(stackHeightBefore - _function.returnVariables.size()); - m_stackAdjustment -= localStackAdjustment; - checkStackHeight(&_function); m_assembly.setStackHeight(stackHeightBefore); } @@ -569,7 +547,6 @@ void CodeTransform::operator()(ForLoop const& _forLoop) finalizeBlock(_forLoop.pre, stackStartHeight); m_context->forLoopStack.pop(); m_scope = originalScope; - checkStackHeight(&_forLoop); } int CodeTransform::appendPopUntil(int _targetDepth) @@ -587,8 +564,6 @@ void CodeTransform::operator()(Break const& _break) Context::JumpInfo const& jump = m_context->forLoopStack.top().done; m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); - - checkStackHeight(&_break); } void CodeTransform::operator()(Continue const& _continue) @@ -598,8 +573,6 @@ void CodeTransform::operator()(Continue const& _continue) Context::JumpInfo const& jump = m_context->forLoopStack.top().post; m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); - - checkStackHeight(&_continue); } void CodeTransform::operator()(Leave const& _leaveStatement) @@ -609,8 +582,6 @@ void CodeTransform::operator()(Leave const& _leaveStatement) Context::JumpInfo const& jump = m_context->functionExitPoints.top(); m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); - - checkStackHeight(&_leaveStatement); } void CodeTransform::operator()(Block const& _block) @@ -693,7 +664,6 @@ void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight { yulAssert(!m_context->variableStackHeights.count(&var), ""); yulAssert(!m_context->variableReferences.count(&var), ""); - m_stackAdjustment++; } else m_assembly.appendInstruction(evmasm::Instruction::POP); @@ -701,7 +671,6 @@ void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight int deposit = m_assembly.stackHeight() - blockStartStackHeight; yulAssert(deposit == 0, "Invalid stack height at end of block: " + to_string(deposit)); - checkStackHeight(&_block); } void CodeTransform::generateMultiAssignment(vector const& _variableNames) @@ -758,16 +727,3 @@ void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const yulAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); } -void CodeTransform::checkStackHeight(void const* _astElement) const -{ - yulAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); - int stackHeightInAnalysis = m_info.stackHeightInfo.at(_astElement); - int stackHeightInCodegen = m_assembly.stackHeight() - m_stackAdjustment; - yulAssert( - stackHeightInAnalysis == stackHeightInCodegen, - "Stack height mismatch between analysis and code generation phase: Analysis: " + - to_string(stackHeightInAnalysis) + - " code gen: " + - to_string(stackHeightInCodegen) - ); -} diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index fb50be95fd70..fb4bc38e128a 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -134,7 +134,6 @@ class CodeTransform _evm15, _identifierAccess, _useNamedLabelsForFunctions, - _assembly.stackHeight(), nullptr ) { @@ -155,7 +154,6 @@ class CodeTransform bool _evm15, ExternalIdentifierAccess const& _identifierAccess, bool _useNamedLabelsForFunctions, - int _stackAdjustment, std::shared_ptr _context ); @@ -206,8 +204,6 @@ class CodeTransform void expectDeposit(int _deposit, int _oldHeight) const; - void checkStackHeight(void const* _astElement) const; - /// Stores the stack error in the list of errors, appends an invalid opcode /// and corrects the stack height to the target stack height. void stackError(StackTooDeepError _error, int _targetStackSize); @@ -225,11 +221,6 @@ class CodeTransform bool const m_evm15 = false; bool const m_useNamedLabelsForFunctions = false; ExternalIdentifierAccess m_identifierAccess; - /// Adjustment between the stack height as determined during the analysis phase - /// and the stack height in the assembly. This is caused by an initial stack being present - /// for inline assembly and different stack heights depending on the EVM backend used - /// (EVM 1.0 or 1.5). - int m_stackAdjustment = 0; std::shared_ptr m_context; /// Set of variables whose reference counter has reached zero, From 4a87f6e4035e9feece7cd228fd82a389965af764 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Mon, 27 Jan 2020 11:39:54 +0100 Subject: [PATCH 010/160] Removes the binary option from JSON AST extraction script. --- scripts/splitSources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/splitSources.py b/scripts/splitSources.py index c14e5bf7bd4e..d99ffb07254a 100755 --- a/scripts/splitSources.py +++ b/scripts/splitSources.py @@ -48,7 +48,7 @@ def writeSourceToFile(lines): if __name__ == '__main__': filePath = sys.argv[1] # decide if file has multiple sources - lines = open(filePath, mode='rb', encoding='utf8').read().splitlines() + lines = open(filePath, mode='r', encoding='utf8').read().splitlines() if lines[0][:12] == "==== Source:": hasMultipleSources = True writeSourceToFile(lines) From a2f28f0a9d1c35d8525739cf3c9531757667f138 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Tue, 28 Jan 2020 16:38:20 +0100 Subject: [PATCH 011/160] Fixes copyright in readthedocs config. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 87a6ec030954..cca7478d073a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -53,7 +53,7 @@ def setup(sphinx): # General information about the project. project = 'Solidity' -copyright = '2016-2019, Ethereum' +copyright = '2016-2020, 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 From 2f1f8e25c1929df1fa273e7f2898f40924aefd03 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 14 Jan 2020 15:55:31 +0100 Subject: [PATCH 012/160] TestFramework: Remove headers from Options.h --- test/ExecutionFramework.h | 2 ++ test/Options.cpp | 1 + test/Options.h | 5 ----- test/liblangutil/CharStream.cpp | 2 ++ test/liblangutil/SourceLocation.cpp | 2 ++ test/libsolidity/ASTJSONTest.cpp | 1 + test/libsolidity/GasTest.cpp | 2 ++ test/libsolidity/InlineAssembly.cpp | 1 + test/libsolidity/Metadata.cpp | 2 ++ test/libsolidity/SMTCheckerJSONTest.cpp | 1 + test/libsolidity/SemVerMatcher.cpp | 2 ++ test/libsolidity/SolidityCompiler.cpp | 2 ++ test/libsolidity/SolidityExpressionCompiler.cpp | 2 ++ test/libsolidity/SolidityNatspecJSON.cpp | 2 ++ test/libsolidity/SolidityParser.cpp | 2 ++ test/libsolidity/SyntaxTest.cpp | 1 + test/libsolutil/Checksum.cpp | 3 ++- test/libsolutil/CommonData.cpp | 2 ++ test/libsolutil/IndentedWriter.cpp | 2 ++ test/libsolutil/IpfsHash.cpp | 2 ++ test/libsolutil/IterateReplacing.cpp | 2 ++ test/libsolutil/JSON.cpp | 2 ++ test/libsolutil/StringUtils.cpp | 2 ++ test/libsolutil/SwarmHash.cpp | 2 ++ test/libsolutil/UTF8.cpp | 2 ++ test/libsolutil/Whiskers.cpp | 2 ++ test/libyul/CompilabilityChecker.cpp | 1 + test/libyul/Metrics.cpp | 1 + test/libyul/ObjectParser.cpp | 1 + test/libyul/Parser.cpp | 1 + test/libyul/StackReuseCodegen.cpp | 2 ++ 31 files changed, 51 insertions(+), 6 deletions(-) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 33301b6f2887..7cf1de82fefd 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -34,6 +34,8 @@ #include +#include + namespace solidity::test { class EVMHost; diff --git a/test/Options.cpp b/test/Options.cpp index 7498dc8c65d1..cb5db64b6a91 100644 --- a/test/Options.cpp +++ b/test/Options.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace std; using namespace solidity::test; diff --git a/test/Options.h b/test/Options.h index 79e2c9920d4e..13f55e018383 100644 --- a/test/Options.h +++ b/test/Options.h @@ -17,13 +17,8 @@ #pragma once -#include #include -#include -#include -#include -#include #include diff --git a/test/liblangutil/CharStream.cpp b/test/liblangutil/CharStream.cpp index fda026532b24..8c8eaca73509 100644 --- a/test/liblangutil/CharStream.cpp +++ b/test/liblangutil/CharStream.cpp @@ -25,6 +25,8 @@ #include +#include + namespace solidity::langutil::test { diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index f1808b22ac36..16aa0bab42ef 100644 --- a/test/liblangutil/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -24,6 +24,8 @@ #include +#include + namespace solidity::langutil::test { diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index 926399352f8b..3d035d608e51 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index c29608b5c94c..6cb5f6cceff2 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 8d55f9a08b1b..4b906e475408 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index e8bf69348b39..a8930a76fa4d 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -27,6 +27,8 @@ #include #include +#include + using namespace std; namespace solidity::frontend::test diff --git a/test/libsolidity/SMTCheckerJSONTest.cpp b/test/libsolidity/SMTCheckerJSONTest.cpp index cee5b560a5f2..42d29fa4034e 100644 --- a/test/libsolidity/SMTCheckerJSONTest.cpp +++ b/test/libsolidity/SMTCheckerJSONTest.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 9b344a435806..82cc07367228 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -27,6 +27,8 @@ #include #include +#include + using namespace std; using namespace solidity::langutil; diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index 1420335568a2..117a41187182 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -22,6 +22,8 @@ #include #include +#include + using namespace std; namespace solidity::frontend::test diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 1a7fa4ce8bbc..56ca6d51dd31 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -33,6 +33,8 @@ #include #include +#include + using namespace std; using namespace solidity::evmasm; using namespace solidity::langutil; diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 965bfc7c8869..24aec0547edd 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -27,6 +27,8 @@ #include #include +#include + using namespace solidity::langutil; namespace solidity::frontend::test diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 6a0c9efbdc0b..58d1a069123c 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -29,6 +29,8 @@ #include #include +#include + using namespace std; using namespace solidity::langutil; diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index a3322fdb2c0d..dc764c6b225e 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/test/libsolutil/Checksum.cpp b/test/libsolutil/Checksum.cpp index e813f7bfb254..bd6abad31c44 100644 --- a/test/libsolutil/Checksum.cpp +++ b/test/libsolutil/Checksum.cpp @@ -21,9 +21,10 @@ #include #include - #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libsolutil/CommonData.cpp b/test/libsolutil/CommonData.cpp index 63bd6bbd7a00..5bd29c056b0c 100644 --- a/test/libsolutil/CommonData.cpp +++ b/test/libsolutil/CommonData.cpp @@ -25,6 +25,8 @@ #include +#include + using namespace std; using namespace solidity::frontend; diff --git a/test/libsolutil/IndentedWriter.cpp b/test/libsolutil/IndentedWriter.cpp index 3f7c31c02967..fcdb096dd05e 100644 --- a/test/libsolutil/IndentedWriter.cpp +++ b/test/libsolutil/IndentedWriter.cpp @@ -22,6 +22,8 @@ #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libsolutil/IpfsHash.cpp b/test/libsolutil/IpfsHash.cpp index bb82f7b5cd48..adaf30d611ae 100644 --- a/test/libsolutil/IpfsHash.cpp +++ b/test/libsolutil/IpfsHash.cpp @@ -22,6 +22,8 @@ #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libsolutil/IterateReplacing.cpp b/test/libsolutil/IterateReplacing.cpp index ebd82c9c7c60..3270baf4739d 100644 --- a/test/libsolutil/IterateReplacing.cpp +++ b/test/libsolutil/IterateReplacing.cpp @@ -22,6 +22,8 @@ #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libsolutil/JSON.cpp b/test/libsolutil/JSON.cpp index 176d3027a464..32d41144a4ea 100644 --- a/test/libsolutil/JSON.cpp +++ b/test/libsolutil/JSON.cpp @@ -23,6 +23,8 @@ #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libsolutil/StringUtils.cpp b/test/libsolutil/StringUtils.cpp index 3b977e0f608f..b2e1d5955799 100644 --- a/test/libsolutil/StringUtils.cpp +++ b/test/libsolutil/StringUtils.cpp @@ -26,6 +26,8 @@ #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libsolutil/SwarmHash.cpp b/test/libsolutil/SwarmHash.cpp index 03c5af5f7737..1c2ff917ed4e 100644 --- a/test/libsolutil/SwarmHash.cpp +++ b/test/libsolutil/SwarmHash.cpp @@ -24,6 +24,8 @@ #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libsolutil/UTF8.cpp b/test/libsolutil/UTF8.cpp index 8bb49e9a4875..86ec4fe9310b 100644 --- a/test/libsolutil/UTF8.cpp +++ b/test/libsolutil/UTF8.cpp @@ -23,6 +23,8 @@ #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libsolutil/Whiskers.cpp b/test/libsolutil/Whiskers.cpp index 293ffedfddef..11754711f54c 100644 --- a/test/libsolutil/Whiskers.cpp +++ b/test/libsolutil/Whiskers.cpp @@ -22,6 +22,8 @@ #include +#include + using namespace std; namespace solidity::util::test diff --git a/test/libyul/CompilabilityChecker.cpp b/test/libyul/CompilabilityChecker.cpp index f8772a1c11c3..23fa82cd71f3 100644 --- a/test/libyul/CompilabilityChecker.cpp +++ b/test/libyul/CompilabilityChecker.cpp @@ -25,6 +25,7 @@ #include +#include using namespace std; diff --git a/test/libyul/Metrics.cpp b/test/libyul/Metrics.cpp index f5e3ec7abfa4..7d1abee58eb7 100644 --- a/test/libyul/Metrics.cpp +++ b/test/libyul/Metrics.cpp @@ -25,6 +25,7 @@ #include #include +#include using namespace std; using namespace solidity::langutil; diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 72ddb02de274..c8756eafb558 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 749ec5ea5e71..e278383a525d 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include diff --git a/test/libyul/StackReuseCodegen.cpp b/test/libyul/StackReuseCodegen.cpp index 574ed9462061..b9d3334848d9 100644 --- a/test/libyul/StackReuseCodegen.cpp +++ b/test/libyul/StackReuseCodegen.cpp @@ -23,6 +23,8 @@ #include #include +#include + using namespace std; namespace solidity::yul::test From b8e2baf5f4f630da6151371843dcfa8b7d357524 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 19 Dec 2019 12:13:48 +0000 Subject: [PATCH 013/160] Use yul::AstWalker to resolve assembly symbols --- libsolidity/analysis/ReferencesResolver.cpp | 169 +++++++++--------- libsolidity/analysis/ReferencesResolver.h | 13 +- .../ASTJSON/assembly/nested_functions.json | 166 +++++++++++++++++ .../ASTJSON/assembly/nested_functions.sol | 12 ++ .../assembly/nested_functions_legacy.json | 148 +++++++++++++++ test/libsolidity/ASTJSON/assembly/switch.json | 18 +- .../ASTJSON/assembly/switch_legacy.json | 15 +- .../invalid/nested_function_local_access.sol | 12 ++ 8 files changed, 469 insertions(+), 84 deletions(-) create mode 100644 test/libsolidity/ASTJSON/assembly/nested_functions.json create mode 100644 test/libsolidity/ASTJSON/assembly/nested_functions.sol create mode 100644 test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/nested_function_local_access.sol diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 889ec4d2d0f6..6765f67103bb 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -270,87 +270,10 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) { m_resolver.warnVariablesNamedLikeInstructions(); - // Errors created in this stage are completely ignored because we do not yet know - // the type and size of external identifiers, which would result in false errors. - // The only purpose of this step is to fill the inline assembly annotation with - // external references. - ErrorList errors; - ErrorReporter errorsIgnored(errors); - yul::ExternalIdentifierAccess::Resolver resolver = - [&](yul::Identifier const& _identifier, yul::IdentifierContext _context, bool _crossesFunctionBoundary) { - bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); - bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); - if (_context == yul::IdentifierContext::VariableDeclaration) - { - string namePrefix = _identifier.name.str().substr(0, _identifier.name.str().find('.')); - if (isSlot || isOffset) - declarationError(_identifier.location, "In variable declarations _slot and _offset can not be used as a suffix."); - else if ( - auto declarations = m_resolver.nameFromCurrentScope(namePrefix); - !declarations.empty() - ) - { - SecondarySourceLocation ssl; - for (auto const* decl: declarations) - ssl.append("The shadowed declaration is here:", decl->location()); - if (!ssl.infos.empty()) - declarationError( - _identifier.location, - ssl, - namePrefix.size() < _identifier.name.str().size() ? - "The prefix of this declaration conflicts with a declaration outside the inline assembly block." : - "This declaration shadows a declaration outside the inline assembly block." - ); - } - return size_t(-1); - } - auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); - if (isSlot || isOffset) - { - // special mode to access storage variables - if (!declarations.empty()) - // the special identifier exists itself, we should not allow that. - return size_t(-1); - string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - ( - isSlot ? - string("_slot").size() : - string("_offset").size() - )); - if (realName.empty()) - { - declarationError(_identifier.location, "In variable names _slot and _offset can only be used as a suffix."); - return size_t(-1); - } - declarations = m_resolver.nameFromCurrentScope(realName); - } - if (declarations.size() > 1) - { - declarationError(_identifier.location, "Multiple matching identifiers. Resolving overloaded identifiers is not supported."); - return size_t(-1); - } - else if (declarations.size() == 0) - return size_t(-1); - if (auto var = dynamic_cast(declarations.front())) - if (var->isLocalVariable() && _crossesFunctionBoundary) - { - declarationError(_identifier.location, "Cannot access local Solidity variables from inside an inline assembly function."); - return size_t(-1); - } - _inlineAssembly.annotation().externalReferences[&_identifier].isSlot = isSlot; - _inlineAssembly.annotation().externalReferences[&_identifier].isOffset = isOffset; - _inlineAssembly.annotation().externalReferences[&_identifier].declaration = declarations.front(); - return size_t(1); - }; - - // Will be re-generated later with correct information - // We use the latest EVM version because we will re-run it anyway. - yul::AsmAnalysisInfo analysisInfo; - yul::AsmAnalyzer( - analysisInfo, - errorsIgnored, - yul::EVMDialect::strictAssemblyForEVM(m_evmVersion), - resolver - ).analyze(_inlineAssembly.operations()); + m_yulAnnotation = &_inlineAssembly.annotation(); + (*this)(_inlineAssembly.operations()); + m_yulAnnotation = nullptr; + return false; } @@ -468,6 +391,90 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) _variable.annotation().type = type; } +void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) +{ + bool wasInsideFunction = m_yulInsideFunction; + m_yulInsideFunction = true; + this->operator()(_function.body); + m_yulInsideFunction = wasInsideFunction; +} + +void ReferencesResolver::operator()(yul::Identifier const& _identifier) +{ + bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); + bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); + + auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); + if (isSlot || isOffset) + { + // special mode to access storage variables + if (!declarations.empty()) + // the special identifier exists itself, we should not allow that. + return; + string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - ( + isSlot ? + string("_slot").size() : + string("_offset").size() + )); + if (realName.empty()) + { + declarationError(_identifier.location, "In variable names _slot and _offset can only be used as a suffix."); + return; + } + declarations = m_resolver.nameFromCurrentScope(realName); + } + if (declarations.size() > 1) + { + declarationError(_identifier.location, "Multiple matching identifiers. Resolving overloaded identifiers is not supported."); + return; + } + else if (declarations.size() == 0) + return; + if (auto var = dynamic_cast(declarations.front())) + if (var->isLocalVariable() && m_yulInsideFunction) + { + declarationError(_identifier.location, "Cannot access local Solidity variables from inside an inline assembly function."); + return; + } + + m_yulAnnotation->externalReferences[&_identifier].isSlot = isSlot; + m_yulAnnotation->externalReferences[&_identifier].isOffset = isOffset; + m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front(); +} + +void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) +{ + for (auto const& identifier: _varDecl.variables) + { + bool isSlot = boost::algorithm::ends_with(identifier.name.str(), "_slot"); + bool isOffset = boost::algorithm::ends_with(identifier.name.str(), "_offset"); + + string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.')); + if (isSlot || isOffset) + declarationError(identifier.location, "In variable declarations _slot and _offset can not be used as a suffix."); + else if ( + auto declarations = m_resolver.nameFromCurrentScope(namePrefix); + !declarations.empty() + ) + { + SecondarySourceLocation ssl; + for (auto const* decl: declarations) + ssl.append("The shadowed declaration is here:", decl->location()); + if (!ssl.infos.empty()) + declarationError( + identifier.location, + ssl, + namePrefix.size() < identifier.name.str().size() ? + "The prefix of this declaration conflicts with a declaration outside the inline assembly block." : + "This declaration shadows a declaration outside the inline assembly block." + ); + } + } + + if (_varDecl.value) + visit(*_varDecl.value); +} + void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description) { m_errorOccurred = true; diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index a368f7d39252..2bbce69d0f31 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,7 @@ class NameAndTypeResolver; * Resolves references to declarations (of variables and types) and also establishes the link * between a return statement and the return parameter list. */ -class ReferencesResolver: private ASTConstVisitor +class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker { public: ReferencesResolver( @@ -64,6 +65,9 @@ class ReferencesResolver: private ASTConstVisitor bool resolve(ASTNode const& _root); private: + using yul::ASTWalker::visit; + using yul::ASTWalker::operator(); + bool visit(Block const& _block) override; void endVisit(Block const& _block) override; bool visit(ForStatement const& _for) override; @@ -83,6 +87,10 @@ class ReferencesResolver: private ASTConstVisitor bool visit(Return const& _return) override; void endVisit(VariableDeclaration const& _variable) override; + void operator()(yul::FunctionDefinition const& _function) override; + void operator()(yul::Identifier const& _identifier) override; + void operator()(yul::VariableDeclaration const& _varDecl) override; + /// Adds a new error to the list of errors. void typeError(langutil::SourceLocation const& _location, std::string const& _description); @@ -105,6 +113,9 @@ class ReferencesResolver: private ASTConstVisitor std::vector m_returnParameters; bool const m_resolveInsideCode; bool m_errorOccurred = false; + + InlineAssemblyAnnotation* m_yulAnnotation = nullptr; + bool m_yulInsideFunction = false; }; } diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions.json b/test/libsolidity/ASTJSON/assembly/nested_functions.json new file mode 100644 index 000000000000..b990a963fb66 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/nested_functions.json @@ -0,0 +1,166 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 8 + ] + }, + "id": 9, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 8, + "linearizedBaseContracts": + [ + 8 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 6, + "nodeType": "Block", + "src": "57:97:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "72:78:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "94:50:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "118:3:1", + "statements": [] + }, + "name": "f2", + "nodeType": "YulFunctionDefinition", + "src": "104:17:1" + }, + { + "nodeType": "YulAssignment", + "src": "130:6:1", + "value": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "135:1:1", + "type": "", + "value": "2" + }, + "variableNames": + [ + { + "name": "x", + "nodeType": "YulIdentifier", + "src": "130:1:1" + } + ] + } + ] + }, + "name": "f1", + "nodeType": "YulFunctionDefinition", + "src": "80:64:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 5, + "nodeType": "InlineAssembly", + "src": "63:87:1" + } + ] + }, + "documentation": null, + "functionSelector": "26121ff0", + "id": 7, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "overrides": null, + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "25:2:1" + }, + "returnParameters": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 3, + "name": "x", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 7, + "src": "49:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "49:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "48:8:1" + }, + "scope": 8, + "src": "15:139:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "scope": 9, + "src": "0:156:1" + } + ], + "src": "0:157:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions.sol b/test/libsolidity/ASTJSON/assembly/nested_functions.sol new file mode 100644 index 000000000000..412dd05a4863 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/nested_functions.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure returns (uint x) { + assembly { + function f1() { + function f2() { } + x := 2 + } + } + } +} + +// ---- diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json new file mode 100644 index 000000000000..abaf60fb73e6 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json @@ -0,0 +1,148 @@ +{ + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 8 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 8 + ], + "name": "C", + "scope": 9 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "functionSelector": "26121ff0", + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "f", + "overrides": null, + "scope": 8, + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "25:2:1" + }, + { + "children": + [ + { + "attributes": + { + "constant": false, + "name": "x", + "overrides": null, + "scope": 7, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "name": "uint", + "type": "uint256" + }, + "id": 2, + "name": "ElementaryTypeName", + "src": "49:4:1" + } + ], + "id": 3, + "name": "VariableDeclaration", + "src": "49:6:1" + } + ], + "id": 4, + "name": "ParameterList", + "src": "48:8:1" + }, + { + "children": + [ + { + "attributes": + { + "evmVersion": %EVMVERSION%, + "externalReferences": + [ + null + ], + "operations": "{\n function f1()\n {\n function f2()\n { }\n x := 2\n }\n}" + }, + "children": [], + "id": 5, + "name": "InlineAssembly", + "src": "63:87:1" + } + ], + "id": 6, + "name": "Block", + "src": "57:97:1" + } + ], + "id": 7, + "name": "FunctionDefinition", + "src": "15:139:1" + } + ], + "id": 8, + "name": "ContractDefinition", + "src": "0:156:1" + } + ], + "id": 9, + "name": "SourceUnit", + "src": "0:157:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/switch.json b/test/libsolidity/ASTJSON/assembly/switch.json index 5e16f8cfbcea..854c5b6cdce0 100644 --- a/test/libsolidity/ASTJSON/assembly/switch.json +++ b/test/libsolidity/ASTJSON/assembly/switch.json @@ -158,7 +158,23 @@ ] }, "evmVersion": %EVMVERSION%, - "externalReferences": [], + "externalReferences": + [ + { + "declaration": 5, + "isOffset": false, + "isSlot": false, + "src": "141:1:1", + "valueSize": 18446744073709551615 + }, + { + "declaration": 5, + "isOffset": false, + "isSlot": false, + "src": "172:1:1", + "valueSize": 18446744073709551615 + } + ], "id": 3, "nodeType": "InlineAssembly", "src": "52:138:1" diff --git a/test/libsolidity/ASTJSON/assembly/switch_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_legacy.json index 9d055e52e2f2..5b9322f4c97d 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/switch_legacy.json @@ -92,7 +92,20 @@ "evmVersion": %EVMVERSION%, "externalReferences": [ - null + { + "declaration": 5, + "isOffset": false, + "isSlot": false, + "src": "141:1:1", + "valueSize": 18446744073709551615 + }, + { + "declaration": 5, + "isOffset": false, + "isSlot": false, + "src": "172:1:1", + "valueSize": 18446744073709551615 + } ], "operations": "{\n let f := 0\n switch calldatasize()\n case 0 { f := 1 }\n default { f := 2 }\n}" }, diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/nested_function_local_access.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/nested_function_local_access.sol new file mode 100644 index 000000000000..3bb6223a88f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/nested_function_local_access.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure returns (uint x) { + assembly { + function f1() { + function f2() { } + x := 2 + } + } + } +} +// ---- +// DeclarationError: (130-131): Cannot access local Solidity variables from inside an inline assembly function. From dc298886010120078fe86fdf24796f344dce54b4 Mon Sep 17 00:00:00 2001 From: Nicolas <32513365+nicos99@users.noreply.github.com> Date: Wed, 29 Jan 2020 08:13:53 +0100 Subject: [PATCH 014/160] =?UTF-8?q?fix=20parenthesis=20error=20in=20=C2=A7?= =?UTF-8?q?=20"Semantic=20and=20Syntactic=20Changes"=20of=20v0.5.0=20Break?= =?UTF-8?q?ing=20Changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/050-breaking-changes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 497936f31078..93238e189927 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -63,7 +63,7 @@ This section highlights changes that affect syntax and semantics. last one only works for value types). Change every ``keccak256(a, b, c)`` to ``keccak256(abi.encodePacked(a, b, c))``. Even though it is not a breaking change, it is suggested that developers change - ``x.call(bytes4(keccak256("f(uint256)"), a, b)`` to + ``x.call(bytes4(keccak256("f(uint256)")), a, b)`` to ``x.call(abi.encodeWithSignature("f(uint256)", a, b))``. * Functions ``.call()``, ``.delegatecall()`` and ``.staticcall()`` now return From 49514bc577b7aedea154a4c298d08f9722cf47d0 Mon Sep 17 00:00:00 2001 From: Nicolas <32513365+nicos99@users.noreply.github.com> Date: Wed, 29 Jan 2020 08:21:48 +0100 Subject: [PATCH 015/160] =?UTF-8?q?fix=20a=20logical=20contradiction=20in?= =?UTF-8?q?=20new=20version=20of=20=C2=A7=20Example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "if (x > 100) throw;" --> "require(x <= 100);" --- docs/050-breaking-changes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 93238e189927..ede846cc399c 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -455,7 +455,7 @@ New version: uint z = someInteger(); x += z; // Throw is now disallowed. - require(x > 100); + require(x <= 100); int y = -3 >> 1; require(y == -2); do { From a6dfb6a4ef7348c7ffa4566a8e3adec85bfc5be0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Jan 2020 09:10:09 +0100 Subject: [PATCH 016/160] Changelog entry about zeroing out memory. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index e503591274b9..dbf923afbc45 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: + * Code Generator: Use ``calldatacopy`` past input to zero out memory instead of ``codecopy``. Bugfixes: From 00e4d139754acc88a0b4ed775ecc53604271ba97 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 14 Jan 2020 17:48:17 +0100 Subject: [PATCH 017/160] TestFramework: Merge Options.h into Common.h --- test/CMakeLists.txt | 2 - test/Common.cpp | 23 ++++++- test/Common.h | 11 +++- test/ExecutionFramework.cpp | 8 +-- test/ExecutionFramework.h | 2 +- test/Options.cpp | 61 ------------------- test/Options.h | 39 ------------ test/boostTest.cpp | 26 +++++--- test/contracts/Wallet.cpp | 2 +- test/libevmasm/Optimiser.cpp | 12 ++-- test/liblangutil/CharStream.cpp | 2 +- test/liblangutil/SourceLocation.cpp | 2 +- test/libsolidity/ABIDecoderTests.cpp | 8 +-- test/libsolidity/ABIEncoderTests.cpp | 4 +- test/libsolidity/ABIJsonTest.cpp | 6 +- test/libsolidity/ASTJSONTest.cpp | 8 +-- test/libsolidity/AnalysisFramework.cpp | 4 +- test/libsolidity/Assembly.cpp | 16 ++--- test/libsolidity/GasCosts.cpp | 10 +-- test/libsolidity/GasMeter.cpp | 14 ++--- test/libsolidity/GasTest.cpp | 2 +- test/libsolidity/Imports.cpp | 12 ++-- test/libsolidity/InlineAssembly.cpp | 22 +++---- test/libsolidity/Metadata.cpp | 22 +++---- test/libsolidity/SMTChecker.cpp | 6 +- test/libsolidity/SMTCheckerJSONTest.cpp | 2 +- test/libsolidity/SMTCheckerTest.cpp | 2 +- test/libsolidity/SemVerMatcher.cpp | 2 +- test/libsolidity/SemanticTest.cpp | 2 +- test/libsolidity/SolidityCompiler.cpp | 4 +- test/libsolidity/SolidityEndToEndTest.cpp | 52 ++++++++-------- .../SolidityExecutionFramework.cpp | 2 +- .../SolidityExpressionCompiler.cpp | 22 +++---- .../SolidityNameAndTypeResolution.cpp | 20 +++--- test/libsolidity/SolidityNatspecJSON.cpp | 6 +- test/libsolidity/SolidityParser.cpp | 4 +- test/libsolidity/SyntaxTest.cpp | 2 +- test/libsolidity/ViewPureChecker.cpp | 8 +-- test/libsolidity/util/TestFileParser.cpp | 2 +- test/libsolutil/Checksum.cpp | 2 +- test/libsolutil/CommonData.cpp | 2 +- test/libsolutil/IndentedWriter.cpp | 2 +- test/libsolutil/IpfsHash.cpp | 2 +- test/libsolutil/IterateReplacing.cpp | 2 +- test/libsolutil/JSON.cpp | 2 +- test/libsolutil/StringUtils.cpp | 2 +- test/libsolutil/SwarmHash.cpp | 2 +- test/libsolutil/UTF8.cpp | 2 +- test/libsolutil/Whiskers.cpp | 2 +- test/libyul/Common.cpp | 8 +-- test/libyul/CompilabilityChecker.cpp | 4 +- test/libyul/EwasmTranslationTest.cpp | 6 +- test/libyul/FunctionSideEffects.cpp | 2 +- test/libyul/Metrics.cpp | 2 +- test/libyul/ObjectParser.cpp | 6 +- test/libyul/Parser.cpp | 2 +- test/libyul/StackReuseCodegen.cpp | 2 +- test/libyul/YulInterpreterTest.cpp | 4 +- test/libyul/YulOptimizerTest.cpp | 6 +- test/tools/CMakeLists.txt | 1 - test/tools/isoltest.cpp | 28 +++++---- 61 files changed, 244 insertions(+), 301 deletions(-) delete mode 100644 test/Options.cpp delete mode 100644 test/Options.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a6b753087677..ce1c127397d5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,8 +9,6 @@ set(sources InteractiveTests.h Metadata.cpp Metadata.h - Options.cpp - Options.h TestCase.cpp TestCase.h ) diff --git a/test/Common.cpp b/test/Common.cpp index a8887166934c..709840bbcef9 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -15,6 +15,7 @@ along with solidity. If not, see . */ +#include #include #include @@ -91,7 +92,11 @@ CommonOptions::CommonOptions(std::string _caption): ("evm-version", po::value(&evmVersionString), "which evm version to use") ("testpath", po::value(&this->testPath)->default_value(solidity::test::testPath()), "path to test files") ("evmonepath", po::value(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library") - ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker"); + ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker") + ("optimize", po::bool_switch(&optimize), "enables optimization") + ("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization") + ("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2") + ("show-messages", po::bool_switch(&showMessages), "enables message output"); } void CommonOptions::validate() const @@ -151,4 +156,20 @@ langutil::EVMVersion CommonOptions::evmVersion() const return langutil::EVMVersion(); } + +CommonOptions const& CommonOptions::get() +{ + if (!m_singleton) + throw std::runtime_error("Options not yet constructed!"); + + return *m_singleton; +} + +void CommonOptions::setSingleton(std::unique_ptr&& _instance) +{ + m_singleton = std::move(_instance); +} + +std::unique_ptr CommonOptions::m_singleton = nullptr; + } diff --git a/test/Common.h b/test/Common.h index 88b75507a878..63437da13d9f 100644 --- a/test/Common.h +++ b/test/Common.h @@ -21,8 +21,8 @@ #include #include -#include #include +#include namespace solidity::test { @@ -48,6 +48,8 @@ struct CommonOptions: boost::noncopyable bool optimize = false; bool optimizeYul = false; bool disableSMT = false; + bool useABIEncoderV2 = false; + bool showMessages = false; langutil::EVMVersion evmVersion() const; @@ -55,13 +57,18 @@ struct CommonOptions: boost::noncopyable // Throws a ConfigException on error virtual void validate() const; -protected: + static CommonOptions const& get(); + static void setSingleton(std::unique_ptr&& _instance); + CommonOptions(std::string caption = ""); + virtual ~CommonOptions() {}; +protected: boost::program_options::options_description options; private: std::string evmVersionString; + static std::unique_ptr m_singleton; }; } diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 51d73284b82d..cc54e7a7b478 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -39,19 +39,19 @@ using namespace solidity::util; using namespace solidity::test; ExecutionFramework::ExecutionFramework(): - ExecutionFramework(solidity::test::Options::get().evmVersion()) + ExecutionFramework(solidity::test::CommonOptions::get().evmVersion()) { } ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion), m_optimiserSettings(solidity::frontend::OptimiserSettings::minimal()), - m_showMessages(solidity::test::Options::get().showMessages), + m_showMessages(solidity::test::CommonOptions::get().showMessages), m_evmHost(make_shared(m_evmVersion)) { - if (solidity::test::Options::get().optimizeYul) + if (solidity::test::CommonOptions::get().optimizeYul) m_optimiserSettings = solidity::frontend::OptimiserSettings::full(); - else if (solidity::test::Options::get().optimize) + else if (solidity::test::CommonOptions::get().optimize) m_optimiserSettings = solidity::frontend::OptimiserSettings::standard(); m_evmHost->reset(); diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 7cf1de82fefd..40e12da941cb 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include #include diff --git a/test/Options.cpp b/test/Options.cpp deleted file mode 100644 index cb5db64b6a91..000000000000 --- a/test/Options.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - 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 . -*/ -/** @file TestHelper.h -* @author Marko Simovic -* @date 2014 -*/ - -#include - -#include - -#include -#include - -#include -#include -#include -#include - -using namespace std; -using namespace solidity::test; -namespace fs = boost::filesystem; -namespace po = boost::program_options; - - -Options const& Options::get() -{ - static Options instance; - return instance; -} - - -Options::Options() -{ - auto const& suite = boost::unit_test::framework::master_test_suite(); - - if (suite.argc == 0) - return; - - options.add_options() - ("optimize", po::bool_switch(&optimize), "enables optimization") - ("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization") - ("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2") - ("show-messages", po::bool_switch(&showMessages), "enables message output"); - - parse(suite.argc, suite.argv); -} diff --git a/test/Options.h b/test/Options.h deleted file mode 100644 index 13f55e018383..000000000000 --- a/test/Options.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - 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 . -*/ - -#pragma once - -#include - - -#include - -namespace solidity::test -{ - -struct Options: CommonOptions -{ - bool showMessages = false; - bool useABIEncoderV2 = false; - - static Options const& get(); - -private: - Options(); -}; - -} diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 47b7003819e3..2b5606546eff 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -36,7 +36,7 @@ #pragma GCC diagnostic pop #include -#include +#include #include #include @@ -69,7 +69,7 @@ int registerTests( { int numTestsAdded = 0; fs::path fullpath = _basepath / _path; - TestCase::Config config{fullpath.string(), solidity::test::Options::get().evmVersion()}; + TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion()}; if (fs::is_directory(fullpath)) { test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); @@ -94,7 +94,7 @@ int registerTests( { stringstream errorStream; auto testCase = _testCaseCreator(config); - if (testCase->validateSettings(solidity::test::Options::get().evmVersion())) + if (testCase->validateSettings(solidity::test::CommonOptions::get().evmVersion())) switch (testCase->run(errorStream)) { case TestCase::TestResult::Success: @@ -121,15 +121,27 @@ int registerTests( } return numTestsAdded; } + +void initializeOptions() +{ + auto const& suite = boost::unit_test::framework::master_test_suite(); + + auto options = std::make_unique(); + solAssert(options->parse(suite.argc, suite.argv), "Failed to parse options!"); + options->validate(); + + solidity::test::CommonOptions::setSingleton(std::move(options)); +} } test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; - solidity::test::Options::get().validate(); - bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::Options::get().evmonePath.string()); + initializeOptions(); + + bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::CommonOptions::get().evmonePath.string()); if (disableSemantics) { cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using -- --evmonepath ." << endl; @@ -140,7 +152,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) // Include the interactive tests in the automatic tests as well for (auto const& ts: g_interactiveTestsuites) { - auto const& options = solidity::test::Options::get(); + auto const& options = solidity::test::CommonOptions::get(); if (ts.smt && options.disableSMT) continue; @@ -172,7 +184,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) removeTestSuite(suite); } - if (solidity::test::Options::get().disableSMT) + if (solidity::test::CommonOptions::get().disableSMT) removeTestSuite("SMTChecker"); return 0; diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 1ec71665a0fd..b692b795463e 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -464,7 +464,7 @@ BOOST_AUTO_TEST_CASE(creation) { deployWallet(200); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true)); - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(~0)) == (v2 ? encodeArgs() : encodeArgs(false))); } diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 0eaab67dacd7..7a3125bfdc58 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -20,7 +20,7 @@ * Tests for the Solidity optimizer. */ -#include +#include #include #include @@ -236,7 +236,7 @@ BOOST_AUTO_TEST_CASE(cse_associativity2) BOOST_AUTO_TEST_CASE(cse_double_shift_right_overflow) { - if (solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) { AssemblyItems input{ Instruction::CALLVALUE, @@ -251,7 +251,7 @@ BOOST_AUTO_TEST_CASE(cse_double_shift_right_overflow) BOOST_AUTO_TEST_CASE(cse_double_shift_left_overflow) { - if (solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) { AssemblyItems input{ Instruction::DUP1, @@ -1132,7 +1132,7 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies) main.append(t1.toSubAssemblyTag(subId)); main.append(u256(8)); - main.optimise(true, solidity::test::Options::get().evmVersion(), false, 200); + main.optimise(true, solidity::test::CommonOptions::get().evmVersion(), false, 200); AssemblyItems expectationMain{ AssemblyItem(PushSubSize, 0), @@ -1178,7 +1178,7 @@ BOOST_AUTO_TEST_CASE(cse_sub_zero) BOOST_AUTO_TEST_CASE(cse_remove_redundant_shift_masking) { - if (!solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; for (int i = 1; i < 256; i++) @@ -1326,7 +1326,7 @@ BOOST_AUTO_TEST_CASE(cse_remove_unwanted_masking_of_address) BOOST_AUTO_TEST_CASE(cse_replace_too_large_shift) { - if (!solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; checkCSE({ diff --git a/test/liblangutil/CharStream.cpp b/test/liblangutil/CharStream.cpp index 8c8eaca73509..3b9d980c1fc8 100644 --- a/test/liblangutil/CharStream.cpp +++ b/test/liblangutil/CharStream.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index 16aa0bab42ef..8fe4740fdd2e 100644 --- a/test/liblangutil/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -22,7 +22,7 @@ #include -#include +#include #include diff --git a/test/libsolidity/ABIDecoderTests.cpp b/test/libsolidity/ABIDecoderTests.cpp index c3bc548ec492..39bf7602c6ca 100644 --- a/test/libsolidity/ABIDecoderTests.cpp +++ b/test/libsolidity/ABIDecoderTests.cpp @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(enums) } } )"; - bool newDecoder = solidity::test::Options::get().useABIEncoderV2; + bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2; BOTH_ENCODERS( compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f(uint8)", 0), encodeArgs(u256(0))); @@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(cleanup) } } )"; - bool newDecoder = solidity::test::Options::get().useABIEncoderV2; + bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2; BOTH_ENCODERS( compileAndRun(sourceCode); ABI_CHECK( @@ -573,7 +573,7 @@ BOOST_AUTO_TEST_CASE(validation_function_type) function i(function () external[] calldata a) external pure returns (uint r) { a[0]; r = 4; } } )"; - bool newDecoder = solidity::test::Options::get().useABIEncoderV2; + bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2; string validFun{"01234567890123456789abcd"}; string invalidFun{"01234567890123456789abcdX"}; BOTH_ENCODERS( @@ -950,7 +950,7 @@ BOOST_AUTO_TEST_CASE(out_of_bounds_bool_value) function f(bool b) public pure returns (bool) { return b; } } )"; - bool newDecoder = solidity::test::Options::get().useABIEncoderV2; + bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2; BOTH_ENCODERS( compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(true)); diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 426a34018cee..b816de6fd211 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(memory_array_one_dim) } )"; - if (!solidity::test::Options::get().useABIEncoderV2) + if (!solidity::test::CommonOptions::get().useABIEncoderV2) { compileAndRun(sourceCode); callContractFunction("f()"); @@ -752,7 +752,7 @@ BOOST_AUTO_TEST_CASE(struct_in_constructor_indirect) } } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { NEW_ENCODER( compileAndRun(sourceCode, 0, "D"); diff --git a/test/libsolidity/ABIJsonTest.cpp b/test/libsolidity/ABIJsonTest.cpp index daaee026c48b..1da0193ab42d 100644 --- a/test/libsolidity/ABIJsonTest.cpp +++ b/test/libsolidity/ABIJsonTest.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include #include @@ -52,8 +52,8 @@ TestCase::TestResult ABIJsonTest::run(ostream& _stream, string const& _linePrefi CompilerStack compiler; compiler.setSources({{"", "pragma solidity >=0.0;\n" + m_source}}); - compiler.setEVMVersion(solidity::test::Options::get().evmVersion()); - compiler.setOptimiserSettings(solidity::test::Options::get().optimize); + compiler.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compiler.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); if (!compiler.parseAndAnalyze()) BOOST_THROW_EXCEPTION(runtime_error("Parsing contract failed")); diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index 3d035d608e51..fd65d98f8974 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include @@ -47,7 +47,7 @@ void replaceVersionWithTag(string& _input) { boost::algorithm::replace_all( _input, - "\"" + solidity::test::Options::get().evmVersion().name() + "\"", + "\"" + solidity::test::CommonOptions::get().evmVersion().name() + "\"", "%EVMVERSION%" ); } @@ -57,7 +57,7 @@ void replaceTagWithVersion(string& _input) boost::algorithm::replace_all( _input, "%EVMVERSION%", - "\"" + solidity::test::Options::get().evmVersion().name() + "\"" + "\"" + solidity::test::CommonOptions::get().evmVersion().name() + "\"" ); } @@ -129,7 +129,7 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi sourceIndices[m_sources[i].first] = i + 1; } c.setSources(sources); - c.setEVMVersion(solidity::test::Options::get().evmVersion()); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); if (c.parse()) c.analyze(); else diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index a76af108182b..be14d1cf149b 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include #include @@ -51,7 +51,7 @@ AnalysisFramework::parseAnalyseAndReturnError( { compiler().reset(); compiler().setSources({{"", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source}}); - compiler().setEVMVersion(solidity::test::Options::get().evmVersion()); + compiler().setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); compiler().setParserErrorRecovery(_allowRecoveryErrors); _allowMultipleErrors = _allowMultipleErrors || _allowRecoveryErrors; if (!compiler().parse()) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 12b295f8ce56..3c403f74ebee 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,7 +20,7 @@ * Unit tests for Assembly Items from evmasm/Assembly.h */ -#include +#include #include #include @@ -52,14 +52,14 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) { ErrorList errors; ErrorReporter errorReporter(errors); - Parser parser(errorReporter, solidity::test::Options::get().evmVersion()); + Parser parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()); ASTPointer sourceUnit; BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(_sourceCode))); BOOST_CHECK(!!sourceUnit); map> scopes; GlobalContext globalContext; - NameAndTypeResolver resolver(globalContext, solidity::test::Options::get().evmVersion(), scopes, errorReporter); + NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), scopes, errorReporter); solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->nodes()) @@ -72,7 +72,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - TypeChecker checker(solidity::test::Options::get().evmVersion(), errorReporter); + TypeChecker checker(solidity::test::CommonOptions::get().evmVersion(), errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); @@ -81,9 +81,9 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) if (ContractDefinition* contract = dynamic_cast(node.get())) { Compiler compiler( - solidity::test::Options::get().evmVersion(), + solidity::test::CommonOptions::get().evmVersion(), RevertStrings::Default, - solidity::test::Options::get().optimize ? OptimiserSettings::standard() : OptimiserSettings::minimal() + solidity::test::CommonOptions::get().optimize ? OptimiserSettings::standard() : OptimiserSettings::minimal() ); compiler.compileContract(*contract, map>{}, bytes()); @@ -161,12 +161,12 @@ BOOST_AUTO_TEST_CASE(location_test) } )", ""); AssemblyItems items = compileContract(sourceCode); - bool hasShifts = solidity::test::Options::get().evmVersion().hasBitwiseShifting(); + bool hasShifts = solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting(); auto codegenCharStream = make_shared("", "--CODEGEN--"); vector locations; - if (solidity::test::Options::get().optimize) + if (solidity::test::CommonOptions::get().optimize) locations = vector(4, SourceLocation{2, 82, sourceCode}) + vector(1, SourceLocation{8, 17, codegenCharStream}) + diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index d3312d7e42a3..1fa0bf712042 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -93,14 +93,14 @@ BOOST_AUTO_TEST_CASE(string_storage) m_compiler.overwriteReleaseFlag(true); compileAndRun(sourceCode); - auto evmVersion = solidity::test::Options::get().evmVersion(); + auto evmVersion = solidity::test::CommonOptions::get().evmVersion(); if (evmVersion <= EVMVersion::byzantium()) CHECK_DEPLOY_GAS(134209, 130895, evmVersion); // This is only correct on >=Constantinople. - else if (Options::get().useABIEncoderV2) + else if (CommonOptions::get().useABIEncoderV2) { - if (Options::get().optimizeYul) + if (CommonOptions::get().optimizeYul) { // Costs with 0 are cases which cannot be triggered in tests. if (evmVersion < EVMVersion::istanbul()) @@ -127,9 +127,9 @@ BOOST_AUTO_TEST_CASE(string_storage) if (evmVersion == EVMVersion::byzantium()) CHECK_GAS(21545, 21526, 20); // This is only correct on >=Constantinople. - else if (Options::get().useABIEncoderV2) + else if (CommonOptions::get().useABIEncoderV2) { - if (Options::get().optimizeYul) + if (CommonOptions::get().optimizeYul) { if (evmVersion < EVMVersion::istanbul()) CHECK_GAS(0, 21567, 20); diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 0020d80e4c2a..79394855a510 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -44,7 +44,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework { m_compiler.reset(); m_compiler.setSources({{"", "pragma solidity >=0.0;\n" + _sourceCode}}); - m_compiler.setOptimiserSettings(solidity::test::Options::get().optimize); + m_compiler.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); m_compiler.setEVMVersion(m_evmVersion); BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); @@ -52,7 +52,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework ASTNode const& sourceUnit = m_compiler.ast(""); BOOST_REQUIRE(items != nullptr); m_gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator(solidity::test::Options::get().evmVersion()).structuralEstimation(*items, vector({&sourceUnit})), + GasEstimator(solidity::test::CommonOptions::get().evmVersion()).structuralEstimation(*items, vector({&sourceUnit})), {&sourceUnit} ); } @@ -61,7 +61,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework { compileAndRun(_sourceCode); auto state = make_shared(); - PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName()), solidity::test::Options::get().evmVersion()); + PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName()), solidity::test::CommonOptions::get().evmVersion()); GasMeter::GasConsumption gas = meter.estimateMax(0, state); u256 bytecodeSize(m_compiler.runtimeObject(m_compiler.lastContractName()).bytecode.size()); // costs for deployment @@ -71,7 +71,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework // Skip the tests when we force ABIEncoderV2. // TODO: We should enable this again once the yul optimizer is activated. - if (!solidity::test::Options::get().useABIEncoderV2) + if (!solidity::test::CommonOptions::get().useABIEncoderV2) { BOOST_REQUIRE(!gas.isInfinite); BOOST_CHECK_LE(m_gasUsed, gas.value); @@ -94,13 +94,13 @@ class GasMeterTestFramework: public SolidityExecutionFramework gas = max(gas, gasForTransaction(hash.asBytes() + arguments, false)); } - gas += GasEstimator(solidity::test::Options::get().evmVersion()).functionalEstimation( + gas += GasEstimator(solidity::test::CommonOptions::get().evmVersion()).functionalEstimation( *m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()), _sig ); // Skip the tests when we force ABIEncoderV2. // TODO: We should enable this again once the yul optimizer is activated. - if (!solidity::test::Options::get().useABIEncoderV2) + if (!solidity::test::CommonOptions::get().useABIEncoderV2) { BOOST_REQUIRE(!gas.isInfinite); BOOST_CHECK_LE(m_gasUsed, gas.value); @@ -110,7 +110,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) { - auto evmVersion = solidity::test::Options::get().evmVersion(); + auto evmVersion = solidity::test::CommonOptions::get().evmVersion(); GasMeter::GasConsumption gas = _isCreation ? GasCosts::txCreateGas : GasCosts::txGas; for (auto i: _data) gas += i != 0 ? GasCosts::txDataNonZeroGas(evmVersion) : GasCosts::txDataZeroGas; diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index 6cb5f6cceff2..b1b0d56f23f7 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -16,7 +16,7 @@ */ #include -#include +#include #include #include #include diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 7b6134d15b8c..aa4cab9b8332 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include @@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(remappings) {"s_1.4.6/s.sol", "contract S {} pragma solidity >=0.0;"}, {"Tee/tee.sol", "contract Tee {} pragma solidity >=0.0;"} }); - c.setEVMVersion(solidity::test::Options::get().evmVersion()); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings) {"s_1.4.6/s.sol", "contract SSix {} pragma solidity >=0.0;"}, {"s_1.4.7/s.sol", "contract SSeven {} pragma solidity >=0.0;"} }); - c.setEVMVersion(solidity::test::Options::get().evmVersion()); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -79,7 +79,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_pres {"vendor/foo_1.0.0/foo.sol", "contract Foo1 {} pragma solidity >=0.0;"}, {"vendor/foo_2.0.0/foo.sol", "contract Foo2 {} pragma solidity >=0.0;"} }); - c.setEVMVersion(solidity::test::Options::get().evmVersion()); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -93,7 +93,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent_1) {"d/z.sol", "contract D {} pragma solidity >=0.0;"}, {"e/y/z/z.sol", "contract E {} pragma solidity >=0.0;"} }); - c.setEVMVersion(solidity::test::Options::get().evmVersion()); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent_2) {"d/z.sol", "contract D {} pragma solidity >=0.0;"}, {"e/y/z/z.sol", "contract E {} pragma solidity >=0.0;"} }); - c.setEVMVersion(solidity::test::Options::get().evmVersion()); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_CHECK(c.compile()); } diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 4b906e475408..4ca7130545fb 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -20,7 +20,7 @@ * Unit tests for inline assembly. */ -#include +#include #include @@ -59,7 +59,7 @@ std::optional parseAndReturnFirstError( AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM ) { - AssemblyStack stack(solidity::test::Options::get().evmVersion(), _language, solidity::frontend::OptimiserSettings::none()); + AssemblyStack stack(solidity::test::CommonOptions::get().evmVersion(), _language, solidity::frontend::OptimiserSettings::none()); bool success = false; try { @@ -126,7 +126,7 @@ Error expectError( void parsePrintCompare(string const& _source, bool _canWarn = false) { - AssemblyStack stack(solidity::test::Options::get().evmVersion(), AssemblyStack::Language::Assembly, OptimiserSettings::none()); + AssemblyStack stack(solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::Assembly, OptimiserSettings::none()); BOOST_REQUIRE(stack.parseAndAnalyze("", _source)); if (_canWarn) BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors())); @@ -539,7 +539,7 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; string parsed = "object \"object\" {\n code { let x := \"\\xe1\\xae\\xac\" }\n}\n"; - AssemblyStack stack(solidity::test::Options::get().evmVersion(), AssemblyStack::Language::Assembly, OptimiserSettings::none()); + AssemblyStack stack(solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::Assembly, OptimiserSettings::none()); BOOST_REQUIRE(stack.parseAndAnalyze("", source)); BOOST_REQUIRE(stack.errors().empty()); BOOST_CHECK_EQUAL(stack.print(), parsed); @@ -686,42 +686,42 @@ BOOST_AUTO_TEST_CASE(keccak256) BOOST_AUTO_TEST_CASE(returndatasize) { - if (!solidity::test::Options::get().evmVersion().supportsReturndata()) + if (!solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) return; BOOST_CHECK(successAssemble("{ let r := returndatasize() }")); } BOOST_AUTO_TEST_CASE(returndatacopy) { - if (!solidity::test::Options::get().evmVersion().supportsReturndata()) + if (!solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) return; BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }")); } BOOST_AUTO_TEST_CASE(returndatacopy_functional) { - if (!solidity::test::Options::get().evmVersion().supportsReturndata()) + if (!solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) return; BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }")); } BOOST_AUTO_TEST_CASE(staticcall) { - if (!solidity::test::Options::get().evmVersion().hasStaticCall()) + if (!solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) return; BOOST_CHECK(successAssemble("{ pop(staticcall(10000, 0x123, 64, 0x10, 128, 0x10)) }")); } BOOST_AUTO_TEST_CASE(create2) { - if (!solidity::test::Options::get().evmVersion().hasCreate2()) + if (!solidity::test::CommonOptions::get().evmVersion().hasCreate2()) return; BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }")); } BOOST_AUTO_TEST_CASE(shift) { - if (!solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; BOOST_CHECK(successAssemble("{ pop(shl(10, 32)) }")); BOOST_CHECK(successAssemble("{ pop(shr(10, 32)) }")); @@ -730,7 +730,7 @@ BOOST_AUTO_TEST_CASE(shift) BOOST_AUTO_TEST_CASE(shift_constantinople_warning) { - if (solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", TypeError, "The \"shl\" instruction is only available for Constantinople-compatible VMs"); CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", TypeError, "The \"shr\" instruction is only available for Constantinople-compatible VMs"); diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index a8930a76fa4d..665a175908b9 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -20,7 +20,7 @@ */ #include -#include +#include #include #include #include @@ -68,8 +68,8 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) CompilerStack compilerStack; compilerStack.overwriteReleaseFlag(release); compilerStack.setSources({{"", std::string(sourceCode)}}); - compilerStack.setEVMVersion(solidity::test::Options::get().evmVersion()); - compilerStack.setOptimiserSettings(solidity::test::Options::get().optimize); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); compilerStack.setMetadataHash(metadataHash); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; @@ -129,8 +129,8 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) CompilerStack compilerStack; compilerStack.overwriteReleaseFlag(release); compilerStack.setSources({{"", std::string(sourceCode)}}); - compilerStack.setEVMVersion(solidity::test::Options::get().evmVersion()); - compilerStack.setOptimiserSettings(solidity::test::Options::get().optimize); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); compilerStack.setMetadataHash(metadataHash); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; @@ -191,8 +191,8 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) {"A", std::string(sourceCodeA)}, {"B", std::string(sourceCodeB)}, }); - compilerStack.setEVMVersion(solidity::test::Options::get().evmVersion()); - compilerStack.setOptimiserSettings(solidity::test::Options::get().optimize); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("A"); @@ -232,8 +232,8 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) {"B", std::string(sourceCodeB)}, {"C", std::string(sourceCodeC)} }); - compilerStack.setEVMVersion(solidity::test::Options::get().evmVersion()); - compilerStack.setOptimiserSettings(solidity::test::Options::get().optimize); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("C"); @@ -260,8 +260,8 @@ BOOST_AUTO_TEST_CASE(metadata_useLiteralContent) { CompilerStack compilerStack; compilerStack.setSources({{"", std::string(_src)}}); - compilerStack.setEVMVersion(solidity::test::Options::get().evmVersion()); - compilerStack.setOptimiserSettings(solidity::test::Options::get().optimize); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); compilerStack.useMetadataLiteralSources(_literal); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); string metadata_str = compilerStack.metadata("test"); diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 3cf0c73f5011..06862ad08d98 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -19,7 +19,7 @@ */ #include -#include +#include #include @@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(import_base) } )"} }); - c.setEVMVersion(solidity::test::Options::get().evmVersion()); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_CHECK(c.compile()); unsigned asserts = 0; @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(import_library) } )"} }); - c.setEVMVersion(solidity::test::Options::get().evmVersion()); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_CHECK(c.compile()); unsigned asserts = 0; diff --git a/test/libsolidity/SMTCheckerJSONTest.cpp b/test/libsolidity/SMTCheckerJSONTest.cpp index 42d29fa4034e..82c2b446b168 100644 --- a/test/libsolidity/SMTCheckerJSONTest.cpp +++ b/test/libsolidity/SMTCheckerJSONTest.cpp @@ -16,7 +16,7 @@ */ #include -#include +#include #include #include #include diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index 47ed43ba572a..7db668a2d2c9 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -16,7 +16,7 @@ */ #include -#include +#include #include diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 82cc07367228..36b9c82147a3 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 4082a6ab81fe..eb01764bc519 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -13,7 +13,7 @@ */ #include -#include +#include #include #include #include diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index 117a41187182..971a74a75698 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include @@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions) function f() internal { for (uint i = 0; i < 10; ++i) x += 3 + i; } } )"; - compiler().setOptimiserSettings(solidity::test::Options::get().optimize); + compiler().setOptimiserSettings(solidity::test::CommonOptions::get().optimize); BOOST_REQUIRE(success(sourceCode)); BOOST_REQUIRE_MESSAGE(compiler().compile(), "Compiling contract failed"); bytes const& creationBytecode = solidity::test::bytecodeSansMetadata(compiler().object("C").bytecode); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index a1d200f897b1..48dbe2c432b8 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include #include @@ -2458,7 +2458,7 @@ BOOST_AUTO_TEST_CASE(default_fallback_throws) compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) { char const* sourceCode = R"YY( contract A { @@ -3323,7 +3323,7 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall) BOOST_AUTO_TEST_CASE(generic_staticcall) { - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) { char const* sourceCode = R"**( contract A { @@ -5709,7 +5709,7 @@ BOOST_AUTO_TEST_CASE(bool_conversion) } )"; compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; ABI_CHECK(callContractFunction("f(bool)", 0), encodeArgs(0)); ABI_CHECK(callContractFunction("f(bool)", 1), encodeArgs(1)); ABI_CHECK(callContractFunction("f(bool)", 2), v2 ? encodeArgs() : encodeArgs(1)); @@ -9970,7 +9970,7 @@ BOOST_AUTO_TEST_CASE(cleanup_bytes_types) )"; compileAndRun(sourceCode, 0, "C"); // We input longer data on purpose. - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; ABI_CHECK(callContractFunction("f(bytes2,uint16)", string("abc"), u256(0x040102)), v2 ? encodeArgs() : encodeArgs(0)); } BOOST_AUTO_TEST_CASE(cleanup_bytes_types_shortening) @@ -10007,7 +10007,7 @@ BOOST_AUTO_TEST_CASE(cleanup_address_types) )"; compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; // We input longer data on purpose. ABI_CHECK(callContractFunction("f(address)", u256("0xFFFF1234567890123456789012345678901234567890")), v2 ? encodeArgs() : encodeArgs(0)); ABI_CHECK(callContractFunction("g(address)", u256("0xFFFF1234567890123456789012345678901234567890")), v2 ? encodeArgs() : encodeArgs(0)); @@ -11260,7 +11260,7 @@ BOOST_AUTO_TEST_CASE(shift_right_garbled) } )"; compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(0xf))); ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(0xf))); } @@ -11286,7 +11286,7 @@ BOOST_AUTO_TEST_CASE(shift_right_garbled_signed) } )"; compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); @@ -11480,7 +11480,7 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int8) } )"; compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); @@ -11498,7 +11498,7 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int16) } )"; compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); @@ -11516,7 +11516,7 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int32) } )"; compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::Options::get().useABIEncoderV2; + bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); @@ -12044,7 +12044,7 @@ BOOST_AUTO_TEST_CASE(revert_with_cause) } } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { compileAndRun(sourceCode, 0, "C"); bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; @@ -12118,7 +12118,7 @@ BOOST_AUTO_TEST_CASE(require_with_message) } } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { compileAndRun(sourceCode, 0, "C"); bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; @@ -12164,7 +12164,7 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages) } } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { compileAndRun(sourceCode, 0, "C"); bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; @@ -12202,7 +12202,7 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) } } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { compileAndRun(sourceCode, 0, "C"); bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; @@ -12241,7 +12241,7 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) } } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { compileAndRun(sourceCode, 0, "C"); bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; @@ -12446,7 +12446,7 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address) ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(1))); - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) { char const* sourceCode = R"YY( contract C { @@ -12462,10 +12462,10 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address) BOOST_AUTO_TEST_CASE(bare_call_return_data) { - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { vector calltypes = {"call", "delegatecall"}; - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) calltypes.emplace_back("staticcall"); for (string const& calltype: calltypes) { @@ -12589,7 +12589,7 @@ BOOST_AUTO_TEST_CASE(bare_call_return_data) BOOST_AUTO_TEST_CASE(delegatecall_return_value) { - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { char const* sourceCode = R"DELIMITER( contract C { @@ -13390,7 +13390,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_empty_string) )"; compileAndRun(sourceCode, 0, "C"); - if (!solidity::test::Options::get().useABIEncoderV2) + if (!solidity::test::CommonOptions::get().useABIEncoderV2) { // ABI Encoder V2 has slightly different padding, tested below. ABI_CHECK(callContractFunction("f()"), encodeArgs( @@ -13514,7 +13514,7 @@ BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) compileAndRun(sourceCode, 0, "D"); // This should work (called via CALL) ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) { // These should throw (called via STATICCALL) ABI_CHECK(callContractFunction("fview()"), encodeArgs()); @@ -13529,7 +13529,7 @@ BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople) { - if (!solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; char const* sourceCode = R"( contract C { @@ -13568,7 +13568,7 @@ BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople) BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople) { - if (!solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; char const* sourceCode = R"( contract C { @@ -13635,7 +13635,7 @@ BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople) BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople_combined) { - if (!solidity::test::Options::get().evmVersion().hasBitwiseShifting()) + if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; char const* sourceCode = R"( contract C { @@ -14391,7 +14391,7 @@ BOOST_AUTO_TEST_CASE(try_catch_library_call) } } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) { compileAndRun(sourceCode, 0, "L", bytes()); compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 27deb4d237e0..8f428f13f91b 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -39,7 +39,7 @@ bytes SolidityExecutionFramework::compileContract( // Silence compiler version warning std::string sourceCode = "pragma solidity >=0.0;\n"; if ( - solidity::test::Options::get().useABIEncoderV2 && + solidity::test::CommonOptions::get().useABIEncoderV2 && _sourceCode.find("pragma experimental ABIEncoderV2;") == std::string::npos ) sourceCode += "pragma experimental ABIEncoderV2;\n"; diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 56ca6d51dd31..0cafe42a407a 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include @@ -100,7 +100,7 @@ bytes compileFirstExpression( { ErrorList errors; ErrorReporter errorReporter(errors); - sourceUnit = Parser(errorReporter, solidity::test::Options::get().evmVersion()).parse( + sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse( make_shared(CharStream(_sourceCode, "")) ); if (!sourceUnit) @@ -116,7 +116,7 @@ bytes compileFirstExpression( ErrorReporter errorReporter(errors); GlobalContext globalContext; map> scopes; - NameAndTypeResolver resolver(globalContext, solidity::test::Options::get().evmVersion(), scopes, errorReporter); + NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), scopes, errorReporter); resolver.registerDeclarations(*sourceUnit); vector inheritanceHierarchy; @@ -130,7 +130,7 @@ bytes compileFirstExpression( if (ContractDefinition* contract = dynamic_cast(node.get())) { ErrorReporter errorReporter(errors); - TypeChecker typeChecker(solidity::test::Options::get().evmVersion(), errorReporter); + TypeChecker typeChecker(solidity::test::CommonOptions::get().evmVersion(), errorReporter); BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer const& node: sourceUnit->nodes()) @@ -139,7 +139,7 @@ bytes compileFirstExpression( FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.expression() != nullptr); - CompilerContext context(solidity::test::Options::get().evmVersion()); + CompilerContext context(solidity::test::CommonOptions::get().evmVersion()); context.resetVisitedNodes(contract); context.setInheritanceHierarchy(inheritanceHierarchy); unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack @@ -153,7 +153,7 @@ bytes compileFirstExpression( ExpressionCompiler( context, RevertStrings::Default, - solidity::test::Options::get().optimize + solidity::test::CommonOptions::get().optimize ).compile(*extractor.expression()); for (vector const& function: _functions) @@ -284,7 +284,7 @@ BOOST_AUTO_TEST_CASE(comparison) bytes code = compileFirstExpression(sourceCode); bytes expectation; - if (solidity::test::Options::get().optimize) + if (solidity::test::CommonOptions::get().optimize) expectation = { uint8_t(Instruction::PUSH2), 0x11, 0xaa, uint8_t(Instruction::PUSH2), 0x10, 0xaa, @@ -347,7 +347,7 @@ BOOST_AUTO_TEST_CASE(arithmetic) bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation; - if (solidity::test::Options::get().optimize) + if (solidity::test::CommonOptions::get().optimize) expectation = { uint8_t(Instruction::PUSH1), 0x2, uint8_t(Instruction::PUSH1), 0x3, @@ -428,7 +428,7 @@ BOOST_AUTO_TEST_CASE(unary_operators) bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation; - if (solidity::test::Options::get().optimize) + if (solidity::test::CommonOptions::get().optimize) expectation = { uint8_t(Instruction::DUP1), uint8_t(Instruction::PUSH1), 0x0, @@ -519,7 +519,7 @@ BOOST_AUTO_TEST_CASE(assignment) // Stack: a, b bytes expectation; - if (solidity::test::Options::get().optimize) + if (solidity::test::CommonOptions::get().optimize) expectation = { uint8_t(Instruction::DUP1), uint8_t(Instruction::DUP3), @@ -631,7 +631,7 @@ BOOST_AUTO_TEST_CASE(selfbalance) bytes code = compileFirstExpression(sourceCode, {}, {}); - if (solidity::test::Options::get().evmVersion() == EVMVersion::istanbul()) + if (solidity::test::CommonOptions::get().evmVersion() == EVMVersion::istanbul()) { bytes expectation({uint8_t(Instruction::SELFBALANCE)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e5e82d3927fd..b19644a4cf63 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -22,7 +22,7 @@ #include -#include +#include #include @@ -361,7 +361,7 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; - if (solidity::test::Options::get().evmVersion() == EVMVersion::homestead()) + if (solidity::test::CommonOptions::get().evmVersion() == EVMVersion::homestead()) CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type string memory."); else CHECK_SUCCESS_NO_WARNINGS(sourceCode); @@ -386,7 +386,7 @@ BOOST_AUTO_TEST_CASE(returndatasize_as_variable) vector> expectations(vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} }); - if (!solidity::test::Options::get().evmVersion().supportsReturndata()) + if (!solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible VMs"))); CHECK_ALLOW_MULTI(text, expectations); } @@ -401,7 +401,7 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) vector> expectations(vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} }); - if (!solidity::test::Options::get().evmVersion().hasCreate2()) + if (!solidity::test::CommonOptions::get().evmVersion().hasCreate2()) expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("\"create2\" instruction is only available for Constantinople-compatible VMs"))); CHECK_ALLOW_MULTI(text, expectations); } @@ -416,7 +416,7 @@ BOOST_AUTO_TEST_CASE(extcodehash_as_variable) vector> expectations(vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} }); - if (!solidity::test::Options::get().evmVersion().hasExtCodeHash()) + if (!solidity::test::CommonOptions::get().evmVersion().hasExtCodeHash()) expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("\"extcodehash\" instruction is only available for Constantinople-compatible VMs"))); CHECK_ALLOW_MULTI(text, expectations); } @@ -452,7 +452,7 @@ BOOST_AUTO_TEST_CASE(address_staticcall) } )"; - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) CHECK_SUCCESS_NO_WARNINGS(sourceCode); else CHECK_ERROR(sourceCode, TypeError, "\"staticcall\" is not supported by the VM version."); @@ -460,7 +460,7 @@ BOOST_AUTO_TEST_CASE(address_staticcall) BOOST_AUTO_TEST_CASE(address_staticcall_value) { - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) { char const* sourceCode = R"( contract C { @@ -484,7 +484,7 @@ BOOST_AUTO_TEST_CASE(address_call_full_return_type) } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) CHECK_SUCCESS_NO_WARNINGS(sourceCode); else CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type bytes memory."); @@ -501,7 +501,7 @@ BOOST_AUTO_TEST_CASE(address_delegatecall_full_return_type) } )"; - if (solidity::test::Options::get().evmVersion().supportsReturndata()) + if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) CHECK_SUCCESS_NO_WARNINGS(sourceCode); else CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type bytes memory."); @@ -510,7 +510,7 @@ BOOST_AUTO_TEST_CASE(address_delegatecall_full_return_type) BOOST_AUTO_TEST_CASE(address_staticcall_full_return_type) { - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) { char const* sourceCode = R"( contract C { diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 24aec0547edd..13bd1f3b06f2 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -20,7 +20,7 @@ * Unit tests for the solidity compiler JSON Interface output. */ -#include +#include #include #include #include @@ -46,7 +46,7 @@ class DocumentationChecker { m_compilerStack.reset(); m_compilerStack.setSources({{"", "pragma solidity >=0.0;\n" + _code}}); - m_compilerStack.setEVMVersion(solidity::test::Options::get().evmVersion()); + m_compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedDocumentation; @@ -67,7 +67,7 @@ class DocumentationChecker { m_compilerStack.reset(); m_compilerStack.setSources({{"", "pragma solidity >=0.0;\n" + _code}}); - m_compilerStack.setEVMVersion(solidity::test::Options::get().evmVersion()); + m_compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); BOOST_CHECK(!m_compilerStack.parseAndAnalyze()); BOOST_REQUIRE(Error::containsErrorOfType(m_compilerStack.errors(), Error::Type::DocstringParsingError)); } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 58d1a069123c..3087b4b0f0f6 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -44,7 +44,7 @@ ASTPointer parseText(std::string const& _source, ErrorList& ErrorReporter errorReporter(_errors); ASTPointer sourceUnit = Parser( errorReporter, - solidity::test::Options::get().evmVersion(), + solidity::test::CommonOptions::get().evmVersion(), errorRecovery ).parse(std::make_shared(CharStream(_source, ""))); if (!sourceUnit) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index dc764c6b225e..b28d6c9da576 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -16,7 +16,7 @@ */ #include -#include +#include #include #include #include diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index a29d135256f2..3bde18d27a77 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include @@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(environment_access) "this", "address(1).balance", }; - if (solidity::test::Options::get().evmVersion().hasStaticCall()) + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) view.emplace_back("address(0x4242).staticcall(\"\")"); // ``block.blockhash`` and ``blockhash`` are tested separately below because their usage will @@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(address_staticcall) } } )"; - if (!solidity::test::Options::get().evmVersion().hasStaticCall()) + if (!solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) CHECK_ERROR(text, TypeError, "\"staticcall\" is not supported by the VM version."); else CHECK_SUCCESS_NO_WARNINGS(text); @@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) } } )"; - if (!solidity::test::Options::get().evmVersion().hasStaticCall()) + if (!solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) CHECK_ERROR(text, TypeError, "\"staticcall\" instruction is only available for Byzantium-compatible"); else CHECK_SUCCESS_NO_WARNINGS(text); diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 58314db268e8..4be96c88d452 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include diff --git a/test/libsolutil/Checksum.cpp b/test/libsolutil/Checksum.cpp index bd6abad31c44..159c55efc858 100644 --- a/test/libsolutil/Checksum.cpp +++ b/test/libsolutil/Checksum.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include diff --git a/test/libsolutil/CommonData.cpp b/test/libsolutil/CommonData.cpp index 5bd29c056b0c..d8434c79d16c 100644 --- a/test/libsolutil/CommonData.cpp +++ b/test/libsolutil/CommonData.cpp @@ -23,7 +23,7 @@ #include #include // for IntegerType -#include +#include #include diff --git a/test/libsolutil/IndentedWriter.cpp b/test/libsolutil/IndentedWriter.cpp index fcdb096dd05e..3d0963e9ff23 100644 --- a/test/libsolutil/IndentedWriter.cpp +++ b/test/libsolutil/IndentedWriter.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include diff --git a/test/libsolutil/IpfsHash.cpp b/test/libsolutil/IpfsHash.cpp index adaf30d611ae..4514b2f4c0f3 100644 --- a/test/libsolutil/IpfsHash.cpp +++ b/test/libsolutil/IpfsHash.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include diff --git a/test/libsolutil/IterateReplacing.cpp b/test/libsolutil/IterateReplacing.cpp index 3270baf4739d..d1598dbc3981 100644 --- a/test/libsolutil/IterateReplacing.cpp +++ b/test/libsolutil/IterateReplacing.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include diff --git a/test/libsolutil/JSON.cpp b/test/libsolutil/JSON.cpp index 32d41144a4ea..e45c6fdec35d 100644 --- a/test/libsolutil/JSON.cpp +++ b/test/libsolutil/JSON.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include diff --git a/test/libsolutil/StringUtils.cpp b/test/libsolutil/StringUtils.cpp index b2e1d5955799..b7e653a0ecc2 100644 --- a/test/libsolutil/StringUtils.cpp +++ b/test/libsolutil/StringUtils.cpp @@ -24,7 +24,7 @@ #include // for IntegerType -#include +#include #include diff --git a/test/libsolutil/SwarmHash.cpp b/test/libsolutil/SwarmHash.cpp index 1c2ff917ed4e..a976bfccf0d2 100644 --- a/test/libsolutil/SwarmHash.cpp +++ b/test/libsolutil/SwarmHash.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include diff --git a/test/libsolutil/UTF8.cpp b/test/libsolutil/UTF8.cpp index 86ec4fe9310b..b3e969b9b5e0 100644 --- a/test/libsolutil/UTF8.cpp +++ b/test/libsolutil/UTF8.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include diff --git a/test/libsolutil/Whiskers.cpp b/test/libsolutil/Whiskers.cpp index 11754711f54c..3f2a487335cd 100644 --- a/test/libsolutil/Whiskers.cpp +++ b/test/libsolutil/Whiskers.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index c80b09fa739f..6ea2bb63a359 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include @@ -48,7 +48,7 @@ namespace { Dialect const& defaultDialect(bool _yul) { - return _yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(solidity::test::Options::get().evmVersion()); + return _yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()); } } @@ -64,9 +64,9 @@ void yul::test::printErrors(ErrorList const& _errors) pair, shared_ptr> yul::test::parse(string const& _source, bool _yul) { AssemblyStack stack( - solidity::test::Options::get().evmVersion(), + solidity::test::CommonOptions::get().evmVersion(), _yul ? AssemblyStack::Language::Yul : AssemblyStack::Language::StrictAssembly, - solidity::test::Options::get().optimize ? + solidity::test::CommonOptions::get().optimize ? solidity::frontend::OptimiserSettings::standard() : solidity::frontend::OptimiserSettings::minimal() ); diff --git a/test/libyul/CompilabilityChecker.cpp b/test/libyul/CompilabilityChecker.cpp index 23fa82cd71f3..51aefc6107f5 100644 --- a/test/libyul/CompilabilityChecker.cpp +++ b/test/libyul/CompilabilityChecker.cpp @@ -18,7 +18,7 @@ * Unit tests for the compilability checker. */ -#include +#include #include #include @@ -39,7 +39,7 @@ string check(string const& _input) Object obj; std::tie(obj.code, obj.analysisInfo) = yul::test::parse(_input, false); BOOST_REQUIRE(obj.code); - map functions = CompilabilityChecker::run(EVMDialect::strictAssemblyForEVM(solidity::test::Options::get().evmVersion()), obj, true); + map functions = CompilabilityChecker::run(EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()), obj, true); string out; for (auto const& function: functions) out += function.first.str() + ": " + to_string(function.second) + " "; diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index a5e01d286902..fdee56bda223 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include @@ -67,7 +67,7 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _ return TestResult::FatalError; *m_object = EVMToEwasmTranslator( - EVMDialect::strictAssemblyForEVMObjects(solidity::test::Options::get().evmVersion()) + EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()) ).run(*m_object); // Add call to "main()". @@ -111,7 +111,7 @@ void EwasmTranslationTest::printIndented(ostream& _stream, string const& _output bool EwasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { AssemblyStack stack( - solidity::test::Options::get().evmVersion(), + solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none() ); diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index d7a184dc49da..257faab22b89 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -16,7 +16,7 @@ */ #include -#include +#include #include #include diff --git a/test/libyul/Metrics.cpp b/test/libyul/Metrics.cpp index 7d1abee58eb7..1841abc676f1 100644 --- a/test/libyul/Metrics.cpp +++ b/test/libyul/Metrics.cpp @@ -18,7 +18,7 @@ * Unit tests for the code metrics. */ -#include +#include #include diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index c8756eafb558..2d932006e65d 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -19,7 +19,7 @@ * Unit tests for the Yul object parser. */ -#include +#include #include @@ -49,7 +49,7 @@ std::pair parse(string const& _source) try { AssemblyStack asmStack( - solidity::test::Options::get().evmVersion(), + solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none() ); @@ -240,7 +240,7 @@ BOOST_AUTO_TEST_CASE(to_string) )"; expectation = boost::replace_all_copy(expectation, "\t", " "); AssemblyStack asmStack( - solidity::test::Options::get().evmVersion(), + solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none() ); diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index e278383a525d..95f7dd38f1f2 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -19,7 +19,7 @@ * Unit tests for parsing Yul. */ -#include +#include #include #include diff --git a/test/libyul/StackReuseCodegen.cpp b/test/libyul/StackReuseCodegen.cpp index b9d3334848d9..5103befb03cd 100644 --- a/test/libyul/StackReuseCodegen.cpp +++ b/test/libyul/StackReuseCodegen.cpp @@ -18,7 +18,7 @@ * Unit tests for stack-reusing code generator. */ -#include +#include #include #include diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index 939fa2e1e874..f37d18383b22 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include @@ -99,7 +99,7 @@ void YulInterpreterTest::printIndented(ostream& _stream, string const& _output, bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { AssemblyStack stack( - solidity::test::Options::get().evmVersion(), + solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none() ); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 791551bc6bb5..609473acc310 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include @@ -109,7 +109,7 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename) else if (dialectName == "ewasm") m_dialect = &WasmDialect::instance(); else if (dialectName == "evm") - m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::Options::get().evmVersion()); + m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()); else BOOST_THROW_EXCEPTION(runtime_error("Invalid dialect " + dialectName)); @@ -117,7 +117,7 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename) m_settings.erase("dialect"); } else - m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::Options::get().evmVersion()); + m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()); if (m_settings.count("step")) { diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 69db421071d5..9fabfae68114 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -13,7 +13,6 @@ target_link_libraries(yulopti PRIVATE solidity Boost::boost Boost::program_optio add_executable(isoltest isoltest.cpp IsolTestOptions.cpp - ../Options.cpp ../Common.cpp ../EVMHost.cpp ../TestCase.cpp diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index d96091dcbffb..a8c8748fb45f 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -401,20 +402,25 @@ int main(int argc, char const *argv[]) { setupTerminal(); - solidity::test::IsolTestOptions options(&TestTool::editor); - - try { - if (options.parse(argc, argv)) - options.validate(); - else + auto options = std::make_unique(&TestTool::editor); + + try + { + if (!options->parse(argc, argv)) + return -1; + + options->validate(); + solidity::test::CommonOptions::setSingleton(std::move(options)); + } + catch (std::exception const& _exception) + { + cerr << _exception.what() << endl; return 1; + } } - catch (std::exception const& _exception) - { - cerr << _exception.what() << endl; - return 1; - } + + auto& options = dynamic_cast(solidity::test::CommonOptions::get()); bool disableSemantics = !solidity::test::EVMHost::getVM(options.evmonePath.string()); if (disableSemantics) From abbedb0819d9a383ec64bc99a897705875e89e1a Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Jan 2020 13:49:45 +0100 Subject: [PATCH 018/160] Update Changelog.md Co-Authored-By: Erik K --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index dbf923afbc45..999520996e67 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,7 +5,7 @@ Language Features: Compiler Features: - * Code Generator: Use ``calldatacopy`` past input to zero out memory instead of ``codecopy``. + * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. Bugfixes: From 90c98a3289dc9dc7ef1e8fb131de139e39427e13 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Dec 2019 16:01:28 +0100 Subject: [PATCH 019/160] Introduce typed EVM dialect. --- libyul/AssemblyStack.cpp | 2 +- libyul/backends/evm/EVMDialect.cpp | 64 +++++++++++++++++++++++++----- libyul/backends/evm/EVMDialect.h | 20 +++++++++- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 35bcdf65cd84..87215179e9fc 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -157,7 +157,7 @@ void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _evm15, bool _o dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion); break; case Language::Yul: - dialect = &EVMDialect::yulForEVM(m_evmVersion); + dialect = &EVMDialectTyped::instance(m_evmVersion); break; default: solAssert(false, "Invalid language."); diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 6e2407ced63a..399445efafbc 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -203,15 +203,6 @@ EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _ return *dialects[_version]; } -EVMDialect const& EVMDialect::yulForEVM(langutil::EVMVersion _version) -{ - static map> dialects; - static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; - if (!dialects[_version]) - dialects[_version] = make_unique(_version, false); - return *dialects[_version]; -} - SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instruction) { return SideEffects{ @@ -222,3 +213,58 @@ SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instructio evmasm::SemanticInformation::invalidatesMemory(_instruction) }; } + +EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectAccess): + EVMDialect(_evmVersion, _objectAccess) +{ + defaultType = "u256"_yulstring; + m_functions["lt"_yulstring].returns = {"bool"_yulstring}; + m_functions["gt"_yulstring].returns = {"bool"_yulstring}; + m_functions["slt"_yulstring].returns = {"bool"_yulstring}; + m_functions["sgt"_yulstring].returns = {"bool"_yulstring}; + m_functions["eq"_yulstring].returns = {"bool"_yulstring}; + m_functions["iszero"_yulstring].returns = {"bool"_yulstring}; + m_functions["bitand"_yulstring] = m_functions["and"_yulstring]; + m_functions["bitor"_yulstring] = m_functions["or"_yulstring]; + m_functions["bitxor"_yulstring] = m_functions["xor"_yulstring]; + m_functions["and"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring}; + m_functions["and"_yulstring].returns = {"bool"_yulstring}; + m_functions["or"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring}; + m_functions["or"_yulstring].returns = {"bool"_yulstring}; + m_functions["xor"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring}; + m_functions["xor"_yulstring].returns = {"bool"_yulstring}; + m_functions["isfalse"_yulstring] = m_functions["iszero"_yulstring]; + m_functions["isfalse"_yulstring].parameters = {"bool"_yulstring}; + m_functions["popbool"_yulstring] = m_functions["pop"_yulstring]; + m_functions["popbool"_yulstring].parameters = {"bool"_yulstring}; + m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, false, []( + FunctionCall const&, + AbstractAssembly&, + BuiltinContext&, + std::function _visitArguments + ) { + _visitArguments(); + })); + m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring}; + m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, false, []( + FunctionCall const&, + AbstractAssembly& _assembly, + BuiltinContext&, + std::function _visitArguments + ) { + // TODO Should a value larger than 1 be invalid? + _visitArguments(); + _assembly.appendInstruction(evmasm::Instruction::ISZERO); + _assembly.appendInstruction(evmasm::Instruction::ISZERO); + })); + m_functions["u256_to_bool"_yulstring].returns = {"bool"_yulstring}; +} + +EVMDialectTyped const& EVMDialectTyped::instance(langutil::EVMVersion _version) +{ + static map> dialects; + static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; + if (!dialects[_version]) + dialects[_version] = make_unique(_version, true); + return *dialects[_version]; +} diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index 0d7d8b6d93f6..a670b020f018 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -74,7 +74,6 @@ struct EVMDialect: public Dialect static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version); static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version); - static EVMDialect const& yulForEVM(langutil::EVMVersion _version); langutil::EVMVersion evmVersion() const { return m_evmVersion; } @@ -88,4 +87,23 @@ struct EVMDialect: public Dialect std::map m_functions; }; +/** + * EVM dialect with types u256 (default) and bool. + * Difference to EVMDialect: + * - All comparison functions return type bool + * - bitwise operations are called bitor, bitand, bitxor and bitnot + * - and, or, xor take bool and return bool + * - iszero returns bool + * - isfalse takes bool and returns bool + * - there are conversion functions bool_to_u256 and u256_to_bool. + * - there is popbool + */ +struct EVMDialectTyped: public EVMDialect +{ + /// Constructor, should only be used internally. Use the factory function below. + EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectAccess); + + static EVMDialectTyped const& instance(langutil::EVMVersion _version); +}; + } From fbe5bb0cceba68660378065e1337e2c8a01b87a4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 16 Jan 2020 12:03:19 +0100 Subject: [PATCH 020/160] Parse default dialect and omit when printing. --- libsolidity/codegen/CompilerContext.cpp | 4 ++-- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 1 + libyul/AsmAnalysis.cpp | 2 +- libyul/AsmParser.cpp | 5 ++++- libyul/AsmPrinter.cpp | 6 ++++-- libyul/AsmPrinter.h | 10 +++++++++- libyul/Dialect.h | 2 +- libyul/backends/evm/EVMDialect.cpp | 3 +++ test/libyul/YulOptimizerTest.cpp | 2 +- test/tools/yulopti.cpp | 2 +- 10 files changed, 27 insertions(+), 10 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ef5534a3cf0d..c57e4f8b0dd7 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -387,7 +387,7 @@ void CompilerContext::appendInlineAssembly( yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); auto parserResult = yul::Parser(errorReporter, dialect).parse(scanner, false); #ifdef SOL_OUTPUT_ASM - cout << yul::AsmPrinter()(*parserResult) << endl; + cout << yul::AsmPrinter(&dialect)(*parserResult) << endl; #endif auto reportError = [&](string const& _context) @@ -438,7 +438,7 @@ void CompilerContext::appendInlineAssembly( #ifdef SOL_OUTPUT_ASM cout << "After optimizer:" << endl; - cout << yul::AsmPrinter()(*parserResult) << endl; + cout << yul::AsmPrinter(&dialect)(*parserResult) << endl; #endif } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index fe3e2a7ef4ba..16c11e3e78c5 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -892,6 +892,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) solAssert(holds_alternative(modified), ""); + // Do not provide dialect so that we get the full type information. m_code << yul::AsmPrinter()(std::get(std::move(modified))) << "\n"; return false; } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 148eeabfe7c6..a117f8cd9f0e 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -608,7 +608,7 @@ Scope& AsmAnalyzer::scope(Block const* _block) } void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location) { - if (!_type.empty() && !contains(m_dialect.types, _type)) + if (!_type.empty() && !m_dialect.types.count(_type)) m_errorReporter.typeError( _location, "\"" + _type.str() + "\" is not a valid type (user defined types are not yet supported)." diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 3b955b96f78c..fcfb462615b8 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -366,7 +366,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() location(), kind, YulString{currentLiteral()}, - {} + kind == LiteralKind::Boolean ? m_dialect.boolType : m_dialect.defaultType }; advance(); if (currentToken() == Token::Colon) @@ -497,6 +497,9 @@ TypedName Parser::parseTypedName() typedName.location.end = endPosition(); typedName.type = expectAsmIdentifier(); } + else + typedName.type = m_dialect.defaultType; + return typedName; } diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index a62db6123a63..671a0fa24018 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -238,7 +239,8 @@ string AsmPrinter::formatTypedName(TypedName _variable) const string AsmPrinter::appendTypeName(YulString _type) const { - if (!_type.empty()) + if (_type.empty() || (m_dialect && _type == m_dialect->defaultType)) + return {}; + else return ":" + _type.str(); - return ""; } diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 30569922d70f..9edc3a7c354b 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -30,10 +30,16 @@ namespace solidity::yul { struct Dialect; +/** + * Converts a parsed Yul AST into readable string representation. + * Ignores source locations. + * If a dialect is provided, the dialect's default type is omitted. + */ class AsmPrinter { public: - explicit AsmPrinter() {} + AsmPrinter() {} + explicit AsmPrinter(Dialect const& _dialect): m_dialect(&_dialect) {} std::string operator()(Literal const& _literal) const; std::string operator()(Identifier const& _identifier) const; @@ -53,6 +59,8 @@ class AsmPrinter private: std::string formatTypedName(TypedName _variable) const; std::string appendTypeName(YulString _type) const; + + Dialect const* m_dialect = nullptr; }; } diff --git a/libyul/Dialect.h b/libyul/Dialect.h index b96e1851c8aa..f08a04e6506c 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -51,7 +51,7 @@ struct Dialect: boost::noncopyable YulString defaultType; /// Type used for the literals "true" and "false". YulString boolType; - std::vector types; + std::set types; /// @returns the builtin function of the given name or a nullptr if it is not a builtin function. virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; } diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 399445efafbc..baab0a268ae9 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -218,6 +218,9 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA EVMDialect(_evmVersion, _objectAccess) { defaultType = "u256"_yulstring; + boolType = "bool"_yulstring; + types = {defaultType, boolType}; + m_functions["lt"_yulstring].returns = {"bool"_yulstring}; m_functions["gt"_yulstring].returns = {"bool"_yulstring}; m_functions["slt"_yulstring].returns = {"bool"_yulstring}; diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 609473acc310..44be41fc4631 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -365,7 +365,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line return TestResult::FatalError; } - m_obtainedResult = AsmPrinter{}(*m_ast) + "\n"; + m_obtainedResult = AsmPrinter{*m_dialect}(*m_ast) + "\n"; if (m_optimizerStep != m_validatedSettings["step"]) { diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 23e5357347f7..998c25bae184 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -248,7 +248,7 @@ class YulOpti default: cout << "Unknown option." << endl; } - source = AsmPrinter{}(*m_ast); + source = AsmPrinter{m_dialect}(*m_ast); } } From e7b95de315df91e56c59f688e7baa2e18fc52feb Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 16 Jan 2020 18:06:27 +0100 Subject: [PATCH 021/160] Tests for default type parsing. --- test/libyul/Parser.cpp | 51 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 95f7dd38f1f2..5577e46f7ff3 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -49,7 +50,7 @@ namespace solidity::yul::test namespace { -bool parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter) +shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter) { try { @@ -58,18 +59,19 @@ bool parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorR if (parserResult) { yul::AsmAnalysisInfo analysisInfo; - return (yul::AsmAnalyzer( + if (yul::AsmAnalyzer( analysisInfo, errorReporter, _dialect - )).analyze(*parserResult); + ).analyze(*parserResult)) + return parserResult; } } catch (FatalError const&) { BOOST_FAIL("Fatal error leaked."); } - return false; + return {}; } std::optional parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarnings = true) @@ -564,6 +566,47 @@ BOOST_AUTO_TEST_CASE(builtins_analysis) CHECK_ERROR_DIALECT("{ let a, b := builtin(1, 2) }", DeclarationError, "Variable count mismatch: 2 variables and 3 values.", dialect); } +BOOST_AUTO_TEST_CASE(default_types_set) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + shared_ptr result = parse( + "{" + "let x:bool := true:bool " + "let y := add(1, 2) " + "switch y case 0 {} default {} " + "}", + EVMDialectTyped::instance(EVMVersion{}), + reporter + ); + BOOST_REQUIRE(!!result); + + // Use no dialect so that all types are printed. + // This tests that the default types are properly assigned. + BOOST_CHECK_EQUAL(AsmPrinter{}(*result), + "{\n" + " let x:bool := true:bool\n" + " let y:u256 := add(1:u256, 2:u256)\n" + " switch y\n" + " case 0:u256 { }\n" + " default { }\n" + "}" + ); + + // Now test again with type dialect. Now the default types + // should be omitted. + BOOST_CHECK_EQUAL(AsmPrinter{EVMDialectTyped::instance(EVMVersion{})}(*result), + "{\n" + " let x:bool := true:bool\n" + " let y := add(1, 2)\n" + " switch y\n" + " case 0 { }\n" + " default { }\n" + "}" + ); +} + + BOOST_AUTO_TEST_SUITE_END() From 23667ce12d4e6e72a9221c945c3c60dda3907b85 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 16 Jan 2020 20:27:33 +0100 Subject: [PATCH 022/160] Update Yul optimizer tests. --- .../disambiguator/for_statement.yul | 8 ++++---- .../disambiguator/funtion_call.yul | 12 ++++-------- .../disambiguator/if_statement.yul | 4 ++-- .../yulOptimizerTests/disambiguator/long_names.yul | 6 +++--- .../disambiguator/switch_statement.yul | 10 +++++----- .../yulOptimizerTests/disambiguator/variables.yul | 6 +++--- .../disambiguator/variables_clash.yul | 8 ++++---- .../disambiguator/variables_inside_functions.yul | 14 +++++++------- .../yulOptimizerTests/expressionInliner/simple.yul | 8 ++++---- .../expressionInliner/with_args.yul | 6 +++--- .../functionGrouper/empty_block.yul | 6 +++--- .../functionGrouper/multi_fun_mixed.yul | 12 ++++++------ .../functionGrouper/nested_fun.yul | 10 +++++----- .../functionGrouper/single_fun.yul | 4 ++-- .../functionHoister/empty_block.yul | 6 +++--- .../functionHoister/multi_mixed.yul | 12 ++++++------ .../yulOptimizerTests/functionHoister/nested.yul | 10 +++++----- .../yulOptimizerTests/functionHoister/single.yul | 4 ++-- .../yulOptimizerTests/mainFunction/empty_block.yul | 6 +++--- .../mainFunction/multi_fun_mixed.yul | 12 ++++++------ .../yulOptimizerTests/mainFunction/nested_fun.yul | 10 +++++----- .../mainFunction/{sigle_fun.yul => single_fun.yul} | 4 ++-- 22 files changed, 87 insertions(+), 91 deletions(-) rename test/libyul/yulOptimizerTests/mainFunction/{sigle_fun.yul => single_fun.yul} (87%) diff --git a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul index 79b372972a8f..ce79ef5e6bd9 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul @@ -7,13 +7,13 @@ } } // ==== -// step: disambiguator // dialect: yul +// step: disambiguator // ---- // { -// { let a:u256, b:u256 } +// { let a, b } // { -// for { let a_1:u256 } a_1 { a_1 := a_1 } -// { let b_2:u256 := a_1 } +// for { let a_1 } a_1 { a_1 := a_1 } +// { let b_2 := a_1 } // } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul index fe54227d35bd..7a4cfe739a0e 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul @@ -7,17 +7,13 @@ } } // ==== -// step: disambiguator // dialect: yul +// step: disambiguator // ---- // { +// { let a, b, c, d, f } // { -// let a:u256, b:u256, c:u256, d:u256, f:u256 -// } -// { -// function f_1(a_2:u256) -> c_3:u256, d_4:u256 -// { -// let b_5:u256, c_1:u256 := f_1(a_2) -// } +// function f_1(a_2) -> c_3, d_4 +// { let b_5, c_1 := f_1(a_2) } // } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul index 2dee55ae7e2f..def0a2d3159b 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul @@ -6,11 +6,11 @@ } } // ==== -// step: disambiguator // dialect: yul +// step: disambiguator // ---- // { -// { let a:u256, b:u256, c:u256 } +// { let a, b, c } // { // let a_1:bool // if a_1 { let b_2:bool := a_1 } diff --git a/test/libyul/yulOptimizerTests/disambiguator/long_names.yul b/test/libyul/yulOptimizerTests/disambiguator/long_names.yul index b5db97637adb..b403dc095c44 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/long_names.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/long_names.yul @@ -1,13 +1,13 @@ { { let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 } { let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 } } // ==== -// step: disambiguator // dialect: yul +// step: disambiguator // ---- // { // { -// let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 +// let aanteuhdaoneudbrgkjiuaothduiathudaoeuh // } // { -// let aanteuhdaoneudbrgkjiuaothduiathudaoeuh_1:u256 +// let aanteuhdaoneudbrgkjiuaothduiathudaoeuh_1 // } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul index 647c78fda6ea..0948b51b9470 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul @@ -8,15 +8,15 @@ } } // ==== -// step: disambiguator // dialect: yul +// step: disambiguator // ---- // { -// { let a:u256, b:u256, c:u256 } +// { let a, b, c } // { -// let a_1:u256 +// let a_1 // switch a_1 -// case 0:u256 { let b_2:u256 := a_1 } -// default { let c_3:u256 := a_1 } +// case 0 { let b_2 := a_1 } +// default { let c_3 := a_1 } // } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables.yul b/test/libyul/yulOptimizerTests/disambiguator/variables.yul index 319809706808..7b197f4205b4 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables.yul @@ -1,9 +1,9 @@ { { let a:u256 } { let a:u256 } } // ==== -// step: disambiguator // dialect: yul +// step: disambiguator // ---- // { -// { let a:u256 } -// { let a_1:u256 } +// { let a } +// { let a_1 } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul index 9474dfc86cb8..9c72b82ff885 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul @@ -1,12 +1,12 @@ { { let a:u256 let a_1:u256 } { let a:u256 } } // ==== -// step: disambiguator // dialect: yul +// step: disambiguator // ---- // { // { -// let a:u256 -// let a_1:u256 +// let a +// let a_1 // } -// { let a_2:u256 } +// { let a_2 } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul index 66d36e43120b..fecb67a653df 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul @@ -6,18 +6,18 @@ } } // ==== -// step: disambiguator // dialect: yul +// step: disambiguator // ---- // { // { -// let c:u256 -// let b:u256 +// let c +// let b // } -// function f(a:u256, c_1:u256) -> b_2:u256 -// { let x:u256 } +// function f(a, c_1) -> b_2 +// { let x } // { -// let a_3:u256 -// let x_4:u256 +// let a_3 +// let x_4 // } // } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/simple.yul b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul index c1a1c147e222..8c3cd28e3ee0 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/simple.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul @@ -3,11 +3,11 @@ let y:u256 := f() } // ==== -// step: expressionInliner // dialect: yul +// step: expressionInliner // ---- // { -// function f() -> x:u256 -// { x := 2:u256 } -// let y:u256 := 2:u256 +// function f() -> x +// { x := 2 } +// let y := 2 // } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul index 8dd694aec5af..5b73ed3fe025 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul @@ -3,11 +3,11 @@ let y:u256 := f(7:u256) } // ==== -// step: expressionInliner // dialect: yul +// step: expressionInliner // ---- // { -// function f(a:u256) -> x:u256 +// function f(a) -> x // { x := a } -// let y:u256 := 7:u256 +// let y := 7 // } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul index 950118647507..f08de996b807 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul @@ -1,16 +1,16 @@ { let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } } // ==== -// step: functionGrouper // dialect: yul +// step: functionGrouper // ---- // { // { -// let a:u256 +// let a // { } // } // function f() -> x:bool // { -// let b:u256 := 4:u256 +// let b := 4 // { } // for { } f() { } // { } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul index 559452a98a19..5fb398ad7516 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul @@ -5,17 +5,17 @@ let e:u256 } // ==== -// step: functionGrouper // dialect: yul +// step: functionGrouper // ---- // { // { -// let a:u256 -// let c:u256 -// let e:u256 +// let a +// let c +// let e // } // function f() -// { let b:u256 } +// { let b } // function g() -// { let d:u256 } +// { let d } // } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul index 14b28fbabb8d..455da2ef5bbe 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul @@ -9,16 +9,16 @@ } } // ==== -// step: functionGrouper // dialect: yul +// step: functionGrouper // ---- // { -// { let a:u256 } +// { let a } // function f() // { -// let b:u256 +// let b // function g() -// { let c:u256 } -// let d:u256 +// { let c } +// let d // } // } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul index 94af922877e9..f7836b6c64fc 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul @@ -2,11 +2,11 @@ let a:u256 function f() {} } // ==== -// step: functionGrouper // dialect: yul +// step: functionGrouper // ---- // { -// { let a:u256 } +// { let a } // function f() // { } // } diff --git a/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul index 4a7c0a7a22a4..276fd4b0229e 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul @@ -8,14 +8,14 @@ } } // ==== -// step: functionHoister // dialect: yul +// step: functionHoister // ---- // { -// let a:u256 +// let a // function f() -> x:bool // { -// let b:u256 := 4:u256 +// let b := 4 // for { } f() { } // { } // } diff --git a/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul index 78e4b49aba87..4e9e9604f3e7 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul @@ -6,15 +6,15 @@ let e:u256 } // ==== -// step: functionHoister // dialect: yul +// step: functionHoister // ---- // { -// let a:u256 -// let c:u256 -// let e:u256 +// let a +// let c +// let e // function f() -// { let b:u256 } +// { let b } // function g() -// { let d:u256 } +// { let d } // } diff --git a/test/libyul/yulOptimizerTests/functionHoister/nested.yul b/test/libyul/yulOptimizerTests/functionHoister/nested.yul index 8135985037a5..89ea5c0ffc23 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/nested.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/nested.yul @@ -7,16 +7,16 @@ } } // ==== -// step: functionHoister // dialect: yul +// step: functionHoister // ---- // { -// let a:u256 +// let a // function g() -// { let c:u256 } +// { let c } // function f() // { -// let b:u256 -// let d:u256 +// let b +// let d // } // } diff --git a/test/libyul/yulOptimizerTests/functionHoister/single.yul b/test/libyul/yulOptimizerTests/functionHoister/single.yul index 70ecfdb8e082..6cc82e68ad09 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/single.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/single.yul @@ -3,11 +3,11 @@ function f() {} } // ==== -// step: functionHoister // dialect: yul +// step: functionHoister // ---- // { -// let a:u256 +// let a // function f() // { } // } diff --git a/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul index 09a04e1d3734..741efd42b4e4 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul @@ -8,18 +8,18 @@ } } // ==== -// step: mainFunction // dialect: yul +// step: mainFunction // ---- // { // function main() // { -// let a:u256 +// let a // { } // } // function f() -> x:bool // { -// let b:u256 := 4:u256 +// let b := 4 // { } // for { } f() { } // { } diff --git a/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul index 7aaab11e5a0f..69fb0bef0cae 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul @@ -6,18 +6,18 @@ let e:u256 } // ==== -// step: mainFunction // dialect: yul +// step: mainFunction // ---- // { // function main() // { -// let a:u256 -// let c:u256 -// let e:u256 +// let a +// let c +// let e // } // function f() -// { let b:u256 } +// { let b } // function g() -// { let d:u256 } +// { let d } // } diff --git a/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul index 61cb672333a0..b97bb2f4d861 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul @@ -7,17 +7,17 @@ } } // ==== -// step: mainFunction // dialect: yul +// step: mainFunction // ---- // { // function main() -// { let a:u256 } +// { let a } // function f() // { -// let b:u256 +// let b // function g() -// { let c:u256 } -// let d:u256 +// { let c } +// let d // } // } diff --git a/test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul similarity index 87% rename from test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul rename to test/libyul/yulOptimizerTests/mainFunction/single_fun.yul index 63154e8c3ffb..1ab108f6b2e8 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul @@ -3,12 +3,12 @@ function f() {} } // ==== -// step: mainFunction // dialect: yul +// step: mainFunction // ---- // { // function main() -// { let a:u256 } +// { let a } // function f() // { } // } From d07dd550961ce15f7b86210925cfbc7c4aa9ac9a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 16 Jan 2020 20:35:42 +0100 Subject: [PATCH 023/160] Update wasm tests. --- test/cmdlineTests/evm_to_wasm/output | 36 +++++++++---------- .../standard_eWasm_requested/output.json | 16 +++++---- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index 1c31f9488f2e..2832ed7b7ac3 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -15,37 +15,37 @@ object "object" { function main() { let _1 := 0 - mstore_internal(_1, _1, _1, _1, _1) - mstore_internal(32, _1, _1, _1, 1) - eth.storageStore(_1, 32) + mstore_internal(0:i64, _1, _1, _1, _1) + mstore_internal(32:i64, _1, _1, _1, 1) + eth.storageStore(0:i64, 32:i64) } - function endian_swap_16(x) -> y + function endian_swap_16(x:i64) -> y:i64 { - y := i64.or(i64.and(i64.shl(x, 8), 0xff00), i64.and(i64.shr_u(x, 8), 0xff)) + y := i64.or(i64.and(i64.shl(x, 8:i64), 0xff00:i64), i64.and(i64.shr_u(x, 8:i64), 0xff:i64)) } - function endian_swap_32(x) -> y + function endian_swap_32(x:i64) -> y:i64 { - let hi := i64.shl(endian_swap_16(x), 16) - y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16))) + let hi:i64 := i64.shl(endian_swap_16(x), 16:i64) + y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16:i64))) } - function endian_swap(x) -> y + function endian_swap(x:i64) -> y:i64 { - let hi := i64.shl(endian_swap_32(x), 32) - y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) + let hi:i64 := i64.shl(endian_swap_32(x), 32:i64) + y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32:i64))) } - function mstore_internal(pos, y1, y2, y3, y4) + function mstore_internal(pos:i64, y1:i64, y2:i64, y3:i64, y4:i64) { i64.store(pos, endian_swap(y1)) - i64.store(i64.add(pos, 8), endian_swap(y2)) - i64.store(i64.add(pos, 16), endian_swap(y3)) - i64.store(i64.add(pos, 24), endian_swap(y4)) + i64.store(i64.add(pos, 8:i64), endian_swap(y2)) + i64.store(i64.add(pos, 16:i64), endian_swap(y3)) + i64.store(i64.add(pos, 24:i64), endian_swap(y4)) } } } Binary representation: -0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010ab501052801017e420021002000200020002000200010054220200020002000420110052000a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3501007e2000a720011004370300200042087ca720021004370300200042107ca720031004370300200042187ca7200410043703000b +0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010ab501052801017e420021004200200020002000200010054220200020002000420110054200a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3501007e2000a720011004370300200042087ca720021004370300200042107ca720031004370300200042187ca7200410043703000b Text representation: (module @@ -56,9 +56,9 @@ Text representation: (func $main (local $_1 i64) (local.set $_1 (i64.const 0)) - (call $mstore_internal (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) + (call $mstore_internal (i64.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $mstore_internal (i64.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1)) - (call $eth.storageStore (i32.wrap_i64 (local.get $_1)) (i32.wrap_i64 (i64.const 32))) + (call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32))) ) (func $endian_swap_16 diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index 11e7bb252e54..0652b99e4494 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -10,21 +10,23 @@ (local $p i64) (local $r i64) (local $hi i64) - (local $y i64) (local $hi_1 i64) + (local $y i64) + (local $hi_2 i64) (local $_2 i64) (local.set $_1 (i64.const 0)) (local.set $p (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64))) (local.set $r (i64.add (local.get $p) (i64.const 64))) (if (i64.ne (i64.extend_i32_u (i64.lt_u (local.get $r) (local.get $p))) (i64.const 0)) (then (unreachable))) - (local.set $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (local.get $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) + (local.set $hi (i64.shl (call $endian_swap_16 (local.get $_1)) (i64.const 16))) + (local.set $hi_1 (i64.shl (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) + (local.set $y (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) (i64.store (i32.wrap_i64 (local.get $r)) (local.get $y)) (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 8))) (local.get $y)) (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 16))) (local.get $y)) - (local.set $hi_1 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) - (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 24))) (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) + (local.set $hi_2 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) + (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 24))) (i64.or (local.get $hi_2) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) (local.set $_2 (datasize \"C_2_deployed\")) (call $eth.codeCopy (i32.wrap_i64 (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\"))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_2)))) (call $eth.finish (i32.wrap_i64 (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_2)))) @@ -37,9 +39,9 @@ (param $x4 i64) (result i64) (local $v i64) - (if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then + (if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then (unreachable))) - (if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then + (if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then (unreachable))) (local.set $v (local.get $x4)) (local.get $v) From a66782537aa9f6ac6b9154e57311e571bd27e509 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Jan 2020 18:14:03 +0100 Subject: [PATCH 024/160] Use old yul dialect only in tests. --- libyul/AssemblyStack.cpp | 6 +++--- libyul/Dialect.cpp | 2 +- libyul/Dialect.h | 3 ++- libyul/Object.cpp | 8 ++++---- libyul/Object.h | 8 +++++--- test/cmdlineTests/evm_to_wasm/output | 30 ++++++++++++++-------------- test/libyul/Common.cpp | 2 +- test/libyul/Parser.cpp | 6 +++--- test/libyul/YulOptimizerTest.cpp | 2 +- 9 files changed, 35 insertions(+), 32 deletions(-) diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 87215179e9fc..9f7cc57a5945 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -59,12 +59,12 @@ Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _ case AssemblyStack::Language::StrictAssembly: return EVMDialect::strictAssemblyForEVMObjects(_version); case AssemblyStack::Language::Yul: - return Dialect::yul(); + return EVMDialectTyped::instance(_version); case AssemblyStack::Language::Ewasm: return WasmDialect::instance(); } yulAssert(false, ""); - return Dialect::yul(); + return Dialect::yulDeprecated(); } } @@ -236,7 +236,7 @@ string AssemblyStack::print() const { yulAssert(m_parserResult, ""); yulAssert(m_parserResult->code, ""); - return m_parserResult->toString(m_language == Language::Yul) + "\n"; + return m_parserResult->toString(&languageToDialect(m_language, m_evmVersion)) + "\n"; } shared_ptr AssemblyStack::parserResult() const diff --git a/libyul/Dialect.cpp b/libyul/Dialect.cpp index fd9f54d0a96a..6bc056a759a0 100644 --- a/libyul/Dialect.cpp +++ b/libyul/Dialect.cpp @@ -23,7 +23,7 @@ using namespace solidity::yul; using namespace std; -Dialect const& Dialect::yul() +Dialect const& Dialect::yulDeprecated() { static unique_ptr dialect; static YulStringRepository::ResetCallback callback{[&] { dialect.reset(); }}; diff --git a/libyul/Dialect.h b/libyul/Dialect.h index f08a04e6506c..f4eb91f2be92 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -65,7 +65,8 @@ struct Dialect: boost::noncopyable Dialect() = default; virtual ~Dialect() = default; - static Dialect const& yul(); + /// Old "yul" dialect. This is only used for testing. + static Dialect const& yulDeprecated(); }; } diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 2d4a5e33ae96..13ad9f8caf48 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -45,18 +45,18 @@ string indent(std::string const& _input) } -string Data::toString(bool) const +string Data::toString(Dialect const*) const { return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\""; } -string Object::toString(bool _yul) const +string Object::toString(Dialect const* _dialect) const { yulAssert(code, "No code"); - string inner = "code " + AsmPrinter{}(*code); + string inner = "code " + (_dialect ? AsmPrinter{*_dialect} : AsmPrinter{})(*code); for (auto const& obj: subObjects) - inner += "\n" + obj->toString(_yul); + inner += "\n" + obj->toString(_dialect); return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}"; } diff --git a/libyul/Object.h b/libyul/Object.h index d7926d8bbbcf..6ae79ed42cf1 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -30,6 +30,7 @@ namespace solidity::yul { +struct Dialect; struct AsmAnalysisInfo; @@ -39,7 +40,8 @@ struct AsmAnalysisInfo; struct ObjectNode { virtual ~ObjectNode() = default; - virtual std::string toString(bool _yul) const = 0; + virtual std::string toString(Dialect const* _dialect) const = 0; + std::string toString() { return toString(nullptr); } YulString name; }; @@ -50,7 +52,7 @@ struct ObjectNode struct Data: ObjectNode { Data(YulString _name, bytes _data): data(std::move(_data)) { name = _name; } - std::string toString(bool _yul) const override; + std::string toString(Dialect const* _dialect) const override; bytes data; }; @@ -62,7 +64,7 @@ struct Object: ObjectNode { public: /// @returns a (parseable) string representation. Includes types if @a _yul is set. - std::string toString(bool _yul) const override; + std::string toString(Dialect const* _dialect) const override; /// @returns the set of names of data objects accessible from within the code of /// this object. diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index 2832ed7b7ac3..a85239b230df 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -15,30 +15,30 @@ object "object" { function main() { let _1 := 0 - mstore_internal(0:i64, _1, _1, _1, _1) - mstore_internal(32:i64, _1, _1, _1, 1) - eth.storageStore(0:i64, 32:i64) + mstore_internal(0, _1, _1, _1, _1) + mstore_internal(32, _1, _1, _1, 1) + eth.storageStore(0, 32) } - function endian_swap_16(x:i64) -> y:i64 + function endian_swap_16(x) -> y { - y := i64.or(i64.and(i64.shl(x, 8:i64), 0xff00:i64), i64.and(i64.shr_u(x, 8:i64), 0xff:i64)) + y := i64.or(i64.and(i64.shl(x, 8), 0xff00), i64.and(i64.shr_u(x, 8), 0xff)) } - function endian_swap_32(x:i64) -> y:i64 + function endian_swap_32(x) -> y { - let hi:i64 := i64.shl(endian_swap_16(x), 16:i64) - y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16:i64))) + let hi := i64.shl(endian_swap_16(x), 16) + y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16))) } - function endian_swap(x:i64) -> y:i64 + function endian_swap(x) -> y { - let hi:i64 := i64.shl(endian_swap_32(x), 32:i64) - y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32:i64))) + let hi := i64.shl(endian_swap_32(x), 32) + y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) } - function mstore_internal(pos:i64, y1:i64, y2:i64, y3:i64, y4:i64) + function mstore_internal(pos, y1, y2, y3, y4) { i64.store(pos, endian_swap(y1)) - i64.store(i64.add(pos, 8:i64), endian_swap(y2)) - i64.store(i64.add(pos, 16:i64), endian_swap(y3)) - i64.store(i64.add(pos, 24:i64), endian_swap(y4)) + i64.store(i64.add(pos, 8), endian_swap(y2)) + i64.store(i64.add(pos, 16), endian_swap(y3)) + i64.store(i64.add(pos, 24), endian_swap(y4)) } } } diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index 6ea2bb63a359..ff1517eecd5f 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -48,7 +48,7 @@ namespace { Dialect const& defaultDialect(bool _yul) { - return _yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()); + return _yul ? yul::Dialect::yulDeprecated() : yul::EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()); } } diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 5577e46f7ff3..7e1dfb7bfdf4 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -99,12 +99,12 @@ std::optional parseAndReturnFirstError(string const& _source, Dialect con return {}; } -bool successParse(std::string const& _source, Dialect const& _dialect = Dialect::yul(), bool _allowWarnings = true) +bool successParse(std::string const& _source, Dialect const& _dialect = Dialect::yulDeprecated(), bool _allowWarnings = true) { return !parseAndReturnFirstError(_source, _dialect, _allowWarnings); } -Error expectError(std::string const& _source, Dialect const& _dialect = Dialect::yul(), bool _allowWarnings = false) +Error expectError(std::string const& _source, Dialect const& _dialect = Dialect::yulDeprecated(), bool _allowWarnings = false) { auto error = parseAndReturnFirstError(_source, _dialect, _allowWarnings); @@ -122,7 +122,7 @@ do \ BOOST_CHECK(solidity::frontend::test::searchErrorMessage(err, (substring))); \ } while(0) -#define CHECK_ERROR(text, typ, substring) CHECK_ERROR_DIALECT(text, typ, substring, Dialect::yul()) +#define CHECK_ERROR(text, typ, substring) CHECK_ERROR_DIALECT(text, typ, substring, Dialect::yulDeprecated()) BOOST_AUTO_TEST_SUITE(YulParser) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 44be41fc4631..c4a6a48d6e48 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -105,7 +105,7 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename) { auto dialectName = m_settings["dialect"]; if (dialectName == "yul") - m_dialect = &Dialect::yul(); + m_dialect = &Dialect::yulDeprecated(); else if (dialectName == "ewasm") m_dialect = &WasmDialect::instance(); else if (dialectName == "evm") From d3b53ee394d7c9761883800d2e629a94e48538ba Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Jan 2020 18:14:20 +0100 Subject: [PATCH 025/160] Make bool type optional for bool literals. --- libyul/AsmPrinter.cpp | 14 +++++++++++--- libyul/AsmPrinter.h | 2 +- libyul/Dialect.h | 1 + test/libyul/Parser.cpp | 5 ++++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index 671a0fa24018..93fb089edb91 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -50,7 +50,7 @@ string AsmPrinter::operator()(Literal const& _literal) const return _literal.value.str() + appendTypeName(_literal.type); case LiteralKind::Boolean: yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "Invalid bool literal."); - return ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type); + return ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type, true); case LiteralKind::String: break; } @@ -237,9 +237,17 @@ string AsmPrinter::formatTypedName(TypedName _variable) const return _variable.name.str() + appendTypeName(_variable.type); } -string AsmPrinter::appendTypeName(YulString _type) const +string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const { - if (_type.empty() || (m_dialect && _type == m_dialect->defaultType)) + if (m_dialect && !_type.empty()) + { + if (!_isBoolLiteral && _type == m_dialect->defaultType) + _type = {}; + else if (_isBoolLiteral && _type == m_dialect->boolType && !m_dialect->defaultType.empty()) + // Special case: If we have a bool type but empty default type, do not remove the type. + _type = {}; + } + if (_type.empty()) return {}; else return ":" + _type.str(); diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 9edc3a7c354b..f7ee9ff54b86 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -58,7 +58,7 @@ class AsmPrinter private: std::string formatTypedName(TypedName _variable) const; - std::string appendTypeName(YulString _type) const; + std::string appendTypeName(YulString _type, bool _isBoolLiteral = false) const; Dialect const* m_dialect = nullptr; }; diff --git a/libyul/Dialect.h b/libyul/Dialect.h index f4eb91f2be92..c25ba1ac3569 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -48,6 +48,7 @@ struct BuiltinFunction struct Dialect: boost::noncopyable { + /// Default type, can be omitted. YulString defaultType; /// Type used for the literals "true" and "false". YulString boolType; diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 7e1dfb7bfdf4..8ec5512587a1 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -573,6 +573,7 @@ BOOST_AUTO_TEST_CASE(default_types_set) shared_ptr result = parse( "{" "let x:bool := true:bool " + "let z:bool := true " "let y := add(1, 2) " "switch y case 0 {} default {} " "}", @@ -586,6 +587,7 @@ BOOST_AUTO_TEST_CASE(default_types_set) BOOST_CHECK_EQUAL(AsmPrinter{}(*result), "{\n" " let x:bool := true:bool\n" + " let z:bool := true:bool\n" " let y:u256 := add(1:u256, 2:u256)\n" " switch y\n" " case 0:u256 { }\n" @@ -597,7 +599,8 @@ BOOST_AUTO_TEST_CASE(default_types_set) // should be omitted. BOOST_CHECK_EQUAL(AsmPrinter{EVMDialectTyped::instance(EVMVersion{})}(*result), "{\n" - " let x:bool := true:bool\n" + " let x:bool := true\n" + " let z:bool := true\n" " let y := add(1, 2)\n" " switch y\n" " case 0 { }\n" From db33ff640815597d3b08da21a136af27eb2c8240 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Jan 2020 18:45:35 +0100 Subject: [PATCH 026/160] Clean up built-in instructions. --- libyul/backends/evm/EVMDialect.cpp | 17 ++++++++++++++--- libyul/backends/evm/EVMDialect.h | 3 +-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index baab0a268ae9..4115df4e1938 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -226,19 +226,30 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA m_functions["slt"_yulstring].returns = {"bool"_yulstring}; m_functions["sgt"_yulstring].returns = {"bool"_yulstring}; m_functions["eq"_yulstring].returns = {"bool"_yulstring}; - m_functions["iszero"_yulstring].returns = {"bool"_yulstring}; + + // "not" and "bitnot" replace "iszero" and "not" + m_functions["bitnot"_yulstring] = m_functions["not"_yulstring]; + m_functions["bitnot"_yulstring].name = "bitnot"_yulstring; + m_functions["not"_yulstring] = m_functions["iszero"_yulstring]; + m_functions["not"_yulstring].name = "not"_yulstring; + m_functions["not"_yulstring].returns = {"bool"_yulstring}; + m_functions["not"_yulstring].parameters = {"bool"_yulstring}; + m_functions.erase("iszero"_yulstring); + m_functions["bitand"_yulstring] = m_functions["and"_yulstring]; + m_functions["bitand"_yulstring].name = "bitand"_yulstring; m_functions["bitor"_yulstring] = m_functions["or"_yulstring]; + m_functions["bitor"_yulstring].name = "bitor"_yulstring; m_functions["bitxor"_yulstring] = m_functions["xor"_yulstring]; + m_functions["bitxor"_yulstring].name = "bitxor"_yulstring; m_functions["and"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring}; m_functions["and"_yulstring].returns = {"bool"_yulstring}; m_functions["or"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring}; m_functions["or"_yulstring].returns = {"bool"_yulstring}; m_functions["xor"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring}; m_functions["xor"_yulstring].returns = {"bool"_yulstring}; - m_functions["isfalse"_yulstring] = m_functions["iszero"_yulstring]; - m_functions["isfalse"_yulstring].parameters = {"bool"_yulstring}; m_functions["popbool"_yulstring] = m_functions["pop"_yulstring]; + m_functions["popbool"_yulstring].name = "popbool"_yulstring; m_functions["popbool"_yulstring].parameters = {"bool"_yulstring}; m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, false, []( FunctionCall const&, diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index a670b020f018..40842fa57e63 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -93,8 +93,7 @@ struct EVMDialect: public Dialect * - All comparison functions return type bool * - bitwise operations are called bitor, bitand, bitxor and bitnot * - and, or, xor take bool and return bool - * - iszero returns bool - * - isfalse takes bool and returns bool + * - iszero is replaced by not, which takes bool and returns bool * - there are conversion functions bool_to_u256 and u256_to_bool. * - there is popbool */ From 2bcdb24ebe41442adc357ddca102281fc5faad7d Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Jan 2020 18:45:47 +0100 Subject: [PATCH 027/160] Cause failing assertion for invalid u256 to bool conversion. --- libyul/backends/evm/EVMDialect.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 4115df4e1938..3274f440f63e 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -266,10 +266,15 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA BuiltinContext&, std::function _visitArguments ) { - // TODO Should a value larger than 1 be invalid? + // A value larger than 1 causes an invalid instruction. _visitArguments(); - _assembly.appendInstruction(evmasm::Instruction::ISZERO); - _assembly.appendInstruction(evmasm::Instruction::ISZERO); + _assembly.appendConstant(2); + _assembly.appendInstruction(evmasm::Instruction::DUP2); + _assembly.appendInstruction(evmasm::Instruction::LT); + AbstractAssembly::LabelID inRange = _assembly.newLabelId(); + _assembly.appendJumpToIf(inRange); + _assembly.appendInstruction(evmasm::Instruction::INVALID); + _assembly.appendLabel(inRange); })); m_functions["u256_to_bool"_yulstring].returns = {"bool"_yulstring}; } From d41e1db74a874c69f4a1c660dd9ebccc5fdd788a Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Jan 2020 19:10:53 +0100 Subject: [PATCH 028/160] Adjust and add commandline tests for yul. --- test/cmdlineTests.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 95e7bb31543f..754cd0bac24a 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -395,7 +395,9 @@ printTask "Testing assemble, yul, strict-assembly and optimize..." # Test yul and strict assembly output # Non-empty code results in non-empty binary representation with optimizations turned off, # while it results in empty binary representation with optimizations turned on. - test_solc_assembly_output "{ let x:u256 := 0:u256 }" "{ let x:u256 := 0:u256 }" "--yul" + test_solc_assembly_output "{ let x:u256 := 0:u256 }" "{ let x := 0 }" "--yul" + test_solc_assembly_output "{ let x:u256 := bitnot(7:u256) }" "{ let x := bitnot(7) }" "--yul" + test_solc_assembly_output "{ let t:bool := not(true) }" "{ let t:bool := not(true) }" "--yul" test_solc_assembly_output "{ let x := 0 }" "{ let x := 0 }" "--strict-assembly" test_solc_assembly_output "{ let x := 0 }" "{ { } }" "--strict-assembly --optimize" ) From 642653ea04109724cc8a698734dce9f47a8b656e Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 22 Jan 2020 17:12:04 +0100 Subject: [PATCH 029/160] Make yul::Parser::parse() return unique_ptr rather than shared_ptr - unique_ptr is more flexible and generally recommended for factory methods. It gets automatically converted to shared_ptr if necessary. Returning shared_ptr, on the other hand, forces the caller to use shared_ptr because a conversion to unique_ptr is not possible. --- libsolidity/codegen/CompilerContext.cpp | 2 +- libyul/AsmParser.cpp | 4 ++-- libyul/AsmParser.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ef5534a3cf0d..9f233cfc008b 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -385,7 +385,7 @@ void CompilerContext::appendInlineAssembly( ErrorReporter errorReporter(errors); auto scanner = make_shared(langutil::CharStream(_assembly, "--CODEGEN--")); yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); - auto parserResult = yul::Parser(errorReporter, dialect).parse(scanner, false); + shared_ptr parserResult = yul::Parser(errorReporter, dialect).parse(scanner, false); #ifdef SOL_OUTPUT_ASM cout << yul::AsmPrinter()(*parserResult) << endl; #endif diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 3b955b96f78c..7ba122775154 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -37,7 +37,7 @@ using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul; -shared_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) +unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) { m_recursionDepth = 0; @@ -47,7 +47,7 @@ shared_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ try { m_scanner = _scanner; - auto block = make_shared(parseBlock()); + auto block = make_unique(parseBlock()); if (!_reuseScanner) expectToken(Token::EOS); return block; diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 8583550e7c19..8abdcc91a891 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -52,7 +52,7 @@ class Parser: public langutil::ParserBase /// Parses an inline assembly block starting with `{` and ending with `}`. /// @param _reuseScanner if true, do check for end of input after the `}`. /// @returns an empty shared pointer on error. - std::shared_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); + std::unique_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); /// @returns a map of all EVM instructions available to assembly. static std::map const& instructions(); From af7139c19f99243dffea50e92c35b0e3a5a5698e Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 22 Jan 2020 17:12:41 +0100 Subject: [PATCH 030/160] Enable colorized compiler output when running gcc via cmake --- cmake/EthCompilerSettings.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 4d73a52cf19b..cc4489e9faee 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -54,6 +54,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA message(FATAL_ERROR "${PROJECT_NAME} requires g++ 5.0 or greater.") endif () + # Use fancy colors in the compiler diagnostics + add_compile_options(-fdiagnostics-color) + # Additional Clang-specific compiler settings. elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") From 97e9ce9d0af3354e794eb378edac1f913f736e7f Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 30 Jan 2020 12:18:27 +0100 Subject: [PATCH 031/160] Disable ossfuzzing on circle ci It currently takes >5h. It is not known whether due to a bug or whether it's normal. --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 868b292b299f..a2afa937d6ad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -723,7 +723,7 @@ workflows: - b_docs: *workflow_trigger_on_tags - b_archlinux: *workflow_trigger_on_tags - b_ubu_cxx20: *workflow_trigger_on_tags - - b_ubu_ossfuzz: *workflow_trigger_on_tags +# - b_ubu_ossfuzz: *workflow_trigger_on_tags # OS/X build and tests - b_osx: *workflow_trigger_on_tags @@ -768,8 +768,8 @@ workflows: jobs: # OSSFUZZ builds and (regression) tests - - b_ubu_ossfuzz: *workflow_trigger_on_tags - - t_ubu_ossfuzz: *workflow_ubuntu1904_ossfuzz +# - b_ubu_ossfuzz: *workflow_trigger_on_tags +# - t_ubu_ossfuzz: *workflow_ubuntu1904_ossfuzz # Code Coverage enabled build and tests - b_ubu_codecov: *workflow_trigger_on_tags From 2ee7e6042b2d1ab0e920d84db86b45a286e7fb63 Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 29 Jan 2020 18:05:18 +0100 Subject: [PATCH 032/160] OptimiserSuite: Remove VarNameCleaner from the list of available steps --- libyul/optimiser/Suite.cpp | 8 +++----- libyul/optimiser/VarNameCleaner.h | 4 ++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 6cf592f8a3fe..b57cf76451c8 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -308,9 +308,7 @@ void OptimiserSuite::run( if (ast.statements.size() > 1 && std::get(ast.statements.front()).statements.empty()) ast.statements.erase(ast.statements.begin()); } - suite.runSequence({ - VarNameCleaner::name - }, ast); + VarNameCleaner::run(suite.m_context, ast); *_object.analysisInfo = AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object); } @@ -366,9 +364,9 @@ map> const& OptimiserSuite::allSteps() SSATransform, StructuralSimplifier, UnusedPruner, - VarDeclInitializer, - VarNameCleaner + VarDeclInitializer >(); + // Does not include VarNameCleaner because it destroys the property of unique names. return instance; } diff --git a/libyul/optimiser/VarNameCleaner.h b/libyul/optimiser/VarNameCleaner.h index 97ad27250d3c..4bd4ca100e0a 100644 --- a/libyul/optimiser/VarNameCleaner.h +++ b/libyul/optimiser/VarNameCleaner.h @@ -39,6 +39,10 @@ struct Dialect; * renumbered by their base name. * Function names are not modified. * + * NOTE: This step destroys the promise of the Disambiguator and thus cannot + * be used in the main loop of the optimizer without running the disambiguator again. + * Because of that, it is not included in the step list of the Optimizer Suite. + * * Prerequisites: Disambiguator, FunctionHoister, FunctionGrouper */ class VarNameCleaner: public ASTModifier From ed5210490dea18a1811596e782d4656cb95631e4 Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 29 Jan 2020 18:35:33 +0100 Subject: [PATCH 033/160] [yulopti] Run disambiguator after VarNameCleaner - This is a trivial change and it makes it safe to use in combination with other step. --- test/tools/yulopti.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 23e5357347f7..96ab59642c53 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -180,6 +180,8 @@ class YulOpti break; case 'l': VarNameCleaner::run(context, *m_ast); + // VarNameCleaner destroys the unique names guarantee of the disambiguator. + disambiguated = false; break; case 'x': ExpressionSplitter::run(context, *m_ast); From ec27c2e507a264ac052fc8f05112890f7cf92adb Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 29 Jan 2020 23:13:42 +0100 Subject: [PATCH 034/160] Introduce AST node for structured documentation. --- liblangutil/Scanner.cpp | 2 + libsolidity/analysis/DocStringAnalyser.cpp | 18 +++--- libsolidity/analysis/DocStringAnalyser.h | 14 ++--- libsolidity/ast/AST.h | 66 ++++++++++++++++++---- libsolidity/ast/ASTAnnotations.h | 14 ++--- libsolidity/ast/ASTForward.h | 1 + libsolidity/ast/ASTVisitor.h | 4 ++ libsolidity/ast/AST_accept.h | 32 +++++++++++ libsolidity/ast/Types.cpp | 6 +- libsolidity/ast/Types.h | 6 +- libsolidity/parsing/Parser.cpp | 40 +++++++------ libsolidity/parsing/Parser.h | 1 + test/libsolidity/SolidityParser.cpp | 2 +- 13 files changed, 147 insertions(+), 59 deletions(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index f9a8aab12b39..f983274057e9 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -428,6 +428,7 @@ Token Scanner::scanSlash() // doxygen style /// comment Token comment; m_skippedComments[NextNext].location.start = firstSlashPosition; + m_skippedComments[NextNext].location.source = m_source; comment = scanSingleLineDocComment(); m_skippedComments[NextNext].location.end = sourcePos(); m_skippedComments[NextNext].token = comment; @@ -454,6 +455,7 @@ Token Scanner::scanSlash() // we actually have a multiline documentation comment Token comment; m_skippedComments[NextNext].location.start = firstSlashPosition; + m_skippedComments[NextNext].location.source = m_source; comment = scanMultiLineDocComment(); m_skippedComments[NextNext].location.end = sourcePos(); m_skippedComments[NextNext].token = comment; diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 1b3bd598b482..cadd12fc3c4e 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -73,7 +73,7 @@ bool DocStringAnalyser::visit(EventDefinition const& _event) void DocStringAnalyser::checkParameters( CallableDeclaration const& _callable, - DocumentedAnnotation& _annotation + StructurallyDocumentedAnnotation& _annotation ) { set validParams; @@ -95,8 +95,8 @@ void DocStringAnalyser::checkParameters( void DocStringAnalyser::handleConstructor( CallableDeclaration const& _callable, - Documented const& _node, - DocumentedAnnotation& _annotation + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation ) { static set const validTags = set{"author", "dev", "notice", "param"}; @@ -106,8 +106,8 @@ void DocStringAnalyser::handleConstructor( void DocStringAnalyser::handleCallable( CallableDeclaration const& _callable, - Documented const& _node, - DocumentedAnnotation& _annotation + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation ) { static set const validTags = set{"author", "dev", "notice", "return", "param"}; @@ -116,16 +116,16 @@ void DocStringAnalyser::handleCallable( } void DocStringAnalyser::parseDocStrings( - Documented const& _node, - DocumentedAnnotation& _annotation, + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation, set const& _validTags, string const& _nodeName ) { DocStringParser parser; - if (_node.documentation() && !_node.documentation()->empty()) + if (_node.documentation() && !_node.documentation()->text()->empty()) { - if (!parser.parse(*_node.documentation(), m_errorReporter)) + if (!parser.parse(*_node.documentation()->text(), m_errorReporter)) m_errorOccured = true; _annotation.docTags = parser.tags(); } diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index 653413346181..2b9e231958ab 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -51,24 +51,24 @@ class DocStringAnalyser: private ASTConstVisitor void checkParameters( CallableDeclaration const& _callable, - DocumentedAnnotation& _annotation + StructurallyDocumentedAnnotation& _annotation ); void handleConstructor( CallableDeclaration const& _callable, - Documented const& _node, - DocumentedAnnotation& _annotation + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation ); void handleCallable( CallableDeclaration const& _callable, - Documented const& _node, - DocumentedAnnotation& _annotation + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation ); void parseDocStrings( - Documented const& _node, - DocumentedAnnotation& _annotation, + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation, std::set const& _validTags, std::string const& _nodeName ); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index d5ef041f9976..5a5e561c9142 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -345,6 +345,30 @@ class VariableScope std::vector m_localVariables; }; +/** + * The doxygen-style, structured documentation class that represents an AST node. + */ +class StructuredDocumentation: public ASTNode +{ +public: + StructuredDocumentation( + int64_t _id, + SourceLocation const& _location, + ASTPointer const& _text + ): ASTNode(_id, _location), m_text(_text) + {} + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + /// @return A shared pointer of an ASTString. + /// Contains doxygen-style, structured documentation that is parsed later on. + ASTPointer const& text() const { return m_text; } + +private: + ASTPointer m_text; +}; + /** * Abstract class that is added to each AST node that can receive documentation. */ @@ -362,6 +386,24 @@ class Documented ASTPointer m_documentation; }; +/** + * Abstract class that is added to each AST node that can receive a structured documentation. + */ +class StructurallyDocumented +{ +public: + virtual ~StructurallyDocumented() = default; + explicit StructurallyDocumented(ASTPointer const& _documentation): m_documentation(_documentation) {} + + /// @return A shared pointer of a FormalDocumentation. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer const& documentation() const { return m_documentation; } + +protected: + ASTPointer m_documentation; +}; + + /** * Abstract class that is added to AST nodes that can be marked as not being fully implemented */ @@ -385,21 +427,21 @@ class ImplementationOptional * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ -class ContractDefinition: public Declaration, public Documented +class ContractDefinition: public Declaration, public StructurallyDocumented { public: ContractDefinition( int64_t _id, SourceLocation const& _location, ASTPointer const& _name, - ASTPointer const& _documentation, + ASTPointer const& _documentation, std::vector> const& _baseContracts, std::vector> const& _subNodes, ContractKind _contractKind = ContractKind::Contract, bool _abstract = false ): Declaration(_id, _location, _name), - Documented(_documentation), + StructurallyDocumented(_documentation), m_baseContracts(_baseContracts), m_subNodes(_subNodes), m_contractKind(_contractKind), @@ -681,7 +723,7 @@ class OverrideSpecifier: public ASTNode std::vector> m_overrides; }; -class FunctionDefinition: public CallableDeclaration, public Documented, public ImplementationOptional +class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional { public: FunctionDefinition( @@ -693,14 +735,14 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public Token _kind, bool _isVirtual, ASTPointer const& _overrides, - ASTPointer const& _documentation, + ASTPointer const& _documentation, ASTPointer const& _parameters, std::vector> const& _modifiers, ASTPointer const& _returnParameters, ASTPointer const& _body ): CallableDeclaration(_id, _location, _name, _visibility, _parameters, _isVirtual, _overrides, _returnParameters), - Documented(_documentation), + StructurallyDocumented(_documentation), ImplementationOptional(_body != nullptr), m_stateMutability(_stateMutability), m_kind(_kind), @@ -870,21 +912,21 @@ class VariableDeclaration: public Declaration /** * Definition of a function modifier. */ -class ModifierDefinition: public CallableDeclaration, public Documented +class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented { public: ModifierDefinition( int64_t _id, SourceLocation const& _location, ASTPointer const& _name, - ASTPointer const& _documentation, + ASTPointer const& _documentation, ASTPointer const& _parameters, bool _isVirtual, ASTPointer const& _overrides, ASTPointer const& _body ): CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides), - Documented(_documentation), + StructurallyDocumented(_documentation), m_body(_body) { } @@ -935,19 +977,19 @@ class ModifierInvocation: public ASTNode /** * Definition of a (loggable) event. */ -class EventDefinition: public CallableDeclaration, public Documented +class EventDefinition: public CallableDeclaration, public StructurallyDocumented { public: EventDefinition( int64_t _id, SourceLocation const& _location, ASTPointer const& _name, - ASTPointer const& _documentation, + ASTPointer const& _documentation, ASTPointer const& _parameters, bool _anonymous = false ): CallableDeclaration(_id, _location, _name, Visibility::Default, _parameters), - Documented(_documentation), + StructurallyDocumented(_documentation), m_anonymous(_anonymous) { } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index ae447f884490..86636684c159 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -56,9 +56,9 @@ struct DocTag std::string paramName; ///< Only used for @param, stores the parameter name. }; -struct DocumentedAnnotation +struct StructurallyDocumentedAnnotation { - virtual ~DocumentedAnnotation() = default; + virtual ~StructurallyDocumentedAnnotation() = default; /// Mapping docstring tag name -> content. std::multimap docTags; }; @@ -101,7 +101,7 @@ struct TypeDeclarationAnnotation: DeclarationAnnotation std::string canonicalName; }; -struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation +struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation { /// List of functions without a body. Can also contain functions from base classes. std::vector unimplementedFunctions; @@ -122,15 +122,15 @@ struct CallableDeclarationAnnotation: DeclarationAnnotation std::set baseFunctions; }; -struct FunctionDefinitionAnnotation: CallableDeclarationAnnotation, DocumentedAnnotation +struct FunctionDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation { }; -struct EventDefinitionAnnotation: CallableDeclarationAnnotation, DocumentedAnnotation +struct EventDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation { }; -struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, DocumentedAnnotation +struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation { }; @@ -142,7 +142,7 @@ struct VariableDeclarationAnnotation: DeclarationAnnotation std::set baseFunctions; }; -struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation +struct StatementAnnotation: ASTAnnotation { }; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index a455f30b2f33..38da35218ebe 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -93,6 +93,7 @@ class PrimaryExpression; class Identifier; class ElementaryTypeNameExpression; class Literal; +class StructuredDocumentation; class VariableScope; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 0ffbbd31a359..90dad003a18b 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -92,6 +92,7 @@ class ASTVisitor virtual bool visit(Identifier& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); } virtual bool visit(Literal& _node) { return visitNode(_node); } + virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); } @@ -143,6 +144,7 @@ class ASTVisitor virtual void endVisit(Identifier& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); } virtual void endVisit(Literal& _node) { endVisitNode(_node); } + virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); } protected: /// Generic function called by default for each node, to be overridden by derived classes @@ -207,6 +209,7 @@ class ASTConstVisitor virtual bool visit(Identifier const& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); } virtual bool visit(Literal const& _node) { return visitNode(_node); } + virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } @@ -258,6 +261,7 @@ class ASTConstVisitor virtual void endVisit(Identifier const& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); } virtual void endVisit(Literal const& _node) { endVisitNode(_node); } + virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); } protected: /// Generic function called by default for each node, to be overridden by derived classes diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 4ca48e5147c0..bc0123d513d7 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -67,10 +67,24 @@ void ImportDirective::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void StructuredDocumentation::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void StructuredDocumentation::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + void ContractDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); listAccept(m_baseContracts, _visitor); listAccept(m_subNodes, _visitor); } @@ -81,6 +95,8 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); listAccept(m_baseContracts, _visitor); listAccept(m_subNodes, _visitor); } @@ -203,6 +219,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); m_parameters->accept(_visitor); @@ -219,6 +237,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); m_parameters->accept(_visitor); @@ -263,6 +283,8 @@ void ModifierDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); m_parameters->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); @@ -275,6 +297,8 @@ void ModifierDefinition::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); m_parameters->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); @@ -308,14 +332,22 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const void EventDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) + { + if (m_documentation) + m_documentation->accept(_visitor); m_parameters->accept(_visitor); + } _visitor.endVisit(*this); } void EventDefinition::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) + { + if (m_documentation) + m_documentation->accept(_visitor); m_parameters->accept(_visitor); + } _visitor.endVisit(*this); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f23ae8a1996c..dbeb63033001 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3323,13 +3323,13 @@ Type const* FunctionType::selfType() const return m_parameterTypes.at(0); } -ASTPointer FunctionType::documentation() const +ASTPointer FunctionType::documentation() const { - auto function = dynamic_cast(m_declaration); + auto function = dynamic_cast(m_declaration); if (function) return function->documentation(); - return ASTPointer(); + return ASTPointer(); } bool FunctionType::padArguments() const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 7ad662dbb874..669250970f96 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1208,9 +1208,9 @@ class FunctionType: public Type /// Currently, this will only return true for internal functions like keccak and ecrecover. bool isPure() const; bool isPayable() const { return m_stateMutability == StateMutability::Payable; } - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer documentation() const; + /// @return A shared pointer of StructuredDocumentation. + /// Can contain a nullptr in which case indicates absence of documentation. + ASTPointer documentation() const; /// true iff arguments are to be padded to multiples of 32 bytes for external calls /// The only functions that do not pad are hash functions, the low-level call functions diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 6de3a3e5be4a..2b0e9131f92c 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -135,6 +135,19 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector c ); } +ASTPointer Parser::parseStructuredDocumentation() +{ + if (m_scanner->currentCommentLiteral() != "") + { + ASTNodeFactory nodeFactory{*this}; + nodeFactory.setLocation(m_scanner->currentCommentLocation()); + return nodeFactory.createNode( + make_shared(m_scanner->currentCommentLiteral()) + ); + } + return nullptr; +} + ASTPointer Parser::parsePragmaDirective() { RecursionGuard recursionGuard(*this); @@ -276,14 +289,13 @@ ASTPointer Parser::parseContractDefinition() RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name = nullptr; - ASTPointer docString; + ASTPointer documentation; vector> baseContracts; vector> subNodes; std::pair contractKind{}; try { - if (m_scanner->currentCommentLiteral() != "") - docString = make_shared(m_scanner->currentCommentLiteral()); + documentation = parseStructuredDocumentation(); contractKind = parseContractKind(); name = expectIdentifierToken(); if (m_scanner->currentToken() == Token::Is) @@ -350,7 +362,7 @@ ASTPointer Parser::parseContractDefinition() expectToken(Token::RBrace); return nodeFactory.createNode( name, - docString, + documentation, baseContracts, subNodes, contractKind.first, @@ -538,9 +550,7 @@ ASTPointer Parser::parseFunctionDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->currentCommentLiteral() != "") - docstring = make_shared(m_scanner->currentCommentLiteral()); + ASTPointer documentation = parseStructuredDocumentation(); Token kind = m_scanner->currentToken(); ASTPointer name; @@ -598,7 +608,7 @@ ASTPointer Parser::parseFunctionDefinition() kind, header.isVirtual, header.overrides, - docstring, + documentation, header.parameters, header.modifiers, header.returnParameters, @@ -792,9 +802,7 @@ ASTPointer Parser::parseModifierDefinition() m_insideModifier = true; ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->currentCommentLiteral() != "") - docstring = make_shared(m_scanner->currentCommentLiteral()); + ASTPointer documentation = parseStructuredDocumentation(); expectToken(Token::Modifier); ASTPointer name(expectIdentifierToken()); @@ -835,16 +843,14 @@ ASTPointer Parser::parseModifierDefinition() ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); - return nodeFactory.createNode(name, docstring, parameters, isVirtual, overrides, block); + return nodeFactory.createNode(name, documentation, parameters, isVirtual, overrides, block); } ASTPointer Parser::parseEventDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->currentCommentLiteral() != "") - docstring = make_shared(m_scanner->currentCommentLiteral()); + ASTPointer documentation = parseStructuredDocumentation(); expectToken(Token::Event); ASTPointer name(expectIdentifierToken()); @@ -861,7 +867,7 @@ ASTPointer Parser::parseEventDefinition() } nodeFactory.markEndPosition(); expectToken(Token::Semicolon); - return nodeFactory.createNode(name, docstring, parameters, anonymous); + return nodeFactory.createNode(name, documentation, parameters, anonymous); } ASTPointer Parser::parseUsingDirective() @@ -1355,7 +1361,7 @@ ASTPointer Parser::parseEmitStatement(ASTPointer const if (m_scanner->currentToken() != Token::Period) break; m_scanner->next(); - }; + } auto eventName = expressionFromIndexAccessStructure(iap); expectToken(Token::LParen); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index e6d244fdc0a1..25bb2b93f401 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -80,6 +80,7 @@ class Parser: public langutil::ParserBase ///@{ ///@name Parsing functions for the AST nodes void parsePragmaVersion(langutil::SourceLocation const& _location, std::vector const& _tokens, std::vector const& _literals); + ASTPointer parseStructuredDocumentation(); ASTPointer parsePragmaDirective(); ASTPointer parseImportDirective(); /// @returns an std::pair, where diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 3087b4b0f0f6..3f70f9160eff 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -98,7 +98,7 @@ void checkFunctionNatspec( std::string const& _expectedDoc ) { - auto doc = _function->documentation(); + auto doc = _function->documentation()->text(); BOOST_CHECK_MESSAGE(doc != nullptr, "Function does not have Natspec Doc as expected"); BOOST_CHECK_EQUAL(*doc, _expectedDoc); } From 4a179056edeb4a15bbd9e55dc5e55b38f6835826 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 29 Jan 2020 23:27:21 +0100 Subject: [PATCH 035/160] Adds JSON import / export for structured documentation. --- libsolidity/ast/ASTJsonConverter.cpp | 19 +++- libsolidity/ast/ASTJsonConverter.h | 1 + libsolidity/ast/ASTJsonImporter.cpp | 22 ++++- libsolidity/ast/ASTJsonImporter.h | 1 + test/libsolidity/ASTJSON/documentation.json | 92 ++++++++++++------- .../ASTJSON/documentation_legacy.json | 62 +++++++++---- 6 files changed, 139 insertions(+), 58 deletions(-) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 71bb33f77ea6..ed3c6fff948a 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -268,7 +268,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node) { setJsonNode(_node, "ContractDefinition", { make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), + make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("contractKind", contractKind(_node.contractKind())), make_pair("abstract", _node.abstract()), make_pair("fullyImplemented", _node.annotation().unimplementedFunctions.empty()), @@ -349,7 +349,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) { std::vector> attributes = { make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), + make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("kind", TokenTraits::toString(_node.kind())), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), @@ -400,7 +400,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node) { std::vector> attributes = { make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), + make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("virtual", _node.markedVirtual()), @@ -427,7 +427,7 @@ bool ASTJsonConverter::visit(EventDefinition const& _node) m_inEvent = true; setJsonNode(_node, "EventDefinition", { make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), + make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("parameters", toJson(_node.parameterList())), make_pair("anonymous", _node.isAnonymous()) }); @@ -833,6 +833,17 @@ bool ASTJsonConverter::visit(Literal const& _node) return false; } +bool ASTJsonConverter::visit(StructuredDocumentation const& _node) +{ + Json::Value text{*_node.text()}; + std::vector> attributes = { + make_pair("text", text) + }; + setJsonNode(_node, "StructuredDocumentation", std::move(attributes)); + return false; +} + + void ASTJsonConverter::endVisit(EventDefinition const&) { diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 2fd57fb12d56..a1d3cd4ad843 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -119,6 +119,7 @@ class ASTJsonConverter: public ASTConstVisitor bool visit(Identifier const& _node) override; bool visit(ElementaryTypeNameExpression const& _node) override; bool visit(Literal const& _node) override; + bool visit(StructuredDocumentation const& _node) override; void endVisit(EventDefinition const&) override; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 6e8364454142..8bdecdbb836f 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -208,6 +208,8 @@ ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js return createElementaryTypeNameExpression(_json); if (nodeType == "Literal") return createLiteral(_json); + if (nodeType == "StructuredDocumentation") + return createDocumentation(_json); else astAssert(false, "Unknown type of ASTNode: " + nodeType); } @@ -283,7 +285,7 @@ ASTPointer ASTJsonImporter::createContractDefinition(Json::V return createASTNode( _node, make_shared(_node["name"].asString()), - nullOrASTString(_node, "documentation"), + _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), baseContracts, subNodes, contractKind(_node), @@ -397,7 +399,7 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V kind, memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), - nullOrASTString(_node, "documentation"), + _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), createParameterList(member(_node, "parameters")), modifiers, createParameterList(member(_node, "returnParameters")), @@ -428,7 +430,7 @@ ASTPointer ASTJsonImporter::createModifierDefinition(Json::V return createASTNode( _node, memberAsASTString(_node, "name"), - nullOrASTString(_node,"documentation"), + _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), createParameterList(member(_node, "parameters")), memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), @@ -453,7 +455,7 @@ ASTPointer ASTJsonImporter::createEventDefinition(Json::Value c return createASTNode( _node, memberAsASTString(_node, "name"), - nullOrASTString(_node, "documentation"), + _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), createParameterList(member(_node, "parameters")), memberAsBool(_node, "anonymous") ); @@ -842,6 +844,18 @@ ASTPointer ASTJsonImporter::createLiteral(Json::Value const& _node) ); } +ASTPointer ASTJsonImporter::createDocumentation(Json::Value const& _node) +{ + static string const textString = "text"; + + astAssert(member(_node, textString).isString(), "'text' must be a string"); + + return createASTNode( + _node, + make_shared(_node[textString].asString()) + ); +} + // ===== helper functions ========== Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _name) diff --git a/libsolidity/ast/ASTJsonImporter.h b/libsolidity/ast/ASTJsonImporter.h index 8e05c60f3ce9..ebcbd660c108 100644 --- a/libsolidity/ast/ASTJsonImporter.h +++ b/libsolidity/ast/ASTJsonImporter.h @@ -119,6 +119,7 @@ class ASTJsonImporter ASTPointer createIdentifier(Json::Value const& _node); ASTPointer createElementaryTypeNameExpression(Json::Value const& _node); ASTPointer createLiteral(Json::Value const& _node); + ASTPointer createDocumentation(Json::Value const& _node); ///@} // =============== general helper functions =================== diff --git a/test/libsolidity/ASTJSON/documentation.json b/test/libsolidity/ASTJSON/documentation.json index 8c700498c565..8671978b062e 100644 --- a/test/libsolidity/ASTJSON/documentation.json +++ b/test/libsolidity/ASTJSON/documentation.json @@ -4,10 +4,10 @@ { "C": [ - 1 + 2 ] }, - "id": 2, + "id": 3, "nodeType": "SourceUnit", "nodes": [ @@ -16,17 +16,23 @@ "baseContracts": [], "contractDependencies": [], "contractKind": "contract", - "documentation": "This contract is empty", + "documentation": + { + "id": 1, + "nodeType": "StructuredDocumentation", + "src": "0:27:1", + "text": "This contract is empty" + }, "fullyImplemented": true, - "id": 1, + "id": 2, "linearizedBaseContracts": [ - 1 + 2 ], "name": "C", "nodeType": "ContractDefinition", "nodes": [], - "scope": 2, + "scope": 3, "src": "28:13:1" } ], @@ -38,10 +44,10 @@ { "C": [ - 3 + 5 ] }, - "id": 4, + "id": 6, "nodeType": "SourceUnit", "nodes": [ @@ -50,17 +56,23 @@ "baseContracts": [], "contractDependencies": [], "contractKind": "contract", - "documentation": "This contract is empty\nand has a line-breaking comment.", + "documentation": + { + "id": 4, + "nodeType": "StructuredDocumentation", + "src": "0:61:2", + "text": "This contract is empty\nand has a line-breaking comment." + }, "fullyImplemented": true, - "id": 3, + "id": 5, "linearizedBaseContracts": [ - 3 + 5 ], "name": "C", "nodeType": "ContractDefinition", "nodes": [], - "scope": 4, + "scope": 6, "src": "62:13:2" } ], @@ -72,10 +84,10 @@ { "C": [ - 15 + 20 ] }, - "id": 16, + "id": 21, "nodeType": "SourceUnit", "nodes": [ @@ -86,10 +98,10 @@ "contractKind": "contract", "documentation": null, "fullyImplemented": true, - "id": 15, + "id": 20, "linearizedBaseContracts": [ - 15 + 20 ], "name": "C", "nodeType": "ContractDefinition", @@ -97,13 +109,19 @@ [ { "anonymous": false, - "documentation": "Some comment on Evt.", - "id": 6, + "documentation": + { + "id": 7, + "nodeType": "StructuredDocumentation", + "src": "15:26:3", + "text": "Some comment on Evt." + }, + "id": 9, "name": "Evt", "nodeType": "EventDefinition", "parameters": { - "id": 5, + "id": 8, "nodeType": "ParameterList", "parameters": [], "src": "51:2:3" @@ -113,26 +131,32 @@ { "body": { - "id": 9, + "id": 13, "nodeType": "Block", "src": "99:6:3", "statements": [ { - "id": 8, + "id": 12, "nodeType": "PlaceholderStatement", "src": "101:1:3" } ] }, - "documentation": "Some comment on mod.", - "id": 10, + "documentation": + { + "id": 10, + "nodeType": "StructuredDocumentation", + "src": "57:26:3", + "text": "Some comment on mod." + }, + "id": 14, "name": "mod", "nodeType": "ModifierDefinition", "overrides": null, "parameters": { - "id": 7, + "id": 11, "nodeType": "ParameterList", "parameters": [], "src": "96:2:3" @@ -144,14 +168,20 @@ { "body": { - "id": 13, + "id": 18, "nodeType": "Block", "src": "155:2:3", "statements": [] }, - "documentation": "Some comment on fn.", + "documentation": + { + "id": 15, + "nodeType": "StructuredDocumentation", + "src": "108:25:3", + "text": "Some comment on fn." + }, "functionSelector": "a4a2c40b", - "id": 14, + "id": 19, "implemented": true, "kind": "function", "modifiers": [], @@ -160,26 +190,26 @@ "overrides": null, "parameters": { - "id": 11, + "id": 16, "nodeType": "ParameterList", "parameters": [], "src": "145:2:3" }, "returnParameters": { - "id": 12, + "id": 17, "nodeType": "ParameterList", "parameters": [], "src": "155:0:3" }, - "scope": 15, + "scope": 20, "src": "134:23:3", "stateMutability": "nonpayable", "virtual": false, "visibility": "public" } ], - "scope": 16, + "scope": 21, "src": "0:159:3" } ], diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json index cd8012d9cf8f..90866e7fbd91 100644 --- a/test/libsolidity/ASTJSON/documentation_legacy.json +++ b/test/libsolidity/ASTJSON/documentation_legacy.json @@ -6,7 +6,7 @@ { "C": [ - 15 + 20 ] } }, @@ -29,10 +29,10 @@ "fullyImplemented": true, "linearizedBaseContracts": [ - 15 + 20 ], "name": "C", - "scope": 16 + "scope": 21 }, "children": [ @@ -40,11 +40,19 @@ "attributes": { "anonymous": false, - "documentation": "Some comment on Evt.", "name": "Evt" }, "children": [ + { + "attributes": + { + "text": "Some comment on Evt." + }, + "id": 7, + "name": "StructuredDocumentation", + "src": "15:26:3" + }, { "attributes": { @@ -54,19 +62,18 @@ ] }, "children": [], - "id": 5, + "id": 8, "name": "ParameterList", "src": "51:2:3" } ], - "id": 6, + "id": 9, "name": "EventDefinition", "src": "42:12:3" }, { "attributes": { - "documentation": "Some comment on mod.", "name": "mod", "overrides": null, "virtual": false, @@ -74,6 +81,15 @@ }, "children": [ + { + "attributes": + { + "text": "Some comment on mod." + }, + "id": 10, + "name": "StructuredDocumentation", + "src": "57:26:3" + }, { "attributes": { @@ -83,7 +99,7 @@ ] }, "children": [], - "id": 7, + "id": 11, "name": "ParameterList", "src": "96:2:3" }, @@ -91,24 +107,23 @@ "children": [ { - "id": 8, + "id": 12, "name": "PlaceholderStatement", "src": "101:1:3" } ], - "id": 9, + "id": 13, "name": "Block", "src": "99:6:3" } ], - "id": 10, + "id": 14, "name": "ModifierDefinition", "src": "84:21:3" }, { "attributes": { - "documentation": "Some comment on fn.", "functionSelector": "a4a2c40b", "implemented": true, "isConstructor": false, @@ -119,13 +134,22 @@ ], "name": "fn", "overrides": null, - "scope": 15, + "scope": 20, "stateMutability": "nonpayable", "virtual": false, "visibility": "public" }, "children": [ + { + "attributes": + { + "text": "Some comment on fn." + }, + "id": 15, + "name": "StructuredDocumentation", + "src": "108:25:3" + }, { "attributes": { @@ -135,7 +159,7 @@ ] }, "children": [], - "id": 11, + "id": 16, "name": "ParameterList", "src": "145:2:3" }, @@ -148,7 +172,7 @@ ] }, "children": [], - "id": 12, + "id": 17, "name": "ParameterList", "src": "155:0:3" }, @@ -161,22 +185,22 @@ ] }, "children": [], - "id": 13, + "id": 18, "name": "Block", "src": "155:2:3" } ], - "id": 14, + "id": 19, "name": "FunctionDefinition", "src": "134:23:3" } ], - "id": 15, + "id": 20, "name": "ContractDefinition", "src": "0:159:3" } ], - "id": 16, + "id": 21, "name": "SourceUnit", "src": "0:160:3" } From 306f6d963d3f34c802851cc7fe5a492608987341 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Thu, 30 Jan 2020 17:33:33 +0100 Subject: [PATCH 036/160] Adds changelog entry for structured AST node introduction. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 999520996e67..c4c5450eb803 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. + * AST: Add a new node for doxygen-style, structured documentation that can be received by contract, function, event and modifier definitions. Bugfixes: From 1b1a0a695307262cb6626048adc5da9ee1d9887e Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Tue, 21 Jan 2020 14:18:05 +0100 Subject: [PATCH 037/160] Add return area clarification --- docs/yul.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/yul.rst b/docs/yul.rst index f086029647b9..93bfe48a9f79 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -833,15 +833,19 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a | insize, out, outsize) | | | providing g gas and v wei and output area | | | | | mem[out...(out+outsize)) returning 0 on error (eg. out of gas) | | | | | and 1 on success | +| | | | :ref:`See more ` | +-------------------------+-----+---+-----------------------------------------------------------------+ | callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay | | insize, out, outsize) | | | in the context of the current contract otherwise | +| | | | :ref:`See more ` | +-------------------------+-----+---+-----------------------------------------------------------------+ | delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` | | insize, out, outsize) | | | and ``callvalue`` | +| | | | :ref:`See more ` | +-------------------------+-----+---+-----------------------------------------------------------------+ | staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | | insize, out, outsize) | | | not allow state modifications | +| | | | :ref:`See more ` | +-------------------------+-----+---+-----------------------------------------------------------------+ | return(p, s) | `-` | F | end execution, return data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ @@ -888,6 +892,17 @@ which are used to access other parts of a Yul object. as arguments and return the size and offset in the data area, respectively. For the EVM, the ``datacopy`` function is equivalent to ``codecopy``. +.. _yul-call-return-area: + +.. note:: + The ``call*`` instructions use the ``out`` and ``outsize`` parameters to define an area in memory where + the return data is placed. This area is written to depending on how many bytes the called contract returns. + If it returns more data, only the first ``outsize`` bytes are written. You can access the rest of the data + using the ``returndatacopy`` opcode. If it returns less data, then the remaining bytes are not touched at all. + You need to use the ``returndatasize`` opcode to check which part of this memory area contains the return data. + The remaining bytes will retain their values as of before the call. If the call fails (it returns ``0``), + nothing is written to that area, but you can still retrieve the failure data using ``returndatacopy``. + .. _yul-object: Specification of Yul Object From e4b18e85e69f11bf492c845460e4de4cfeb57c35 Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 3 Feb 2020 08:04:21 +0100 Subject: [PATCH 038/160] Replaced SourceLocation::isEmpty() with isValid() and hasText(). The function SourceLocation::isEmpty() had somewhat dual role. Sometimes it indicates that the SourceLocation is invalid. Sometimes it means that there is no corresponding source text. Hence the proposal is to replace it with two functions, isValid() and hasText(). I also removed Scanner::sourceAt(). (Do we have a rule of thumb to remove unused code?) Since hasText() checks that start and end are valid indices for source, I adjusted a couple of tests to avoid empty source strings. --- libevmasm/Assembly.cpp | 10 +++++----- liblangutil/Exceptions.cpp | 4 ++-- liblangutil/Scanner.h | 6 ------ liblangutil/SourceLocation.h | 20 ++++++++++++++------ libsolidity/analysis/ControlFlowAnalyzer.cpp | 2 +- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/ast/AsmJsonImporter.cpp | 2 +- libyul/AsmParser.h | 2 +- test/libevmasm/Assembler.cpp | 4 ++-- test/libevmasm/Optimiser.cpp | 2 +- test/liblangutil/SourceLocation.cpp | 6 +++--- 11 files changed, 31 insertions(+), 29 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index adc7f5b14315..10503c484dd0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -43,7 +43,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i) assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); m_deposit += _i.deposit(); m_items.emplace_back(_i); - if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty()) + if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid()) m_items.back().setLocation(m_currentSourceLocation); m_items.back().m_modifierDepth = m_currentModifierDepth; return m_items.back(); @@ -69,7 +69,7 @@ namespace string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) { - if (_location.isEmpty() || !_location.source.get() || _sourceCodes.empty() || _location.start >= _location.end || _location.start < 0) + if (!_location.hasText() || _sourceCodes.empty()) return ""; auto it = _sourceCodes.find(_location.source->name()); @@ -97,7 +97,7 @@ class Functionalizer void feed(AssemblyItem const& _item) { - if (!_item.location().isEmpty() && _item.location() != m_location) + if (_item.location().isValid() && _item.location() != m_location) { flush(); m_location = _item.location(); @@ -141,12 +141,12 @@ class Functionalizer void printLocation() { - if (!m_location.source && m_location.isEmpty()) + if (!m_location.isValid()) return; m_out << m_prefix << " /*"; if (m_location.source) m_out << " \"" + m_location.source->name() + "\""; - if (!m_location.isEmpty()) + if (m_location.hasText()) m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end); m_out << " " << locationFromSources(m_sourceCodes, m_location); m_out << " */" << endl; diff --git a/liblangutil/Exceptions.cpp b/liblangutil/Exceptions.cpp index 59d81b6e6332..f035f50444f3 100644 --- a/liblangutil/Exceptions.cpp +++ b/liblangutil/Exceptions.cpp @@ -51,7 +51,7 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip break; } - if (!_location.isEmpty()) + if (_location.isValid()) *this << errinfo_sourceLocation(_location); if (!_description.empty()) *this << util::errinfo_comment(_description); @@ -60,7 +60,7 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip Error::Error(Error::Type _type, std::string const& _description, SourceLocation const& _location): Error(_type) { - if (!_location.isEmpty()) + if (_location.isValid()) *this << errinfo_sourceLocation(_location); *this << util::errinfo_comment(_description); } diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 77af2635789a..1e08e0b6e813 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -167,12 +167,6 @@ class Scanner /// Do only use in error cases, they are quite expensive. std::string lineAtPosition(int _position) const { return m_source->lineAtPosition(_position); } std::tuple translatePositionToLineColumn(int _position) const { return m_source->translatePositionToLineColumn(_position); } - std::string sourceAt(SourceLocation const& _location) const - { - solAssert(!_location.isEmpty(), ""); - solAssert(m_source.get() == _location.source.get(), "CharStream memory locations must match."); - return m_source->source().substr(_location.start, _location.end - _location.start); - } ///@} private: diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index 46808f8e0929..5f02f8092435 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -56,24 +56,32 @@ struct SourceLocation inline bool contains(SourceLocation const& _other) const { - if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get()) + if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) return false; return start <= _other.start && _other.end <= end; } inline bool intersects(SourceLocation const& _other) const { - if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get()) + if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) return false; return _other.start < end && start < _other.end; } - bool isEmpty() const { return start == -1 && end == -1; } + bool isValid() const { return source || start != -1 || end != -1; } + + bool hasText() const + { + return source + && 0 <= start + && start <= end + && end <= int(source->source().length()); + } std::string text() const { assertThrow(source, SourceLocationError, "Requested text from null source."); - assertThrow(!isEmpty(), SourceLocationError, "Requested text from empty source location."); + assertThrow(0 <= start, SourceLocationError, "Invalid source location."); assertThrow(start <= end, SourceLocationError, "Invalid source location."); assertThrow(end <= int(source->source().length()), SourceLocationError, "Invalid source location."); return source->source().substr(start, end - start); @@ -109,13 +117,13 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string /// Stream output for Location (used e.g. in boost exceptions). inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location) { - if (_location.isEmpty()) + if (!_location.isValid()) return _out << "NO_LOCATION_SPECIFIED"; if (_location.source) _out << _location.source->name(); - _out << "[" << _location.start << "," << _location.end << ")"; + _out << "[" << _location.start << "," << _location.end << "]"; return _out; } diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index a774dee6156e..c8a824c544c2 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -163,7 +163,7 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* std::set unreachable; util::BreadthFirstSearch{{_exit, _revert}}.run( [&](CFGNode const* _node, auto&& _addChild) { - if (!reachable.count(_node) && !_node->location.isEmpty()) + if (!reachable.count(_node) && _node->location.isValid()) unreachable.insert(_node->location); for (CFGNode const* entry: _node->entries) _addChild(entry); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 82a362989b42..e4aa399d862f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2762,7 +2762,7 @@ bool TypeChecker::visit(Identifier const& _identifier) SecondarySourceLocation ssl; for (Declaration const* declaration: annotation.overloadedDeclarations) - if (declaration->location().isEmpty()) + if (!declaration->location().isValid()) { // Try to re-construct function definition string description; diff --git a/libsolidity/ast/AsmJsonImporter.cpp b/libsolidity/ast/AsmJsonImporter.cpp index 8f4559a558e2..a2ff7298b5d9 100644 --- a/libsolidity/ast/AsmJsonImporter.cpp +++ b/libsolidity/ast/AsmJsonImporter.cpp @@ -54,7 +54,7 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; r.location = createSourceLocation(_node); - astAssert(!r.location.isEmpty() || !r.location.source, "Invalid source location in Asm AST"); + astAssert(!r.location.isValid() || r.location.hasText(), "Invalid source location in Asm AST"); return r; } diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 8abdcc91a891..42663ae0a7a7 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -65,7 +65,7 @@ class Parser: public langutil::ParserBase { T r; r.location = _loc; - if (r.location.isEmpty()) + if (!r.location.hasText()) { r.location.start = position(); r.location.end = endPosition(); diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 31b30dd45805..eac83e1bbe03 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -51,11 +51,11 @@ BOOST_AUTO_TEST_SUITE(Assembler) BOOST_AUTO_TEST_CASE(all_assembly_items) { Assembly _assembly; - auto root_asm = make_shared("", "root.asm"); + auto root_asm = make_shared("lorem ipsum", "root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); Assembly _subAsm; - auto sub_asm = make_shared("", "sub.asm"); + auto sub_asm = make_shared("lorem ipsum", "sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 7a3125bfdc58..4d93f4e76914 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -72,7 +72,7 @@ namespace for (AssemblyItem const& item: output) { - BOOST_CHECK(item == Instruction::POP || !item.location().isEmpty()); + BOOST_CHECK(item == Instruction::POP || item.location().isValid()); } return output; } diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index 8fe4740fdd2e..c5b47d0f682a 100644 --- a/test/liblangutil/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -33,9 +33,9 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest) BOOST_AUTO_TEST_CASE(test_fail) { - auto const source = std::make_shared("", "source"); - auto const sourceA = std::make_shared("", "sourceA"); - auto const sourceB = std::make_shared("", "sourceB"); + auto const source = std::make_shared("lorem ipsum", "source"); + auto const sourceA = std::make_shared("lorem ipsum", "sourceA"); + auto const sourceB = std::make_shared("lorem ipsum", "sourceB"); BOOST_CHECK(SourceLocation{} == SourceLocation{}); BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB})); From 9f0c59b3c6a0be35fce3d9271758e33f81baac26 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Mon, 3 Feb 2020 11:55:53 +0100 Subject: [PATCH 039/160] Reenable ossfuzz builds (but not tests) refs #8208 --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a2afa937d6ad..a779044748be 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -723,7 +723,7 @@ workflows: - b_docs: *workflow_trigger_on_tags - b_archlinux: *workflow_trigger_on_tags - b_ubu_cxx20: *workflow_trigger_on_tags -# - b_ubu_ossfuzz: *workflow_trigger_on_tags + - b_ubu_ossfuzz: *workflow_trigger_on_tags # OS/X build and tests - b_osx: *workflow_trigger_on_tags @@ -768,7 +768,7 @@ workflows: jobs: # OSSFUZZ builds and (regression) tests -# - b_ubu_ossfuzz: *workflow_trigger_on_tags + - b_ubu_ossfuzz: *workflow_trigger_on_tags # - t_ubu_ossfuzz: *workflow_ubuntu1904_ossfuzz # Code Coverage enabled build and tests From 9c8187bd29170678675b1d6482e012116202add7 Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 29 Jan 2020 17:37:10 +0100 Subject: [PATCH 040/160] CommonData: Add invertMap() function --- libsolutil/CommonData.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index c7c9d80e3e92..45f19bcb550f 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -156,6 +156,23 @@ T convertContainer(U&& _from) }; } +/// Gets a @a K -> @a V map and returns a map where values from the original map are keys and keys +/// from the original map are values. +/// +/// @pre @a originalMap must have unique values. +template +std::map invertMap(std::map const& originalMap) +{ + std::map inverseMap; + for (auto const& [key, value]: originalMap) + { + assert(inverseMap.count(value) == 0); + inverseMap.insert({value, key}); + } + + return inverseMap; +} + // String conversion functions, mainly to/from hex/nibble/byte representations. enum class WhenError From 4e7c1c7876dadd3b406b9f0539cf8dd8bc8ece52 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 24 Jan 2020 22:03:44 +0100 Subject: [PATCH 041/160] OptimiserSuite: Add two maps for converting between step names and abbreviations - Abbreviations match those used in yulopti. - I considered using boost::bimap but I think it would be an overkill. Two simple maps are good enough in a situation where data is constant and there isn't much of it. - I could also use InvertibleMap from libsolutil but I don't even need any of its methods since my map is a constant. I also don't need the inverted map to store sets because my values are unique. - The reverseMap() is generic enough to be moved to some global file with utilities but I don't sure if it's going to actually be useful to others in practice. --- libyul/optimiser/Suite.cpp | 43 ++++++++++++++++++++++++++++++++++++++ libyul/optimiser/Suite.h | 2 ++ 2 files changed, 45 insertions(+) diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index b57cf76451c8..47c245752c88 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -370,6 +370,49 @@ map> const& OptimiserSuite::allSteps() return instance; } +map const& OptimiserSuite::stepNameToAbbreviationMap() +{ + static map lookupTable{ + {BlockFlattener::name, 'f'}, + {CommonSubexpressionEliminator::name, 'c'}, + {ConditionalSimplifier::name, 'C'}, + {ConditionalUnsimplifier::name, 'U'}, + {ControlFlowSimplifier::name, 'n'}, + {DeadCodeEliminator::name, 'D'}, + {EquivalentFunctionCombiner::name, 'v'}, + {ExpressionInliner::name, 'e'}, + {ExpressionJoiner::name, 'j'}, + {ExpressionSimplifier::name, 's'}, + {ExpressionSplitter::name, 'x'}, + {ForLoopConditionIntoBody::name, 'I'}, + {ForLoopConditionOutOfBody::name, 'O'}, + {ForLoopInitRewriter::name, 'o'}, + {FullInliner::name, 'i'}, + {FunctionGrouper::name, 'g'}, + {FunctionHoister::name, 'h'}, + {LiteralRematerialiser::name, 'T'}, + {LoadResolver::name, 'L'}, + {LoopInvariantCodeMotion::name, 'M'}, + {RedundantAssignEliminator::name, 'r'}, + {Rematerialiser::name, 'm'}, + {SSAReverser::name, 'V'}, + {SSATransform::name, 'a'}, + {StructuralSimplifier::name, 't'}, + {UnusedPruner::name, 'u'}, + {VarDeclInitializer::name, 'd'}, + }; + yulAssert(lookupTable.size() == allSteps().size(), ""); + + return lookupTable; +} + +map const& OptimiserSuite::stepAbbreviationToNameMap() +{ + static map lookupTable = util::invertMap(stepNameToAbbreviationMap()); + + return lookupTable; +} + void OptimiserSuite::runSequence(std::vector const& _steps, Block& _ast) { unique_ptr copy; diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index 299a465fb88d..6ce6b485c71d 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -62,6 +62,8 @@ class OptimiserSuite void runSequence(std::vector const& _steps, Block& _ast); static std::map> const& allSteps(); + static std::map const& stepNameToAbbreviationMap(); + static std::map const& stepAbbreviationToNameMap(); private: OptimiserSuite( From 4129c2749597419fe31adc46e386c9d0248696f9 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 24 Jan 2020 23:51:35 +0100 Subject: [PATCH 042/160] [yulopti] Replace hard-coded step list with OptimiserSuite's maps --- test/tools/yulopti.cpp | 126 +++++------------------------------------ 1 file changed, 14 insertions(+), 112 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index b0c0e0b35ff5..fa291672de01 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -30,40 +30,11 @@ #include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include -#include -#include -#include #include -#include -#include +#include #include @@ -71,6 +42,7 @@ #include +#include #include #include #include @@ -150,90 +122,26 @@ class YulOpti cout << ' ' << char(option) << endl; OptimiserStepContext context{m_dialect, *m_nameDispenser, reservedIdentifiers}; - switch (option) + + map const& abbreviationMap = OptimiserSuite::stepAbbreviationToNameMap(); + assert(abbreviationMap.count('q') == 0 && "We have chosen 'q' for quit"); + assert(abbreviationMap.count('p') == 0 && "We have chosen 'p' for StackCompressor"); + + auto abbreviationAndName = abbreviationMap.find(option); + if (abbreviationAndName != abbreviationMap.end()) + { + OptimiserStep const& step = *OptimiserSuite::allSteps().at(abbreviationAndName->second); + step.run(context, *m_ast); + } + else switch (option) { case 'q': return; - case 'f': - BlockFlattener::run(context, *m_ast); - break; - case 'o': - ForLoopInitRewriter::run(context, *m_ast); - break; - case 'O': - ForLoopConditionOutOfBody::run(context, *m_ast); - break; - case 'I': - ForLoopConditionIntoBody::run(context, *m_ast); - break; - case 'c': - CommonSubexpressionEliminator::run(context, *m_ast); - break; - case 'C': - ConditionalSimplifier::run(context, *m_ast); - break; - case 'U': - ConditionalUnsimplifier::run(context, *m_ast); - break; - case 'd': - VarDeclInitializer::run(context, *m_ast); - break; case 'l': VarNameCleaner::run(context, *m_ast); // VarNameCleaner destroys the unique names guarantee of the disambiguator. disambiguated = false; break; - case 'x': - ExpressionSplitter::run(context, *m_ast); - break; - case 'j': - ExpressionJoiner::run(context, *m_ast); - break; - case 'g': - FunctionGrouper::run(context, *m_ast); - break; - case 'h': - FunctionHoister::run(context, *m_ast); - break; - case 'e': - ExpressionInliner::run(context, *m_ast); - break; - case 'i': - FullInliner::run(context, *m_ast); - break; - case 's': - ExpressionSimplifier::run(context, *m_ast); - break; - case 't': - StructuralSimplifier::run(context, *m_ast); - break; - case 'T': - LiteralRematerialiser::run(context, *m_ast); - break; - case 'n': - ControlFlowSimplifier::run(context, *m_ast); - break; - case 'u': - UnusedPruner::run(context, *m_ast); - break; - case 'D': - DeadCodeEliminator::run(context, *m_ast); - break; - case 'a': - SSATransform::run(context, *m_ast); - break; - case 'r': - RedundantAssignEliminator::run(context, *m_ast); - break; - case 'm': - Rematerialiser::run(context, *m_ast); - break; - case 'v': - EquivalentFunctionCombiner::run(context, *m_ast); - break; - case 'V': - SSAReverser::run(context, *m_ast); - break; case 'p': { Object obj; @@ -241,12 +149,6 @@ class YulOpti StackCompressor::run(m_dialect, obj, true, 16); break; } - case 'L': - LoadResolver::run(context, *m_ast); - break; - case 'M': - LoopInvariantCodeMotion::run(context, *m_ast); - break; default: cout << "Unknown option." << endl; } From 5fbc4d4afaafe56f9201a8589ea0fc2a5f61d6dc Mon Sep 17 00:00:00 2001 From: cameel Date: Sat, 25 Jan 2020 00:46:16 +0100 Subject: [PATCH 043/160] [yulopti] Automate printing of the usage banner - This now displays internal step names rather than human-readable ones but the internal ones are readable enough and it's not something worth creating another map. - Options in the banner are now aligned in columns and thus easier to read. --- test/tools/yulopti.cpp | 54 +++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index fa291672de01..de074e642fa7 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -42,7 +42,6 @@ #include -#include #include #include #include @@ -94,6 +93,40 @@ class YulOpti return true; } + void printUsageBanner( + map const& _optimizationSteps, + map const& _extraOptions, + size_t _columns + ) + { + auto hasShorterString = [](auto const& a, auto const& b){ return a.second.size() < b.second.size(); }; + size_t longestDescriptionLength = max( + max_element(_optimizationSteps.begin(), _optimizationSteps.end(), hasShorterString)->second.size(), + max_element(_extraOptions.begin(), _extraOptions.end(), hasShorterString)->second.size() + ); + + size_t index = 0; + auto printPair = [&](auto const& optionAndDescription) + { + cout << optionAndDescription.first << ": "; + cout << setw(longestDescriptionLength) << setiosflags(ios::left); + cout << optionAndDescription.second << " "; + + ++index; + if (index % _columns == 0) + cout << endl; + }; + + for (auto const& optionAndDescription: _extraOptions) + { + yulAssert(_optimizationSteps.count(optionAndDescription.first) == 0, ""); + printPair(optionAndDescription); + } + + for (auto const& abbreviationAndName: _optimizationSteps) + printPair(abbreviationAndName); + } + void runInteractive(string source) { bool disambiguated = false; @@ -111,22 +144,21 @@ class YulOpti m_nameDispenser = make_shared(m_dialect, *m_ast, reservedIdentifiers); disambiguated = true; } - cout << "(q)uit/(f)latten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; - cout << " (e)xpr inline/(i)nline/(s)implify/varname c(l)eaner/(u)nusedprune/ss(a) transform/" << endl; - cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/for-loop-condition-(I)nto-body/" << endl; - cout << " for-loop-condition-(O)ut-of-body/s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/" << endl; - cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/" << endl; - cout << " (C)onditional simplifier/conditional (U)nsimplifier/loop-invariant code (M)otion?" << endl; + map const& abbreviationMap = OptimiserSuite::stepAbbreviationToNameMap(); + map const& extraOptions = { + {'q', "quit"}, + {'l', "VarNameCleaner"}, + {'p', "StackCompressor"}, + }; + + printUsageBanner(abbreviationMap, extraOptions, 4); + cout << "? "; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; OptimiserStepContext context{m_dialect, *m_nameDispenser, reservedIdentifiers}; - map const& abbreviationMap = OptimiserSuite::stepAbbreviationToNameMap(); - assert(abbreviationMap.count('q') == 0 && "We have chosen 'q' for quit"); - assert(abbreviationMap.count('p') == 0 && "We have chosen 'p' for StackCompressor"); - auto abbreviationAndName = abbreviationMap.find(option); if (abbreviationAndName != abbreviationMap.end()) { From c4f8df327270727e65b8d58516ea3d73878df5e1 Mon Sep 17 00:00:00 2001 From: cameel Date: Thu, 30 Jan 2020 02:12:54 +0100 Subject: [PATCH 044/160] Workaround for clang 5.0.0 on Ubuntu Trusty in Travis CI failing to compile a structural binding clang fails with: /home/travis/build/ethereum/solidity/libsolutil/CommonData.h:167:19: error: unused variable '' [-Werror,-Wunused-variable] for (auto const& [key, value]: originalMap) --- libsolutil/CommonData.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index 45f19bcb550f..5afb654705e3 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -164,10 +164,10 @@ template std::map invertMap(std::map const& originalMap) { std::map inverseMap; - for (auto const& [key, value]: originalMap) + for (auto const& originalPair: originalMap) { - assert(inverseMap.count(value) == 0); - inverseMap.insert({value, key}); + assert(inverseMap.count(originalPair.second) == 0); + inverseMap.insert({originalPair.second, originalPair.first}); } return inverseMap; From 1b310a93a24fd6a771acb32910f08e82646782d0 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 3 Feb 2020 11:55:26 +0100 Subject: [PATCH 045/160] travis: Run byte code comparison only if current travis job is a pull request (not a push-build). --- scripts/bytecodecompare/storebytecode.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/bytecodecompare/storebytecode.sh b/scripts/bytecodecompare/storebytecode.sh index c993f0c32492..451490e0c66f 100755 --- a/scripts/bytecodecompare/storebytecode.sh +++ b/scripts/bytecodecompare/storebytecode.sh @@ -27,6 +27,13 @@ set -e +if [[ "${TRAVIS_PULL_REQUEST_BRANCH}" != "" ]]; then + # Variable is set to the branch's name iff current job is a pull request, + # or is set to empty string if it is a push build. + echo "Skipping bytecode comparison." + exit 0 +fi + REPO_ROOT="$(dirname "$0")"/../.. if test -z "$1"; then From 76cfe4e2ced4a7368853a04a80a338c141fcde60 Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 3 Feb 2020 08:04:21 +0100 Subject: [PATCH 046/160] Replaced SourceLocation::isEmpty() with isValid() and hasText(). The function SourceLocation::isEmpty() had somewhat dual role. Sometimes it indicates that the SourceLocation is invalid. Sometimes it means that there is no corresponding source text. Hence the proposal is to replace it with two functions, isValid() and hasText(). I also removed Scanner::sourceAt(). (Do we have a rule of thumb to remove unused code?) Since hasText() checks that start and end are valid indices for source, I adjusted a couple of tests to avoid empty source strings. --- libevmasm/Assembly.cpp | 10 +++++----- liblangutil/Exceptions.cpp | 4 ++-- liblangutil/Scanner.h | 6 ------ liblangutil/SourceLocation.h | 21 ++++++++++++++------ libsolidity/analysis/ControlFlowAnalyzer.cpp | 2 +- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/ast/AsmJsonImporter.cpp | 2 +- libyul/AsmParser.h | 2 +- test/libevmasm/Assembler.cpp | 4 ++-- test/libevmasm/Optimiser.cpp | 2 +- test/liblangutil/SourceLocation.cpp | 6 +++--- 11 files changed, 32 insertions(+), 29 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index adc7f5b14315..10503c484dd0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -43,7 +43,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i) assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); m_deposit += _i.deposit(); m_items.emplace_back(_i); - if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty()) + if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid()) m_items.back().setLocation(m_currentSourceLocation); m_items.back().m_modifierDepth = m_currentModifierDepth; return m_items.back(); @@ -69,7 +69,7 @@ namespace string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) { - if (_location.isEmpty() || !_location.source.get() || _sourceCodes.empty() || _location.start >= _location.end || _location.start < 0) + if (!_location.hasText() || _sourceCodes.empty()) return ""; auto it = _sourceCodes.find(_location.source->name()); @@ -97,7 +97,7 @@ class Functionalizer void feed(AssemblyItem const& _item) { - if (!_item.location().isEmpty() && _item.location() != m_location) + if (_item.location().isValid() && _item.location() != m_location) { flush(); m_location = _item.location(); @@ -141,12 +141,12 @@ class Functionalizer void printLocation() { - if (!m_location.source && m_location.isEmpty()) + if (!m_location.isValid()) return; m_out << m_prefix << " /*"; if (m_location.source) m_out << " \"" + m_location.source->name() + "\""; - if (!m_location.isEmpty()) + if (m_location.hasText()) m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end); m_out << " " << locationFromSources(m_sourceCodes, m_location); m_out << " */" << endl; diff --git a/liblangutil/Exceptions.cpp b/liblangutil/Exceptions.cpp index 59d81b6e6332..f035f50444f3 100644 --- a/liblangutil/Exceptions.cpp +++ b/liblangutil/Exceptions.cpp @@ -51,7 +51,7 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip break; } - if (!_location.isEmpty()) + if (_location.isValid()) *this << errinfo_sourceLocation(_location); if (!_description.empty()) *this << util::errinfo_comment(_description); @@ -60,7 +60,7 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip Error::Error(Error::Type _type, std::string const& _description, SourceLocation const& _location): Error(_type) { - if (!_location.isEmpty()) + if (_location.isValid()) *this << errinfo_sourceLocation(_location); *this << util::errinfo_comment(_description); } diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 77af2635789a..1e08e0b6e813 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -167,12 +167,6 @@ class Scanner /// Do only use in error cases, they are quite expensive. std::string lineAtPosition(int _position) const { return m_source->lineAtPosition(_position); } std::tuple translatePositionToLineColumn(int _position) const { return m_source->translatePositionToLineColumn(_position); } - std::string sourceAt(SourceLocation const& _location) const - { - solAssert(!_location.isEmpty(), ""); - solAssert(m_source.get() == _location.source.get(), "CharStream memory locations must match."); - return m_source->source().substr(_location.start, _location.end - _location.start); - } ///@} private: diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index 46808f8e0929..e6f97942eae6 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -56,24 +56,33 @@ struct SourceLocation inline bool contains(SourceLocation const& _other) const { - if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get()) + if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) return false; return start <= _other.start && _other.end <= end; } inline bool intersects(SourceLocation const& _other) const { - if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get()) + if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) return false; return _other.start < end && start < _other.end; } - bool isEmpty() const { return start == -1 && end == -1; } + bool isValid() const { return source || start != -1 || end != -1; } + + bool hasText() const + { + return + source && + 0 <= start && + start <= end && + end <= int(source->source().length()); + } std::string text() const { assertThrow(source, SourceLocationError, "Requested text from null source."); - assertThrow(!isEmpty(), SourceLocationError, "Requested text from empty source location."); + assertThrow(0 <= start, SourceLocationError, "Invalid source location."); assertThrow(start <= end, SourceLocationError, "Invalid source location."); assertThrow(end <= int(source->source().length()), SourceLocationError, "Invalid source location."); return source->source().substr(start, end - start); @@ -109,13 +118,13 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string /// Stream output for Location (used e.g. in boost exceptions). inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location) { - if (_location.isEmpty()) + if (!_location.isValid()) return _out << "NO_LOCATION_SPECIFIED"; if (_location.source) _out << _location.source->name(); - _out << "[" << _location.start << "," << _location.end << ")"; + _out << "[" << _location.start << "," << _location.end << "]"; return _out; } diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index a774dee6156e..c8a824c544c2 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -163,7 +163,7 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* std::set unreachable; util::BreadthFirstSearch{{_exit, _revert}}.run( [&](CFGNode const* _node, auto&& _addChild) { - if (!reachable.count(_node) && !_node->location.isEmpty()) + if (!reachable.count(_node) && _node->location.isValid()) unreachable.insert(_node->location); for (CFGNode const* entry: _node->entries) _addChild(entry); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 82a362989b42..e4aa399d862f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2762,7 +2762,7 @@ bool TypeChecker::visit(Identifier const& _identifier) SecondarySourceLocation ssl; for (Declaration const* declaration: annotation.overloadedDeclarations) - if (declaration->location().isEmpty()) + if (!declaration->location().isValid()) { // Try to re-construct function definition string description; diff --git a/libsolidity/ast/AsmJsonImporter.cpp b/libsolidity/ast/AsmJsonImporter.cpp index 8f4559a558e2..a2ff7298b5d9 100644 --- a/libsolidity/ast/AsmJsonImporter.cpp +++ b/libsolidity/ast/AsmJsonImporter.cpp @@ -54,7 +54,7 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; r.location = createSourceLocation(_node); - astAssert(!r.location.isEmpty() || !r.location.source, "Invalid source location in Asm AST"); + astAssert(!r.location.isValid() || r.location.hasText(), "Invalid source location in Asm AST"); return r; } diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 8abdcc91a891..42663ae0a7a7 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -65,7 +65,7 @@ class Parser: public langutil::ParserBase { T r; r.location = _loc; - if (r.location.isEmpty()) + if (!r.location.hasText()) { r.location.start = position(); r.location.end = endPosition(); diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 31b30dd45805..eac83e1bbe03 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -51,11 +51,11 @@ BOOST_AUTO_TEST_SUITE(Assembler) BOOST_AUTO_TEST_CASE(all_assembly_items) { Assembly _assembly; - auto root_asm = make_shared("", "root.asm"); + auto root_asm = make_shared("lorem ipsum", "root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); Assembly _subAsm; - auto sub_asm = make_shared("", "sub.asm"); + auto sub_asm = make_shared("lorem ipsum", "sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 7a3125bfdc58..4d93f4e76914 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -72,7 +72,7 @@ namespace for (AssemblyItem const& item: output) { - BOOST_CHECK(item == Instruction::POP || !item.location().isEmpty()); + BOOST_CHECK(item == Instruction::POP || item.location().isValid()); } return output; } diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index 8fe4740fdd2e..c5b47d0f682a 100644 --- a/test/liblangutil/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -33,9 +33,9 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest) BOOST_AUTO_TEST_CASE(test_fail) { - auto const source = std::make_shared("", "source"); - auto const sourceA = std::make_shared("", "sourceA"); - auto const sourceB = std::make_shared("", "sourceB"); + auto const source = std::make_shared("lorem ipsum", "source"); + auto const sourceA = std::make_shared("lorem ipsum", "sourceA"); + auto const sourceB = std::make_shared("lorem ipsum", "sourceB"); BOOST_CHECK(SourceLocation{} == SourceLocation{}); BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB})); From 0508e24385e418315f1f225f2a113f301b15e2d7 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 3 Feb 2020 12:48:45 +0100 Subject: [PATCH 047/160] Add symlinks to solc-bin/wasm for releases. --- scripts/travis-emscripten/publish_binary.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/travis-emscripten/publish_binary.sh b/scripts/travis-emscripten/publish_binary.sh index 7bf6f3690bf5..fa5e627dbc8d 100755 --- a/scripts/travis-emscripten/publish_binary.sh +++ b/scripts/travis-emscripten/publish_binary.sh @@ -81,18 +81,22 @@ else fi -NEWFILE=./bin/"soljson-$FULLVERSION.js" +NEWFILE="soljson-$FULLVERSION.js" # Prepare for update script npm install # This file is assumed to be the product of the build_emscripten.sh script. -cp ../soljson.js "$NEWFILE" +cp ../soljson.js ./bin/"$NEWFILE" + +# For releases, add a symlink to the wasm directory. +[ "$TRAVIS_BRANCH" = release ] && ln -sf ../bin/"$NEWFILE" ./wasm/"$NEWFILE" # Run update script npm run update # Publish updates -git add "$NEWFILE" +git add ./bin/"$NEWFILE" +[ "$TRAVIS_BRANCH" = release ] && git add ./wasm/"$NEWFILE" git commit -a -m "Added compiler version $FULLVERSION" git push origin gh-pages From a44ba4e994612e0bee230c9f22cb2cda4ac87d20 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 3 Feb 2020 12:50:19 +0100 Subject: [PATCH 048/160] Do not remove leading zeros from commit hashes while publishing. --- scripts/travis-emscripten/publish_binary.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/travis-emscripten/publish_binary.sh b/scripts/travis-emscripten/publish_binary.sh index fa5e627dbc8d..eba3cdac5514 100755 --- a/scripts/travis-emscripten/publish_binary.sh +++ b/scripts/travis-emscripten/publish_binary.sh @@ -36,9 +36,6 @@ VER="v$VER" COMMIT=$(git rev-parse --short=8 HEAD) DATE=$(date --date="$(git log -1 --date=iso --format=%ad HEAD)" --utc +%Y.%-m.%-d) -# remove leading zeros in components - they are not semver-compatible -COMMIT=$(echo "$COMMIT" | sed -e 's/^0*//') - ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} From a3b7c73e3f864da288f3da8bacb778c69a03bce0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 3 Feb 2020 14:21:37 +0100 Subject: [PATCH 049/160] Some clarification about try-catch. --- docs/control-structures.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 46e0da366f1b..248641e3414e 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -717,3 +717,13 @@ in scope in the block that follows. in a catch block or the execution of the try/catch statement itself reverts (for example due to decoding failures as noted above or due to not providing a low-level catch clause). + +.. note:: + The reason behind a failed call can be manifold. Do not assume that + the error message is coming directly from the called contract: + 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 + even if the called contract goes out of gas, the caller still + has some gas left. \ No newline at end of file From 211227f50b63f845e64ce692bcee4bbaa7023dce Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 4 Feb 2020 04:18:34 +0100 Subject: [PATCH 050/160] Modified SourceLocation::hasText() to allow empty source. --- liblangutil/SourceLocation.h | 5 +++-- libsolidity/ast/AsmJsonImporter.cpp | 3 +-- test/libevmasm/Assembler.cpp | 4 ++-- test/liblangutil/SourceLocation.cpp | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index e6f97942eae6..063c3e417cdc 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -76,7 +76,8 @@ struct SourceLocation source && 0 <= start && start <= end && - end <= int(source->source().length()); + // in some cases (json import) source->source() can be empty + (source->source().empty() || end <= int(source->source().length())); } std::string text() const @@ -124,7 +125,7 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat if (_location.source) _out << _location.source->name(); - _out << "[" << _location.start << "," << _location.end << "]"; + _out << "[" << _location.start << "," << _location.end << ")"; return _out; } diff --git a/libsolidity/ast/AsmJsonImporter.cpp b/libsolidity/ast/AsmJsonImporter.cpp index a2ff7298b5d9..fb6acc07be41 100644 --- a/libsolidity/ast/AsmJsonImporter.cpp +++ b/libsolidity/ast/AsmJsonImporter.cpp @@ -54,9 +54,8 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; r.location = createSourceLocation(_node); - astAssert(!r.location.isValid() || r.location.hasText(), "Invalid source location in Asm AST"); + astAssert(r.location.hasText(), "Invalid source location in Asm AST"); return r; - } Json::Value AsmJsonImporter::member(Json::Value const& _node, string const& _name) diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index eac83e1bbe03..31b30dd45805 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -51,11 +51,11 @@ BOOST_AUTO_TEST_SUITE(Assembler) BOOST_AUTO_TEST_CASE(all_assembly_items) { Assembly _assembly; - auto root_asm = make_shared("lorem ipsum", "root.asm"); + auto root_asm = make_shared("", "root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); Assembly _subAsm; - auto sub_asm = make_shared("lorem ipsum", "sub.asm"); + auto sub_asm = make_shared("", "sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index c5b47d0f682a..8fe4740fdd2e 100644 --- a/test/liblangutil/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -33,9 +33,9 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest) BOOST_AUTO_TEST_CASE(test_fail) { - auto const source = std::make_shared("lorem ipsum", "source"); - auto const sourceA = std::make_shared("lorem ipsum", "sourceA"); - auto const sourceB = std::make_shared("lorem ipsum", "sourceB"); + auto const source = std::make_shared("", "source"); + auto const sourceA = std::make_shared("", "sourceA"); + auto const sourceB = std::make_shared("", "sourceB"); BOOST_CHECK(SourceLocation{} == SourceLocation{}); BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB})); From 05b4ac0d29b3dc0d29fa13eef1a2deace634a3ea Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 15 Jan 2020 14:19:13 +0100 Subject: [PATCH 051/160] CircleCI: Adds pylint test for all python files in test/ directory. --- .circleci/config.yml | 17 ++++++++++ scripts/pylint_all.py | 56 ++++++++++++++++++++++++++++++++ scripts/pylintrc | 74 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100755 scripts/pylint_all.py create mode 100644 scripts/pylintrc diff --git a/.circleci/config.yml b/.circleci/config.yml index a779044748be..4a3698c62686 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -257,6 +257,22 @@ jobs: name: Check for C++ coding style command: ./scripts/check_style.sh + chk_pylint: + docker: + - image: buildpack-deps:eoan + steps: + - checkout + - run: + name: Install pip + command: apt -q update && apt install -y python3-pip + - run: + name: Install pylint + command: python3 -m pip install pylint z3-solver + # also z3-solver to make sure pylint knows about this module + - run: + name: Linting Python Scripts + command: ./scripts/pylint_all.py + chk_buglist: docker: - image: circleci/node @@ -718,6 +734,7 @@ workflows: # DISABLED FOR 0.6.0 - chk_docs_examples: *workflow_trigger_on_tags - chk_buglist: *workflow_trigger_on_tags - chk_proofs: *workflow_trigger_on_tags + - chk_pylint: *workflow_trigger_on_tags # build-only - b_docs: *workflow_trigger_on_tags diff --git a/scripts/pylint_all.py b/scripts/pylint_all.py new file mode 100755 index 000000000000..a0ddf701cab4 --- /dev/null +++ b/scripts/pylint_all.py @@ -0,0 +1,56 @@ +#! /usr/bin/env python3 + +""" +Performs pylint on all python files in the project repo's test directory recursively. + +This script is meant to be run from the CI but can also be easily in local dev environment, +where you can optionally pass `-d` as command line argument to let this script abort on first error. +""" + +from os import path, walk, system +from sys import argv, exit as brexit + +PROJECT_ROOT = path.dirname(path.realpath(__file__)) +PYLINT_RCFILE = "{}/pylintrc".format(PROJECT_ROOT) + +SGR_INFO = "\033[1;32m" +SGR_CLEAR = "\033[0m" + +def pylint_all_filenames(dev_mode, rootdirs): + """ Performs pylint on all python files within given root directory (recursively). """ + filenames = [] + for rootdir in rootdirs: + for rootpath, _, filenames_w in walk(rootdir): + for filename in filenames_w: + if filename.endswith('.py'): + filenames.append(path.join(rootpath, filename)) + + checked_count = 0 + failed = [] + for filename in filenames: + checked_count += 1 + cmdline = "pylint --rcfile=\"{}\" \"{}\"".format(PYLINT_RCFILE, filename) + print("{}[{}/{}] Running pylint on file: {}{}".format(SGR_INFO, checked_count, len(filenames), + filename, SGR_CLEAR)) + exit_code = system(cmdline) + if exit_code != 0: + if dev_mode: + return 1, checked_count + else: + failed.append(filename) + + return len(failed), len(filenames) + +def main(): + """" Collects all python script root dirs and runs pylint on them. """ + dev_mode = len(argv) == 2 and argv[1] == "-d" + failed_count, total_count = pylint_all_filenames(dev_mode, [ + path.abspath(path.dirname(__file__) + "/../scripts"), + path.abspath(path.dirname(__file__) + "/../test")]) + if failed_count != 0: + brexit("pylint failed on {}/{} files.".format(failed_count, total_count)) + else: + print("Successfully tested {} files.".format(total_count)) + +if __name__ == "__main__": + main() diff --git a/scripts/pylintrc b/scripts/pylintrc new file mode 100644 index 000000000000..bf1f3c0c4e07 --- /dev/null +++ b/scripts/pylintrc @@ -0,0 +1,74 @@ +# vim:et:ts=4 +[MESSAGES CONTROL] + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" + +# 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. +disable= + bad-continuation, + bad-indentation, + bad-whitespace, + consider-using-sys-exit, + invalid-name, + missing-docstring, + mixed-indentation, + no-else-return, + no-self-use, + pointless-string-statement, + redefined-builtin, + redefined-outer-name, + singleton-comparison, + superfluous-parens, + too-few-public-methods, + trailing-newlines, + undefined-variable, + ungrouped-imports, + unnecessary-semicolon, + unused-import, + unused-variable, + unused-wildcard-import, + useless-object-inheritance, + wildcard-import + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +# added: f, h, x, a, b, p +good-names= + Run, + _, + a, + b, + ex, + f, + f, + h, + h, + i, + j, + k, + l, + m, + n, + p, + x + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format=LF + +# Maximum number of characters on a single line. +max-line-length=130 From a3421709fe2bb5e26385516c234f2e6c9ababb19 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Fri, 31 Jan 2020 14:26:55 +0100 Subject: [PATCH 052/160] python: Fixing some python2-to-python3 migrations that I missed in the last PR. --- scripts/splitSources.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/splitSources.py b/scripts/splitSources.py index d99ffb07254a..01a9f9278b3e 100755 --- a/scripts/splitSources.py +++ b/scripts/splitSources.py @@ -27,8 +27,8 @@ def extractSourceName(line): # writes the following source into a file named sourceName def writeSourceToFile(lines): filePath, srcName = extractSourceName(lines[0]) - # print "sourceName is", srcName - # print "filePath is", filePath + # print("sourceName is ", srcName) + # print("filePath is", filePath) if filePath != False: os.system("mkdir -p " + filePath) f = open(srcName, mode='a+', encoding='utf8') From 4b38f499ae33ecf4971af24481b1bd9b565bed53 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 3 Feb 2020 11:44:28 +0100 Subject: [PATCH 053/160] pylint: reduces max-line-length to something more reasonable --- scripts/bytecodecompare/prepare_report.py | 3 ++- scripts/fix_homebrew_paths_in_standalone_zip.py | 4 +++- scripts/pylintrc | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index baeff7884d9b..7dbe85965153 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -33,7 +33,8 @@ for contractName in sorted(result['contracts'][filename].keys()): contractData = result['contracts'][filename][contractName] if 'evm' in contractData and 'bytecode' in contractData['evm']: - REPORT_FILE.write(filename + ':' + contractName + ' ' + contractData['evm']['bytecode']['object'] + '\n') + REPORT_FILE.write(filename + ':' + contractName + ' ' + + contractData['evm']['bytecode']['object'] + '\n') else: REPORT_FILE.write(filename + ':' + contractName + ' NO BYTECODE\n') REPORT_FILE.write(filename + ':' + contractName + ' ' + contractData['metadata'] + '\n') diff --git a/scripts/fix_homebrew_paths_in_standalone_zip.py b/scripts/fix_homebrew_paths_in_standalone_zip.py index 980ba1a887e8..173f8a071f4c 100755 --- a/scripts/fix_homebrew_paths_in_standalone_zip.py +++ b/scripts/fix_homebrew_paths_in_standalone_zip.py @@ -49,7 +49,9 @@ def readDependencies(fname): for line in o.stdout: if line[0] == '\t': library = line.split(' ', 1)[0][1:] - if library.startswith("/usr/local/lib") or library.startswith("/usr/local/opt") or library.startswith("/Users/"): + if (library.startswith("/usr/local/lib") or + library.startswith("/usr/local/opt") or + library.startswith("/Users/")): if (os.path.basename(library) != os.path.basename(fname)): command = "install_name_tool -change " + \ library + " @executable_path/./" + \ diff --git a/scripts/pylintrc b/scripts/pylintrc index bf1f3c0c4e07..ca2141ef59b6 100644 --- a/scripts/pylintrc +++ b/scripts/pylintrc @@ -71,4 +71,4 @@ good-names= expected-line-ending-format=LF # Maximum number of characters on a single line. -max-line-length=130 +max-line-length=110 From 33f990a96a9ea30bb1e77c267bded7f8b4618d68 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Feb 2020 17:01:13 +0100 Subject: [PATCH 054/160] Fix style of documentation config. --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index cca7478d073a..c8d60690b755 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,8 @@ import os import re +from pygments_lexer_solidity import SolidityLexer + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -24,7 +26,6 @@ def setup(sphinx): thisdir = os.path.dirname(os.path.realpath(__file__)) sys.path.insert(0, thisdir + '/utils') - from pygments_lexer_solidity import SolidityLexer sphinx.add_lexer('Solidity', SolidityLexer()) sphinx.add_stylesheet('css/custom.css') From d3cbfb0c5cc337e77a430c2c941dcb2178dc3ecc Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 4 Feb 2020 10:59:23 +0100 Subject: [PATCH 055/160] Allow user-defined types as mapping keys in parser and restrict to contracts during type checking. --- Changelog.md | 2 +- docs/grammar.txt | 2 +- docs/types/mapping-types.rst | 5 +- libsolidity/analysis/TypeChecker.cpp | 19 ++ libsolidity/analysis/TypeChecker.h | 1 + libsolidity/ast/AST.h | 6 +- libsolidity/ast/ASTJsonImporter.cpp | 2 +- libsolidity/parsing/Parser.cpp | 18 +- test/libsolidity/ASTJSON/mappings.json | 227 ++++++++++++++++ test/libsolidity/ASTJSON/mappings.sol | 8 + test/libsolidity/ASTJSON/mappings_legacy.json | 248 ++++++++++++++++++ .../types/mapping_contract_key.sol | 28 ++ .../types/mapping_contract_key_getter.sol | 38 +++ .../types/mapping_contract_key_library.sol | 35 +++ .../semanticTests/types/mapping_enum_key.sol | 30 +++ .../types/mapping_enum_key_getter.sol | 38 +++ .../types/mapping_enum_key_getter_v2.sol | 39 +++ .../types/mapping_enum_key_library.sol | 35 +++ .../syntaxTests/mappings/contract_mapping.sol | 9 + .../mappings/contract_mapping_invalid.sol | 13 + .../syntaxTests/mappings/enum_mapping.sol | 7 + .../mappings/enum_mapping_invalid.sol | 10 + .../parsing/mapping_nonelementary_key_2.sol | 2 +- .../parsing/mapping_nonelementary_key_3.sol | 2 +- .../variable_definition_in_mapping.sol | 2 +- 25 files changed, 808 insertions(+), 18 deletions(-) create mode 100644 test/libsolidity/ASTJSON/mappings.json create mode 100644 test/libsolidity/ASTJSON/mappings.sol create mode 100644 test/libsolidity/ASTJSON/mappings_legacy.json create mode 100644 test/libsolidity/semanticTests/types/mapping_contract_key.sol create mode 100644 test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol create mode 100644 test/libsolidity/semanticTests/types/mapping_contract_key_library.sol create mode 100644 test/libsolidity/semanticTests/types/mapping_enum_key.sol create mode 100644 test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol create mode 100644 test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol create mode 100644 test/libsolidity/semanticTests/types/mapping_enum_key_library.sol create mode 100644 test/libsolidity/syntaxTests/mappings/contract_mapping.sol create mode 100644 test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol create mode 100644 test/libsolidity/syntaxTests/mappings/enum_mapping.sol create mode 100644 test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol diff --git a/Changelog.md b/Changelog.md index c4c5450eb803..c316e891f8ff 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ ### 0.6.3 (unreleased) Language Features: - + * Allow contract types and enums as keys for mappings. Compiler Features: diff --git a/docs/grammar.txt b/docs/grammar.txt index 06b30649939c..490e53bdad9b 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -59,7 +59,7 @@ TypeName = ElementaryTypeName UserDefinedTypeName = Identifier ( '.' Identifier )* -Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' +Mapping = 'mapping' '(' ( ElementaryTypeName | UserDefinedTypeName ) '=>' TypeName ')' ArrayTypeName = TypeName '[' Expression? ']' FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )* ( 'returns' FunctionTypeParameterList )? diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index 5b41d4bd0cc8..acb48fb8a268 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -7,9 +7,8 @@ Mapping Types Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables of mapping type are declared using the syntax ``mapping(_KeyType => _ValueType) _VariableName``. The ``_KeyType`` can be any -built-in value type plus ``bytes`` and ``string``. User-defined -or complex types such as contract types, enums, mappings, structs or array types -apart from ``bytes`` and ``string`` are not allowed. +built-in value type, ``bytes``, ``string``, or any contract or enum type. Other user-defined +or complex types, such as mappings, structs or array types are not allowed. ``_ValueType`` can be any type, including mappings, arrays and structs. You can think of mappings as `hash tables `_, which are virtually initialised diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 82a362989b42..1673424a2aa4 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2876,6 +2876,25 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.annotation().isPure = true; } +bool TypeChecker::visit(Mapping const& _mapping) +{ + if (auto const* keyType = dynamic_cast(&_mapping.keyType())) + { + if ( + keyType->annotation().type->category() != Type::Category::Contract && + keyType->annotation().type->category() != Type::Category::Enum + ) + m_errorReporter.typeError( + keyType->location(), + "Only elementary types, contract types or enums are allowed as mapping keys." + ); + } + else + solAssert(dynamic_cast(&_mapping.keyType()), ""); + return true; +} + + bool TypeChecker::contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set const& _seenContracts diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index d428a6ac9754..5dae91e2d85c 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -143,6 +143,7 @@ class TypeChecker: private ASTConstVisitor bool visit(Identifier const& _identifier) override; void endVisit(ElementaryTypeNameExpression const& _expr) override; void endVisit(Literal const& _literal) override; + bool visit(Mapping const& _mapping) override; bool contractDependenciesAreCyclic( ContractDefinition const& _contract, diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 5a5e561c9142..4bb9a0570931 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1152,18 +1152,18 @@ class Mapping: public TypeName Mapping( int64_t _id, SourceLocation const& _location, - ASTPointer const& _keyType, + ASTPointer const& _keyType, ASTPointer const& _valueType ): TypeName(_id, _location), m_keyType(_keyType), m_valueType(_valueType) {} void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; - ElementaryTypeName const& keyType() const { return *m_keyType; } + TypeName const& keyType() const { return *m_keyType; } TypeName const& valueType() const { return *m_valueType; } private: - ASTPointer m_keyType; + ASTPointer m_keyType; ASTPointer m_valueType; }; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 8bdecdbb836f..aa28e3f17f06 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -511,7 +511,7 @@ ASTPointer ASTJsonImporter::createMapping(Json::Value const& _node) { return createASTNode( _node, - createElementaryTypeName(member(_node, "keyType")), + convertJsonToASTNode(member(_node, "keyType")), convertJsonToASTNode(member(_node, "valueType")) ); } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 2b0e9131f92c..6072ef6bb4bf 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1019,16 +1019,22 @@ ASTPointer Parser::parseMapping() ASTNodeFactory nodeFactory(*this); expectToken(Token::Mapping); expectToken(Token::LParen); - ASTPointer keyType; + ASTPointer keyType; Token token = m_scanner->currentToken(); - if (!TokenTraits::isElementaryTypeName(token)) - fatalParserError(string("Expected elementary type name for mapping key type")); unsigned firstSize; unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); - ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize); - keyType = ASTNodeFactory(*this).createNode(elemTypeName); - m_scanner->next(); + if (token == Token::Identifier) + keyType = parseUserDefinedTypeName(); + else if (TokenTraits::isElementaryTypeName(token)) + { + keyType = ASTNodeFactory(*this).createNode( + ElementaryTypeNameToken{token, firstSize, secondSize} + ); + m_scanner->next(); + } + else + fatalParserError(string("Expected elementary type name or identifier for mapping key type")); expectToken(Token::Arrow); bool const allowVar = false; ASTPointer valueType = parseTypeName(allowVar); diff --git a/test/libsolidity/ASTJSON/mappings.json b/test/libsolidity/ASTJSON/mappings.json new file mode 100644 index 000000000000..0bd1ecd989d3 --- /dev/null +++ b/test/libsolidity/ASTJSON/mappings.json @@ -0,0 +1,227 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 17 + ] + }, + "id": 18, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 17, + "linearizedBaseContracts": + [ + 17 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "canonicalName": "C.E", + "id": 4, + "members": + [ + { + "id": 1, + "name": "A", + "nodeType": "EnumValue", + "src": "26:1:1" + }, + { + "id": 2, + "name": "B", + "nodeType": "EnumValue", + "src": "29:1:1" + }, + { + "id": 3, + "name": "C", + "nodeType": "EnumValue", + "src": "32:1:1" + } + ], + "name": "E", + "nodeType": "EnumDefinition", + "src": "17:18:1" + }, + { + "constant": false, + "id": 8, + "name": "a", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "40:20:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_contract$_C_$17_$_t_bool_$", + "typeString": "mapping(contract C => bool)" + }, + "typeName": + { + "id": 7, + "keyType": + { + "contractScope": null, + "id": 5, + "name": "C", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 17, + "src": "48:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_C_$17", + "typeString": "contract C" + } + }, + "nodeType": "Mapping", + "src": "40:18:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_contract$_C_$17_$_t_bool_$", + "typeString": "mapping(contract C => bool)" + }, + "valueType": + { + "id": 6, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "53:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + }, + "value": null, + "visibility": "internal" + }, + { + "constant": false, + "id": 12, + "name": "b", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "66:26:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_address_$_t_bool_$", + "typeString": "mapping(address => bool)" + }, + "typeName": + { + "id": 11, + "keyType": + { + "id": 9, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "74:7:1", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "Mapping", + "src": "66:24:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_address_$_t_bool_$", + "typeString": "mapping(address => bool)" + }, + "valueType": + { + "id": 10, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "85:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + }, + "value": null, + "visibility": "internal" + }, + { + "constant": false, + "id": 16, + "name": "c", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "98:20:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_enum$_E_$4_$_t_bool_$", + "typeString": "mapping(enum C.E => bool)" + }, + "typeName": + { + "id": 15, + "keyType": + { + "contractScope": null, + "id": 13, + "name": "E", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 4, + "src": "106:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_enum$_E_$4", + "typeString": "enum C.E" + } + }, + "nodeType": "Mapping", + "src": "98:18:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_enum$_E_$4_$_t_bool_$", + "typeString": "mapping(enum C.E => bool)" + }, + "valueType": + { + "id": 14, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "111:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + }, + "value": null, + "visibility": "internal" + } + ], + "scope": 18, + "src": "0:121:1" + } + ], + "src": "0:122:1" +} diff --git a/test/libsolidity/ASTJSON/mappings.sol b/test/libsolidity/ASTJSON/mappings.sol new file mode 100644 index 000000000000..794396a7d247 --- /dev/null +++ b/test/libsolidity/ASTJSON/mappings.sol @@ -0,0 +1,8 @@ +contract C { + enum E { A, B, C } + mapping(C => bool) a; + mapping(address => bool) b; + mapping(E => bool) c; +} + +// ---- diff --git a/test/libsolidity/ASTJSON/mappings_legacy.json b/test/libsolidity/ASTJSON/mappings_legacy.json new file mode 100644 index 000000000000..79814effec1f --- /dev/null +++ b/test/libsolidity/ASTJSON/mappings_legacy.json @@ -0,0 +1,248 @@ +{ + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 17 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 17 + ], + "name": "C", + "scope": 18 + }, + "children": + [ + { + "attributes": + { + "canonicalName": "C.E", + "name": "E" + }, + "children": + [ + { + "attributes": + { + "name": "A" + }, + "id": 1, + "name": "EnumValue", + "src": "26:1:1" + }, + { + "attributes": + { + "name": "B" + }, + "id": 2, + "name": "EnumValue", + "src": "29:1:1" + }, + { + "attributes": + { + "name": "C" + }, + "id": 3, + "name": "EnumValue", + "src": "32:1:1" + } + ], + "id": 4, + "name": "EnumDefinition", + "src": "17:18:1" + }, + { + "attributes": + { + "constant": false, + "name": "a", + "overrides": null, + "scope": 17, + "stateVariable": true, + "storageLocation": "default", + "type": "mapping(contract C => bool)", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "type": "mapping(contract C => bool)" + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "C", + "referencedDeclaration": 17, + "type": "contract C" + }, + "id": 5, + "name": "UserDefinedTypeName", + "src": "48:1:1" + }, + { + "attributes": + { + "name": "bool", + "type": "bool" + }, + "id": 6, + "name": "ElementaryTypeName", + "src": "53:4:1" + } + ], + "id": 7, + "name": "Mapping", + "src": "40:18:1" + } + ], + "id": 8, + "name": "VariableDeclaration", + "src": "40:20:1" + }, + { + "attributes": + { + "constant": false, + "name": "b", + "overrides": null, + "scope": 17, + "stateVariable": true, + "storageLocation": "default", + "type": "mapping(address => bool)", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "type": "mapping(address => bool)" + }, + "children": + [ + { + "attributes": + { + "name": "address", + "type": "address" + }, + "id": 9, + "name": "ElementaryTypeName", + "src": "74:7:1" + }, + { + "attributes": + { + "name": "bool", + "type": "bool" + }, + "id": 10, + "name": "ElementaryTypeName", + "src": "85:4:1" + } + ], + "id": 11, + "name": "Mapping", + "src": "66:24:1" + } + ], + "id": 12, + "name": "VariableDeclaration", + "src": "66:26:1" + }, + { + "attributes": + { + "constant": false, + "name": "c", + "overrides": null, + "scope": 17, + "stateVariable": true, + "storageLocation": "default", + "type": "mapping(enum C.E => bool)", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "type": "mapping(enum C.E => bool)" + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "E", + "referencedDeclaration": 4, + "type": "enum C.E" + }, + "id": 13, + "name": "UserDefinedTypeName", + "src": "106:1:1" + }, + { + "attributes": + { + "name": "bool", + "type": "bool" + }, + "id": 14, + "name": "ElementaryTypeName", + "src": "111:4:1" + } + ], + "id": 15, + "name": "Mapping", + "src": "98:18:1" + } + ], + "id": 16, + "name": "VariableDeclaration", + "src": "98:20:1" + } + ], + "id": 17, + "name": "ContractDefinition", + "src": "0:121:1" + } + ], + "id": 18, + "name": "SourceUnit", + "src": "0:122:1" +} diff --git a/test/libsolidity/semanticTests/types/mapping_contract_key.sol b/test/libsolidity/semanticTests/types/mapping_contract_key.sol new file mode 100644 index 000000000000..877ead6de1d2 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_contract_key.sol @@ -0,0 +1,28 @@ +interface A {} +contract test { + mapping(A => uint8) table; + function get(A k) public returns (uint8 v) { + return table[k]; + } + function set(A k, uint8 v) public { + table[k] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// get(address): 0 -> 0 +// get(address): 0x01 -> 0 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0xa1 -> +// get(address): 0 -> 0 +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x00, 0xef -> +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0x05 -> +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0x05 +// get(address): 0xa7 -> 0 diff --git a/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol b/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol new file mode 100644 index 000000000000..ee916a63d750 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol @@ -0,0 +1,38 @@ +interface A {} +contract test { + mapping(A => uint8) public table; + function set(A k, uint8 v) public { + table[k] = v; + } + function get(A k) public returns (uint8) { + return this.table(k); + } +} +// ---- +// table(address): 0 -> 0 +// table(address): 0x01 -> 0 +// table(address): 0xa7 -> 0 +// get(address): 0 -> 0 +// get(address): 0x01 -> 0 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0xa1 -> +// table(address): 0 -> 0 +// table(address): 0x01 -> 0xa1 +// table(address): 0xa7 -> 0 +// get(address): 0 -> 0 +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x00, 0xef -> +// table(address): 0 -> 0xef +// table(address): 0x01 -> 0xa1 +// table(address): 0xa7 -> 0 +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0x05 -> +// table(address): 0 -> 0xef +// table(address): 0x01 -> 0x05 +// table(address): 0xa7 -> 0 +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0x05 +// get(address): 0xa7 -> 0 diff --git a/test/libsolidity/semanticTests/types/mapping_contract_key_library.sol b/test/libsolidity/semanticTests/types/mapping_contract_key_library.sol new file mode 100644 index 000000000000..bedb605c177e --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_contract_key_library.sol @@ -0,0 +1,35 @@ +interface A {} +library L { + function get(mapping(A => uint8) storage table, A k) external returns (uint8) { + return table[k]; + } + function set(mapping(A => uint8) storage table, A k, uint8 v) external { + table[k] = v; + } +} +contract test { + mapping(A => uint8) table; + function get(A k) public returns (uint8 v) { + return L.get(table, k); + } + function set(A k, uint8 v) public { + L.set(table, k, v); + } +} +// ---- +// library: L +// get(address): 0 -> 0 +// get(address): 0x01 -> 0 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0xa1 -> +// get(address): 0 -> 0 +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x00, 0xef -> +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0x05 -> +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0x05 +// get(address): 0xa7 -> 0 diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key.sol b/test/libsolidity/semanticTests/types/mapping_enum_key.sol new file mode 100644 index 000000000000..befc46a87d40 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key.sol @@ -0,0 +1,30 @@ +enum E { A, B, C } +contract test { + mapping(E => uint8) table; + function get(E k) public returns (uint8 v) { + return table[k]; + } + function set(E k, uint8 v) public { + table[k] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0x02 -> 0 +// get(uint8): 0x03 -> FAILURE +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol new file mode 100644 index 000000000000..42ee8ab1709e --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol @@ -0,0 +1,38 @@ +contract test { + enum E { A, B, C } + mapping(E => uint8) public table; + function set(E k, uint8 v) public { + table[k] = v; + } + function get(E k) public returns (uint8) { + return this.table(k); + } +} +// ---- +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0 +// table(uint8): 0xa7 -> 0 +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> 0 +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> 0 +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0x05 +// table(uint8): 0xa7 -> 0 +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol new file mode 100644 index 000000000000..f1fd184eda90 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol @@ -0,0 +1,39 @@ +pragma experimental ABIEncoderV2; +contract test { + enum E { A, B, C } + mapping(E => uint8) public table; + function set(E k, uint8 v) public { + table[k] = v; + } + function get(E k) public returns (uint8) { + return this.table(k); + } +} +// ---- +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0 +// table(uint8): 0xa7 -> FAILURE +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> FAILURE +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> FAILURE +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0x05 +// table(uint8): 0xa7 -> FAILURE +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol new file mode 100644 index 000000000000..d1484238ddc5 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol @@ -0,0 +1,35 @@ +enum E { A, B, C } +library L { + function get(mapping(E => uint8) storage table, E k) external returns (uint8) { + return table[k]; + } + function set(mapping(E => uint8) storage table, E k, uint8 v) external { + table[k] = v; + } +} +contract test { + mapping(E => uint8) table; + function get(E k) public returns (uint8 v) { + return L.get(table, k); + } + function set(E k, uint8 v) public { + L.set(table, k, v); + } +} +// ---- +// library: L +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/syntaxTests/mappings/contract_mapping.sol b/test/libsolidity/syntaxTests/mappings/contract_mapping.sol new file mode 100644 index 000000000000..f1a4f54a9efe --- /dev/null +++ b/test/libsolidity/syntaxTests/mappings/contract_mapping.sol @@ -0,0 +1,9 @@ +interface I {} +contract J {} +contract C { + mapping(I => bool) i; + mapping(J => bool) j; + function f(I x, J y) public view returns (bool, bool) { + return (i[x], j[y]); + } +} diff --git a/test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol b/test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol new file mode 100644 index 000000000000..ffe100facd5c --- /dev/null +++ b/test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol @@ -0,0 +1,13 @@ +interface I {} +contract J {} +contract C { + mapping(I => bool) i; + mapping(J => bool) j; + function f(I x, J y, address z) public view returns (bool, bool, bool) { + return (i[y], j[x], i[z]); + } +} +// ---- +// TypeError: (189-190): Type contract J is not implicitly convertible to expected type contract I. +// TypeError: (195-196): Type contract I is not implicitly convertible to expected type contract J. +// TypeError: (201-202): Type address is not implicitly convertible to expected type contract I. diff --git a/test/libsolidity/syntaxTests/mappings/enum_mapping.sol b/test/libsolidity/syntaxTests/mappings/enum_mapping.sol new file mode 100644 index 000000000000..88163302a912 --- /dev/null +++ b/test/libsolidity/syntaxTests/mappings/enum_mapping.sol @@ -0,0 +1,7 @@ +enum E { A, B, C } +contract C { + mapping(E => bool) e; + function f(E v) public view returns (bool, bool) { + return (e[v], e[E.A]); + } +} diff --git a/test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol b/test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol new file mode 100644 index 000000000000..02eb516e6956 --- /dev/null +++ b/test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol @@ -0,0 +1,10 @@ +enum E { A, B, C } +contract C { + mapping(E => bool) e; + function f(uint256 a, uint8 b) public view returns (bool, bool) { + return (e[a], e[b]); + } +} +// ---- +// TypeError: (146-147): Type uint256 is not implicitly convertible to expected type enum E. +// TypeError: (152-153): Type uint8 is not implicitly convertible to expected type enum E. diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol index 713cddeb35e7..e15e1aea98c8 100644 --- a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol @@ -5,4 +5,4 @@ contract c { mapping(S => uint) data; } // ---- -// ParserError: (47-48): Expected elementary type name for mapping key type +// TypeError: (47-48): Only elementary types, contract types or enums are allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol index 655af9de7ec7..b17cf4139f40 100644 --- a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol @@ -5,4 +5,4 @@ contract c { mapping(S => uint) data; } // ---- -// ParserError: (49-50): Expected elementary type name for mapping key type +// TypeError: (49-50): Only elementary types, contract types or enums are allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol b/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol index 61f5be532a7d..f9ccfb5805a9 100644 --- a/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol +++ b/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol @@ -4,4 +4,4 @@ contract test { } } // ---- -// ParserError: (44-47): Expected elementary type name for mapping key type +// ParserError: (44-47): Expected elementary type name or identifier for mapping key type From af9fc8b63488d55ebdb3dffe853e12e85e542c5e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 4 Feb 2020 14:59:33 +0100 Subject: [PATCH 056/160] Mapping getters for Yul IR. --- libsolidity/codegen/ir/IRGenerator.cpp | 75 +++++++++++++++---- .../viaYul/mapping_enum_key_getter.sol | 26 +++++++ .../semanticTests/viaYul/mapping_getters.sol | 40 ++++++++++ 3 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol create mode 100644 test/libsolidity/semanticTests/viaYul/mapping_getters.sol diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index bd2099c4ac5c..0798ebc37b69 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -159,21 +159,70 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) solAssert(!_varDecl.isConstant(), ""); solAssert(_varDecl.isStateVariable(), ""); - solUnimplementedAssert(type->isValueType(), ""); + if (auto const* mappingType = dynamic_cast(type)) + return m_context.functionCollector()->createFunction(functionName, [&]() { + pair slot_offset = m_context.storageLocationOfVariable(_varDecl); + solAssert(slot_offset.second == 0, ""); + FunctionType funType(_varDecl); + solUnimplementedAssert(funType.returnParameterTypes().size() == 1, ""); + TypePointer returnType = funType.returnParameterTypes().front(); + unsigned num_keys = 0; + stringstream indexAccesses; + string slot = m_context.newYulVariable(); + do + { + solUnimplementedAssert( + mappingType->keyType()->sizeOnStack() == 1, + "Multi-slot mapping key unimplemented - might not be a problem" + ); + indexAccesses << + slot << + " := " << + m_utils.mappingIndexAccessFunction(*mappingType, *mappingType->keyType()) << + "(" << + slot; + if (mappingType->keyType()->sizeOnStack() > 0) + indexAccesses << + ", " << + suffixedVariableNameList("key", num_keys, num_keys + mappingType->keyType()->sizeOnStack()); + indexAccesses << ")\n"; + num_keys += mappingType->keyType()->sizeOnStack(); + } + while ((mappingType = dynamic_cast(mappingType->valueType()))); - return m_context.functionCollector()->createFunction(functionName, [&]() { - pair slot_offset = m_context.storageLocationOfVariable(_varDecl); + return Whiskers(R"( + function () -> rval { + let := + + rval := () + } + )") + ("functionName", functionName) + ("keys", suffixedVariableNameList("key", 0, num_keys)) + ("readStorage", m_utils.readFromStorage(*returnType, 0, false)) + ("indexAccesses", indexAccesses.str()) + ("slot", slot) + ("base", slot_offset.first.str()) + .render(); + }); + else + { + solUnimplementedAssert(type->isValueType(), ""); - return Whiskers(R"( - function () -> rval { - rval := () - } - )") - ("functionName", functionName) - ("readStorage", m_utils.readFromStorage(*type, slot_offset.second, false)) - ("slot", slot_offset.first.str()) - .render(); - }); + return m_context.functionCollector()->createFunction(functionName, [&]() { + pair slot_offset = m_context.storageLocationOfVariable(_varDecl); + + return Whiskers(R"( + function () -> rval { + rval := () + } + )") + ("functionName", functionName) + ("readStorage", m_utils.readFromStorage(*type, slot_offset.second, false)) + ("slot", slot_offset.first.str()) + .render(); + }); + } } string IRGenerator::constructorCode(ContractDefinition const& _contract) diff --git a/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol b/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol new file mode 100644 index 000000000000..78d9a39b9b16 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol @@ -0,0 +1,26 @@ +pragma experimental ABIEncoderV2; +contract test { + enum E { A, B, C } + mapping(E => uint8) public table; + function set(E k, uint8 v) public { + table[k] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0 +// table(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0x05 +// table(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/mapping_getters.sol b/test/libsolidity/semanticTests/viaYul/mapping_getters.sol new file mode 100644 index 000000000000..1db4eb8c5933 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/mapping_getters.sol @@ -0,0 +1,40 @@ +contract test { + mapping(uint256 => uint256) public m1; + mapping(uint256 => mapping(uint256 => uint256)) public m2; + function set(uint256 k, uint256 v) public { + m1[k] = v; + } + function set(uint256 k1, uint256 k2, uint256 v) public { + m2[k1][k2] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// m1(uint256): 0 -> 0 +// m1(uint256): 0x01 -> 0 +// m1(uint256): 0xa7 -> 0 +// set(uint256,uint256): 0x01, 0xa1 -> +// m1(uint256): 0 -> 0 +// m1(uint256): 0x01 -> 0xa1 +// m1(uint256): 0xa7 -> 0 +// set(uint256,uint256): 0x00, 0xef -> +// m1(uint256): 0 -> 0xef +// m1(uint256): 0x01 -> 0xa1 +// m1(uint256): 0xa7 -> 0 +// set(uint256,uint256): 0x01, 0x05 -> +// m1(uint256): 0 -> 0xef +// m1(uint256): 0x01 -> 0x05 +// m1(uint256): 0xa7 -> 0 +// m2(uint256,uint256): 0, 0 -> 0 +// m2(uint256,uint256): 0, 0x01 -> 0 +// m2(uint256,uint256): 0xa7, 0 -> 0 +// m2(uint256,uint256): 0xa7, 0x01 -> 0 +// set(uint256,uint256,uint256): 0xa7, 0x01, 0x23 +// m2(uint256,uint256): 0, 0x01 -> 0 +// m2(uint256,uint256): 0xa7, 0 -> 0 +// m2(uint256,uint256): 0xa7, 0x01 -> 0x23 +// set(uint256,uint256,uint256): 0, 0x01, 0xef +// m2(uint256,uint256): 0, 0x01 -> 0xef +// m2(uint256,uint256): 0xa7, 0 -> 0 +// m2(uint256,uint256): 0xa7, 0x01 -> 0x23 From d882b6fa83f6fc3c46b70d024dc577bbd87f2e7c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 4 Feb 2020 16:01:30 +0100 Subject: [PATCH 057/160] Allow tests to be marked ABIEncoderV1Only. --- test/libsolidity/SemanticTest.cpp | 16 ++++++++++++++++ test/libsolidity/SemanticTest.h | 3 +++ .../types/mapping_enum_key_getter.sol | 2 ++ 3 files changed, 21 insertions(+) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index eb01764bc519..027fcf1a0d32 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -61,10 +61,26 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer } m_settings.erase("compileViaYul"); } + if (m_settings.count("ABIEncoderV1Only")) + { + if (m_settings["ABIEncoderV1Only"] == "true") + { + m_validatedSettings["ABIEncoderV1Only"] = "true"; + m_runWithABIEncoderV1Only = true; + } + m_settings.erase("ABIEncoderV1Only"); + } parseExpectations(file); soltestAssert(!m_tests.empty(), "No tests specified in " + _filename); } +bool SemanticTest::validateSettings(langutil::EVMVersion _evmVersion) +{ + if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) + return false; + return EVMVersionRestrictedTestCase::validateSettings(_evmVersion); +} + TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { for(bool compileViaYul: set{!m_runWithoutYul, m_runWithYul}) diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 680e03316825..5cd8e5cc76f2 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -44,6 +44,8 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion); + bool validateSettings(langutil::EVMVersion _evmVersion) override; + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix = "") const override; @@ -64,6 +66,7 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict std::vector m_tests; bool m_runWithYul = false; bool m_runWithoutYul = true; + bool m_runWithABIEncoderV1Only = false; }; } diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol index 42ee8ab1709e..9b1b23b83cd9 100644 --- a/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol @@ -8,6 +8,8 @@ contract test { return this.table(k); } } +// ==== +// ABIEncoderV1Only: true // ---- // table(uint8): 0 -> 0 // table(uint8): 0x01 -> 0 From 9f0cef97c249d6bdf980992a1cb7ef99e178262d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 30 Jan 2020 19:40:22 +0100 Subject: [PATCH 058/160] Specify default type for word size transform externally. --- libyul/backends/wasm/EVMToEwasmTranslator.cpp | 2 +- libyul/backends/wasm/WordSizeTransform.cpp | 10 +++++++--- libyul/backends/wasm/WordSizeTransform.h | 7 ++++++- test/libyul/YulOptimizerTest.cpp | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index 004f29092e65..2090dd2f5112 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -1232,7 +1232,7 @@ Object EVMToEwasmTranslator::run(Object const& _object) MainFunction{}(ast); ForLoopConditionIntoBody::run(context, ast); ExpressionSplitter::run(context, ast); - WordSizeTransform::run(m_dialect, ast, nameDispenser); + WordSizeTransform::run(m_dialect, WasmDialect::instance().defaultType, ast, nameDispenser); NameDisplacer{nameDispenser, m_polyfillFunctions}(ast); for (auto const& st: m_polyfill->statements) diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 47d91b7d1673..3aac84870024 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -204,12 +204,16 @@ void WordSizeTransform::operator()(Block& _block) ); } -void WordSizeTransform::run(Dialect const& _inputDialect, Block& _ast, NameDispenser& _nameDispenser) +void WordSizeTransform::run( + Dialect const& _inputDialect, + YulString _targetDefaultType, + Block& _ast, + NameDispenser& _nameDispenser +) { // Free the name `or_bool`. NameDisplacer{_nameDispenser, {"or_bool"_yulstring}}(_ast); - YulString defaultType; // should be i64 at some point. - WordSizeTransform{_inputDialect, _nameDispenser, defaultType}(_ast); + WordSizeTransform{_inputDialect, _nameDispenser, _targetDefaultType}(_ast); } void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList) diff --git a/libyul/backends/wasm/WordSizeTransform.h b/libyul/backends/wasm/WordSizeTransform.h index 75afa279e334..71e526b7fbcc 100644 --- a/libyul/backends/wasm/WordSizeTransform.h +++ b/libyul/backends/wasm/WordSizeTransform.h @@ -67,7 +67,12 @@ class WordSizeTransform: public ASTModifier void operator()(ForLoop&) override; void operator()(Block& _block) override; - static void run(Dialect const& _inputDialect, Block& _ast, NameDispenser& _nameDispenser); + static void run( + Dialect const& _inputDialect, + YulString _targetDefaultType, + Block& _ast, + NameDispenser& _nameDispenser + ); private: explicit WordSizeTransform( diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index c4a6a48d6e48..f3906aead44e 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -349,7 +349,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line { disambiguate(); ExpressionSplitter::run(*m_context, *m_ast); - WordSizeTransform::run(*m_dialect, *m_ast, *m_nameDispenser); + WordSizeTransform::run(*m_dialect, ""_yulstring, *m_ast, *m_nameDispenser); } else if (m_optimizerStep == "fullSuite") { From d3739fe62067fb715651ec5cd4c797a8e888e3c5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Feb 2020 18:12:22 +0100 Subject: [PATCH 059/160] Fix WordSizeTransform to properly assign default types. --- libyul/backends/wasm/WordSizeTransform.cpp | 27 ++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 3aac84870024..9ebf3173836a 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -43,7 +43,11 @@ void WordSizeTransform::operator()(FunctionCall& _fc) { if (BuiltinFunction const* fun = m_inputDialect.builtin(_fc.functionName.name)) if (fun->literalArguments) + { + for (Expression& arg: _fc.arguments) + get(arg).type = m_defaultType; return; + } rewriteFunctionCallArguments(_fc.arguments); } @@ -85,8 +89,13 @@ void WordSizeTransform::operator()(Block& _block) { VariableDeclaration& varDecl = std::get(_s); - // Special handling for datasize and dataoffset - they will only need one variable. - if (varDecl.value && holds_alternative(*varDecl.value)) + if (!varDecl.value) + rewriteVarDeclList(varDecl.variables); + else if (holds_alternative(*varDecl.value)) + { + visit(*varDecl.value); + + // Special handling for datasize and dataoffset - they will only need one variable. if (BuiltinFunction const* f = m_inputDialect.builtin(std::get(*varDecl.value).functionName.name)) if (f->literalArguments) { @@ -108,12 +117,6 @@ void WordSizeTransform::operator()(Block& _block) return {std::move(ret)}; } - if ( - !varDecl.value || - holds_alternative(*varDecl.value) - ) - { - if (varDecl.value) visit(*varDecl.value); rewriteVarDeclList(varDecl.variables); return std::nullopt; } @@ -144,8 +147,11 @@ void WordSizeTransform::operator()(Block& _block) Assignment& assignment = std::get(_s); yulAssert(assignment.value, ""); - // Special handling for datasize and dataoffset - they will only need one variable. if (holds_alternative(*assignment.value)) + { + visit(*assignment.value); + + // Special handling for datasize and dataoffset - they will only need one variable. if (BuiltinFunction const* f = m_inputDialect.builtin(std::get(*assignment.value).functionName.name)) if (f->literalArguments) { @@ -167,9 +173,6 @@ void WordSizeTransform::operator()(Block& _block) return {std::move(ret)}; } - if (holds_alternative(*assignment.value)) - { - if (assignment.value) visit(*assignment.value); rewriteIdentifierList(assignment.variableNames); return std::nullopt; } From cafad3770d703062da2e4e67fa9cbd2db9b1b23f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Feb 2020 18:27:01 +0100 Subject: [PATCH 060/160] Update tests. --- test/cmdlineTests/evm_to_wasm/output | 10 +++++----- .../standard_eWasm_requested/output.json | 12 +++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index a85239b230df..1c31f9488f2e 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -15,9 +15,9 @@ object "object" { function main() { let _1 := 0 - mstore_internal(0, _1, _1, _1, _1) + mstore_internal(_1, _1, _1, _1, _1) mstore_internal(32, _1, _1, _1, 1) - eth.storageStore(0, 32) + eth.storageStore(_1, 32) } function endian_swap_16(x) -> y { @@ -45,7 +45,7 @@ object "object" { Binary representation: -0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010ab501052801017e420021004200200020002000200010054220200020002000420110054200a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3501007e2000a720011004370300200042087ca720021004370300200042107ca720031004370300200042187ca7200410043703000b +0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010ab501052801017e420021002000200020002000200010054220200020002000420110052000a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3501007e2000a720011004370300200042087ca720021004370300200042107ca720031004370300200042187ca7200410043703000b Text representation: (module @@ -56,9 +56,9 @@ Text representation: (func $main (local $_1 i64) (local.set $_1 (i64.const 0)) - (call $mstore_internal (i64.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) + (call $mstore_internal (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $mstore_internal (i64.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1)) - (call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32))) + (call $eth.storageStore (i32.wrap_i64 (local.get $_1)) (i32.wrap_i64 (i64.const 32))) ) (func $endian_swap_16 diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index 0652b99e4494..d273d0b859c3 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -10,23 +10,21 @@ (local $p i64) (local $r i64) (local $hi i64) - (local $hi_1 i64) (local $y i64) - (local $hi_2 i64) + (local $hi_1 i64) (local $_2 i64) (local.set $_1 (i64.const 0)) (local.set $p (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64))) (local.set $r (i64.add (local.get $p) (i64.const 64))) (if (i64.ne (i64.extend_i32_u (i64.lt_u (local.get $r) (local.get $p))) (i64.const 0)) (then (unreachable))) - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $_1)) (i64.const 16))) - (local.set $hi_1 (i64.shl (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) - (local.set $y (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) + (local.set $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (local.get $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) (i64.store (i32.wrap_i64 (local.get $r)) (local.get $y)) (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 8))) (local.get $y)) (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 16))) (local.get $y)) - (local.set $hi_2 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) - (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 24))) (i64.or (local.get $hi_2) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) + (local.set $hi_1 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) + (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 24))) (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) (local.set $_2 (datasize \"C_2_deployed\")) (call $eth.codeCopy (i32.wrap_i64 (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\"))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_2)))) (call $eth.finish (i32.wrap_i64 (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_2)))) From 3416c029cf136d81ee1d0b159f99665d6206ccc7 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 4 Feb 2020 20:37:51 +0100 Subject: [PATCH 061/160] Relaxed assert in AsmJsonImporter::createAsmNode. --- liblangutil/SourceLocation.h | 5 ++--- libsolidity/ast/AsmJsonImporter.cpp | 5 ++++- test/libevmasm/Assembler.cpp | 4 ++-- test/liblangutil/SourceLocation.cpp | 6 +++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index 063c3e417cdc..e6f97942eae6 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -76,8 +76,7 @@ struct SourceLocation source && 0 <= start && start <= end && - // in some cases (json import) source->source() can be empty - (source->source().empty() || end <= int(source->source().length())); + end <= int(source->source().length()); } std::string text() const @@ -125,7 +124,7 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat if (_location.source) _out << _location.source->name(); - _out << "[" << _location.start << "," << _location.end << ")"; + _out << "[" << _location.start << "," << _location.end << "]"; return _out; } diff --git a/libsolidity/ast/AsmJsonImporter.cpp b/libsolidity/ast/AsmJsonImporter.cpp index fb6acc07be41..a47ea27f76b9 100644 --- a/libsolidity/ast/AsmJsonImporter.cpp +++ b/libsolidity/ast/AsmJsonImporter.cpp @@ -54,7 +54,10 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; r.location = createSourceLocation(_node); - astAssert(r.location.hasText(), "Invalid source location in Asm AST"); + astAssert( + r.location.source && 0 <= r.location.start && r.location.start <= r.location.end, + "Invalid source location in Asm AST" + ); return r; } diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 31b30dd45805..eac83e1bbe03 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -51,11 +51,11 @@ BOOST_AUTO_TEST_SUITE(Assembler) BOOST_AUTO_TEST_CASE(all_assembly_items) { Assembly _assembly; - auto root_asm = make_shared("", "root.asm"); + auto root_asm = make_shared("lorem ipsum", "root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); Assembly _subAsm; - auto sub_asm = make_shared("", "sub.asm"); + auto sub_asm = make_shared("lorem ipsum", "sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp index 8fe4740fdd2e..c5b47d0f682a 100644 --- a/test/liblangutil/SourceLocation.cpp +++ b/test/liblangutil/SourceLocation.cpp @@ -33,9 +33,9 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest) BOOST_AUTO_TEST_CASE(test_fail) { - auto const source = std::make_shared("", "source"); - auto const sourceA = std::make_shared("", "sourceA"); - auto const sourceB = std::make_shared("", "sourceB"); + auto const source = std::make_shared("lorem ipsum", "source"); + auto const sourceA = std::make_shared("lorem ipsum", "sourceA"); + auto const sourceB = std::make_shared("lorem ipsum", "sourceB"); BOOST_CHECK(SourceLocation{} == SourceLocation{}); BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB})); From 6ab385d34ab467e9d54ef68e634856c422ca366b Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 15 Jan 2020 14:19:13 +0100 Subject: [PATCH 062/160] CircleCI: Adds pylint test for all python files in test/ directory. --- .circleci/config.yml | 4 ++-- scripts/pylintrc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4a3698c62686..a211edab0cff 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -267,8 +267,8 @@ jobs: command: apt -q update && apt install -y python3-pip - run: name: Install pylint - command: python3 -m pip install pylint z3-solver - # also z3-solver to make sure pylint knows about this module + command: python3 -m pip install pylint z3-solver pygments-lexer-solidity + # also z3-solver to make sure pylint knows about this module, pygments-lexer-solidity for docs - run: name: Linting Python Scripts command: ./scripts/pylint_all.py diff --git a/scripts/pylintrc b/scripts/pylintrc index ca2141ef59b6..bf1f3c0c4e07 100644 --- a/scripts/pylintrc +++ b/scripts/pylintrc @@ -71,4 +71,4 @@ good-names= expected-line-ending-format=LF # Maximum number of characters on a single line. -max-line-length=110 +max-line-length=130 From 07a04bed446c0cb2581c820e747896a090de5c0f Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Fri, 31 Jan 2020 14:26:55 +0100 Subject: [PATCH 063/160] python: Fixing some python2-to-python3 migrations that I missed in the last PR. --- scripts/pylint_all.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/pylint_all.py b/scripts/pylint_all.py index a0ddf701cab4..f35ec93e800d 100755 --- a/scripts/pylint_all.py +++ b/scripts/pylint_all.py @@ -1,14 +1,14 @@ #! /usr/bin/env python3 """ -Performs pylint on all python files in the project repo's test directory recursively. +Performs pylint on all python files in the project repo's {test,script,docs} directory recursively. This script is meant to be run from the CI but can also be easily in local dev environment, where you can optionally pass `-d` as command line argument to let this script abort on first error. """ from os import path, walk, system -from sys import argv, exit as brexit +from sys import argv, exit as exitwith PROJECT_ROOT = path.dirname(path.realpath(__file__)) PYLINT_RCFILE = "{}/pylintrc".format(PROJECT_ROOT) @@ -36,19 +36,20 @@ def pylint_all_filenames(dev_mode, rootdirs): if exit_code != 0: if dev_mode: return 1, checked_count - else: - failed.append(filename) + failed.append(filename) return len(failed), len(filenames) def main(): - """" Collects all python script root dirs and runs pylint on them. """ + """ Collects all python script root dirs and runs pylint on them. You can optionally + pass `-d` as command line argument to let this script abort on first error. """ dev_mode = len(argv) == 2 and argv[1] == "-d" failed_count, total_count = pylint_all_filenames(dev_mode, [ + path.abspath(path.dirname(__file__) + "/../docs"), path.abspath(path.dirname(__file__) + "/../scripts"), path.abspath(path.dirname(__file__) + "/../test")]) if failed_count != 0: - brexit("pylint failed on {}/{} files.".format(failed_count, total_count)) + exitwith("pylint failed on {}/{} files.".format(failed_count, total_count)) else: print("Successfully tested {} files.".format(total_count)) From 8f546849f2c06ad6e6af85bad1939834e94c8f83 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 30 Jan 2020 19:40:06 +0100 Subject: [PATCH 064/160] Add i32 functions to wasm dialect. --- libyul/backends/wasm/WasmDialect.cpp | 117 +++++++++++++++------------ libyul/backends/wasm/WasmDialect.h | 8 +- 2 files changed, 74 insertions(+), 51 deletions(-) diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 896059870619..52fbafe3aa6d 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -20,70 +20,86 @@ #include +#include + using namespace std; using namespace solidity::yul; WasmDialect::WasmDialect() { - defaultType = "i64"_yulstring; - boolType = "i64"_yulstring; - types = {"i64"_yulstring, "i32"_yulstring}; - - for (auto const& name: { - "i64.add", - "i64.sub", - "i64.mul", - "i64.div_u", - "i64.rem_u", - "i64.and", - "i64.or", - "i64.xor", - "i64.shl", - "i64.shr_u", - "i64.eq", - "i64.ne", - "i64.lt_u", - "i64.gt_u", - "i64.le_u", - "i64.ge_u" - }) - addFunction(name, 2, 1); - - m_functions["i64.lt_u"_yulstring].returns.front() = "i32"_yulstring; - m_functions["i64.gt_u"_yulstring].returns.front() = "i32"_yulstring; - m_functions["i64.le_u"_yulstring].returns.front() = "i32"_yulstring; - m_functions["i64.ge_u"_yulstring].returns.front() = "i32"_yulstring; - m_functions["i64.eq"_yulstring].returns.front() = "i32"_yulstring; - m_functions["i64.ne"_yulstring].returns.front() = "i32"_yulstring; - - addFunction("i64.eqz", 1, 1); - m_functions["i64.eqz"_yulstring].returns.front() = "i32"_yulstring; - - addFunction("i64.clz", 1, 1); - - addFunction("i64.store", 2, 0, false); - m_functions["i64.store"_yulstring].parameters.front() = "i32"_yulstring; + YulString i64 = "i64"_yulstring; + YulString i32 = "i32"_yulstring; + defaultType = i64; + boolType = i32; + types = {i64, i32}; + + for (auto t: types) + for (auto const& name: { + "add", + "sub", + "mul", + "div_u", + "rem_u", + "and", + "or", + "xor", + "shl", + "shr_u", + }) + addFunction(t.str() + "." + name, {t, t}, {t}); + + for (auto t: types) + for (auto const& name: { + "eq", + "ne", + "lt_u", + "gt_u", + "le_u", + "ge_u" + }) + addFunction(t.str() + "." + name, {t, t}, {i32}); + + addFunction("i32.eqz", {i32}, {i32}); + addFunction("i64.eqz", {i64}, {i32}); + + addFunction("i32.clz", {i32}, {i32}); + addFunction("i64.clz", {i64}, {i64}); + + addFunction("i32.wrap_i64", {i64}, {i32}); + + addFunction("i64.extend_i32_u", {i32}, {i64}); + + addFunction("i32.store", {i32, i32}, {}, false); + m_functions["i32.store"_yulstring].sideEffects.invalidatesStorage = false; + addFunction("i64.store", {i32, i64}, {}, false); m_functions["i64.store"_yulstring].sideEffects.invalidatesStorage = false; - addFunction("i64.store8", 2, 0, false); - m_functions["i64.store8"_yulstring].parameters.front() = "i32"_yulstring; + addFunction("i32.store8", {i32, i32}, {}, false); + m_functions["i32.store8"_yulstring].sideEffects.invalidatesStorage = false; + addFunction("i64.store8", {i32, i64}, {}, false); m_functions["i64.store8"_yulstring].sideEffects.invalidatesStorage = false; - addFunction("i64.load", 1, 1, false); - m_functions["i64.load"_yulstring].parameters.front() = "i32"_yulstring; + addFunction("i32.load", {i32}, {i32}, false); + m_functions["i32.load"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i32.load"_yulstring].sideEffects.invalidatesMemory = false; + m_functions["i32.load"_yulstring].sideEffects.sideEffectFree = true; + m_functions["i32.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; + addFunction("i64.load", {i32}, {i64}, false); m_functions["i64.load"_yulstring].sideEffects.invalidatesStorage = false; m_functions["i64.load"_yulstring].sideEffects.invalidatesMemory = false; m_functions["i64.load"_yulstring].sideEffects.sideEffectFree = true; m_functions["i64.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; - addFunction("drop", 1, 0); + // Drop is actually overloaded for all types, but Yul does not support that. + // We could introduce "i32.drop". + addFunction("drop", {i64}, {}); - addFunction("unreachable", 0, 0, false); + addFunction("unreachable", {}, {}, false); m_functions["unreachable"_yulstring].sideEffects.invalidatesStorage = false; m_functions["unreachable"_yulstring].sideEffects.invalidatesMemory = false; - addFunction("datasize", 1, 1, true, true); - addFunction("dataoffset", 1, 1, true, true); + addFunction("datasize", {i64}, {i64}, true, true); + addFunction("dataoffset", {i64}, {i64}, true, true); addEthereumExternals(); } @@ -167,8 +183,8 @@ void WasmDialect::addEthereumExternals() void WasmDialect::addFunction( string _name, - size_t _params, - size_t _returns, + vector _params, + vector _returns, bool _movable, bool _literalArguments ) @@ -176,8 +192,9 @@ void WasmDialect::addFunction( YulString name{move(_name)}; BuiltinFunction& f = m_functions[name]; f.name = name; - f.parameters.resize(_params); - f.returns.resize(_returns); + f.parameters = std::move(_params); + yulAssert(_returns.size() <= 1, "The Wasm 1.0 specification only allows up to 1 return value."); + f.returns = std::move(_returns); f.sideEffects = _movable ? SideEffects{} : SideEffects::worst(); f.isMSize = false; f.literalArguments = _literalArguments; diff --git a/libyul/backends/wasm/WasmDialect.h b/libyul/backends/wasm/WasmDialect.h index 66b70864ee8a..cf1a76d9dd6c 100644 --- a/libyul/backends/wasm/WasmDialect.h +++ b/libyul/backends/wasm/WasmDialect.h @@ -56,7 +56,13 @@ struct WasmDialect: public Dialect private: void addEthereumExternals(); - void addFunction(std::string _name, size_t _params, size_t _returns, bool _movable = true, bool _literalArguments = false); + void addFunction( + std::string _name, + std::vector _params, + std::vector _returns, + bool _movable = true, + bool _literalArguments = false + ); std::map m_functions; }; From c41ef15a86767573022ecd6cbfaec6066b1bec23 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 30 Jan 2020 20:53:07 +0100 Subject: [PATCH 065/160] Fix types for polyfill. --- libyul/backends/wasm/EVMToEwasmTranslator.cpp | 393 +++++++++--------- 1 file changed, 198 insertions(+), 195 deletions(-) diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index 2090dd2f5112..19bd98da79f7 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -48,25 +48,26 @@ using namespace solidity::langutil; namespace { -static string const polyfill{R"({ -function or_bool(a, b, c, d) -> r { - r := i64.or(i64.or(a, b), i64.or(c, d)) +static string const polyfill{R"( +{ +function or_bool(a, b, c, d) -> r:i32 { + r := i32.eqz(i64.eqz(i64.or(i64.or(a, b), i64.or(c, d)))) } -function or_bool_320(a, b, c, d, e) -> r { - r := i64.or(or_bool(a, b, c, d), e) +function or_bool_320(a, b, c, d, e) -> r:i32 { + r := i32.or(or_bool(a, b, c, 0), or_bool(d, e, 0, 0)) } -function or_bool_512(a, b, c, d, e, f, g, h) -> r { - r := i64.or(or_bool(a, b, c, d), or_bool(e, f, g, h)) +function or_bool_512(a, b, c, d, e, f, g, h) -> r:i32 { + r := i32.or(or_bool(a, b, c, d), or_bool(e, f, g, h)) } // returns a + y + c plus carry value on 64 bit values. // c should be at most 1 function add_carry(x, y, c) -> r, r_c { let t := i64.add(x, y) r := i64.add(t, c) - r_c := i64.or( + r_c := i64.extend_i32_u(i32.or( i64.lt_u(t, x), i64.lt_u(r, t) - ) + )) } function add(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { let carry @@ -254,8 +255,8 @@ function div(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { let m3 := 0 let m4 := 1 - for {} 1 {} { - if i64.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, x1, x2, x3, x4)) { + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, x1, x2, x3, x4)) { break } y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) @@ -276,7 +277,7 @@ function div(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { function sdiv(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SDIV.wast - let sign := i64.shr_u(i64.xor(x1, y1), 63) + let sign:i32 := i32.wrap_i64(i64.shr_u(i64.xor(x1, y1), 63)) if i64.eqz(i64.clz(x1)) { x1, x2, x3, x4 := xor( @@ -320,8 +321,8 @@ function mod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { let m3 := 0 let m4 := 1 - for {} 1 {} { - if i64.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, r1, r2, r3, r4)) { + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, r1, r2, r3, r4)) { break } @@ -356,8 +357,8 @@ function mod320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { r4 := x4 r5 := x5 - for {} 1 {} { - if i64.or(i64.eqz(i64.clz(y1)), gte_320x320_64(y1, y2, y3, y4, y5, r1, r2, r3, r4, r5)) { + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_320x320_64(y1, y2, y3, y4, y5, r1, r2, r3, r4, r5)) { break } y1, y2, y3, y4, y5 := shl320_internal(1, y1, y2, y3, y4, y5) @@ -397,8 +398,8 @@ function mod512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) r7 := x7 r8 := x8 - for {} 1 {} { - if i64.or( + for {} true {} { + if i32.or( i64.eqz(i64.clz(y1)), gte_512x512_64(y1, y2, y3, y4, y5, y6, y7, y8, r1, r2, r3, r4, r5, r6, r7, r8) ) @@ -425,7 +426,7 @@ function smod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { let m3 := 0 let m4 := 1 - let sign := i64.shr_u(x1, 63) + let sign:i32 := i32.wrap_i64(i64.shr_u(x1, 63)) if i64.eqz(i64.clz(x1)) { x1, x2, x3, x4 := xor( @@ -456,7 +457,7 @@ function smod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { function exp(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { r4 := 1 for {} or_bool(y1, y2, y3, y4) {} { - if i64.and(y4, 1) { + if i32.wrap_i64(i64.and(y4, 1)) { r1, r2, r3, r4 := mul(r1, r2, r3, r4, x1, x2, x3, x4) } x1, x2, x3, x4 := mul(x1, x2, x3, x4, x1, x2, x3, x4) @@ -499,17 +500,17 @@ function not(x1, x2, x3, x4) -> r1, r2, r3, r4 { let mask := 0xffffffffffffffff r1, r2, r3, r4 := xor(x1, x2, x3, x4, mask, mask, mask, mask) } -function iszero(x1, x2, x3, x4) -> r1, r2, r3 ,r4 { - r4 := iszero256(x1, x2, x3, x4) +function iszero(x1, x2, x3, x4) -> r1, r2, r3, r4 { + r4 := i64.extend_i32_u(iszero256(x1, x2, x3, x4)) } -function iszero256(x1, x2, x3, x4) -> r { +function iszero256(x1, x2, x3, x4) -> r:i32 { r := i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4))) } -function iszero320(x1, x2, x3, x4, x5) -> r { +function iszero320(x1, x2, x3, x4, x5) -> r:i32 { r := i64.eqz(i64.or(i64.or(i64.or(x1, x2), i64.or(x3, x4)), x5)) } -function iszero512(x1, x2, x3, x4, x5, x6, x7, x8) -> r { - r := i64.and(iszero256(x1, x2, x3, x4), iszero256(x5, x6, x7, x8)) +function iszero512(x1, x2, x3, x4, x5, x6, x7, x8) -> r:i32 { + r := i32.and(iszero256(x1, x2, x3, x4), iszero256(x5, x6, x7, x8)) } function eq(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { if i64.eq(x1, y1) { @@ -524,107 +525,108 @@ function eq(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { } // returns 0 if a == b, -1 if a < b and 1 if a > b -function cmp(a, b) -> r { +function cmp(a, b) -> r:i32 { switch i64.lt_u(a, b) - case 1 { r := 0xffffffffffffffff } + case 1:i32 { r := 0xffffffff:i32 } default { r := i64.ne(a, b) } } -function lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z { +function lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z:i32 { switch cmp(x1, y1) - case 0 { + case 0:i32 { switch cmp(x2, y2) - case 0 { + case 0:i32 { switch cmp(x3, y3) - case 0 { + case 0:i32 { switch cmp(x4, y4) - case 0 { + case 0:i32 { z := i64.lt_u(x5, y5) } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } -function lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z { +function lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z:i32 { switch cmp(x1, y1) - case 0 { + case 0:i32 { switch cmp(x2, y2) - case 0 { + case 0:i32 { switch cmp(x3, y3) - case 0 { + case 0:i32 { switch cmp(x4, y4) - case 0 { + case 0:i32 { switch cmp(x5, y5) - case 0 { + case 0:i32 { switch cmp(x6, y6) - case 0 { + case 0:i32 { switch cmp(x7, y7) - case 0 { + case 0:i32 { z := i64.lt_u(x8, y8) } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } -} + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } +}/* )" // Split long string to make it compilable on msvc // https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?view=vs-2019 R"( -function lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z { +*/ +function lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z:i32 { switch cmp(x1, y1) - case 0 { + case 0:i32 { switch cmp(x2, y2) - case 0 { + case 0:i32 { switch cmp(x3, y3) - case 0 { + case 0:i32 { z := i64.lt_u(x4, y4) } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } - case 1 { z := 0 } - default { z := 1 } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } } function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - z4 := lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) + z4 := i64.extend_i32_u(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) } -function gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z { - z := i64.eqz(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) +function gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z:i32 { + z := i32.eqz(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) } -function gte_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z { - z := i64.eqz(lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5)) +function gte_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z:i32 { + z := i32.eqz(lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5)) } -function gte_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z { - z := i64.eqz(lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8)) +function gte_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z:i32 { + z := i32.eqz(lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8)) } function gt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { z1, z2, z3, z4 := lt(y1, y2, y3, y4, x1, x2, x3, x4) @@ -646,7 +648,7 @@ function shl_single(a, amount) -> x, y { } function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - if i64.and(i64.eqz(x1), i64.eqz(x2)) { + if i32.and(i64.eqz(x1), i64.eqz(x2)) { if i64.eqz(x3) { if i64.lt_u(x4, 256) { if i64.ge_u(x4, 128) { @@ -683,7 +685,7 @@ function shr_single(a, amount) -> x, y { } function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - if i64.and(i64.eqz(x1), i64.eqz(x2)) { + if i32.and(i64.eqz(x1), i64.eqz(x2)) { if i64.eqz(x3) { if i64.lt_u(x4, 256) { if i64.ge_u(x4, 128) { @@ -713,7 +715,7 @@ function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { } } function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - if i64.clz(y1) { + if i64.gt_u(i64.clz(y1), 0) { z1, z2, z3, z4 := shr(x1, x2, x3, x4, y1, y2, y3, y4) leave } @@ -773,10 +775,10 @@ function u256_to_i64(x1, x2, x3, x4) -> v { v := x4 } -function u256_to_i32(x1, x2, x3, x4) -> v { +function u256_to_i32(x1, x2, x3, x4) -> v:i32 { if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } if i64.ne(0, i64.shr_u(x4, 32)) { invalid() } - v := x4 + v := i32.wrap_i64(x4) } function u256_to_byte(x1, x2, x3, x4) -> v { @@ -785,14 +787,14 @@ function u256_to_byte(x1, x2, x3, x4) -> v { v := x4 } -function u256_to_i32ptr(x1, x2, x3, x4) -> v { +function u256_to_i32ptr(x1, x2, x3, x4) -> v:i32 { v := u256_to_i32(x1, x2, x3, x4) } -function to_internal_i32ptr(x1, x2, x3, x4) -> r { - let p := u256_to_i32ptr(x1, x2, x3, x4) - r := i64.add(p, 64) - if i64.lt_u(r, p) { invalid() } +function to_internal_i32ptr(x1, x2, x3, x4) -> r:i32 { + let p:i32 := u256_to_i32ptr(x1, x2, x3, x4) + r := i32.add(p, 64:i32) + if i32.lt_u(r, p) { invalid() } } function u256_to_address(x1, x2, x3, x4) -> r1, r2, r3 { @@ -809,13 +811,13 @@ function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { } function address() -> z1, z2, z3, z4 { - eth.getAddress(0) - z1, z2, z3, z4 := mload_internal(0) + eth.getAddress(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) } function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 { - mstore_address(0, x1, x2, x3, x4) - eth.getExternalBalance(12, 48) - z1, z2, z3, z4 := mload_internal(32) + mstore_address(0:i32, x1, x2, x3, x4) + eth.getExternalBalance(12:i32, 48:i32) + z1, z2, z3, z4 := mload_internal(32:i32) } function selfbalance() -> z1, z2, z3, z4 { // TODO: not part of current Ewasm spec @@ -826,23 +828,23 @@ function chainid() -> z1, z2, z3, z4 { unreachable() } function origin() -> z1, z2, z3, z4 { - eth.getTxOrigin(0) - z1, z2, z3, z4 := mload_internal(0) + eth.getTxOrigin(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) } function caller() -> z1, z2, z3, z4 { - eth.getCaller(0) - z1, z2, z3, z4 := mload_internal(0) + eth.getCaller(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) } function callvalue() -> z1, z2, z3, z4 { - eth.getCallValue(0) - z1, z2, z3, z4 := mload_internal(0) + eth.getCallValue(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) } function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - eth.callDataCopy(0, u256_to_i32(x1, x2, x3, x4), 32) - z1, z2, z3, z4 := mload_internal(0) + eth.callDataCopy(0:i32, u256_to_i32(x1, x2, x3, x4), 32:i32) + z1, z2, z3, z4 := mload_internal(0:i32) } function calldatasize() -> z1, z2, z3, z4 { - z4 := eth.getCallDataSize() + z4 := i64.extend_i32_u(eth.getCallDataSize()) } function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { eth.callDataCopy( @@ -854,7 +856,7 @@ function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { // Needed? function codesize() -> z1, z2, z3, z4 { - z4 := eth.getCodeSize() + z4 := i64.extend_i32_u(eth.getCodeSize()) } function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { eth.codeCopy( @@ -869,29 +871,29 @@ function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { } function gasprice() -> z1, z2, z3, z4 { - eth.getTxGasPrice(0) - z1, z2, z3, z4 := mload_internal(0) + eth.getTxGasPrice(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) } -function extcodesize_internal(x1, x2, x3, x4) -> r { - mstore_address(0, x1, x2, x3, x4) - r := eth.getExternalCodeSize(12) +function extcodesize_internal(x1, x2, x3, x4) -> r:i32 { + mstore_address(0:i32, x1, x2, x3, x4) + r := eth.getExternalCodeSize(12:i32) } function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 { - z4 := extcodesize_internal(x1, x2, x3, x4) + z4 := i64.extend_i32_u(extcodesize_internal(x1, x2, x3, x4)) } function extcodehash(x1, x2, x3, x4) -> z1, z2, z3, z4 { // TODO: not part of current Ewasm spec unreachable() } function extcodecopy(a1, a2, a3, a4, p1, p2, p3, p4, o1, o2, o3, o4, l1, l2, l3, l4) { - mstore_address(0, a1, a2, a3, a4) - let codeOffset := u256_to_i32(o1, o2, o3, o4) - let codeLength := u256_to_i32(l1, l2, l3, l4) - eth.externalCodeCopy(12, to_internal_i32ptr(p1, p2, p3, p4), codeOffset, codeLength) + mstore_address(0:i32, a1, a2, a3, a4) + let codeOffset:i32 := u256_to_i32(o1, o2, o3, o4) + let codeLength:i32 := u256_to_i32(l1, l2, l3, l4) + eth.externalCodeCopy(12:i32, to_internal_i32ptr(p1, p2, p3, p4), codeOffset, codeLength) } function returndatasize() -> z1, z2, z3, z4 { - z4 := eth.getReturnDataSize() + z4 := i64.extend_i32_u(eth.getReturnDataSize()) } function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { eth.returnDataCopy( @@ -902,14 +904,14 @@ function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { } function blockhash(x1, x2, x3, x4) -> z1, z2, z3, z4 { - let r := eth.getBlockHash(u256_to_i64(x1, x2, x3, x4), 0) - if i64.eqz(r) { - z1, z2, z3, z4 := mload_internal(0) + let r:i32 := eth.getBlockHash(u256_to_i64(x1, x2, x3, x4), 0:i32) + if i32.eqz(r) { + z1, z2, z3, z4 := mload_internal(0:i32) } } function coinbase() -> z1, z2, z3, z4 { - eth.getBlockCoinbase(0) - z1, z2, z3, z4 := mload_internal(0) + eth.getBlockCoinbase(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) } function timestamp() -> z1, z2, z3, z4 { z4 := eth.getBlockTimestamp() @@ -918,8 +920,8 @@ function number() -> z1, z2, z3, z4 { z4 := eth.getBlockNumber() } function difficulty() -> z1, z2, z3, z4 { - eth.getBlockDifficulty(0) - z1, z2, z3, z4 := mload_internal(0) + eth.getBlockDifficulty(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) } function gaslimit() -> z1, z2, z3, z4 { z4 := eth.getBlockGasLimit() @@ -947,56 +949,56 @@ function endian_swap(x) -> y { y := i64.or(hi, lo) } function save_temp_mem_32() -> t1, t2, t3, t4 { - t1 := i64.load(0) - t2 := i64.load(8) - t3 := i64.load(16) - t4 := i64.load(24) + t1 := i64.load(0:i32) + t2 := i64.load(8:i32) + t3 := i64.load(16:i32) + t4 := i64.load(24:i32) } function restore_temp_mem_32(t1, t2, t3, t4) { - i64.store(0, t1) - i64.store(8, t2) - i64.store(16, t3) - i64.store(24, t4) + i64.store(0:i32, t1) + i64.store(8:i32, t2) + i64.store(16:i32, t3) + i64.store(24:i32, t4) } function save_temp_mem_64() -> t1, t2, t3, t4, t5, t6, t7, t8 { - t1 := i64.load(0) - t2 := i64.load(8) - t3 := i64.load(16) - t4 := i64.load(24) - t5 := i64.load(32) - t6 := i64.load(40) - t7 := i64.load(48) - t8 := i64.load(54) + t1 := i64.load(0:i32) + t2 := i64.load(8:i32) + t3 := i64.load(16:i32) + t4 := i64.load(24:i32) + t5 := i64.load(32:i32) + t6 := i64.load(40:i32) + t7 := i64.load(48:i32) + t8 := i64.load(54:i32) } function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) { - i64.store(0, t1) - i64.store(8, t2) - i64.store(16, t3) - i64.store(24, t4) - i64.store(32, t5) - i64.store(40, t6) - i64.store(48, t7) - i64.store(54, t8) + i64.store(0:i32, t1) + i64.store(8:i32, t2) + i64.store(16:i32, t3) + i64.store(24:i32, t4) + i64.store(32:i32, t5) + i64.store(40:i32, t6) + i64.store(48:i32, t7) + i64.store(54:i32, t8) } function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 { z1, z2, z3, z4 := mload_internal(to_internal_i32ptr(x1, x2, x3, x4)) } -function mload_internal(pos) -> z1, z2, z3, z4 { +function mload_internal(pos:i32) -> z1, z2, z3, z4 { z1 := endian_swap(i64.load(pos)) - z2 := endian_swap(i64.load(i64.add(pos, 8))) - z3 := endian_swap(i64.load(i64.add(pos, 16))) - z4 := endian_swap(i64.load(i64.add(pos, 24))) + z2 := endian_swap(i64.load(i32.add(pos, 8:i32))) + z3 := endian_swap(i64.load(i32.add(pos, 16:i32))) + z4 := endian_swap(i64.load(i32.add(pos, 24:i32))) } function mstore(x1, x2, x3, x4, y1, y2, y3, y4) { mstore_internal(to_internal_i32ptr(x1, x2, x3, x4), y1, y2, y3, y4) } -function mstore_internal(pos, y1, y2, y3, y4) { +function mstore_internal(pos:i32, y1, y2, y3, y4) { i64.store(pos, endian_swap(y1)) - i64.store(i64.add(pos, 8), endian_swap(y2)) - i64.store(i64.add(pos, 16), endian_swap(y3)) - i64.store(i64.add(pos, 24), endian_swap(y4)) + i64.store(i32.add(pos, 8:i32), endian_swap(y2)) + i64.store(i32.add(pos, 16:i32), endian_swap(y3)) + i64.store(i32.add(pos, 24:i32), endian_swap(y4)) } -function mstore_address(pos, a1, a2, a3, a4) { +function mstore_address(pos:i32, a1, a2, a3, a4) { a1, a2, a3 := u256_to_address(a1, a2, a3, a4) mstore_internal(pos, 0, a1, a2, a3) } @@ -1010,15 +1012,15 @@ function msize() -> z1, z2, z3, z4 { unreachable() } function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - mstore_internal(0, x1, x2, x3, x4) - eth.storageLoad(0, 32) - z1, z2, z3, z4 := mload_internal(32) + mstore_internal(0:i32, x1, x2, x3, x4) + eth.storageLoad(0:i32, 32:i32) + z1, z2, z3, z4 := mload_internal(32:i32) } function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { - mstore_internal(0, x1, x2, x3, x4) - mstore_internal(32, y1, y2, y3, y4) - eth.storageStore(0, 32) + mstore_internal(0:i32, x1, x2, x3, x4) + mstore_internal(32:i32, y1, y2, y3, y4) + eth.storageStore(0:i32, 32:i32) } // Needed? @@ -1034,7 +1036,7 @@ function log0(p1, p2, p3, p4, s1, s2, s3, s4) { eth.log( to_internal_i32ptr(p1, p2, p3, p4), u256_to_i32(s1, s2, s3, s4), - 0, 0, 0, 0, 0 + 0:i32, 0:i32, 0:i32, 0:i32, 0:i32 ) } function log1( @@ -1044,9 +1046,9 @@ function log1( eth.log( to_internal_i32ptr(p1, p2, p3, p4), u256_to_i32(s1, s2, s3, s4), - 1, + 1:i32, to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - 0, 0, 0 + 0:i32, 0:i32, 0:i32 ) } function log2( @@ -1057,10 +1059,10 @@ function log2( eth.log( to_internal_i32ptr(p1, p2, p3, p4), u256_to_i32(s1, s2, s3, s4), - 2, + 2:i32, to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), - 0, 0 + 0:i32, 0:i32 ) } function log3( @@ -1072,11 +1074,11 @@ function log3( eth.log( to_internal_i32ptr(p1, p2, p3, p4), u256_to_i32(s1, s2, s3, s4), - 3, + 3:i32, to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), - 0 + 0:i32 ) } function log4( @@ -1089,7 +1091,7 @@ function log4( eth.log( to_internal_i32ptr(p1, p2, p3, p4), u256_to_i32(s1, s2, s3, s4), - 4, + 4:i32, to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), @@ -1103,11 +1105,11 @@ function create( z1, z2, z3, z4 ) -> a1, a2, a3, a4 { let v1, v2 := u256_to_u128(x1, x2, x3, x4) - mstore_internal(0, 0, 0, v1, v2) + mstore_internal(0:i32, 0, 0, v1, v2) - let r := eth.create(0, to_internal_i32ptr(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4), 32) - if i64.eqz(r) { - a1, a2, a3, a4 := mload_internal(32) + let r:i32 := eth.create(0:i32, to_internal_i32ptr(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4), 32:i32) + if i32.eqz(r) { + a1, a2, a3, a4 := mload_internal(32:i32) } } function call( @@ -1120,12 +1122,12 @@ function call( g1, g2, g3, g4 ) -> x1, x2, x3, x4 { let g := u256_to_i64(a1, a2, a3, a4) - mstore_address(0, b1, b2, b3, b4) + mstore_address(0:i32, b1, b2, b3, b4) let v1, v2 := u256_to_u128(c1, c2, c3, c4) - mstore_internal(32, 0, 0, v1, v2) + mstore_internal(32:i32, 0, 0, v1, v2) - x4 := eth.call(g, 12, 32, to_internal_i32ptr(d1, d2, d3, d4), u256_to_i32(e1, e2, e3, e4)) + x4 := i64.extend_i32_u(eth.call(g, 12:i32, 32:i32, to_internal_i32ptr(d1, d2, d3, d4), u256_to_i32(e1, e2, e3, e4))) } function callcode( a1, a2, a3, a4, @@ -1136,18 +1138,18 @@ function callcode( f1, f2, f3, f4, g1, g2, g3, g4 ) -> x1, x2, x3, x4 { - mstore_address(0, b1, b2, b3, b4) + mstore_address(0:i32, b1, b2, b3, b4) let v1, v2 := u256_to_u128(c1, c2, c3, c4) - mstore_internal(32, 0, 0, v1, v2) + mstore_internal(32:i32, 0, 0, v1, v2) - x4 := eth.callCode( + x4 := i64.extend_i32_u(eth.callCode( u256_to_i64(a1, a2, a3, a4), - 12, - 32, + 12:i32, + 32:i32, to_internal_i32ptr(d1, d2, d3, d4), u256_to_i32(e1, e2, e3, e4) - ) + )) } function delegatecall( a1, a2, a3, a4, @@ -1157,14 +1159,14 @@ function delegatecall( e1, e2, e3, e4, f1, f2, f3, f4 ) -> x1, x2, x3, x4 { - mstore_address(0, b1, b2, b3, b4) + mstore_address(0:i32, b1, b2, b3, b4) - x4 := eth.callDelegate( + x4 := i64.extend_i32_u(eth.callDelegate( u256_to_i64(a1, a2, a3, a4), - 12, + 12:i32, to_internal_i32ptr(c1, c2, c3, c4), u256_to_i32(d1, d2, d3, d4) - ) + )) } function staticcall( a1, a2, a3, a4, @@ -1174,14 +1176,14 @@ function staticcall( e1, e2, e3, e4, f1, f2, f3, f4 ) -> x1, x2, x3, x4 { - mstore_address(0, b1, b2, b3, b4) + mstore_address(0:i32, b1, b2, b3, b4) - x4 := eth.callStatic( + x4 := i64.extend_i32_u(eth.callStatic( u256_to_i64(a1, a2, a3, a4), - 12, + 12:i32, to_internal_i32ptr(c1, c2, c3, c4), u256_to_i32(d1, d2, d3, d4) - ) + )) } function create2( a1, a2, a3, a4, @@ -1193,9 +1195,9 @@ function create2( unreachable() } function selfdestruct(a1, a2, a3, a4) { - mstore_address(0, a1, a2, a3, a4) + mstore_address(0:i32, a1, a2, a3, a4) // In EVM, addresses are padded to 32 bytes, so discard the first 12. - eth.selfDestruct(12) + eth.selfDestruct(12:i32) } function return(x1, x2, x3, x4, y1, y2, y3, y4) { @@ -1213,7 +1215,8 @@ function revert(x1, x2, x3, x4, y1, y2, y3, y4) { function invalid() { unreachable() } -})"}; +} +)"}; } From a7624ffc458136348ffc988507f169f71f99b11f Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 3 Feb 2020 23:35:19 +0100 Subject: [PATCH 066/160] Implement i32 builtins for the interpreter. --- .../EwasmBuiltinInterpreter.cpp | 209 ++++++++++++------ .../yulInterpreter/EwasmBuiltinInterpreter.h | 18 +- 2 files changed, 158 insertions(+), 69 deletions(-) diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp index 7abd4311274b..d105c505db6f 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp @@ -95,80 +95,139 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a } else if (_fun == "drop"_yulstring) return {}; + else if (_fun == "i32.wrap_i64"_yulstring) + return arg.at(0) & uint32_t(-1); + else if (_fun == "i64.extend_i32_u"_yulstring) + // Return the same as above because everything is u256 anyway. + return arg.at(0) & uint32_t(-1); else if (_fun == "unreachable"_yulstring) { logTrace(evmasm::Instruction::INVALID, {}); throw ExplicitlyTerminated(); } - else if (_fun == "i64.add"_yulstring) + else if (_fun == "i64.store"_yulstring) + { + accessMemory(arg[0], 8); + writeMemoryWord(arg[0], arg[1]); + return 0; + } + else if (_fun == "i64.store8"_yulstring || _fun == "i32.store8"_yulstring) + { + accessMemory(arg[0], 1); + writeMemoryByte(arg[0], static_cast(arg[1] & 0xff)); + return 0; + } + else if (_fun == "i64.load"_yulstring) + { + accessMemory(arg[0], 8); + return readMemoryWord(arg[0]); + } + else if (_fun == "i32.store"_yulstring) + { + accessMemory(arg[0], 4); + writeMemoryHalfWord(arg[0], arg[1]); + return 0; + } + else if (_fun == "i32.load"_yulstring) + { + accessMemory(arg[0], 4); + return readMemoryHalfWord(arg[0]); + } + + + string prefix = _fun.str(); + string suffix; + auto dot = prefix.find("."); + if (dot != string::npos) + { + suffix = prefix.substr(dot + 1); + prefix.resize(dot); + } + + if (prefix == "i32") + { + vector halfWordArgs; + for (uint64_t a: arg) + halfWordArgs.push_back(uint32_t(a & uint32_t(-1))); + return evalWasmBuiltin(suffix, halfWordArgs); + } + else if (prefix == "i64") + return evalWasmBuiltin(suffix, arg); + else if (prefix == "eth") + return evalEthBuiltin(suffix, arg); + + yulAssert(false, "Unknown builtin: " + _fun.str() + " (or implementation did not return)"); + + return 0; +} + +template +u256 EwasmBuiltinInterpreter::evalWasmBuiltin(string const& _fun, vector const& _arguments) +{ + vector const& arg = _arguments; + + if (_fun == "add") return arg[0] + arg[1]; - else if (_fun == "i64.sub"_yulstring) + else if (_fun == "sub") return arg[0] - arg[1]; - else if (_fun == "i64.mul"_yulstring) + else if (_fun == "mul") return arg[0] * arg[1]; - else if (_fun == "i64.div_u"_yulstring) + else if (_fun == "div_u") { if (arg[1] == 0) throw ExplicitlyTerminated(); else return arg[0] / arg[1]; } - else if (_fun == "i64.rem_u"_yulstring) + else if (_fun == "rem_u") { if (arg[1] == 0) throw ExplicitlyTerminated(); else return arg[0] % arg[1]; } - else if (_fun == "i64.and"_yulstring) + else if (_fun == "and") return arg[0] & arg[1]; - else if (_fun == "i64.or"_yulstring) + else if (_fun == "or") return arg[0] | arg[1]; - else if (_fun == "i64.xor"_yulstring) + else if (_fun == "xor") return arg[0] ^ arg[1]; - else if (_fun == "i64.shl"_yulstring) + else if (_fun == "shl") return arg[0] << arg[1]; - else if (_fun == "i64.shr_u"_yulstring) + else if (_fun == "shr_u") return arg[0] >> arg[1]; - else if (_fun == "i64.eq"_yulstring) + else if (_fun == "eq") return arg[0] == arg[1] ? 1 : 0; - else if (_fun == "i64.ne"_yulstring) + else if (_fun == "ne") return arg[0] != arg[1] ? 1 : 0; - else if (_fun == "i64.eqz"_yulstring) + else if (_fun == "eqz") return arg[0] == 0 ? 1 : 0; - else if (_fun == "i64.clz"_yulstring) + else if (_fun == "clz") return clz(arg[0]); - else if (_fun == "i64.lt_u"_yulstring) + else if (_fun == "lt_u") return arg[0] < arg[1] ? 1 : 0; - else if (_fun == "i64.gt_u"_yulstring) + else if (_fun == "gt_u") return arg[0] > arg[1] ? 1 : 0; - else if (_fun == "i64.le_u"_yulstring) + else if (_fun == "le_u") return arg[0] <= arg[1] ? 1 : 0; - else if (_fun == "i64.ge_u"_yulstring) + else if (_fun == "ge_u") return arg[0] >= arg[1] ? 1 : 0; - else if (_fun == "i64.store"_yulstring) - { - accessMemory(arg[0], 8); - writeMemoryWord(arg[0], arg[1]); - return 0; - } - else if (_fun == "i64.store8"_yulstring) - { - accessMemory(arg[0], 1); - writeMemoryByte(arg[0], static_cast(arg[1] & 0xff)); - return 0; - } - else if (_fun == "i64.load"_yulstring) - { - accessMemory(arg[0], 8); - return readMemoryWord(arg[0]); - } - else if (_fun == "eth.getAddress"_yulstring) + + yulAssert(false, "Unknown builtin: " + _fun + " (or implementation did not return)"); + + return 0; +} + +u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector const& _arguments) +{ + vector const& arg = _arguments; + + if (_fun == "getAddress") { writeAddress(arg[0], m_state.address); return 0; } - else if (_fun == "eth.getExternalBalance"_yulstring) + else if (_fun == "getExternalBalance") { // TODO this does not read the address, but is consistent with // EVM interpreter implementation. @@ -176,7 +235,7 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a writeU128(arg[1], m_state.balance); return 0; } - else if (_fun == "eth.getBlockHash"_yulstring) + else if (_fun == "getBlockHash") { if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber) return 1; @@ -186,14 +245,14 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a return 0; } } - else if (_fun == "eth.call"_yulstring) + else if (_fun == "call") { // TODO read args from memory // TODO use readAddress to read address. logTrace(evmasm::Instruction::CALL, {}); return arg[0] & 1; } - else if (_fun == "eth.callDataCopy"_yulstring) + else if (_fun == "callDataCopy") { if (arg[1] + arg[2] < arg[1] || arg[1] + arg[2] > m_state.calldata.size()) throw ExplicitlyTerminated(); @@ -204,51 +263,51 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a ); return {}; } - else if (_fun == "eth.getCallDataSize"_yulstring) + else if (_fun == "getCallDataSize") return m_state.calldata.size(); - else if (_fun == "eth.callCode"_yulstring) + else if (_fun == "callCode") { // TODO read args from memory // TODO use readAddress to read address. logTrace(evmasm::Instruction::CALLCODE, {}); return arg[0] & 1; } - else if (_fun == "eth.callDelegate"_yulstring) + else if (_fun == "callDelegate") { // TODO read args from memory // TODO use readAddress to read address. logTrace(evmasm::Instruction::DELEGATECALL, {}); return arg[0] & 1; } - else if (_fun == "eth.callStatic"_yulstring) + else if (_fun == "callStatic") { // TODO read args from memory // TODO use readAddress to read address. logTrace(evmasm::Instruction::STATICCALL, {}); return arg[0] & 1; } - else if (_fun == "eth.storageStore"_yulstring) + else if (_fun == "storageStore") { m_state.storage[h256(readU256(arg[0]))] = readU256((arg[1])); return 0; } - else if (_fun == "eth.storageLoad"_yulstring) + else if (_fun == "storageLoad") { writeU256(arg[1], m_state.storage[h256(readU256(arg[0]))]); return 0; } - else if (_fun == "eth.getCaller"_yulstring) + else if (_fun == "getCaller") { // TODO should this only write 20 bytes? writeAddress(arg[0], m_state.caller); return 0; } - else if (_fun == "eth.getCallValue"_yulstring) + else if (_fun == "getCallValue") { writeU128(arg[0], m_state.callvalue); return 0; } - else if (_fun == "eth.codeCopy"_yulstring) + else if (_fun == "codeCopy") { if (accessMemory(arg[0], arg[2])) copyZeroExtended( @@ -257,26 +316,26 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a ); return 0; } - else if (_fun == "eth.getCodeSize"_yulstring) + else if (_fun == "getCodeSize") return m_state.code.size(); - else if (_fun == "eth.getBlockCoinbase"_yulstring) + else if (_fun == "getBlockCoinbase") { writeAddress(arg[0], m_state.coinbase); return 0; } - else if (_fun == "eth.create"_yulstring) + else if (_fun == "create") { // TODO access memory // TODO use writeAddress to store resulting address logTrace(evmasm::Instruction::CREATE, {}); return 0xcccccc + arg[1]; } - else if (_fun == "eth.getBlockDifficulty"_yulstring) + else if (_fun == "getBlockDifficulty") { writeU256(arg[0], m_state.difficulty); return 0; } - else if (_fun == "eth.externalCodeCopy"_yulstring) + else if (_fun == "externalCodeCopy") { // TODO use readAddress to read address. if (accessMemory(arg[1], arg[3])) @@ -287,19 +346,19 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a ); return 0; } - else if (_fun == "eth.getExternalCodeSize"_yulstring) + else if (_fun == "getExternalCodeSize") // Generate "random" code length. Make sure it fits the page size. return u256(keccak256(h256(readAddress(arg[0])))) & 0xfff; - else if (_fun == "eth.getGasLeft"_yulstring) + else if (_fun == "getGasLeft") return 0x99; - else if (_fun == "eth.getBlockGasLimit"_yulstring) + else if (_fun == "getBlockGasLimit") return uint64_t(m_state.gaslimit); - else if (_fun == "eth.getTxGasPrice"_yulstring) + else if (_fun == "getTxGasPrice") { writeU128(arg[0], m_state.gasprice); return 0; } - else if (_fun == "eth.log"_yulstring) + else if (_fun == "log") { uint64_t numberOfTopics = arg[2]; if (numberOfTopics > 4) @@ -307,14 +366,14 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a logTrace(evmasm::logInstruction(numberOfTopics), {}); return 0; } - else if (_fun == "eth.getBlockNumber"_yulstring) + else if (_fun == "getBlockNumber") return m_state.blockNumber; - else if (_fun == "eth.getTxOrigin"_yulstring) + else if (_fun == "getTxOrigin") { writeAddress(arg[0], m_state.origin); return 0; } - else if (_fun == "eth.finish"_yulstring) + else if (_fun == "finish") { bytes data; if (accessMemory(arg[0], arg[1])) @@ -322,7 +381,7 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a logTrace(evmasm::Instruction::RETURN, {}, data); throw ExplicitlyTerminated(); } - else if (_fun == "eth.revert"_yulstring) + else if (_fun == "revert") { bytes data; if (accessMemory(arg[0], arg[1])) @@ -330,9 +389,9 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a logTrace(evmasm::Instruction::REVERT, {}, data); throw ExplicitlyTerminated(); } - else if (_fun == "eth.getReturnDataSize"_yulstring) + else if (_fun == "getReturnDataSize") return m_state.returndata.size(); - else if (_fun == "eth.returnDataCopy"_yulstring) + else if (_fun == "returnDataCopy") { if (arg[1] + arg[2] < arg[1] || arg[1] + arg[2] > m_state.returndata.size()) throw ExplicitlyTerminated(); @@ -343,16 +402,16 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a ); return {}; } - else if (_fun == "eth.selfDestruct"_yulstring) + else if (_fun == "selfDestruct") { // TODO use readAddress to read address. logTrace(evmasm::Instruction::SELFDESTRUCT, {}); throw ExplicitlyTerminated(); } - else if (_fun == "eth.getBlockTimestamp"_yulstring) + else if (_fun == "getBlockTimestamp") return m_state.timestamp; - yulAssert(false, "Unknown builtin: " + _fun.str() + " (or implementation did not return)"); + yulAssert(false, "Unknown builtin: " + _fun + " (or implementation did not return)"); return 0; } @@ -388,12 +447,26 @@ uint64_t EwasmBuiltinInterpreter::readMemoryWord(uint64_t _offset) return r; } +uint32_t EwasmBuiltinInterpreter::readMemoryHalfWord(uint64_t _offset) +{ + uint32_t r = 0; + for (size_t i = 0; i < 4; i++) + r |= uint64_t(m_state.memory[_offset + i]) << (i * 8); + return r; +} + void EwasmBuiltinInterpreter::writeMemoryWord(uint64_t _offset, uint64_t _value) { for (size_t i = 0; i < 8; i++) m_state.memory[_offset + i] = uint8_t((_value >> (i * 8)) & 0xff); } +void EwasmBuiltinInterpreter::writeMemoryHalfWord(uint64_t _offset, uint32_t _value) +{ + for (size_t i = 0; i < 4; i++) + m_state.memory[_offset + i] = uint8_t((_value >> (i * 8)) & 0xff); +} + void EwasmBuiltinInterpreter::writeMemoryByte(uint64_t _offset, uint8_t _value) { m_state.memory[_offset] = _value; diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h index c6819743c0a9..62b6bc0245ab 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h @@ -71,6 +71,16 @@ class EwasmBuiltinInterpreter u256 evalBuiltin(YulString _fun, std::vector const& _arguments); private: + template + u256 evalWasmBuiltin( + std::string const& _fun, + std::vector const& _arguments + ); + u256 evalEthBuiltin( + std::string const& _fun, + std::vector const& _arguments + ); + /// Checks if the memory access is not too large for the interpreter and adjusts /// msize accordingly. /// @returns false if the amount of bytes read is lager than 0xffff @@ -78,12 +88,18 @@ class EwasmBuiltinInterpreter /// @returns the memory contents at the provided address. /// Does not adjust msize, use @a accessMemory for that bytes readMemory(uint64_t _offset, uint64_t _size = 32); - /// @returns the memory contents at the provided address (little-endian). + /// @returns the memory contents (8 bytes) at the provided address (little-endian). /// Does not adjust msize, use @a accessMemory for that uint64_t readMemoryWord(uint64_t _offset); + /// @returns the memory contents (4 bytes) at the provided address (little-endian). + /// Does not adjust msize, use @a accessMemory for that + uint32_t readMemoryHalfWord(uint64_t _offset); /// Writes a word to memory (little-endian) /// Does not adjust msize, use @a accessMemory for that void writeMemoryWord(uint64_t _offset, uint64_t _value); + /// Writes a 4-byte value to memory (little-endian) + /// Does not adjust msize, use @a accessMemory for that + void writeMemoryHalfWord(uint64_t _offset, uint32_t _value); /// Writes a byte to memory /// Does not adjust msize, use @a accessMemory for that void writeMemoryByte(uint64_t _offset, uint8_t _value); From 48933df18d49f351558b858558a2049683c2b23d Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Feb 2020 18:54:35 +0100 Subject: [PATCH 067/160] Add some missing wasm functions. --- libyul/backends/wasm/BinaryTransform.cpp | 6 +++++- libyul/backends/wasm/WasmDialect.cpp | 1 + test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libyul/backends/wasm/BinaryTransform.cpp b/libyul/backends/wasm/BinaryTransform.cpp index 8abd2b080136..ad2628695624 100644 --- a/libyul/backends/wasm/BinaryTransform.cpp +++ b/libyul/backends/wasm/BinaryTransform.cpp @@ -311,7 +311,11 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call) bytes args = visit(_call.arguments); if (_call.functionName == "unreachable") - return toBytes(Opcode::Unreachable); + return toBytes(Opcode::Unreachable); + else if (_call.functionName == "nop") + return toBytes(Opcode::Nop); + else if (_call.functionName == "drop") + return toBytes(Opcode::Drop); else { yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found"); diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 52fbafe3aa6d..947326306d96 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -94,6 +94,7 @@ WasmDialect::WasmDialect() // We could introduce "i32.drop". addFunction("drop", {i64}, {}); + addFunction("nop", {}, {}); addFunction("unreachable", {}, {}, false); m_functions["unreachable"_yulstring].sideEffects.invalidatesStorage = false; m_functions["unreachable"_yulstring].sideEffects.invalidatesMemory = false; diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp index d105c505db6f..e4c190e9cdca 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp @@ -93,7 +93,7 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a ); return 0; } - else if (_fun == "drop"_yulstring) + else if (_fun == "drop"_yulstring || _fun == "nop"_yulstring) return {}; else if (_fun == "i32.wrap_i64"_yulstring) return arg.at(0) & uint32_t(-1); From 527c011c2e839c51f3de14e1d6f74407afd10da0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Feb 2020 22:43:40 +0100 Subject: [PATCH 068/160] Update tests. --- test/cmdlineTests/evm_to_wasm/output | 26 +++++++++---------- .../standard_eWasm_requested/output.json | 26 ++++++++++--------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index 1c31f9488f2e..4e83eea9d162 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -15,9 +15,9 @@ object "object" { function main() { let _1 := 0 - mstore_internal(_1, _1, _1, _1, _1) - mstore_internal(32, _1, _1, _1, 1) - eth.storageStore(_1, 32) + mstore_internal(0:i32, _1, _1, _1, _1) + mstore_internal(32:i32, _1, _1, _1, 1) + eth.storageStore(0:i32, 32:i32) } function endian_swap_16(x) -> y { @@ -33,19 +33,19 @@ object "object" { let hi := i64.shl(endian_swap_32(x), 32) y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) } - function mstore_internal(pos, y1, y2, y3, y4) + function mstore_internal(pos:i32, y1, y2, y3, y4) { i64.store(pos, endian_swap(y1)) - i64.store(i64.add(pos, 8), endian_swap(y2)) - i64.store(i64.add(pos, 16), endian_swap(y3)) - i64.store(i64.add(pos, 24), endian_swap(y4)) + i64.store(i32.add(pos, 8:i32), endian_swap(y2)) + i64.store(i32.add(pos, 16:i32), endian_swap(y3)) + i64.store(i32.add(pos, 24:i32), endian_swap(y4)) } } } Binary representation: -0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010ab501052801017e420021002000200020002000200010054220200020002000420110052000a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3501007e2000a720011004370300200042087ca720021004370300200042107ca720031004370300200042187ca7200410043703000b +0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010abe01052801017e420021004200200020002000200010054220200020002000420110054200a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3e01007e2000a7200110043703002000a74208a76aada7200210043703002000a74210a76aada7200310043703002000a74218a76aada7200410043703000b Text representation: (module @@ -56,9 +56,9 @@ Text representation: (func $main (local $_1 i64) (local.set $_1 (i64.const 0)) - (call $mstore_internal (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) + (call $mstore_internal (i64.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $mstore_internal (i64.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1)) - (call $eth.storageStore (i32.wrap_i64 (local.get $_1)) (i32.wrap_i64 (i64.const 32))) + (call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32))) ) (func $endian_swap_16 @@ -96,9 +96,9 @@ Text representation: (param $y3 i64) (param $y4 i64) (i64.store (i32.wrap_i64 (local.get $pos)) (call $endian_swap (local.get $y1))) - (i64.store (i32.wrap_i64 (i64.add (local.get $pos) (i64.const 8))) (call $endian_swap (local.get $y2))) - (i64.store (i32.wrap_i64 (i64.add (local.get $pos) (i64.const 16))) (call $endian_swap (local.get $y3))) - (i64.store (i32.wrap_i64 (i64.add (local.get $pos) (i64.const 24))) (call $endian_swap (local.get $y4))) + (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 8))))) (call $endian_swap (local.get $y2))) + (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 16))))) (call $endian_swap (local.get $y3))) + (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 24))))) (call $endian_swap (local.get $y4))) ) ) diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index d273d0b859c3..0d796bce5bae 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -10,21 +10,23 @@ (local $p i64) (local $r i64) (local $hi i64) - (local $y i64) (local $hi_1 i64) + (local $y i64) + (local $hi_2 i64) (local $_2 i64) (local.set $_1 (i64.const 0)) (local.set $p (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64))) - (local.set $r (i64.add (local.get $p) (i64.const 64))) - (if (i64.ne (i64.extend_i32_u (i64.lt_u (local.get $r) (local.get $p))) (i64.const 0)) (then + (local.set $r (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $p)) (i32.wrap_i64 (i64.const 64))))) + (if (i64.ne (i64.extend_i32_u (i32.lt_u (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (local.get $p)))) (i64.const 0)) (then (unreachable))) - (local.set $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (local.get $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) + (local.set $hi (i64.shl (call $endian_swap_16 (local.get $_1)) (i64.const 16))) + (local.set $hi_1 (i64.shl (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) + (local.set $y (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) (i64.store (i32.wrap_i64 (local.get $r)) (local.get $y)) - (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 8))) (local.get $y)) - (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 16))) (local.get $y)) - (local.set $hi_1 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) - (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 24))) (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) + (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 8))))) (local.get $y)) + (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 16))))) (local.get $y)) + (local.set $hi_2 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) + (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 24))))) (i64.or (local.get $hi_2) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) (local.set $_2 (datasize \"C_2_deployed\")) (call $eth.codeCopy (i32.wrap_i64 (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\"))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_2)))) (call $eth.finish (i32.wrap_i64 (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_2)))) @@ -41,7 +43,7 @@ (unreachable))) (if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then (unreachable))) - (local.set $v (local.get $x4)) + (local.set $v (i64.extend_i32_u (i32.wrap_i64 (local.get $x4)))) (local.get $v) ) @@ -54,8 +56,8 @@ (local $r i64) (local $p i64) (local.set $p (call $u256_to_i32 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) - (local.set $r (i64.add (local.get $p) (i64.const 64))) - (if (i64.ne (i64.extend_i32_u (i64.lt_u (local.get $r) (local.get $p))) (i64.const 0)) (then + (local.set $r (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $p)) (i32.wrap_i64 (i64.const 64))))) + (if (i64.ne (i64.extend_i32_u (i32.lt_u (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (local.get $p)))) (i64.const 0)) (then (unreachable))) (local.get $r) ) From 0b9c842656c644c209280e5f570f94dee77a1606 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 5 Feb 2020 12:14:14 +0100 Subject: [PATCH 069/160] Fix abstract without contract. --- Changelog.md | 1 + libsolidity/parsing/Parser.cpp | 3 ++- test/libsolidity/syntaxTests/abstract/abstract_only.sol | 4 ++++ .../syntaxTests/abstract/abstract_without_contract.sol | 3 +++ 4 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/abstract/abstract_only.sol create mode 100644 test/libsolidity/syntaxTests/abstract/abstract_without_contract.sol diff --git a/Changelog.md b/Changelog.md index c316e891f8ff..a53c6a858043 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: Bugfixes: + * Parser: Fix an internal error for ``abstract`` without ``contract``. diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 6072ef6bb4bf..9f165c75c9c8 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -278,7 +278,8 @@ std::pair Parser::parseContractKind() kind = ContractKind::Library; break; default: - solAssert(false, "Invalid contract kind."); + parserError("Expected keyword \"contract\", \"interface\" or \"library\"."); + return std::make_pair(ContractKind::Contract, abstract); } m_scanner->next(); return std::make_pair(kind, abstract); diff --git a/test/libsolidity/syntaxTests/abstract/abstract_only.sol b/test/libsolidity/syntaxTests/abstract/abstract_only.sol new file mode 100644 index 000000000000..1445324a2875 --- /dev/null +++ b/test/libsolidity/syntaxTests/abstract/abstract_only.sol @@ -0,0 +1,4 @@ +abstract +// ---- +// ParserError: (9-9): Expected keyword "contract", "interface" or "library". +// ParserError: (9-9): Expected identifier but got end of source diff --git a/test/libsolidity/syntaxTests/abstract/abstract_without_contract.sol b/test/libsolidity/syntaxTests/abstract/abstract_without_contract.sol new file mode 100644 index 000000000000..5e5e25bbe12f --- /dev/null +++ b/test/libsolidity/syntaxTests/abstract/abstract_without_contract.sol @@ -0,0 +1,3 @@ +abstract A { } +// ---- +// ParserError: (9-10): Expected keyword "contract", "interface" or "library". From ef07fc30660fb1fe6b1e51836a0381e52e9f03c5 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 4 Feb 2020 19:40:56 +0100 Subject: [PATCH 070/160] Fix formatting --- test/libyul/FunctionSideEffects.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index 257faab22b89..2d74d5501a97 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -68,7 +68,8 @@ FunctionSideEffects::FunctionSideEffects(string const& _filename) file.exceptions(ios::badbit); m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file);} + m_expectation = parseSimpleExpectations(file); +} TestCase::TestResult FunctionSideEffects::run(ostream& _stream, string const& _linePrefix, bool _formatted) { From fc10e701fc21fcf5c8b0fc0c6d7c9a3c5095a0ce Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 4 Feb 2020 19:40:32 +0100 Subject: [PATCH 071/160] Implement yul syntax tests --- test/CMakeLists.txt | 4 + test/CommonSyntaxTest.cpp | 252 ++++++++++++++++++ test/CommonSyntaxTest.h | 86 ++++++ test/InteractiveTests.h | 2 + test/libsolidity/SyntaxTest.cpp | 205 +------------- test/libsolidity/SyntaxTest.h | 46 +--- test/libyul/SyntaxTest.cpp | 139 ++++++++++ test/libyul/SyntaxTest.h | 53 ++++ .../yulSyntaxTests/simple_functions.yul | 8 + test/tools/CMakeLists.txt | 2 + 10 files changed, 551 insertions(+), 246 deletions(-) create mode 100644 test/CommonSyntaxTest.cpp create mode 100644 test/CommonSyntaxTest.h create mode 100644 test/libyul/SyntaxTest.cpp create mode 100644 test/libyul/SyntaxTest.h create mode 100644 test/libyul/yulSyntaxTests/simple_functions.yul diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ce1c127397d5..e7a9d903fe64 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,6 +2,8 @@ set(sources boostTest.cpp Common.cpp Common.h + CommonSyntaxTest.cpp + CommonSyntaxTest.h EVMHost.cpp EVMHost.h ExecutionFramework.cpp @@ -127,6 +129,8 @@ set(libyul_sources libyul/ObjectParser.cpp libyul/Parser.cpp libyul/StackReuseCodegen.cpp + libyul/SyntaxTest.h + libyul/SyntaxTest.cpp libyul/YulInterpreterTest.cpp libyul/YulInterpreterTest.h libyul/YulOptimizerTest.cpp diff --git a/test/CommonSyntaxTest.cpp b/test/CommonSyntaxTest.cpp new file mode 100644 index 000000000000..6d72ce010ff3 --- /dev/null +++ b/test/CommonSyntaxTest.cpp @@ -0,0 +1,252 @@ +/* + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::util; +using namespace solidity::util::formatting; +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::frontend::test; +using namespace solidity::test; +using namespace boost::unit_test; +namespace fs = boost::filesystem; + +namespace +{ + +int parseUnsignedInteger(string::iterator& _it, string::iterator _end) +{ + if (_it == _end || !isdigit(*_it)) + throw runtime_error("Invalid test expectation. Source location expected."); + int result = 0; + while (_it != _end && isdigit(*_it)) + { + result *= 10; + result += *_it - '0'; + ++_it; + } + return result; +} + +} + +CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) +{ + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); + file.exceptions(ios::badbit); + + m_sources = parseSourcesAndSettings(file); + + m_expectations = parseExpectations(file); +} + +TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) +{ + parseAndAnalyze(); + + return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; +} + +bool CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted) +{ + if (m_expectations != m_errorList) + { + string nextIndentLevel = _linePrefix + " "; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; + printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); + return false; + } + return true; +} + +void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const +{ + if (m_sources.empty()) + return; + + bool outputSourceNames = true; + if (m_sources.size() == 1 && m_sources.begin()->first.empty()) + outputSourceNames = false; + + if (_formatted) + for (auto const& [name, source]: m_sources) + { + if (source.empty()) + continue; + + if (outputSourceNames) + _stream << _linePrefix << formatting::CYAN << "==== Source: " << name << " ====" << formatting::RESET << endl; + vector sourceFormatting(source.length(), formatting::RESET); + for (auto const& error: m_errorList) + if (error.sourceName == name && error.locationStart >= 0 && error.locationEnd >= 0) + { + assert(static_cast(error.locationStart) <= source.length()); + assert(static_cast(error.locationEnd) <= source.length()); + bool isWarning = error.type == "Warning"; + for (int i = error.locationStart; i < error.locationEnd; i++) + if (isWarning) + { + if (sourceFormatting[i] == formatting::RESET) + sourceFormatting[i] = formatting::ORANGE_BACKGROUND_256; + } + else + sourceFormatting[i] = formatting::RED_BACKGROUND; + } + + _stream << _linePrefix << sourceFormatting.front() << source.front(); + for (size_t i = 1; i < source.length(); i++) + { + if (sourceFormatting[i] != sourceFormatting[i - 1]) + _stream << sourceFormatting[i]; + if (source[i] != '\n') + _stream << source[i]; + else + { + _stream << formatting::RESET << endl; + if (i + 1 < source.length()) + _stream << _linePrefix << sourceFormatting[i]; + } + } + _stream << formatting::RESET; + } + else + for (auto const& [name, source]: m_sources) + { + if (outputSourceNames) + _stream << _linePrefix << "==== Source: " + name << " ====" << endl; + stringstream stream(source); + string line; + while (getline(stream, line)) + _stream << _linePrefix << line << endl; + } +} + +void CommonSyntaxTest::printErrorList( + ostream& _stream, + vector const& _errorList, + string const& _linePrefix, + bool _formatted +) +{ + if (_errorList.empty()) + AnsiColorized(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; + else + for (auto const& error: _errorList) + { + { + AnsiColorized scope(_stream, _formatted, {BOLD, (error.type == "Warning") ? YELLOW : RED}); + _stream << _linePrefix; + _stream << error.type << ": "; + } + if (!error.sourceName.empty() || error.locationStart >= 0 || error.locationEnd >= 0) + { + _stream << "("; + if (!error.sourceName.empty()) + _stream << error.sourceName << ":"; + if (error.locationStart >= 0) + _stream << error.locationStart; + _stream << "-"; + if (error.locationEnd >= 0) + _stream << error.locationEnd; + _stream << "): "; + } + _stream << error.message << endl; + } +} + +string CommonSyntaxTest::errorMessage(Exception const& _e) +{ + if (_e.comment() && !_e.comment()->empty()) + return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); + else + return "NONE"; +} + +vector CommonSyntaxTest::parseExpectations(istream& _stream) +{ + vector expectations; + string line; + while (getline(_stream, line)) + { + auto it = line.begin(); + + skipSlashes(it, line.end()); + skipWhitespace(it, line.end()); + + if (it == line.end()) continue; + + auto typeBegin = it; + while (it != line.end() && *it != ':') + ++it; + string errorType(typeBegin, it); + + // skip colon + if (it != line.end()) it++; + + skipWhitespace(it, line.end()); + + int locationStart = -1; + int locationEnd = -1; + std::string sourceName; + + if (it != line.end() && *it == '(') + { + ++it; + if (it != line.end() && !isdigit(*it)) + { + auto sourceNameStart = it; + while (it != line.end() && *it != ':') + ++it; + sourceName = std::string(sourceNameStart, it); + expect(it, line.end(), ':'); + } + locationStart = parseUnsignedInteger(it, line.end()); + expect(it, line.end(), '-'); + locationEnd = parseUnsignedInteger(it, line.end()); + expect(it, line.end(), ')'); + expect(it, line.end(), ':'); + } + + skipWhitespace(it, line.end()); + + string errorMessage(it, line.end()); + expectations.emplace_back(SyntaxTestError{ + move(errorType), + move(errorMessage), + move(sourceName), + locationStart, + locationEnd + }); + } + return expectations; +} diff --git a/test/CommonSyntaxTest.h b/test/CommonSyntaxTest.h new file mode 100644 index 000000000000..22acd5413a14 --- /dev/null +++ b/test/CommonSyntaxTest.h @@ -0,0 +1,86 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace solidity::test +{ + +struct SyntaxTestError +{ + std::string type; + std::string message; + std::string sourceName; + int locationStart = -1; + int locationEnd = -1; + bool operator==(SyntaxTestError const& _rhs) const + { + return type == _rhs.type && + message == _rhs.message && + sourceName == _rhs.sourceName && + locationStart == _rhs.locationStart && + locationEnd == _rhs.locationEnd; + } +}; + + +class CommonSyntaxTest: public frontend::test::EVMVersionRestrictedTestCase +{ +public: + CommonSyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion); + + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; + + void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool _formatted = false) const override; + void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override + { + if (!m_errorList.empty()) + printErrorList(_stream, m_errorList, _linePrefix, false); + } + + static std::string errorMessage(util::Exception const& _e); +protected: + virtual void parseAndAnalyze() = 0; + + static void printErrorList( + std::ostream& _stream, + std::vector const& _errors, + std::string const& _linePrefix, + bool _formatted = false + ); + + virtual bool printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false); + + static std::vector parseExpectations(std::istream& _stream); + + std::map m_sources; + std::vector m_expectations; + std::vector m_errorList; + langutil::EVMVersion const m_evmVersion; +}; + +} diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 8069ae522b06..409d8607daf4 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -57,6 +58,7 @@ Testsuite const g_interactiveTestsuites[] = { {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, {"Function Side Effects","libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, + {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, {"Error Recovery", "libsolidity", "errorRecoveryTests", false, false, &SyntaxTest::createErrorRecovery}, {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index b28d6c9da576..1938cfe86a33 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -35,34 +35,8 @@ using namespace solidity::frontend::test; using namespace boost::unit_test; namespace fs = boost::filesystem; -namespace +SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): CommonSyntaxTest(_filename, _evmVersion) { - -int parseUnsignedInteger(string::iterator& _it, string::iterator _end) -{ - if (_it == _end || !isdigit(*_it)) - throw runtime_error("Invalid test expectation. Source location expected."); - int result = 0; - while (_it != _end && isdigit(*_it)) - { - result *= 10; - result += *_it - '0'; - ++_it; - } - return result; -} - -} - -SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): m_evmVersion(_evmVersion) -{ - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_sources = parseSourcesAndSettings(file); - if (m_settings.count("optimize-yul")) { if (m_settings["optimize-yul"] == "true") @@ -77,7 +51,6 @@ SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion m_optimiseYul = false; } } - m_expectations = parseExpectations(file); m_parserErrorRecovery = _parserErrorRecovery; } @@ -90,83 +63,6 @@ TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; } -bool SyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted) -{ - if (m_expectations != m_errorList) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); - return false; - } - return true; -} - -void SyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const -{ - - if (m_sources.empty()) - return; - - bool outputSourceNames = true; - if (m_sources.size() == 1 && m_sources.begin()->first.empty()) - outputSourceNames = false; - - if (_formatted) - { - for (auto const& [name, source]: m_sources) - { - if (outputSourceNames) - _stream << _linePrefix << formatting::CYAN << "==== Source: " << name << " ====" << formatting::RESET << endl; - vector sourceFormatting(source.length(), formatting::RESET); - for (auto const& error: m_errorList) - if (error.sourceName == name && error.locationStart >= 0 && error.locationEnd >= 0) - { - assert(static_cast(error.locationStart) <= source.length()); - assert(static_cast(error.locationEnd) <= source.length()); - bool isWarning = error.type == "Warning"; - for (int i = error.locationStart; i < error.locationEnd; i++) - if (isWarning) - { - if (sourceFormatting[i] == formatting::RESET) - sourceFormatting[i] = formatting::ORANGE_BACKGROUND_256; - } - else - sourceFormatting[i] = formatting::RED_BACKGROUND; - } - - _stream << _linePrefix << sourceFormatting.front() << source.front(); - for (size_t i = 1; i < source.length(); i++) - { - if (sourceFormatting[i] != sourceFormatting[i - 1]) - _stream << sourceFormatting[i]; - if (source[i] != '\n') - _stream << source[i]; - else - { - _stream << formatting::RESET << endl; - if (i + 1 < source.length()) - _stream << _linePrefix << sourceFormatting[i]; - } - } - _stream << formatting::RESET; - } - - } - else - for (auto const& [name, source]: m_sources) - { - if (outputSourceNames) - _stream << _linePrefix << "==== Source: " + name << " ====" << endl; - stringstream stream(source); - string line; - while (getline(stream, line)) - _stream << _linePrefix << line << endl; - } -} - void SyntaxTest::setupCompiler() { string const versionPragma = "pragma solidity >=0.0;\n"; @@ -231,102 +127,3 @@ void SyntaxTest::filterObtainedErrors() } } -void SyntaxTest::printErrorList( - ostream& _stream, - vector const& _errorList, - string const& _linePrefix, - bool _formatted -) -{ - if (_errorList.empty()) - AnsiColorized(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; - else - for (auto const& error: _errorList) - { - { - AnsiColorized scope(_stream, _formatted, {BOLD, (error.type == "Warning") ? YELLOW : RED}); - _stream << _linePrefix; - _stream << error.type << ": "; - } - if (!error.sourceName.empty() || error.locationStart >= 0 || error.locationEnd >= 0) - { - _stream << "("; - if (!error.sourceName.empty()) - _stream << error.sourceName << ":"; - if (error.locationStart >= 0) - _stream << error.locationStart; - _stream << "-"; - if (error.locationEnd >= 0) - _stream << error.locationEnd; - _stream << "): "; - } - _stream << error.message << endl; - } -} - -string SyntaxTest::errorMessage(Exception const& _e) -{ - if (_e.comment() && !_e.comment()->empty()) - return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); - else - return "NONE"; -} - -vector SyntaxTest::parseExpectations(istream& _stream) -{ - vector expectations; - string line; - while (getline(_stream, line)) - { - auto it = line.begin(); - - skipSlashes(it, line.end()); - skipWhitespace(it, line.end()); - - if (it == line.end()) continue; - - auto typeBegin = it; - while (it != line.end() && *it != ':') - ++it; - string errorType(typeBegin, it); - - // skip colon - if (it != line.end()) it++; - - skipWhitespace(it, line.end()); - - int locationStart = -1; - int locationEnd = -1; - std::string sourceName; - - if (it != line.end() && *it == '(') - { - ++it; - if (it != line.end() && !isdigit(*it)) - { - auto sourceNameStart = it; - while (it != line.end() && *it != ':') - ++it; - sourceName = std::string(sourceNameStart, it); - expect(it, line.end(), ':'); - } - locationStart = parseUnsignedInteger(it, line.end()); - expect(it, line.end(), '-'); - locationEnd = parseUnsignedInteger(it, line.end()); - expect(it, line.end(), ')'); - expect(it, line.end(), ':'); - } - - skipWhitespace(it, line.end()); - - string errorMessage(it, line.end()); - expectations.emplace_back(SyntaxTestError{ - move(errorType), - move(errorMessage), - move(sourceName), - locationStart, - locationEnd - }); - } - return expectations; -} diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 0a1752f10f4d..dbc5d68b3440 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -30,25 +31,9 @@ namespace solidity::frontend::test { -struct SyntaxTestError -{ - std::string type; - std::string message; - std::string sourceName; - int locationStart; - int locationEnd; - bool operator==(SyntaxTestError const& _rhs) const - { - return type == _rhs.type && - message == _rhs.message && - sourceName == _rhs.sourceName && - locationStart == _rhs.locationStart && - locationEnd == _rhs.locationEnd; - } -}; - +using solidity::test::SyntaxTestError; -class SyntaxTest: public AnalysisFramework, public EVMVersionRestrictedTestCase +class SyntaxTest: public AnalysisFramework, public solidity::test::CommonSyntaxTest { public: static std::unique_ptr create(Config const& _config) @@ -63,35 +48,12 @@ class SyntaxTest: public AnalysisFramework, public EVMVersionRestrictedTestCase TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; - void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool _formatted = false) const override; - void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override - { - if (!m_errorList.empty()) - printErrorList(_stream, m_errorList, _linePrefix, false); - } - - static std::string errorMessage(util::Exception const& _e); protected: void setupCompiler(); - void parseAndAnalyze(); + void parseAndAnalyze() override; void filterObtainedErrors(); - static void printErrorList( - std::ostream& _stream, - std::vector const& _errors, - std::string const& _linePrefix, - bool _formatted = false - ); - - virtual bool printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false); - - static std::vector parseExpectations(std::istream& _stream); - - std::map m_sources; - std::vector m_expectations; - std::vector m_errorList; bool m_optimiseYul = true; - langutil::EVMVersion const m_evmVersion; bool m_parserErrorRecovery = false; }; diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp new file mode 100644 index 000000000000..330aba2ee241 --- /dev/null +++ b/test/libyul/SyntaxTest.cpp @@ -0,0 +1,139 @@ +/* + 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 . +*/ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul::test; + +namespace +{ +std::map const validDialects = { + { + "evm", + [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& + { return yul::EVMDialect::strictAssemblyForEVM(_evmVersion); } + }, + { + "evmTyped", + [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& + { return yul::EVMDialectTyped::strictAssemblyForEVM(_evmVersion); } + }, + { + "yul", + [](langutil::EVMVersion) -> yul::Dialect const& + { return yul::Dialect::yulDeprecated(); } + }, + { + "ewasm", + [](langutil::EVMVersion) -> yul::Dialect const& + { return yul::WasmDialect::instance(); } + } +}; + +vector validDialectNames() +{ + vector names{size(validDialects), ""}; + transform(begin(validDialects), end(validDialects), names.begin(), [](auto const& dialect) { return dialect.first; }); + + return names; +} +} + +void SyntaxTest::parseAndAnalyze() +{ + string dialectName = m_validatedSettings.count("Dialect") ? m_validatedSettings["Dialect"] : "evmTyped"; + + yul::Dialect const& dialect = validDialects.at(dialectName)(m_evmVersion); + + if (m_sources.size() != 1) + BOOST_THROW_EXCEPTION(runtime_error{"Expected only one source for yul test."}); + + string const& name = m_sources.begin()->first; + string const& source = m_sources.begin()->second; + + ErrorList errorList{}; + ErrorReporter errorReporter{errorList}; + + auto scanner = make_shared(CharStream(source, name)); + auto parserResult = yul::Parser(errorReporter, dialect).parse(scanner, false); + + if (parserResult) + { + yul::AsmAnalysisInfo analysisInfo; + yul::AsmAnalyzer(analysisInfo, errorReporter, dialect).analyze(*parserResult); + } + + for (auto const& error: errorList) + { + int locationStart = -1; + int locationEnd = -1; + + if (auto location = boost::get_error_info(*error)) + { + locationStart = location->start; + locationEnd = location->end; + } + + m_errorList.emplace_back(SyntaxTestError{ + error->typeName(), + errorMessage(*error), + name, + locationStart, + locationEnd + }); + } + +} + +bool SyntaxTest::validateSettings(langutil::EVMVersion _evmVersion) +{ + if (!CommonSyntaxTest::validateSettings(_evmVersion)) + return false; + + if (!m_settings.count("Dialect")) + return true; + + string const dialect = m_settings["Dialect"]; + m_validatedSettings["Dialect"] = dialect; + m_settings.erase("Dialect"); + + if (!validDialects.count(dialect)) + BOOST_THROW_EXCEPTION(runtime_error{ + "Invalid Dialect \"" + + dialect + + "\". Valid dialects are " + + joinHumanReadable(validDialectNames(), ", ", " and ") + + "." + }); + + return true; +} diff --git a/test/libyul/SyntaxTest.h b/test/libyul/SyntaxTest.h new file mode 100644 index 000000000000..e355a59329d4 --- /dev/null +++ b/test/libyul/SyntaxTest.h @@ -0,0 +1,53 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include + +namespace solidity::yul::test +{ + +using solidity::test::SyntaxTestError; + +class SyntaxTest: public solidity::test::CommonSyntaxTest +{ +public: + static std::unique_ptr create(Config const& _config) + { + return std::make_unique(_config.filename, _config.evmVersion); + } + static std::unique_ptr createErrorRecovery(Config const& _config) + { + return std::make_unique(_config.filename, _config.evmVersion); + } + SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion): + CommonSyntaxTest(_filename, _evmVersion) {} + virtual ~SyntaxTest() {} + + /// Validates the settings, i.e. moves them from m_settings to m_validatedSettings. + /// Throws a runtime exception if any setting is left at this class (i.e. unknown setting). + /// Returns true, if the test case is supported in the current environment and false + /// otherwise which causes this test to be skipped. + /// This might check e.g. for restrictions on the EVM version. + bool validateSettings(langutil::EVMVersion _evmVersion) override; +protected: + void parseAndAnalyze() override; +}; + +} diff --git a/test/libyul/yulSyntaxTests/simple_functions.yul b/test/libyul/yulSyntaxTests/simple_functions.yul new file mode 100644 index 000000000000..dd5c8bdde0b1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/simple_functions.yul @@ -0,0 +1,8 @@ +{ + function a() {} + function f() { mstore(0, 1) } + function g() { sstore(0, 1) } + function h() { let x := msize() } + function i() { let z := mload(0) } +} +// ---- diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 9fabfae68114..5ea8486ec5f5 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(isoltest isoltest.cpp IsolTestOptions.cpp ../Common.cpp + ../CommonSyntaxTest.cpp ../EVMHost.cpp ../TestCase.cpp ../libsolidity/util/BytesUtils.cpp @@ -34,6 +35,7 @@ add_executable(isoltest ../libyul/EwasmTranslationTest.cpp ../libyul/FunctionSideEffects.cpp ../libyul/ObjectCompilerTest.cpp + ../libyul/SyntaxTest.cpp ../libyul/YulOptimizerTest.cpp ../libyul/YulInterpreterTest.cpp ) From f2701db0aa71f271b53476b568102363f7bf9fe4 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Mon, 9 Dec 2019 17:01:31 +0100 Subject: [PATCH 072/160] Adds documentation for Solidity source upgrader. --- .circleci/config.yml | 7 + CMakeLists.txt | 1 + docs/using-the-compiler.rst | 237 ++++++++ libsolidity/analysis/OverrideChecker.cpp | 4 +- libsolidity/analysis/OverrideChecker.h | 15 +- libsolidity/ast/AST.h | 2 +- tools/CMakeLists.txt | 14 + tools/solidityUpgrade/SourceTransform.h | 159 ++++++ tools/solidityUpgrade/SourceUpgrade.cpp | 525 ++++++++++++++++++ tools/solidityUpgrade/SourceUpgrade.h | 158 ++++++ tools/solidityUpgrade/Upgrade050.cpp | 58 ++ tools/solidityUpgrade/Upgrade050.h | 58 ++ tools/solidityUpgrade/Upgrade060.cpp | 212 +++++++ tools/solidityUpgrade/Upgrade060.h | 69 +++ tools/solidityUpgrade/UpgradeChange.cpp | 73 +++ tools/solidityUpgrade/UpgradeChange.h | 82 +++ tools/solidityUpgrade/UpgradeSuite.h | 90 +++ .../contracts/DocsExamplePass.sol | 23 + tools/solidityUpgrade/contracts/Test.sol | 18 + .../contracts/TestMultiline.sol | 21 + .../contracts/TestNonFixable.sol | 15 + tools/solidityUpgrade/main.cpp | 81 +++ 22 files changed, 1911 insertions(+), 11 deletions(-) create mode 100644 tools/CMakeLists.txt create mode 100644 tools/solidityUpgrade/SourceTransform.h create mode 100644 tools/solidityUpgrade/SourceUpgrade.cpp create mode 100644 tools/solidityUpgrade/SourceUpgrade.h create mode 100644 tools/solidityUpgrade/Upgrade050.cpp create mode 100644 tools/solidityUpgrade/Upgrade050.h create mode 100644 tools/solidityUpgrade/Upgrade060.cpp create mode 100644 tools/solidityUpgrade/Upgrade060.h create mode 100644 tools/solidityUpgrade/UpgradeChange.cpp create mode 100644 tools/solidityUpgrade/UpgradeChange.h create mode 100644 tools/solidityUpgrade/UpgradeSuite.h create mode 100644 tools/solidityUpgrade/contracts/DocsExamplePass.sol create mode 100644 tools/solidityUpgrade/contracts/Test.sol create mode 100644 tools/solidityUpgrade/contracts/TestMultiline.sol create mode 100644 tools/solidityUpgrade/contracts/TestNonFixable.sol create mode 100644 tools/solidityUpgrade/main.cpp diff --git a/.circleci/config.yml b/.circleci/config.yml index a211edab0cff..c9fc35abcd50 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -62,6 +62,11 @@ defaults: path: build/solc/solc destination: solc + # compiled tool executable target + - artifacts_tools: &artifacts_tools + path: build/tools/solidity-upgrade + destination: solidity-upgrade + # compiled executable targets - artifacts_executables: &artifacts_executables root: build @@ -324,6 +329,7 @@ jobs: - checkout - run: *run_build - store_artifacts: *artifacts_solc + - store_artifacts: *artifacts_tools - persist_to_workspace: *artifacts_executables b_ubu_release: &build_ubuntu1904_release @@ -455,6 +461,7 @@ jobs: - /usr/local/Homebrew - run: *run_build - store_artifacts: *artifacts_solc + - store_artifacts: *artifacts_tools - persist_to_workspace: *artifacts_build_dir t_osx_soltest: diff --git a/CMakeLists.txt b/CMakeLists.txt index 408b42024438..1ab3e374180f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ add_subdirectory(libevmasm) add_subdirectory(libyul) add_subdirectory(libsolidity) add_subdirectory(libsolc) +add_subdirectory(tools) if (NOT EMSCRIPTEN) add_subdirectory(solc) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index c91e4e505c7e..fcb611f60e0f 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -474,3 +474,240 @@ Error types 11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue. 12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue. 13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible. + + +.. _compiler-tools: + +Compiler tools +************** + +solidity-upgrade +---------------- + +``solidity-upgrade`` can help you to semi-automatically upgrade your contracts +to breaking language changes. While it does not and cannot implement all +required changes for every breaking release, it still supports the ones, that +would need plenty of repetitive manual adjustments otherwise. + +.. note:: + + ``solidity-upgrade`` carries out a large part of the work, but your + contracts will most likely need further manual adjustments. We recommend + using a version control system for your files. This helps reviewing and + eventually rolling back the changes made. + +.. warning:: + + ``solidity-upgrade`` is not considered to be complete or free from bugs, so + please use with care. + +How it works +~~~~~~~~~~~~ + +You can pass (a) Solidity source file(s) to ``solidity-upgrade [files]``. If +these make use of ``import`` statement which refer to files outside the +current source file's directory, you need to specify directories that +are allowed to read and import files from, by passing +``--allow-paths [directory]``. You can ignore missing files by passing +``--ignore-missing``. + +``solidity-upgrade`` is based on ``libsolidity`` and can parse, compile and +analyse your source files, and might find applicable source upgrades in them. + +Source upgrades are considered to be small textual changes to your source code. +They are applied to an in-memory representation of the source files +given. The corresponding source file is updated by default, but you can pass +``--dry-run`` to simulate to whole upgrade process without writing to any file. + +The upgrade process itself has two phases. In the first phase source files are +parsed, and since it is not possible to upgrade source code on that level, +errors are collected and can be logged by passing ``--verbose``. No source +upgrades available at this point. + +In the second phase, all sources are compiled and all activated upgrade analysis +modules are run alongside compilation. By default, all available modules are +activated. Please read the documentation on +:ref:`available modules ` for further details. + + +This can result in compilation errors that may +be fixed by source upgrades. If no errors occur, no source upgrades are being +reported and you're done. +If errors occur and some upgrade module reported a source upgrade, the first +reported one gets applied and compilation is triggered again for all given +source files. The previous step is repeated as long as source upgrades are +reported. If errors still occur, you can log them by passing ``--verbose``. +If no errors occur, your contracts are up to date and can be compiled with +the latest version of the compiler. + +.. _upgrade-modules: + +Available upgrade modules +~~~~~~~~~~~~~~~~~~~~~~~~~ + ++-----------------+---------+--------------------------------------------------+ +| Module | Version | Description | ++=================+=========+==================================================+ +| ``constructor`` | 0.5.0 | Constructors must now be defined using the | +| | | ``constructor`` keyword. | ++-----------------+---------+--------------------------------------------------+ +| ``visibility`` | 0.5.0 | Explicit function visibility is now mandatory, | +| | | defaults to ``public``. | ++-----------------+---------+--------------------------------------------------+ +| ``abstract`` | 0.6.0 | The keyword ``abstract`` has to be used if a | +| | | contract does not implement all its functions. | ++-----------------+---------+--------------------------------------------------+ +| ``virtual`` | 0.6.0 | Functions without implementation outside an | +| | | interface have to be marked ``virtual``. | ++-----------------+---------+--------------------------------------------------+ +| ``override`` | 0.6.0 | When overriding a function or modifier, the new | +| | | keyword ``override`` must be used. | ++-----------------+---------+--------------------------------------------------+ + +Please read :doc:`0.5.0 release notes <050-breaking-changes>` and +:doc:`0.6.0 release notes <060-breaking-changes>` for further details. + +Synopsis +~~~~~~~~ + +.. code-block:: none + + Usage: solidity-upgrade [options] contract.sol + + Allowed options: + --help Show help message and exit. + --version Show version and exit. + --allow-paths path(s) + Allow a given path for imports. A list of paths can be + supplied by separating them with a comma. + --ignore-missing Ignore missing files. + --modules module(s) Only activate a specific upgrade module. A list of + modules can be supplied by separating them with a comma. + --dry-run Apply changes in-memory only and don't write to input + file. + --verbose Print logs, errors and changes. Shortens output of + upgrade patches. + --unsafe Accept *unsafe* changes. + + + +Bug Reports / Feature requests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you found a bug or if you have a feature request, please +`file an issue `_ on Github. + + +Example +~~~~~~~ + +Assume you have the following contracts you want to update declared in ``Source.sol``: + +.. code-block:: none + + // This will not compile + pragma solidity >0.4.23; + + contract Updateable { + function run() public view returns (bool); + function update() public; + } + + contract Upgradable { + function run() public view returns (bool); + function upgrade(); + } + + contract Source is Updateable, Upgradable { + function Source() public {} + + function run() + public + view + returns (bool) {} + + function update() {} + function upgrade() {} + } + + +Required changes +^^^^^^^^^^^^^^^^ + +To bring the contracts up to date with the current Solidity version, the +following upgrade modules have to be executed: ``constructor``, +``visibility``, ``abstract``, ``override`` and ``virtual``. Please read the +documentation on :ref:`available modules ` for further details. + +Running the upgrade +^^^^^^^^^^^^^^^^^^^ + +In this example, all modules needed to upgrade the contracts above, +are available and all of them are activated by default. Therefore you +do not need to specify the ``--modules`` option. + +.. code-block:: none + + $ solidity-upgrade Source.sol --dry-run + +.. code-block:: none + + Running analysis (and upgrade) on given source files. + .............. + + After upgrade: + + Found 0 errors. + Found 0 upgrades. + +The above performs a dry-ran upgrade on the given file and logs statistics after all. +In this case, the upgrade was successful and no further adjustments are needed. + +Finally, you can run the upgrade and also write to the source file. + +.. code-block:: none + + $ solidity-upgrade Source.sol + +.. code-block:: none + + Running analysis (and upgrade) on given source files. + .............. + + After upgrade: + + Found 0 errors. + Found 0 upgrades. + + +Review changes +^^^^^^^^^^^^^^ + +The command above applies all changes as shown below. Please review them carefully. + +.. code-block:: none + + pragma solidity >0.4.23; + + abstract contract Updateable { + function run() public view virtual returns (bool); + function update() public virtual; + } + + abstract contract Upgradable { + function run() public view virtual returns (bool); + function upgrade() public virtual; + } + + contract Source is Updateable, Upgradable { + constructor() public {} + + function run() + public + view + override(Updateable,Upgradable) + returns (bool) {} + + function update() public override {} + function upgrade() public override {} + } diff --git a/libsolidity/analysis/OverrideChecker.cpp b/libsolidity/analysis/OverrideChecker.cpp index fc2c8df6272b..8367f9f589ba 100644 --- a/libsolidity/analysis/OverrideChecker.cpp +++ b/libsolidity/analysis/OverrideChecker.cpp @@ -144,8 +144,8 @@ vector resolveDirectBaseContracts(ContractDefinition Declaration const* baseDecl = specifier->name().annotation().referencedDeclaration; auto contract = dynamic_cast(baseDecl); - solAssert(contract, "contract is null"); - resolvedContracts.emplace_back(contract); + if (contract) + resolvedContracts.emplace_back(contract); } return resolvedContracts; diff --git a/libsolidity/analysis/OverrideChecker.h b/libsolidity/analysis/OverrideChecker.h index 48fa543147e0..d4b96663c3dc 100644 --- a/libsolidity/analysis/OverrideChecker.h +++ b/libsolidity/analysis/OverrideChecker.h @@ -129,6 +129,7 @@ class OverrideProxy class OverrideChecker { public: + using OverrideProxyBySignatureMultiSet = std::multiset; /// @param _errorReporter provides the error logging functionality. explicit OverrideChecker(langutil::ErrorReporter& _errorReporter): @@ -137,12 +138,17 @@ class OverrideChecker void check(ContractDefinition const& _contract); -private: struct CompareByID { bool operator()(ContractDefinition const* _a, ContractDefinition const* _b) const; }; + /// Returns all functions of bases (including public state variables) that have not yet been overwritten. + /// May contain the same function multiple times when used with shared bases. + OverrideProxyBySignatureMultiSet const& inheritedFunctions(ContractDefinition const& _contract) const; + OverrideProxyBySignatureMultiSet const& inheritedModifiers(ContractDefinition const& _contract) const; + +private: void checkIllegalOverrides(ContractDefinition const& _contract); /// Performs various checks related to @a _overriding overriding @a _super like /// different return type, invalid visibility change, etc. @@ -174,15 +180,8 @@ class OverrideChecker /// Resolves an override list of UserDefinedTypeNames to a list of contracts. std::set resolveOverrideList(OverrideSpecifier const& _overrides) const; - using OverrideProxyBySignatureMultiSet = std::multiset; - void checkOverrideList(OverrideProxy _item, OverrideProxyBySignatureMultiSet const& _inherited); - /// Returns all functions of bases (including public state variables) that have not yet been overwritten. - /// May contain the same function multiple times when used with shared bases. - OverrideProxyBySignatureMultiSet const& inheritedFunctions(ContractDefinition const& _contract) const; - OverrideProxyBySignatureMultiSet const& inheritedModifiers(ContractDefinition const& _contract) const; - langutil::ErrorReporter& m_errorReporter; /// Cache for inheritedFunctions(). diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4bb9a0570931..c0c29be7d198 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -797,7 +797,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen { return CallableDeclaration::virtualSemantics() || - annotation().contract->isInterface(); + (annotation().contract && annotation().contract->isInterface()); } private: StateMutability m_stateMutability; diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 000000000000..bdcf615afedd --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable(solidity-upgrade + solidityUpgrade/main.cpp + solidityUpgrade/UpgradeChange.h + solidityUpgrade/UpgradeChange.cpp + solidityUpgrade/UpgradeSuite.h + solidityUpgrade/Upgrade050.cpp + solidityUpgrade/Upgrade060.cpp + solidityUpgrade/SourceTransform.h + solidityUpgrade/SourceUpgrade.cpp +) +target_link_libraries(solidity-upgrade PRIVATE solidity Boost::boost Boost::program_options Boost::system) + +include(GNUInstallDirs) +install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/tools/solidityUpgrade/SourceTransform.h b/tools/solidityUpgrade/SourceTransform.h new file mode 100644 index 000000000000..2b42471c15f5 --- /dev/null +++ b/tools/solidityUpgrade/SourceTransform.h @@ -0,0 +1,159 @@ +/* + 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 . +*/ + +#include + +#include + +#include + +namespace solidity::tools +{ + +/** + * Helper that provides functions which analyze certain source locations + * on a textual base. They utilize regular expression to search for + * keywords or to determine formatting. + */ +class SourceAnalysis { +public: + static bool isMultilineKeyword( + langutil::SourceLocation const& _location, + std::string const& _keyword + ) + { + return regex_search( + _location.text(), + std::regex{"(\\b" + _keyword + "\\b\\n|\\r|\\r\\n)"} + ); + } + + static bool hasMutabilityKeyword(langutil::SourceLocation const& _location) + { + return regex_search( + _location.text(), + std::regex{"(\\b(pure|view|nonpayable|payable)\\b)"} + ); + } + + static bool hasVirtualKeyword(langutil::SourceLocation const& _location) + { + return regex_search(_location.text(), std::regex{"(\\b(virtual)\\b)"}); + } + + static bool hasVisibilityKeyword(langutil::SourceLocation const& _location) + { + return regex_search(_location.text(), std::regex{"\\bpublic\\b"}); + } +}; + +/** + * Helper that provides functions which can analyse declarations and + * generate source snippets based on the information retrieved. + */ +class SourceGeneration +{ +public: + using CompareFunction = frontend::OverrideChecker::CompareByID; + using Contracts = std::set; + + /// Generates an `override` declaration for single overrides + /// or `override(...)` with contract list for multiple overrides. + static std::string functionOverride(Contracts const& _contracts) + { + if (_contracts.size() <= 1) + return "override"; + + std::string overrideList; + for (auto inheritedContract: _contracts) + overrideList += inheritedContract->name() + ","; + + return "override(" + overrideList.substr(0, overrideList.size() - 1) + ")"; + } +}; + +/** + * Helper that provides functions which apply changes to Solidity source code + * on a textual base. In general, these utilize regular expressions applied + * to the given source location. + */ +class SourceTransform +{ +public: + /// Searches for the keyword given and prepends the expression. + /// E.g. `function f() view;` -> `function f() public view;` + static std::string insertBeforeKeyword( + langutil::SourceLocation const& _location, + std::string const& _keyword, + std::string const& _expression + ) + { + return regex_replace( + _location.text(), + std::regex{"(\\b" + _keyword + "\\b)"}, + _expression + " " + _keyword + ); + } + + /// Searches for the keyword given and appends the expression. + /// E.g. `function f() public {}` -> `function f() public override {}` + static std::string insertAfterKeyword( + langutil::SourceLocation const& _location, + std::string const& _keyword, + std::string const& _expression + ) + { + bool isMultiline = SourceAnalysis::isMultilineKeyword(_location, _keyword); + std::string toAppend = isMultiline ? ("\n " + _expression) : (" " + _expression); + std::regex keyword{"(\\b" + _keyword + "\\b)"}; + + return regex_replace(_location.text(), keyword, _keyword + toAppend); + } + + /// Searches for the first right parenthesis and appends the expression + /// given. + /// E.g. `function f() {}` -> `function f() public {}` + static std::string insertAfterRightParenthesis( + langutil::SourceLocation const& _location, + std::string const& _expression + ) + { + return regex_replace( + _location.text(), + std::regex{"(\\))"}, + ") " + _expression + ); + } + + /// Searches for the `function` keyword and its identifier and replaces + /// both by the expression given. + /// E.g. `function Storage() {}` -> `constructor() {}` + static std::string replaceFunctionName( + langutil::SourceLocation const& _location, + std::string const& _name, + std::string const& _expression + ) + { + return regex_replace( + _location.text(), + std::regex{"(\\bfunction\\s*" + _name + "\\b)"}, + _expression + ); + } +}; + +} diff --git a/tools/solidityUpgrade/SourceUpgrade.cpp b/tools/solidityUpgrade/SourceUpgrade.cpp new file mode 100644 index 000000000000..82d8fecf0b9b --- /dev/null +++ b/tools/solidityUpgrade/SourceUpgrade.cpp @@ -0,0 +1,525 @@ +/* + 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 . +*/ +#include + +#include +#include + +#include + +#include +#include +#include + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::tools; +using namespace solidity::util; +using namespace solidity::frontend; +using namespace std; + + +static string const g_argHelp = "help"; +static string const g_argVersion = "version"; +static string const g_argInputFile = "input-file"; +static string const g_argModules = "modules"; +static string const g_argDryRun = "dry-run"; +static string const g_argUnsafe = "unsafe"; +static string const g_argVerbose = "verbose"; +static string const g_argIgnoreMissingFiles = "ignore-missing"; +static string const g_argAllowPaths = "allow-paths"; + +namespace +{ + +ostream& out() +{ + return cout; +} + +AnsiColorized log() +{ + return AnsiColorized(cout, true, {}); +} + +AnsiColorized success() +{ + return AnsiColorized(cout, true, {formatting::CYAN}); +} + +AnsiColorized warning() +{ + return AnsiColorized(cout, true, {formatting::YELLOW}); +} + +AnsiColorized error() +{ + return AnsiColorized(cout, true, {formatting::MAGENTA}); +} + +void logVersion() +{ + /// TODO Replace by variable that can be set during build. + out() << "0.1.0" << endl; +} + +void logProgress() +{ + out() << "."; + out().flush(); +} + +} + +bool SourceUpgrade::parseArguments(int _argc, char** _argv) +{ + po::options_description desc(R"(solidity-upgrade, the Solidity upgrade assistant. + +The solidity-upgrade tool can help upgrade smart contracts to breaking language features. + +It does not support all breaking changes for each version, but will hopefully assist +upgrading your contracts to the desired Solidity version. + +solidity-upgrade is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. Please be careful when running upgrades on +your contracts. + +Usage: solidity-upgrade [options] contract.sol + +Allowed options)", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23 + ); + desc.add_options() + (g_argHelp.c_str(), "Show help message and exit.") + (g_argVersion.c_str(), "Show version and exit.") + ( + g_argAllowPaths.c_str(), + po::value()->value_name("path(s)"), + "Allow a given path for imports. A list of paths can be supplied by separating them " + "with a comma." + ) + (g_argIgnoreMissingFiles.c_str(), "Ignore missing files.") + ( + g_argModules.c_str(), + po::value()->value_name("module(s)"), + "Only activate a specific upgrade module. A list of " + "modules can be supplied by separating them with a comma." + ) + (g_argDryRun.c_str(), "Apply changes in-memory only and don't write to input file.") + (g_argVerbose.c_str(), "Print logs, errors and changes. Shortens output of upgrade patches.") + (g_argUnsafe.c_str(), "Accept *unsafe* changes."); + + + po::options_description allOptions = desc; + allOptions.add_options()("input-file", po::value>(), "input file"); + + po::positional_options_description filesPositions; + filesPositions.add("input-file", -1); + + // parse the compiler arguments + try + { + po::command_line_parser cmdLineParser(_argc, _argv); + cmdLineParser.style( + po::command_line_style::default_style & (~po::command_line_style::allow_guessing) + ); + cmdLineParser.options(allOptions).positional(filesPositions); + po::store(cmdLineParser.run(), m_args); + } + catch (po::error const& _exception) + { + error() << _exception.what() << endl; + return false; + } + + if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) + { + out() << endl; + log() << desc; + return false; + } + + if (m_args.count(g_argVersion)) + { + logVersion(); + return false; + } + + if (m_args.count(g_argModules)) + { + vector moduleArgs; + auto modules = boost::split( + moduleArgs, m_args[g_argModules].as(), boost::is_any_of(",") + ); + + /// All modules are activated by default. Clear them before activating single ones. + m_suite.deactivateModules(); + + for (string const& module: modules) + { + if (module == "constructor") + m_suite.activateModule(Module::ConstructorKeyword); + else if (module == "visibility") + m_suite.activateModule(Module::VisibilitySpecifier); + else if (module == "abstract") + m_suite.activateModule(Module::AbstractContract); + else if (module == "override") + m_suite.activateModule(Module::OverridingFunction); + else if (module == "virtual") + m_suite.activateModule(Module::VirtualFunction); + else + { + error() << "Unknown upgrade module \"" + module + "\"" << endl; + return false; + } + } + } + + /// TODO Share with solc commandline interface. + if (m_args.count(g_argAllowPaths)) + { + vector paths; + auto allowedPaths = boost::split( + paths, m_args[g_argAllowPaths].as(), boost::is_any_of(",") + ); + for (string const& path: allowedPaths) + { + auto filesystem_path = boost::filesystem::path(path); + /// If the given path had a trailing slash, the Boost filesystem + /// path will have it's last component set to '.'. This breaks + /// path comparison in later parts of the code, so we need to strip + /// it. + if (filesystem_path.filename() == ".") + filesystem_path.remove_filename(); + m_allowedDirectories.push_back(filesystem_path); + } + } + + + return true; +} + +void SourceUpgrade::printPrologue() +{ + out() << endl; + out() << endl; + + log() << + "solidity-upgrade does not support all breaking changes for each version." << + endl << + "Please run `solidity-upgrade --help` and get a list of implemented upgrades." << + endl << + endl; + + log() << + "Running analysis (and upgrade) on given source files." << + endl; +} + +bool SourceUpgrade::processInput() +{ + if (!readInputFiles()) + return false; + + resetCompiler(fileReader()); + + tryCompile(); + runUpgrade(); + + printStatistics(); + + return true; +} + +void SourceUpgrade::tryCompile() const +{ + bool verbose = m_args.count(g_argVerbose); + + if (verbose) + log() << "Running compilation phases." << endl << endl; + else + logProgress(); + + try + { + if (m_compiler->parse()) + { + if (m_compiler->analyze()) + m_compiler->compile(); + else + if (verbose) + { + error() << + "Compilation errors that solidity-upgrade may resolve occurred." << + endl << + endl; + + printErrors(); + } + } + else + if (verbose) + { + error() << + "Compilation errors that solidity-upgrade cannot resolve occurred." << + endl << + endl; + + printErrors(); + } + } + catch (Exception const& _exception) + { + error() << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; + } + catch (std::exception const& _e) + { + error() << (_e.what() ? ": " + string(_e.what()) : ".") << endl; + } + catch (...) + { + error() << "Unknown exception during compilation." << endl; + } +} + +void SourceUpgrade::runUpgrade() +{ + bool recompile = true; + + while (recompile && !m_compiler->errors().empty()) + { + for (auto& sourceCode: m_sourceCodes) + { + recompile = analyzeAndUpgrade(sourceCode); + if (recompile) + break; + } + + if (recompile) + { + m_suite.reset(); + resetCompiler(); + tryCompile(); + } + } +} + +bool SourceUpgrade::analyzeAndUpgrade(pair const& _sourceCode) +{ + bool applyUnsafe = m_args.count(g_argUnsafe); + bool verbose = m_args.count(g_argVerbose); + + if (verbose) + log() << "Analyzing and upgrading " << _sourceCode.first << "." << endl; + + if (m_compiler->state() >= CompilerStack::State::AnalysisPerformed) + m_suite.analyze(m_compiler->ast(_sourceCode.first)); + + if (!m_suite.changes().empty()) + { + auto& change = m_suite.changes().front(); + + if (verbose) + change.log(true); + + if (change.level() == UpgradeChange::Level::Safe) + { + applyChange(_sourceCode, change); + return true; + } + else if (change.level() == UpgradeChange::Level::Unsafe) + { + if (applyUnsafe) + { + applyChange(_sourceCode, change); + return true; + } + } + } + + return false; +} + +void SourceUpgrade::applyChange( + pair const& _sourceCode, + UpgradeChange& _change +) +{ + bool dryRun = m_args.count(g_argDryRun); + bool verbose = m_args.count(g_argVerbose); + + if (verbose) + { + log() << "Applying change to " << _sourceCode.first << endl << endl; + log() << _change.patch(); + } + + _change.apply(); + m_sourceCodes[_sourceCode.first] = _change.source(); + + if (!dryRun) + writeInputFile(_sourceCode.first, _change.source()); +} + +void SourceUpgrade::printErrors() const +{ + auto formatter = make_unique(cout, true); + + for (auto const& error: m_compiler->errors()) + if (error->type() != langutil::Error::Type::Warning) + formatter->printErrorInformation(*error); +} + +void SourceUpgrade::printStatistics() const +{ + out() << endl; + out() << endl; + out() << "After upgrade:" << endl; + out() << endl; + error() << "Found " << m_compiler->errors().size() << " errors." << endl; + success() << "Found " << m_suite.changes().size() << " upgrades." << endl; +} + +bool SourceUpgrade::readInputFiles() +{ + bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); + + /// TODO Share with solc commandline interface. + if (m_args.count(g_argInputFile)) + for (string path: m_args[g_argInputFile].as>()) + { + auto infile = boost::filesystem::path(path); + if (!boost::filesystem::exists(infile)) + { + if (!ignoreMissing) + { + error() << infile << " is not found." << endl; + return false; + } + else + error() << infile << " is not found. Skipping." << endl; + + continue; + } + + if (!boost::filesystem::is_regular_file(infile)) + { + if (!ignoreMissing) + { + error() << infile << " is not a valid file." << endl; + return false; + } + else + error() << infile << " is not a valid file. Skipping." << endl; + + continue; + } + + m_sourceCodes[infile.generic_string()] = readFileAsString(infile.string()); + path = boost::filesystem::canonical(infile).string(); + } + + if (m_sourceCodes.size() == 0) + { + warning() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; + return false; + } + + return true; +} + +bool SourceUpgrade::writeInputFile(string const& _path, string const& _source) +{ + bool verbose = m_args.count(g_argVerbose); + + if (verbose) + { + out() << endl; + log() << "Writing to input file " << _path << "." << endl; + } + + ofstream file(_path, ios::trunc); + file << _source; + + return true; +} + +ReadCallback::Callback SourceUpgrade::fileReader() +{ + /// TODO Share with solc commandline interface. + ReadCallback::Callback fileReader = [this](string const&, string const& _path) + { + try + { + auto path = boost::filesystem::path(_path); + auto canonicalPath = boost::filesystem::weakly_canonical(path); + bool isAllowed = false; + for (auto const& allowedDir: m_allowedDirectories) + { + // If dir is a prefix of boostPath, we are fine. + if ( + std::distance(allowedDir.begin(), allowedDir.end()) <= std::distance(canonicalPath.begin(), canonicalPath.end()) && + std::equal(allowedDir.begin(), allowedDir.end(), canonicalPath.begin()) + ) + { + isAllowed = true; + break; + } + } + if (!isAllowed) + return ReadCallback::Result{false, "File outside of allowed directories."}; + + if (!boost::filesystem::exists(canonicalPath)) + return ReadCallback::Result{false, "File not found."}; + + if (!boost::filesystem::is_regular_file(canonicalPath)) + return ReadCallback::Result{false, "Not a valid file."}; + + auto contents = readFileAsString(canonicalPath.string()); + m_sourceCodes[path.generic_string()] = contents; + return ReadCallback::Result{true, contents}; + } + catch (Exception const& _exception) + { + return ReadCallback::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)}; + } + catch (...) + { + return ReadCallback::Result{false, "Unknown exception in read callback."}; + } + }; + + return fileReader; +} + +void SourceUpgrade::resetCompiler() +{ + m_compiler->reset(); + m_compiler->setSources(m_sourceCodes); + m_compiler->setParserErrorRecovery(true); +} + +void SourceUpgrade::resetCompiler(ReadCallback::Callback const& _callback) +{ + m_compiler.reset(new CompilerStack(_callback)); + m_compiler->setSources(m_sourceCodes); + m_compiler->setParserErrorRecovery(true); +} diff --git a/tools/solidityUpgrade/SourceUpgrade.h b/tools/solidityUpgrade/SourceUpgrade.h new file mode 100644 index 000000000000..9ace60e575af --- /dev/null +++ b/tools/solidityUpgrade/SourceUpgrade.h @@ -0,0 +1,158 @@ +/* + 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 . +*/ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace solidity::tools +{ + +/** + * The Solidity source upgrade tool. It supplies a command line interface + * and connects this to a compiler stack that the upgrade logic is facilitated + * with. + */ +class SourceUpgrade +{ +public: + /// Parse command line arguments and return false in case of a failure. + bool parseArguments(int _argc, char** _argv); + /// Prints additional information on the upgrade tool. + void printPrologue(); + /// Parse / compile files and runs upgrade analysis on them. + bool processInput(); + +private: + /// All available upgrade modules + enum class Module + { + ConstructorKeyword, + VisibilitySpecifier, + AbstractContract, + OverridingFunction, + VirtualFunction + }; + + /// Upgrade suite that hosts all available modules. + class Suite: public UpgradeSuite + { + public: + void analyze(frontend::SourceUnit const& _sourceUnit) + { + /// Solidity 0.5.0 + if (isActivated(Module::ConstructorKeyword)) + ConstructorKeyword{m_changes}.analyze(_sourceUnit); + if (isActivated(Module::VisibilitySpecifier)) + VisibilitySpecifier{m_changes}.analyze(_sourceUnit); + + /// Solidity 0.6.0 + if (isActivated(Module::AbstractContract)) + AbstractContract{m_changes}.analyze(_sourceUnit); + if (isActivated(Module::OverridingFunction)) + OverridingFunction{m_changes}.analyze(_sourceUnit); + if (isActivated(Module::VirtualFunction)) + VirtualFunction{m_changes}.analyze(_sourceUnit); + } + + void activateModule(Module _module) { m_modules.insert(_module); } + void deactivateModules() { m_modules.clear(); } + + private: + bool isActivated(Module _module) const + { + return m_modules.find(_module) != m_modules.end(); + } + + /// All modules are activated by default. Clear them before activating + /// single ones. + std::set m_modules = { + Module::ConstructorKeyword, + Module::VisibilitySpecifier, + Module::AbstractContract, + Module::OverridingFunction, + Module::VirtualFunction + }; + }; + + /// Parses the current sources and runs analyses as well as compilation on + /// them if parsing was successful. + void tryCompile() const; + /// Analyses and upgrades the sources given. The upgrade happens in a loop, + /// applying one change at a time, which is run until no applicable changes + /// are found any more. Only one change is done at a time and all sources + /// are being compiled again after each change. + void runUpgrade(); + /// Runs upgrade analysis on source and applies upgrades changes to it. + /// Returns `true` if there're still changes that can be applied, + /// `false` otherwise. + bool analyzeAndUpgrade( + std::pair const& _sourceCode + ); + + /// Applies the change given to its source code. If no `--dry-run` was + /// passed via the commandline, the upgraded source code is written back + /// to its file. + void applyChange( + std::pair const& _sourceCode, + UpgradeChange& _change + ); + + /// Prints all errors (excluding warnings) the compiler currently reported. + void printErrors() const; + /// Prints error and upgrade overview at the end of each full run. + void printStatistics() const; + + /// Reads all input files given and stores sources in the internal data + /// structure. Reports errors if files cannot be found. + bool readInputFiles(); + /// Writes source to file given. + bool writeInputFile(std::string const& _path, std::string const& _source); + /// Returns a file reader function that fills `m_sources`. + frontend::ReadCallback::Callback fileReader(); + + /// Resets the compiler stack and configures sources to compile. + /// Also enables error recovery. + void resetCompiler(); + /// Resets the compiler stack and configures sources to compile. + /// Also enables error recovery. Passes read callback to the compiler stack. + void resetCompiler(frontend::ReadCallback::Callback const& _callback); + + /// Compiler arguments variable map + boost::program_options::variables_map m_args; + /// Map of input files to source code strings + std::map m_sourceCodes; + /// Solidity compiler stack + std::unique_ptr m_compiler; + /// List of allowed directories to read files from + std::vector m_allowedDirectories; + /// Holds all upgrade modules and source upgrades. + Suite m_suite; +}; + +} diff --git a/tools/solidityUpgrade/Upgrade050.cpp b/tools/solidityUpgrade/Upgrade050.cpp new file mode 100644 index 000000000000..ac939c2a10fe --- /dev/null +++ b/tools/solidityUpgrade/Upgrade050.cpp @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +#include +#include + +#include + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::frontend; +using namespace solidity::tools; + +void ConstructorKeyword::endVisit(ContractDefinition const& _contract) +{ + for (auto const* function: _contract.definedFunctions()) + if (function->name() == _contract.name()) + m_changes.push_back( + UpgradeChange{ + UpgradeChange::Level::Safe, + function->location(), + SourceTransform::replaceFunctionName( + function->location(), + function->name(), + "constructor" + ) + } + ); +} + +void VisibilitySpecifier::endVisit(FunctionDefinition const& _function) +{ + if (_function.noVisibilitySpecified()) + m_changes.push_back( + UpgradeChange{ + UpgradeChange::Level::Safe, + _function.location(), + SourceTransform::insertAfterRightParenthesis(_function.location(), "public") + } + ); +} diff --git a/tools/solidityUpgrade/Upgrade050.h b/tools/solidityUpgrade/Upgrade050.h new file mode 100644 index 000000000000..d85b699122d7 --- /dev/null +++ b/tools/solidityUpgrade/Upgrade050.h @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +#pragma once + +#include +#include + +#include + +namespace solidity::tools +{ + +/** + * Module that performs analysis on the AST. It visits all contract + * definitions and its defined functions and reports a source upgrade, + * if one of the declared functions is the constructor but does not + * use the `constructor` keyword. + */ +class ConstructorKeyword: public AnalysisUpgrade +{ +public: + using AnalysisUpgrade::AnalysisUpgrade; + + void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } +private: + void endVisit(frontend::ContractDefinition const& _contract); +}; + +/** + * Module that performs analysis on the AST. It visits function definitions + * and reports a source upgrade, if this function's visibility is `public`, + * but not marked explicitly as such. + */ +class VisibilitySpecifier: public AnalysisUpgrade +{ +public: + using AnalysisUpgrade::AnalysisUpgrade; + + void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } +private: + void endVisit(frontend::FunctionDefinition const& _function); +}; + +} diff --git a/tools/solidityUpgrade/Upgrade060.cpp b/tools/solidityUpgrade/Upgrade060.cpp new file mode 100644 index 000000000000..9cd996f53312 --- /dev/null +++ b/tools/solidityUpgrade/Upgrade060.cpp @@ -0,0 +1,212 @@ +/* + 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 . +*/ +#include +#include + +#include + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::frontend; +using namespace solidity::tools; + +using Contracts = set; + +namespace +{ + +inline string appendOverride( + FunctionDefinition const& _function, + Contracts const& _expectedContracts +) +{ + auto location = _function.location(); + string upgradedCode; + string overrideExpression = SourceGeneration::functionOverride(_expectedContracts); + + if (SourceAnalysis::hasVirtualKeyword(location)) + upgradedCode = SourceTransform::insertAfterKeyword( + location, + "virtual", + overrideExpression + ); + else if (SourceAnalysis::hasMutabilityKeyword(location)) + upgradedCode = SourceTransform::insertAfterKeyword( + location, + stateMutabilityToString(_function.stateMutability()), + overrideExpression + ); + else if (SourceAnalysis::hasVisibilityKeyword(location)) + upgradedCode = SourceTransform::insertAfterKeyword( + location, + Declaration::visibilityToString(_function.visibility()), + overrideExpression + ); + else + upgradedCode = SourceTransform::insertAfterRightParenthesis( + location, + overrideExpression + ); + + return upgradedCode; +} + +inline string appendVirtual(FunctionDefinition const& _function) +{ + auto location = _function.location(); + string upgradedCode; + + if (SourceAnalysis::hasMutabilityKeyword(location)) + upgradedCode = SourceTransform::insertAfterKeyword( + location, + stateMutabilityToString(_function.stateMutability()), + "virtual" + ); + else if (SourceAnalysis::hasVisibilityKeyword(location)) + upgradedCode = SourceTransform::insertAfterKeyword( + location, + Declaration::visibilityToString(_function.visibility()), + "virtual" + ); + else + upgradedCode = SourceTransform::insertAfterRightParenthesis( + _function.location(), + "virtual" + ); + + return upgradedCode; +} + +} + +void AbstractContract::endVisit(ContractDefinition const& _contract) +{ + bool isFullyImplemented = _contract.annotation().unimplementedFunctions.empty(); + + if ( + !isFullyImplemented && + !_contract.abstract() && + !_contract.isInterface() + ) + m_changes.push_back( + UpgradeChange{ + UpgradeChange::Level::Safe, + _contract.location(), + SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract") + } + ); +} + +void OverridingFunction::endVisit(ContractDefinition const& _contract) +{ + auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract); + + for (auto const* function: _contract.definedFunctions()) + { + Contracts expectedContracts; + OverrideProxy proxy{function}; + + if (!function->isConstructor()) + { + /// Build list of contracts expected to be mentioned in the override list (if any). + for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++) + expectedContracts.insert(&begin->contract()); + + /// Add override with contract list, if needed. + if (!function->overrides() && expectedContracts.size() > 1) + m_changes.push_back( + UpgradeChange{ + UpgradeChange::Level::Safe, + function->location(), + appendOverride(*function, expectedContracts) + } + ); + + for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++) + { + auto& super = (*begin); + auto functionType = FunctionType(*function).asCallableFunction(false); + auto superType = super.functionType()->asCallableFunction(false); + + if (functionType && functionType->hasEqualParameterTypes(*superType)) + { + /// If function does not specify override and no override with + /// contract list was added before. + if (!function->overrides() && expectedContracts.size() <= 1) + m_changes.push_back( + UpgradeChange{ + UpgradeChange::Level::Safe, + function->location(), + appendOverride(*function, expectedContracts) + } + ); + } + } + } + } +} + +void VirtualFunction::endVisit(ContractDefinition const& _contract) +{ + auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract); + + for (FunctionDefinition const* function: _contract.definedFunctions()) + { + OverrideProxy proxy{function}; + + if (!function->isConstructor()) + { + if ( + !function->markedVirtual() && + !function->isImplemented() && + !function->virtualSemantics() && + function->visibility() > Visibility::Private + ) + { + m_changes.push_back( + UpgradeChange{ + UpgradeChange::Level::Safe, + function->location(), + appendVirtual(*function) + } + ); + } + + for (auto [begin, end] = inheritedFunctions.equal_range(proxy); begin != end; begin++) + { + auto& super = (*begin); + if ( + !function->markedVirtual() && + !super.virtualSemantics() + ) + { + m_changes.push_back( + UpgradeChange{ + UpgradeChange::Level::Safe, + function->location(), + appendVirtual(*function) + } + ); + } + } + } + } +} diff --git a/tools/solidityUpgrade/Upgrade060.h b/tools/solidityUpgrade/Upgrade060.h new file mode 100644 index 000000000000..ea29d3a7b295 --- /dev/null +++ b/tools/solidityUpgrade/Upgrade060.h @@ -0,0 +1,69 @@ +/* + 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 . +*/ +#pragma once + +#include +#include + +#include + +namespace solidity::tools +{ + +/** + * Module that performs analysis on the AST. Finds abstract contracts that are + * not marked as such and adds the `abstract` keyword. + */ +class AbstractContract: public AnalysisUpgrade +{ +public: + using AnalysisUpgrade::AnalysisUpgrade; + + void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } +private: + void endVisit(frontend::ContractDefinition const& _contract); +}; + +/** + * Module that performs analysis on the AST. Finds functions that need to be + * marked `override` and adds the keyword to the function header. + */ +class OverridingFunction: public AnalysisUpgrade +{ +public: + using AnalysisUpgrade::AnalysisUpgrade; + + void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } +private: + void endVisit(frontend::ContractDefinition const& _contract); +}; + +/** + * Module that performs analysis on the AST. Finds functions that need to be + * marked `virtual` and adds the keyword to the function header. + */ +class VirtualFunction: public AnalysisUpgrade +{ +public: + using AnalysisUpgrade::AnalysisUpgrade; + + void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } +private: + void endVisit(frontend::ContractDefinition const& _function); +}; + +} diff --git a/tools/solidityUpgrade/UpgradeChange.cpp b/tools/solidityUpgrade/UpgradeChange.cpp new file mode 100644 index 000000000000..8bbd6214cabe --- /dev/null +++ b/tools/solidityUpgrade/UpgradeChange.cpp @@ -0,0 +1,73 @@ +/* + 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 . +*/ +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::tools; + +void UpgradeChange::apply() +{ + m_source.replace(m_location.start, m_location.end - m_location.start, m_patch); +} + +void UpgradeChange::log(bool const _shorten) const +{ + stringstream os; + SourceReferenceFormatterHuman formatter{os, true}; + + string start = to_string(m_location.start); + string end = to_string(m_location.end); + + auto color = m_level == Level::Unsafe ? formatting::MAGENTA : formatting::CYAN; + auto level = m_level == Level::Unsafe ? "unsafe" : "safe"; + + os << endl; + AnsiColorized(os, true, {formatting::BOLD, color}) << "Upgrade change (" << level << ")" << endl; + os << "=======================" << endl; + formatter.printSourceLocation(SourceReferenceExtractor::extract(&m_location)); + os << endl; + + LineColumn lineEnd = m_location.source->translatePositionToLineColumn(m_location.end); + int const leftpad = static_cast(log10(max(lineEnd.line, 1))) + 2; + + stringstream output; + output << (_shorten ? shortenSource(m_patch) : m_patch); + + string line; + while (getline(output, line)) + { + os << string(leftpad, ' '); + AnsiColorized(os, true, {formatting::BOLD, formatting::BLUE}) << "| "; + AnsiColorized(os, true, {}) << line << endl; + } + cout << os.str(); + cout << endl; +} + +string UpgradeChange::shortenSource(string const& _source) +{ + size_t constexpr maxSourceLength = 1000; + if (_source.size() > maxSourceLength) + return _source.substr(0, min(_source.size(), maxSourceLength)) + "..."; + return _source; +} diff --git a/tools/solidityUpgrade/UpgradeChange.h b/tools/solidityUpgrade/UpgradeChange.h new file mode 100644 index 000000000000..4d279fa60905 --- /dev/null +++ b/tools/solidityUpgrade/UpgradeChange.h @@ -0,0 +1,82 @@ +/* + 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 . +*/ +#pragma once + +#include + +#include + +#include + +namespace solidity::tools +{ + +/** + * Models a single source code change, based on the initial source location + * and a patch, which needs to be applied. + * It implements the concept of level of confidence in the change and distiguishes + * safe from unsafe changes. A "safe" change is considered to not break + * compilation or change semantics. An "unsafe" change is considered to potentially + * change semantics or require further manual management. + */ +class UpgradeChange +{ +public: + enum class Level + { + Safe, + Unsafe + }; + + UpgradeChange( + Level _level, + langutil::SourceLocation _location, + std::string _patch + ) + : + m_location(_location), + m_source(_location.source->source()), + m_patch(_patch), + m_level(_level) {} + + ~UpgradeChange() {} + + langutil::SourceLocation const& location() { return m_location; } + std::string source() const { return m_source; } + std::string patch() { return m_patch; } + Level level() const { return m_level; } + + /// Does the actual replacement of code under at current source location. + /// The change is applied on the upgrade-specific copy of source code. + /// The altered code is then requested by the upgrade routine later on. + void apply(); + /// Does a pretty-print of this upgrade change. It uses a source formatter + /// provided by the compiler in order to print affected code. Since the patch + /// can contain a lot of code lines, it can be shortened, which is signaled + /// by setting the flag. + void log(bool const _shorten = true) const; +private: + langutil::SourceLocation m_location; + std::string m_source; + std::string m_patch; + Level m_level; + + /// Shortens the given source to a constant limit. + static std::string shortenSource(std::string const& _source); +}; + +} diff --git a/tools/solidityUpgrade/UpgradeSuite.h b/tools/solidityUpgrade/UpgradeSuite.h new file mode 100644 index 000000000000..b9b094e70e43 --- /dev/null +++ b/tools/solidityUpgrade/UpgradeSuite.h @@ -0,0 +1,90 @@ +/* + 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 . +*/ +#pragma once + +#include + +#include + +#include +#include + +#include + +namespace solidity::tools +{ + +/** + * The base upgrade module that can be inherited from. Doing so + * creates a basic upgrade module that facilitates access to + * change reporting. + */ +class Upgrade +{ +public: + Upgrade(std::vector& _changes): m_changes(_changes) {} + +protected: + /// A reference to a suite-specific set of changes. + /// It is passed to all upgrade modules and meant to collect + /// reported changes. + std::vector& m_changes; +}; + +/** + * A specific upgrade module meant to be run after the analysis phase + * of the compiler. + */ +class AnalysisUpgrade: public Upgrade, public frontend::ASTConstVisitor +{ +public: + AnalysisUpgrade(std::vector& _changes): + Upgrade(_changes), + m_errorReporter(m_errors), + m_overrideChecker(m_errorReporter) + {} + /// Interface function for all upgrade modules that are meant + /// be run after the analysis phase of the compiler. + void analyze(frontend::SourceUnit const&) {} +protected: + langutil::ErrorList m_errors; + langutil::ErrorReporter m_errorReporter; + frontend::OverrideChecker m_overrideChecker; +}; + +/** + * The generic upgrade suite. Should be inherited from for each set of + * desired upgrade modules. + */ +class UpgradeSuite +{ +public: + /// The base interface function that needs to be implemented for each + /// suite. It should create suite-specific upgrade modules and trigger + /// their analysis. + void analyze(frontend::SourceUnit const& _sourceUnit); + /// Resets all changes collected so far. + void reset() { m_changes.clear(); } + + std::vector& changes() { return m_changes; } + std::vector const& changes() const { return m_changes; } + +protected: + std::vector m_changes; +}; + +} diff --git a/tools/solidityUpgrade/contracts/DocsExamplePass.sol b/tools/solidityUpgrade/contracts/DocsExamplePass.sol new file mode 100644 index 000000000000..d8e4dc8e09dd --- /dev/null +++ b/tools/solidityUpgrade/contracts/DocsExamplePass.sol @@ -0,0 +1,23 @@ +pragma solidity >0.4.23; + +contract Updateable { + function run() public view returns (bool); + function update() public; +} + +contract Upgradable { + function run() public view returns (bool); + function upgrade(); +} + +contract Source is Updateable, Upgradable { + function Source() public {} + + function run() + public + view + returns (bool) {} + + function update() {} + function upgrade() {} +} \ No newline at end of file diff --git a/tools/solidityUpgrade/contracts/Test.sol b/tools/solidityUpgrade/contracts/Test.sol new file mode 100644 index 000000000000..8958a091a0ad --- /dev/null +++ b/tools/solidityUpgrade/contracts/Test.sol @@ -0,0 +1,18 @@ +pragma solidity >0.4.23; + +contract Storage { + function Storage() public {} + function start(); + function state() public view returns (bool); + function stop() public; +} + +contract Observable { + function state() public view returns (bool); +} + +contract VolatileStorage is Storage, Observable { + function start() {} + function state() public view returns (bool) {} + function stop() {} +} \ No newline at end of file diff --git a/tools/solidityUpgrade/contracts/TestMultiline.sol b/tools/solidityUpgrade/contracts/TestMultiline.sol new file mode 100644 index 000000000000..7083da4ea608 --- /dev/null +++ b/tools/solidityUpgrade/contracts/TestMultiline.sol @@ -0,0 +1,21 @@ +pragma solidity >0.4.23; + +contract Storage { + function Storage() {} + function init() public; + function idle(); + function destroy() public view; +} + +contract VolatileStorage is Storage { + function init() + public + {} + + function idle() {} + + function destroy() + public + view + {} +} \ No newline at end of file diff --git a/tools/solidityUpgrade/contracts/TestNonFixable.sol b/tools/solidityUpgrade/contracts/TestNonFixable.sol new file mode 100644 index 000000000000..0a5c2f00e8b3 --- /dev/null +++ b/tools/solidityUpgrade/contracts/TestNonFixable.sol @@ -0,0 +1,15 @@ +pragma solidity >0.4.23; + +contract Storage { + function Storage() {} + function init() public; + function idle(); + function destroy() public view; +} + +contract VolatileStorage is Storage { + uint[] array; + function init() public { array.length = 3; } + function idle() {} + function destroy() public view {} +} \ No newline at end of file diff --git a/tools/solidityUpgrade/main.cpp b/tools/solidityUpgrade/main.cpp new file mode 100644 index 000000000000..0f2b7a473500 --- /dev/null +++ b/tools/solidityUpgrade/main.cpp @@ -0,0 +1,81 @@ +/* + 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 . +*/ +#include + +#include +#include + +//#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#endif + +using namespace solidity; +using namespace std; +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +namespace +{ + +void setupTerminal() +{ +#if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) + // Set output mode to handle virtual terminal (ANSI escape sequences) + // ignore any error, as this is just a "nice-to-have" + // only windows needs to be taken care of, as other platforms (Linux/OSX) support them natively. + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return; + + DWORD dwMode = 0; + if (!GetConsoleMode(hOut, &dwMode)) + return; + + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(hOut, dwMode)) + return; +#endif +} + +} + +int main(int argc, char** argv) +{ + setupTerminal(); + + tools::SourceUpgrade upgrade; + if (!upgrade.parseArguments(argc, argv)) + return 1; + upgrade.printPrologue(); + if (!upgrade.processInput()) + return 1; + + return 0; +} From 5247a6600edee11a5e2b44925396ad35be71d060 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 5 Feb 2020 16:59:02 +0100 Subject: [PATCH 073/160] Fix function call option parsing and add to grammar. --- docs/grammar.txt | 1 + libsolidity/parsing/Parser.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index 490e53bdad9b..b8b2893a22c5 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -96,6 +96,7 @@ Expression | IndexRangeAccess | MemberAccess | FunctionCall + | Expression '{' NameValueList '}' | '(' Expression ')' | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression | Expression '**' Expression diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 9f165c75c9c8..c6f139dbfdf4 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1768,7 +1768,7 @@ ASTPointer Parser::parseLeftHandSideExpression( nodeFactory.markEndPosition(); expectToken(Token::RBrace); - expression = parseLeftHandSideExpression(nodeFactory.createNode(expression, optionList.first, optionList.second)); + expression = nodeFactory.createNode(expression, optionList.first, optionList.second); break; } default: From e7d204383de9efe5d0052715c744dd5bcb531b7d Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 17 Jan 2020 07:19:05 +0100 Subject: [PATCH 074/160] [yul-phaser] An empty command-line application for the new tool --- tools/CMakeLists.txt | 6 ++++++ tools/yulPhaser/main.cpp | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tools/yulPhaser/main.cpp diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index bdcf615afedd..8a71a1750499 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -12,3 +12,9 @@ target_link_libraries(solidity-upgrade PRIVATE solidity Boost::boost Boost::prog include(GNUInstallDirs) install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(yul-phaser + yulPhaser/main.cpp +) + +install(TARGETS yul-phaser DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp new file mode 100644 index 000000000000..4c24c8240537 --- /dev/null +++ b/tools/yulPhaser/main.cpp @@ -0,0 +1,21 @@ +/* + 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 . +*/ + +int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) +{ + return 0; +} From b75370d93eceaeb862c9beb1fd2bdf2c5ae52a7e Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 17 Jan 2020 07:24:18 +0100 Subject: [PATCH 075/160] [yul-phaser] Printing help and accepting input file on the command line --- tools/CMakeLists.txt | 1 + tools/yulPhaser/main.cpp | 86 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 8a71a1750499..19764052a1e1 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -16,5 +16,6 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp ) +target_link_libraries(yul-phaser PRIVATE Boost::program_options) install(TARGETS yul-phaser DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 4c24c8240537..66943378d0aa 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -15,7 +15,91 @@ along with solidity. If not, see . */ -int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) + +#include + +#include +#include + +using namespace std; +namespace po = boost::program_options; + +namespace +{ + +struct CommandLineParsingResult +{ + int exitCode; + po::variables_map arguments; +}; + +void runAlgorithm(string const& _sourcePath) +{ + cout << "Input: " << _sourcePath << endl; +} + +CommandLineParsingResult parseCommandLine(int argc, char** argv) +{ + po::options_description description( + "yul-phaser, a tool for finding the best sequence of Yul optimisation phases.\n" + "\n" + "Usage: yul-phaser [options] \n" + "Reads as Yul code and tries to find the best order in which to run optimisation" + " phases using a genetic algorithm.\n" + "Example:\n" + "yul-phaser program.yul\n" + "\n" + "Allowed options", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23 + ); + + description.add_options() + ("help", "Show help message and exit.") + ("input-file", po::value()->required(), "Input file") + ; + + po::positional_options_description positionalDescription; + po::variables_map arguments; + positionalDescription.add("input-file", 1); + po::notify(arguments); + + try + { + po::command_line_parser parser(argc, argv); + parser.options(description).positional(positionalDescription); + po::store(parser.run(), arguments); + } + catch (po::error const & _exception) + { + cerr << _exception.what() << endl; + return {1, move(arguments)}; + } + + if (arguments.count("help") > 0) + { + cout << description << endl; + return {2, move(arguments)}; + } + + if (arguments.count("input-file") == 0) + { + cerr << "Missing argument: input-file." << endl; + return {1, move(arguments)}; + } + + return {0, arguments}; +} + +} + +int main(int argc, char** argv) { + CommandLineParsingResult parsingResult = parseCommandLine(argc, argv); + if (parsingResult.exitCode != 0) + return parsingResult.exitCode; + + runAlgorithm(parsingResult.arguments["input-file"].as()); + return 0; } From 513d41c315d392471662e7aae1c39670f01ae0ac Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 17 Jan 2020 07:34:18 +0100 Subject: [PATCH 076/160] [yul-phaser] Add Program class --- tools/CMakeLists.txt | 4 +- tools/yulPhaser/Exceptions.h | 27 +++++++ tools/yulPhaser/Program.cpp | 140 +++++++++++++++++++++++++++++++++++ tools/yulPhaser/Program.h | 108 +++++++++++++++++++++++++++ tools/yulPhaser/main.cpp | 17 ++++- 5 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 tools/yulPhaser/Exceptions.h create mode 100644 tools/yulPhaser/Program.cpp create mode 100644 tools/yulPhaser/Program.h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 19764052a1e1..acd49c59bf5b 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -15,7 +15,9 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp + yulPhaser/Program.h + yulPhaser/Program.cpp ) -target_link_libraries(yul-phaser PRIVATE Boost::program_options) +target_link_libraries(yul-phaser PRIVATE solidity Boost::program_options) install(TARGETS yul-phaser DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/tools/yulPhaser/Exceptions.h b/tools/yulPhaser/Exceptions.h new file mode 100644 index 000000000000..ae75d19ef9c6 --- /dev/null +++ b/tools/yulPhaser/Exceptions.h @@ -0,0 +1,27 @@ +/* + 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 . +*/ + +#pragma once + +#include + +namespace solidity::phaser +{ + +struct InvalidProgram: virtual util::Exception {}; + +} diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp new file mode 100644 index 000000000000..3a167755f77c --- /dev/null +++ b/tools/yulPhaser/Program.cpp @@ -0,0 +1,140 @@ +/* + 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 . +*/ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::yul; +using namespace solidity::util; +using namespace solidity::phaser; + +set const Program::s_externallyUsedIdentifiers = {}; + +Program Program::load(string const& _sourcePath) +{ + Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); + shared_ptr ast = parseSource(dialect, loadSource(_sourcePath)); + unique_ptr analysisInfo = analyzeAST(dialect, *ast); + + Program program( + dialect, + disambiguateAST(dialect, *ast, *analysisInfo) + ); + program.optimise({ + FunctionHoister::name, + FunctionGrouper::name, + ForLoopInitRewriter::name, + }); + + return program; +} + +void Program::optimise(vector const& _optimisationSteps) +{ + applyOptimisationSteps(m_optimiserStepContext, *m_ast, _optimisationSteps); +} + +CharStream Program::loadSource(string const& _sourcePath) +{ + assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); + + string sourceCode = readFileAsString(_sourcePath); + return CharStream(sourceCode, _sourcePath); +} + +shared_ptr Program::parseSource(Dialect const& _dialect, CharStream _source) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared(move(_source)); + Parser parser(errorReporter, _dialect); + + shared_ptr ast = parser.parse(scanner, false); + assertThrow(ast != nullptr, InvalidProgram, "Error parsing source"); + assert(errorReporter.errors().empty()); + + return ast; +} + +unique_ptr Program::analyzeAST(Dialect const& _dialect, Block const& _ast) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + auto analysisInfo = make_unique(); + AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect); + + bool analysisSuccessful = analyzer.analyze(_ast); + assertThrow(analysisSuccessful, InvalidProgram, "Error analyzing source"); + assert(errorReporter.errors().empty()); + + return analysisInfo; +} + +unique_ptr Program::disambiguateAST( + Dialect const& _dialect, + Block const& _ast, + AsmAnalysisInfo const& _analysisInfo +) +{ + Disambiguator disambiguator(_dialect, _analysisInfo, s_externallyUsedIdentifiers); + + return make_unique(get(disambiguator(_ast))); +} + +void Program::applyOptimisationSteps( + OptimiserStepContext& _context, + Block& _ast, + vector const& _optimisationSteps +) +{ + for (string const& step: _optimisationSteps) + OptimiserSuite::allSteps().at(step)->run(_context, _ast); +} + +size_t Program::computeCodeSize(Block const& _ast) +{ + return CodeSize::codeSizeIncludingFunctions(_ast); +} diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h new file mode 100644 index 000000000000..575dc29c4424 --- /dev/null +++ b/tools/yulPhaser/Program.h @@ -0,0 +1,108 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace solidity::langutil +{ + +class CharStream; + +} + +namespace solidity::yul +{ + +struct AsmAnalysisInfo; +struct Block; +struct Dialect; +class YulString; + +} + +namespace solidity::phaser +{ + +/** + * Class representing parsed and analysed Yul program that we can apply optimisations to. + * The program is already disambiguated and has several prerequisite optimiser steps applied to it + * so that the requirements of any possible step that could be later applied by the user are + * already satisfied. + * + * The class allows the user to apply extra optimisations and obtain metrics and general + * information about the resulting syntax tree. + */ +class Program +{ +public: + static Program load(std::string const& _sourcePath); + void optimise(std::vector const& _optimisationSteps); + + size_t codeSize() const { return computeCodeSize(*m_ast); } + yul::Block const& ast() const { return *m_ast; } + +private: + Program( + yul::Dialect const& _dialect, + std::shared_ptr _ast + ): + m_ast(_ast), + m_nameDispenser(_dialect, *_ast, s_externallyUsedIdentifiers), + m_optimiserStepContext{_dialect, m_nameDispenser, s_externallyUsedIdentifiers} + {} + + static langutil::CharStream loadSource(std::string const& _sourcePath); + static std::shared_ptr parseSource( + yul::Dialect const& _dialect, + langutil::CharStream _source + ); + static std::unique_ptr analyzeAST( + yul::Dialect const& _dialect, + yul::Block const& _ast + ); + static std::unique_ptr disambiguateAST( + yul::Dialect const& _dialect, + yul::Block const& _ast, + yul::AsmAnalysisInfo const& _analysisInfo + ); + static void applyOptimisationSteps( + yul::OptimiserStepContext& _context, + yul::Block& _ast, + std::vector const& _optimisationSteps + ); + static size_t computeCodeSize(yul::Block const& _ast); + + std::shared_ptr m_ast; + yul::NameDispenser m_nameDispenser; + yul::OptimiserStepContext m_optimiserStepContext; + + // Always empty set of reserved identifiers. It could be a constructor parameter but I don't + // think it would be useful in this tool. Other tools (like yulopti) have it empty too. + // We need it to live as long as m_optimiserStepContext because it stores a reference + // but since it's empty and read-only we can just have all objects share one static instance. + static std::set const s_externallyUsedIdentifiers; +}; + +} diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 66943378d0aa..6814acf6e3f6 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -15,6 +15,8 @@ along with solidity. If not, see . */ +#include +#include #include @@ -22,6 +24,8 @@ #include using namespace std; +using namespace solidity::phaser; + namespace po = boost::program_options; namespace @@ -35,7 +39,8 @@ struct CommandLineParsingResult void runAlgorithm(string const& _sourcePath) { - cout << "Input: " << _sourcePath << endl; + Program::load(_sourcePath); + cout << "Program load successful." << endl; } CommandLineParsingResult parseCommandLine(int argc, char** argv) @@ -99,7 +104,15 @@ int main(int argc, char** argv) if (parsingResult.exitCode != 0) return parsingResult.exitCode; - runAlgorithm(parsingResult.arguments["input-file"].as()); + try + { + runAlgorithm(parsingResult.arguments["input-file"].as()); + } + catch (InvalidProgram const& _exception) + { + cerr << "ERROR: " << _exception.what() << endl; + return 1; + } return 0; } From 14d726ff013a7eeba6d15396b4e8a3c649d92a18 Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 22 Jan 2020 17:33:36 +0100 Subject: [PATCH 077/160] [yul-phaser] Program: Use unique_ptr rather than shared_ptr to hold a pointer to the AST root - The class never shares the instance so unique_ptr makes more sense. --- tools/yulPhaser/Program.cpp | 7 +++---- tools/yulPhaser/Program.h | 12 ++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 3a167755f77c..36c7d9204836 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -56,7 +55,7 @@ set const Program::s_externallyUsedIdentifiers = {}; Program Program::load(string const& _sourcePath) { Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - shared_ptr ast = parseSource(dialect, loadSource(_sourcePath)); + unique_ptr ast = parseSource(dialect, loadSource(_sourcePath)); unique_ptr analysisInfo = analyzeAST(dialect, *ast); Program program( @@ -85,14 +84,14 @@ CharStream Program::loadSource(string const& _sourcePath) return CharStream(sourceCode, _sourcePath); } -shared_ptr Program::parseSource(Dialect const& _dialect, CharStream _source) +unique_ptr Program::parseSource(Dialect const& _dialect, CharStream _source) { ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(move(_source)); Parser parser(errorReporter, _dialect); - shared_ptr ast = parser.parse(scanner, false); + unique_ptr ast = parser.parse(scanner, false); assertThrow(ast != nullptr, InvalidProgram, "Error parsing source"); assert(errorReporter.errors().empty()); diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index 575dc29c4424..b4245fa9cb5e 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -36,7 +37,6 @@ namespace solidity::yul { struct AsmAnalysisInfo; -struct Block; struct Dialect; class YulString; @@ -66,15 +66,15 @@ class Program private: Program( yul::Dialect const& _dialect, - std::shared_ptr _ast + std::unique_ptr _ast ): - m_ast(_ast), - m_nameDispenser(_dialect, *_ast, s_externallyUsedIdentifiers), + m_ast(std::move(_ast)), + m_nameDispenser(_dialect, *m_ast, s_externallyUsedIdentifiers), m_optimiserStepContext{_dialect, m_nameDispenser, s_externallyUsedIdentifiers} {} static langutil::CharStream loadSource(std::string const& _sourcePath); - static std::shared_ptr parseSource( + static std::unique_ptr parseSource( yul::Dialect const& _dialect, langutil::CharStream _source ); @@ -94,7 +94,7 @@ class Program ); static size_t computeCodeSize(yul::Block const& _ast); - std::shared_ptr m_ast; + std::unique_ptr m_ast; yul::NameDispenser m_nameDispenser; yul::OptimiserStepContext m_optimiserStepContext; From 2aa42b32e5663acaa93fa50d131e0639e237f0d5 Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 22 Jan 2020 19:38:19 +0100 Subject: [PATCH 078/160] [yul-phaser] Make Program noncopyable - Copying worked but resulted in OptimiserStepContext having a reference to NameDispenser instance of the other object. --- tools/yulPhaser/Program.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index b4245fa9cb5e..f039ddcc500a 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -21,6 +21,8 @@ #include #include +#include + #include #include #include @@ -54,9 +56,17 @@ namespace solidity::phaser * The class allows the user to apply extra optimisations and obtain metrics and general * information about the resulting syntax tree. */ -class Program +class Program: private boost::noncopyable { public: + Program(Program&& program): + m_ast(std::move(program.m_ast)), + m_nameDispenser(std::move(program.m_nameDispenser)), + // Creating a new instance because a copied one would have a dangling reference to the old dispenser + m_optimiserStepContext{program.m_optimiserStepContext.dialect, m_nameDispenser, s_externallyUsedIdentifiers} + {} + Program operator=(Program&& program) = delete; + static Program load(std::string const& _sourcePath); void optimise(std::vector const& _optimisationSteps); From 3baa191b9463ee20d6977d1487728b2d21aee5df Mon Sep 17 00:00:00 2001 From: cameel Date: Thu, 23 Jan 2020 10:27:50 +0100 Subject: [PATCH 079/160] [yul-phaser] Printing and JSON conversion for the Program class --- tools/yulPhaser/Program.cpp | 22 +++++++++++++++++++++- tools/yulPhaser/Program.h | 4 ++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 36c7d9204836..271c047aaec6 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -25,7 +25,9 @@ #include #include +#include #include +#include #include #include #include @@ -33,10 +35,10 @@ #include #include #include - #include #include +#include #include @@ -50,6 +52,13 @@ using namespace solidity::yul; using namespace solidity::util; using namespace solidity::phaser; +namespace solidity::phaser +{ + +ostream& operator<<(ostream& _stream, Program const& _program); + +} + set const Program::s_externallyUsedIdentifiers = {}; Program Program::load(string const& _sourcePath) @@ -76,6 +85,17 @@ void Program::optimise(vector const& _optimisationSteps) applyOptimisationSteps(m_optimiserStepContext, *m_ast, _optimisationSteps); } +ostream& phaser::operator<<(ostream& _stream, Program const& _program) +{ + return _stream << AsmPrinter()(*_program.m_ast); +} + +string Program::toJson() const +{ + Json::Value serializedAst = AsmJsonConverter(0)(*m_ast); + return jsonPrettyPrint(serializedAst); +} + CharStream Program::loadSource(string const& _sourcePath) { assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index f039ddcc500a..3648d6e2177d 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -73,6 +74,9 @@ class Program: private boost::noncopyable size_t codeSize() const { return computeCodeSize(*m_ast); } yul::Block const& ast() const { return *m_ast; } + friend std::ostream& operator<<(std::ostream& _stream, Program const& _program); + std::string toJson() const; + private: Program( yul::Dialect const& _dialect, From 21a2b69f7442708a2c07253f307cd112b0a33d69 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 24 Jan 2020 23:22:01 +0100 Subject: [PATCH 080/160] [yul-phaser] Create OptimiserStepContext on demand instead of storing it in Program class - This also lets us get rid of the static s_externallyUsedIdentifiers. --- tools/yulPhaser/Program.cpp | 18 ++++++++++++------ tools/yulPhaser/Program.h | 22 +++++++--------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 271c047aaec6..459fca2e68be 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -59,8 +60,6 @@ ostream& operator<<(ostream& _stream, Program const& _program); } -set const Program::s_externallyUsedIdentifiers = {}; - Program Program::load(string const& _sourcePath) { Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); @@ -82,7 +81,7 @@ Program Program::load(string const& _sourcePath) void Program::optimise(vector const& _optimisationSteps) { - applyOptimisationSteps(m_optimiserStepContext, *m_ast, _optimisationSteps); + applyOptimisationSteps(m_dialect, m_nameDispenser, *m_ast, _optimisationSteps); } ostream& phaser::operator<<(ostream& _stream, Program const& _program) @@ -138,19 +137,26 @@ unique_ptr Program::disambiguateAST( AsmAnalysisInfo const& _analysisInfo ) { - Disambiguator disambiguator(_dialect, _analysisInfo, s_externallyUsedIdentifiers); + set const externallyUsedIdentifiers = {}; + Disambiguator disambiguator(_dialect, _analysisInfo, externallyUsedIdentifiers); return make_unique(get(disambiguator(_ast))); } void Program::applyOptimisationSteps( - OptimiserStepContext& _context, + Dialect const& _dialect, + NameDispenser& _nameDispenser, Block& _ast, vector const& _optimisationSteps ) { + // An empty set of reserved identifiers. It could be a constructor parameter but I don't + // think it would be useful in this tool. Other tools (like yulopti) have it empty too. + set const externallyUsedIdentifiers = {}; + OptimiserStepContext context{_dialect, _nameDispenser, externallyUsedIdentifiers}; + for (string const& step: _optimisationSteps) - OptimiserSuite::allSteps().at(step)->run(_context, _ast); + OptimiserSuite::allSteps().at(step)->run(context, _ast); } size_t Program::computeCodeSize(Block const& _ast) diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index 3648d6e2177d..bb382b37496e 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -17,7 +17,6 @@ #pragma once -#include #include #include @@ -41,7 +40,6 @@ namespace solidity::yul struct AsmAnalysisInfo; struct Dialect; -class YulString; } @@ -62,9 +60,8 @@ class Program: private boost::noncopyable public: Program(Program&& program): m_ast(std::move(program.m_ast)), - m_nameDispenser(std::move(program.m_nameDispenser)), - // Creating a new instance because a copied one would have a dangling reference to the old dispenser - m_optimiserStepContext{program.m_optimiserStepContext.dialect, m_nameDispenser, s_externallyUsedIdentifiers} + m_dialect{program.m_dialect}, + m_nameDispenser(std::move(program.m_nameDispenser)) {} Program operator=(Program&& program) = delete; @@ -83,8 +80,8 @@ class Program: private boost::noncopyable std::unique_ptr _ast ): m_ast(std::move(_ast)), - m_nameDispenser(_dialect, *m_ast, s_externallyUsedIdentifiers), - m_optimiserStepContext{_dialect, m_nameDispenser, s_externallyUsedIdentifiers} + m_dialect{_dialect}, + m_nameDispenser(_dialect, *m_ast, {}) {} static langutil::CharStream loadSource(std::string const& _sourcePath); @@ -102,21 +99,16 @@ class Program: private boost::noncopyable yul::AsmAnalysisInfo const& _analysisInfo ); static void applyOptimisationSteps( - yul::OptimiserStepContext& _context, + yul::Dialect const& _dialect, + yul::NameDispenser& _nameDispenser, yul::Block& _ast, std::vector const& _optimisationSteps ); static size_t computeCodeSize(yul::Block const& _ast); std::unique_ptr m_ast; + yul::Dialect const& m_dialect; yul::NameDispenser m_nameDispenser; - yul::OptimiserStepContext m_optimiserStepContext; - - // Always empty set of reserved identifiers. It could be a constructor parameter but I don't - // think it would be useful in this tool. Other tools (like yulopti) have it empty too. - // We need it to live as long as m_optimiserStepContext because it stores a reference - // but since it's empty and read-only we can just have all objects share one static instance. - static std::set const s_externallyUsedIdentifiers; }; } From 57ab8922cd387f7d86359b5d05d6f8c67fb645ff Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 17 Jan 2020 07:42:43 +0100 Subject: [PATCH 081/160] [yul-phaser] Add random number generators using Mersenne Twister from boost --- tools/CMakeLists.txt | 2 ++ tools/yulPhaser/Random.cpp | 44 ++++++++++++++++++++++++++++++++++++++ tools/yulPhaser/Random.h | 28 ++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 tools/yulPhaser/Random.cpp create mode 100644 tools/yulPhaser/Random.h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index acd49c59bf5b..ecfece5d02ee 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -17,6 +17,8 @@ add_executable(yul-phaser yulPhaser/main.cpp yulPhaser/Program.h yulPhaser/Program.cpp + yulPhaser/Random.h + yulPhaser/Random.cpp ) target_link_libraries(yul-phaser PRIVATE solidity Boost::program_options) diff --git a/tools/yulPhaser/Random.cpp b/tools/yulPhaser/Random.cpp new file mode 100644 index 000000000000..645afb39539e --- /dev/null +++ b/tools/yulPhaser/Random.cpp @@ -0,0 +1,44 @@ +/* + 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 . +*/ + +#include + +#include +#include +#include + +#include + +using namespace solidity; + +uint32_t phaser::uniformRandomInt(uint32_t _min, uint32_t _max) +{ + // TODO: Seed must be configurable + static boost::random::mt19937 generator(time(0)); + boost::random::uniform_int_distribution<> distribution(_min, _max); + + return distribution(generator); +} + +uint32_t phaser::binomialRandomInt(uint32_t _numTrials, double _successProbability) +{ + // TODO: Seed must be configurable + static boost::random::mt19937 generator(time(0)); + boost::random::binomial_distribution<> distribution(_numTrials, _successProbability); + + return distribution(generator); +} diff --git a/tools/yulPhaser/Random.h b/tools/yulPhaser/Random.h new file mode 100644 index 000000000000..25091ddef7ec --- /dev/null +++ b/tools/yulPhaser/Random.h @@ -0,0 +1,28 @@ +/* + 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 . +*/ + +#pragma once + +#include + +namespace solidity::phaser +{ + +uint32_t uniformRandomInt(uint32_t _min, uint32_t _max); +uint32_t binomialRandomInt(uint32_t _numTrials, double _successProbability); + +} From f6d955db0bcf75abc6595e017229d21ea73fd640 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 17 Jan 2020 07:41:03 +0100 Subject: [PATCH 082/160] [yul-phaser] Add Chromosome class --- tools/CMakeLists.txt | 2 + tools/yulPhaser/Chromosome.cpp | 112 +++++++++++++++++++++++++++++++++ tools/yulPhaser/Chromosome.h | 65 +++++++++++++++++++ tools/yulPhaser/main.cpp | 5 +- 4 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 tools/yulPhaser/Chromosome.cpp create mode 100644 tools/yulPhaser/Chromosome.h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index ecfece5d02ee..6b98f92c5c45 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -15,6 +15,8 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp + yulPhaser/Chromosome.h + yulPhaser/Chromosome.cpp yulPhaser/Program.h yulPhaser/Program.cpp yulPhaser/Random.h diff --git a/tools/yulPhaser/Chromosome.cpp b/tools/yulPhaser/Chromosome.cpp new file mode 100644 index 000000000000..6e887bd11ec4 --- /dev/null +++ b/tools/yulPhaser/Chromosome.cpp @@ -0,0 +1,112 @@ +/* + 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 . +*/ + +#include + +#include + +#include +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::util; +using namespace solidity::yul; +using namespace solidity::phaser; + +namespace solidity::phaser +{ + +ostream& operator<<(ostream& _stream, Chromosome const& _chromosome); + +} + +Chromosome Chromosome::makeRandom(size_t _length) +{ + vector steps; + for (size_t i = 0; i < _length; ++i) + steps.push_back(randomOptimisationStep()); + + return Chromosome(move(steps)); +} + +ostream& phaser::operator<<(ostream& _stream, Chromosome const& _chromosome) +{ + for (auto const& stepName: _chromosome.m_optimisationSteps) + _stream << OptimiserSuite::stepNameToAbbreviationMap().at(stepName); + + return _stream; +} + +vector Chromosome::allStepNames() +{ + vector stepNames; + for (auto const& step: OptimiserSuite::allSteps()) + stepNames.push_back(step.first); + + return stepNames; +} + +vector Chromosome::allStepNamesExcept(vector const& _excludedStepNames) +{ + // This is not very efficient but vectors are small and the caller will cache the results anyway. + // What matters a bit more is that using vector rather than a set gives us O(1) access to + // random elements in other functions. + return convertContainer>( + convertContainer>(allStepNames()) - + convertContainer>(_excludedStepNames) + ); +} + +string const& Chromosome::randomOptimisationStep() +{ + static vector stepNames = allStepNamesExcept({ + // All possible steps, listed and commented-out for easy tweaking. + // The uncommented ones are not used (possibly because they fail). + //{BlockFlattener::name}, + //{CommonSubexpressionEliminator::name}, + //{ConditionalSimplifier::name}, + //{ConditionalUnsimplifier::name}, + //{ControlFlowSimplifier::name}, + //{DeadCodeEliminator::name}, + //{EquivalentFunctionCombiner::name}, + //{ExpressionInliner::name}, + //{ExpressionJoiner::name}, + //{ExpressionSimplifier::name}, + //{ExpressionSplitter::name}, + //{ForLoopConditionIntoBody::name}, + //{ForLoopConditionOutOfBody::name}, + //{ForLoopInitRewriter::name}, + //{FullInliner::name}, + //{FunctionGrouper::name}, + //{FunctionHoister::name}, + //{LiteralRematerialiser::name}, + //{LoadResolver::name}, + //{LoopInvariantCodeMotion::name}, + //{RedundantAssignEliminator::name}, + //{Rematerialiser::name}, + //{SSAReverser::name}, + //{SSATransform::name}, + //{StructuralSimplifier::name}, + //{UnusedPruner::name}, + //{VarDeclInitializer::name}, + }); + + return stepNames[uniformRandomInt(0, stepNames.size() - 1)]; +} diff --git a/tools/yulPhaser/Chromosome.h b/tools/yulPhaser/Chromosome.h new file mode 100644 index 000000000000..84b33d356202 --- /dev/null +++ b/tools/yulPhaser/Chromosome.h @@ -0,0 +1,65 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include +#include +#include + +namespace solidity::phaser +{ + +/** + * An object that represents a sequence of optimiser steps that can be applied to a program. + * Such sequences are used in our genetic algorithm to represent individual members of the + * population. + * + * To calculate the fitness of an individual one must apply its sequence to a specific program. + * This class does not provide any means to do so. It just stores information. + * + * Once created a sequence cannot be changed. The only way to mutate it is to generate a new + * chromosome based on the old one. + */ +class Chromosome +{ +public: + Chromosome() = default; + explicit Chromosome(std::vector _optimisationSteps): + m_optimisationSteps(std::move(_optimisationSteps)) {} + static Chromosome makeRandom(size_t _length); + + size_t length() const { return m_optimisationSteps.size(); } + std::vector const& optimisationSteps() const { return m_optimisationSteps; } + + friend std::ostream& operator<<(std::ostream& _stream, Chromosome const& _chromosome); + + bool operator==(Chromosome const& _other) const { return m_optimisationSteps == _other.m_optimisationSteps; } + bool operator!=(Chromosome const& _other) const { return !(*this == _other); } + +private: + static std::vector allStepNames(); + static std::vector allStepNamesExcept( + std::vector const& _excludedStepNames + ); + static std::string const& randomOptimisationStep(); + + std::vector m_optimisationSteps; +}; + +} diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 6814acf6e3f6..8bf32b1353ce 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -15,6 +15,7 @@ along with solidity. If not, see . */ +#include #include #include @@ -39,8 +40,8 @@ struct CommandLineParsingResult void runAlgorithm(string const& _sourcePath) { - Program::load(_sourcePath); - cout << "Program load successful." << endl; + Program::load(_sourcePath).optimize(Chromosome::makeRandom(15).asSequence()); + cout << "Program load and optimization successful." << endl; } CommandLineParsingResult parseCommandLine(int argc, char** argv) From f0fb046038c5d4d45d544181ebf03a196424fe28 Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 29 Jan 2020 20:00:16 +0100 Subject: [PATCH 083/160] [yul-phaser] Remove Chromosome::allStepNamesExcept() --- tools/yulPhaser/Chromosome.cpp | 44 +--------------------------------- tools/yulPhaser/Chromosome.h | 3 --- 2 files changed, 1 insertion(+), 46 deletions(-) diff --git a/tools/yulPhaser/Chromosome.cpp b/tools/yulPhaser/Chromosome.cpp index 6e887bd11ec4..b332266ba354 100644 --- a/tools/yulPhaser/Chromosome.cpp +++ b/tools/yulPhaser/Chromosome.cpp @@ -26,7 +26,6 @@ using namespace std; using namespace solidity; -using namespace solidity::util; using namespace solidity::yul; using namespace solidity::phaser; @@ -63,50 +62,9 @@ vector Chromosome::allStepNames() return stepNames; } -vector Chromosome::allStepNamesExcept(vector const& _excludedStepNames) -{ - // This is not very efficient but vectors are small and the caller will cache the results anyway. - // What matters a bit more is that using vector rather than a set gives us O(1) access to - // random elements in other functions. - return convertContainer>( - convertContainer>(allStepNames()) - - convertContainer>(_excludedStepNames) - ); -} - string const& Chromosome::randomOptimisationStep() { - static vector stepNames = allStepNamesExcept({ - // All possible steps, listed and commented-out for easy tweaking. - // The uncommented ones are not used (possibly because they fail). - //{BlockFlattener::name}, - //{CommonSubexpressionEliminator::name}, - //{ConditionalSimplifier::name}, - //{ConditionalUnsimplifier::name}, - //{ControlFlowSimplifier::name}, - //{DeadCodeEliminator::name}, - //{EquivalentFunctionCombiner::name}, - //{ExpressionInliner::name}, - //{ExpressionJoiner::name}, - //{ExpressionSimplifier::name}, - //{ExpressionSplitter::name}, - //{ForLoopConditionIntoBody::name}, - //{ForLoopConditionOutOfBody::name}, - //{ForLoopInitRewriter::name}, - //{FullInliner::name}, - //{FunctionGrouper::name}, - //{FunctionHoister::name}, - //{LiteralRematerialiser::name}, - //{LoadResolver::name}, - //{LoopInvariantCodeMotion::name}, - //{RedundantAssignEliminator::name}, - //{Rematerialiser::name}, - //{SSAReverser::name}, - //{SSATransform::name}, - //{StructuralSimplifier::name}, - //{UnusedPruner::name}, - //{VarDeclInitializer::name}, - }); + static vector stepNames = allStepNames(); return stepNames[uniformRandomInt(0, stepNames.size() - 1)]; } diff --git a/tools/yulPhaser/Chromosome.h b/tools/yulPhaser/Chromosome.h index 84b33d356202..6db99dc4e009 100644 --- a/tools/yulPhaser/Chromosome.h +++ b/tools/yulPhaser/Chromosome.h @@ -54,9 +54,6 @@ class Chromosome private: static std::vector allStepNames(); - static std::vector allStepNamesExcept( - std::vector const& _excludedStepNames - ); static std::string const& randomOptimisationStep(); std::vector m_optimisationSteps; From 7b7c88ae95c85611ffcf5e23973d66b4bceaaf01 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 17 Jan 2020 07:45:10 +0100 Subject: [PATCH 084/160] [yul-phaser] Add Population class --- tools/CMakeLists.txt | 2 + tools/yulPhaser/Population.cpp | 137 +++++++++++++++++++++++++++++++++ tools/yulPhaser/Population.h | 87 +++++++++++++++++++++ tools/yulPhaser/main.cpp | 7 +- 4 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 tools/yulPhaser/Population.cpp create mode 100644 tools/yulPhaser/Population.h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 6b98f92c5c45..a2aa807af414 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -15,6 +15,8 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp + yulPhaser/Population.h + yulPhaser/Population.cpp yulPhaser/Chromosome.h yulPhaser/Chromosome.cpp yulPhaser/Program.h diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp new file mode 100644 index 000000000000..a5a65fa7fc99 --- /dev/null +++ b/tools/yulPhaser/Population.cpp @@ -0,0 +1,137 @@ +/* + 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 . +*/ + +#include + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::phaser; + +namespace solidity::phaser +{ + +ostream& operator<<(ostream& _stream, Individual const& _individual); +ostream& operator<<(ostream& _stream, Population const& _population); + +} + +ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) +{ + _stream << "Fitness: "; + if (_individual.fitness.has_value()) + _stream << _individual.fitness.value(); + else + _stream << ""; + _stream << ", optimisations: " << _individual.chromosome; + + return _stream; +} + +Population::Population(string const& _sourcePath, vector const& _chromosomes): + m_sourcePath{_sourcePath} +{ + for (auto const& chromosome: _chromosomes) + m_individuals.push_back({chromosome}); +} + +Population Population::makeRandom(string const& _sourcePath, size_t _size) +{ + vector individuals; + for (size_t i = 0; i < _size; ++i) + individuals.push_back({Chromosome::makeRandom(randomChromosomeLength())}); + + return Population(_sourcePath, individuals); +} + +size_t Population::measureFitness(Chromosome const& _chromosome, string const& _sourcePath) +{ + auto program = Program::load(_sourcePath); + program.optimise(_chromosome.optimisationSteps()); + return program.codeSize(); +} + +void Population::run(optional _numRounds, ostream& _outputStream) +{ + doEvaluation(); + for (size_t round = 0; !_numRounds.has_value() || round < _numRounds.value(); ++round) + { + doMutation(); + doSelection(); + doEvaluation(); + + _outputStream << "---------- ROUND " << round << " ----------" << endl; + _outputStream << *this; + } +} + +ostream& phaser::operator<<(ostream& _stream, Population const& _population) +{ + _stream << "Source: " << _population.m_sourcePath << endl; + + auto individual = _population.m_individuals.begin(); + for (; individual != _population.m_individuals.end(); ++individual) + _stream << *individual << endl; + + return _stream; +} + +void Population::doMutation() +{ + // TODO: Implement mutation and crossover +} + +void Population::doEvaluation() +{ + for (auto& individual: m_individuals) + if (!individual.fitness.has_value()) + individual.fitness = measureFitness(individual.chromosome, m_sourcePath); +} + +void Population::doSelection() +{ + assert(all_of(m_individuals.begin(), m_individuals.end(), [](auto& i){ return i.fitness.has_value(); })); + + sort( + m_individuals.begin(), + m_individuals.end(), + [](auto const& a, auto const& b){ return a.fitness.value() < b.fitness.value(); } + ); + + randomizeWorstChromosomes(m_individuals, m_individuals.size() / 2); +} + +void Population::randomizeWorstChromosomes( + vector& _individuals, + size_t _count +) +{ + assert(_individuals.size() >= _count); + // ASSUMPTION: _individuals is sorted in ascending order + + auto individual = _individuals.begin() + (_individuals.size() - _count); + for (; individual != _individuals.end(); ++individual) + { + *individual = {Chromosome::makeRandom(randomChromosomeLength())}; + } +} diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h new file mode 100644 index 000000000000..3c5596b034da --- /dev/null +++ b/tools/yulPhaser/Population.h @@ -0,0 +1,87 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace solidity::phaser +{ + +/** + * Information describing the state of an individual member of the population during the course + * of the genetic algorithm. + */ +struct Individual +{ + Chromosome chromosome; + std::optional fitness = std::nullopt; + + friend std::ostream& operator<<(std::ostream& _stream, Individual const& _individual); +}; + +/** + * Represents a changing set of individuals undergoing a genetic algorithm. + * Each round of the algorithm involves mutating existing individuals, evaluating their fitness + * and selecting the best ones for the next round. + * + * An individual is a sequence of optimiser steps represented by a @a Chromosome instance. The whole + * population is associated with a fixed Yul program. By loading the source code into a @a Program + * instance the class can compute fitness of the individual. + */ +class Population +{ +public: + static constexpr size_t MaxChromosomeLength = 30; + + explicit Population(std::string const& _sourcePath, std::vector const& _chromosomes = {}); + static Population makeRandom(std::string const& _sourcePath, size_t _size); + + void run(std::optional _numRounds, std::ostream& _outputStream); + + std::vector const& individuals() const { return m_individuals; } + + static size_t randomChromosomeLength() { return binomialRandomInt(MaxChromosomeLength, 0.5); } + static size_t measureFitness(Chromosome const& _chromosome, std::string const& _sourcePath); + + friend std::ostream& operator<<(std::ostream& _stream, Population const& _population); + +private: + explicit Population(std::string const& _sourcePath, std::vector _individuals = {}): + m_sourcePath{_sourcePath}, + m_individuals{std::move(_individuals)} {} + + void doMutation(); + void doEvaluation(); + void doSelection(); + + static void randomizeWorstChromosomes( + std::vector& _individuals, + size_t _count + ); + + std::string m_sourcePath; + + std::vector m_individuals; +}; + +} diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 8bf32b1353ce..280262d7cd4b 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -15,9 +15,8 @@ along with solidity. If not, see . */ -#include #include -#include +#include #include @@ -40,8 +39,8 @@ struct CommandLineParsingResult void runAlgorithm(string const& _sourcePath) { - Program::load(_sourcePath).optimize(Chromosome::makeRandom(15).asSequence()); - cout << "Program load and optimization successful." << endl; + auto population = Population::makeRandom(_sourcePath, 10); + population.run(nullopt, cout); } CommandLineParsingResult parseCommandLine(int argc, char** argv) From 785f65d0f511b8a6bda589b2a77c45e6b73dc63b Mon Sep 17 00:00:00 2001 From: cameel Date: Wed, 22 Jan 2020 20:14:36 +0100 Subject: [PATCH 085/160] [yul-phaser] Make Program and Population classes accept source code rather than file path - I need some sample .yul files for testing but I see that existing tests generally have source code hard-coded in them rather than in standalone .yul files. There are lots of .yul files but they seem to be automatically processed by a special test case rather loaded ad-hoc by manually created tests. - Program and Population required a file name until now. I'm making them accept loaded source code to be able to give them data hard-coded in a test. --- tools/yulPhaser/Population.cpp | 17 +++++++++-------- tools/yulPhaser/Population.h | 14 ++++++++------ tools/yulPhaser/Program.cpp | 16 +++------------- tools/yulPhaser/Program.h | 3 +-- tools/yulPhaser/main.cpp | 17 ++++++++++++++++- 5 files changed, 37 insertions(+), 30 deletions(-) diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index a5a65fa7fc99..ca6fe7891878 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -26,6 +26,7 @@ using namespace std; using namespace solidity; +using namespace solidity::langutil; using namespace solidity::phaser; namespace solidity::phaser @@ -48,25 +49,25 @@ ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) return _stream; } -Population::Population(string const& _sourcePath, vector const& _chromosomes): - m_sourcePath{_sourcePath} +Population::Population(CharStream _sourceCode, vector const& _chromosomes): + m_sourceCode{move(_sourceCode)} { for (auto const& chromosome: _chromosomes) m_individuals.push_back({chromosome}); } -Population Population::makeRandom(string const& _sourcePath, size_t _size) +Population Population::makeRandom(CharStream _sourceCode, size_t _size) { vector individuals; for (size_t i = 0; i < _size; ++i) individuals.push_back({Chromosome::makeRandom(randomChromosomeLength())}); - return Population(_sourcePath, individuals); + return Population(move(_sourceCode), individuals); } -size_t Population::measureFitness(Chromosome const& _chromosome, string const& _sourcePath) +size_t Population::measureFitness(Chromosome const& _chromosome, CharStream& _sourceCode) { - auto program = Program::load(_sourcePath); + auto program = Program::load(_sourceCode); program.optimise(_chromosome.optimisationSteps()); return program.codeSize(); } @@ -87,7 +88,7 @@ void Population::run(optional _numRounds, ostream& _outputStream) ostream& phaser::operator<<(ostream& _stream, Population const& _population) { - _stream << "Source: " << _population.m_sourcePath << endl; + _stream << "Stream name: " << _population.m_sourceCode.name() << endl; auto individual = _population.m_individuals.begin(); for (; individual != _population.m_individuals.end(); ++individual) @@ -105,7 +106,7 @@ void Population::doEvaluation() { for (auto& individual: m_individuals) if (!individual.fitness.has_value()) - individual.fitness = measureFitness(individual.chromosome, m_sourcePath); + individual.fitness = measureFitness(individual.chromosome, m_sourceCode); } void Population::doSelection() diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index 3c5596b034da..6e30b6193dfb 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -20,6 +20,8 @@ #include #include +#include + #include #include #include @@ -53,21 +55,21 @@ class Population public: static constexpr size_t MaxChromosomeLength = 30; - explicit Population(std::string const& _sourcePath, std::vector const& _chromosomes = {}); - static Population makeRandom(std::string const& _sourcePath, size_t _size); + explicit Population(langutil::CharStream _sourceCode, std::vector const& _chromosomes = {}); + static Population makeRandom(langutil::CharStream _sourceCode, size_t _size); void run(std::optional _numRounds, std::ostream& _outputStream); std::vector const& individuals() const { return m_individuals; } static size_t randomChromosomeLength() { return binomialRandomInt(MaxChromosomeLength, 0.5); } - static size_t measureFitness(Chromosome const& _chromosome, std::string const& _sourcePath); + static size_t measureFitness(Chromosome const& _chromosome, langutil::CharStream& _sourceCode); friend std::ostream& operator<<(std::ostream& _stream, Population const& _population); private: - explicit Population(std::string const& _sourcePath, std::vector _individuals = {}): - m_sourcePath{_sourcePath}, + explicit Population(langutil::CharStream _sourceCode, std::vector _individuals = {}): + m_sourceCode{std::move(_sourceCode)}, m_individuals{std::move(_individuals)} {} void doMutation(); @@ -79,7 +81,7 @@ class Population size_t _count ); - std::string m_sourcePath; + langutil::CharStream m_sourceCode; std::vector m_individuals; }; diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 459fca2e68be..6137ac244bcd 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -38,11 +38,8 @@ #include #include -#include #include -#include - #include #include @@ -60,10 +57,11 @@ ostream& operator<<(ostream& _stream, Program const& _program); } -Program Program::load(string const& _sourcePath) +Program Program::load(CharStream& _sourceCode) { + // ASSUMPTION: parseSource() rewinds the stream on its own Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - unique_ptr ast = parseSource(dialect, loadSource(_sourcePath)); + unique_ptr ast = parseSource(dialect, _sourceCode); unique_ptr analysisInfo = analyzeAST(dialect, *ast); Program program( @@ -95,14 +93,6 @@ string Program::toJson() const return jsonPrettyPrint(serializedAst); } -CharStream Program::loadSource(string const& _sourcePath) -{ - assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); - - string sourceCode = readFileAsString(_sourcePath); - return CharStream(sourceCode, _sourcePath); -} - unique_ptr Program::parseSource(Dialect const& _dialect, CharStream _source) { ErrorList errors; diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index bb382b37496e..822503f2671b 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -65,7 +65,7 @@ class Program: private boost::noncopyable {} Program operator=(Program&& program) = delete; - static Program load(std::string const& _sourcePath); + static Program load(langutil::CharStream& _sourceCode); void optimise(std::vector const& _optimisationSteps); size_t codeSize() const { return computeCodeSize(*m_ast); } @@ -84,7 +84,6 @@ class Program: private boost::noncopyable m_nameDispenser(_dialect, *m_ast, {}) {} - static langutil::CharStream loadSource(std::string const& _sourcePath); static std::unique_ptr parseSource( yul::Dialect const& _dialect, langutil::CharStream _source diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 280262d7cd4b..6c72bd0f0c10 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -18,13 +18,20 @@ #include #include +#include +#include +#include + +#include #include #include #include using namespace std; +using namespace solidity::langutil; using namespace solidity::phaser; +using namespace solidity::util; namespace po = boost::program_options; @@ -37,9 +44,17 @@ struct CommandLineParsingResult po::variables_map arguments; }; +CharStream loadSource(string const& _sourcePath) +{ + assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); + + string sourceCode = readFileAsString(_sourcePath); + return CharStream(sourceCode, _sourcePath); +} + void runAlgorithm(string const& _sourcePath) { - auto population = Population::makeRandom(_sourcePath, 10); + auto population = Population::makeRandom(loadSource(_sourcePath), 10); population.run(nullopt, cout); } From f8e397b487cd74e51f355ff6614d756f61a49c27 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 17 Jan 2020 07:51:45 +0100 Subject: [PATCH 086/160] [yul-phaser] Create test suite for Chromosome --- test/CMakeLists.txt | 6 ++++++ test/yulPhaser/Chromosome.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 test/yulPhaser/Chromosome.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e7a9d903fe64..938d14bcbe38 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -138,6 +138,11 @@ set(libyul_sources ) detect_stray_source_files("${libyul_sources}" "libyul/") +set(yul_phaser_sources + yulPhaser/Chromosome.cpp +) +detect_stray_source_files("${yul_phaser_sources}" "yulPhaser/") + add_executable(soltest ${sources} ${contracts_sources} ${libsolutil_sources} @@ -146,6 +151,7 @@ add_executable(soltest ${sources} ${libyul_sources} ${libsolidity_sources} ${libsolidity_util_sources} + ${yul_phaser_sources} ) target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::program_options Boost::unit_test_framework evmc) diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp new file mode 100644 index 000000000000..fef68df73c1d --- /dev/null +++ b/test/yulPhaser/Chromosome.cpp @@ -0,0 +1,26 @@ +/* + 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 . +*/ + +#include + +namespace solidity::phaser::test +{ + +BOOST_AUTO_TEST_SUITE(ChromosomeTest) +BOOST_AUTO_TEST_SUITE_END() + +} From bee62cdd9e2241159debd0f2a1aece7f77ab88dc Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 17 Jan 2020 07:53:21 +0100 Subject: [PATCH 087/160] [yul-phaser] Tests for Chromosome class --- test/CMakeLists.txt | 6 +++ test/yulPhaser/Chromosome.cpp | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 938d14bcbe38..6bd4f3952533 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -140,6 +140,12 @@ detect_stray_source_files("${libyul_sources}" "libyul/") set(yul_phaser_sources yulPhaser/Chromosome.cpp + + # FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries(). + # My current workaround is just to include its source files here but this introduces + # unnecessary duplication. Create a library or find a way to reuse the list in both places. + ../tools/yulPhaser/Chromosome.cpp + ../tools/yulPhaser/Random.cpp ) detect_stray_source_files("${yul_phaser_sources}" "yulPhaser/") diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index fef68df73c1d..dd250d0ecddf 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -15,12 +15,87 @@ along with solidity. If not, see . */ +#include + +#include +#include +#include +#include + +#include + #include +using namespace std; +using namespace solidity::yul; +using namespace solidity::util; + namespace solidity::phaser::test { +BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(ChromosomeTest) + +BOOST_AUTO_TEST_CASE(makeRandom_should_create_chromosome_with_random_optimisation_steps) +{ + constexpr uint32_t numSteps = 1000; + + auto chromosome1 = Chromosome::makeRandom(numSteps); + auto chromosome2 = Chromosome::makeRandom(numSteps); + BOOST_CHECK_EQUAL(chromosome1.length(), numSteps); + BOOST_CHECK_EQUAL(chromosome2.length(), numSteps); + + multiset steps1; + multiset steps2; + for (auto const& step: chromosome1.optimisationSteps()) + steps1.insert(step); + for (auto const& step: chromosome2.optimisationSteps()) + steps2.insert(step); + + // Check if steps are different and also if they're not just a permutation of the same set. + // Technically they could be the same and still random but the probability is infinitesimally low. + BOOST_TEST(steps1 != steps2); +} + +BOOST_AUTO_TEST_CASE(constructor_should_store_optimisation_steps) +{ + vector steps = { + StructuralSimplifier::name, + BlockFlattener::name, + UnusedPruner::name, + }; + Chromosome chromosome(steps); + + BOOST_TEST(steps == chromosome.optimisationSteps()); +} + +BOOST_AUTO_TEST_CASE(constructor_should_allow_duplicate_steps) +{ + vector steps = { + StructuralSimplifier::name, + StructuralSimplifier::name, + BlockFlattener::name, + UnusedPruner::name, + BlockFlattener::name, + }; + Chromosome chromosome(steps); + + BOOST_TEST(steps == chromosome.optimisationSteps()); +} + +BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_string_representation) +{ + vector allSteps; + for (auto const& step: OptimiserSuite::allSteps()) + allSteps.push_back(step.first); + Chromosome chromosome(allSteps); + + BOOST_TEST(chromosome.length() == allSteps.size()); + BOOST_TEST(chromosome.optimisationSteps() == allSteps); + BOOST_TEST(toString(chromosome) == "fcCUnDvejsxIOoighTLMrmVatud"); +} + +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() } From 24d63a93cf3b6d006d78f751dbd830979f479f51 Mon Sep 17 00:00:00 2001 From: cameel Date: Thu, 23 Jan 2020 19:02:59 +0100 Subject: [PATCH 088/160] [yul-phaser] Tests for Program class --- test/CMakeLists.txt | 2 + test/yulPhaser/Program.cpp | 266 +++++++++++++++++++++++++++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 test/yulPhaser/Program.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6bd4f3952533..957d3af21ce7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -140,11 +140,13 @@ detect_stray_source_files("${libyul_sources}" "libyul/") set(yul_phaser_sources yulPhaser/Chromosome.cpp + yulPhaser/Program.cpp # FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries(). # My current workaround is just to include its source files here but this introduces # unnecessary duplication. Create a library or find a way to reuse the list in both places. ../tools/yulPhaser/Chromosome.cpp + ../tools/yulPhaser/Program.cpp ../tools/yulPhaser/Random.cpp ) detect_stray_source_files("${yul_phaser_sources}" "yulPhaser/") diff --git a/test/yulPhaser/Program.cpp b/test/yulPhaser/Program.cpp new file mode 100644 index 000000000000..26f2934eafcc --- /dev/null +++ b/test/yulPhaser/Program.cpp @@ -0,0 +1,266 @@ +/* + 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 . +*/ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::yul; +using namespace boost::unit_test::framework; + +namespace +{ + /// If the specified block is redundant (i.e. the only thing it contains is another block) + /// the function recurses into it and returns the first non-redundant one it finds. + /// If the block isn't redundant it just returns it immediately. + Block const& skipRedundantBlocks(Block const& _block) + { + if (_block.statements.size() == 1 && holds_alternative(_block.statements[0])) + return skipRedundantBlocks(get(_block.statements[0])); + else + return _block; + } + + string stripWhitespace(string const& input) + { + regex whitespaceRegex("\\s+"); + return regex_replace(input, whitespaceRegex, ""); + } +} + +namespace solidity::phaser::test +{ + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(ProgramTest) + +BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream) +{ + string sourceCode( + "{\n" + " let x := 1\n" + " let y := 2\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + sourceStream.setPosition(5); + + auto program = Program::load(sourceStream); + + BOOST_TEST(CodeSize::codeSize(program.ast()) == 2); +} + +BOOST_AUTO_TEST_CASE(load_should_disambiguate) +{ + string sourceCode( + "{\n" + " {\n" + " let x := 1\n" + " }\n" + " {\n" + " let x := 2\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto program = Program::load(sourceStream); + + // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not. + Block const& parentBlock = skipRedundantBlocks(program.ast()); + BOOST_TEST(parentBlock.statements.size() == 2); + + Block const& innerBlock1 = get(parentBlock.statements[0]); + Block const& innerBlock2 = get(parentBlock.statements[1]); + VariableDeclaration const& declaration1 = get(innerBlock1.statements[0]); + VariableDeclaration const& declaration2 = get(innerBlock2.statements[0]); + + BOOST_TEST(declaration1.variables[0].name.str() == "x"); + BOOST_TEST(declaration2.variables[0].name.str() != "x"); +} + +BOOST_AUTO_TEST_CASE(load_should_do_function_grouping_and_hoisting) +{ + string sourceCode( + "{\n" + " function foo() -> result\n" + " {\n" + " result := 1\n" + " }\n" + " let x := 1\n" + " function bar(a) -> result\n" + " {\n" + " result := 2\n" + " }\n" + " let y := 2\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto program = Program::load(sourceStream); + + BOOST_TEST(program.ast().statements.size() == 3); + BOOST_TEST(holds_alternative(program.ast().statements[0])); + BOOST_TEST(holds_alternative(program.ast().statements[1])); + BOOST_TEST(holds_alternative(program.ast().statements[2])); +} + +BOOST_AUTO_TEST_CASE(load_should_do_loop_init_rewriting) +{ + string sourceCode( + "{\n" + " for { let i := 0 } true {}\n" + " {\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto program = Program::load(sourceStream); + + // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not. + Block const& parentBlock = skipRedundantBlocks(program.ast()); + BOOST_TEST(holds_alternative(parentBlock.statements[0])); + BOOST_TEST(holds_alternative(parentBlock.statements[1])); +} + +BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_parsed) +{ + string sourceCode("invalid program\n"); + CharStream sourceStream(sourceCode, current_test_case().p_name); + + BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram); +} + +BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyzed) +{ + // This should be parsed just fine but fail the analysis with: + // Error: Variable not found or variable not lvalue. + string sourceCode( + "{\n" + " x := 1\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + + BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram); +} + +BOOST_AUTO_TEST_CASE(optimise) +{ + string sourceCode( + "{\n" + " {\n" + " if 1 { let x := 1 }\n" + " if 0 { let y := 2 }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto program = Program::load(sourceStream); + + [[maybe_unused]] Block const& parentBlockBefore = skipRedundantBlocks(program.ast()); + assert(parentBlockBefore.statements.size() == 2); + assert(holds_alternative(parentBlockBefore.statements[0])); + assert(holds_alternative(parentBlockBefore.statements[1])); + + program.optimise({StructuralSimplifier::name, BlockFlattener::name}); + + Block const& parentBlockAfter = program.ast(); + BOOST_TEST(parentBlockAfter.statements.size() == 1); + BOOST_TEST(holds_alternative(parentBlockAfter.statements[0])); +} + +BOOST_AUTO_TEST_CASE(output_operator) +{ + string sourceCode( + "{\n" + " let factor := 13\n" + " {\n" + " if factor\n" + " {\n" + " let variable := add(1, 2)\n" + " }\n" + " let result := factor\n" + " }\n" + " let something := 6\n" + " let something_else := mul(something, factor)\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto program = Program::load(sourceStream); + + // NOTE: The snippet above was chosen so that the few optimisations applied automatically by load() + // as of now do not change the code significantly. If that changes, you may have to update it. + BOOST_TEST(stripWhitespace(toString(program)) == stripWhitespace("{" + sourceCode + "}")); +} + +BOOST_AUTO_TEST_CASE(toJson) +{ + string sourceCode( + "{\n" + " let a := 3\n" + " if a\n" + " {\n" + " let abc := add(1, 2)\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto program = Program::load(sourceStream); + + Json::Value parsingResult; + string errors; + BOOST_TEST(jsonParseStrict(program.toJson(), parsingResult, &errors)); + BOOST_TEST(errors.empty()); +} + +BOOST_AUTO_TEST_CASE(codeSize) +{ + string sourceCode( + "{\n" + " function foo() -> result\n" + " {\n" + " result := 15\n" + " }\n" + " let a := 1\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto program = Program::load(sourceStream); + + BOOST_TEST(program.codeSize() == CodeSize::codeSizeIncludingFunctions(program.ast())); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} From ccaff1b08eaf58e68e1a4029bfccffa48bcd2775 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 24 Jan 2020 03:32:56 +0100 Subject: [PATCH 089/160] [yul-phaser] Tests for random number generators --- test/CMakeLists.txt | 1 + test/yulPhaser/Random.cpp | 95 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 test/yulPhaser/Random.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 957d3af21ce7..2ac7f1b7e683 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -141,6 +141,7 @@ detect_stray_source_files("${libyul_sources}" "libyul/") set(yul_phaser_sources yulPhaser/Chromosome.cpp yulPhaser/Program.cpp + yulPhaser/Random.cpp # FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries(). # My current workaround is just to include its source files here but this introduces diff --git a/test/yulPhaser/Random.cpp b/test/yulPhaser/Random.cpp new file mode 100644 index 000000000000..c69b91055177 --- /dev/null +++ b/test/yulPhaser/Random.cpp @@ -0,0 +1,95 @@ +/* + 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 . +*/ + +#include + +#include + +#include + +using namespace std; + +namespace solidity::phaser::test +{ + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(RandomTest) + +BOOST_AUTO_TEST_CASE(uniformRandomInt_returns_different_values_when_called_multiple_times) +{ + constexpr uint32_t numSamples = 1000; + constexpr uint32_t numOutcomes = 100; + + vector samples1; + vector samples2; + for (uint32_t i = 0; i < numSamples; ++i) + { + samples1.push_back(uniformRandomInt(0, numOutcomes - 1)); + samples2.push_back(uniformRandomInt(0, numOutcomes - 1)); + } + + vector counts1(numSamples, 0); + vector counts2(numSamples, 0); + for (uint32_t i = 0; i < numSamples; ++i) + { + ++counts1[samples1[i]]; + ++counts2[samples2[i]]; + } + + // This test rules out not only the possibility that the two sequences are the same but also + // that they're just different permutations of the same values. The test is probabilistic so + // it's technically possible for it to fail even if generator is good but the probability is + // so low that it would happen on average once very 10^125 billion years if you repeated it + // every second. The chance is much lower than 1 in 1000^100 / 100!. + // + // This does not really guarantee that the generated numbers have the right distribution or + // or that they don't come in long, repeating sequences but the implementation is very simple + // (it just calls a generator from boost) so our goal here is just to make sure it's used + // properly and we're not getting something totally non-random, e.g. the same number every time. + BOOST_TEST(counts1 != counts2); +} + +BOOST_AUTO_TEST_CASE(binomialRandomInt_returns_different_values_when_called_multiple_times) +{ + constexpr uint32_t numSamples = 1000; + constexpr uint32_t numTrials = 100; + constexpr double successProbability = 0.6; + + vector samples1; + vector samples2; + for (uint32_t i = 0; i < numSamples; ++i) + { + samples1.push_back(binomialRandomInt(numTrials, successProbability)); + samples2.push_back(binomialRandomInt(numTrials, successProbability)); + } + + vector counts1(numSamples, 0); + vector counts2(numSamples, 0); + for (uint32_t i = 0; i < numSamples; ++i) + { + ++counts1[samples1[i]]; + ++counts2[samples2[i]]; + } + + // See remark for uniformRandomInt() above. Same applies here. + BOOST_TEST(counts1 != counts2); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} From 33cf4e4769409fd330174e2e8ef0e9660e6ee962 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 24 Jan 2020 04:56:32 +0100 Subject: [PATCH 090/160] [yul-phaser] Tests for Population class --- test/CMakeLists.txt | 2 + test/yulPhaser/Population.cpp | 176 ++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 test/yulPhaser/Population.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2ac7f1b7e683..7cf466e0bfd4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -140,6 +140,7 @@ detect_stray_source_files("${libyul_sources}" "libyul/") set(yul_phaser_sources yulPhaser/Chromosome.cpp + yulPhaser/Population.cpp yulPhaser/Program.cpp yulPhaser/Random.cpp @@ -147,6 +148,7 @@ set(yul_phaser_sources # My current workaround is just to include its source files here but this introduces # unnecessary duplication. Create a library or find a way to reuse the list in both places. ../tools/yulPhaser/Chromosome.cpp + ../tools/yulPhaser/Population.cpp ../tools/yulPhaser/Program.cpp ../tools/yulPhaser/Random.cpp ) diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp new file mode 100644 index 000000000000..d0ee5a0c73fd --- /dev/null +++ b/test/yulPhaser/Population.cpp @@ -0,0 +1,176 @@ +/* + 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 . +*/ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity::langutil; +using namespace solidity::yul; +using namespace boost::unit_test::framework; + +namespace solidity::phaser::test +{ + +namespace +{ + bool fitnessNotSet(Individual const& individual) + { + return !individual.fitness.has_value(); + } + + bool fitnessSet(Individual const& individual) + { + return individual.fitness.has_value(); + } +} + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(PopulationTest) + +string const& sampleSourceCode = + "{\n" + " let factor := 13\n" + " {\n" + " if factor\n" + " {\n" + " let variable := add(1, 2)\n" + " }\n" + " let result := factor\n" + " }\n" + " let something := 6\n" + " {\n" + " {\n" + " {\n" + " let value := 15\n" + " }\n" + " }\n" + " }\n" + " let something_else := mul(mul(something, 1), add(factor, 0))\n" + " if 1 { let x := 1 }\n" + " if 0 { let y := 2 }\n" + "}\n"; + +BOOST_AUTO_TEST_CASE(constructor_should_copy_chromosomes_and_not_compute_fitness) +{ + CharStream sourceStream(sampleSourceCode, current_test_case().p_name); + vector chromosomes = { + Chromosome::makeRandom(5), + Chromosome::makeRandom(10), + }; + Population population(sourceStream, chromosomes); + + BOOST_TEST(population.individuals().size() == 2); + BOOST_TEST(population.individuals()[0].chromosome == chromosomes[0]); + BOOST_TEST(population.individuals()[1].chromosome == chromosomes[1]); + + auto fitnessNotSet = [](auto const& individual){ return !individual.fitness.has_value(); }; + BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet)); +} + +BOOST_AUTO_TEST_CASE(makeRandom_should_return_population_with_random_chromosomes) +{ + CharStream sourceStream(sampleSourceCode, current_test_case().p_name); + auto population1 = Population::makeRandom(sourceStream, 100); + auto population2 = Population::makeRandom(sourceStream, 100); + + BOOST_TEST(population1.individuals().size() == 100); + BOOST_TEST(population2.individuals().size() == 100); + + int numMatchingPositions = 0; + for (size_t i = 0; i < 100; ++i) + if (population1.individuals()[i].chromosome == population2.individuals()[i].chromosome) + ++numMatchingPositions; + + // Assume that the results are random if there are no more than 10 identical chromosomes on the + // same positions. One duplicate is very unlikely but still possible after billions of runs + // (especially for short chromosomes). For ten the probability is so small that we can ignore it. + BOOST_TEST(numMatchingPositions < 10); +} + +BOOST_AUTO_TEST_CASE(makeRandom_should_not_compute_fitness) +{ + CharStream sourceStream(sampleSourceCode, current_test_case().p_name); + auto population = Population::makeRandom(sourceStream, 5); + + BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet)); +} + +BOOST_AUTO_TEST_CASE(run_should_evaluate_fitness) +{ + stringstream output; + CharStream sourceStream(sampleSourceCode, current_test_case().p_name); + auto population = Population::makeRandom(sourceStream, 5); + assert(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet)); + + population.run(1, output); + + BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessSet)); +} + +BOOST_AUTO_TEST_CASE(run_should_not_make_fitness_of_top_chromosomes_worse) +{ + stringstream output; + CharStream sourceStream(sampleSourceCode, current_test_case().p_name); + vector chromosomes = { + Chromosome({StructuralSimplifier::name}), + Chromosome({BlockFlattener::name}), + Chromosome({SSAReverser::name}), + Chromosome({UnusedPruner::name}), + Chromosome({StructuralSimplifier::name, BlockFlattener::name}), + }; + Population population(sourceStream, chromosomes); + + size_t initialTopFitness[2] = { + Population::measureFitness(chromosomes[0], sourceStream), + Population::measureFitness(chromosomes[1], sourceStream), + }; + + for (int i = 0; i < 6; ++i) + { + population.run(1, output); + BOOST_TEST(population.individuals().size() == 5); + BOOST_TEST(fitnessSet(population.individuals()[0])); + BOOST_TEST(fitnessSet(population.individuals()[1])); + + size_t currentTopFitness[2] = { + population.individuals()[0].fitness.value(), + population.individuals()[1].fitness.value(), + }; + BOOST_TEST(currentTopFitness[0] <= initialTopFitness[0]); + BOOST_TEST(currentTopFitness[1] <= initialTopFitness[1]); + BOOST_TEST(currentTopFitness[0] <= currentTopFitness[1]); + } +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} From 0ae30536186a9eebaeb7f276944bc09f08e696ee Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 3 Feb 2020 21:50:18 -0500 Subject: [PATCH 091/160] Add support for reading standard-json from file. Currently `--standard-json` only supports reading input from stdin, reading input from a specified file may simplify debugging. --- solc/CommandLineInterface.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 1598bbb6880c..7edf31ae64f0 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -750,7 +750,7 @@ Allowed options)", ( g_argStandardJSON.c_str(), "Switch to Standard JSON input / output mode, ignoring all options. " - "It reads from standard input and provides the result on the standard output." + "It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output." ) ( g_argImportAst.c_str(), @@ -965,7 +965,22 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argStandardJSON)) { - string input = readStandardInput(); + vector inputFiles; + string jsonFile; + if (m_args.count(g_argInputFile)) + inputFiles = m_args[g_argInputFile].as>(); + if (inputFiles.size() == 1) + jsonFile = inputFiles[0]; + else if (inputFiles.size() > 1) + { + serr() << "If --" << g_argStandardJSON << " is used, only zero or one input files are supported." << endl; + return false; + } + string input; + if (jsonFile.empty()) + input = readStandardInput(); + else + input = readFileAsString(jsonFile); StandardCompiler compiler(fileReader); sout() << compiler.compile(std::move(input)) << endl; return true; From 7fecab07a8a5ece29177c4f03b4a920de4b58baf Mon Sep 17 00:00:00 2001 From: a3d4 Date: Wed, 5 Feb 2020 22:13:03 +0100 Subject: [PATCH 092/160] Simplified Parser::createWithLocation(). In all but one case the function was called with the default argument value. And when it was location, the location should be valid (see Parser::parseElementaryOperation()). --- libyul/AsmParser.cpp | 4 ++-- libyul/AsmParser.h | 13 +++---------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 5a0f615d0bca..6d251c3ba04e 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -206,8 +206,8 @@ Statement Parser::parseStatement() elementary = parseElementaryOperation(); } - Assignment assignment = - createWithLocation(std::get(elementary).location); + Assignment assignment; + assignment.location = std::get(elementary).location; assignment.variableNames = std::move(variableNames); expectToken(Token::AssemblyAssign); diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 42663ae0a7a7..6148ef725039 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -60,18 +60,11 @@ class Parser: public langutil::ParserBase protected: using ElementaryOperation = std::variant; - /// Creates an inline assembly node with the given source location. - template T createWithLocation(langutil::SourceLocation const& _loc = {}) const + /// Creates an inline assembly node with the current source location. + template T createWithLocation() const { T r; - r.location = _loc; - if (!r.location.hasText()) - { - r.location.start = position(); - r.location.end = endPosition(); - } - if (!r.location.source) - r.location.source = m_scanner->charStream(); + r.location = location(); return r; } langutil::SourceLocation location() const { return {position(), endPosition(), m_scanner->charStream()}; } From 4ec4d23886cf0f686c56d36d1f501b5ac4a8aab3 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Thu, 6 Feb 2020 00:04:18 +0100 Subject: [PATCH 093/160] Replaced ParserBase::position() and ParserBase::endPosition() with ParserBase::currentLocation(). It might be simpler to pass `SourceLocation` object instead of splitting it into `start` and `end`, and creating another SourceLocation object using the same `start` and `end` later. --- liblangutil/ParserBase.cpp | 19 +++++++------------ liblangutil/ParserBase.h | 6 ++---- liblangutil/Scanner.cpp | 1 + libsolidity/parsing/Parser.cpp | 14 +++++++------- libyul/AsmParser.cpp | 24 ++++++++++++------------ libyul/AsmParser.h | 3 +-- 6 files changed, 30 insertions(+), 37 deletions(-) diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp index 3c0fe0b89f4c..747cb37cfdd8 100644 --- a/liblangutil/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -28,14 +28,9 @@ using namespace std; using namespace solidity; using namespace solidity::langutil; -int ParserBase::position() const +SourceLocation ParserBase::currentLocation() const { - return m_scanner->currentLocation().start; -} - -int ParserBase::endPosition() const -{ - return m_scanner->currentLocation().end; + return m_scanner->currentLocation(); } Token ParserBase::currentToken() const @@ -101,8 +96,8 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN Token tok = m_scanner->currentToken(); if (tok != _value) { - int startPosition = position(); - SourceLocation errorLoc = SourceLocation{startPosition, endPosition(), source()}; + SourceLocation errorLoc = currentLocation(); + int startPosition = errorLoc.start; while (m_scanner->currentToken() != _value && m_scanner->currentToken() != Token::EOS) m_scanner->next(); @@ -150,7 +145,7 @@ void ParserBase::decreaseRecursionDepth() void ParserBase::parserWarning(string const& _description) { - m_errorReporter.warning(SourceLocation{position(), endPosition(), source()}, _description); + m_errorReporter.warning(currentLocation(), _description); } void ParserBase::parserError(SourceLocation const& _location, string const& _description) @@ -160,12 +155,12 @@ void ParserBase::parserError(SourceLocation const& _location, string const& _des void ParserBase::parserError(string const& _description) { - parserError(SourceLocation{position(), endPosition(), source()}, _description); + parserError(currentLocation(), _description); } void ParserBase::fatalParserError(string const& _description) { - fatalParserError(SourceLocation{position(), endPosition(), source()}, _description); + fatalParserError(currentLocation(), _description); } void ParserBase::fatalParserError(SourceLocation const& _location, string const& _description) diff --git a/liblangutil/ParserBase.h b/liblangutil/ParserBase.h index 08f2a9dadad9..575a049e2b7a 100644 --- a/liblangutil/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -62,10 +62,8 @@ class ParserBase ParserBase& m_parser; }; - /// Start position of the current token - int position() const; - /// End position of the current token - int endPosition() const; + /// Location of the current token + SourceLocation currentLocation() const; ///@{ ///@name Helper functions diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index f983274057e9..9b9ad8847733 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -681,6 +681,7 @@ void Scanner::scanToken() } while (token == Token::Whitespace); m_tokens[NextNext].location.end = sourcePos(); + m_tokens[NextNext].location.source = m_source; m_tokens[NextNext].token = token; m_tokens[NextNext].extendedTokenInfo = make_tuple(m, n); } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index c6f139dbfdf4..99aa6e2a1ed7 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -45,11 +45,11 @@ class Parser::ASTNodeFactory { public: explicit ASTNodeFactory(Parser& _parser): - m_parser(_parser), m_location{_parser.position(), -1, _parser.source()} {} + m_parser(_parser), m_location{_parser.currentLocation().start, -1, _parser.currentLocation().source} {} ASTNodeFactory(Parser& _parser, ASTPointer const& _childNode): m_parser(_parser), m_location{_childNode->location()} {} - void markEndPosition() { m_location.end = m_parser.endPosition(); } + void markEndPosition() { m_location.end = m_parser.currentLocation().end; } void setLocation(SourceLocation const& _location) { m_location = _location; } void setLocationEmpty() { m_location.end = m_location.start; } /// Set the end position to the one of the given node. @@ -218,12 +218,12 @@ ASTPointer Parser::parseImportDirective() while (true) { ASTPointer alias; - SourceLocation aliasLocation = SourceLocation{position(), endPosition(), source()}; + SourceLocation aliasLocation = currentLocation(); ASTPointer id = parseIdentifier(); if (m_scanner->currentToken() == Token::As) { expectToken(Token::As); - aliasLocation = SourceLocation{position(), endPosition(), source()}; + aliasLocation = currentLocation(); alias = expectIdentifierToken(); } symbolAliases.emplace_back(ImportDirective::SymbolAlias{move(id), move(alias), aliasLocation}); @@ -1189,7 +1189,7 @@ ASTPointer Parser::parseStatement() ASTPointer Parser::parseInlineAssembly(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); - SourceLocation location{position(), -1, source()}; + SourceLocation location = currentLocation(); expectToken(Token::Assembly); yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); @@ -2024,13 +2024,13 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath() ASTPointer endIndex; if (m_scanner->currentToken() != Token::RBrack) endIndex = parseExpression(); - indexLocation.end = endPosition(); + indexLocation.end = currentLocation().end; iap.indices.emplace_back(IndexAccessedPath::Index{index, {endIndex}, indexLocation}); expectToken(Token::RBrack); } else { - indexLocation.end = endPosition(); + indexLocation.end = currentLocation().end; iap.indices.emplace_back(IndexAccessedPath::Index{index, {}, indexLocation}); expectToken(Token::RBrack); } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 6d251c3ba04e..708e76339b50 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -88,7 +88,7 @@ Block Parser::parseBlock() expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); - block.location.end = endPosition(); + block.location.end = currentLocation().end; advance(); return block; } @@ -151,7 +151,7 @@ Statement Parser::parseStatement() { Statement stmt{createWithLocation()}; if (!m_insideFunction) - m_errorReporter.syntaxError(location(), "Keyword \"leave\" can only be used inside a function."); + m_errorReporter.syntaxError(currentLocation(), "Keyword \"leave\" can only be used inside a function."); m_scanner->next(); return stmt; } @@ -328,13 +328,13 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() YulString literal{currentLiteral()}; if (m_dialect.builtin(literal)) { - Identifier identifier{location(), literal}; + Identifier identifier{currentLocation(), literal}; advance(); expectToken(Token::LParen, false); return FunctionCall{identifier.location, identifier, {}}; } else - ret = Identifier{location(), literal}; + ret = Identifier{currentLocation(), literal}; advance(); break; } @@ -363,7 +363,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() } Literal literal{ - location(), + currentLocation(), kind, YulString{currentLiteral()}, kind == LiteralKind::Boolean ? m_dialect.boolType : m_dialect.defaultType @@ -372,7 +372,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - literal.location.end = endPosition(); + literal.location.end = currentLocation().end; literal.type = expectAsmIdentifier(); } @@ -415,7 +415,7 @@ FunctionDefinition Parser::parseFunctionDefinition() if (m_currentForLoopComponent == ForLoopComponent::ForLoopPre) m_errorReporter.syntaxError( - location(), + currentLocation(), "Functions cannot be defined inside a for-loop init block." ); @@ -481,7 +481,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) ret.arguments.emplace_back(parseExpression()); } } - ret.location.end = endPosition(); + ret.location.end = currentLocation().end; expectToken(Token::RParen); return ret; } @@ -494,7 +494,7 @@ TypedName Parser::parseTypedName() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - typedName.location.end = endPosition(); + typedName.location.end = currentLocation().end; typedName.type = expectAsmIdentifier(); } else @@ -530,13 +530,13 @@ void Parser::checkBreakContinuePosition(string const& _which) switch (m_currentForLoopComponent) { case ForLoopComponent::None: - m_errorReporter.syntaxError(location(), "Keyword \"" + _which + "\" needs to be inside a for-loop body."); + m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body."); break; case ForLoopComponent::ForLoopPre: - m_errorReporter.syntaxError(location(), "Keyword \"" + _which + "\" in for-loop init block is not allowed."); + m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed."); break; case ForLoopComponent::ForLoopPost: - m_errorReporter.syntaxError(location(), "Keyword \"" + _which + "\" in for-loop post block is not allowed."); + m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed."); break; case ForLoopComponent::ForLoopBody: break; diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 6148ef725039..54c79a0e7e61 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -64,10 +64,9 @@ class Parser: public langutil::ParserBase template T createWithLocation() const { T r; - r.location = location(); + r.location = currentLocation(); return r; } - langutil::SourceLocation location() const { return {position(), endPosition(), m_scanner->charStream()}; } Block parseBlock(); Statement parseStatement(); From a189c8b6e21fd81c6756911ca86b5181cfe32c82 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 31 Jan 2020 11:32:44 +0100 Subject: [PATCH 094/160] [yul-phaser] Pass unique_ptr rather than a mutable reference to Program::applyOptimisationSteps() - Giving the function ownership of an object and then receiving back another object is better than letting it implicitly modify data passed in an argument. --- tools/yulPhaser/Program.cpp | 10 ++++++---- tools/yulPhaser/Program.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 6137ac244bcd..001110bed9a3 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -79,7 +79,7 @@ Program Program::load(CharStream& _sourceCode) void Program::optimise(vector const& _optimisationSteps) { - applyOptimisationSteps(m_dialect, m_nameDispenser, *m_ast, _optimisationSteps); + m_ast = applyOptimisationSteps(m_dialect, m_nameDispenser, move(m_ast), _optimisationSteps); } ostream& phaser::operator<<(ostream& _stream, Program const& _program) @@ -133,10 +133,10 @@ unique_ptr Program::disambiguateAST( return make_unique(get(disambiguator(_ast))); } -void Program::applyOptimisationSteps( +unique_ptr Program::applyOptimisationSteps( Dialect const& _dialect, NameDispenser& _nameDispenser, - Block& _ast, + unique_ptr _ast, vector const& _optimisationSteps ) { @@ -146,7 +146,9 @@ void Program::applyOptimisationSteps( OptimiserStepContext context{_dialect, _nameDispenser, externallyUsedIdentifiers}; for (string const& step: _optimisationSteps) - OptimiserSuite::allSteps().at(step)->run(context, _ast); + OptimiserSuite::allSteps().at(step)->run(context, *_ast); + + return _ast; } size_t Program::computeCodeSize(Block const& _ast) diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index 822503f2671b..417adb4f5969 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -97,10 +97,10 @@ class Program: private boost::noncopyable yul::Block const& _ast, yul::AsmAnalysisInfo const& _analysisInfo ); - static void applyOptimisationSteps( + static std::unique_ptr applyOptimisationSteps( yul::Dialect const& _dialect, yul::NameDispenser& _nameDispenser, - yul::Block& _ast, + std::unique_ptr _ast, std::vector const& _optimisationSteps ); static size_t computeCodeSize(yul::Block const& _ast); From 57fb64d467af2855a2eafb28a73bbb7f2087cc82 Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 31 Jan 2020 13:23:57 +0100 Subject: [PATCH 095/160] [yul-phaser] Make Program copyable by doing a deep copy of the AST in the copy constructor --- test/yulPhaser/Program.cpp | 20 ++++++++++++++++++++ tools/yulPhaser/Program.cpp | 7 +++++++ tools/yulPhaser/Program.h | 6 +++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/test/yulPhaser/Program.cpp b/test/yulPhaser/Program.cpp index 26f2934eafcc..44c2bfa3c6e5 100644 --- a/test/yulPhaser/Program.cpp +++ b/test/yulPhaser/Program.cpp @@ -65,6 +65,26 @@ namespace solidity::phaser::test BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(ProgramTest) +BOOST_AUTO_TEST_CASE(copy_constructor_should_make_deep_copy_of_ast) +{ + string sourceCode( + "{\n" + " let x := 1\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto program = Program::load(sourceStream); + + Program programCopy(program); + + BOOST_TEST(&programCopy.ast() != &program.ast()); + + // There might be a more direct way to compare ASTs but converting to JSON should be good enough + // as long as the conversion is deterministic. A very nice side effect of doing it this way is + // that BOOST_TEST will print the complete AST structure of both programs in case of a mismatch. + BOOST_TEST(programCopy.toJson() == program.toJson()); +} + BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream) { string sourceCode( diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 001110bed9a3..42cc8bf61438 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -57,6 +57,13 @@ ostream& operator<<(ostream& _stream, Program const& _program); } +Program::Program(Program const& program): + m_ast(make_unique(get(ASTCopier{}(*program.m_ast)))), + m_dialect{program.m_dialect}, + m_nameDispenser(program.m_nameDispenser) +{ +} + Program Program::load(CharStream& _sourceCode) { // ASSUMPTION: parseSource() rewinds the stream on its own diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index 417adb4f5969..5e240e98dc3f 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -20,8 +20,6 @@ #include #include -#include - #include #include #include @@ -55,14 +53,16 @@ namespace solidity::phaser * The class allows the user to apply extra optimisations and obtain metrics and general * information about the resulting syntax tree. */ -class Program: private boost::noncopyable +class Program { public: + Program(Program const& program); Program(Program&& program): m_ast(std::move(program.m_ast)), m_dialect{program.m_dialect}, m_nameDispenser(std::move(program.m_nameDispenser)) {} + Program operator=(Program const& program) = delete; Program operator=(Program&& program) = delete; static Program load(langutil::CharStream& _sourceCode); From e4c7b738973e3a7550743b7a90f3a99679151bdd Mon Sep 17 00:00:00 2001 From: cameel Date: Fri, 31 Jan 2020 13:26:54 +0100 Subject: [PATCH 096/160] [yul-phaser] Store already loaded program in Population and make copies when computing fitness - Until now the source code was being parsed during every fitness computation. Now the parsed program is reused and only the optimisation steps are applied each time. --- test/yulPhaser/Population.cpp | 19 +++++++++++-------- tools/yulPhaser/Population.cpp | 20 +++++++++----------- tools/yulPhaser/Population.h | 18 ++++++++---------- tools/yulPhaser/main.cpp | 4 +++- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index d0ee5a0c73fd..d2c11a886c72 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -85,7 +86,7 @@ BOOST_AUTO_TEST_CASE(constructor_should_copy_chromosomes_and_not_compute_fitness Chromosome::makeRandom(5), Chromosome::makeRandom(10), }; - Population population(sourceStream, chromosomes); + Population population(Program::load(sourceStream), chromosomes); BOOST_TEST(population.individuals().size() == 2); BOOST_TEST(population.individuals()[0].chromosome == chromosomes[0]); @@ -98,8 +99,9 @@ BOOST_AUTO_TEST_CASE(constructor_should_copy_chromosomes_and_not_compute_fitness BOOST_AUTO_TEST_CASE(makeRandom_should_return_population_with_random_chromosomes) { CharStream sourceStream(sampleSourceCode, current_test_case().p_name); - auto population1 = Population::makeRandom(sourceStream, 100); - auto population2 = Population::makeRandom(sourceStream, 100); + auto program = Program::load(sourceStream); + auto population1 = Population::makeRandom(program, 100); + auto population2 = Population::makeRandom(program, 100); BOOST_TEST(population1.individuals().size() == 100); BOOST_TEST(population2.individuals().size() == 100); @@ -118,7 +120,7 @@ BOOST_AUTO_TEST_CASE(makeRandom_should_return_population_with_random_chromosomes BOOST_AUTO_TEST_CASE(makeRandom_should_not_compute_fitness) { CharStream sourceStream(sampleSourceCode, current_test_case().p_name); - auto population = Population::makeRandom(sourceStream, 5); + auto population = Population::makeRandom(Program::load(sourceStream), 5); BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet)); } @@ -127,7 +129,7 @@ BOOST_AUTO_TEST_CASE(run_should_evaluate_fitness) { stringstream output; CharStream sourceStream(sampleSourceCode, current_test_case().p_name); - auto population = Population::makeRandom(sourceStream, 5); + auto population = Population::makeRandom(Program::load(sourceStream), 5); assert(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet)); population.run(1, output); @@ -146,11 +148,12 @@ BOOST_AUTO_TEST_CASE(run_should_not_make_fitness_of_top_chromosomes_worse) Chromosome({UnusedPruner::name}), Chromosome({StructuralSimplifier::name, BlockFlattener::name}), }; - Population population(sourceStream, chromosomes); + auto program = Program::load(sourceStream); + Population population(program, chromosomes); size_t initialTopFitness[2] = { - Population::measureFitness(chromosomes[0], sourceStream), - Population::measureFitness(chromosomes[1], sourceStream), + Population::measureFitness(chromosomes[0], program), + Population::measureFitness(chromosomes[1], program), }; for (int i = 0; i < 6; ++i) diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index ca6fe7891878..a28db27a4582 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -49,27 +49,27 @@ ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) return _stream; } -Population::Population(CharStream _sourceCode, vector const& _chromosomes): - m_sourceCode{move(_sourceCode)} +Population::Population(Program _program, vector const& _chromosomes): + m_program{move(_program)} { for (auto const& chromosome: _chromosomes) m_individuals.push_back({chromosome}); } -Population Population::makeRandom(CharStream _sourceCode, size_t _size) +Population Population::makeRandom(Program _program, size_t _size) { vector individuals; for (size_t i = 0; i < _size; ++i) individuals.push_back({Chromosome::makeRandom(randomChromosomeLength())}); - return Population(move(_sourceCode), individuals); + return Population(move(_program), individuals); } -size_t Population::measureFitness(Chromosome const& _chromosome, CharStream& _sourceCode) +size_t Population::measureFitness(Chromosome const& _chromosome, Program const& _program) { - auto program = Program::load(_sourceCode); - program.optimise(_chromosome.optimisationSteps()); - return program.codeSize(); + Program programCopy = _program; + programCopy.optimise(_chromosome.optimisationSteps()); + return programCopy.codeSize(); } void Population::run(optional _numRounds, ostream& _outputStream) @@ -88,8 +88,6 @@ void Population::run(optional _numRounds, ostream& _outputStream) ostream& phaser::operator<<(ostream& _stream, Population const& _population) { - _stream << "Stream name: " << _population.m_sourceCode.name() << endl; - auto individual = _population.m_individuals.begin(); for (; individual != _population.m_individuals.end(); ++individual) _stream << *individual << endl; @@ -106,7 +104,7 @@ void Population::doEvaluation() { for (auto& individual: m_individuals) if (!individual.fitness.has_value()) - individual.fitness = measureFitness(individual.chromosome, m_sourceCode); + individual.fitness = measureFitness(individual.chromosome, m_program); } void Population::doSelection() diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index 6e30b6193dfb..e8a38dc14970 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -18,10 +18,9 @@ #pragma once #include +#include #include -#include - #include #include #include @@ -47,7 +46,7 @@ struct Individual * and selecting the best ones for the next round. * * An individual is a sequence of optimiser steps represented by a @a Chromosome instance. The whole - * population is associated with a fixed Yul program. By loading the source code into a @a Program + * population is associated with a fixed Yul program. By applying the steps to the @a Program * instance the class can compute fitness of the individual. */ class Population @@ -55,21 +54,21 @@ class Population public: static constexpr size_t MaxChromosomeLength = 30; - explicit Population(langutil::CharStream _sourceCode, std::vector const& _chromosomes = {}); - static Population makeRandom(langutil::CharStream _sourceCode, size_t _size); + explicit Population(Program _program, std::vector const& _chromosomes = {}); + static Population makeRandom(Program _program, size_t _size); void run(std::optional _numRounds, std::ostream& _outputStream); std::vector const& individuals() const { return m_individuals; } static size_t randomChromosomeLength() { return binomialRandomInt(MaxChromosomeLength, 0.5); } - static size_t measureFitness(Chromosome const& _chromosome, langutil::CharStream& _sourceCode); + static size_t measureFitness(Chromosome const& _chromosome, Program const& _program); friend std::ostream& operator<<(std::ostream& _stream, Population const& _population); private: - explicit Population(langutil::CharStream _sourceCode, std::vector _individuals = {}): - m_sourceCode{std::move(_sourceCode)}, + explicit Population(Program _program, std::vector _individuals = {}): + m_program{std::move(_program)}, m_individuals{std::move(_individuals)} {} void doMutation(); @@ -81,8 +80,7 @@ class Population size_t _count ); - langutil::CharStream m_sourceCode; - + Program m_program; std::vector m_individuals; }; diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 6c72bd0f0c10..bb11b9fcf6ad 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -54,7 +55,8 @@ CharStream loadSource(string const& _sourcePath) void runAlgorithm(string const& _sourcePath) { - auto population = Population::makeRandom(loadSource(_sourcePath), 10); + CharStream sourceCode = loadSource(_sourcePath); + auto population = Population::makeRandom(Program::load(sourceCode), 10); population.run(nullopt, cout); } From e07274a96f6e53a995d18cc92526239b9f20eb01 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 6 Feb 2020 09:08:28 +0100 Subject: [PATCH 097/160] Disallow libraries as mapping keys. --- libsolidity/analysis/TypeChecker.cpp | 13 +++++++++---- .../mapping}/contract_mapping.sol | 0 .../mapping}/contract_mapping_invalid.sol | 0 .../{mappings => types/mapping}/enum_mapping.sol | 0 .../mapping}/enum_mapping_invalid.sol | 0 .../syntaxTests/types/mapping/library_mapping.sol | 4 ++++ 6 files changed, 13 insertions(+), 4 deletions(-) rename test/libsolidity/syntaxTests/{mappings => types/mapping}/contract_mapping.sol (100%) rename test/libsolidity/syntaxTests/{mappings => types/mapping}/contract_mapping_invalid.sol (100%) rename test/libsolidity/syntaxTests/{mappings => types/mapping}/enum_mapping.sol (100%) rename test/libsolidity/syntaxTests/{mappings => types/mapping}/enum_mapping_invalid.sol (100%) create mode 100644 test/libsolidity/syntaxTests/types/mapping/library_mapping.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f09c388327c3..4590bcdc25e6 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2880,10 +2880,15 @@ bool TypeChecker::visit(Mapping const& _mapping) { if (auto const* keyType = dynamic_cast(&_mapping.keyType())) { - if ( - keyType->annotation().type->category() != Type::Category::Contract && - keyType->annotation().type->category() != Type::Category::Enum - ) + if (auto const* contractType = dynamic_cast(keyType->annotation().type)) + { + if (contractType->contractDefinition().isLibrary()) + m_errorReporter.typeError( + keyType->location(), + "Library types cannot be used as mapping keys." + ); + } + else if (keyType->annotation().type->category() != Type::Category::Enum) m_errorReporter.typeError( keyType->location(), "Only elementary types, contract types or enums are allowed as mapping keys." diff --git a/test/libsolidity/syntaxTests/mappings/contract_mapping.sol b/test/libsolidity/syntaxTests/types/mapping/contract_mapping.sol similarity index 100% rename from test/libsolidity/syntaxTests/mappings/contract_mapping.sol rename to test/libsolidity/syntaxTests/types/mapping/contract_mapping.sol diff --git a/test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol b/test/libsolidity/syntaxTests/types/mapping/contract_mapping_invalid.sol similarity index 100% rename from test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol rename to test/libsolidity/syntaxTests/types/mapping/contract_mapping_invalid.sol diff --git a/test/libsolidity/syntaxTests/mappings/enum_mapping.sol b/test/libsolidity/syntaxTests/types/mapping/enum_mapping.sol similarity index 100% rename from test/libsolidity/syntaxTests/mappings/enum_mapping.sol rename to test/libsolidity/syntaxTests/types/mapping/enum_mapping.sol diff --git a/test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol b/test/libsolidity/syntaxTests/types/mapping/enum_mapping_invalid.sol similarity index 100% rename from test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol rename to test/libsolidity/syntaxTests/types/mapping/enum_mapping_invalid.sol diff --git a/test/libsolidity/syntaxTests/types/mapping/library_mapping.sol b/test/libsolidity/syntaxTests/types/mapping/library_mapping.sol new file mode 100644 index 000000000000..b2512b47b3ca --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_mapping.sol @@ -0,0 +1,4 @@ +library L {} +contract C { mapping(L => bool) i; } +// ---- +// TypeError: (34-35): Library types cannot be used as mapping keys. From 9b8e3800bb5011da9f8b98492a0c88d159060d1f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 6 Feb 2020 10:18:15 +0100 Subject: [PATCH 098/160] Fix upgrade tool build on windows. --- tools/solidityUpgrade/SourceUpgrade.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/solidityUpgrade/SourceUpgrade.cpp b/tools/solidityUpgrade/SourceUpgrade.cpp index 82d8fecf0b9b..8c05ecc9fda2 100644 --- a/tools/solidityUpgrade/SourceUpgrade.cpp +++ b/tools/solidityUpgrade/SourceUpgrade.cpp @@ -25,6 +25,15 @@ #include #include +#ifdef _WIN32 // windows + #include + #define isatty _isatty + #define fileno _fileno +#else // unix + #include +#endif + + namespace po = boost::program_options; namespace fs = boost::filesystem; From 4ae97f4563ac3924b477473702b8b0ac64d4f72a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 6 Feb 2020 19:37:14 +0100 Subject: [PATCH 099/160] Make dialect option lowercase. --- test/libyul/SyntaxTest.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index 330aba2ee241..419e8e00d94a 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -70,7 +70,7 @@ vector validDialectNames() void SyntaxTest::parseAndAnalyze() { - string dialectName = m_validatedSettings.count("Dialect") ? m_validatedSettings["Dialect"] : "evmTyped"; + string dialectName = m_validatedSettings.count("dialect") ? m_validatedSettings["dialect"] : "evmTyped"; yul::Dialect const& dialect = validDialects.at(dialectName)(m_evmVersion); @@ -119,12 +119,12 @@ bool SyntaxTest::validateSettings(langutil::EVMVersion _evmVersion) if (!CommonSyntaxTest::validateSettings(_evmVersion)) return false; - if (!m_settings.count("Dialect")) + if (!m_settings.count("dialect")) return true; - string const dialect = m_settings["Dialect"]; - m_validatedSettings["Dialect"] = dialect; - m_settings.erase("Dialect"); + string const dialect = m_settings["dialect"]; + m_validatedSettings["dialect"] = dialect; + m_settings.erase("dialect"); if (!validDialects.count(dialect)) BOOST_THROW_EXCEPTION(runtime_error{ From 92c9b078b40d124f253c32502229a746706a47d9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 6 Feb 2020 19:44:14 +0100 Subject: [PATCH 100/160] Use correct instance of EVMDialectTyped. --- test/libyul/SyntaxTest.cpp | 2 +- test/libyul/yulSyntaxTests/simple_functions.yul | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index 419e8e00d94a..be1b9990b7d0 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -45,7 +45,7 @@ std::map const vali { "evmTyped", [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& - { return yul::EVMDialectTyped::strictAssemblyForEVM(_evmVersion); } + { return yul::EVMDialectTyped::instance(_evmVersion); } }, { "yul", diff --git a/test/libyul/yulSyntaxTests/simple_functions.yul b/test/libyul/yulSyntaxTests/simple_functions.yul index dd5c8bdde0b1..5b63885f2b5f 100644 --- a/test/libyul/yulSyntaxTests/simple_functions.yul +++ b/test/libyul/yulSyntaxTests/simple_functions.yul @@ -5,4 +5,6 @@ function h() { let x := msize() } function i() { let z := mload(0) } } +// ==== +// dialect: evm // ---- From b39814a4f84f6f4af6b8bf5e6b7f28ab92a3a5a0 Mon Sep 17 00:00:00 2001 From: "Brian L. McMichael" Date: Thu, 6 Feb 2020 16:39:09 -0500 Subject: [PATCH 101/160] Conform constants to style guide Constants should conform to style guide at https://solidity.readthedocs.io/en/latest/style-guide.html#constants --- docs/contracts/constant-state-variables.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst index 2b4b7ff5b462..33511f020d47 100644 --- a/docs/contracts/constant-state-variables.rst +++ b/docs/contracts/constant-state-variables.rst @@ -29,7 +29,7 @@ value types and strings. pragma solidity >=0.4.0 <0.7.0; contract C { - uint constant x = 32**22 + 8; - string constant text = "abc"; - bytes32 constant myHash = keccak256("abc"); + uint constant X = 32**22 + 8; + string constant TEXT = "abc"; + bytes32 constant MY_HASH = keccak256("abc"); } From b3b8441aa4514764f6ae2387f891f42c6c606127 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Fri, 7 Feb 2020 02:36:51 +0100 Subject: [PATCH 102/160] Fix printing source for missing pragma. --- liblangutil/SourceReferenceExtractor.cpp | 2 +- liblangutil/SourceReferenceFormatter.cpp | 2 ++ liblangutil/SourceReferenceFormatterHuman.cpp | 9 ++++++++- libsolidity/analysis/SyntaxChecker.cpp | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/liblangutil/SourceReferenceExtractor.cpp b/liblangutil/SourceReferenceExtractor.cpp index 44f1540ca1f2..0817e7750d8c 100644 --- a/liblangutil/SourceReferenceExtractor.cpp +++ b/liblangutil/SourceReferenceExtractor.cpp @@ -46,7 +46,7 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio if (!_location || !_location->source.get()) // Nothing we can extract here return SourceReference::MessageOnly(std::move(message)); - if (_location->source->source().empty()) // No source text, so we can only extract the source name + if (!_location->hasText()) // No source text, so we can only extract the source name return SourceReference::MessageOnly(std::move(message), _location->source->name()); shared_ptr const& source = _location->source; diff --git a/liblangutil/SourceReferenceFormatter.cpp b/liblangutil/SourceReferenceFormatter.cpp index 588733d27d6c..6b1a8286a20a 100644 --- a/liblangutil/SourceReferenceFormatter.cpp +++ b/liblangutil/SourceReferenceFormatter.cpp @@ -69,6 +69,8 @@ void SourceReferenceFormatter::printSourceName(SourceReference const& _ref) { if (_ref.position.line != -1) m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": "; + else if (!_ref.sourceName.empty()) + m_stream << _ref.sourceName << ": "; } void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category) diff --git a/liblangutil/SourceReferenceFormatterHuman.cpp b/liblangutil/SourceReferenceFormatterHuman.cpp index 6cd5620fb590..669fa200748f 100644 --- a/liblangutil/SourceReferenceFormatterHuman.cpp +++ b/liblangutil/SourceReferenceFormatterHuman.cpp @@ -67,13 +67,20 @@ AnsiColorized SourceReferenceFormatterHuman::diagColored() const void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _ref) { - if (_ref.position.line < 0) + if (_ref.sourceName.empty()) return; // Nothing we can print here int const leftpad = static_cast(log10(max(_ref.position.line, 1))) + 1; // line 0: source name frameColored() << string(leftpad, ' ') << "--> "; + + if (_ref.position.line < 0) + { + m_stream << _ref.sourceName << "\n"; + return; // No line available, nothing else to print + } + m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ":" << '\n'; if (!_ref.multiline) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 666c15dc5c28..74fa8d458cfb 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -68,7 +68,8 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) to_string(recommendedVersion.patch()) + string(";\""); - m_errorReporter.warning(_sourceUnit.location(), errorString); + // when reporting the warning, print the source name only + m_errorReporter.warning({-1, -1, _sourceUnit.location().source}, errorString); } m_sourceUnit = nullptr; } From e3d6a3e44ef14d6f46262f5e72f08e90298a3c7a Mon Sep 17 00:00:00 2001 From: a3d4 Date: Sun, 9 Feb 2020 17:29:13 +0100 Subject: [PATCH 103/160] Updated expectations. --- test/cmdlineTests/output_selection_all_A1/output.json | 10 +++------- test/cmdlineTests/output_selection_all_A2/output.json | 10 +++------- .../output_selection_all_blank/output.json | 10 +++------- .../cmdlineTests/output_selection_all_star/output.json | 10 +++------- .../output_selection_single_A1/output.json | 6 ++---- .../output_selection_single_B1/output.json | 6 ++---- .../output_selection_single_all/output.json | 6 ++---- test/cmdlineTests/storage_layout_bytes/output.json | 6 ++---- test/cmdlineTests/storage_layout_dyn_array/output.json | 6 ++---- test/cmdlineTests/storage_layout_many/output.json | 6 ++---- test/cmdlineTests/storage_layout_mapping/output.json | 6 ++---- test/cmdlineTests/storage_layout_smoke/output.json | 6 ++---- .../storage_layout_smoke_two_contracts/output.json | 6 ++---- test/cmdlineTests/storage_layout_string/output.json | 6 ++---- test/cmdlineTests/storage_layout_struct/output.json | 6 ++---- .../storage_layout_struct_packed/output.json | 6 ++---- .../storage_layout_value_types/output.json | 6 ++---- .../storage_layout_value_types_packed/output.json | 6 ++---- test/cmdlineTests/too_long_line/err | 5 +---- test/cmdlineTests/too_long_line_both_sides_short/err | 5 +---- test/cmdlineTests/too_long_line_edge_in/err | 5 +---- test/cmdlineTests/too_long_line_edge_out/err | 5 +---- test/cmdlineTests/too_long_line_left_short/err | 5 +---- test/cmdlineTests/too_long_line_multiline/err | 5 +---- test/cmdlineTests/too_long_line_right_short/err | 5 +---- 25 files changed, 47 insertions(+), 112 deletions(-) diff --git a/test/cmdlineTests/output_selection_all_A1/output.json b/test/cmdlineTests/output_selection_all_A1/output.json index 86476fac9aaf..e87df29955ce 100644 --- a/test/cmdlineTests/output_selection_all_A1/output.json +++ b/test/cmdlineTests/output_selection_all_A1/output.json @@ -1,10 +1,6 @@ -{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}},"b.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } } -^---------------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":131,"file":"a.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } -^----------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":126,"file":"b.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure +{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}},"b.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } ^------------------------------------------^ ","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":58,"file":"b.sol","start":14},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_all_A2/output.json b/test/cmdlineTests/output_selection_all_A2/output.json index 38b5b86d420d..7601952b6c58 100644 --- a/test/cmdlineTests/output_selection_all_A2/output.json +++ b/test/cmdlineTests/output_selection_all_A2/output.json @@ -1,10 +1,6 @@ -{"contracts":{"a.sol":{"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } } -^---------------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":131,"file":"a.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } -^----------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":126,"file":"b.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure +{"contracts":{"a.sol":{"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } ^------------------------------------------^ ","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":58,"file":"b.sol","start":14},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_all_blank/output.json b/test/cmdlineTests/output_selection_all_blank/output.json index fe1fcd09a6f4..81c98e790982 100644 --- a/test/cmdlineTests/output_selection_all_blank/output.json +++ b/test/cmdlineTests/output_selection_all_blank/output.json @@ -1,10 +1,6 @@ -{"errors":[{"component":"general","formattedMessage":"a.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } } -^---------------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":131,"file":"a.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } -^----------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":126,"file":"b.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure +{"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } ^------------------------------------------^ ","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":58,"file":"b.sol","start":14},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_all_star/output.json b/test/cmdlineTests/output_selection_all_star/output.json index 29cb854b98ab..0a8071aaf15d 100644 --- a/test/cmdlineTests/output_selection_all_star/output.json +++ b/test/cmdlineTests/output_selection_all_star/output.json @@ -1,10 +1,6 @@ -{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}},"b.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"B2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } } -^---------------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":131,"file":"a.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } -^----------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":126,"file":"b.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure +{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}},"b.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"B2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } ^------------------------------------------^ ","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":58,"file":"b.sol","start":14},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_single_A1/output.json b/test/cmdlineTests/output_selection_single_A1/output.json index 0a1972e45469..5bdef1db92be 100644 --- a/test/cmdlineTests/output_selection_single_A1/output.json +++ b/test/cmdlineTests/output_selection_single_A1/output.json @@ -1,4 +1,2 @@ -{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } } -^---------------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":131,"file":"a.sol","start":0},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_single_B1/output.json b/test/cmdlineTests/output_selection_single_B1/output.json index 3768cbf1673e..fafe87ebdfd9 100644 --- a/test/cmdlineTests/output_selection_single_B1/output.json +++ b/test/cmdlineTests/output_selection_single_B1/output.json @@ -1,7 +1,5 @@ -{"contracts":{"b.sol":{"B2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"b.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } -^----------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":126,"file":"b.sol","start":0},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure +{"contracts":{"b.sol":{"B2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","formattedMessage":"b.sol:1:15: Warning: Function state mutability can be restricted to pure contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } ^------------------------------------------^ ","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":58,"file":"b.sol","start":14},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_single_all/output.json b/test/cmdlineTests/output_selection_single_all/output.json index 0dbecdce971c..f9203fa301e2 100644 --- a/test/cmdlineTests/output_selection_single_all/output.json +++ b/test/cmdlineTests/output_selection_single_all/output.json @@ -1,4 +1,2 @@ -{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol:1:1: Warning: Source file does not specify required compiler version! -contract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } } -^---------------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":131,"file":"a.sol","start":0},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/storage_layout_bytes/output.json b/test/cmdlineTests/storage_layout_bytes/output.json index d2829f360e3f..4414b07d58c2 100644 --- a/test/cmdlineTests/storage_layout_bytes/output.json +++ b/test/cmdlineTests/storage_layout_bytes/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_bytes_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_bytes_storage"}],"types":{"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { bytes s1 = \"test\"; bytes s2; } -^-----------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":43,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_bytes_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_bytes_storage"}],"types":{"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_dyn_array/output.json b/test/cmdlineTests/storage_layout_dyn_array/output.json index 51b3ac1a1406..0581b35a1e93 100644 --- a/test/cmdlineTests/storage_layout_dyn_array/output.json +++ b/test/cmdlineTests/storage_layout_dyn_array/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"array1","offset":0,"slot":"0","type":"t_array(t_uint256)dyn_storage"},{"astId":6,"contract":"fileA:A","label":"array2","offset":0,"slot":"1","type":"t_array(t_bool)dyn_storage"}],"types":{"t_array(t_bool)dyn_storage":{"base":"t_bool","encoding":"dynamic_array","label":"bool[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { uint[] array1; bool[] array2; } -^------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":44,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"array1","offset":0,"slot":"0","type":"t_array(t_uint256)dyn_storage"},{"astId":6,"contract":"fileA:A","label":"array2","offset":0,"slot":"1","type":"t_array(t_bool)dyn_storage"}],"types":{"t_array(t_bool)dyn_storage":{"base":"t_bool","encoding":"dynamic_array","label":"bool[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_many/output.json b/test/cmdlineTests/storage_layout_many/output.json index a2b3f8d2d755..8e14940a9347 100644 --- a/test/cmdlineTests/storage_layout_many/output.json +++ b/test/cmdlineTests/storage_layout_many/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"},{"astId":26,"contract":"fileA:A","label":"map","offset":0,"slot":"7","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"},{"astId":29,"contract":"fileA:A","label":"array","offset":0,"slot":"8","type":"t_array(t_uint256)dyn_storage"},{"astId":31,"contract":"fileA:A","label":"s1","offset":0,"slot":"9","type":"t_string_storage"},{"astId":33,"contract":"fileA:A","label":"b1","offset":0,"slot":"10","type":"t_bytes_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { struct S { uint128 a; ... int[] array; string s1; bytes b1; } -^-------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":206,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"},{"astId":26,"contract":"fileA:A","label":"map","offset":0,"slot":"7","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"},{"astId":29,"contract":"fileA:A","label":"array","offset":0,"slot":"8","type":"t_array(t_uint256)dyn_storage"},{"astId":31,"contract":"fileA:A","label":"s1","offset":0,"slot":"9","type":"t_string_storage"},{"astId":33,"contract":"fileA:A","label":"b1","offset":0,"slot":"10","type":"t_bytes_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_mapping/output.json b/test/cmdlineTests/storage_layout_mapping/output.json index 4696d46c85de..c380197cae0e 100644 --- a/test/cmdlineTests/storage_layout_mapping/output.json +++ b/test/cmdlineTests/storage_layout_mapping/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":10,"contract":"fileA:A","label":"map","offset":0,"slot":"2","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; } -^-----------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":79,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":10,"contract":"fileA:A","label":"map","offset":0,"slot":"2","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_smoke/output.json b/test/cmdlineTests/storage_layout_smoke/output.json index 08191bc5d163..fb8c544c6f6c 100644 --- a/test/cmdlineTests/storage_layout_smoke/output.json +++ b/test/cmdlineTests/storage_layout_smoke/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { } -^------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json b/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json index 761cf83347e1..abbace7a94eb 100644 --- a/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json +++ b/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { } -^------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0},"fileB":{"id":1}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0},"fileB":{"id":1}}} diff --git a/test/cmdlineTests/storage_layout_string/output.json b/test/cmdlineTests/storage_layout_string/output.json index 19066171e793..6ce9e236db4c 100644 --- a/test/cmdlineTests/storage_layout_string/output.json +++ b/test/cmdlineTests/storage_layout_string/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_string_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { string s1 = \"test\"; string s2; } -^-------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":45,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_string_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_struct/output.json b/test/cmdlineTests/storage_layout_struct/output.json index a925c99c033a..6437d599ab2f 100644 --- a/test/cmdlineTests/storage_layout_struct/output.json +++ b/test/cmdlineTests/storage_layout_struct/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"7","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"b","offset":0,"slot":"1","type":"t_uint256"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"2","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"4","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"160"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; } -^------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":116,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"7","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"b","offset":0,"slot":"1","type":"t_uint256"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"2","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"4","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"160"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_struct_packed/output.json b/test/cmdlineTests/storage_layout_struct_packed/output.json index fe9bd0d5085e..44adfefda243 100644 --- a/test/cmdlineTests/storage_layout_struct_packed/output.json +++ b/test/cmdlineTests/storage_layout_struct_packed/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; } -^------------------------------------------------------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":122,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_value_types/output.json b/test/cmdlineTests/storage_layout_value_types/output.json index cb43276ed58e..8b9d2437ffbf 100644 --- a/test/cmdlineTests/storage_layout_value_types/output.json +++ b/test/cmdlineTests/storage_layout_value_types/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":6,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":10,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { uint x; uint y; address addr; uint[2] array; } -^---------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":59,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":6,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":10,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_value_types_packed/output.json b/test/cmdlineTests/storage_layout_value_types_packed/output.json index cd7f866aa638..c7d9563616ee 100644 --- a/test/cmdlineTests/storage_layout_value_types_packed/output.json +++ b/test/cmdlineTests/storage_layout_value_types_packed/output.json @@ -1,4 +1,2 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint64"},{"astId":4,"contract":"fileA:A","label":"y","offset":8,"slot":"0","type":"t_uint128"},{"astId":6,"contract":"fileA:A","label":"z","offset":0,"slot":"1","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":12,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"},"t_uint64":{"encoding":"inplace","label":"uint64","numberOfBytes":"8"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! -contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; } -^-------------------------------------------------------------------------^ -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":75,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint64"},{"astId":4,"contract":"fileA:A","label":"y","offset":8,"slot":"0","type":"t_uint128"},{"astId":6,"contract":"fileA:A","label":"z","offset":0,"slot":"1","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":12,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"},"t_uint64":{"encoding":"inplace","label":"uint64","numberOfBytes":"8"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/too_long_line/err b/test/cmdlineTests/too_long_line/err index 6683949aeda8..87612fe59196 100644 --- a/test/cmdlineTests/too_long_line/err +++ b/test/cmdlineTests/too_long_line/err @@ -1,8 +1,5 @@ Warning: Source file does not specify required compiler version! - --> too_long_line/input.sol:1:1: - | -1 | contract C { - | ^ (Relevant source part starts here and spans across multiple lines). + --> too_long_line/input.sol Error: Identifier not found or not unique. --> too_long_line/input.sol:2:164: diff --git a/test/cmdlineTests/too_long_line_both_sides_short/err b/test/cmdlineTests/too_long_line_both_sides_short/err index aeb7c4b7d2fb..81f44930f2a8 100644 --- a/test/cmdlineTests/too_long_line_both_sides_short/err +++ b/test/cmdlineTests/too_long_line_both_sides_short/err @@ -1,8 +1,5 @@ Warning: Source file does not specify required compiler version! - --> too_long_line_both_sides_short/input.sol:1:1: - | -1 | contract C { - | ^ (Relevant source part starts here and spans across multiple lines). + --> too_long_line_both_sides_short/input.sol Error: Identifier not found or not unique. --> too_long_line_both_sides_short/input.sol:2:15: diff --git a/test/cmdlineTests/too_long_line_edge_in/err b/test/cmdlineTests/too_long_line_edge_in/err index edcac5dee594..b00bec7a1c81 100644 --- a/test/cmdlineTests/too_long_line_edge_in/err +++ b/test/cmdlineTests/too_long_line_edge_in/err @@ -1,8 +1,5 @@ Warning: Source file does not specify required compiler version! - --> too_long_line_edge_in/input.sol:1:1: - | -1 | contract C { - | ^ (Relevant source part starts here and spans across multiple lines). + --> too_long_line_edge_in/input.sol Error: Identifier not found or not unique. --> too_long_line_edge_in/input.sol:2:36: diff --git a/test/cmdlineTests/too_long_line_edge_out/err b/test/cmdlineTests/too_long_line_edge_out/err index 17856f629880..3de913b7d95d 100644 --- a/test/cmdlineTests/too_long_line_edge_out/err +++ b/test/cmdlineTests/too_long_line_edge_out/err @@ -1,8 +1,5 @@ Warning: Source file does not specify required compiler version! - --> too_long_line_edge_out/input.sol:1:1: - | -1 | contract C { - | ^ (Relevant source part starts here and spans across multiple lines). + --> too_long_line_edge_out/input.sol Error: Identifier not found or not unique. --> too_long_line_edge_out/input.sol:2:37: diff --git a/test/cmdlineTests/too_long_line_left_short/err b/test/cmdlineTests/too_long_line_left_short/err index 931c208c0931..a6ddfca81590 100644 --- a/test/cmdlineTests/too_long_line_left_short/err +++ b/test/cmdlineTests/too_long_line_left_short/err @@ -1,8 +1,5 @@ Warning: Source file does not specify required compiler version! - --> too_long_line_left_short/input.sol:1:1: - | -1 | contract C { - | ^ (Relevant source part starts here and spans across multiple lines). + --> too_long_line_left_short/input.sol Error: Identifier not found or not unique. --> too_long_line_left_short/input.sol:2:15: diff --git a/test/cmdlineTests/too_long_line_multiline/err b/test/cmdlineTests/too_long_line_multiline/err index 67ebcff07996..5a2655174714 100644 --- a/test/cmdlineTests/too_long_line_multiline/err +++ b/test/cmdlineTests/too_long_line_multiline/err @@ -5,7 +5,4 @@ Error: No visibility specified. Did you intend to add "public"? | ^ (Relevant source part starts here and spans across multiple lines). Warning: Source file does not specify required compiler version! - --> too_long_line_multiline/input.sol:1:1: - | -1 | contract C { - | ^ (Relevant source part starts here and spans across multiple lines). + --> too_long_line_multiline/input.sol diff --git a/test/cmdlineTests/too_long_line_right_short/err b/test/cmdlineTests/too_long_line_right_short/err index 5f291b9792ea..f5801e616c7f 100644 --- a/test/cmdlineTests/too_long_line_right_short/err +++ b/test/cmdlineTests/too_long_line_right_short/err @@ -1,8 +1,5 @@ Warning: Source file does not specify required compiler version! - --> too_long_line_right_short/input.sol:1:1: - | -1 | contract C { - | ^ (Relevant source part starts here and spans across multiple lines). + --> too_long_line_right_short/input.sol Error: Identifier not found or not unique. --> too_long_line_right_short/input.sol:2:164: From be1d92c8367781aea38b7aaa7d5377d97ee3573f Mon Sep 17 00:00:00 2001 From: a3d4 Date: Mon, 10 Feb 2020 01:16:55 +0100 Subject: [PATCH 104/160] Avoided trailing `\r` in CharStream::lineAtPosition(). --- liblangutil/CharStream.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/liblangutil/CharStream.cpp b/liblangutil/CharStream.cpp index be236893ffb3..046aca5d37f7 100644 --- a/liblangutil/CharStream.cpp +++ b/liblangutil/CharStream.cpp @@ -93,10 +93,13 @@ string CharStream::lineAtPosition(int _position) const lineStart = 0; else lineStart++; - return m_source.substr( + string line = m_source.substr( lineStart, min(m_source.find('\n', lineStart), m_source.size()) - lineStart ); + if (!line.empty() && line.back() == '\r') + line.pop_back(); + return line; } tuple CharStream::translatePositionToLineColumn(int _position) const From 36928c7a3576571bf7841b1723f1d8331ba11232 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 22 Jan 2020 11:48:56 -0300 Subject: [PATCH 105/160] Add reason string for internal reverts --- Changelog.md | 1 + docs/using-the-compiler.rst | 2 +- libsolidity/codegen/ABIFunctions.cpp | 73 ++++++++++++++----- libsolidity/codegen/ABIFunctions.h | 11 ++- libsolidity/codegen/Compiler.cpp | 4 +- libsolidity/codegen/Compiler.h | 6 +- libsolidity/codegen/CompilerContext.cpp | 19 +++-- libsolidity/codegen/CompilerContext.h | 24 +++++- libsolidity/codegen/CompilerUtils.cpp | 60 ++++++++++----- libsolidity/codegen/CompilerUtils.h | 4 +- libsolidity/codegen/ContractCompiler.cpp | 21 +++--- libsolidity/codegen/ContractCompiler.h | 5 +- libsolidity/codegen/ExpressionCompiler.cpp | 22 +++--- libsolidity/codegen/ExpressionCompiler.h | 3 - libsolidity/codegen/YulUtilFunctions.cpp | 42 ++++++++++- libsolidity/codegen/YulUtilFunctions.h | 12 +++ .../codegen/ir/IRGenerationContext.cpp | 9 ++- libsolidity/codegen/ir/IRGenerationContext.h | 15 +++- libsolidity/codegen/ir/IRGenerator.cpp | 6 +- libsolidity/codegen/ir/IRGenerator.h | 10 ++- .../codegen/ir/IRGeneratorForStatements.cpp | 4 +- libsolidity/interface/CompilerStack.cpp | 4 +- libsolidity/interface/StandardCompiler.cpp | 4 +- solc/CommandLineInterface.cpp | 4 +- test/libsolidity/Assembly.cpp | 30 +++++--- test/libsolidity/SemanticTest.cpp | 10 +++ .../SolidityExecutionFramework.cpp | 1 + test/libsolidity/SolidityExecutionFramework.h | 3 + .../SolidityExpressionCompiler.cpp | 6 +- test/libsolidity/StandardCompiler.cpp | 31 ++++---- .../revertStrings/array_slices.sol | 11 +++ .../semanticTests/revertStrings/bubble.sol | 15 ++++ .../calldata_array_dynamic_invalid.sol | 11 +++ ...data_array_dynamic_static_short_decode.sol | 12 +++ ...ta_array_dynamic_static_short_reencode.sol | 14 ++++ .../calldata_array_invalid_length.sol | 11 +++ .../calldata_arrays_too_large.sol | 11 +++ .../revertStrings/calldata_too_short.sol | 11 +++ .../called_contract_has_code.sol | 12 +++ .../semanticTests/revertStrings/enum.sol | 12 +++ .../ether_non_payable_function.sol | 9 +++ .../revertStrings/function_entry_checks.sol | 9 +++ .../invalid_abi_decoding_calldata_v1.sol | 13 ++++ .../invalid_abi_decoding_memory_v1.sol | 20 +++++ .../revertStrings/library_non_view_call.sol | 16 ++++ .../revertStrings/short_input_array.sol | 9 +++ .../revertStrings/short_input_bytes.sol | 9 +++ .../semanticTests/revertStrings/transfer.sol | 27 +++++++ .../revertStrings/unknown_sig_no_fallback.sol | 8 ++ 49 files changed, 555 insertions(+), 131 deletions(-) create mode 100644 test/libsolidity/semanticTests/revertStrings/array_slices.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/bubble.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/calldata_too_short.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/called_contract_has_code.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/enum.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/ether_non_payable_function.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/function_entry_checks.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/invalid_abi_decoding_calldata_v1.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/invalid_abi_decoding_memory_v1.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/library_non_view_call.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/short_input_array.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/transfer.sol create mode 100644 test/libsolidity/semanticTests/revertStrings/unknown_sig_no_fallback.sol diff --git a/Changelog.md b/Changelog.md index a53c6a858043..9bde3fffd27e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Language Features: Compiler Features: * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. * AST: Add a new node for doxygen-style, structured documentation that can be received by contract, function, event and modifier definitions. + * Debug: Provide reason strings for compiler-generated internal reverts when using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting on ``debug`` mode. Bugfixes: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index fcb611f60e0f..eb4ddf0c651a 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -244,7 +244,7 @@ Input Description // "default", "strip", "debug" and "verboseDebug". // "default" does not inject compiler-generated revert strings and keeps user-supplied ones. // "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects - // "debug" injects strings for compiler-generated internal reverts (not yet implemented) + // "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now. // "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented) "revertStrings": "default" } diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index df8cacf86f85..0ad044a54df1 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -180,11 +180,12 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) Whiskers templ(R"( function (headStart, dataEnd) { - if slt(sub(dataEnd, headStart), ) { revert(0, 0) } + if slt(sub(dataEnd, headStart), ) { } } )"); templ("functionName", functionName); + templ("revertString", revertReasonIfDebug("ABI decoding: tuple data too short")); templ("minimumSize", to_string(headSize(decodingTypes))); string decodeElements; @@ -211,7 +212,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) R"( { let offset := (add(headStart, )) - if gt(offset, 0xffffffffffffffff) { revert(0, 0) } + if gt(offset, 0xffffffffffffffff) { } := (add(headStart, offset), dataEnd) } )" : @@ -222,6 +223,8 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) } )" ); + // TODO add test + elementTempl("revertString", revertReasonIfDebug("ABI decoding: invalid tuple offset")); elementTempl("load", _fromMemory ? "mload" : "calldataload"); elementTempl("values", boost::algorithm::join(valueNamesLocal, ", ")); elementTempl("pos", to_string(headPos)); @@ -453,12 +456,14 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup( else templ("scaleLengthByStride", Whiskers(R"( - if gt(length, ) { revert(0, 0) } + if gt(length, ) { } length := mul(length, ) )") ("stride", toCompactHexWithPrefix(fromArrayType.calldataStride())) ("maxLength", toCompactHexWithPrefix(u256(-1) / fromArrayType.calldataStride())) + ("revertString", revertReasonIfDebug("ABI encoding: array data too long")) .render() + // TODO add revert test ); templ("readableTypeNameFrom", _from.toString(true)); templ("readableTypeNameTo", _to.toString(true)); @@ -1124,7 +1129,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from R"( // function (offset, end) -> array { - if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { } let length := array := ((length)) let dst := array @@ -1141,6 +1146,8 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from } )" ); + // TODO add test + templ("revertString", revertReasonIfDebug("ABI decoding: invalid calldata array offset")); templ("functionName", functionName); templ("readableTypeName", _type.toString(true)); templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)"); @@ -1159,7 +1166,12 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from } else { - templ("staticBoundsCheck", "if gt(add(src, mul(length, " + calldataStride + ")), end) { revert(0, 0) }"); + templ("staticBoundsCheck", "if gt(add(src, mul(length, " + + calldataStride + + ")), end) { " + + revertReasonIfDebug("ABI decoding: invalid calldata array stride") + + " }" + ); templ("retrieveElementPos", "src"); } templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false)); @@ -1184,11 +1196,11 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) templ = R"( // function (offset, end) -> arrayPos, length { - if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { } length := calldataload(offset) - if gt(length, 0xffffffffffffffff) { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { } arrayPos := add(offset, 0x20) - if gt(add(arrayPos, mul(length, )), end) { revert(0, 0) } + if gt(add(arrayPos, mul(length, )), end) { } } )"; else @@ -1196,10 +1208,14 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) // function (offset, end) -> arrayPos { arrayPos := offset - if gt(add(arrayPos, mul(, )), end) { revert(0, 0) } + if gt(add(arrayPos, mul(, )), end) { } } )"; Whiskers w{templ}; + // TODO add test + w("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid calldata array offset")); + w("revertStringLength", revertReasonIfDebug("ABI decoding: invalid calldata array length")); + w("revertStringPos", revertReasonIfDebug("ABI decoding: invalid calldata array stride")); w("functionName", functionName); w("readableTypeName", _type.toString(true)); w("stride", toCompactHexWithPrefix(_type.calldataStride())); @@ -1223,17 +1239,20 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _ Whiskers templ( R"( function (offset, end) -> array { - if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { } let length := (offset) array := ((length)) mstore(array, length) let src := add(offset, 0x20) let dst := add(array, 0x20) - if gt(add(src, length), end) { revert(0, 0) } + if gt(add(src, length), end) { } (src, dst, length) } )" ); + // TODO add test + templ("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid byte array offset")); + templ("revertStringLength", revertReasonIfDebug("ABI decoding: invalid byte array length")); templ("functionName", functionName); templ("load", _fromMemory ? "mload" : "calldataload"); templ("allocate", m_utils.allocationFunction()); @@ -1254,10 +1273,12 @@ string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type) Whiskers w{R"( // function (offset, end) -> value { - if slt(sub(end, offset), ) { revert(0, 0) } + if slt(sub(end, offset), ) { } value := offset } )"}; + // TODO add test + w("revertString", revertReasonIfDebug("ABI decoding: struct calldata too short")); w("functionName", functionName); w("readableTypeName", _type.toString(true)); w("minimumSize", to_string(_type.isDynamicallyEncoded() ? _type.calldataEncodedTailSize() : _type.calldataEncodedSize(true))); @@ -1277,7 +1298,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr Whiskers templ(R"( // function (headStart, end) -> value { - if slt(sub(end, headStart), ) { revert(0, 0) } + if slt(sub(end, headStart), ) { } value := () <#members> { @@ -1287,6 +1308,8 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr } )"); + // TODO add test + templ("revertString", revertReasonIfDebug("ABI decoding: struct data too short")); templ("functionName", functionName); templ("readableTypeName", _type.toString(true)); templ("allocate", m_utils.allocationFunction()); @@ -1305,7 +1328,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr dynamic ? R"( let offset := (add(headStart, )) - if gt(offset, 0xffffffffffffffff) { revert(0, 0) } + if gt(offset, 0xffffffffffffffff) { } mstore(add(value, ), (add(headStart, offset), end)) )" : R"( @@ -1313,6 +1336,8 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr mstore(add(value, ), (add(headStart, offset), end)) )" ); + // TODO add test + memberTempl("revertString", revertReasonIfDebug("ABI decoding: invalid struct offset")); memberTempl("load", _fromMemory ? "mload" : "calldataload"); memberTempl("pos", to_string(headPos)); memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name))); @@ -1380,7 +1405,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type) Whiskers w(R"( function (base_ref, ptr) -> { let rel_offset_of_tail := calldataload(ptr) - if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { revert(0, 0) } + if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { } value := add(rel_offset_of_tail, base_ref) } @@ -1392,9 +1417,15 @@ string ABIFunctions::calldataAccessFunction(Type const& _type) w("handleLength", Whiskers(R"( length := calldataload(value) value := add(value, 0x20) - if gt(length, 0xffffffffffffffff) { revert(0, 0) } - if sgt(base_ref, sub(calldatasize(), mul(length, ))) { revert(0, 0) } - )")("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride())).render()); + if gt(length, 0xffffffffffffffff) { } + if sgt(base_ref, sub(calldatasize(), mul(length, ))) { } + )") + ("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride())) + // TODO add test + ("revertStringLength", revertReasonIfDebug("Invalid calldata access length")) + // TODO add test + ("revertStringStride", revertReasonIfDebug("Invalid calldata access stride")) + .render()); w("return", "value, length"); } else @@ -1404,6 +1435,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type) } w("neededLength", toCompactHexWithPrefix(tailSize)); w("functionName", functionName); + w("revertStringOffset", revertReasonIfDebug("Invalid calldata access offset")); return w.render(); } else if (_type.isValueType()) @@ -1493,3 +1525,8 @@ size_t ABIFunctions::numVariablesForType(Type const& _type, EncodingOptions cons else return _type.sizeOnStack(); } + +std::string ABIFunctions::revertReasonIfDebug(std::string const& _message) +{ + return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); +} diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index f8f09a3f31ea..a0b8333df9e5 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -25,6 +25,8 @@ #include #include +#include + #include #include @@ -55,11 +57,13 @@ class ABIFunctions public: explicit ABIFunctions( langutil::EVMVersion _evmVersion, + RevertStrings _revertStrings, std::shared_ptr _functionCollector = std::make_shared() ): m_evmVersion(_evmVersion), + m_revertStrings(_revertStrings), m_functionCollector(std::move(_functionCollector)), - m_utils(_evmVersion, m_functionCollector) + m_utils(_evmVersion, m_revertStrings, m_functionCollector) {} /// @returns name of an assembly function to ABI-encode values of @a _givenTypes @@ -249,7 +253,12 @@ class ABIFunctions /// is true), for which it is two. static size_t numVariablesForType(Type const& _type, EncodingOptions const& _options); + /// @returns code that stores @param _message for revert reason + /// if m_revertStrings is debug. + std::string revertReasonIfDebug(std::string const& _message = ""); + langutil::EVMVersion m_evmVersion; + RevertStrings const m_revertStrings; std::shared_ptr m_functionCollector; std::set m_externallyUsedFunctions; YulUtilFunctions m_utils; diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index a6452adb26e0..78198fff038e 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -35,7 +35,7 @@ void Compiler::compileContract( bytes const& _metadata ) { - ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimiserSettings, m_revertStrings); + ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimiserSettings); runtimeCompiler.compileContract(_contract, _otherCompilers); m_runtimeContext.appendAuxiliaryData(_metadata); @@ -45,7 +45,7 @@ void Compiler::compileContract( // The creation code will be executed at most once, so we modify the optimizer // settings accordingly. creationSettings.expectedExecutionsPerDeployment = 1; - ContractCompiler creationCompiler(&runtimeCompiler, m_context, creationSettings, m_revertStrings); + ContractCompiler creationCompiler(&runtimeCompiler, m_context, creationSettings); m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers); m_context.optimise(m_optimiserSettings); diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index e4a8adc6af2a..8bd21e586446 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -37,9 +37,8 @@ class Compiler public: Compiler(langutil::EVMVersion _evmVersion, RevertStrings _revertStrings, OptimiserSettings _optimiserSettings): m_optimiserSettings(std::move(_optimiserSettings)), - m_revertStrings(_revertStrings), - m_runtimeContext(_evmVersion), - m_context(_evmVersion, &m_runtimeContext) + m_runtimeContext(_evmVersion, _revertStrings), + m_context(_evmVersion, _revertStrings, &m_runtimeContext) { } /// Compiles a contract. @@ -80,7 +79,6 @@ class Compiler private: OptimiserSettings const m_optimiserSettings; - RevertStrings const m_revertStrings; CompilerContext m_runtimeContext; size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present. CompilerContext m_context; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 645e6e61ea4b..f1f386300605 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -37,6 +37,8 @@ #include #include +#include + #include #include #include @@ -55,6 +57,7 @@ using namespace std; using namespace solidity; +using namespace solidity::util; using namespace solidity::evmasm; using namespace solidity::frontend; using namespace solidity::langutil; @@ -296,12 +299,13 @@ CompilerContext& CompilerContext::appendConditionalInvalid() return *this; } -CompilerContext& CompilerContext::appendRevert() +CompilerContext& CompilerContext::appendRevert(string const& _message) { - return *this << u256(0) << u256(0) << Instruction::REVERT; + appendInlineAssembly("{ " + revertReasonIfDebug(_message) + " }"); + return *this; } -CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnData) +CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnData, string const& _message) { if (_forwardReturnData && m_evmVersion.supportsReturndata()) appendInlineAssembly(R"({ @@ -311,9 +315,7 @@ CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnDat } })", {"condition"}); else - appendInlineAssembly(R"({ - if condition { revert(0, 0) } - })", {"condition"}); + appendInlineAssembly("{ if condition { " + revertReasonIfDebug(_message) + " } }", {"condition"}); *this << Instruction::POP; return *this; } @@ -488,6 +490,11 @@ vector::const_iterator CompilerContext::superContract return ++it; } +string CompilerContext::revertReasonIfDebug(string const& _message) +{ + return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); +} + void CompilerContext::updateSourceLocation() { m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location()); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 45f16f1f19d9..28aefa036556 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -51,11 +52,16 @@ class Compiler; class CompilerContext { public: - explicit CompilerContext(langutil::EVMVersion _evmVersion, CompilerContext* _runtimeContext = nullptr): + explicit CompilerContext( + langutil::EVMVersion _evmVersion, + RevertStrings _revertStrings, + CompilerContext* _runtimeContext = nullptr + ): m_asm(std::make_shared()), m_evmVersion(_evmVersion), + m_revertStrings(_revertStrings), m_runtimeContext(_runtimeContext), - m_abiFunctions(m_evmVersion) + m_abiFunctions(m_evmVersion, m_revertStrings) { if (m_runtimeContext) m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); @@ -160,12 +166,14 @@ class CompilerContext /// Appends a conditional INVALID instruction CompilerContext& appendConditionalInvalid(); /// Appends a REVERT(0, 0) call - CompilerContext& appendRevert(); + /// @param _message is an optional revert message used in debug mode + CompilerContext& appendRevert(std::string const& _message = ""); /// Appends a conditional REVERT-call, either forwarding the RETURNDATA or providing the /// empty string. Consumes the condition. /// If the current EVM version does not support RETURNDATA, uses REVERT but does not forward /// the data. - CompilerContext& appendConditionalRevert(bool _forwardReturnData = false); + /// @param _message is an optional revert message used in debug mode + CompilerContext& appendConditionalRevert(bool _forwardReturnData = false, std::string const& _message = ""); /// Appends a JUMP to a specific tag CompilerContext& appendJumpTo( evmasm::AssemblyItem const& _tag, @@ -219,6 +227,11 @@ class CompilerContext OptimiserSettings const& _optimiserSettings = OptimiserSettings::none() ); + /// If m_revertStrings is debug, @returns inline assembly code that + /// stores @param _message in memory position 0 and reverts. + /// Otherwise returns "revert(0, 0)". + std::string revertReasonIfDebug(std::string const& _message = ""); + /// Appends arbitrary data to the end of the bytecode. void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } @@ -263,6 +276,8 @@ class CompilerContext void setModifierDepth(size_t _modifierDepth) { m_asm->m_currentModifierDepth = _modifierDepth; } + RevertStrings revertStrings() const { return m_revertStrings; } + private: /// Searches the inheritance hierarchy towards the base starting from @a _searchStart and returns /// the first function definition that is overwritten by _function. @@ -312,6 +327,7 @@ class CompilerContext evmasm::AssemblyPointer m_asm; /// Version of the EVM to compile against. langutil::EVMVersion m_evmVersion; + RevertStrings const m_revertStrings; /// Activated experimental features. std::set m_experimentalFeatures; /// Other already compiled contracts to be used in contract creation calls. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index c15d2537e5a4..4c75081ffbc6 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -130,9 +130,12 @@ void CompilerUtils::accessCalldataTail(Type const& _type) // returns the absolute offset of the tail in "base_ref" m_context.appendInlineAssembly(Whiskers(R"({ let rel_offset_of_tail := calldataload(ptr_to_tail) - if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { revert(0, 0) } + if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { } base_ref := add(base_ref, rel_offset_of_tail) - })")("neededLength", toCompactHexWithPrefix(tailSize)).render(), {"base_ref", "ptr_to_tail"}); + })") + ("neededLength", toCompactHexWithPrefix(tailSize)) + ("revertString", m_context.revertReasonIfDebug("Invalid calldata tail offset")) + .render(), {"base_ref", "ptr_to_tail"}); // stack layout: if (!_type.isDynamicallySized()) @@ -158,9 +161,12 @@ void CompilerUtils::accessCalldataTail(Type const& _type) Whiskers(R"({ length := calldataload(base_ref) base_ref := add(base_ref, 0x20) - if gt(length, 0xffffffffffffffff) { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { } if sgt(base_ref, sub(calldatasize(), mul(length, ))) { revert(0, 0) } - })")("calldataStride", toCompactHexWithPrefix(calldataStride)).render(), + })") + ("calldataStride", toCompactHexWithPrefix(calldataStride)) + ("revertString", m_context.revertReasonIfDebug("Invalid calldata tail length")) + .render(), {"base_ref", "length"} ); // stack layout: @@ -277,7 +283,13 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem size_t encodedSize = 0; for (auto const& t: _typeParameters) encodedSize += t->decodingType()->calldataHeadSize(); - m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"}); + + Whiskers templ(R"({ + if lt(len, ) { } + })"); + templ("encodedSize", to_string(encodedSize)); + templ("revertString", m_context.revertReasonIfDebug("Calldata too short")); + m_context.appendInlineAssembly(templ.render(), {"len"}); m_context << Instruction::DUP2 << Instruction::ADD; m_context << Instruction::SWAP1; @@ -319,19 +331,23 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem // Check that the data pointer is valid and that length times // item size is still inside the range. Whiskers templ(R"({ - if gt(ptr, 0x100000000) { revert(0, 0) } + if gt(ptr, 0x100000000) { } ptr := add(ptr, base_offset) let array_data_start := add(ptr, 0x20) - if gt(array_data_start, input_end) { revert(0, 0) } + if gt(array_data_start, input_end) { } let array_length := mload(ptr) if or( gt(array_length, 0x100000000), gt(add(array_data_start, mul(array_length, )), input_end) - ) { revert(0, 0) } + ) { } mstore(dst, array_length) dst := add(dst, 0x20) })"); templ("item_size", to_string(arrayType.calldataStride())); + // TODO add test + templ("revertStringPointer", m_context.revertReasonIfDebug("ABI memory decoding: invalid data pointer")); + templ("revertStringStart", m_context.revertReasonIfDebug("ABI memory decoding: invalid data start")); + templ("revertStringLength", m_context.revertReasonIfDebug("ABI memory decoding: invalid data length")); m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr", "dst"}); // stack: v1 v2 ... v(k-1) dstmem input_end base_offset current_offset data_ptr dstdata m_context << Instruction::SWAP1; @@ -359,24 +375,33 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem loadFromMemoryDynamic(*TypeProvider::uint256(), !_fromMemory); m_context << Instruction::SWAP1; // stack: input_end base_offset next_pointer data_offset - m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"}); + m_context.appendInlineAssembly(Whiskers(R"({ + if gt(data_offset, 0x100000000) { } + })") + // TODO add test + ("revertString", m_context.revertReasonIfDebug("ABI calldata decoding: invalid data offset")) + .render(), {"data_offset"}); m_context << Instruction::DUP3 << Instruction::ADD; // stack: input_end base_offset next_pointer array_head_ptr - m_context.appendInlineAssembly( - "{ if gt(add(array_head_ptr, 0x20), input_end) { revert(0, 0) } }", - {"input_end", "base_offset", "next_ptr", "array_head_ptr"} - ); + m_context.appendInlineAssembly(Whiskers(R"({ + if gt(add(array_head_ptr, 0x20), input_end) { } + })") + ("revertString", m_context.revertReasonIfDebug("ABI calldata decoding: invalid head pointer")) + .render(), {"input_end", "base_offset", "next_ptr", "array_head_ptr"}); + // retrieve length loadFromMemoryDynamic(*TypeProvider::uint256(), !_fromMemory, true); // stack: input_end base_offset next_pointer array_length data_pointer m_context << Instruction::SWAP2; // stack: input_end base_offset data_pointer array_length next_pointer - m_context.appendInlineAssembly(R"({ + m_context.appendInlineAssembly(Whiskers(R"({ if or( gt(array_length, 0x100000000), gt(add(data_ptr, mul(array_length, )" + to_string(arrayType.calldataStride()) + R"()), input_end) - ) { revert(0, 0) } - })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); + ) { } + })") + ("revertString", m_context.revertReasonIfDebug("ABI calldata decoding: invalid data pointer")) + .render(), {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); } else { @@ -792,8 +817,7 @@ void CompilerUtils::convertType( solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; if (_asPartOfArgumentDecoding) - // TODO: error message? - m_context.appendConditionalRevert(); + m_context.appendConditionalRevert(false, "Enum out of range"); else m_context.appendConditionalInvalid(); enumOverflowCheckPending = false; diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 5cc0ac9c8d44..3d5d858251ae 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -34,7 +35,8 @@ class Type; // forward class CompilerUtils { public: - explicit CompilerUtils(CompilerContext& _context): m_context(_context) {} + explicit CompilerUtils(CompilerContext& _context): m_context(_context) + {} /// Stores the initial value of the free-memory-pointer at its position; void initialiseFreeMemoryPointer(); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index ecf62d29a8ac..bbf6472de0ae 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -128,8 +128,7 @@ void ContractCompiler::appendCallValueCheck() { // Throw if function is not payable but call contained ether. m_context << Instruction::CALLVALUE; - // TODO: error message? - m_context.appendConditionalRevert(); + m_context.appendConditionalRevert(false, "Ether sent to non-payable function"); } void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) @@ -409,7 +408,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac m_context << notFoundOrReceiveEther; if (!fallback && !etherReceiver) - m_context.appendRevert(); + m_context.appendRevert("Contract does not have fallback nor receive functions"); else { if (etherReceiver) @@ -440,8 +439,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac m_context << Instruction::STOP; } else - // TODO: error message here? - m_context.appendRevert(); + m_context.appendRevert("Unknown signature and no fallback defined"); } @@ -457,7 +455,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac // If the function is not a view function and is called without DELEGATECALL, // we revert. m_context << dupInstruction(2); - m_context.appendConditionalRevert(); + m_context.appendConditionalRevert(false, "Non-view function of library called without DELEGATECALL"); } m_context.setStackOffset(0); // We have to allow this for libraries, because value of the previous @@ -517,7 +515,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); for (VariableDeclaration const* variable: _contract.stateVariables()) if (variable->value() && !variable->isConstant()) - ExpressionCompiler(m_context, m_revertStrings, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable); + ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable); } bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration) @@ -530,10 +528,10 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration) m_continueTags.clear(); if (_variableDeclaration.isConstant()) - ExpressionCompiler(m_context, m_revertStrings, m_optimiserSettings.runOrderLiterals) + ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals) .appendConstStateVariableAccessor(_variableDeclaration); else - ExpressionCompiler(m_context, m_revertStrings, m_optimiserSettings.runOrderLiterals) + ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals) .appendStateVariableAccessor(_variableDeclaration); return false; @@ -954,7 +952,8 @@ void ContractCompiler::handleCatch(vector> const& _ca revert(0, returndatasize()) })"); else - m_context.appendRevert(); + // Since both returndata and revert are >=byzantium, this should be unreachable. + solAssert(false, ""); } m_context << endTag; } @@ -1316,7 +1315,7 @@ void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration con void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType) { - ExpressionCompiler expressionCompiler(m_context, m_revertStrings, m_optimiserSettings.runOrderLiterals); + ExpressionCompiler expressionCompiler(m_context, m_optimiserSettings.runOrderLiterals); expressionCompiler.compile(_expression); if (_targetType) CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 02fcbe4ca4aa..0a2ecf7e8741 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -43,11 +43,9 @@ class ContractCompiler: private ASTConstVisitor explicit ContractCompiler( ContractCompiler* _runtimeCompiler, CompilerContext& _context, - OptimiserSettings _optimiserSettings, - RevertStrings _revertStrings + OptimiserSettings _optimiserSettings ): m_optimiserSettings(std::move(_optimiserSettings)), - m_revertStrings(_revertStrings), m_runtimeCompiler(_runtimeCompiler), m_context(_context) { @@ -140,7 +138,6 @@ class ContractCompiler: private ASTConstVisitor void storeStackHeight(ASTNode const* _node); OptimiserSettings const m_optimiserSettings; - RevertStrings const m_revertStrings; /// Pointer to the runtime compiler in case this is a creation compiler. ContractCompiler* m_runtimeCompiler = nullptr; CompilerContext& m_context; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index a2812ce514c8..c8ded907874d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -677,7 +677,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << errorCase; } else - // TODO: Can we bubble up here? There might be different reasons for failure, I think. m_context.appendConditionalRevert(true); break; } @@ -734,8 +733,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) if (function.kind() == FunctionType::Kind::Transfer) { // Check if zero (out of stack or not enough balance). - // TODO: bubble up here, but might also be different error. m_context << Instruction::ISZERO; + // Revert message bubbles up. m_context.appendConditionalRevert(true); } break; @@ -752,7 +751,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // function-sel(Error(string)) + encoding solAssert(arguments.size() == 1, ""); solAssert(function.parameterTypes().size() == 1, ""); - if (m_revertStrings == RevertStrings::Strip) + if (m_context.revertStrings() == RevertStrings::Strip) { if (!arguments.front()->annotation().isPure) { @@ -1032,7 +1031,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { acceptAndConvert(*arguments.front(), *function.parameterTypes().front(), false); - bool haveReasonString = arguments.size() > 1 && m_revertStrings != RevertStrings::Strip; + bool haveReasonString = arguments.size() > 1 && m_context.revertStrings() != RevertStrings::Strip; if (arguments.size() > 1) { @@ -1041,7 +1040,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // function call. solAssert(arguments.size() == 2, ""); solAssert(function.kind() == FunctionType::Kind::Require, ""); - if (m_revertStrings == RevertStrings::Strip) + if (m_context.revertStrings() == RevertStrings::Strip) { if (!arguments.at(1)->annotation().isPure) { @@ -1821,12 +1820,16 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess) m_context.appendInlineAssembly( Whiskers(R"({ - if gt(sliceStart, sliceEnd) { revert(0, 0) } - if gt(sliceEnd, length) { revert(0, 0) } + if gt(sliceStart, sliceEnd) { } + if gt(sliceEnd, length) { } offset := add(offset, mul(sliceStart, )) length := sub(sliceEnd, sliceStart) - })")("stride", toString(arrayType->calldataStride())).render(), + })") + ("stride", toString(arrayType->calldataStride())) + ("revertStringStartEnd", m_context.revertReasonIfDebug("Slice starts after end")) + ("revertStringEndLength", m_context.revertReasonIfDebug("Slice is greater than length")) + .render(), {"offset", "length", "sliceStart", "sliceEnd"} ); @@ -2299,8 +2302,7 @@ void ExpressionCompiler::appendExternalFunctionCall( if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; - // TODO: error message? - m_context.appendConditionalRevert(); + m_context.appendConditionalRevert(false, "Target contract does not contain code"); existenceChecked = true; } diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index a64efd657da3..33fb2696fe90 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -58,10 +58,8 @@ class ExpressionCompiler: private ASTConstVisitor public: ExpressionCompiler( CompilerContext& _compilerContext, - RevertStrings _revertStrings, bool _optimiseOrderLiterals ): - m_revertStrings(_revertStrings), m_optimiseOrderLiterals(_optimiseOrderLiterals), m_context(_compilerContext) {} @@ -139,7 +137,6 @@ class ExpressionCompiler: private ASTConstVisitor /// @returns the CompilerUtils object containing the current context. CompilerUtils utils(); - RevertStrings m_revertStrings; bool m_optimiseOrderLiterals; CompilerContext& m_context; std::unique_ptr m_currentLValue; diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index b06d071825ba..4cc2c87144c1 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -134,7 +134,7 @@ string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _mess FixedHash(keccak256("Error(string)")) )) << (256 - hashHeaderSize * byteSize); - string const encodeFunc = ABIFunctions(m_evmVersion, m_functionCollector) + string const encodeFunc = ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector) .tupleEncoder( {_messageType}, {TypeProvider::stringMemory()} @@ -1627,7 +1627,7 @@ string YulUtilFunctions::packedHashFunction( templ("variables", suffixedVariableNameList("var_", 1, 1 + sizeOnStack)); templ("comma", sizeOnStack > 0 ? "," : ""); templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)); - templ("packedEncode", ABIFunctions(m_evmVersion, m_functionCollector).tupleEncoderPacked(_givenTypes, _targetTypes)); + templ("packedEncode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleEncoderPacked(_givenTypes, _targetTypes)); return templ.render(); }); } @@ -1905,3 +1905,41 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC .render(); }); } + +string YulUtilFunctions::revertReasonIfDebug(RevertStrings revertStrings, string const& _message) +{ + if (revertStrings >= RevertStrings::Debug && !_message.empty()) + { + Whiskers templ(R"({ + mstore(0, ) + mstore(4, 0x20) + mstore(add(4, 0x20), ) + let reasonPos := add(4, 0x40) + <#word> + mstore(add(reasonPos, ), ) + + revert(0, add(reasonPos, )) + })"); + templ("sig", (u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)).str()); + templ("length", to_string(_message.length())); + + size_t words = (_message.length() + 31) / 32; + vector> wordParams(words); + for (size_t i = 0; i < words; ++i) + { + wordParams[i]["offset"] = to_string(i * 32); + wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32)); + } + templ("word", wordParams); + templ("end", to_string(words * 32)); + + return templ.render(); + } + else + return "revert(0, 0)"; +} + +string YulUtilFunctions::revertReasonIfDebug(string const& _message) +{ + return revertReasonIfDebug(m_revertStrings, _message); +} diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index df6292b42da9..859290ffabe3 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -24,6 +24,8 @@ #include +#include + #include #include #include @@ -44,9 +46,11 @@ class YulUtilFunctions public: explicit YulUtilFunctions( langutil::EVMVersion _evmVersion, + RevertStrings _revertStrings, std::shared_ptr _functionCollector ): m_evmVersion(_evmVersion), + m_revertStrings(_revertStrings), m_functionCollector(std::move(_functionCollector)) {} @@ -281,6 +285,13 @@ class YulUtilFunctions /// zero /// signature: (slot, offset) -> std::string storageSetToZeroFunction(Type const& _type); + + /// If revertStrings is debug, @returns inline assembly code that + /// stores @param _message in memory position 0 and reverts. + /// Otherwise returns "revert(0, 0)". + static std::string revertReasonIfDebug(RevertStrings revertStrings, std::string const& _message = ""); + + std::string revertReasonIfDebug(std::string const& _message = ""); private: /// Special case of conversionFunction - handles everything that does not /// use exactly one variable to hold the value. @@ -289,6 +300,7 @@ class YulUtilFunctions std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata); langutil::EVMVersion m_evmVersion; + RevertStrings m_revertStrings; std::shared_ptr m_functionCollector; }; diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index 9c8e3b974ea5..bd54e1fbda01 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -133,7 +133,7 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out) )"); templ("functionName", funName); templ("comma", _in > 0 ? "," : ""); - YulUtilFunctions utils(m_evmVersion, m_functions); + YulUtilFunctions utils(m_evmVersion, m_revertStrings, m_functions); templ("in", suffixedVariableNameList("in_", 0, _in)); templ("arrow", _out > 0 ? "->" : ""); templ("out", suffixedVariableNameList("out_", 0, _out)); @@ -161,5 +161,10 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out) YulUtilFunctions IRGenerationContext::utils() { - return YulUtilFunctions(m_evmVersion, m_functions); + return YulUtilFunctions(m_evmVersion, m_revertStrings, m_functions); +} + +std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message) +{ + return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); } diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 9c1844f08e0c..698a6d38a854 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include @@ -47,8 +48,13 @@ class YulUtilFunctions; class IRGenerationContext { public: - IRGenerationContext(langutil::EVMVersion _evmVersion, OptimiserSettings _optimiserSettings): + IRGenerationContext( + langutil::EVMVersion _evmVersion, + RevertStrings _revertStrings, + OptimiserSettings _optimiserSettings + ): m_evmVersion(_evmVersion), + m_revertStrings(_revertStrings), m_optimiserSettings(std::move(_optimiserSettings)), m_functions(std::make_shared()) {} @@ -92,8 +98,15 @@ class IRGenerationContext langutil::EVMVersion evmVersion() const { return m_evmVersion; }; + /// @returns code that stores @param _message for revert reason + /// if m_revertStrings is debug. + std::string revertReasonIfDebug(std::string const& _message = ""); + + RevertStrings revertStrings() const { return m_revertStrings; } + private: langutil::EVMVersion m_evmVersion; + RevertStrings m_revertStrings; OptimiserSettings m_optimiserSettings; std::vector m_inheritanceHierarchy; std::map m_localVariables; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 0798ebc37b69..69c47df00dea 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -333,7 +333,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) templ["assignToParams"] = paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := "; templ["assignToRetParams"] = retVars == 0 ? "" : "let " + suffixedVariableNameList("ret_", 0, retVars) + " := "; - ABIFunctions abiFunctions(m_evmVersion, m_context.functionCollector()); + ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector()); templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes()); templ["params"] = suffixedVariableNameList("param_", 0, paramVars); templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0); @@ -386,8 +386,8 @@ void IRGenerator::resetContext(ContractDefinition const& _contract) m_context.functionCollector()->requestedFunctions().empty(), "Reset context while it still had functions." ); - m_context = IRGenerationContext(m_evmVersion, m_optimiserSettings); - m_utils = YulUtilFunctions(m_evmVersion, m_context.functionCollector()); + m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings); + m_utils = YulUtilFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector()); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); for (auto const& var: ContractType(_contract).stateVariables()) diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index 2481f08dd603..d1ec580f61d4 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -37,11 +37,15 @@ class SourceUnit; class IRGenerator { public: - IRGenerator(langutil::EVMVersion _evmVersion, OptimiserSettings _optimiserSettings): + IRGenerator( + langutil::EVMVersion _evmVersion, + RevertStrings _revertStrings, + OptimiserSettings _optimiserSettings + ): m_evmVersion(_evmVersion), m_optimiserSettings(_optimiserSettings), - m_context(_evmVersion, std::move(_optimiserSettings)), - m_utils(_evmVersion, m_context.functionCollector()) + m_context(_evmVersion, _revertStrings, std::move(_optimiserSettings)), + m_utils(_evmVersion, m_context.revertStrings(), m_context.functionCollector()) {} /// Generates and returns the IR code, in unoptimized and optimized form diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 16c11e3e78c5..2d38fe026097 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -534,7 +534,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { auto const& event = dynamic_cast(functionType->declaration()); TypePointers paramTypes = functionType->parameterTypes(); - ABIFunctions abi(m_context.evmVersion(), m_context.functionCollector()); + ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); vector indexedArgs; string nonIndexedArgs; @@ -1151,7 +1151,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( m_code << "mstore(add(" << fetchFreeMem() << ", " << to_string(retSize) << "), 0)\n"; } - ABIFunctions abi(m_context.evmVersion(), m_context.functionCollector()); + ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); solUnimplementedAssert(!funType.isBareCall(), ""); Whiskers templ(R"( diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index dfa7629da342..6940730cfe12 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -165,7 +165,7 @@ void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings) { if (m_stackState >= ParsingPerformed) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set revert string settings before parsing.")); - solUnimplementedAssert(_revertStrings == RevertStrings::Default || _revertStrings == RevertStrings::Strip, ""); + solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug, ""); m_revertStrings = _revertStrings; } @@ -1112,7 +1112,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) for (auto const* dependency: _contract.annotation().contractDependencies) generateIR(*dependency); - IRGenerator generator(m_evmVersion, m_optimiserSettings); + IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings); tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract); } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 3a86914d1441..1b09b2f2c8f7 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -661,10 +661,10 @@ boost::variant StandardCompile std::optional revertStrings = revertStringsFromString(settings["debug"]["revertStrings"].asString()); if (!revertStrings) return formatFatalError("JSONError", "Invalid value for settings.debug.revertStrings."); - if (*revertStrings != RevertStrings::Default && *revertStrings != RevertStrings::Strip) + if (*revertStrings == RevertStrings::VerboseDebug) return formatFatalError( "UnimplementedFeatureError", - "Only \"default\" and \"strip\" are implemented for settings.debug.revertStrings for now." + "Only \"default\", \"strip\" and \"debug\" are implemented for settings.debug.revertStrings for now." ); ret.revertStrings = *revertStrings; } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 1598bbb6880c..57cdaf94352e 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -875,9 +875,9 @@ Allowed options)", serr() << "Invalid option for --" << g_strRevertStrings << ": " << revertStringsString << endl; return false; } - if (*revertStrings != RevertStrings::Default && *revertStrings != RevertStrings::Strip) + if (*revertStrings == RevertStrings::VerboseDebug) { - serr() << "Only \"default\" and \"strip\" are implemented for --" << g_strRevertStrings << " for now." << endl; + serr() << "Only \"default\", \"strip\" and \"debug\" are implemented for --" << g_strRevertStrings << " for now." << endl; return false; } m_revertStrings = *revertStrings; diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 3c403f74ebee..f762849e838c 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -169,26 +169,32 @@ BOOST_AUTO_TEST_CASE(location_test) if (solidity::test::CommonOptions::get().optimize) locations = vector(4, SourceLocation{2, 82, sourceCode}) + - vector(1, SourceLocation{8, 17, codegenCharStream}) + - vector(3, SourceLocation{5, 7, codegenCharStream}) + - vector(1, SourceLocation{30, 31, codegenCharStream}) + + vector(1, SourceLocation{5, 14, codegenCharStream}) + + vector(3, SourceLocation{2, 4, codegenCharStream}) + vector(1, SourceLocation{27, 28, codegenCharStream}) + - vector(1, SourceLocation{20, 32, codegenCharStream}) + - vector(1, SourceLocation{5, 7, codegenCharStream}) + - vector(19, SourceLocation{2, 82, sourceCode}) + + vector(1, SourceLocation{24, 25, codegenCharStream}) + + vector(1, SourceLocation{17, 29, codegenCharStream}) + + vector(1, SourceLocation{2, 4, codegenCharStream}) + + vector(16, SourceLocation{2, 82, sourceCode}) + + vector(1, SourceLocation{12, 13, codegenCharStream}) + + vector(1, SourceLocation{9, 10, codegenCharStream}) + + vector(1, SourceLocation{2, 14, codegenCharStream}) + vector(21, SourceLocation{20, 79, sourceCode}) + vector(1, SourceLocation{72, 74, sourceCode}) + vector(2, SourceLocation{20, 79, sourceCode}); else locations = vector(4, SourceLocation{2, 82, sourceCode}) + - vector(1, SourceLocation{8, 17, codegenCharStream}) + - vector(3, SourceLocation{5, 7, codegenCharStream}) + - vector(1, SourceLocation{30, 31, codegenCharStream}) + + vector(1, SourceLocation{5, 14, codegenCharStream}) + + vector(3, SourceLocation{2, 4, codegenCharStream}) + vector(1, SourceLocation{27, 28, codegenCharStream}) + - vector(1, SourceLocation{20, 32, codegenCharStream}) + - vector(1, SourceLocation{5, 7, codegenCharStream}) + - vector(hasShifts ? 19 : 20, SourceLocation{2, 82, sourceCode}) + + vector(1, SourceLocation{24, 25, codegenCharStream}) + + vector(1, SourceLocation{17, 29, codegenCharStream}) + + vector(1, SourceLocation{2, 4, codegenCharStream}) + + vector(hasShifts ? 16 : 17, SourceLocation{2, 82, sourceCode}) + + vector(1, SourceLocation{12, 13, codegenCharStream}) + + vector(1, SourceLocation{9, 10, codegenCharStream}) + + vector(1, SourceLocation{2, 14, codegenCharStream}) + vector(24, SourceLocation{20, 79, sourceCode}) + vector(1, SourceLocation{49, 58, sourceCode}) + vector(1, SourceLocation{72, 74, sourceCode}) + diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 027fcf1a0d32..f083fae76c48 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -70,6 +70,16 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer } m_settings.erase("ABIEncoderV1Only"); } + + if (m_settings.count("revertStrings")) + { + auto revertStrings = revertStringsFromString(m_settings["revertStrings"]); + if (revertStrings) + m_revertStrings = *revertStrings; + m_validatedSettings["revertStrings"] = revertStringsToString(m_revertStrings); + m_settings.erase("revertStrings"); + } + parseExpectations(file); soltestAssert(!m_tests.empty(), "No tests specified in " + _filename); } diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 8f428f13f91b..dcfb3e6bfbc3 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -51,6 +51,7 @@ bytes SolidityExecutionFramework::compileContract( m_compiler.setEVMVersion(m_evmVersion); m_compiler.setOptimiserSettings(m_optimiserSettings); m_compiler.enableIRGeneration(m_compileViaYul); + m_compiler.setRevertStringBehaviour(m_revertStrings); if (!m_compiler.compile()) { langutil::SourceReferenceFormatter formatter(std::cerr); diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 28246a26cc7e..dfcf4a46b705 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -27,6 +27,7 @@ #include #include +#include #include @@ -67,6 +68,8 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework protected: solidity::frontend::CompilerStack m_compiler; bool m_compileViaYul = false; + RevertStrings m_revertStrings = RevertStrings::Default; + }; } // end namespaces diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 0cafe42a407a..cb0066bee2ab 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -139,7 +139,10 @@ bytes compileFirstExpression( FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.expression() != nullptr); - CompilerContext context(solidity::test::CommonOptions::get().evmVersion()); + CompilerContext context( + solidity::test::CommonOptions::get().evmVersion(), + RevertStrings::Default + ); context.resetVisitedNodes(contract); context.setInheritanceHierarchy(inheritanceHierarchy); unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack @@ -152,7 +155,6 @@ bytes compileFirstExpression( ExpressionCompiler( context, - RevertStrings::Default, solidity::test::CommonOptions::get().optimize ).compile(*extractor.expression()); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 31c37060a799..6ade0849fc48 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -366,14 +366,15 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["assembly"].isString()); BOOST_CHECK(contract["evm"]["assembly"].asString().find( " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n " - "callvalue\n /* \"--CODEGEN--\":8:17 */\n dup1\n " - "/* \"--CODEGEN--\":5:7 */\n iszero\n tag_1\n jumpi\n " - "/* \"--CODEGEN--\":30:31 */\n 0x00\n /* \"--CODEGEN--\":27:28 */\n " - "dup1\n /* \"--CODEGEN--\":20:32 */\n revert\n /* \"--CODEGEN--\":5:7 */\n" + "callvalue\n /* \"--CODEGEN--\":5:14 */\n dup1\n " + "/* \"--CODEGEN--\":2:4 */\n iszero\n tag_1\n jumpi\n " + "/* \"--CODEGEN--\":27:28 */\n 0x00\n /* \"--CODEGEN--\":24:25 */\n " + "dup1\n /* \"--CODEGEN--\":17:29 */\n revert\n /* \"--CODEGEN--\":2:4 */\n" "tag_1:\n /* \"fileA\":0:14 contract A { } */\n pop\n dataSize(sub_0)\n dup1\n " "dataOffset(sub_0)\n 0x00\n codecopy\n 0x00\n return\nstop\n\nsub_0: assembly {\n " - "/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n 0x00\n " - "dup1\n revert\n\n auxdata: 0xa26469706673582212" + "/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n " + "/* \"--CODEGEN--\":12:13 */\n 0x00\n /* \"--CODEGEN--\":9:10 */\n " + "dup1\n /* \"--CODEGEN--\":2:14 */\n revert\n\n auxdata: 0xa26469706673582212" ) == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); BOOST_CHECK_EQUAL(contract["evm"]["gasEstimates"].size(), 1); @@ -397,15 +398,15 @@ BOOST_AUTO_TEST_CASE(basic_compilation) "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"40\"}," "{\"begin\":0,\"end\":14,\"name\":\"MSTORE\"}," "{\"begin\":0,\"end\":14,\"name\":\"CALLVALUE\"}," - "{\"begin\":8,\"end\":17,\"name\":\"DUP1\"}," - "{\"begin\":5,\"end\":7,\"name\":\"ISZERO\"}," - "{\"begin\":5,\"end\":7,\"name\":\"PUSH [tag]\",\"value\":\"1\"}," - "{\"begin\":5,\"end\":7,\"name\":\"JUMPI\"}," - "{\"begin\":30,\"end\":31,\"name\":\"PUSH\",\"value\":\"0\"}," - "{\"begin\":27,\"end\":28,\"name\":\"DUP1\"}," - "{\"begin\":20,\"end\":32,\"name\":\"REVERT\"}," - "{\"begin\":5,\"end\":7,\"name\":\"tag\",\"value\":\"1\"}," - "{\"begin\":5,\"end\":7,\"name\":\"JUMPDEST\"}," + "{\"begin\":5,\"end\":14,\"name\":\"DUP1\"}," + "{\"begin\":2,\"end\":4,\"name\":\"ISZERO\"}," + "{\"begin\":2,\"end\":4,\"name\":\"PUSH [tag]\",\"value\":\"1\"}," + "{\"begin\":2,\"end\":4,\"name\":\"JUMPI\"}," + "{\"begin\":27,\"end\":28,\"name\":\"PUSH\",\"value\":\"0\"}," + "{\"begin\":24,\"end\":25,\"name\":\"DUP1\"}," + "{\"begin\":17,\"end\":29,\"name\":\"REVERT\"}," + "{\"begin\":2,\"end\":4,\"name\":\"tag\",\"value\":\"1\"}," + "{\"begin\":2,\"end\":4,\"name\":\"JUMPDEST\"}," "{\"begin\":0,\"end\":14,\"name\":\"POP\"}," "{\"begin\":0,\"end\":14,\"name\":\"PUSH #[$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":0,\"end\":14,\"name\":\"DUP1\"}," diff --git a/test/libsolidity/semanticTests/revertStrings/array_slices.sol b/test/libsolidity/semanticTests/revertStrings/array_slices.sol new file mode 100644 index 000000000000..dec3e916d763 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/array_slices.sol @@ -0,0 +1,11 @@ +contract C { + function f(uint256 start, uint256 end, uint256[] calldata arr) external pure { + arr[start:end]; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f(uint256,uint256,uint256[]): 2, 1, 0x80, 3, 1, 2, 3 -> FAILURE, hex"08c379a0", 0x20, 22, "Slice starts after end" +// f(uint256,uint256,uint256[]): 1, 5, 0x80, 3, 1, 2, 3 -> FAILURE, hex"08c379a0", 0x20, 28, "Slice is greater than length" diff --git a/test/libsolidity/semanticTests/revertStrings/bubble.sol b/test/libsolidity/semanticTests/revertStrings/bubble.sol new file mode 100644 index 000000000000..5e9a0730df5f --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/bubble.sol @@ -0,0 +1,15 @@ +contract A { + function g() public { revert("fail"); } +} + +contract C { + A a = new A(); + function f() public { + a.g(); + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f() -> FAILURE, hex"08c379a0", 0x20, 4, "fail" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol new file mode 100644 index 000000000000..2538a2175f07 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol @@ -0,0 +1,11 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[][] calldata a) external returns (uint) { + return 42; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f(uint256[][]): 0x20, 1 -> FAILURE, hex"08c379a0", 0x20, 43, "ABI decoding: invalid calldata a", "rray stride" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol new file mode 100644 index 000000000000..9f9296d2a53a --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol @@ -0,0 +1,12 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[][2][] calldata x) external returns (uint256) { + x[0]; + return 23; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f(uint256[][2][]): 0x20, 0x01, 0x20, 0x00 -> FAILURE, hex"08c379a0", 0x20, 28, "Invalid calldata tail offset" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol new file mode 100644 index 000000000000..7aac0f7f8d26 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[][2][] calldata x) external returns (uint256) { + return 42; + } + function g(uint256[][2][] calldata x) external returns (uint256) { + return this.f(x); + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// g(uint256[][2][]): 0x20, 0x01, 0x20, 0x00 -> FAILURE, hex"08c379a0", 0x20, 30, "Invalid calldata access offset" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol new file mode 100644 index 000000000000..3b68bacea336 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol @@ -0,0 +1,11 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[][] calldata x) external returns (uint256) { + return x[0].length; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f(uint256[][]): 0x20, 1, 0x20, 0x0100000000000000000000 -> FAILURE, hex"08c379a0", 0x20, 28, "Invalid calldata tail length" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol b/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol new file mode 100644 index 000000000000..52809a0b2d8c --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol @@ -0,0 +1,11 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint a, uint[] calldata b, uint c) external pure returns (uint) { + return 7; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f(uint256,uint256[],uint256): 6, 0x60, 9, 0x1000000000000000000000000000000000000000000000000000000000000002, 1, 2 -> FAILURE, hex"08c379a0", 0x20, 43, "ABI decoding: invalid calldata a", "rray length" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_too_short.sol b/test/libsolidity/semanticTests/revertStrings/calldata_too_short.sol new file mode 100644 index 000000000000..fea9fe72cf47 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/calldata_too_short.sol @@ -0,0 +1,11 @@ +contract C { + function d(bytes memory _data) public pure returns (uint8) { + return abi.decode(_data, (uint8)); + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ABIEncoderV1Only: true +// ---- +// d(bytes): 0x20, 0x01, 0x0000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"08c379a0", 0x20, 18, "Calldata too short" diff --git a/test/libsolidity/semanticTests/revertStrings/called_contract_has_code.sol b/test/libsolidity/semanticTests/revertStrings/called_contract_has_code.sol new file mode 100644 index 000000000000..b21f85cc8d86 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/called_contract_has_code.sol @@ -0,0 +1,12 @@ +contract C { + function f() external {} + function g() external { + C c = C(0x0000000000000000000000000000000000000000000000000000000000000000); + c.f(); + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// g() -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code" diff --git a/test/libsolidity/semanticTests/revertStrings/enum.sol b/test/libsolidity/semanticTests/revertStrings/enum.sol new file mode 100644 index 000000000000..db3f9f7e2d16 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/enum.sol @@ -0,0 +1,12 @@ +contract C { + enum E {X, Y} + function f(E[] calldata arr) external { + arr[1]; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ABIEncoderV1Only: true +// ---- +// f(uint8[]): 0x20, 2, 3, 3 -> FAILURE, hex"08c379a0", 0x20, 17, "Enum out of range" diff --git a/test/libsolidity/semanticTests/revertStrings/ether_non_payable_function.sol b/test/libsolidity/semanticTests/revertStrings/ether_non_payable_function.sol new file mode 100644 index 000000000000..ffaea68787d9 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/ether_non_payable_function.sol @@ -0,0 +1,9 @@ +contract C { + function f() public {} +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f(), 1 ether -> FAILURE, hex"08c379a0", 0x20, 34, "Ether sent to non-payable functi", "on" +// () -> FAILURE, hex"08c379a0", 0x20, 53, "Contract does not have fallback ", "nor receive functions" diff --git a/test/libsolidity/semanticTests/revertStrings/function_entry_checks.sol b/test/libsolidity/semanticTests/revertStrings/function_entry_checks.sol new file mode 100644 index 000000000000..4072db952a2b --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/function_entry_checks.sol @@ -0,0 +1,9 @@ +contract C { + function t(uint) public pure {} +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: true +// revertStrings: debug +// ---- +// t(uint256) -> FAILURE, hex"08c379a0", 0x20, 34, "ABI decoding: tuple data too sho", "rt" diff --git a/test/libsolidity/semanticTests/revertStrings/invalid_abi_decoding_calldata_v1.sol b/test/libsolidity/semanticTests/revertStrings/invalid_abi_decoding_calldata_v1.sol new file mode 100644 index 000000000000..3468bfc0733c --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/invalid_abi_decoding_calldata_v1.sol @@ -0,0 +1,13 @@ +contract C { + function d(bytes memory _data) public pure returns (uint8) { + return abi.decode(_data, (uint8)); + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ABIEncoderV1Only: true +// ---- +// d(bytes): 0x20, 0x20, 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0 +// d(bytes): 0x100, 0x20, 0x0000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"08c379a0", 0x20, 43, "ABI calldata decoding: invalid h", "ead pointer" +// d(bytes): 0x20, 0x100, 0x0000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"08c379a0", 0x20, 43, "ABI calldata decoding: invalid d", "ata pointer" diff --git a/test/libsolidity/semanticTests/revertStrings/invalid_abi_decoding_memory_v1.sol b/test/libsolidity/semanticTests/revertStrings/invalid_abi_decoding_memory_v1.sol new file mode 100644 index 000000000000..68981a3cb24e --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/invalid_abi_decoding_memory_v1.sol @@ -0,0 +1,20 @@ +contract C { + function dyn(uint ptr, uint start, uint x) public returns (bytes memory a) { + assembly { + mstore(0, start) + mstore(start, add(start, 1)) + return(ptr, x) + } + } + function f(uint ptr, uint start, uint x) public returns (bool) { + this.dyn(ptr, start, x); + return true; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ABIEncoderV1Only: true +// ---- +// f(uint256,uint256,uint256): 0, 0x200, 0x60 -> FAILURE, hex"08c379a0", 0x20, 39, "ABI memory decoding: invalid dat", "a start" +// f(uint256,uint256,uint256): 0, 0x20, 0x60 -> FAILURE, hex"08c379a0", 0x20, 40, "ABI memory decoding: invalid dat", "a length" diff --git a/test/libsolidity/semanticTests/revertStrings/library_non_view_call.sol b/test/libsolidity/semanticTests/revertStrings/library_non_view_call.sol new file mode 100644 index 000000000000..1a079a04ab02 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/library_non_view_call.sol @@ -0,0 +1,16 @@ +library L { + function g() external {} +} +contract C { + function f() public returns (bytes memory) { + (bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("g()")); + assert(!success); + return result; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// library: L +// f() -> 32, 132, 3963877391197344453575983046348115674221700746820753546331534351508065746944, 862718293348820473429344482784628181556388621521298319395315527974912, 1518017211910606845658622928256476421055725129218887721595913401102969, 14649601406562900601407788686537400806574002225747213573947654179243427889152, 0 diff --git a/test/libsolidity/semanticTests/revertStrings/short_input_array.sol b/test/libsolidity/semanticTests/revertStrings/short_input_array.sol new file mode 100644 index 000000000000..1a80acc26891 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/short_input_array.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint[] memory a) public pure returns (uint) { return 7; } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f(uint256[]): 0x20, 1 -> FAILURE, hex"08c379a0", 0x20, 43, "ABI decoding: invalid calldata a", "rray stride" diff --git a/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol b/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol new file mode 100644 index 000000000000..f361da7b60d6 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; +contract C { + function e(bytes memory a) public pure returns (uint) { return 7; } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// e(bytes): 0x20, 7 -> FAILURE, hex"08c379a0", 0x20, 39, "ABI decoding: invalid byte array", " length" diff --git a/test/libsolidity/semanticTests/revertStrings/transfer.sol b/test/libsolidity/semanticTests/revertStrings/transfer.sol new file mode 100644 index 000000000000..fcf971a5c927 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/transfer.sol @@ -0,0 +1,27 @@ +contract A { + receive() external payable { + revert("no_receive"); + } +} + +contract C { + A a = new A(); + receive() external payable {} + function f() public { + address(a).transfer(1 wei); + } + function h() public { + address(a).transfer(100 ether); + } + function g() public view returns (uint) { + return address(this).balance; + } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// (), 10 ether -> +// g() -> 10 +// f() -> FAILURE, hex"08c379a0", 0x20, 10, "no_receive" +// h() -> FAILURE diff --git a/test/libsolidity/semanticTests/revertStrings/unknown_sig_no_fallback.sol b/test/libsolidity/semanticTests/revertStrings/unknown_sig_no_fallback.sol new file mode 100644 index 000000000000..62b245885146 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/unknown_sig_no_fallback.sol @@ -0,0 +1,8 @@ +contract A { + receive () external payable {} +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// (): hex"00" -> FAILURE, hex"08c379a0", 0x20, 41, "Unknown signature and no fallbac", "k defined" From 59e7206c8f9340c90fd628d975cec35094f59727 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 10 Feb 2020 20:48:29 +0530 Subject: [PATCH 106/160] yul proto fuzzer: Support boolean literal generation --- test/tools/ossfuzz/protoToYul.cpp | 4 ++++ test/tools/ossfuzz/yulProto.proto | 1 + 2 files changed, 5 insertions(+) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 86a053e1de2d..cff7334f047f 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -120,6 +120,8 @@ string ProtoConverter::visit(Literal const& _x) return "0x" + createHex(_x.hexval()); case Literal::kStrval: return "\"" + createAlphaNum(_x.strval()) + "\""; + case Literal::kBoolval: + return _x.boolval() ? "true" : "false"; case Literal::LITERAL_ONEOF_NOT_SET: return dictionaryToken(); } @@ -1170,6 +1172,8 @@ void ProtoConverter::visit(CaseStmt const& _x) if (noDoubleQuoteStr.empty()) yulAssert(literalVal == 0, "Proto fuzzer: Empty string does not evaluate to zero"); } + else if (_x.case_lit().has_boolval()) + literalVal = _x.case_lit().boolval() ? u256(1) : u256(0); else literalVal = u256(literal); diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 129638e278a0..7d046747da9f 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -100,6 +100,7 @@ message Literal { uint64 intval = 1; string hexval = 2; string strval = 3; + bool boolval = 4; } } From 79f1917422787e891a1d24dc5036e61cd295b198 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 11 Feb 2020 13:58:36 +0530 Subject: [PATCH 107/160] yul proto fuzzer: Refactor dataoffset/size specification --- test/tools/ossfuzz/protoToYul.cpp | 4 ++-- test/tools/ossfuzz/protoToYul.h | 2 +- test/tools/ossfuzz/yulProto.proto | 14 +------------- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index cff7334f047f..5414405c6eaf 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -1767,12 +1767,12 @@ void ProtoConverter::visit(LeaveStmt const&) m_output << "leave\n"; } -string ProtoConverter::getObjectIdentifier(ObjectId const& _x) +string ProtoConverter::getObjectIdentifier(unsigned _x) { unsigned currentId = currentObjectId(); yulAssert(m_objectScopeTree.size() > currentId, "Proto fuzzer: Error referencing object"); std::vector objectIdsInScope = m_objectScopeTree[currentId]; - return objectIdsInScope[_x.id() % objectIdsInScope.size()]; + return objectIdsInScope[_x % objectIdsInScope.size()]; } void ProtoConverter::visit(Code const& _x) diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index a85dbc909fa5..d16a9981af6c 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -298,7 +298,7 @@ class ProtoConverter /// Returns a pseudo-randomly chosen object identifier that is in the /// scope of the Yul object being visited. - std::string getObjectIdentifier(ObjectId const& _x); + std::string getObjectIdentifier(unsigned _x); /// Return new object identifier as string. Identifier string /// is a template of the form "\"object\"" where is diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 7d046747da9f..c08151b2e351 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -174,7 +174,7 @@ message UnaryOpData { OFFSET = 2; } required UOpData op = 1; - required ObjectId identifier = 2; + required uint64 identifier = 2; } message TernaryOp { @@ -208,18 +208,6 @@ message ExtCodeCopy { required Expression size = 4; } -message ObjectId { - required uint64 id = 1; -} - -message DataSize { - required ObjectId identifier = 1; -} - -message DataOffset { - required ObjectId identifier = 1; -} - message NullaryOp { enum NOp { PC = 1; From 3623026505515f38279d3e3c8ac0297cc96c62cc Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 11 Feb 2020 14:43:37 +0530 Subject: [PATCH 108/160] Switch nightly fuzzer build from ASan (slow) to UBSan (fast) --- .circleci/config.yml | 3 +-- cmake/toolchains/libfuzzer.cmake | 4 ++-- scripts/regressions.py | 8 ++------ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c9fc35abcd50..954579033de1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -414,7 +414,6 @@ jobs: name: Regression tests command: | mkdir -p test_results - export ASAN_OPTIONS="check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2" scripts/regressions.py -o test_results - run: *gitter_notify_failure - run: *gitter_notify_success @@ -793,7 +792,7 @@ workflows: jobs: # OSSFUZZ builds and (regression) tests - b_ubu_ossfuzz: *workflow_trigger_on_tags -# - t_ubu_ossfuzz: *workflow_ubuntu1904_ossfuzz + - t_ubu_ossfuzz: *workflow_ubuntu1904_ossfuzz # Code Coverage enabled build and tests - b_ubu_codecov: *workflow_trigger_on_tags diff --git a/cmake/toolchains/libfuzzer.cmake b/cmake/toolchains/libfuzzer.cmake index 354e6d09b966..53c36d3c90c5 100644 --- a/cmake/toolchains/libfuzzer.cmake +++ b/cmake/toolchains/libfuzzer.cmake @@ -7,5 +7,5 @@ set(USE_CVC4 OFF CACHE BOOL "Disable CVC4" FORCE) set(OSSFUZZ ON CACHE BOOL "Enable fuzzer build" FORCE) # Use libfuzzer as the fuzzing back-end set(LIB_FUZZING_ENGINE "-fsanitize=fuzzer" CACHE STRING "Use libfuzzer back-end" FORCE) -# clang/libfuzzer specific flags for ASan instrumentation -set(CMAKE_CXX_FLAGS "-O1 -gline-tables-only -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -stdlib=libstdc++" CACHE STRING "Custom compilation flags" FORCE) +# clang/libfuzzer specific flags for UBSan instrumentation +set(CMAKE_CXX_FLAGS "-O1 -gline-tables-only -fsanitize=undefined -fsanitize=fuzzer-no-link -stdlib=libstdc++" CACHE STRING "Custom compilation flags" FORCE) diff --git a/scripts/regressions.py b/scripts/regressions.py index 42e3b41c697b..22a9f1c86511 100755 --- a/scripts/regressions.py +++ b/scripts/regressions.py @@ -31,8 +31,7 @@ def run(self): time.sleep(self.interval) class regressor(): - _re_sanitizer_log = re.compile(r"""ERROR: (?P\w+).*""") - _error_blacklist = ["AddressSanitizer", "libFuzzer"] + _re_sanitizer_log = re.compile(r"""(.*runtime error: (?P\w+).*|std::exception::what: (?P\w+).*)""") def __init__(self, description, args): self._description = description @@ -85,16 +84,13 @@ def process_log(self, logfile): bool: Test status. True -> Success False -> Failure - int: Number of suppressed memory leaks """ ## Log may contain non ASCII characters, so we simply stringify them ## since they don't matter for regular expression matching rawtext = str(open(logfile, 'rb').read()) list = re.findall(self._re_sanitizer_log, rawtext) - numSuppressedLeaks = list.count("LeakSanitizer") - rv = any(word in list for word in self._error_blacklist) - return not rv, numSuppressedLeaks + return len(list) == 0 def run(self): """ From 3a4cb016ffc34819781d04429d82eb702b668df0 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 6 Feb 2020 13:05:24 +0100 Subject: [PATCH 109/160] Add codestyle rule to prevent include ".." --- scripts/check_style.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 340aba47ebe4..0bd6f6bc0252 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -17,6 +17,7 @@ fi FORMATERROR=$( ( + git grep -nIE "#include \"" -- '*.h' '*.cpp' | egrep -v -e "license.h" -e "BuildInfo.h" # Use include with <> characters git grep -nIE "\<(if|for|while|switch)\(" -- '*.h' '*.cpp' # no space after "if", "for", "while" or "switch" git grep -nIE "\\s*\([^=]*\>\s:\s.*\)" -- '*.h' '*.cpp' # no space before range based for-loop git grep -nIE "\\s*\(.*\)\s*\{\s*$" -- '*.h' '*.cpp' # "{\n" on same line as "if" / "for" From 9c3151748eef7ffdcca85b87d5979366d533081a Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 11 Feb 2020 13:35:23 +0100 Subject: [PATCH 110/160] Move mapping key checks to ReferencesResolver and make them fatal. --- libsolidity/analysis/ReferencesResolver.cpp | 29 ++++++++++++++++--- libsolidity/analysis/ReferencesResolver.h | 2 +- libsolidity/analysis/TypeChecker.cpp | 24 --------------- libsolidity/analysis/TypeChecker.h | 1 - .../types/struct_mapping_recursion.sol | 9 ++++++ 5 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 test/libsolidity/syntaxTests/types/struct_mapping_recursion.sol diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 6765f67103bb..4fe2d01c9c0b 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -224,15 +224,36 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) _typeName.annotation().type = TypeProvider::function(_typeName); } -void ReferencesResolver::endVisit(Mapping const& _typeName) +void ReferencesResolver::endVisit(Mapping const& _mapping) { - TypePointer keyType = _typeName.keyType().annotation().type; - TypePointer valueType = _typeName.valueType().annotation().type; + if (auto const* typeName = dynamic_cast(&_mapping.keyType())) + { + if (auto const* contractType = dynamic_cast(typeName->annotation().type)) + { + if (contractType->contractDefinition().isLibrary()) + m_errorReporter.fatalTypeError( + typeName->location(), + "Library types cannot be used as mapping keys." + ); + } + else if (typeName->annotation().type->category() != Type::Category::Enum) + m_errorReporter.fatalTypeError( + typeName->location(), + "Only elementary types, contract types or enums are allowed as mapping keys." + ); + } + else + solAssert(dynamic_cast(&_mapping.keyType()), ""); + + TypePointer keyType = _mapping.keyType().annotation().type; + TypePointer valueType = _mapping.valueType().annotation().type; + // Convert key type to memory. keyType = TypeProvider::withLocationIfReference(DataLocation::Memory, keyType); + // Convert value type to storage reference. valueType = TypeProvider::withLocationIfReference(DataLocation::Storage, valueType); - _typeName.annotation().type = TypeProvider::mapping(keyType, valueType); + _mapping.annotation().type = TypeProvider::mapping(keyType, valueType); } void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 2bbce69d0f31..c560be31ee3a 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -81,7 +81,7 @@ class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker void endVisit(ModifierDefinition const& _modifierDefinition) override; void endVisit(UserDefinedTypeName const& _typeName) override; void endVisit(FunctionTypeName const& _typeName) override; - void endVisit(Mapping const& _typeName) override; + void endVisit(Mapping const& _mapping) override; void endVisit(ArrayTypeName const& _typeName) override; bool visit(InlineAssembly const& _inlineAssembly) override; bool visit(Return const& _return) override; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 4590bcdc25e6..e4aa399d862f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2876,30 +2876,6 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.annotation().isPure = true; } -bool TypeChecker::visit(Mapping const& _mapping) -{ - if (auto const* keyType = dynamic_cast(&_mapping.keyType())) - { - if (auto const* contractType = dynamic_cast(keyType->annotation().type)) - { - if (contractType->contractDefinition().isLibrary()) - m_errorReporter.typeError( - keyType->location(), - "Library types cannot be used as mapping keys." - ); - } - else if (keyType->annotation().type->category() != Type::Category::Enum) - m_errorReporter.typeError( - keyType->location(), - "Only elementary types, contract types or enums are allowed as mapping keys." - ); - } - else - solAssert(dynamic_cast(&_mapping.keyType()), ""); - return true; -} - - bool TypeChecker::contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set const& _seenContracts diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 5dae91e2d85c..d428a6ac9754 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -143,7 +143,6 @@ class TypeChecker: private ASTConstVisitor bool visit(Identifier const& _identifier) override; void endVisit(ElementaryTypeNameExpression const& _expr) override; void endVisit(Literal const& _literal) override; - bool visit(Mapping const& _mapping) override; bool contractDependenciesAreCyclic( ContractDefinition const& _contract, diff --git a/test/libsolidity/syntaxTests/types/struct_mapping_recursion.sol b/test/libsolidity/syntaxTests/types/struct_mapping_recursion.sol new file mode 100644 index 000000000000..4786e009d72c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/struct_mapping_recursion.sol @@ -0,0 +1,9 @@ +// Used to segfault. +contract C { + struct S { + mapping(S => uint) a; + } + function g (S calldata) external view {} + } +// ---- +// TypeError: (56-57): Only elementary types, contract types or enums are allowed as mapping keys. From 1a3998648cf5ee404cc68f916a87098c730519f2 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 4 Feb 2020 12:01:13 +0100 Subject: [PATCH 111/160] Run yul optimizer on user code without refs --- libsolidity/codegen/ContractCompiler.cpp | 79 ++++++++++++++++- libyul/optimiser/ASTCopier.h | 2 +- test/cmdlineTests/optimizer_user_yul/args | 1 + .../cmdlineTests/optimizer_user_yul/input.sol | 37 ++++++++ test/cmdlineTests/optimizer_user_yul/output | 87 +++++++++++++++++++ 5 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 test/cmdlineTests/optimizer_user_yul/args create mode 100644 test/cmdlineTests/optimizer_user_yul/input.sol create mode 100644 test/cmdlineTests/optimizer_user_yul/output diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index bbf6472de0ae..bd6cc26bf90d 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -20,6 +20,8 @@ * Solidity compiler. */ + +#include #include #include #include @@ -27,7 +29,16 @@ #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include #include @@ -40,6 +51,13 @@ #include #include + +// Change to "define" to output all intermediate code +#undef SOL_OUTPUT_ASM +#ifdef SOL_OUTPUT_ASM +#include +#endif + using namespace std; using namespace solidity; using namespace solidity::evmasm; @@ -788,10 +806,65 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) _assembly.appendInstruction(Instruction::POP); } }; - solAssert(_inlineAssembly.annotation().analysisInfo, ""); + + yul::Block const* code = &_inlineAssembly.operations(); + yul::AsmAnalysisInfo* analysisInfo = _inlineAssembly.annotation().analysisInfo.get(); + + // Only used in the scope below, but required to live outside to keep the + // shared_ptr's alive + yul::Object object; + + // The optimiser cannot handle external references + if ( + m_optimiserSettings.runYulOptimiser && + _inlineAssembly.annotation().externalReferences.empty() + ) + { + // Create a modifiable copy of the code and analysis + object.code = make_shared(yul::ASTCopier().translate(*code)); + object.analysisInfo = make_shared(); + + { + ErrorList errList; + ErrorReporter errorReporter{errList}; + + yul::AsmAnalyzer analyzer( + *object.analysisInfo, + errorReporter, + _inlineAssembly.dialect() + ); + solAssert(analyzer.analyze(*object.code), "Inline analysis failed."); + } + + yul::EVMDialect const* dialect = dynamic_cast(&_inlineAssembly.dialect()); + solAssert(dialect, ""); + +#ifdef SOL_OUTPUT_ASM + cout << yul::AsmPrinter(*dialect)(*object.code) << endl; +#endif + + bool const isCreation = m_context.runtimeContext() != nullptr; + yul::GasMeter meter(*dialect, isCreation, m_optimiserSettings.expectedExecutionsPerDeployment); + yul::OptimiserSuite::run( + *dialect, + &meter, + object, + m_optimiserSettings.optimizeStackAllocation, + {} + ); + +#ifdef SOL_OUTPUT_ASM + cout << "After optimizer:" << endl; + cout << yul::AsmPrinter(*dialect)(*object.code) << endl; +#endif + code = object.code.get(); + analysisInfo = object.analysisInfo.get(); + } + + solAssert(analysisInfo, ""); yul::CodeGenerator::assemble( - _inlineAssembly.operations(), - *_inlineAssembly.annotation().analysisInfo, + *code, + *analysisInfo, *m_context.assemblyPtr(), m_context.evmVersion(), identifierAccess, diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index 45cce07c23fc..0ac0b5f8207e 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -84,6 +84,7 @@ class ASTCopier: public ExpressionCopier, public StatementCopier virtual Expression translate(Expression const& _expression); virtual Statement translate(Statement const& _statement); + Block translate(Block const& _block); protected: template std::vector translateVector(std::vector const& _values); @@ -94,7 +95,6 @@ class ASTCopier: public ExpressionCopier, public StatementCopier return _v ? std::make_unique(translate(*_v)) : nullptr; } - Block translate(Block const& _block); Case translate(Case const& _case); virtual Identifier translate(Identifier const& _identifier); Literal translate(Literal const& _literal); diff --git a/test/cmdlineTests/optimizer_user_yul/args b/test/cmdlineTests/optimizer_user_yul/args new file mode 100644 index 000000000000..8942fcc3523a --- /dev/null +++ b/test/cmdlineTests/optimizer_user_yul/args @@ -0,0 +1 @@ +--optimize --asm --metadata-hash none diff --git a/test/cmdlineTests/optimizer_user_yul/input.sol b/test/cmdlineTests/optimizer_user_yul/input.sol new file mode 100644 index 000000000000..74dc84cbd831 --- /dev/null +++ b/test/cmdlineTests/optimizer_user_yul/input.sol @@ -0,0 +1,37 @@ +pragma solidity >=0.0; + +contract C +{ + constructor() public payable + { + int a; + + // Can't be optimized due to external reference "a" + assembly + { + let x,y,z + + sstore(0, 1) + + for { } sload(4) { } { + z := exp(x, y) + } + + a := 2 + } + + // Can be optimized due to no external references + assembly + { + let x,y,z + + sstore(2, 3) + + for { } sload(5) { } { + // Expected to be optimized out for yulOptimizer, but not for + // old optimizer + z := exp(x, y) + } + } + } +} diff --git a/test/cmdlineTests/optimizer_user_yul/output b/test/cmdlineTests/optimizer_user_yul/output new file mode 100644 index 000000000000..a2b1fd077372 --- /dev/null +++ b/test/cmdlineTests/optimizer_user_yul/output @@ -0,0 +1,87 @@ + +======= optimizer_user_yul/input.sol:C ======= +EVM assembly: + /* "optimizer_user_yul/input.sol":24:489 contract C... */ + mstore(0x40, 0x80) + /* "optimizer_user_yul/input.sol":72:77 int a */ + 0x00 + /* "optimizer_user_yul/input.sol":38:487 constructor() public payable... */ + dup1 + 0x00 + dup1 + /* "optimizer_user_yul/input.sol":176:177 1 */ + 0x01 + /* "optimizer_user_yul/input.sol":173:174 0 */ + 0x00 + /* "optimizer_user_yul/input.sol":166:178 sstore(0, 1) */ + sstore + /* "optimizer_user_yul/input.sol":183:229 for { } sload(4) { } {... */ +tag_3: + /* "optimizer_user_yul/input.sol":197:198 4 */ + 0x04 + /* "optimizer_user_yul/input.sol":191:199 sload(4) */ + sload + /* "optimizer_user_yul/input.sol":183:229 for { } sload(4) { } {... */ + iszero + tag_5 + jumpi + pop + /* "optimizer_user_yul/input.sol":215:224 exp(x, y) */ + dup1 + dup3 + exp + /* "optimizer_user_yul/input.sol":183:229 for { } sload(4) { } {... */ + jump(tag_3) +tag_5: + /* "optimizer_user_yul/input.sol":187:190 { } */ + pop + pop + pop + /* "optimizer_user_yul/input.sol":239:240 2 */ + 0x02 + /* "optimizer_user_yul/input.sol":234:240 a := 2 */ + swap1 + pop + /* "optimizer_user_yul/input.sol":340:341 3 */ + 0x03 + /* "optimizer_user_yul/input.sol":337:338 2 */ + 0x02 + /* "optimizer_user_yul/input.sol":330:342 sstore(2, 3) */ + sstore + /* "optimizer_user_yul/input.sol":347:480 for { } sload(5) { } {... */ +tag_6: + /* "optimizer_user_yul/input.sol":361:362 5 */ + 0x05 + /* "optimizer_user_yul/input.sol":355:363 sload(5) */ + sload + tag_9 + jumpi + jump(tag_8) +tag_9: + /* "optimizer_user_yul/input.sol":347:480 for { } sload(5) { } {... */ + jump(tag_6) +tag_8: + /* "optimizer_user_yul/input.sol":311:484 {... */ + pop + /* "optimizer_user_yul/input.sol":24:489 contract C... */ + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "optimizer_user_yul/input.sol":24:489 contract C... */ + mstore(0x40, 0x80) + /* "--CODEGEN--":12:13 */ + 0x00 + /* "--CODEGEN--":9:10 */ + dup1 + /* "--CODEGEN--":2:14 */ + revert + + auxdata: 0xa164736f6c63782a302e362e332d646576656c6f702e323032302e322e362b636f6d6d69742e31353237396436392e6d6f640032 +} From 80bd0f47ccbb79f51e28e4c146e34da13bed0972 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 6 Feb 2020 16:32:33 +0100 Subject: [PATCH 112/160] Unify optimize yul code in CompilerContext/ContractCompiler --- libsolidity/codegen/CompilerContext.cpp | 35 ++++++++++---- libsolidity/codegen/CompilerContext.h | 6 +++ libsolidity/codegen/ContractCompiler.cpp | 52 ++++----------------- test/cmdlineTests.sh | 1 + test/cmdlineTests/optimizer_user_yul/output | 2 +- 5 files changed, 42 insertions(+), 54 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index f1f386300605..8c1099855bed 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -423,18 +423,12 @@ void CompilerContext::appendInlineAssembly( // so we essentially only optimize the ABI functions. if (_optimiserSettings.runYulOptimiser && _localVariables.empty()) { - bool const isCreation = m_runtimeContext != nullptr; - yul::GasMeter meter(dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment); yul::Object obj; obj.code = parserResult; obj.analysisInfo = make_shared(analysisInfo); - yul::OptimiserSuite::run( - dialect, - &meter, - obj, - _optimiserSettings.optimizeStackAllocation, - externallyUsedIdentifiers - ); + + optimizeYul(obj, dialect, _optimiserSettings, externallyUsedIdentifiers); + analysisInfo = std::move(*obj.analysisInfo); parserResult = std::move(obj.code); @@ -462,6 +456,29 @@ void CompilerContext::appendInlineAssembly( updateSourceLocation(); } + +void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _dialect, OptimiserSettings const& _optimiserSettings, std::set const& _externalIdentifiers) +{ +#ifdef SOL_OUTPUT_ASM + cout << yul::AsmPrinter(*dialect)(*_object.code) << endl; +#endif + + bool const isCreation = runtimeContext() != nullptr; + yul::GasMeter meter(_dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment); + yul::OptimiserSuite::run( + _dialect, + &meter, + _object, + _optimiserSettings.optimizeStackAllocation, + _externalIdentifiers + ); + +#ifdef SOL_OUTPUT_ASM + cout << "After optimizer:" << endl; + cout << yul::AsmPrinter(*dialect)(*object.code) << endl; +#endif +} + FunctionDefinition const& CompilerContext::resolveVirtualFunction( FunctionDefinition const& _function, vector::const_iterator _searchStart diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 28aefa036556..0ee379f5aec7 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -32,9 +32,13 @@ #include #include +#include #include #include +#include +#include + #include #include #include @@ -232,6 +236,8 @@ class CompilerContext /// Otherwise returns "revert(0, 0)". std::string revertReasonIfDebug(std::string const& _message = ""); + void optimizeYul(yul::Object& _object, yul::EVMDialect const& _dialect, OptimiserSettings const& _optimiserSetting, std::set const& _externalIdentifiers = {}); + /// Appends arbitrary data to the end of the bytecode. void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index bd6cc26bf90d..298437b1deee 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -21,7 +21,6 @@ */ -#include #include #include #include @@ -30,8 +29,8 @@ #include #include +#include #include -#include #include #include #include @@ -49,14 +48,8 @@ #include #include -#include - -// Change to "define" to output all intermediate code -#undef SOL_OUTPUT_ASM -#ifdef SOL_OUTPUT_ASM -#include -#endif +#include using namespace std; using namespace solidity; @@ -812,7 +805,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) // Only used in the scope below, but required to live outside to keep the // shared_ptr's alive - yul::Object object; + yul::Object object = {}; // The optimiser cannot handle external references if ( @@ -820,48 +813,19 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) _inlineAssembly.annotation().externalReferences.empty() ) { - // Create a modifiable copy of the code and analysis - object.code = make_shared(yul::ASTCopier().translate(*code)); - object.analysisInfo = make_shared(); - - { - ErrorList errList; - ErrorReporter errorReporter{errList}; - - yul::AsmAnalyzer analyzer( - *object.analysisInfo, - errorReporter, - _inlineAssembly.dialect() - ); - solAssert(analyzer.analyze(*object.code), "Inline analysis failed."); - } - yul::EVMDialect const* dialect = dynamic_cast(&_inlineAssembly.dialect()); solAssert(dialect, ""); -#ifdef SOL_OUTPUT_ASM - cout << yul::AsmPrinter(*dialect)(*object.code) << endl; -#endif - - bool const isCreation = m_context.runtimeContext() != nullptr; - yul::GasMeter meter(*dialect, isCreation, m_optimiserSettings.expectedExecutionsPerDeployment); - yul::OptimiserSuite::run( - *dialect, - &meter, - object, - m_optimiserSettings.optimizeStackAllocation, - {} - ); + // Create a modifiable copy of the code and analysis + object.code = make_shared(yul::ASTCopier().translate(*code)); + object.analysisInfo = make_shared(yul::AsmAnalyzer::analyzeStrictAssertCorrect(*dialect, object)); + + m_context.optimizeYul(object, *dialect, m_optimiserSettings); -#ifdef SOL_OUTPUT_ASM - cout << "After optimizer:" << endl; - cout << yul::AsmPrinter(*dialect)(*object.code) << endl; -#endif code = object.code.get(); analysisInfo = object.analysisInfo.get(); } - solAssert(analysisInfo, ""); yul::CodeGenerator::assemble( *code, *analysisInfo, diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 754cd0bac24a..cf832e6141ed 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -155,6 +155,7 @@ function test_solc_behaviour() rm "$stdout_path.bak" else sed -i.bak -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" + sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1AUXDATA REMOVED/' "$stdout_path" sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path" # Remove trailing empty lines. Needs a line break to make OSX sed happy. sed -i.bak -e '1{/^$/d diff --git a/test/cmdlineTests/optimizer_user_yul/output b/test/cmdlineTests/optimizer_user_yul/output index a2b1fd077372..b95f2e0fa943 100644 --- a/test/cmdlineTests/optimizer_user_yul/output +++ b/test/cmdlineTests/optimizer_user_yul/output @@ -83,5 +83,5 @@ sub_0: assembly { /* "--CODEGEN--":2:14 */ revert - auxdata: 0xa164736f6c63782a302e362e332d646576656c6f702e323032302e322e362b636f6d6d69742e31353237396436392e6d6f640032 + auxdata: AUXDATA REMOVED } From 4644d4616f0da0bc6d70c0152bba85003a4b4099 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 11 Feb 2020 16:54:09 +0100 Subject: [PATCH 113/160] Fix builtin function types for typed evm dialect. --- libyul/AsmScopeFiller.cpp | 12 ++++++------ libyul/backends/evm/EVMDialect.cpp | 11 +++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/libyul/AsmScopeFiller.cpp b/libyul/AsmScopeFiller.cpp index 3a8e548b82c0..4c37bf5ecc7c 100644 --- a/libyul/AsmScopeFiller.cpp +++ b/libyul/AsmScopeFiller.cpp @@ -151,13 +151,13 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef) { - vector arguments; - for (auto const& _argument: _funDef.parameters) - arguments.emplace_back(_argument.type.str()); + vector parameters; + for (auto const& parameter: _funDef.parameters) + parameters.emplace_back(parameter.type); vector returns; - for (auto const& _return: _funDef.returnVariables) - returns.emplace_back(_return.type.str()); - if (!m_currentScope->registerFunction(_funDef.name, std::move(arguments), std::move(returns))) + for (auto const& returnVariable: _funDef.returnVariables) + returns.emplace_back(returnVariable.type); + if (!m_currentScope->registerFunction(_funDef.name, std::move(parameters), std::move(returns))) { //@TODO secondary location m_errorReporter.declarationError( diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 3274f440f63e..39fc65632dda 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -221,6 +221,15 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA boolType = "bool"_yulstring; types = {defaultType, boolType}; + // Set all types to ``defaultType`` + for (auto& fun: m_functions) + { + for (auto& p: fun.second.parameters) + p = defaultType; + for (auto& r: fun.second.returns) + r = defaultType; + } + m_functions["lt"_yulstring].returns = {"bool"_yulstring}; m_functions["gt"_yulstring].returns = {"bool"_yulstring}; m_functions["slt"_yulstring].returns = {"bool"_yulstring}; @@ -260,6 +269,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA _visitArguments(); })); m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring}; + m_functions["bool_to_u256"_yulstring].returns = {"u256"_yulstring}; m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, false, []( FunctionCall const&, AbstractAssembly& _assembly, @@ -276,6 +286,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA _assembly.appendInstruction(evmasm::Instruction::INVALID); _assembly.appendLabel(inRange); })); + m_functions["u256_to_bool"_yulstring].parameters = {"u256"_yulstring}; m_functions["u256_to_bool"_yulstring].returns = {"bool"_yulstring}; } From 24d6702986e899a85709e0e81c0628c34ef131b8 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 7 Feb 2020 03:28:24 +0300 Subject: [PATCH 114/160] [Yul] Prune functions that call each other but are otherwise unreferenced --- Changelog.md | 1 + libyul/CMakeLists.txt | 2 + libyul/optimiser/CircularReferencesPruner.cpp | 61 +++++++++++++++++++ libyul/optimiser/CircularReferencesPruner.h | 58 ++++++++++++++++++ libyul/optimiser/Suite.cpp | 18 +++++- test/libyul/YulOptimizerTest.cpp | 8 +++ .../called_from_non_function.yul | 20 ++++++ .../nested_different_names.yul | 14 +++++ .../nested_same_name.yul | 14 +++++ .../circularReferencesPruner/trivial.yul | 8 +++ test/yulPhaser/Chromosome.cpp | 2 +- 11 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 libyul/optimiser/CircularReferencesPruner.cpp create mode 100644 libyul/optimiser/CircularReferencesPruner.h create mode 100644 test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul create mode 100644 test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul create mode 100644 test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul create mode 100644 test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul diff --git a/Changelog.md b/Changelog.md index 9bde3fffd27e..7fcfc08b6099 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. * AST: Add a new node for doxygen-style, structured documentation that can be received by contract, function, event and modifier definitions. * Debug: Provide reason strings for compiler-generated internal reverts when using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting on ``debug`` mode. + * Yul Optimizer: Prune functions that call each other but are otherwise unreferenced. Bugfixes: diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 416a02455700..12d2866c2745 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -69,6 +69,8 @@ add_library(yul optimiser/BlockHasher.h optimiser/CallGraphGenerator.cpp optimiser/CallGraphGenerator.h + optimiser/CircularReferencesPruner.cpp + optimiser/CircularReferencesPruner.h optimiser/CommonSubexpressionEliminator.cpp optimiser/CommonSubexpressionEliminator.h optimiser/ConditionalSimplifier.cpp diff --git a/libyul/optimiser/CircularReferencesPruner.cpp b/libyul/optimiser/CircularReferencesPruner.cpp new file mode 100644 index 000000000000..f9c849568b3b --- /dev/null +++ b/libyul/optimiser/CircularReferencesPruner.cpp @@ -0,0 +1,61 @@ +/* + 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 . +*/ +#include + +#include +#include +#include + +#include + +using namespace std; +using namespace solidity::yul; + +void CircularReferencesPruner::run(OptimiserStepContext& _context, Block& _ast) +{ + CircularReferencesPruner{_context.reservedIdentifiers}(_ast); +} + +void CircularReferencesPruner::operator()(Block& _block) +{ + set functionsToKeep = + functionsCalledFromOutermostContext(CallGraphGenerator::callGraph(_block)); + + for (auto&& statement: _block.statements) + if (holds_alternative(statement)) + { + FunctionDefinition const& funDef = std::get(statement); + if (!functionsToKeep.count(funDef.name)) + statement = Block{}; + } + + removeEmptyBlocks(_block); +} + +set CircularReferencesPruner::functionsCalledFromOutermostContext(CallGraph const& _callGraph) +{ + set verticesToTraverse = m_reservedIdentifiers; + verticesToTraverse.insert(YulString("")); + + return util::BreadthFirstSearch{{verticesToTraverse.begin(), verticesToTraverse.end()}}.run( + [&_callGraph](YulString _function, auto&& _addChild) { + if (_callGraph.functionCalls.count(_function)) + for (auto const& callee: _callGraph.functionCalls.at(_function)) + if (_callGraph.functionCalls.count(callee)) + _addChild(callee); + }).visited; +} diff --git a/libyul/optimiser/CircularReferencesPruner.h b/libyul/optimiser/CircularReferencesPruner.h new file mode 100644 index 000000000000..2a51045d12e7 --- /dev/null +++ b/libyul/optimiser/CircularReferencesPruner.h @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimization stage that removes functions that call each other but are + * otherwise unreferenced. + * + * Prerequisites: Disambiguator, FunctionHoister. + */ + +#pragma once + +#include +#include +#include + +namespace solidity::yul +{ + +/** + * Optimization stage that removes functions that call each other but are + * neither externally referenced nor referenced from the outermost context. + */ +class CircularReferencesPruner: public ASTModifier +{ +public: + static constexpr char const* name{"CircularReferencesPruner"}; + static void run(OptimiserStepContext& _context, Block& _ast); + + using ASTModifier::operator(); + void operator()(Block& _block) override; +private: + CircularReferencesPruner(std::set const& _reservedIdentifiers): + m_reservedIdentifiers(_reservedIdentifiers) + {} + + /// Run a breadth-first search starting from the outermost context and + /// externally referenced functions to find all the functions that are + /// called from there either directly or indirectly. + std::set functionsCalledFromOutermostContext(CallGraph const& _callGraph); + + std::set const& m_reservedIdentifiers; +}; + +} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 47c245752c88..a2fec4cc4979 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,7 @@ void OptimiserSuite::run( FunctionGrouper::name, EquivalentFunctionCombiner::name, UnusedPruner::name, + CircularReferencesPruner::name, BlockFlattener::name, ControlFlowSimplifier::name, LiteralRematerialiser::name, @@ -151,7 +153,8 @@ void OptimiserSuite::run( BlockFlattener::name, DeadCodeEliminator::name, ForLoopConditionIntoBody::name, - UnusedPruner::name + UnusedPruner::name, + CircularReferencesPruner::name }, ast); } @@ -161,6 +164,7 @@ void OptimiserSuite::run( LoadResolver::name, CommonSubexpressionEliminator::name, UnusedPruner::name, + CircularReferencesPruner::name, }, ast); } @@ -170,6 +174,7 @@ void OptimiserSuite::run( SSAReverser::name, CommonSubexpressionEliminator::name, UnusedPruner::name, + CircularReferencesPruner::name, ExpressionJoiner::name, ExpressionJoiner::name, @@ -183,6 +188,7 @@ void OptimiserSuite::run( suite.runSequence({ ExpressionInliner::name, UnusedPruner::name, + CircularReferencesPruner::name, }, ast); } @@ -193,8 +199,10 @@ void OptimiserSuite::run( SSATransform::name, RedundantAssignEliminator::name, UnusedPruner::name, + CircularReferencesPruner::name, RedundantAssignEliminator::name, UnusedPruner::name, + CircularReferencesPruner::name, }, ast); } @@ -244,6 +252,7 @@ void OptimiserSuite::run( RedundantAssignEliminator::name, ForLoopConditionIntoBody::name, UnusedPruner::name, + CircularReferencesPruner::name, CommonSubexpressionEliminator::name, }, ast); } @@ -255,10 +264,13 @@ void OptimiserSuite::run( ExpressionJoiner::name, Rematerialiser::name, UnusedPruner::name, + CircularReferencesPruner::name, ExpressionJoiner::name, UnusedPruner::name, + CircularReferencesPruner::name, ExpressionJoiner::name, UnusedPruner::name, + CircularReferencesPruner::name, SSAReverser::name, CommonSubexpressionEliminator::name, @@ -266,10 +278,12 @@ void OptimiserSuite::run( ForLoopConditionOutOfBody::name, CommonSubexpressionEliminator::name, UnusedPruner::name, + CircularReferencesPruner::name, ExpressionJoiner::name, Rematerialiser::name, UnusedPruner::name, + CircularReferencesPruner::name, }, ast); // This is a tuning parameter, but actually just prevents infinite loops. @@ -339,6 +353,7 @@ map> const& OptimiserSuite::allSteps() if (instance.empty()) instance = optimiserStepCollection< BlockFlattener, + CircularReferencesPruner, CommonSubexpressionEliminator, ConditionalSimplifier, ConditionalUnsimplifier, @@ -374,6 +389,7 @@ map const& OptimiserSuite::stepNameToAbbreviationMap() { static map lookupTable{ {BlockFlattener::name, 'f'}, + {CircularReferencesPruner::name, 'l'}, {CommonSubexpressionEliminator::name, 'c'}, {ConditionalSimplifier::name, 'C'}, {ConditionalUnsimplifier::name, 'U'}, diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index f3906aead44e..3ac745d6fd67 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -250,6 +251,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line CommonSubexpressionEliminator::run(*m_context, *m_ast); ExpressionSimplifier::run(*m_context, *m_ast); UnusedPruner::run(*m_context, *m_ast); + CircularReferencesPruner::run(*m_context, *m_ast); DeadCodeEliminator::run(*m_context, *m_ast); ExpressionJoiner::run(*m_context, *m_ast); ExpressionJoiner::run(*m_context, *m_ast); @@ -259,6 +261,12 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line disambiguate(); UnusedPruner::run(*m_context, *m_ast); } + else if (m_optimizerStep == "circularReferencesPruner") + { + disambiguate(); + FunctionHoister::run(*m_context, *m_ast); + CircularReferencesPruner::run(*m_context, *m_ast); + } else if (m_optimizerStep == "deadCodeEliminator") { disambiguate(); diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul new file mode 100644 index 000000000000..07eca2709077 --- /dev/null +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul @@ -0,0 +1,20 @@ +{ + let a + function f() -> x { x := g() } + function g() -> y { y := f() } + function h() -> z { z := g() } + a := h() +} +// ==== +// step: circularReferencesPruner +// ---- +// { +// let a +// a := h() +// function f() -> x +// { x := g() } +// function g() -> y +// { y := f() } +// function h() -> z +// { z := g() } +// } diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul new file mode 100644 index 000000000000..0630fe35f27b --- /dev/null +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul @@ -0,0 +1,14 @@ +{ + { + function a() -> x { x := b() } + function b() -> y { y := a() } + } + { + function c() -> z { z := d() } + function d() -> w { w := c() } + } +} +// ==== +// step: circularReferencesPruner +// ---- +// { } diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul new file mode 100644 index 000000000000..9873c717fcff --- /dev/null +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul @@ -0,0 +1,14 @@ +{ + { + function z() -> x { x := y() } + function y() -> x { x := z() } + } + { + function z() -> x { x := y() } + function y() -> x { x := z() } + } +} +// ==== +// step: circularReferencesPruner +// ---- +// { } diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul new file mode 100644 index 000000000000..c0fe6834ad8e --- /dev/null +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul @@ -0,0 +1,8 @@ +{ + function f() -> x { x := g() } + function g() -> x { x := f() } +} +// ==== +// step: circularReferencesPruner +// ---- +// { } diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index dd250d0ecddf..bd07e1b2e9f0 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_strin BOOST_TEST(chromosome.length() == allSteps.size()); BOOST_TEST(chromosome.optimisationSteps() == allSteps); - BOOST_TEST(toString(chromosome) == "fcCUnDvejsxIOoighTLMrmVatud"); + BOOST_TEST(toString(chromosome) == "flcCUnDvejsxIOoighTLMrmVatud"); } BOOST_AUTO_TEST_SUITE_END() From 6abe0a50b1cdcb2fb49d081b2ed295f1e80e2530 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 4 Feb 2020 17:08:55 +0100 Subject: [PATCH 115/160] Define stack slot names in types. --- libsolidity/ast/Types.cpp | 71 ++++++++++++++++++++++++++------------- libsolidity/ast/Types.h | 60 ++++++++++++++++++++++++++------- 2 files changed, 94 insertions(+), 37 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index dbeb63033001..bcc3af3379c5 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -151,6 +151,8 @@ util::Result transformParametersToExternal(TypePointers const& _pa void Type::clearCache() const { m_members.clear(); + m_stackSlots.reset(); + m_stackSize.reset(); } void StorageOffsets::computeOffsets(TypePointers const& _types) @@ -1701,15 +1703,14 @@ u256 ArrayType::storageSize() const return max(1, u256(size)); } -unsigned ArrayType::sizeOnStack() const +vector> ArrayType::makeStackSlots() const { - if (m_location == DataLocation::CallData) - // offset [length] (stack top) - return 1 + (isDynamicallySized() ? 1 : 0); + if (m_location == DataLocation::CallData && isDynamicallySized()) + return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())}; else // storage slot or memory offset // byte offset inside storage value is omitted - return 1; + return {std::make_tuple(string(), nullptr)}; } string ArrayType::toString(bool _short) const @@ -1891,6 +1892,11 @@ string ArraySliceType::toString(bool _short) const return m_arrayType.toString(_short) + " slice"; } +std::vector> ArraySliceType::makeStackSlots() const +{ + return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}}; +} + string ContractType::richIdentifier() const { return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + to_string(m_contract.id()); @@ -1989,6 +1995,14 @@ vector> ContractType::stateVar return variablesAndOffsets; } +vector> ContractType::makeStackSlots() const +{ + if (m_super) + return {}; + else + return {make_tuple("address", isPayable() ? TypeProvider::payableAddress() :TypeProvider::address())}; +} + void StructType::clearCache() const { Type::clearCache(); @@ -2422,12 +2436,17 @@ u256 TupleType::storageSize() const solAssert(false, "Storage size of non-storable tuple type requested."); } -unsigned TupleType::sizeOnStack() const +vector> TupleType::makeStackSlots() const { - unsigned size = 0; + vector> slots; + unsigned i = 1; for (auto const& t: components()) - size += t ? t->sizeOnStack() : 0; - return size; + { + if (t) + slots.emplace_back("component_" + std::to_string(i), t); + ++i; + } + return slots; } TypePointer TupleType::mobileType() const @@ -2883,8 +2902,9 @@ unsigned FunctionType::storageBytes() const solAssert(false, "Storage size of non-storable function type requested."); } -unsigned FunctionType::sizeOnStack() const +vector> FunctionType::makeStackSlots() const { + vector> slots; Kind kind = m_kind; if (m_kind == Kind::SetGas || m_kind == Kind::SetValue) { @@ -2892,39 +2912,42 @@ unsigned FunctionType::sizeOnStack() const kind = dynamic_cast(*m_returnParameterTypes.front()).m_kind; } - unsigned size = 0; - switch (kind) { case Kind::External: case Kind::DelegateCall: - size = 2; + slots = {make_tuple("address", TypeProvider::address()), make_tuple("functionIdentifier", TypeProvider::fixedBytes(4))}; break; case Kind::BareCall: case Kind::BareCallCode: case Kind::BareDelegateCall: case Kind::BareStaticCall: + case Kind::Transfer: + case Kind::Send: + slots = {make_tuple("address", TypeProvider::address())}; + break; case Kind::Internal: + slots = {make_tuple(string(), nullptr)}; + break; case Kind::ArrayPush: case Kind::ArrayPop: case Kind::ByteArrayPush: - case Kind::Transfer: - case Kind::Send: - size = 1; + slots = {make_tuple(string(), nullptr)}; break; default: break; } if (m_gasSet) - size++; + slots.emplace_back("gas", TypeProvider::uint256()); if (m_valueSet) - size++; + slots.emplace_back("value", TypeProvider::uint256()); if (m_saltSet) - size++; + slots.emplace_back("salt", TypeProvider::uint256()); if (bound()) - size += m_parameterTypes.front()->sizeOnStack(); - return size; + for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackSlots()) + slots.emplace_back("self_" + boundName, boundType); + return slots; } FunctionTypePointer FunctionType::interfaceFunctionType() const @@ -3418,12 +3441,12 @@ u256 TypeType::storageSize() const solAssert(false, "Storage size of non-storable type type requested."); } -unsigned TypeType::sizeOnStack() const +vector> TypeType::makeStackSlots() const { if (auto contractType = dynamic_cast(m_actualType)) if (contractType->contractDefinition().isLibrary()) - return 1; - return 0; + return {make_tuple("address", TypeProvider::address())}; + return {}; } MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 669250970f96..7359e96294b9 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -259,7 +259,26 @@ class Type /// Returns true if the type can be stored as a value (as opposed to a reference) on the stack, /// i.e. it behaves differently in lvalue context and in value context. virtual bool isValueType() const { return false; } - virtual unsigned sizeOnStack() const { return 1; } + std::vector> const& stackSlots() const + { + if (!m_stackSlots) + m_stackSlots = makeStackSlots(); + return *m_stackSlots; + } + unsigned sizeOnStack() const + { + if (!m_stackSize) + { + size_t sizeOnStack = 0; + for (auto const& slot: stackSlots()) + if (std::get<1>(slot)) + sizeOnStack += std::get<1>(slot)->sizeOnStack(); + else + ++sizeOnStack; + m_stackSize = sizeOnStack; + } + return *m_stackSize; + } /// If it is possible to initialize such a value in memory by just writing zeros /// of the size memoryHeadSize(). virtual bool hasSimpleZeroValueInMemory() const { return true; } @@ -336,9 +355,16 @@ class Type { return MemberList::MemberMap(); } + virtual std::vector> makeStackSlots() const + { + return {std::make_tuple(std::string(), nullptr)}; + } + /// List of member types (parameterised by scape), will be lazy-initialized. mutable std::map> m_members; + mutable std::optional>> m_stackSlots; + mutable std::optional m_stackSize; }; /** @@ -562,7 +588,6 @@ class StringLiteralType: public Type bool canBeStored() const override { return false; } bool canLiveOutsideStorage() const override { return false; } - unsigned sizeOnStack() const override { return 0; } std::string toString(bool) const override; TypePointer mobileType() const override; @@ -571,6 +596,8 @@ class StringLiteralType: public Type std::string const& value() const { return m_value; } +protected: + std::vector> makeStackSlots() const override { return {}; } private: std::string m_value; }; @@ -725,7 +752,6 @@ class ArrayType: public ReferenceType bool isDynamicallyEncoded() const override; u256 storageSize() const override; bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); } - unsigned sizeOnStack() const override; std::string toString(bool _short) const override; std::string canonicalName() const override; std::string signatureInExternalFunction(bool _structsByName) const override; @@ -756,6 +782,8 @@ class ArrayType: public ReferenceType void clearCache() const override; +protected: + std::vector> makeStackSlots() const override; private: /// String is interpreted as a subtype of Bytes. enum class ArrayKind { Ordinary, Bytes, String }; @@ -785,7 +813,6 @@ class ArraySliceType: public ReferenceType bool isDynamicallySized() const override { return true; } bool isDynamicallyEncoded() const override { return true; } bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); } - unsigned sizeOnStack() const override { return 2; } std::string toString(bool _short) const override; /// @returns true if this is valid to be stored in calldata @@ -796,6 +823,8 @@ class ArraySliceType: public ReferenceType std::unique_ptr copyForLocation(DataLocation, bool) const override { solAssert(false, ""); } +protected: + std::vector> makeStackSlots() const override; private: ArrayType const& m_arrayType; }; @@ -825,7 +854,6 @@ class ContractType: public Type unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } bool leftAligned() const override { solAssert(!isSuper(), ""); return false; } bool canLiveOutsideStorage() const override { return !isSuper(); } - unsigned sizeOnStack() const override { return m_super ? 0 : 1; } bool isValueType() const override { return !isSuper(); } std::string toString(bool _short) const override; std::string canonicalName() const override; @@ -856,7 +884,8 @@ class ContractType: public Type /// @returns a list of all state variables (including inherited) of the contract and their /// offsets in storage. std::vector> stateVariables() const; - +protected: + std::vector> makeStackSlots() const override; private: ContractDefinition const& m_contract; /// If true, this is a special "super" type of m_contract containing only members that m_contract inherited @@ -989,7 +1018,6 @@ class TupleType: public Type bool canBeStored() const override { return false; } u256 storageSize() const override; bool canLiveOutsideStorage() const override { return false; } - unsigned sizeOnStack() const override; bool hasSimpleZeroValueInMemory() const override { return false; } TypePointer mobileType() const override; /// Converts components to their temporary types and performs some wildcard matching. @@ -997,6 +1025,8 @@ class TupleType: public Type std::vector const& components() const { return m_components; } +protected: + std::vector> makeStackSlots() const override; private: std::vector const m_components; }; @@ -1158,7 +1188,6 @@ class FunctionType: public Type unsigned storageBytes() const override; bool isValueType() const override { return true; } bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } - unsigned sizeOnStack() const override; bool hasSimpleZeroValueInMemory() const override { return false; } MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; TypePointer encodingType() const override; @@ -1252,6 +1281,8 @@ class FunctionType: public Type /// @param _bound if true, the function type is set to be bound. FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const; +protected: + std::vector> makeStackSlots() const override; private: static TypePointers parseElementaryTypeVector(strings const& _types); @@ -1321,12 +1352,13 @@ class TypeType: public Type bool canBeStored() const override { return false; } u256 storageSize() const override; bool canLiveOutsideStorage() const override { return false; } - unsigned sizeOnStack() const override; bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; +protected: + std::vector> makeStackSlots() const override; private: TypePointer m_actualType; }; @@ -1346,12 +1378,13 @@ class ModifierType: public Type bool canBeStored() const override { return false; } u256 storageSize() const override; bool canLiveOutsideStorage() const override { return false; } - unsigned sizeOnStack() const override { return 0; } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } std::string richIdentifier() const override; bool operator==(Type const& _other) const override; std::string toString(bool _short) const override; +protected: + std::vector> makeStackSlots() const override { return {}; } private: TypePointers m_parameterTypes; }; @@ -1374,11 +1407,12 @@ class ModuleType: public Type bool canBeStored() const override { return false; } bool canLiveOutsideStorage() const override { return true; } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - unsigned sizeOnStack() const override { return 0; } MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; std::string toString(bool _short) const override; +protected: + std::vector> makeStackSlots() const override { return {}; } private: SourceUnit const& m_sourceUnit; }; @@ -1413,7 +1447,6 @@ class MagicType: public Type bool canBeStored() const override { return false; } bool canLiveOutsideStorage() const override { return true; } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - unsigned sizeOnStack() const override { return 0; } MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; std::string toString(bool _short) const override; @@ -1422,6 +1455,8 @@ class MagicType: public Type TypePointer typeArgument() const; +protected: + std::vector> makeStackSlots() const override { return {}; } private: Kind m_kind; /// Contract type used for contract metadata magic. @@ -1445,7 +1480,6 @@ class InaccessibleDynamicType: public Type bool canBeStored() const override { return false; } bool canLiveOutsideStorage() const override { return false; } bool isValueType() const override { return true; } - unsigned sizeOnStack() const override { return 1; } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } std::string toString(bool) const override { return "inaccessible dynamic type"; } TypePointer decodingType() const override; From 3c9f18b749b8688229aa9214d6bb900204ac15e5 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 6 Feb 2020 14:05:25 +0100 Subject: [PATCH 116/160] Use IRVariable's in IR code generation and implement tuples. --- libsolidity/CMakeLists.txt | 3 +- libsolidity/ast/Types.cpp | 42 +- libsolidity/ast/Types.h | 43 +- libsolidity/codegen/YulUtilFunctions.cpp | 38 ++ .../codegen/ir/IRGenerationContext.cpp | 32 +- libsolidity/codegen/ir/IRGenerationContext.h | 12 +- libsolidity/codegen/ir/IRGenerator.cpp | 4 +- .../codegen/ir/IRGeneratorForStatements.cpp | 635 +++++++++++------- .../codegen/ir/IRGeneratorForStatements.h | 31 +- libsolidity/codegen/ir/IRLValue.cpp | 228 ------- libsolidity/codegen/ir/IRLValue.h | 134 +--- libsolidity/codegen/ir/IRVariable.cpp | 111 +++ libsolidity/codegen/ir/IRVariable.h | 85 +++ .../yul_string_format_ascii/output.json | 8 +- .../yul_string_format_ascii_long/output.json | 8 +- .../assign_tuple_from_function_call.sol | 16 + .../define_tuple_from_function_call.sol | 18 + .../viaYul/local_tuple_assignment.sol | 32 + .../viaYul/local_variable_without_init.sol | 10 + .../viaYul/tuple_evaluation_order.sol | 14 + 20 files changed, 855 insertions(+), 649 deletions(-) delete mode 100644 libsolidity/codegen/ir/IRLValue.cpp create mode 100644 libsolidity/codegen/ir/IRVariable.cpp create mode 100644 libsolidity/codegen/ir/IRVariable.h create mode 100644 test/libsolidity/semanticTests/viaYul/assign_tuple_from_function_call.sol create mode 100644 test/libsolidity/semanticTests/viaYul/define_tuple_from_function_call.sol create mode 100644 test/libsolidity/semanticTests/viaYul/local_tuple_assignment.sol create mode 100644 test/libsolidity/semanticTests/viaYul/local_variable_without_init.sol create mode 100644 test/libsolidity/semanticTests/viaYul/tuple_evaluation_order.sol diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index c5aaaa6e01e4..bb54c836f83d 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -79,8 +79,9 @@ set(sources codegen/ir/IRGeneratorForStatements.h codegen/ir/IRGenerationContext.cpp codegen/ir/IRGenerationContext.h - codegen/ir/IRLValue.cpp codegen/ir/IRLValue.h + codegen/ir/IRVariable.cpp + codegen/ir/IRVariable.h formal/BMC.cpp formal/BMC.h formal/CHC.cpp diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bcc3af3379c5..c4d4c9fd4d90 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -151,7 +151,7 @@ util::Result transformParametersToExternal(TypePointers const& _pa void Type::clearCache() const { m_members.clear(); - m_stackSlots.reset(); + m_stackItems.reset(); m_stackSize.reset(); } @@ -1703,14 +1703,22 @@ u256 ArrayType::storageSize() const return max(1, u256(size)); } -vector> ArrayType::makeStackSlots() const +vector> ArrayType::makeStackItems() const { - if (m_location == DataLocation::CallData && isDynamicallySized()) - return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())}; - else - // storage slot or memory offset - // byte offset inside storage value is omitted - return {std::make_tuple(string(), nullptr)}; + switch (m_location) + { + case DataLocation::CallData: + if (isDynamicallySized()) + return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())}; + else + return {std::make_tuple("offset", TypeProvider::uint256())}; + case DataLocation::Memory: + return {std::make_tuple("mpos", TypeProvider::uint256())}; + case DataLocation::Storage: + // byte offset inside storage value is omitted + return {std::make_tuple("slot", TypeProvider::uint256())}; + } + solAssert(false, ""); } string ArrayType::toString(bool _short) const @@ -1892,7 +1900,7 @@ string ArraySliceType::toString(bool _short) const return m_arrayType.toString(_short) + " slice"; } -std::vector> ArraySliceType::makeStackSlots() const +std::vector> ArraySliceType::makeStackItems() const { return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}}; } @@ -1995,12 +2003,12 @@ vector> ContractType::stateVar return variablesAndOffsets; } -vector> ContractType::makeStackSlots() const +vector> ContractType::makeStackItems() const { if (m_super) return {}; else - return {make_tuple("address", isPayable() ? TypeProvider::payableAddress() :TypeProvider::address())}; + return {make_tuple("address", isPayable() ? TypeProvider::payableAddress() : TypeProvider::address())}; } void StructType::clearCache() const @@ -2436,7 +2444,7 @@ u256 TupleType::storageSize() const solAssert(false, "Storage size of non-storable tuple type requested."); } -vector> TupleType::makeStackSlots() const +vector> TupleType::makeStackItems() const { vector> slots; unsigned i = 1; @@ -2902,7 +2910,7 @@ unsigned FunctionType::storageBytes() const solAssert(false, "Storage size of non-storable function type requested."); } -vector> FunctionType::makeStackSlots() const +vector> FunctionType::makeStackItems() const { vector> slots; Kind kind = m_kind; @@ -2927,12 +2935,12 @@ vector> FunctionType::makeStackSlots() const slots = {make_tuple("address", TypeProvider::address())}; break; case Kind::Internal: - slots = {make_tuple(string(), nullptr)}; + slots = {make_tuple("functionIdentifier", TypeProvider::uint256())}; break; case Kind::ArrayPush: case Kind::ArrayPop: case Kind::ByteArrayPush: - slots = {make_tuple(string(), nullptr)}; + slots = {make_tuple("slot", TypeProvider::uint256())}; break; default: break; @@ -2945,7 +2953,7 @@ vector> FunctionType::makeStackSlots() const if (m_saltSet) slots.emplace_back("salt", TypeProvider::uint256()); if (bound()) - for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackSlots()) + for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems()) slots.emplace_back("self_" + boundName, boundType); return slots; } @@ -3441,7 +3449,7 @@ u256 TypeType::storageSize() const solAssert(false, "Storage size of non-storable type type requested."); } -vector> TypeType::makeStackSlots() const +vector> TypeType::makeStackItems() const { if (auto contractType = dynamic_cast(m_actualType)) if (contractType->contractDefinition().isLibrary()) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 7359e96294b9..5aada86c3a5f 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -259,18 +259,25 @@ class Type /// Returns true if the type can be stored as a value (as opposed to a reference) on the stack, /// i.e. it behaves differently in lvalue context and in value context. virtual bool isValueType() const { return false; } - std::vector> const& stackSlots() const + /// @returns a list of named and typed stack items that determine the layout of this type on the stack. + /// A stack item either has an empty name and type ``nullptr`` referring to a single stack slot, or + /// has a non-empty name and a valid type referring to the stack layout of that type. + /// The complete layout of a type on the stack can be obtained from its stack items recursively as follows: + /// - Each unnamed stack item is untyped (its type is ``nullptr``) and contributes exactly one stack slot. + /// - Each named stack item is typed and contributes the stack slots given by the stack items of its type. + std::vector> const& stackItems() const { - if (!m_stackSlots) - m_stackSlots = makeStackSlots(); - return *m_stackSlots; + if (!m_stackItems) + m_stackItems = makeStackItems(); + return *m_stackItems; } + /// Total number of stack slots occupied by this type. This is the sum of ``sizeOnStack`` of all ``stackItems()``. unsigned sizeOnStack() const { if (!m_stackSize) { size_t sizeOnStack = 0; - for (auto const& slot: stackSlots()) + for (auto const& slot: stackItems()) if (std::get<1>(slot)) sizeOnStack += std::get<1>(slot)->sizeOnStack(); else @@ -355,7 +362,9 @@ class Type { return MemberList::MemberMap(); } - virtual std::vector> makeStackSlots() const + /// Generates the stack items to be returned by ``stackItems()``. Defaults + /// to exactly one unnamed and untyped stack item referring to a single stack slot. + virtual std::vector> makeStackItems() const { return {std::make_tuple(std::string(), nullptr)}; } @@ -363,7 +372,7 @@ class Type /// List of member types (parameterised by scape), will be lazy-initialized. mutable std::map> m_members; - mutable std::optional>> m_stackSlots; + mutable std::optional>> m_stackItems; mutable std::optional m_stackSize; }; @@ -597,7 +606,7 @@ class StringLiteralType: public Type std::string const& value() const { return m_value; } protected: - std::vector> makeStackSlots() const override { return {}; } + std::vector> makeStackItems() const override { return {}; } private: std::string m_value; }; @@ -783,7 +792,7 @@ class ArrayType: public ReferenceType void clearCache() const override; protected: - std::vector> makeStackSlots() const override; + std::vector> makeStackItems() const override; private: /// String is interpreted as a subtype of Bytes. enum class ArrayKind { Ordinary, Bytes, String }; @@ -824,7 +833,7 @@ class ArraySliceType: public ReferenceType std::unique_ptr copyForLocation(DataLocation, bool) const override { solAssert(false, ""); } protected: - std::vector> makeStackSlots() const override; + std::vector> makeStackItems() const override; private: ArrayType const& m_arrayType; }; @@ -885,7 +894,7 @@ class ContractType: public Type /// offsets in storage. std::vector> stateVariables() const; protected: - std::vector> makeStackSlots() const override; + std::vector> makeStackItems() const override; private: ContractDefinition const& m_contract; /// If true, this is a special "super" type of m_contract containing only members that m_contract inherited @@ -1026,7 +1035,7 @@ class TupleType: public Type std::vector const& components() const { return m_components; } protected: - std::vector> makeStackSlots() const override; + std::vector> makeStackItems() const override; private: std::vector const m_components; }; @@ -1282,7 +1291,7 @@ class FunctionType: public Type FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const; protected: - std::vector> makeStackSlots() const override; + std::vector> makeStackItems() const override; private: static TypePointers parseElementaryTypeVector(strings const& _types); @@ -1358,7 +1367,7 @@ class TypeType: public Type BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; protected: - std::vector> makeStackSlots() const override; + std::vector> makeStackItems() const override; private: TypePointer m_actualType; }; @@ -1384,7 +1393,7 @@ class ModifierType: public Type std::string toString(bool _short) const override; protected: - std::vector> makeStackSlots() const override { return {}; } + std::vector> makeStackItems() const override { return {}; } private: TypePointers m_parameterTypes; }; @@ -1412,7 +1421,7 @@ class ModuleType: public Type std::string toString(bool _short) const override; protected: - std::vector> makeStackSlots() const override { return {}; } + std::vector> makeStackItems() const override { return {}; } private: SourceUnit const& m_sourceUnit; }; @@ -1456,7 +1465,7 @@ class MagicType: public Type TypePointer typeArgument() const; protected: - std::vector> makeStackSlots() const override { return {}; } + std::vector> makeStackItems() const override { return {}; } private: Kind m_kind; /// Contract type used for contract metadata magic. diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 4cc2c87144c1..f696c265cc17 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1791,6 +1791,44 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const "_to_" + _to.identifier(); return m_functionCollector->createFunction(functionName, [&]() { + if ( + auto fromTuple = dynamic_cast(&_from), toTuple = dynamic_cast(&_to); + fromTuple && toTuple && fromTuple->components().size() == toTuple->components().size() + ) + { + size_t sourceStackSize = 0; + size_t destStackSize = 0; + std::string conversions; + for (size_t i = 0; i < fromTuple->components().size(); ++i) + { + auto fromComponent = fromTuple->components()[i]; + auto toComponent = toTuple->components()[i]; + solAssert(fromComponent, ""); + if (toComponent) + { + conversions += + suffixedVariableNameList("converted", destStackSize, destStackSize + toComponent->sizeOnStack()) + + " := " + + conversionFunction(*fromComponent, *toComponent) + + "(" + + suffixedVariableNameList("value", sourceStackSize, sourceStackSize + fromComponent->sizeOnStack()) + + ")\n"; + destStackSize += toComponent->sizeOnStack(); + } + sourceStackSize += fromComponent->sizeOnStack(); + } + return Whiskers(R"( + function () -> { + + } + )") + ("functionName", functionName) + ("values", suffixedVariableNameList("value", 0, sourceStackSize)) + ("converted", suffixedVariableNameList("converted", 0, destStackSize)) + ("conversions", conversions) + .render(); + } + solUnimplementedAssert( _from.category() == Type::Category::StringLiteral, "Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented." diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index bd54e1fbda01..43cb6a040b84 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -31,23 +31,22 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; -string IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl) +IRVariable const& IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl) { - solUnimplementedAssert( - _varDecl.annotation().type->sizeOnStack() == 1, - "Multi-slot types not yet implemented." + auto const& [it, didInsert] = m_localVariables.emplace( + std::make_pair(&_varDecl, IRVariable{_varDecl}) ); - - return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id()); + solAssert(didInsert, "Local variable added multiple times."); + return it->second; } -string IRGenerationContext::localVariableName(VariableDeclaration const& _varDecl) +IRVariable const& IRGenerationContext::localVariable(VariableDeclaration const& _varDecl) { solAssert( m_localVariables.count(&_varDecl), "Unknown variable: " + _varDecl.name() ); - return m_localVariables[&_varDecl]; + return m_localVariables.at(&_varDecl); } void IRGenerationContext::addStateVariable( @@ -98,23 +97,6 @@ string IRGenerationContext::newYulVariable() return "_" + to_string(++m_varCounter); } -string IRGenerationContext::variable(Expression const& _expression) -{ - unsigned size = _expression.annotation().type->sizeOnStack(); - string var = "expr_" + to_string(_expression.id()); - if (size == 1) - return var; - else - return suffixedVariableNameList(move(var) + "_", 1, 1 + size); -} - -string IRGenerationContext::variablePart(Expression const& _expression, string const& _part) -{ - size_t numVars = _expression.annotation().type->sizeOnStack(); - solAssert(numVars > 1, ""); - return "expr_" + to_string(_expression.id()) + "_" + _part; -} - string IRGenerationContext::internalDispatch(size_t _in, size_t _out) { string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out); diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 698a6d38a854..d4c29ab104df 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -20,6 +20,7 @@ #pragma once +#include #include #include @@ -68,9 +69,9 @@ class IRGenerationContext } - std::string addLocalVariable(VariableDeclaration const& _varDecl); + IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl); bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); } - std::string localVariableName(VariableDeclaration const& _varDecl); + IRVariable const& localVariable(VariableDeclaration const& _varDecl); void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset); bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); } @@ -85,11 +86,6 @@ class IRGenerationContext std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration); std::string newYulVariable(); - /// @returns the variable (or comma-separated list of variables) that contain - /// the value of the given expression. - std::string variable(Expression const& _expression); - /// @returns the sub-variable of a multi-variable expression. - std::string variablePart(Expression const& _expression, std::string const& _part); std::string internalDispatch(size_t _in, size_t _out); @@ -109,7 +105,7 @@ class IRGenerationContext RevertStrings m_revertStrings; OptimiserSettings m_optimiserSettings; std::vector m_inheritanceHierarchy; - std::map m_localVariables; + std::map m_localVariables; /// Storage offsets of state variables std::map> m_stateVariables; std::shared_ptr m_functions; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 69c47df00dea..9c2d8807c88b 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -139,11 +139,11 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) t("functionName", functionName); string params; for (auto const& varDecl: _function.parameters()) - params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl); + params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); t("params", params); string retParams; for (auto const& varDecl: _function.returnParameters()) - retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl); + retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); t("returns", retParams.empty() ? "" : " -> " + retParams); t("body", generate(_function.body())); return t.render(); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 2d38fe026097..3ed5abad0393 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,9 @@ #include #include #include +#include + +#include using namespace std; using namespace solidity; @@ -113,7 +117,7 @@ struct CopyTranslate: public yul::ASTCopier return yul::Identifier{ _identifier.location, - yul::YulString{m_context.localVariableName(*varDecl)} + yul::YulString{m_context.localVariable(*varDecl).name()} }; } @@ -125,8 +129,6 @@ struct CopyTranslate: public yul::ASTCopier } - - string IRGeneratorForStatements::code() const { solAssert(!m_currentLValue, "LValue not reset!"); @@ -140,36 +142,42 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va if (_varDecl.value()) { _varDecl.value()->accept(*this); - string value = m_context.newYulVariable(); - Type const& varType = *_varDecl.type(); - - m_code << "let " << value << " := " << expressionAsType(*_varDecl.value(), varType) << "\n"; - m_code << IRStorageItem{m_context, _varDecl}.storeValue(value, varType); + writeToLValue(IRLValue{ + *_varDecl.annotation().type, + IRLValue::Storage{ + util::toCompactHexWithPrefix(m_context.storageLocationOfVariable(_varDecl).first), + m_context.storageLocationOfVariable(_varDecl).second + } + }, *_varDecl.value()); } } void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) { - for (auto const& decl: _varDeclStatement.declarations()) - if (decl) - m_context.addLocalVariable(*decl); - if (Expression const* expression = _varDeclStatement.initialValue()) { - solUnimplementedAssert(_varDeclStatement.declarations().size() == 1, ""); - - VariableDeclaration const& varDecl = *_varDeclStatement.declarations().front(); - m_code << - "let " << - m_context.localVariableName(varDecl) << - " := " << - expressionAsType(*expression, *varDecl.type()) << - "\n"; + if (_varDeclStatement.declarations().size() > 1) + { + auto const* tupleType = dynamic_cast(expression->annotation().type); + solAssert(tupleType, "Expected expression of tuple type."); + solAssert(_varDeclStatement.declarations().size() == tupleType->components().size(), "Invalid number of tuple components."); + for (size_t i = 0; i < _varDeclStatement.declarations().size(); ++i) + if (auto const& decl = _varDeclStatement.declarations()[i]) + { + solAssert(tupleType->components()[i], ""); + define(m_context.addLocalVariable(*decl), IRVariable(*expression).tupleComponent(i)); + } + } + else + { + VariableDeclaration const& varDecl = *_varDeclStatement.declarations().front(); + define(m_context.addLocalVariable(varDecl), *expression); + } } else for (auto const& decl: _varDeclStatement.declarations()) if (decl) - m_code << "let " << m_context.localVariableName(*decl) << "\n"; + declare(m_context.addLocalVariable(*decl)); } bool IRGeneratorForStatements::visit(Assignment const& _assignment) @@ -178,8 +186,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) Type const* intermediateType = type(_assignment.rightHandSide()).closestTemporaryType( &type(_assignment.leftHandSide()) ); - string value = m_context.newYulVariable(); - m_code << "let " << value << " := " << expressionAsType(_assignment.rightHandSide(), *intermediateType) << "\n"; + IRVariable value = convert(_assignment.rightHandSide(), *intermediateType); _assignment.leftHandSide().accept(*this); solAssert(!!m_currentLValue, "LValue not retrieved."); @@ -189,19 +196,19 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) solAssert(type(_assignment.leftHandSide()) == *intermediateType, ""); solAssert(intermediateType->isValueType(), "Compound operators only available for value types."); - string leftIntermediate = m_context.newYulVariable(); - m_code << "let " << leftIntermediate << " := " << m_currentLValue->retrieveValue() << "\n"; - m_code << value << " := " << binaryOperation( + IRVariable leftIntermediate = readFromLValue(*m_currentLValue); + m_code << value.name() << " := " << binaryOperation( TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()), *intermediateType, - leftIntermediate, - value + leftIntermediate.name(), + value.name() ); } - m_code << m_currentLValue->storeValue(value, *intermediateType); + writeToLValue(*m_currentLValue, value); m_currentLValue.reset(); - defineExpression(_assignment) << value << "\n"; + if (*_assignment.annotation().type != *TypeProvider::emptyTuple()) + define(_assignment, value); return false; } @@ -212,11 +219,43 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) solUnimplementedAssert(false, ""); else { - solUnimplementedAssert(!_tuple.annotation().lValueRequested, ""); - solUnimplementedAssert(_tuple.components().size() == 1, ""); - solAssert(_tuple.components().front(), ""); - _tuple.components().front()->accept(*this); - defineExpression(_tuple) << m_context.variable(*_tuple.components().front()) << "\n"; + bool lValueRequested = _tuple.annotation().lValueRequested; + if (lValueRequested) + solAssert(!m_currentLValue, ""); + if (_tuple.components().size() == 1) + { + solAssert(_tuple.components().front(), ""); + _tuple.components().front()->accept(*this); + if (lValueRequested) + solAssert(!!m_currentLValue, ""); + else + define(_tuple, *_tuple.components().front()); + } + else + { + vector> lvalues; + for (size_t i = 0; i < _tuple.components().size(); ++i) + if (auto const& component = _tuple.components()[i]) + { + component->accept(*this); + if (lValueRequested) + { + solAssert(!!m_currentLValue, ""); + lvalues.emplace_back(std::move(m_currentLValue)); + m_currentLValue.reset(); + } + else + define(IRVariable(_tuple).tupleComponent(i), *component); + } + else if (lValueRequested) + lvalues.emplace_back(); + + if (_tuple.annotation().lValueRequested) + m_currentLValue.emplace(IRLValue{ + *_tuple.annotation().type, + IRLValue::Tuple{std::move(lvalues)} + }); + } } return false; } @@ -284,17 +323,11 @@ void IRGeneratorForStatements::endVisit(Return const& _return) solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); vector> const& returnParameters = _return.annotation().functionReturnParameters->parameters(); - TypePointers types; - for (auto const& retVariable: returnParameters) - types.push_back(retVariable->annotation().type); - - // TODO support tuples - solUnimplementedAssert(types.size() == 1, "Multi-returns not implemented."); - m_code << - m_context.localVariableName(*returnParameters.front()) << - " := " << - expressionAsType(*value, *types.front()) << - "\n"; + if (returnParameters.size() > 1) + for (size_t i = 0; i < returnParameters.size(); ++i) + assign(m_context.localVariable(*returnParameters[i]), IRVariable(*value).tupleComponent(i)); + else if (returnParameters.size() == 1) + assign(m_context.localVariable(*returnParameters.front()), *value); } m_code << "leave\n"; } @@ -307,15 +340,30 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) if (op == Token::Delete) { solAssert(!!m_currentLValue, "LValue not retrieved."); - m_code << m_currentLValue->setToZero(); - m_currentLValue.reset(); + std::visit( + util::GenericVisitor{ + [&](IRLValue::Storage const& _storage) { + m_code << + m_utils.storageSetToZeroFunction(m_currentLValue->type) << + "(" << + _storage.slot << + ", " << + _storage.offsetString() << + ")\n"; + m_currentLValue.reset(); + }, + [&](auto const&) { + IRVariable zeroValue(m_context.newYulVariable(), m_currentLValue->type); + define(zeroValue) << m_utils.zeroValueFunction(m_currentLValue->type) << "()\n"; + writeToLValue(*m_currentLValue, zeroValue); + m_currentLValue.reset(); + } + }, + m_currentLValue->kind + ); } else if (resultType.category() == Type::Category::RationalNumber) - { - defineExpression(_unaryOperation) << - formatNumber(resultType.literalValue(nullptr)) << - "\n"; - } + define(_unaryOperation) << formatNumber(resultType.literalValue(nullptr)) << "\n"; else if (resultType.category() == Type::Category::Integer) { solAssert(resultType == type(_unaryOperation.subExpression()), "Result type doesn't match!"); @@ -323,28 +371,21 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) if (op == Token::Inc || op == Token::Dec) { solAssert(!!m_currentLValue, "LValue not retrieved."); - string fetchValueExpr = m_currentLValue->retrieveValue(); - string modifiedValue = m_context.newYulVariable(); - string originalValue = m_context.newYulVariable(); + IRVariable modifiedValue(m_context.newYulVariable(), resultType); + IRVariable originalValue = readFromLValue(*m_currentLValue); - m_code << "let " << originalValue << " := " << fetchValueExpr << "\n"; - m_code << - "let " << - modifiedValue << - " := " << + define(modifiedValue) << (op == Token::Inc ? m_utils.incrementCheckedFunction(resultType) : m_utils.decrementCheckedFunction(resultType) ) << "(" << - originalValue << + originalValue.name() << ")\n"; - m_code << m_currentLValue->storeValue(modifiedValue, resultType); + writeToLValue(*m_currentLValue, modifiedValue); m_currentLValue.reset(); - defineExpression(_unaryOperation) << - (_unaryOperation.isPrefixOperation() ? modifiedValue : originalValue) << - "\n"; + define(_unaryOperation, _unaryOperation.isPrefixOperation() ? modifiedValue : originalValue); } else if (op == Token::BitNot) appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression()); @@ -354,11 +395,10 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) else if (op == Token::Sub) { IntegerType const& intType = *dynamic_cast(&resultType); - - defineExpression(_unaryOperation) << + define(_unaryOperation) << m_utils.negateNumberCheckedFunction(intType) << "(" << - m_context.variable(_unaryOperation.subExpression()) << + IRVariable(_unaryOperation.subExpression()).name() << ")\n"; } else @@ -394,9 +434,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) _binOp.rightExpression().accept(*this); if (commonType->category() == Type::Category::RationalNumber) - defineExpression(_binOp) << - toCompactHexWithPrefix(commonType->literalValue(nullptr)) << - "\n"; + define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n"; else if (TokenTraits::isCompareOp(op)) { if (auto type = dynamic_cast(commonType)) @@ -430,13 +468,13 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) expr = (isSigned ? "slt(" : "lt(") + move(args) + ")"; else solAssert(false, "Unknown comparison operator."); - defineExpression(_binOp) << expr << "\n"; + define(_binOp) << expr << "\n"; } else { string left = expressionAsType(_binOp.leftExpression(), *commonType); string right = expressionAsType(_binOp.rightExpression(), *commonType); - defineExpression(_binOp) << binaryOperation(_binOp.getOperator(), *commonType, left, right); + define(_binOp) << binaryOperation(_binOp.getOperator(), *commonType, left, right) << "\n"; } return false; } @@ -455,11 +493,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { solAssert(funcType.category() == Type::Category::TypeType, "Expected category to be TypeType"); solAssert(_functionCall.arguments().size() == 1, "Expected one argument for type conversion"); - - defineExpression(_functionCall) << - expressionAsType(*_functionCall.arguments().front(), type(_functionCall)) << - "\n"; - + define(_functionCall, *_functionCall.arguments().front()); return; } @@ -495,16 +529,16 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) vector args; for (unsigned i = 0; i < arguments.size(); ++i) if (functionType->takesArbitraryParameters()) - args.emplace_back(m_context.variable(*arguments[i])); + args.emplace_back(IRVariable(*arguments[i]).commaSeparatedList()); else - args.emplace_back(expressionAsType(*arguments[i], *parameterTypes[i])); + args.emplace_back(convert(*arguments[i], *parameterTypes[i]).commaSeparatedList()); if (auto identifier = dynamic_cast(&_functionCall.expression())) { solAssert(!functionType->bound(), ""); if (auto functionDef = dynamic_cast(identifier->annotation().referencedDeclaration)) { - defineExpression(_functionCall) << + define(_functionCall) << m_context.virtualFunctionName(*functionDef) << "(" << joinHumanReadable(args) << @@ -513,11 +547,11 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } } - args = vector{m_context.variable(_functionCall.expression())} + args; - defineExpression(_functionCall) << + define(_functionCall) << m_context.internalDispatch(functionType->parameterTypes().size(), functionType->returnParameterTypes().size()) << "(" << - joinHumanReadable(args) << + IRVariable(_functionCall.expression()).part("functionIdentifier").name() << + joinHumanReadablePrefixed(args) << ")\n"; break; } @@ -536,36 +570,31 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) TypePointers paramTypes = functionType->parameterTypes(); ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); - vector indexedArgs; + vector indexedArgs; string nonIndexedArgs; TypePointers nonIndexedArgTypes; TypePointers nonIndexedParamTypes; if (!event.isAnonymous()) - { - indexedArgs.emplace_back(m_context.newYulVariable()); - string signature = formatNumber(u256(h256::Arith(keccak256(functionType->externalSignature())))); - m_code << "let " << indexedArgs.back() << " := " << signature << "\n"; - } + define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::uint256())) << + formatNumber(u256(h256::Arith(keccak256(functionType->externalSignature())))) << "\n"; for (size_t i = 0; i < event.parameters().size(); ++i) { Expression const& arg = *arguments[i]; if (event.parameters()[i]->isIndexed()) { string value; - indexedArgs.emplace_back(m_context.newYulVariable()); if (auto const& referenceType = dynamic_cast(paramTypes[i])) - value = - m_utils.packedHashFunction({arg.annotation().type}, {referenceType}) + - "(" + - m_context.variable(arg) + + define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::uint256())) << + m_utils.packedHashFunction({arg.annotation().type}, {referenceType}) << + "(" << + IRVariable(arg).commaSeparatedList() << ")"; else - value = expressionAsType(arg, *paramTypes[i]); - m_code << "let " << indexedArgs.back() << " := " << value << "\n"; + indexedArgs.emplace_back(convert(arg, *paramTypes[i])); } else { - string vars = m_context.variable(arg); + string vars = IRVariable(arg).commaSeparatedList(); if (!vars.empty()) // In reverse because abi_encode expects it like that. nonIndexedArgs = ", " + move(vars) + nonIndexedArgs; @@ -585,7 +614,9 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes)); templ("nonIndexedArgs", nonIndexedArgs); templ("log", "log" + to_string(indexedArgs.size())); - templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs)); + templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) { + return _arg.commaSeparatedList(); + }))); m_code << templ.render(); break; } @@ -601,9 +632,9 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) messageArgumentType ); - m_code << move(requireOrAssertFunction) << "(" << m_context.variable(*arguments[0]); + m_code << move(requireOrAssertFunction) << "(" << IRVariable(*arguments[0]).name(); if (messageArgumentType && messageArgumentType->sizeOnStack() > 0) - m_code << ", " << m_context.variable(*arguments[1]); + m_code << ", " << IRVariable(*arguments[1]).commaSeparatedList(); m_code << ")\n"; break; @@ -614,12 +645,12 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) ArrayType const& arrayType = dynamic_cast(*_functionCall.annotation().type); solAssert(arguments.size() == 1, ""); - defineExpression(_functionCall) << + IRVariable value = convert(*arguments[0], *TypeProvider::uint256()); + define(_functionCall) << m_utils.allocateMemoryArrayFunction(arrayType) << "(" << - expressionAsType(*arguments[0], *TypeProvider::uint256()) << + value.commaSeparatedList() << ")\n"; - break; } case FunctionType::Kind::KECCAK256: @@ -627,60 +658,61 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) solAssert(arguments.size() == 1, ""); ArrayType const* arrayType = TypeProvider::bytesMemory(); - string const& array = m_context.newYulVariable(); - m_code << "let " << array << " := " << expressionAsType(*arguments[0], *arrayType) << "\n"; + auto array = convert(*arguments[0], *arrayType); - defineExpression(_functionCall) << + define(_functionCall) << "keccak256(" << - m_utils.arrayDataAreaFunction(*arrayType) << "(" << - array << + m_utils.arrayDataAreaFunction(*arrayType) << + "(" << + array.commaSeparatedList() << "), " << m_utils.arrayLengthFunction(*arrayType) << "(" << - array << + array.commaSeparatedList() << "))\n"; - - break; + break; } case FunctionType::Kind::ArrayPop: { - ArrayType const& arrayType = dynamic_cast( - *dynamic_cast(_functionCall.expression()).expression().annotation().type - ); - defineExpression(_functionCall) << + auto const& memberAccessExpression = dynamic_cast(_functionCall.expression()).expression(); + ArrayType const& arrayType = dynamic_cast(*memberAccessExpression.annotation().type); + define(_functionCall) << m_utils.storageArrayPopFunction(arrayType) << "(" << - m_context.variable(_functionCall.expression()) << + IRVariable(_functionCall.expression()).commaSeparatedList() << ")\n"; break; } case FunctionType::Kind::ArrayPush: { - ArrayType const& arrayType = dynamic_cast( - *dynamic_cast(_functionCall.expression()).expression().annotation().type - ); + auto const& memberAccessExpression = dynamic_cast(_functionCall.expression()).expression(); + ArrayType const& arrayType = dynamic_cast(*memberAccessExpression.annotation().type); if (arguments.empty()) { auto slotName = m_context.newYulVariable(); auto offsetName = m_context.newYulVariable(); m_code << "let " << slotName << ", " << offsetName << " := " << m_utils.storageArrayPushZeroFunction(arrayType) << - "(" << m_context.variable(_functionCall.expression()) << ")\n"; - setLValue(_functionCall, make_unique( - m_context.utils(), - slotName, - offsetName, - *arrayType.baseType() - )); + "(" << IRVariable(_functionCall.expression()).commaSeparatedList() << ")\n"; + setLValue(_functionCall, IRLValue{ + *arrayType.baseType(), + IRLValue::Storage{ + slotName, + offsetName, + } + }); } else + { + IRVariable argument = convert(*arguments.front(), *arrayType.baseType()); m_code << m_utils.storageArrayPushFunction(arrayType) << "(" << - m_context.variable(_functionCall.expression()) << + IRVariable(_functionCall.expression()).commaSeparatedList() << ", " << - expressionAsType(*arguments.front(), *arrayType.baseType()) << + argument.commaSeparatedList() << ")\n"; + } break; } default: @@ -717,11 +749,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) else solAssert(false, "Contract member is neither variable nor function."); - defineExpressionPart(_memberAccess, "address") << expressionAsType( - _memberAccess.expression(), - type.isPayable() ? *TypeProvider::payableAddress() : *TypeProvider::address() - ) << "\n"; - defineExpressionPart(_memberAccess, "functionIdentifier") << formatNumber(identifier) << "\n"; + define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); + define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(identifier) << "\n"; } else solAssert(false, "Invalid member access in contract"); @@ -735,21 +764,17 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) case Type::Category::Address: { if (member == "balance") - defineExpression(_memberAccess) << + define(_memberAccess) << "balance(" << expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << ")\n"; else if (set{"send", "transfer"}.count(member)) { solAssert(dynamic_cast(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); - defineExpression(_memberAccess) << - expressionAsType(_memberAccess.expression(), *TypeProvider::payableAddress()) << - "\n"; + define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression()); } else if (set{"call", "callcode", "delegatecall", "staticcall"}.count(member)) - defineExpression(_memberAccess) << - expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << - "\n"; + define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression()); else solAssert(false, "Invalid member access to address"); break; @@ -772,27 +797,27 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) case Type::Category::Magic: // we can ignore the kind of magic and only look at the name of the member if (member == "coinbase") - defineExpression(_memberAccess) << "coinbase()\n"; + define(_memberAccess) << "coinbase()\n"; else if (member == "timestamp") - defineExpression(_memberAccess) << "timestamp()\n"; + define(_memberAccess) << "timestamp()\n"; else if (member == "difficulty") - defineExpression(_memberAccess) << "difficulty()\n"; + define(_memberAccess) << "difficulty()\n"; else if (member == "number") - defineExpression(_memberAccess) << "number()\n"; + define(_memberAccess) << "number()\n"; else if (member == "gaslimit") - defineExpression(_memberAccess) << "gaslimit()\n"; + define(_memberAccess) << "gaslimit()\n"; else if (member == "sender") - defineExpression(_memberAccess) << "caller()\n"; + define(_memberAccess) << "caller()\n"; else if (member == "value") - defineExpression(_memberAccess) << "callvalue()\n"; + define(_memberAccess) << "callvalue()\n"; else if (member == "origin") - defineExpression(_memberAccess) << "origin()\n"; + define(_memberAccess) << "origin()\n"; else if (member == "gasprice") - defineExpression(_memberAccess) << "gasprice()\n"; + define(_memberAccess) << "gasprice()\n"; else if (member == "data") solUnimplementedAssert(false, ""); else if (member == "sig") - defineExpression(_memberAccess) << + define(_memberAccess) << "and(calldataload(0), " << formatNumber(u256(0xffffffff) << (256 - 32)) << ")\n"; @@ -822,7 +847,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) case Type::Category::Enum: { EnumType const& type = dynamic_cast(*_memberAccess.expression().annotation().type); - defineExpression(_memberAccess) << to_string(type.memberValue(_memberAccess.memberName())) << "\n"; + define(_memberAccess) << to_string(type.memberValue(_memberAccess.memberName())) << "\n"; break; } case Type::Category::Array: @@ -832,7 +857,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) if (member == "length") { if (!type.isDynamicallySized()) - defineExpression(_memberAccess) << type.length() << "\n"; + define(_memberAccess) << type.length() << "\n"; else switch (type.location()) { @@ -842,28 +867,25 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) break; case DataLocation::Storage: { - string slot = m_context.variable(_memberAccess.expression()); - defineExpression(_memberAccess) << - m_utils.arrayLengthFunction(type) + "(" + slot + ")\n"; + define(_memberAccess) << + m_utils.arrayLengthFunction(type) << + "(" << + IRVariable(_memberAccess.expression()).commaSeparatedList() << + ")\n"; break; } case DataLocation::Memory: - defineExpression(_memberAccess) << + define(_memberAccess) << "mload(" << - m_context.variable(_memberAccess.expression()) << + IRVariable(_memberAccess.expression()).commaSeparatedList() << ")\n"; break; } } - else if (member == "pop") - { - solAssert(type.location() == DataLocation::Storage, ""); - defineExpression(_memberAccess) << m_context.variable(_memberAccess.expression()) << "\n"; - } - else if (member == "push") + else if (member == "pop" || member == "push") { solAssert(type.location() == DataLocation::Storage, ""); - defineExpression(_memberAccess) << m_context.variable(_memberAccess.expression()) << "\n"; + define(IRVariable{_memberAccess}.part("slot"), IRVariable{_memberAccess.expression()}.part("slot")); } else solAssert(false, "Invalid array member access."); @@ -874,7 +896,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { auto const& type = dynamic_cast(*_memberAccess.expression().annotation().type); if (member == "length") - defineExpression(_memberAccess) << to_string(type.numBytes()); + define(_memberAccess) << to_string(type.numBytes()) << "\n"; else solAssert(false, "Illegal fixed bytes member."); break; @@ -914,18 +936,19 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) Whiskers templ("let := ( )\n"); templ("slot", slot); templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType)); - templ("base", m_context.variable(_indexAccess.baseExpression())); + templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList()); if (keyType.sizeOnStack() == 0) templ("key", ""); else - templ("key", ", " + m_context.variable(*_indexAccess.indexExpression())); + templ("key", ", " + IRVariable(*_indexAccess.indexExpression()).commaSeparatedList()); m_code << templ.render(); - setLValue(_indexAccess, make_unique( - m_context.utils(), - slot, - 0, - *_indexAccess.annotation().type - )); + setLValue(_indexAccess, IRLValue{ + *_indexAccess.annotation().type, + IRLValue::Storage{ + slot, + 0 + } + }); } else if (baseType.category() == Type::Category::Array) { @@ -945,16 +968,14 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) ("slot", slot) ("offset", offset) ("indexFunc", m_utils.storageArrayIndexAccessFunction(arrayType)) - ("array", m_context.variable(_indexAccess.baseExpression())) - ("index", m_context.variable(*_indexAccess.indexExpression())) + ("array", IRVariable(_indexAccess.baseExpression()).part("slot").name()) + ("index", IRVariable(*_indexAccess.indexExpression()).name()) .render(); - setLValue(_indexAccess, make_unique( - m_context.utils(), - slot, - offset, - *_indexAccess.annotation().type - )); + setLValue(_indexAccess, IRLValue{ + *_indexAccess.annotation().type, + IRLValue::Storage{slot, offset} + }); break; } @@ -963,17 +984,15 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) string const memAddress = m_utils.memoryArrayIndexAccessFunction(arrayType) + "(" + - m_context.variable(_indexAccess.baseExpression()) + + IRVariable(_indexAccess.baseExpression()).part("mpos").name() + ", " + expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) + ")"; - setLValue(_indexAccess, make_unique( - m_context.utils(), - memAddress, - false, - *arrayType.baseType() - )); + setLValue(_indexAccess, IRLValue{ + *arrayType.baseType(), + IRLValue::Memory{memAddress} + }); break; } case DataLocation::CallData: @@ -1013,12 +1032,12 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) else { solAssert(_identifier.name() == "this", ""); - defineExpression(_identifier) << "address()\n"; + define(_identifier) << "address()\n"; } break; case Type::Category::Integer: solAssert(_identifier.name() == "now", ""); - defineExpression(_identifier) << "timestamp()\n"; + define(_identifier) << "timestamp()\n"; break; default: break; @@ -1026,22 +1045,28 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) return; } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) - defineExpression(_identifier) << to_string(m_context.virtualFunction(*functionDef).id()) << "\n"; + define(_identifier) << to_string(m_context.virtualFunction(*functionDef).id()) << "\n"; else if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) { // TODO for the constant case, we have to be careful: // If the value is visited twice, `defineExpression` is called twice on // the same expression. solUnimplementedAssert(!varDecl->isConstant(), ""); - unique_ptr lvalue; if (m_context.isLocalVariable(*varDecl)) - lvalue = make_unique(m_context, *varDecl); + setLValue(_identifier, IRLValue{ + *varDecl->annotation().type, + IRLValue::Stack{m_context.localVariable(*varDecl)} + }); else if (m_context.isStateVariable(*varDecl)) - lvalue = make_unique(m_context, *varDecl); + setLValue(_identifier, IRLValue{ + *varDecl->annotation().type, + IRLValue::Storage{ + toCompactHexWithPrefix(m_context.storageLocationOfVariable(*varDecl).first), + m_context.storageLocationOfVariable(*varDecl).second + } + }); else solAssert(false, "Invalid variable kind."); - - setLValue(_identifier, move(lvalue)); } else if (auto contract = dynamic_cast(declaration)) { @@ -1074,7 +1099,7 @@ bool IRGeneratorForStatements::visit(Literal const& _literal) case Type::Category::RationalNumber: case Type::Category::Bool: case Type::Category::Address: - defineExpression(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n"; + define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n"; break; case Type::Category::StringLiteral: break; // will be done during conversion @@ -1130,14 +1155,13 @@ void IRGeneratorForStatements::appendExternalFunctionCall( } TypePointers argumentTypes; - string argumentString; + vector argumentStrings; for (auto const& arg: _arguments) { argumentTypes.emplace_back(&type(*arg)); - string var = m_context.variable(*arg); - if (!var.empty()) - argumentString += ", " + move(var); + argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList()); } + string argumentString = joinHumanReadable(argumentStrings); solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); @@ -1178,7 +1202,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( templ("result", m_context.newYulVariable()); templ("freeMem", fetchFreeMem()); templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4))); - templ("funId", m_context.variablePart(_functionCall.expression(), "functionIdentifier")); + templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name()); // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place. // Move arguments to memory, will not update the free memory pointer (but will update the memory @@ -1204,7 +1228,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( else if (useStaticCall) solAssert(!funType.valueSet(), "Value set for staticcall"); else if (funType.valueSet()) - templ("value", m_context.variablePart(_functionCall.expression(), "value")); + templ("value", IRVariable(_functionCall.expression()).part("value").name()); else templ("value", "0"); @@ -1213,7 +1237,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( templ("checkExistence", checkExistence); if (funType.gasSet()) - templ("gas", m_context.variablePart(_functionCall.expression(), "gas")); + templ("gas", IRVariable(_functionCall.expression()).part("gas").name()); else if (m_context.evmVersion().canOverchargeGasForCall()) // Send all gas (requires tangerine whistle EVM) templ("gas", "gas()"); @@ -1251,7 +1275,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( templ("returnSize", to_string(retSize)); templ("abiDecode", abi.tupleDecoder(returnTypes, true)); templ("returns", !returnTypes.empty()); - templ("retVars", m_context.variable(_functionCall)); + templ("retVars", IRVariable(_functionCall).commaSeparatedList()); } string IRGeneratorForStatements::fetchFreeMem() const @@ -1259,38 +1283,58 @@ string IRGeneratorForStatements::fetchFreeMem() const return "mload(" + to_string(CompilerUtils::freeMemoryPointer) + ")"; } -string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to) +IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const& _to) { - Type const& from = type(_expression); - if (from.sizeOnStack() == 0) - { - solAssert(from != _to, ""); - return m_utils.conversionFunction(from, _to) + "()"; - } + if (_from.type() == _to) + return _from; else { - string varName = m_context.variable(_expression); - - if (from == _to) - return varName; - else - return m_utils.conversionFunction(from, _to) + "(" + std::move(varName) + ")"; + IRVariable converted(m_context.newYulVariable(), _to); + define(converted, _from); + return converted; } } -ostream& IRGeneratorForStatements::defineExpression(Expression const& _expression) +std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to) { - string vars = m_context.variable(_expression); - if (!vars.empty()) - m_code << "let " << move(vars) << " := "; - return m_code; + IRVariable from(_expression); + if (from.type() == _to) + return from.commaSeparatedList(); + else + return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")"; } -ostream& IRGeneratorForStatements::defineExpressionPart(Expression const& _expression, string const& _part) +std::ostream& IRGeneratorForStatements::define(IRVariable const& _var) { - return m_code << "let " << m_context.variablePart(_expression, _part) << " := "; + if (_var.type().sizeOnStack() > 0) + m_code << "let " << _var.commaSeparatedList() << " := "; + return m_code; } +void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable const& _rhs, bool _declare) +{ + string output; + if (_lhs.type() == _rhs.type()) + for (auto const& [stackItemName, stackItemType]: _lhs.type().stackItems()) + if (stackItemType) + declareAssign(_lhs.part(stackItemName), _rhs.part(stackItemName), _declare); + else + m_code << (_declare ? "let ": "") << _lhs.part(stackItemName).name() << " := " << _rhs.part(stackItemName).name() << "\n"; + else + m_code << + (_declare ? "let ": "") << + _lhs.commaSeparatedList() << + " := " << + m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) << + "(" << + _rhs.commaSeparatedList() << + ")\n"; +} +void IRGeneratorForStatements::declare(IRVariable const& _var) +{ + if (_var.type().sizeOnStack() > 0) + m_code << "let " << _var.commaSeparatedList() << "\n"; +} void IRGeneratorForStatements::appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr) { @@ -1303,12 +1347,12 @@ void IRGeneratorForStatements::appendSimpleUnaryOperation(UnaryOperation const& else solAssert(false, "Invalid Token!"); - defineExpression(_operation) << + define(_operation) << m_utils.cleanupFunction(type(_expr)) << "(" << func << "(" << - m_context.variable(_expr) << + IRVariable(_expr).commaSeparatedList() << ")" << ")\n"; } @@ -1361,26 +1405,139 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b _binOp.leftExpression().accept(*this); - string value = m_context.variable(_binOp); - m_code << "let " << value << " := " << m_context.variable(_binOp.leftExpression()) << "\n"; + IRVariable value(_binOp); + define(value, _binOp.leftExpression()); if (op == Token::Or) - m_code << "if iszero(" << value << ") {\n"; + m_code << "if iszero(" << value.name() << ") {\n"; else - m_code << "if " << value << " {\n"; + m_code << "if " << value.name() << " {\n"; _binOp.rightExpression().accept(*this); - m_code << value << " := " + m_context.variable(_binOp.rightExpression()) << "\n"; + assign(value, _binOp.rightExpression()); m_code << "}\n"; } -void IRGeneratorForStatements::setLValue(Expression const& _expression, unique_ptr _lvalue) +void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable const& _value) +{ + std::visit( + util::GenericVisitor{ + [&](IRLValue::Storage const& _storage) { + std::optional offset; + + if (std::holds_alternative(_storage.offset)) + offset = std::get(_storage.offset); + + m_code << + m_utils.updateStorageValueFunction(_lvalue.type, offset) << + "(" << + _storage.slot << + ( + std::holds_alternative(_storage.offset) ? + (", " + std::get(_storage.offset)) : + "" + ) << + _value.commaSeparatedListPrefixed() << + ")\n"; + }, + [&](IRLValue::Memory const& _memory) { + if (_lvalue.type.isValueType()) + { + IRVariable prepared(m_context.newYulVariable(), _lvalue.type); + define(prepared, _value); + + if (_memory.byteArrayElement) + { + solAssert(_lvalue.type == *TypeProvider::byte(), ""); + m_code << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n"; + } + else + m_code << m_utils.writeToMemoryFunction(_lvalue.type) << + "(" << + _memory.address << + ", " << + prepared.commaSeparatedList() << + ")\n"; + } + else + { + solAssert(_lvalue.type.sizeOnStack() == 1, ""); + solAssert(dynamic_cast(&_lvalue.type), ""); + auto const* valueReferenceType = dynamic_cast(&_value.type()); + solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), ""); + m_code << "mstore(" + _memory.address + ", " + _value.name() + ")\n"; + } + }, + [&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); }, + [&](IRLValue::Tuple const& _tuple) { + auto components = std::move(_tuple.components); + for (size_t i = 0; i < components.size(); i++) + { + size_t idx = components.size() - i - 1; + if (components[idx]) + writeToLValue(*components[idx], _value.tupleComponent(idx)); + } + } + }, + _lvalue.kind + ); +} + +IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) +{ + IRVariable result{m_context.newYulVariable(), _lvalue.type}; + std::visit(GenericVisitor{ + [&](IRLValue::Storage const& _storage) { + if (!_lvalue.type.isValueType()) + define(result) << _storage.slot << "\n"; + else if (std::holds_alternative(_storage.offset)) + define(result) << + m_utils.readFromStorageDynamic(_lvalue.type, false) << + "(" << + _storage.slot << + ", " << + std::get(_storage.offset) << + ")\n"; + else + define(result) << + m_utils.readFromStorage(_lvalue.type, std::get(_storage.offset), false) << + "(" << + _storage.slot << + ")\n"; + }, + [&](IRLValue::Memory const& _memory) { + if (_memory.byteArrayElement) + define(result) << + m_utils.cleanupFunction(_lvalue.type) << + "(mload(" << + _memory.address << + "))\n"; + else if (_lvalue.type.isValueType()) + define(result) << + m_utils.readFromMemory(_lvalue.type) << + "(" << + _memory.address << + ")\n"; + else + define(result) << "mload(" << _memory.address << ")\n"; + }, + [&](IRLValue::Stack const& _stack) { + define(result, _stack.variable); + }, + [&](IRLValue::Tuple const&) { + solAssert(false, "Attempted to read from tuple lvalue."); + } + }, _lvalue.kind); + return result; +} + +void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue _lvalue) { solAssert(!m_currentLValue, ""); if (_expression.annotation().lValueRequested) - // Do not define the expression, so it cannot be used as value. - m_currentLValue = std::move(_lvalue); + m_currentLValue.emplace(std::move(_lvalue)); else - defineExpression(_expression) << _lvalue->retrieveValue() << "\n"; + // Only define the expression, if it will not be written to. + define(_expression, readFromLValue(_lvalue)); } void IRGeneratorForStatements::generateLoop( diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 9b4d9ab28f6d..15c49ce02ff2 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -22,6 +22,7 @@ #include #include +#include namespace solidity::frontend { @@ -75,12 +76,25 @@ class IRGeneratorForStatements: public ASTConstVisitor std::string fetchFreeMem() const; + /// Generates the required conversion code and @returns an IRVariable referring to the value of @a _variable + /// converted to type @a _to. + IRVariable convert(IRVariable const& _variable, Type const& _to); + /// @returns a Yul expression representing the current value of @a _expression, /// converted to type @a _to if it does not yet have that type. std::string expressionAsType(Expression const& _expression, Type const& _to); - std::ostream& defineExpression(Expression const& _expression); - /// Defines only one of many variables corresponding to an expression. - std::ostream& defineExpressionPart(Expression const& _expression, std::string const& _part); + + /// @returns an output stream that can be used to define @a _var using a function call or + /// single stack slot expression. + std::ostream& define(IRVariable const& _var); + /// Defines @a _var using the value of @a _value while performing type conversions, if required. + void define(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, true); } + /// Assigns @a _var to the value of @a _value while performing type conversions, if required. + void assign(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, false); } + /// Declares variable @a _var. + void declare(IRVariable const& _var); + + void declareAssign(IRVariable const& _var, IRVariable const& _value, bool _define); void appendAndOrOperatorCode(BinaryOperation const& _binOp); void appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr); @@ -93,7 +107,14 @@ class IRGeneratorForStatements: public ASTConstVisitor std::string const& _right ); - void setLValue(Expression const& _expression, std::unique_ptr _lvalue); + /// Assigns the value of @a _value to the lvalue @a _lvalue. + void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value); + /// @returns a fresh IR variable containing the value of the lvalue @a _lvalue. + IRVariable readFromLValue(IRLValue const& _lvalue); + + /// Stores the given @a _lvalue in m_currentLValue, if it will be written to (lValueRequested). Otherwise + /// defines the expression @a _expression by reading the value from @a _lvalue. + void setLValue(Expression const& _expression, IRLValue _lvalue); void generateLoop( Statement const& _body, Expression const* _conditionExpression, @@ -107,7 +128,7 @@ class IRGeneratorForStatements: public ASTConstVisitor std::ostringstream m_code; IRGenerationContext& m_context; YulUtilFunctions& m_utils; - std::unique_ptr m_currentLValue; + std::optional m_currentLValue; }; } diff --git a/libsolidity/codegen/ir/IRLValue.cpp b/libsolidity/codegen/ir/IRLValue.cpp deleted file mode 100644 index 9cbf4e992e1f..000000000000 --- a/libsolidity/codegen/ir/IRLValue.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - 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 . -*/ -/** - * Generator for code that handles LValues. - */ - -#include - -#include -#include -#include -#include - -#include - -using namespace std; -using namespace solidity; -using namespace solidity::frontend; - -IRLocalVariable::IRLocalVariable( - IRGenerationContext& _context, - VariableDeclaration const& _varDecl -): - IRLValue(_context.utils(), _varDecl.annotation().type), - m_variableName(_context.localVariableName(_varDecl)) -{ -} - -string IRLocalVariable::storeValue(string const& _value, Type const& _type) const -{ - solAssert(_type == *m_type, "Storing different types - not necessarily a problem."); - return m_variableName + " := " + _value + "\n"; -} - -string IRLocalVariable::setToZero() const -{ - return storeValue(m_utils.zeroValueFunction(*m_type) + "()", *m_type); -} - -IRStorageItem::IRStorageItem( - IRGenerationContext& _context, - VariableDeclaration const& _varDecl -): - IRStorageItem( - _context.utils(), - *_varDecl.annotation().type, - _context.storageLocationOfVariable(_varDecl) - ) -{ } - -IRStorageItem::IRStorageItem( - YulUtilFunctions _utils, - Type const& _type, - std::pair slot_offset -): - IRLValue(std::move(_utils), &_type), - m_slot(util::toCompactHexWithPrefix(slot_offset.first)), - m_offset(slot_offset.second) -{ -} - -IRStorageItem::IRStorageItem( - YulUtilFunctions _utils, - string _slot, - boost::variant _offset, - Type const& _type -): - IRLValue(std::move(_utils), &_type), - m_slot(std::move(_slot)), - m_offset(std::move(_offset)) -{ - solAssert(!m_offset.empty(), ""); - solAssert(!m_slot.empty(), ""); -} - -string IRStorageItem::retrieveValue() const -{ - if (!m_type->isValueType()) - return m_slot; - solUnimplementedAssert(m_type->category() != Type::Category::Function, ""); - if (m_offset.type() == typeid(string)) - return - m_utils.readFromStorageDynamic(*m_type, false) + - "(" + - m_slot + - ", " + - boost::get(m_offset) + - ")"; - else if (m_offset.type() == typeid(unsigned)) - return - m_utils.readFromStorage(*m_type, boost::get(m_offset), false) + - "(" + - m_slot + - ")"; - - solAssert(false, ""); -} - -string IRStorageItem::storeValue(string const& _value, Type const& _sourceType) const -{ - if (m_type->isValueType()) - solAssert(_sourceType == *m_type, "Different type, but might not be an error."); - - std::optional offset; - - if (m_offset.type() == typeid(unsigned)) - offset = get(m_offset); - - return - m_utils.updateStorageValueFunction(*m_type, offset) + - "(" + - m_slot + - (m_offset.type() == typeid(string) ? - (", " + get(m_offset)) : - "" - ) + - ", " + - _value + - ")\n"; -} - -string IRStorageItem::setToZero() const -{ - return - m_utils.storageSetToZeroFunction(*m_type) + - "(" + - m_slot + - ", " + - ( - m_offset.type() == typeid(unsigned) ? - to_string(get(m_offset)) : - get(m_offset) - ) + - ")\n"; -} - -IRMemoryItem::IRMemoryItem( - YulUtilFunctions _utils, - std::string _address, - bool _byteArrayElement, - Type const& _type -): - IRLValue(std::move(_utils), &_type), - m_address(move(_address)), - m_byteArrayElement(_byteArrayElement) -{ } - -string IRMemoryItem::retrieveValue() const -{ - if (m_byteArrayElement) - return m_utils.cleanupFunction(*m_type) + - "(mload(" + - m_address + - "))"; - - if (m_type->isValueType()) - return m_utils.readFromMemory(*m_type) + - "(" + - m_address + - ")"; - else - return "mload(" + m_address + ")"; -} - -string IRMemoryItem::storeValue(string const& _value, Type const& _type) const -{ - if (!m_type->isValueType()) - { - solUnimplementedAssert(_type == *m_type, "Conversion not implemented for assignment to memory."); - - solAssert(m_type->sizeOnStack() == 1, ""); - solAssert(dynamic_cast(m_type), ""); - - return "mstore(" + m_address + ", " + _value + ")\n"; - } - - solAssert(_type.isValueType(), ""); - - string prepared = _value; - - // Exists to see if this case ever happens - solAssert(_type == *m_type, ""); - - if (_type != *m_type) - prepared = - m_utils.conversionFunction(_type, *m_type) + - "(" + - _value + - ")"; - else - prepared = - m_utils.cleanupFunction(*m_type) + - "(" + - _value + - ")"; - - if (m_byteArrayElement) - { - solAssert(*m_type == *TypeProvider::byte(), ""); - return "mstore8(" + m_address + ", byte(0, " + prepared + "))\n"; - } - else - return m_utils.writeToMemoryFunction(*m_type) + - "(" + - m_address + - ", " + - prepared + - ")\n"; -} - -string IRMemoryItem::setToZero() const -{ - return storeValue(m_utils.zeroValueFunction(*m_type) + "()", *m_type); -} diff --git a/libsolidity/codegen/ir/IRLValue.h b/libsolidity/codegen/ir/IRLValue.h index 7b58416c01c5..02d46b6e4146 100644 --- a/libsolidity/codegen/ir/IRLValue.h +++ b/libsolidity/codegen/ir/IRLValue.h @@ -15,115 +15,51 @@ along with solidity. If not, see . */ /** - * Generator for code that handles LValues. + * Classes that store locations of lvalues. */ #pragma once -#include - -#include - -#include -#include -#include +#include +#include namespace solidity::frontend { -class VariableDeclaration; -class IRGenerationContext; class Type; -class ArrayType; - -/** - * Abstract class used to retrieve, delete and store data in LValues. - */ -class IRLValue -{ -protected: - explicit IRLValue(YulUtilFunctions _utils, Type const* _type = nullptr): - m_utils(std::move(_utils)), - m_type(_type) - {} - -public: - virtual ~IRLValue() = default; - /// @returns an expression to retrieve the value of the lvalue. - virtual std::string retrieveValue() const = 0; - /// Returns code that stores the value of @a _value (should be an identifier) - /// of type @a _type in the lvalue. Might perform type conversion. - virtual std::string storeValue(std::string const& _value, Type const& _type) const = 0; - /// Returns code that will reset the stored value to zero - virtual std::string setToZero() const = 0; -protected: - YulUtilFunctions mutable m_utils; - Type const* m_type; -}; - -class IRLocalVariable: public IRLValue +struct IRLValue { -public: - IRLocalVariable( - IRGenerationContext& _context, - VariableDeclaration const& _varDecl - ); - std::string retrieveValue() const override { return m_variableName; } - std::string storeValue(std::string const& _value, Type const& _type) const override; - - std::string setToZero() const override; -private: - std::string m_variableName; -}; - -class IRStorageItem: public IRLValue -{ -public: - IRStorageItem( - IRGenerationContext& _context, - VariableDeclaration const& _varDecl - ); - IRStorageItem( - YulUtilFunctions _utils, - std::string _slot, - boost::variant _offset, - Type const& _type - ); - std::string retrieveValue() const override; - std::string storeValue(std::string const& _value, Type const& _type) const override; - - std::string setToZero() const override; -private: - IRStorageItem( - YulUtilFunctions _utils, - Type const& _type, - std::pair slot_offset - ); - - std::string const m_slot; - /// unsigned: Used when the offset is known at compile time, uses optimized - /// functions - /// string: Used when the offset is determined at run time - boost::variant const m_offset; -}; - -class IRMemoryItem: public IRLValue -{ -public: - IRMemoryItem( - YulUtilFunctions _utils, - std::string _address, - bool _byteArrayElement, - Type const& _type - ); - std::string retrieveValue() const override; - std::string storeValue(std::string const& _value, Type const& _type) const override; - - std::string setToZero() const override; -private: - std::string const m_address; - bool m_byteArrayElement; + Type const& type; + struct Stack + { + IRVariable variable; + }; + struct Storage + { + std::string const slot; + /// unsigned: Used when the offset is known at compile time, uses optimized + /// functions + /// string: Used when the offset is determined at run time + std::variant const offset; + std::string offsetString() const + { + if (std::holds_alternative(offset)) + return std::to_string(std::get(offset)); + else + return std::get(offset); + } + }; + struct Memory + { + std::string const address; + bool byteArrayElement = false; + }; + struct Tuple + { + std::vector> components; + }; + std::variant kind; }; -} +} \ No newline at end of file diff --git a/libsolidity/codegen/ir/IRVariable.cpp b/libsolidity/codegen/ir/IRVariable.cpp new file mode 100644 index 000000000000..dac1087f0080 --- /dev/null +++ b/libsolidity/codegen/ir/IRVariable.cpp @@ -0,0 +1,111 @@ +/* + 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 . +*/ +#include +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::frontend; +using namespace solidity::util; + +IRVariable::IRVariable(std::string _baseName, Type const& _type): + m_baseName(std::move(_baseName)), m_type(_type) +{ +} + +IRVariable::IRVariable(VariableDeclaration const& _declaration): + IRVariable( + "vloc_" + _declaration.name() + '_' + std::to_string(_declaration.id()), + *_declaration.annotation().type + ) +{ + solAssert(!_declaration.isStateVariable(), ""); +} + +IRVariable::IRVariable(Expression const& _expression): + IRVariable( + "expr_" + to_string(_expression.id()), + *_expression.annotation().type + ) +{ +} + +IRVariable IRVariable::part(string const& _name) const +{ + for (auto const& [itemName, itemType]: m_type.stackItems()) + if (itemName == _name) + { + solAssert(itemName.empty() || itemType, ""); + return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type}; + } + solAssert(false, "Invalid stack item name."); +} + +vector IRVariable::stackSlots() const +{ + vector result; + for (auto const& [itemName, itemType]: m_type.stackItems()) + if (itemType) + { + solAssert(!itemName.empty(), ""); + solAssert(m_type != *itemType, ""); + result += IRVariable{suffixedName(itemName), *itemType}.stackSlots(); + } + else + { + solAssert(itemName.empty(), ""); + result.emplace_back(m_baseName); + } + return result; +} + +string IRVariable::commaSeparatedList() const +{ + return joinHumanReadable(stackSlots()); +} + +string IRVariable::commaSeparatedListPrefixed() const +{ + return joinHumanReadablePrefixed(stackSlots()); +} + +string IRVariable::name() const +{ + solAssert(m_type.sizeOnStack() == 1, ""); + auto const& [itemName, type] = m_type.stackItems().front(); + solAssert(!type, ""); + return suffixedName(itemName); +} + +IRVariable IRVariable::tupleComponent(size_t _i) const +{ + solAssert( + m_type.category() == Type::Category::Tuple, + "Requested tuple component of non-tuple IR variable." + ); + return part("component_" + std::to_string(_i + 1)); +} + +string IRVariable::suffixedName(string const& _suffix) const +{ + if (_suffix.empty()) + return m_baseName; + else + return m_baseName + '_' + _suffix; +} diff --git a/libsolidity/codegen/ir/IRVariable.h b/libsolidity/codegen/ir/IRVariable.h new file mode 100644 index 000000000000..8967f55fa8e8 --- /dev/null +++ b/libsolidity/codegen/ir/IRVariable.h @@ -0,0 +1,85 @@ +/* + 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 . +*/ +#pragma once + +#include +#include +#include + +namespace solidity::frontend +{ + +class VariableDeclaration; +class Type; +class Expression; + +/** + * An IRVariable refers to a set of yul variables that correspond to the stack layout of a Solidity variable or expression + * of a specific S + * olidity type. If the Solidity type occupies a single stack slot, the IRVariable refers to a single yul variable. + * Otherwise the set of yul variables it refers to is (recursively) determined by @see ``Type::stackItems()``. + * For example, an IRVariable referring to a dynamically sized calldata array will consist of two parts named + * ``offset`` and ``length``, whereas an IRVariable referring to a statically sized calldata type, a storage reference + * type or a memory reference type will contain a single unnamed part containing an offset. An IRVariable referring to + * a value type will contain a single unnamed part containing the value, an IRVariable referring to a tuple will + * have the typed tuple components as parts. + */ +class IRVariable +{ +public: + /// IR variable with explicit base name @a _baseName and type @a _type. + IRVariable(std::string _baseName, Type const& _type); + /// IR variable referring to the declaration @a _decl. + explicit IRVariable(VariableDeclaration const& _decl); + /// IR variable referring to the expression @a _expr. + /// Intentionally not defined as explicit to allow defining IRVariables for expressions directly via implicit conversions. + IRVariable(Expression const& _expression); + + /// @returns the name of the variable, if it occupies a single stack slot (otherwise throws). + std::string name() const; + + /// @returns a comma-separated list of the stack slots of the variable. + std::string commaSeparatedList() const; + + /// @returns a comma-separated list of the stack slots of the variable that is + /// prefixed with a comma, unless it is empty. + std::string commaSeparatedListPrefixed() const; + + /// @returns an IRVariable referring to the tuple component @a _i of a tuple variable. + IRVariable tupleComponent(std::size_t _i) const; + + /// @returns the type of the variable. + Type const& type() const { return m_type; } + + /// @returns an IRVariable referring to the stack component @a _slot of the variable. + /// @a _slot must be among the stack slots in ``m_type.stackItems()``. + /// The returned IRVariable is itself typed with the type of the stack slot as defined + /// in ``m_type.stackItems()`` and may again occupy multiple stack slots. + IRVariable part(std::string const& _slot) const; +private: + /// @returns a vector containing the names of the stack slots of the variable. + std::vector stackSlots() const; + + /// @returns a name consisting of the base name appended with an underscore and @æ _suffix, + /// unless @a _suffix is empty, in which case the base name itself is returned. + std::string suffixedName(std::string const& _suffix) const; + std::string m_baseName; + Type const& m_type; +}; + + +} diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index d0d92473c2b0..fc88acf50039 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -34,8 +34,8 @@ object \"C_10\" { } - function fun_f_9() -> vloc__4 { - vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() + function fun_f_9() -> vloc__4_mpos { + vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave } @@ -130,8 +130,8 @@ object \"C_10\" { } } - function fun_f_9() -> vloc__4 { - vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() + function fun_f_9() -> vloc__4_mpos { + vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave } diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 466a857b5006..4302701bdcd3 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -38,8 +38,8 @@ object \"C_10\" { } - function fun_f_9() -> vloc__4 { - vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() + function fun_f_9() -> vloc__4_mpos { + vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave } @@ -138,8 +138,8 @@ object \"C_10\" { } } - function fun_f_9() -> vloc__4 { - vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() + function fun_f_9() -> vloc__4_mpos { + vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave } diff --git a/test/libsolidity/semanticTests/viaYul/assign_tuple_from_function_call.sol b/test/libsolidity/semanticTests/viaYul/assign_tuple_from_function_call.sol new file mode 100644 index 000000000000..103c1bf1b3b6 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/assign_tuple_from_function_call.sol @@ -0,0 +1,16 @@ +contract C { + function f() public pure returns (uint, uint, uint) { + return (1, 2, 3); + } + function g() public pure returns (uint a, uint b, uint c) { + (c, b, a) = f(); + } + function h() public pure returns (uint a) { + (,,a) = f(); + } +} +// ==== +// compileViaYul: also +// ---- +// g() -> 3, 2, 1 +// h() -> 3 diff --git a/test/libsolidity/semanticTests/viaYul/define_tuple_from_function_call.sol b/test/libsolidity/semanticTests/viaYul/define_tuple_from_function_call.sol new file mode 100644 index 000000000000..b49f21cd436d --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/define_tuple_from_function_call.sol @@ -0,0 +1,18 @@ +contract C { + function f() public pure returns (uint, uint, uint) { + return (1, 2, 3); + } + function g() public pure returns (uint x, uint y, uint z) { + (uint c, uint b, uint a) = f(); + (x, y, z) = (a, b, c); + } + function h() public pure returns (uint) { + (,,uint a) = f(); + return a; + } +} +// ==== +// compileViaYul: also +// ---- +// g() -> 3, 2, 1 +// h() -> 3 diff --git a/test/libsolidity/semanticTests/viaYul/local_tuple_assignment.sol b/test/libsolidity/semanticTests/viaYul/local_tuple_assignment.sol new file mode 100644 index 000000000000..2fb33f08af11 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/local_tuple_assignment.sol @@ -0,0 +1,32 @@ +contract C { + uint public x = 17; + function f(uint a1, uint a2) public returns (uint r1, uint r2) { + (uint b1, uint b2) = (a1, a2); + (r1, x, r2) = (b1, b2, b2); + } + function g() public returns (uint a, uint b, uint c) { + uint256[3] memory m; + (m[0], m[1], m[2]) = (1, x, 3); + return (m[2], m[1], m[0]); + } + function h() public returns (uint a, uint b, uint c) { + uint256[3] memory m; + (m[0], m[1], , m[2], m[0]) = (1, x, 3, 4, 42); + return (m[2], m[1], m[0]); + } + function i() public returns (uint a, uint b, uint c, uint d) { + (a) = 42; + (((((b))))) = 23; + c = (((17))); + (((d))) = (13); + } +} +// ==== +// compileViaYul: also +// ---- +// x() -> 17 +// f(uint256,uint256): 23, 42 -> 23, 42 +// x() -> 42 +// g() -> 3, 42, 1 +// h() -> 4, 42, 1 +// i() -> 42, 23, 17, 13 diff --git a/test/libsolidity/semanticTests/viaYul/local_variable_without_init.sol b/test/libsolidity/semanticTests/viaYul/local_variable_without_init.sol new file mode 100644 index 000000000000..2788e6370c60 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/local_variable_without_init.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure returns (uint) { + uint x; + return x; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/tuple_evaluation_order.sol b/test/libsolidity/semanticTests/viaYul/tuple_evaluation_order.sol new file mode 100644 index 000000000000..304c8aa3b724 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/tuple_evaluation_order.sol @@ -0,0 +1,14 @@ +contract C { + uint256 x; + uint256 y; + function set(uint256 v) public returns (uint256) { x = v; return v; } + function f() public returns (uint256, uint256) { + (y, y, y) = (set(1), set(2), set(3)); + assert(y == 1 && x == 3); + return (x, y); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 3, 1 From df0873d1385fb7b7f47cf313b43dfd066cc6726b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 11 Feb 2020 17:43:43 +0100 Subject: [PATCH 117/160] Implement calldata arrays for Yul IR --- libsolidity/codegen/YulUtilFunctions.cpp | 54 ++++++++++++++++++- libsolidity/codegen/YulUtilFunctions.h | 7 ++- .../codegen/ir/IRGeneratorForStatements.cpp | 38 +++++++++++-- .../viaYul/calldata_array_access.sol | 20 +++++++ .../viaYul/calldata_array_length.sol | 34 ++++++++++++ .../calldata_array_three_dimensional.sol | 20 +++++++ .../viaYul/calldata_bytes_array_bounds.sol | 12 +++++ 7 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 test/libsolidity/semanticTests/viaYul/calldata_array_access.sol create mode 100644 test/libsolidity/semanticTests/viaYul/calldata_array_length.sol create mode 100644 test/libsolidity/semanticTests/viaYul/calldata_array_three_dimensional.sol create mode 100644 test/libsolidity/semanticTests/viaYul/calldata_bytes_array_bounds.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index f696c265cc17..0472dd588aba 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -908,6 +908,58 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type) }); } +string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type) +{ + solAssert(_type.dataStoredIn(DataLocation::CallData), ""); + string functionName = "calldata_array_index_access_" + _type.identifier(); + return m_functionCollector->createFunction(functionName, [&]() { + return Whiskers(R"( + function (base_ref, length, index) -> addr, len { + if iszero(lt(index, length)) { invalid() } + addr := add(base_ref, mul(index, )) + + addr, len := (base_ref, addr) + + } + )") + ("functionName", functionName) + ("stride", to_string(_type.calldataStride())) + ("dynamicallySized", _type.isDynamicallySized()) + ("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded()) + ("dynamicallySizedBase", _type.baseType()->isDynamicallySized()) + ("arrayLen", toCompactHexWithPrefix(_type.length())) + ("accessCalldataTail", _type.baseType()->isDynamicallyEncoded() ? accessCalldataTailFunction(*_type.baseType()): "") + .render(); + }); +} + +string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) +{ + solAssert(_type.isDynamicallyEncoded(), ""); + solAssert(_type.dataStoredIn(DataLocation::CallData), ""); + string functionName = "access_calldata_tail_" + _type.identifier(); + return m_functionCollector->createFunction(functionName, [&]() { + return Whiskers(R"( + function (base_ref, ptr_to_tail) -> addr, length { + let rel_offset_of_tail := calldataload(ptr_to_tail) + if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { revert(0, 0) } + addr := add(base_ref, rel_offset_of_tail) + + length := calldataload(addr) + if gt(length, 0xffffffffffffffff) { revert(0, 0) } + if sgt(base_ref, sub(calldatasize(), mul(length, ))) { revert(0, 0) } + addr := add(addr, 32) + + } + )") + ("functionName", functionName) + ("dynamicallySized", _type.isDynamicallySized()) + ("neededLength", toCompactHexWithPrefix(_type.calldataEncodedTailSize())) + ("calldataStride", toCompactHexWithPrefix(_type.isDynamicallySized() ? dynamic_cast(_type).calldataStride() : 0)) + .render(); + }); +} + string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) { solAssert(!_type.isByteArray(), ""); @@ -1932,7 +1984,7 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC function (memPtr) -> value { value := (memPtr) - value := (value) + (value) } )") diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 859290ffabe3..8c92d7b4f1ba 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -172,9 +172,14 @@ class YulUtilFunctions /// @returns the name of a function that returns the calldata address for the /// given array base ref and index. - /// signature: (baseRef, index) -> address + /// signature: (baseRef, index) -> offset[, length] std::string calldataArrayIndexAccessFunction(ArrayType const& _type); + /// @returns the name of a function that follows a calldata tail while performing + /// bounds checks. + /// signature: (baseRef, tailPointer) -> offset[, length] + std::string accessCalldataTailFunction(Type const& _type); + /// @returns the name of a function that advances an array data pointer to the next element. /// Only works for memory arrays, calldata arrays and storage arrays that every item occupies one or multiple full slots. std::string nextArrayElementFunction(ArrayType const& _type); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 3ed5abad0393..09a3661605e1 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -815,7 +815,12 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) else if (member == "gasprice") define(_memberAccess) << "gasprice()\n"; else if (member == "data") - solUnimplementedAssert(false, ""); + { + IRVariable var(_memberAccess); + declare(var); + define(var.part("offset")) << "0\n"; + define(var.part("length")) << "calldatasize()\n"; + } else if (member == "sig") define(_memberAccess) << "and(calldataload(0), " << @@ -862,8 +867,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) switch (type.location()) { case DataLocation::CallData: - solUnimplementedAssert(false, ""); - //m_context << Instruction::SWAP1 << Instruction::POP; + define(_memberAccess, IRVariable(_memberAccess.expression()).part("length")); break; case DataLocation::Storage: { @@ -997,8 +1001,29 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) } case DataLocation::CallData: { - solUnimplemented("calldata not yet implemented!"); - + IRVariable var(m_context.newYulVariable(), *arrayType.baseType()); + define(var) << + m_utils.calldataArrayIndexAccessFunction(arrayType) << + "(" << + IRVariable(_indexAccess.baseExpression()).commaSeparatedList() << + ", " << + expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) << + ")\n"; + if (arrayType.isByteArray()) + define(_indexAccess) << + m_utils.cleanupFunction(*arrayType.baseType()) << + "(calldataload(" << + var.name() << + "))\n"; + else if (arrayType.baseType()->isValueType()) + define(_indexAccess) << + m_utils.readFromCalldata(*arrayType.baseType()) << + "(" << + var.commaSeparatedList() << + ")\n"; + else + define(_indexAccess, var); + break; } } } @@ -1534,7 +1559,10 @@ void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue solAssert(!m_currentLValue, ""); if (_expression.annotation().lValueRequested) + { m_currentLValue.emplace(std::move(_lvalue)); + solAssert(!_lvalue.type.dataStoredIn(DataLocation::CallData), ""); + } else // Only define the expression, if it will not be written to. define(_expression, readFromLValue(_lvalue)); diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_access.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_access.sol new file mode 100644 index 000000000000..115015f03446 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_access.sol @@ -0,0 +1,20 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[] calldata x, uint256 i) external returns (uint256) { + return x[i]; + } + function f(uint256[][] calldata x, uint256 i, uint256 j) external returns (uint256) { + return x[i][j]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[],uint256): 0x40, 0, 0 -> FAILURE +// f(uint256[],uint256): 0x40, 0, 1, 23 -> 23 +// f(uint256[],uint256): 0x40, 1, 1, 23 -> FAILURE +// f(uint256[],uint256): 0x40, 0, 2, 23, 42 -> 23 +// f(uint256[],uint256): 0x40, 1, 2, 23, 42 -> 42 +// f(uint256[],uint256): 0x40, 2, 2, 23, 42 -> FAILURE +// f(uint256[][],uint256,uint256): 0x60, 0, 0 -> FAILURE +// f(uint256[][],uint256,uint256): 0x60, 0, 0, 1, 0x20, 1, 23 -> 23 diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_length.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_length.sol new file mode 100644 index 000000000000..507380667e99 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_length.sol @@ -0,0 +1,34 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[] calldata x) external returns (uint256) { + return x.length; + } + function f(uint256[][] calldata x) external returns (uint256 l1, uint256 l2, uint256 l3) { + l1 = x.length; + if (l1 > 0) l2 = x[0].length; + if (l1 > 1) l3 = x[1].length; + } + function f(uint256[2] calldata x) external returns (uint256) { + return x.length; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[]): 0x20, 0 -> 0 +// f(uint256[]): 0x20, 1, 23 -> 1 +// f(uint256[]): 0x20, 2, 23, 42 -> 2 +// f(uint256[]): 0x20, 3, 23, 42, 17 -> 3 +// f(uint256[2]): 23, 42 -> 2 +// f(uint256[][]): 0x20, 0 -> 0, 0, 0 +// f(uint256[][]): 0x20, 1, 0x20, 0 -> 1, 0, 0 +// f(uint256[][]): 0x20, 1, 0x00 -> 1, 0, 0 +// f(uint256[][]): 0x20, 1, 0x20, 1, 23 -> 1, 1, 0 +// f(uint256[][]): 0x20, 1, 0x20, 2, 23, 42 -> 1, 2, 0 +// f(uint256[][]): 0x20, 1, 0x40, 0, 2, 23, 42 -> 1, 2, 0 +// f(uint256[][]): 0x20, 1, -32 -> 1, 1, 0 +// f(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 2, 2 +// f(uint256[][]): 0x20, 2, 0x40, 0xa0, 2, 23, 42, 0 -> 2, 2, 0 +// f(uint256[][]): 0x20, 2, 0xA0, 0x40, 2, 23, 42, 0 -> 2, 0, 2 +// f(uint256[][]): 0x20, 2, 0x40, 0xA0, 2, 23, 42, 1, 17 -> 2, 2, 1 +// f(uint256[][]): 0x20, 2, 0x40, 0xA0, 2, 23, 42, 2, 17, 13 -> 2, 2, 2 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_three_dimensional.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_three_dimensional.sol new file mode 100644 index 000000000000..6742b995a5e7 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_three_dimensional.sol @@ -0,0 +1,20 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint256[][2][] calldata x, uint256 i, uint256 j, uint256 k) external returns (uint256 a, uint256 b, uint256 c, uint256 d) { + a = x.length; + b = x[i].length; + c = x[i][j].length; + d = x[i][j][k]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 0, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> 1, 2, 1, 42 +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 1, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> 1, 2, 1, 23 +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 1, 0, 1, 0x20, 0x40, 0x80, 1, 42, 2, 23, 17 -> 1, 2, 2, 23 +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 1, 1, 1, 0x20, 0x40, 0x80, 1, 42, 2, 23, 17 -> 1, 2, 2, 17 +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 1, 0, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 2, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 2, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/calldata_bytes_array_bounds.sol b/test/libsolidity/semanticTests/viaYul/calldata_bytes_array_bounds.sol new file mode 100644 index 000000000000..f9eebf14e674 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/calldata_bytes_array_bounds.sol @@ -0,0 +1,12 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(bytes[] calldata a, uint256 i) external returns (uint) { + return uint8(a[0][i]); + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes[],uint256): 0x40, 0, 1, 0x20, 2, hex"6162" -> 0x61 +// f(bytes[],uint256): 0x40, 1, 1, 0x20, 2, hex"6162" -> 0x62 +// f(bytes[],uint256): 0x40, 2, 1, 0x20, 2, hex"6162" -> FAILURE From ba576bc6c3b6176de600c4e263b072cd37d4b6b0 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Feb 2020 22:04:25 -0300 Subject: [PATCH 118/160] Fix new namespaces --- libsolidity/formal/ModelChecker.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index f22ca59b887c..ac19ba2706c9 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -46,7 +46,7 @@ class ModelChecker /// should be used, even if all are available. The default choice is to use all. ModelChecker( langutil::ErrorReporter& _errorReporter, - std::map const& _smtlib2Responses, + std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback = ReadCallback::Callback(), smt::SMTSolverChoice _enabledSolvers = smt::SMTSolverChoice::All() ); From 6451a4d2a0bc55d57e5123bf8a0afee18aba7760 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Feb 2020 22:09:45 -0300 Subject: [PATCH 119/160] Move VerificationTarget and add BMCVerificationTarget --- libsolidity/formal/BMC.cpp | 24 +++++++++++++----------- libsolidity/formal/BMC.h | 21 +++++++++------------ libsolidity/formal/SMTEncoder.h | 7 +++++++ 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index d56c45e3c593..8fb5bfd3007a 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -575,7 +575,7 @@ void BMC::checkVerificationTargets(smt::Expression const& _constraints) checkVerificationTarget(target, _constraints); } -void BMC::checkVerificationTarget(VerificationTarget& _target, smt::Expression const& _constraints) +void BMC::checkVerificationTarget(BMCVerificationTarget& _target, smt::Expression const& _constraints) { switch (_target.type) { @@ -606,7 +606,7 @@ void BMC::checkVerificationTarget(VerificationTarget& _target, smt::Expression c } } -void BMC::checkConstantCondition(VerificationTarget& _target) +void BMC::checkConstantCondition(BMCVerificationTarget& _target) { checkBooleanNotConstant( *_target.expression, @@ -617,7 +617,7 @@ void BMC::checkConstantCondition(VerificationTarget& _target) ); } -void BMC::checkUnderflow(VerificationTarget& _target, smt::Expression const& _constraints) +void BMC::checkUnderflow(BMCVerificationTarget& _target, smt::Expression const& _constraints) { solAssert( _target.type == VerificationTarget::Type::Underflow || @@ -637,7 +637,7 @@ void BMC::checkUnderflow(VerificationTarget& _target, smt::Expression const& _co ); } -void BMC::checkOverflow(VerificationTarget& _target, smt::Expression const& _constraints) +void BMC::checkOverflow(BMCVerificationTarget& _target, smt::Expression const& _constraints) { solAssert( _target.type == VerificationTarget::Type::Overflow || @@ -657,7 +657,7 @@ void BMC::checkOverflow(VerificationTarget& _target, smt::Expression const& _con ); } -void BMC::checkDivByZero(VerificationTarget& _target) +void BMC::checkDivByZero(BMCVerificationTarget& _target) { solAssert(_target.type == VerificationTarget::Type::DivByZero, ""); checkCondition( @@ -671,7 +671,7 @@ void BMC::checkDivByZero(VerificationTarget& _target) ); } -void BMC::checkBalance(VerificationTarget& _target) +void BMC::checkBalance(BMCVerificationTarget& _target) { solAssert(_target.type == VerificationTarget::Type::Balance, ""); checkCondition( @@ -684,7 +684,7 @@ void BMC::checkBalance(VerificationTarget& _target) ); } -void BMC::checkAssert(VerificationTarget& _target) +void BMC::checkAssert(BMCVerificationTarget& _target) { solAssert(_target.type == VerificationTarget::Type::Assert, ""); if (!m_safeAssertions.count(_target.expression)) @@ -703,10 +703,12 @@ void BMC::addVerificationTarget( Expression const* _expression ) { - VerificationTarget target{ - _type, - _value, - currentPathConditions() && m_context.assertions(), + BMCVerificationTarget target{ + { + _type, + _value, + currentPathConditions() && m_context.assertions() + }, _expression, m_callStack, modelExpressions() diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index 387e84d4a53b..f4b427edeb24 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -117,24 +117,21 @@ class BMC: public SMTEncoder /// Verification targets. //@{ - struct VerificationTarget + struct BMCVerificationTarget: VerificationTarget { - enum class Type { ConstantCondition, Underflow, Overflow, UnderOverflow, DivByZero, Balance, Assert } type; - smt::Expression value; - smt::Expression constraints; Expression const* expression; std::vector callStack; std::pair, std::vector> modelExpressions; }; void checkVerificationTargets(smt::Expression const& _constraints); - void checkVerificationTarget(VerificationTarget& _target, smt::Expression const& _constraints = smt::Expression(true)); - void checkConstantCondition(VerificationTarget& _target); - void checkUnderflow(VerificationTarget& _target, smt::Expression const& _constraints); - void checkOverflow(VerificationTarget& _target, smt::Expression const& _constraints); - void checkDivByZero(VerificationTarget& _target); - void checkBalance(VerificationTarget& _target); - void checkAssert(VerificationTarget& _target); + void checkVerificationTarget(BMCVerificationTarget& _target, smt::Expression const& _constraints = smt::Expression(true)); + void checkConstantCondition(BMCVerificationTarget& _target); + void checkUnderflow(BMCVerificationTarget& _target, smt::Expression const& _constraints); + void checkOverflow(BMCVerificationTarget& _target, smt::Expression const& _constraints); + void checkDivByZero(BMCVerificationTarget& _target); + void checkBalance(BMCVerificationTarget& _target); + void checkAssert(BMCVerificationTarget& _target); void addVerificationTarget( VerificationTarget::Type _type, smt::Expression const& _value, @@ -179,7 +176,7 @@ class BMC: public SMTEncoder /// ErrorReporter that comes from CompilerStack. langutil::ErrorReporter& m_outerErrorReporter; - std::vector m_verificationTargets; + std::vector m_verificationTargets; /// Assertions that are known to be safe. std::set m_safeAssertions; diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index eddab0eb1963..3dbcaba6d33b 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -233,6 +233,13 @@ class SMTEncoder: public ASTConstVisitor /// @returns a note to be added to warnings. std::string extraComment(); + struct VerificationTarget + { + enum class Type { ConstantCondition, Underflow, Overflow, UnderOverflow, DivByZero, Balance, Assert } type; + smt::Expression value; + smt::Expression constraints; + }; + smt::VariableUsage m_variableUsage; bool m_arrayAssignmentHappened = false; // True if the "No SMT solver available" warning was already created. From 34d64761d9067dce7b4d17968fa196ceb4f5c409 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Feb 2020 22:11:28 -0300 Subject: [PATCH 120/160] Extract symbolicArguments function --- libsolidity/formal/BMC.cpp | 21 +-------------------- libsolidity/formal/SMTEncoder.cpp | 28 ++++++++++++++++++++++++++++ libsolidity/formal/SMTEncoder.h | 5 +++++ 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 8fb5bfd3007a..dd1e5136cd40 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -441,26 +441,7 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) } else { - vector funArgs; - Expression const* calledExpr = &_funCall.expression(); - auto const& funType = dynamic_cast(calledExpr->annotation().type); - solAssert(funType, ""); - - auto const& functionParams = funDef->parameters(); - auto const& arguments = _funCall.arguments(); - unsigned firstParam = 0; - if (funType->bound()) - { - auto const& boundFunction = dynamic_cast(calledExpr); - solAssert(boundFunction, ""); - funArgs.push_back(expr(boundFunction->expression(), functionParams.front()->type())); - firstParam = 1; - } - - solAssert((arguments.size() + firstParam) == functionParams.size(), ""); - for (unsigned i = 0; i < arguments.size(); ++i) - funArgs.push_back(expr(*arguments.at(i), functionParams.at(i + firstParam)->type())); - initializeFunctionCallParameters(*funDef, funArgs); + initializeFunctionCallParameters(*funDef, symbolicArguments(_funCall)); // The reason why we need to pushCallStack here instead of visit(FunctionDefinition) // is that there we don't have `_funCall`. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 2c86eae0b396..ac81a5d878ca 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1682,3 +1682,31 @@ void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall) else if (returnParams.size() == 1) defineExpr(_funCall, currentValue(*returnParams.front())); } + +vector SMTEncoder::symbolicArguments(FunctionCall const& _funCall) +{ + auto const* function = functionCallToDefinition(_funCall); + solAssert(function, ""); + + vector args; + Expression const* calledExpr = &_funCall.expression(); + auto const& funType = dynamic_cast(calledExpr->annotation().type); + solAssert(funType, ""); + + auto const& functionParams = function->parameters(); + auto const& arguments = _funCall.arguments(); + unsigned firstParam = 0; + if (funType->bound()) + { + auto const& boundFunction = dynamic_cast(calledExpr); + solAssert(boundFunction, ""); + args.push_back(expr(boundFunction->expression(), functionParams.front()->type())); + firstParam = 1; + } + + solAssert((arguments.size() + firstParam) == functionParams.size(), ""); + for (unsigned i = 0; i < arguments.size(); ++i) + args.push_back(expr(*arguments.at(i), functionParams.at(i + firstParam)->type())); + + return args; +} diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 3dbcaba6d33b..b0a67257427e 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -230,6 +230,11 @@ class SMTEncoder: public ASTConstVisitor /// and set them as the components of the symbolic tuple. void createReturnedExpressions(FunctionCall const& _funCall); + /// @returns the symbolic arguments for a function call, + /// taking into account bound functions and + /// type conversion. + std::vector symbolicArguments(FunctionCall const& _funCall); + /// @returns a note to be added to warnings. std::string extraComment(); From d31a2a8d2145f34cfd1fc56ba2bcd26f158be7c6 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Feb 2020 22:12:42 -0300 Subject: [PATCH 121/160] CHC clears indices so that initial is 0 and current is 1 --- libsolidity/formal/CHC.cpp | 17 +++++++++++++++++ libsolidity/formal/CHC.h | 1 + libsolidity/formal/SMTEncoder.h | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index cfe3efb8b9ff..25201474b6d2 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -505,6 +505,23 @@ void CHC::eraseKnowledge() m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.hasReferenceOrMappingType(); }); } +void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function) +{ + SMTEncoder::clearIndices(_contract, _function); + for (auto const* var: m_stateVariables) + /// SSA index 0 is reserved for state variables at the beginning + /// of the current transaction. + m_context.variable(*var)->increaseIndex(); + if (_function) + { + for (auto const& var: _function->parameters() + _function->returnParameters()) + m_context.variable(*var)->increaseIndex(); + for (auto const& var: _function->localVariables()) + m_context.variable(*var)->increaseIndex(); + } +} + + bool CHC::shouldVisit(ContractDefinition const& _contract) const { if ( diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index a732d2ff2a8f..6f912fbd87b3 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -83,6 +83,7 @@ class CHC: public SMTEncoder //@{ void reset(); void eraseKnowledge(); + void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override; bool shouldVisit(ContractDefinition const& _contract) const; bool shouldVisit(FunctionDefinition const& _function) const; void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector const* _arguments = nullptr); diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index b0a67257427e..21bf094c06b4 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -217,7 +217,7 @@ class SMTEncoder: public ASTConstVisitor /// Resets the variable indices. void resetVariableIndices(VariableIndices const& _indices); /// Used when starting a new block. - void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr); + virtual void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr); /// @returns variables that are touched in _node's subtree. From d0eeca80143eeb8b48864d3f842d3d083976d540 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 3 Feb 2020 19:01:23 -0500 Subject: [PATCH 122/160] Fix CompilerStack::loadMissingSources() - FatalError Exception was not caught - fixes #8102 --- libsolidity/interface/CompilerStack.cpp | 61 +- .../more_than_256_importerrors.sol | 521 ++++++++++++++++++ 2 files changed, 555 insertions(+), 27 deletions(-) create mode 100644 test/libsolidity/syntaxTests/more_than_256_importerrors.sol diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 6940730cfe12..572b3bbbff94 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -906,35 +906,42 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string { solAssert(m_stackState < ParsingPerformed, ""); StringMap newSources; - for (auto const& node: _ast.nodes()) - if (ImportDirective const* import = dynamic_cast(node.get())) - { - solAssert(!import->path().empty(), "Import path cannot be empty."); - - string importPath = util::absolutePath(import->path(), _sourcePath); - // The current value of `path` is the absolute path as seen from this source file. - // We first have to apply remappings before we can store the actual absolute path - // as seen globally. - importPath = applyRemapping(importPath, _sourcePath); - import->annotation().absolutePath = importPath; - if (m_sources.count(importPath) || newSources.count(importPath)) - continue; - - ReadCallback::Result result{false, string("File not supplied initially.")}; - if (m_readFile) - result = m_readFile(ReadCallback::kindString(ReadCallback::Kind::ReadFile), importPath); - - if (result.success) - newSources[importPath] = result.responseOrErrorMessage; - else + try + { + for (auto const& node: _ast.nodes()) + if (ImportDirective const* import = dynamic_cast(node.get())) { - m_errorReporter.parserError( - import->location(), - string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage) - ); - continue; + solAssert(!import->path().empty(), "Import path cannot be empty."); + + string importPath = util::absolutePath(import->path(), _sourcePath); + // The current value of `path` is the absolute path as seen from this source file. + // We first have to apply remappings before we can store the actual absolute path + // as seen globally. + importPath = applyRemapping(importPath, _sourcePath); + import->annotation().absolutePath = importPath; + if (m_sources.count(importPath) || newSources.count(importPath)) + continue; + + ReadCallback::Result result{false, string("File not supplied initially.")}; + if (m_readFile) + result = m_readFile(ReadCallback::kindString(ReadCallback::Kind::ReadFile), importPath); + + if (result.success) + newSources[importPath] = result.responseOrErrorMessage; + else + { + m_errorReporter.parserError( + import->location(), + string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage) + ); + continue; + } } - } + } + catch (FatalError const&) + { + solAssert(m_errorReporter.hasErrors(), ""); + } return newSources; } diff --git a/test/libsolidity/syntaxTests/more_than_256_importerrors.sol b/test/libsolidity/syntaxTests/more_than_256_importerrors.sol new file mode 100644 index 000000000000..ed6e085f622e --- /dev/null +++ b/test/libsolidity/syntaxTests/more_than_256_importerrors.sol @@ -0,0 +1,521 @@ +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; +import "a.sol"; + +contract C { + function f() public { + } +} +// ---- +// ParserError: (0-15): Source "a.sol" not found: File not supplied initially. +// ParserError: (16-31): Source "a.sol" not found: File not supplied initially. +// ParserError: (32-47): Source "a.sol" not found: File not supplied initially. +// ParserError: (48-63): Source "a.sol" not found: File not supplied initially. +// ParserError: (64-79): Source "a.sol" not found: File not supplied initially. +// ParserError: (80-95): Source "a.sol" not found: File not supplied initially. +// ParserError: (96-111): Source "a.sol" not found: File not supplied initially. +// ParserError: (112-127): Source "a.sol" not found: File not supplied initially. +// ParserError: (128-143): Source "a.sol" not found: File not supplied initially. +// ParserError: (144-159): Source "a.sol" not found: File not supplied initially. +// ParserError: (160-175): Source "a.sol" not found: File not supplied initially. +// ParserError: (176-191): Source "a.sol" not found: File not supplied initially. +// ParserError: (192-207): Source "a.sol" not found: File not supplied initially. +// ParserError: (208-223): Source "a.sol" not found: File not supplied initially. +// ParserError: (224-239): Source "a.sol" not found: File not supplied initially. +// ParserError: (240-255): Source "a.sol" not found: File not supplied initially. +// ParserError: (256-271): Source "a.sol" not found: File not supplied initially. +// ParserError: (272-287): Source "a.sol" not found: File not supplied initially. +// ParserError: (288-303): Source "a.sol" not found: File not supplied initially. +// ParserError: (304-319): Source "a.sol" not found: File not supplied initially. +// ParserError: (320-335): Source "a.sol" not found: File not supplied initially. +// ParserError: (336-351): Source "a.sol" not found: File not supplied initially. +// ParserError: (352-367): Source "a.sol" not found: File not supplied initially. +// ParserError: (368-383): Source "a.sol" not found: File not supplied initially. +// ParserError: (384-399): Source "a.sol" not found: File not supplied initially. +// ParserError: (400-415): Source "a.sol" not found: File not supplied initially. +// ParserError: (416-431): Source "a.sol" not found: File not supplied initially. +// ParserError: (432-447): Source "a.sol" not found: File not supplied initially. +// ParserError: (448-463): Source "a.sol" not found: File not supplied initially. +// ParserError: (464-479): Source "a.sol" not found: File not supplied initially. +// ParserError: (480-495): Source "a.sol" not found: File not supplied initially. +// ParserError: (496-511): Source "a.sol" not found: File not supplied initially. +// ParserError: (512-527): Source "a.sol" not found: File not supplied initially. +// ParserError: (528-543): Source "a.sol" not found: File not supplied initially. +// ParserError: (544-559): Source "a.sol" not found: File not supplied initially. +// ParserError: (560-575): Source "a.sol" not found: File not supplied initially. +// ParserError: (576-591): Source "a.sol" not found: File not supplied initially. +// ParserError: (592-607): Source "a.sol" not found: File not supplied initially. +// ParserError: (608-623): Source "a.sol" not found: File not supplied initially. +// ParserError: (624-639): Source "a.sol" not found: File not supplied initially. +// ParserError: (640-655): Source "a.sol" not found: File not supplied initially. +// ParserError: (656-671): Source "a.sol" not found: File not supplied initially. +// ParserError: (672-687): Source "a.sol" not found: File not supplied initially. +// ParserError: (688-703): Source "a.sol" not found: File not supplied initially. +// ParserError: (704-719): Source "a.sol" not found: File not supplied initially. +// ParserError: (720-735): Source "a.sol" not found: File not supplied initially. +// ParserError: (736-751): Source "a.sol" not found: File not supplied initially. +// ParserError: (752-767): Source "a.sol" not found: File not supplied initially. +// ParserError: (768-783): Source "a.sol" not found: File not supplied initially. +// ParserError: (784-799): Source "a.sol" not found: File not supplied initially. +// ParserError: (800-815): Source "a.sol" not found: File not supplied initially. +// ParserError: (816-831): Source "a.sol" not found: File not supplied initially. +// ParserError: (832-847): Source "a.sol" not found: File not supplied initially. +// ParserError: (848-863): Source "a.sol" not found: File not supplied initially. +// ParserError: (864-879): Source "a.sol" not found: File not supplied initially. +// ParserError: (880-895): Source "a.sol" not found: File not supplied initially. +// ParserError: (896-911): Source "a.sol" not found: File not supplied initially. +// ParserError: (912-927): Source "a.sol" not found: File not supplied initially. +// ParserError: (928-943): Source "a.sol" not found: File not supplied initially. +// ParserError: (944-959): Source "a.sol" not found: File not supplied initially. +// ParserError: (960-975): Source "a.sol" not found: File not supplied initially. +// ParserError: (976-991): Source "a.sol" not found: File not supplied initially. +// ParserError: (992-1007): Source "a.sol" not found: File not supplied initially. +// ParserError: (1008-1023): Source "a.sol" not found: File not supplied initially. +// ParserError: (1024-1039): Source "a.sol" not found: File not supplied initially. +// ParserError: (1040-1055): Source "a.sol" not found: File not supplied initially. +// ParserError: (1056-1071): Source "a.sol" not found: File not supplied initially. +// ParserError: (1072-1087): Source "a.sol" not found: File not supplied initially. +// ParserError: (1088-1103): Source "a.sol" not found: File not supplied initially. +// ParserError: (1104-1119): Source "a.sol" not found: File not supplied initially. +// ParserError: (1120-1135): Source "a.sol" not found: File not supplied initially. +// ParserError: (1136-1151): Source "a.sol" not found: File not supplied initially. +// ParserError: (1152-1167): Source "a.sol" not found: File not supplied initially. +// ParserError: (1168-1183): Source "a.sol" not found: File not supplied initially. +// ParserError: (1184-1199): Source "a.sol" not found: File not supplied initially. +// ParserError: (1200-1215): Source "a.sol" not found: File not supplied initially. +// ParserError: (1216-1231): Source "a.sol" not found: File not supplied initially. +// ParserError: (1232-1247): Source "a.sol" not found: File not supplied initially. +// ParserError: (1248-1263): Source "a.sol" not found: File not supplied initially. +// ParserError: (1264-1279): Source "a.sol" not found: File not supplied initially. +// ParserError: (1280-1295): Source "a.sol" not found: File not supplied initially. +// ParserError: (1296-1311): Source "a.sol" not found: File not supplied initially. +// ParserError: (1312-1327): Source "a.sol" not found: File not supplied initially. +// ParserError: (1328-1343): Source "a.sol" not found: File not supplied initially. +// ParserError: (1344-1359): Source "a.sol" not found: File not supplied initially. +// ParserError: (1360-1375): Source "a.sol" not found: File not supplied initially. +// ParserError: (1376-1391): Source "a.sol" not found: File not supplied initially. +// ParserError: (1392-1407): Source "a.sol" not found: File not supplied initially. +// ParserError: (1408-1423): Source "a.sol" not found: File not supplied initially. +// ParserError: (1424-1439): Source "a.sol" not found: File not supplied initially. +// ParserError: (1440-1455): Source "a.sol" not found: File not supplied initially. +// ParserError: (1456-1471): Source "a.sol" not found: File not supplied initially. +// ParserError: (1472-1487): Source "a.sol" not found: File not supplied initially. +// ParserError: (1488-1503): Source "a.sol" not found: File not supplied initially. +// ParserError: (1504-1519): Source "a.sol" not found: File not supplied initially. +// ParserError: (1520-1535): Source "a.sol" not found: File not supplied initially. +// ParserError: (1536-1551): Source "a.sol" not found: File not supplied initially. +// ParserError: (1552-1567): Source "a.sol" not found: File not supplied initially. +// ParserError: (1568-1583): Source "a.sol" not found: File not supplied initially. +// ParserError: (1584-1599): Source "a.sol" not found: File not supplied initially. +// ParserError: (1600-1615): Source "a.sol" not found: File not supplied initially. +// ParserError: (1616-1631): Source "a.sol" not found: File not supplied initially. +// ParserError: (1632-1647): Source "a.sol" not found: File not supplied initially. +// ParserError: (1648-1663): Source "a.sol" not found: File not supplied initially. +// ParserError: (1664-1679): Source "a.sol" not found: File not supplied initially. +// ParserError: (1680-1695): Source "a.sol" not found: File not supplied initially. +// ParserError: (1696-1711): Source "a.sol" not found: File not supplied initially. +// ParserError: (1712-1727): Source "a.sol" not found: File not supplied initially. +// ParserError: (1728-1743): Source "a.sol" not found: File not supplied initially. +// ParserError: (1744-1759): Source "a.sol" not found: File not supplied initially. +// ParserError: (1760-1775): Source "a.sol" not found: File not supplied initially. +// ParserError: (1776-1791): Source "a.sol" not found: File not supplied initially. +// ParserError: (1792-1807): Source "a.sol" not found: File not supplied initially. +// ParserError: (1808-1823): Source "a.sol" not found: File not supplied initially. +// ParserError: (1824-1839): Source "a.sol" not found: File not supplied initially. +// ParserError: (1840-1855): Source "a.sol" not found: File not supplied initially. +// ParserError: (1856-1871): Source "a.sol" not found: File not supplied initially. +// ParserError: (1872-1887): Source "a.sol" not found: File not supplied initially. +// ParserError: (1888-1903): Source "a.sol" not found: File not supplied initially. +// ParserError: (1904-1919): Source "a.sol" not found: File not supplied initially. +// ParserError: (1920-1935): Source "a.sol" not found: File not supplied initially. +// ParserError: (1936-1951): Source "a.sol" not found: File not supplied initially. +// ParserError: (1952-1967): Source "a.sol" not found: File not supplied initially. +// ParserError: (1968-1983): Source "a.sol" not found: File not supplied initially. +// ParserError: (1984-1999): Source "a.sol" not found: File not supplied initially. +// ParserError: (2000-2015): Source "a.sol" not found: File not supplied initially. +// ParserError: (2016-2031): Source "a.sol" not found: File not supplied initially. +// ParserError: (2032-2047): Source "a.sol" not found: File not supplied initially. +// ParserError: (2048-2063): Source "a.sol" not found: File not supplied initially. +// ParserError: (2064-2079): Source "a.sol" not found: File not supplied initially. +// ParserError: (2080-2095): Source "a.sol" not found: File not supplied initially. +// ParserError: (2096-2111): Source "a.sol" not found: File not supplied initially. +// ParserError: (2112-2127): Source "a.sol" not found: File not supplied initially. +// ParserError: (2128-2143): Source "a.sol" not found: File not supplied initially. +// ParserError: (2144-2159): Source "a.sol" not found: File not supplied initially. +// ParserError: (2160-2175): Source "a.sol" not found: File not supplied initially. +// ParserError: (2176-2191): Source "a.sol" not found: File not supplied initially. +// ParserError: (2192-2207): Source "a.sol" not found: File not supplied initially. +// ParserError: (2208-2223): Source "a.sol" not found: File not supplied initially. +// ParserError: (2224-2239): Source "a.sol" not found: File not supplied initially. +// ParserError: (2240-2255): Source "a.sol" not found: File not supplied initially. +// ParserError: (2256-2271): Source "a.sol" not found: File not supplied initially. +// ParserError: (2272-2287): Source "a.sol" not found: File not supplied initially. +// ParserError: (2288-2303): Source "a.sol" not found: File not supplied initially. +// ParserError: (2304-2319): Source "a.sol" not found: File not supplied initially. +// ParserError: (2320-2335): Source "a.sol" not found: File not supplied initially. +// ParserError: (2336-2351): Source "a.sol" not found: File not supplied initially. +// ParserError: (2352-2367): Source "a.sol" not found: File not supplied initially. +// ParserError: (2368-2383): Source "a.sol" not found: File not supplied initially. +// ParserError: (2384-2399): Source "a.sol" not found: File not supplied initially. +// ParserError: (2400-2415): Source "a.sol" not found: File not supplied initially. +// ParserError: (2416-2431): Source "a.sol" not found: File not supplied initially. +// ParserError: (2432-2447): Source "a.sol" not found: File not supplied initially. +// ParserError: (2448-2463): Source "a.sol" not found: File not supplied initially. +// ParserError: (2464-2479): Source "a.sol" not found: File not supplied initially. +// ParserError: (2480-2495): Source "a.sol" not found: File not supplied initially. +// ParserError: (2496-2511): Source "a.sol" not found: File not supplied initially. +// ParserError: (2512-2527): Source "a.sol" not found: File not supplied initially. +// ParserError: (2528-2543): Source "a.sol" not found: File not supplied initially. +// ParserError: (2544-2559): Source "a.sol" not found: File not supplied initially. +// ParserError: (2560-2575): Source "a.sol" not found: File not supplied initially. +// ParserError: (2576-2591): Source "a.sol" not found: File not supplied initially. +// ParserError: (2592-2607): Source "a.sol" not found: File not supplied initially. +// ParserError: (2608-2623): Source "a.sol" not found: File not supplied initially. +// ParserError: (2624-2639): Source "a.sol" not found: File not supplied initially. +// ParserError: (2640-2655): Source "a.sol" not found: File not supplied initially. +// ParserError: (2656-2671): Source "a.sol" not found: File not supplied initially. +// ParserError: (2672-2687): Source "a.sol" not found: File not supplied initially. +// ParserError: (2688-2703): Source "a.sol" not found: File not supplied initially. +// ParserError: (2704-2719): Source "a.sol" not found: File not supplied initially. +// ParserError: (2720-2735): Source "a.sol" not found: File not supplied initially. +// ParserError: (2736-2751): Source "a.sol" not found: File not supplied initially. +// ParserError: (2752-2767): Source "a.sol" not found: File not supplied initially. +// ParserError: (2768-2783): Source "a.sol" not found: File not supplied initially. +// ParserError: (2784-2799): Source "a.sol" not found: File not supplied initially. +// ParserError: (2800-2815): Source "a.sol" not found: File not supplied initially. +// ParserError: (2816-2831): Source "a.sol" not found: File not supplied initially. +// ParserError: (2832-2847): Source "a.sol" not found: File not supplied initially. +// ParserError: (2848-2863): Source "a.sol" not found: File not supplied initially. +// ParserError: (2864-2879): Source "a.sol" not found: File not supplied initially. +// ParserError: (2880-2895): Source "a.sol" not found: File not supplied initially. +// ParserError: (2896-2911): Source "a.sol" not found: File not supplied initially. +// ParserError: (2912-2927): Source "a.sol" not found: File not supplied initially. +// ParserError: (2928-2943): Source "a.sol" not found: File not supplied initially. +// ParserError: (2944-2959): Source "a.sol" not found: File not supplied initially. +// ParserError: (2960-2975): Source "a.sol" not found: File not supplied initially. +// ParserError: (2976-2991): Source "a.sol" not found: File not supplied initially. +// ParserError: (2992-3007): Source "a.sol" not found: File not supplied initially. +// ParserError: (3008-3023): Source "a.sol" not found: File not supplied initially. +// ParserError: (3024-3039): Source "a.sol" not found: File not supplied initially. +// ParserError: (3040-3055): Source "a.sol" not found: File not supplied initially. +// ParserError: (3056-3071): Source "a.sol" not found: File not supplied initially. +// ParserError: (3072-3087): Source "a.sol" not found: File not supplied initially. +// ParserError: (3088-3103): Source "a.sol" not found: File not supplied initially. +// ParserError: (3104-3119): Source "a.sol" not found: File not supplied initially. +// ParserError: (3120-3135): Source "a.sol" not found: File not supplied initially. +// ParserError: (3136-3151): Source "a.sol" not found: File not supplied initially. +// ParserError: (3152-3167): Source "a.sol" not found: File not supplied initially. +// ParserError: (3168-3183): Source "a.sol" not found: File not supplied initially. +// ParserError: (3184-3199): Source "a.sol" not found: File not supplied initially. +// ParserError: (3200-3215): Source "a.sol" not found: File not supplied initially. +// ParserError: (3216-3231): Source "a.sol" not found: File not supplied initially. +// ParserError: (3232-3247): Source "a.sol" not found: File not supplied initially. +// ParserError: (3248-3263): Source "a.sol" not found: File not supplied initially. +// ParserError: (3264-3279): Source "a.sol" not found: File not supplied initially. +// ParserError: (3280-3295): Source "a.sol" not found: File not supplied initially. +// ParserError: (3296-3311): Source "a.sol" not found: File not supplied initially. +// ParserError: (3312-3327): Source "a.sol" not found: File not supplied initially. +// ParserError: (3328-3343): Source "a.sol" not found: File not supplied initially. +// ParserError: (3344-3359): Source "a.sol" not found: File not supplied initially. +// ParserError: (3360-3375): Source "a.sol" not found: File not supplied initially. +// ParserError: (3376-3391): Source "a.sol" not found: File not supplied initially. +// ParserError: (3392-3407): Source "a.sol" not found: File not supplied initially. +// ParserError: (3408-3423): Source "a.sol" not found: File not supplied initially. +// ParserError: (3424-3439): Source "a.sol" not found: File not supplied initially. +// ParserError: (3440-3455): Source "a.sol" not found: File not supplied initially. +// ParserError: (3456-3471): Source "a.sol" not found: File not supplied initially. +// ParserError: (3472-3487): Source "a.sol" not found: File not supplied initially. +// ParserError: (3488-3503): Source "a.sol" not found: File not supplied initially. +// ParserError: (3504-3519): Source "a.sol" not found: File not supplied initially. +// ParserError: (3520-3535): Source "a.sol" not found: File not supplied initially. +// ParserError: (3536-3551): Source "a.sol" not found: File not supplied initially. +// ParserError: (3552-3567): Source "a.sol" not found: File not supplied initially. +// ParserError: (3568-3583): Source "a.sol" not found: File not supplied initially. +// ParserError: (3584-3599): Source "a.sol" not found: File not supplied initially. +// ParserError: (3600-3615): Source "a.sol" not found: File not supplied initially. +// ParserError: (3616-3631): Source "a.sol" not found: File not supplied initially. +// ParserError: (3632-3647): Source "a.sol" not found: File not supplied initially. +// ParserError: (3648-3663): Source "a.sol" not found: File not supplied initially. +// ParserError: (3664-3679): Source "a.sol" not found: File not supplied initially. +// ParserError: (3680-3695): Source "a.sol" not found: File not supplied initially. +// ParserError: (3696-3711): Source "a.sol" not found: File not supplied initially. +// ParserError: (3712-3727): Source "a.sol" not found: File not supplied initially. +// ParserError: (3728-3743): Source "a.sol" not found: File not supplied initially. +// ParserError: (3744-3759): Source "a.sol" not found: File not supplied initially. +// ParserError: (3760-3775): Source "a.sol" not found: File not supplied initially. +// ParserError: (3776-3791): Source "a.sol" not found: File not supplied initially. +// ParserError: (3792-3807): Source "a.sol" not found: File not supplied initially. +// ParserError: (3808-3823): Source "a.sol" not found: File not supplied initially. +// ParserError: (3824-3839): Source "a.sol" not found: File not supplied initially. +// ParserError: (3840-3855): Source "a.sol" not found: File not supplied initially. +// ParserError: (3856-3871): Source "a.sol" not found: File not supplied initially. +// ParserError: (3872-3887): Source "a.sol" not found: File not supplied initially. +// ParserError: (3888-3903): Source "a.sol" not found: File not supplied initially. +// ParserError: (3904-3919): Source "a.sol" not found: File not supplied initially. +// ParserError: (3920-3935): Source "a.sol" not found: File not supplied initially. +// ParserError: (3936-3951): Source "a.sol" not found: File not supplied initially. +// ParserError: (3952-3967): Source "a.sol" not found: File not supplied initially. +// ParserError: (3968-3983): Source "a.sol" not found: File not supplied initially. +// ParserError: (3984-3999): Source "a.sol" not found: File not supplied initially. +// ParserError: (4000-4015): Source "a.sol" not found: File not supplied initially. +// ParserError: (4016-4031): Source "a.sol" not found: File not supplied initially. +// ParserError: (4032-4047): Source "a.sol" not found: File not supplied initially. +// ParserError: (4048-4063): Source "a.sol" not found: File not supplied initially. +// ParserError: (4064-4079): Source "a.sol" not found: File not supplied initially. +// ParserError: (4080-4095): Source "a.sol" not found: File not supplied initially. +// Warning: There are more than 256 errors. Aborting. From 45f80e98959298e45a9d41307508fa6b6801d909 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 12 Feb 2020 12:08:07 +0100 Subject: [PATCH 123/160] Move assertion in PostTypeChecker's ConstStateVarCircularReferenceChecker to account for function type variables --- libsolidity/analysis/PostTypeChecker.cpp | 2 +- .../syntaxTests/cycle_checker_function_type.sol | 6 ++++++ .../types/constant_of_invalid_function_type.sol | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/cycle_checker_function_type.sol create mode 100644 test/libsolidity/syntaxTests/types/constant_of_invalid_function_type.sol diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index 6ed3062696cf..62317587ab34 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -133,9 +133,9 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker bool visit(VariableDeclaration const& _variable) override { - solAssert(!m_currentConstVariable, ""); if (_variable.isConstant()) { + solAssert(!m_currentConstVariable, ""); m_currentConstVariable = &_variable; m_constVariables.push_back(&_variable); } diff --git a/test/libsolidity/syntaxTests/cycle_checker_function_type.sol b/test/libsolidity/syntaxTests/cycle_checker_function_type.sol new file mode 100644 index 000000000000..1a8477eaef61 --- /dev/null +++ b/test/libsolidity/syntaxTests/cycle_checker_function_type.sol @@ -0,0 +1,6 @@ +// Used to cause ICE. +contract C { + function ( ) internal returns ( bytes [ ] storage , mapping ( bytes => mapping ( bytes => mapping ( uint => mapping ( bytes => mapping ( string => mapping ( uint => mapping ( uint => mapping ( uint => mapping ( uint => mapping ( string => mapping ( string => mapping ( uint => mapping ( bytes => mapping ( uint => mapping ( uint => mapping ( uint => mapping ( uint => mapping ( uint => mapping ( bytes => mapping ( uint => mapping ( uint => mapping ( uint => mapping ( uint => mapping ( string => mapping ( uint => string ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) [ ] storage ) constant c = c ; +} +// ---- +// TypeError: (43-643): The value of the constant c has a cyclic dependency via c. diff --git a/test/libsolidity/syntaxTests/types/constant_of_invalid_function_type.sol b/test/libsolidity/syntaxTests/types/constant_of_invalid_function_type.sol new file mode 100644 index 000000000000..8c24520dff24 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/constant_of_invalid_function_type.sol @@ -0,0 +1,7 @@ +contract C { + // Used to cause internal compiler error. + function() returns (x) constant x = x; + +} +// ---- +// TypeError: (77-78): Name has to refer to a struct, enum or contract. From 9aed40ab19b8b7c7fd321ea95e6fdbac34f595aa Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 12 Feb 2020 18:28:30 +0100 Subject: [PATCH 124/160] Check that functions exist in isoltest. --- test/libsolidity/SemanticTest.cpp | 21 ++++++++++++++++--- .../semanticTests/empty_contract.sol | 1 + .../functionCall/member_accessors.sol | 1 + .../functionCall/multiple_functions.sol | 2 ++ .../semanticTests/smoke/failure.sol | 1 + .../semanticTests/smoke/multiline.sol | 1 + .../semanticTests/viaYul/smoke_test.sol | 1 + test/libsolidity/util/TestFunctionCall.cpp | 4 ++++ test/libsolidity/util/TestFunctionCall.h | 3 +++ 9 files changed, 32 insertions(+), 3 deletions(-) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index f083fae76c48..35ea80bd9509 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -80,6 +80,12 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer m_settings.erase("revertStrings"); } + if (m_settings.count("allowNonExistingFunctions")) + { + m_validatedSettings["allowNonExistingFunctions"] = true; + m_settings.erase("allowNonExistingFunctions"); + } + parseExpectations(file); soltestAssert(!m_tests.empty(), "No tests specified in " + _filename); } @@ -143,13 +149,22 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref } else { - bytes output = test.call().useCallWithoutSignature ? - callLowLevel(test.call().arguments.rawBytes(), test.call().value) : - callContractFunctionWithValueNoEncoding( + bytes output; + if (test.call().useCallWithoutSignature) + output = callLowLevel(test.call().arguments.rawBytes(), test.call().value); + else + { + soltestAssert( + m_validatedSettings.count("allowNonExistingFunctions") || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), + "The function " + test.call().signature + " is not known to the compiler" + ); + + output = callContractFunctionWithValueNoEncoding( test.call().signature, test.call().value, test.call().arguments.rawBytes() ); + } if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) success = false; diff --git a/test/libsolidity/semanticTests/empty_contract.sol b/test/libsolidity/semanticTests/empty_contract.sol index 8950f01d1ec3..f81e0aa2a4e1 100644 --- a/test/libsolidity/semanticTests/empty_contract.sol +++ b/test/libsolidity/semanticTests/empty_contract.sol @@ -2,5 +2,6 @@ contract test { } // ==== // compileViaYul: also +// allowNonExistingFunctions: true // ---- // i_am_not_there() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/member_accessors.sol b/test/libsolidity/semanticTests/functionCall/member_accessors.sol index 67cb767db03f..238d99a61380 100644 --- a/test/libsolidity/semanticTests/functionCall/member_accessors.sol +++ b/test/libsolidity/semanticTests/functionCall/member_accessors.sol @@ -13,6 +13,7 @@ contract test { uint256 super_secret_data; } // ==== +// allowNonExistingFunctions: true // compileViaYul: also // ---- // data() -> 8 diff --git a/test/libsolidity/semanticTests/functionCall/multiple_functions.sol b/test/libsolidity/semanticTests/functionCall/multiple_functions.sol index 981608d89101..3c60a13f11cf 100644 --- a/test/libsolidity/semanticTests/functionCall/multiple_functions.sol +++ b/test/libsolidity/semanticTests/functionCall/multiple_functions.sol @@ -4,6 +4,8 @@ contract test { function c() public returns(uint n) { return 2; } function f() public returns(uint n) { return 3; } } +// ==== +// allowNonExistingFunctions: true // ---- // a() -> 0 // b() -> 1 diff --git a/test/libsolidity/semanticTests/smoke/failure.sol b/test/libsolidity/semanticTests/smoke/failure.sol index ee1064062e93..e693a84596cf 100644 --- a/test/libsolidity/semanticTests/smoke/failure.sol +++ b/test/libsolidity/semanticTests/smoke/failure.sol @@ -14,6 +14,7 @@ contract C { } } // ==== +// allowNonExistingFunctions: true // EVMVersion: >homestead // ---- // _() -> FAILURE diff --git a/test/libsolidity/semanticTests/smoke/multiline.sol b/test/libsolidity/semanticTests/smoke/multiline.sol index 1663a6de4762..ff8ee813e117 100644 --- a/test/libsolidity/semanticTests/smoke/multiline.sol +++ b/test/libsolidity/semanticTests/smoke/multiline.sol @@ -4,6 +4,7 @@ contract C { } } // ==== +// allowNonExistingFunctions: true // compileViaYul: also // ---- // f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1 diff --git a/test/libsolidity/semanticTests/viaYul/smoke_test.sol b/test/libsolidity/semanticTests/viaYul/smoke_test.sol index 0e0a7d4d545d..d5c02573471a 100644 --- a/test/libsolidity/semanticTests/viaYul/smoke_test.sol +++ b/test/libsolidity/semanticTests/viaYul/smoke_test.sol @@ -1,6 +1,7 @@ contract C { } // ==== +// allowNonExistingFunctions: true // compileViaYul: true // ---- // f() -> FAILURE diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index ac7a92a08911..22dc82aa802d 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -114,6 +114,9 @@ string TestFunctionCall::format( } else { + if (m_calledNonExistingFunction) + _errorReporter.warning("The function \"" + m_call.signature + "\" is not known to the compiler."); + bytes output = m_rawBytes; bool const isFailure = m_failure; result = isFailure ? @@ -300,6 +303,7 @@ void TestFunctionCall::reset() { m_rawBytes = bytes{}; m_failure = true; + m_calledNonExistingFunction = false; } bool TestFunctionCall::matchesExpectation() const diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h index baa0990b1e01..6f21fc9eb4cb 100644 --- a/test/libsolidity/util/TestFunctionCall.h +++ b/test/libsolidity/util/TestFunctionCall.h @@ -77,6 +77,7 @@ class TestFunctionCall void reset(); FunctionCall const& call() const { return m_call; } + void calledNonExistingFunction() { m_calledNonExistingFunction = true; } void setFailure(const bool _failure) { m_failure = _failure; } void setRawBytes(const bytes _rawBytes) { m_rawBytes = _rawBytes; } void setContractABI(Json::Value _contractABI) { m_contractABI = std::move(_contractABI); } @@ -129,6 +130,8 @@ class TestFunctionCall /// JSON object which holds the contract ABI and that is used to set the output formatting /// in the interactive update routine. Json::Value m_contractABI; + /// Flags that the test failed because the called function is not known to exist on the contract. + bool m_calledNonExistingFunction = false; }; } From 8911b58a6d386142ba074295cbc651a3a18e0b6b Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 13 Feb 2020 14:33:27 +0100 Subject: [PATCH 125/160] Make type error fatal to prevent assert failure at later point --- libsolidity/analysis/TypeChecker.cpp | 2 +- test/libsolidity/syntaxTests/emit/emit_non_event.sol | 5 ++--- .../syntaxTests/functionCalls/int_not_callable.sol | 8 ++++++++ .../syntaxTests/functionCalls/this_not_callable.sol | 9 +++++++++ .../492_do_not_crash_on_not_lvalue.sol | 2 -- .../tupleAssignments/double_storage_crash.sol | 1 - .../libsolidity/syntaxTests/types/function_call_fail.sol | 2 -- 7 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 test/libsolidity/syntaxTests/functionCalls/int_not_callable.sol create mode 100644 test/libsolidity/syntaxTests/functionCalls/this_not_callable.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index e4aa399d862f..46490297173b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2154,7 +2154,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } default: - m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); + m_errorReporter.fatalTypeError(_functionCall.location(), "Type is not callable"); funcCallAnno.kind = FunctionCallKind::Unset; funcCallAnno.isPure = argumentsArePure; break; diff --git a/test/libsolidity/syntaxTests/emit/emit_non_event.sol b/test/libsolidity/syntaxTests/emit/emit_non_event.sol index d5045ddf98e2..5f9053681f52 100644 --- a/test/libsolidity/syntaxTests/emit/emit_non_event.sol +++ b/test/libsolidity/syntaxTests/emit/emit_non_event.sol @@ -1,10 +1,9 @@ contract C { - uint256 Test; + function() Test; function f() public { emit Test(); } } // ---- -// TypeError: (63-69): Type is not callable -// TypeError: (63-67): Expression has to be an event invocation. +// TypeError: (66-70): Expression has to be an event invocation. diff --git a/test/libsolidity/syntaxTests/functionCalls/int_not_callable.sol b/test/libsolidity/syntaxTests/functionCalls/int_not_callable.sol new file mode 100644 index 000000000000..5ef2559be46e --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/int_not_callable.sol @@ -0,0 +1,8 @@ +contract C +{ + function f ( ) public { + var i = ( ( 1 ( 3 ) ) , 2 ); + } +} +// ---- +// TypeError: (61-68): Type is not callable diff --git a/test/libsolidity/syntaxTests/functionCalls/this_not_callable.sol b/test/libsolidity/syntaxTests/functionCalls/this_not_callable.sol new file mode 100644 index 000000000000..dd32f2324004 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/this_not_callable.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (uint, uint) { + try this() { + } catch Error(string memory) { + } + } +} +// ---- +// TypeError: (72-78): Type is not callable diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/492_do_not_crash_on_not_lvalue.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/492_do_not_crash_on_not_lvalue.sol index 902758049596..2de7f4904db2 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/492_do_not_crash_on_not_lvalue.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/492_do_not_crash_on_not_lvalue.sol @@ -7,5 +7,3 @@ contract C { } // ---- // TypeError: (153-157): Type is not callable -// TypeError: (153-157): Expression has to be an lvalue. -// TypeError: (160-161): Type int_const 2 is not implicitly convertible to expected type tuple(). diff --git a/test/libsolidity/syntaxTests/tupleAssignments/double_storage_crash.sol b/test/libsolidity/syntaxTests/tupleAssignments/double_storage_crash.sol index 3cff3a9a07d7..0e19136f4aea 100644 --- a/test/libsolidity/syntaxTests/tupleAssignments/double_storage_crash.sol +++ b/test/libsolidity/syntaxTests/tupleAssignments/double_storage_crash.sol @@ -8,4 +8,3 @@ contract CrashContract { } // ---- // TypeError: (170-177): Type is not callable -// TypeError: (170-177): Type tuple() is not implicitly convertible to expected type tuple(struct CrashContract.S storage ref,struct CrashContract.S storage ref). diff --git a/test/libsolidity/syntaxTests/types/function_call_fail.sol b/test/libsolidity/syntaxTests/types/function_call_fail.sol index ef52ab4485d3..5ce778a6dcfd 100644 --- a/test/libsolidity/syntaxTests/types/function_call_fail.sol +++ b/test/libsolidity/syntaxTests/types/function_call_fail.sol @@ -5,5 +5,3 @@ contract C { } // ---- // TypeError: (59-63): Type is not callable -// TypeError: (59-63): Expression has to be an lvalue. -// TypeError: (67-68): Type int_const 2 is not implicitly convertible to expected type tuple(). From 3800391a1ae7ff8f3c4165af0178cc29b0406b4e Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 13 Feb 2020 15:42:16 -0400 Subject: [PATCH 126/160] [Yul] Support conditionals --- .../codegen/ir/IRGeneratorForStatements.cpp | 18 ++++++++++++++++++ .../codegen/ir/IRGeneratorForStatements.h | 1 + .../conditional/conditional_multiple.sol | 10 ++++++++++ .../conditional_true_false_literal.sol | 11 +++++++++++ .../viaYul/conditional/conditional_tuple.sol | 11 +++++++++++ .../conditional_with_assignment.sol | 13 +++++++++++++ .../conditional/conditional_with_variables.sol | 13 +++++++++++++ 7 files changed, 77 insertions(+) create mode 100644 test/libsolidity/semanticTests/viaYul/conditional/conditional_multiple.sol create mode 100644 test/libsolidity/semanticTests/viaYul/conditional/conditional_true_false_literal.sol create mode 100644 test/libsolidity/semanticTests/viaYul/conditional/conditional_tuple.sol create mode 100644 test/libsolidity/semanticTests/viaYul/conditional/conditional_with_assignment.sol create mode 100644 test/libsolidity/semanticTests/viaYul/conditional/conditional_with_variables.sol diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 09a3661605e1..f6b70332c652 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -180,6 +180,24 @@ void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _var declare(m_context.addLocalVariable(*decl)); } +bool IRGeneratorForStatements::visit(Conditional const& _conditional) +{ + _conditional.condition().accept(*this); + + string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean()); + declare(_conditional); + + m_code << "switch " << condition << "\n" "case 0 {\n"; + _conditional.falseExpression().accept(*this); + assign(_conditional, _conditional.falseExpression()); + m_code << "}\n" "default {\n"; + _conditional.trueExpression().accept(*this); + assign(_conditional, _conditional.trueExpression()); + m_code << "}\n"; + + return false; +} + bool IRGeneratorForStatements::visit(Assignment const& _assignment) { _assignment.rightHandSide().accept(*this); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 15c49ce02ff2..60861e5be85c 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -48,6 +48,7 @@ class IRGeneratorForStatements: public ASTConstVisitor void initializeStateVar(VariableDeclaration const& _varDecl); void endVisit(VariableDeclarationStatement const& _variableDeclaration) override; + bool visit(Conditional const& _conditional) override; bool visit(Assignment const& _assignment) override; bool visit(TupleExpression const& _tuple) override; bool visit(IfStatement const& _ifStatement) override; diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_multiple.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_multiple.sol new file mode 100644 index 000000000000..0d577fdb97ae --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_multiple.sol @@ -0,0 +1,10 @@ +contract A { + function f() public pure returns (uint) { + uint x = 3 < 0 ? 2 > 1 ? 2 : 1 : 7 > 2 ? 7 : 6; + return x; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_true_false_literal.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_true_false_literal.sol new file mode 100644 index 000000000000..6891be50058f --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_true_false_literal.sol @@ -0,0 +1,11 @@ +contract A { + function f() public pure returns (uint) { + uint x = true ? 1 : 0; + uint y = false ? 0 : 1; + return x + y; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_tuple.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_tuple.sol new file mode 100644 index 000000000000..af8ab732bbf9 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_tuple.sol @@ -0,0 +1,11 @@ +contract A { + function f(bool cond) public pure returns (uint, uint) { + (uint a, uint b) = cond ? (1, 2) : (3, 4); + return (a, b); + } +} +// ==== +// compileViaYul: also +// ---- +// f(bool): true -> 1, 2 +// f(bool): false -> 3, 4 diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_assignment.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_assignment.sol new file mode 100644 index 000000000000..d263c2cea946 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_assignment.sol @@ -0,0 +1,13 @@ +contract A { + function f() public pure returns (uint, uint, uint, uint) { + uint y1 = 1; + uint y2 = 1; + uint x = 3 < 0 ? y1 = 3 : 6; + uint z = 3 < 10 ? y2 = 5 : 6; + return (x, y1, y2, z); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 6, 1, 5, 5 diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_variables.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_variables.sol new file mode 100644 index 000000000000..6d977d7627ce --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_variables.sol @@ -0,0 +1,13 @@ +contract A { + function f() public pure returns (uint, uint, uint, uint) { + uint x = 3; + uint y = 1; + uint z = (x > y) ? x : y; + uint w = x < y ? x : y; + return (x, y, z, w); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 3, 1, 3, 1 From 5ec8202e6e9af9260553bd5f03ba5b4819f4a429 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 12 Feb 2020 18:42:45 +0100 Subject: [PATCH 127/160] Add Ubuntu Focal. --- scripts/deps-ppa/static_z3.sh | 4 ++-- scripts/install_deps.sh | 6 +++++- scripts/release_ppa.sh | 9 +++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts/deps-ppa/static_z3.sh b/scripts/deps-ppa/static_z3.sh index 5a860bf7f8e5..7eb461821068 100755 --- a/scripts/deps-ppa/static_z3.sh +++ b/scripts/deps-ppa/static_z3.sh @@ -29,7 +29,7 @@ packagename=libz3-static-dev version=4.8.7 version_patchsuffix=-1 -DISTRIBUTIONS="bionic disco eoan" +DISTRIBUTIONS="bionic disco eoan focal" for distribution in $DISTRIBUTIONS do @@ -72,7 +72,7 @@ Priority: extra Maintainer: Daniel Kirchner Build-Depends: debhelper (>= 9.0.0), cmake, - g++, + g++ (>= 5.0), git, libgmp-dev, dh-python, diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 0617dd02821c..f3062e21a9bd 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -300,7 +300,11 @@ case $(uname -s) in install_z3="libz3-dev" ;; bionic) - echo "Installing solidity dependencies on Ubuntu Bionic (18.04)." + echo "Installing solidity dependencies." + install_z3="libz3-dev" + ;; + focal) + echo "Installing solidity dependencies." install_z3="libz3-dev" ;; betsy) diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 5b5ca728f839..49634a977365 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -57,7 +57,7 @@ packagename=solc static_build_distribution=disco -DISTRIBUTIONS="bionic disco eoan" +DISTRIBUTIONS="bionic disco eoan focal" if is_release then @@ -83,7 +83,12 @@ else else pparepo=ethereum-dev fi - if [ $distribution = disco ] + if [ $distribution = focal ] + then + SMTDEPENDENCY="libz3-dev, + libcvc4-dev, + " + elif [ $distribution = disco ] then SMTDEPENDENCY="libz3-static-dev, libcvc4-dev, From d14d26717d90a3eae57952efbf719dcc00ccc5d9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 13 Feb 2020 22:53:02 +0100 Subject: [PATCH 128/160] Disable bytecode uploads for appveyor PR builds. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 2e2a2fa8bae8..4b66e027e897 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -71,7 +71,7 @@ test_script: - soltest.exe --show-progress -- --testpath %APPVEYOR_BUILD_FOLDER%\test --no-smt # Skip bytecode compare if private key is not available - cd %APPVEYOR_BUILD_FOLDER% - - ps: if ($env:priv_key) { + - ps: if ($env:priv_key -and -not $env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT) { scripts\bytecodecompare\storebytecode.bat $Env:CONFIGURATION $bytecodedir } - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% From c92fe69a6090de2f3b97e19c4e214c1d797a7557 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 10 Feb 2020 11:44:52 +0100 Subject: [PATCH 129/160] Finishes external call implementation. --- .../codegen/ir/IRGeneratorForStatements.cpp | 60 ++++++++++--------- .../codegen/ir/IRGeneratorForStatements.h | 4 +- .../semanticTests/array/memory.sol | 17 ++++++ .../functionCall/external_call.sol | 17 ++++++ .../external_call_dynamic_returndata.sol | 24 ++++++++ 5 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 test/libsolidity/semanticTests/array/memory.sol create mode 100644 test/libsolidity/semanticTests/functionCall/external_call.sol create mode 100644 test/libsolidity/semanticTests/functionCall/external_call_dynamic_returndata.sol diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index f6b70332c652..dfb95cdd3e61 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -622,13 +622,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } solAssert(indexedArgs.size() <= 4, "Too many indexed arguments."); Whiskers templ(R"({ - let := mload() + let := let := ( ) (, sub(, ) ) })"); templ("pos", m_context.newYulVariable()); templ("end", m_context.newYulVariable()); - templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)); + templ("freeMemory", freeMemory()); templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes)); templ("nonIndexedArgs", nonIndexedArgs); templ("log", "log" + to_string(indexedArgs.size())); @@ -1173,7 +1173,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall()); bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); - unsigned retSize = 0; + unsigned estimatedReturnSize = 0; bool dynamicReturnSize = false; TypePointers returnTypes; if (!returnSuccessConditionAndReturndata) @@ -1188,13 +1188,13 @@ void IRGeneratorForStatements::appendExternalFunctionCall( { solAssert(haveReturndatacopy, ""); dynamicReturnSize = true; - retSize = 0; + estimatedReturnSize = 0; break; } else if (retType->decodingType()) - retSize += retType->decodingType()->calldataEncodedSize(); + estimatedReturnSize += retType->decodingType()->calldataEncodedSize(); else - retSize += retType->calldataEncodedSize(); + estimatedReturnSize += retType->calldataEncodedSize(); } TypePointers argumentTypes; @@ -1204,7 +1204,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( argumentTypes.emplace_back(&type(*arg)); argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList()); } - string argumentString = joinHumanReadable(argumentStrings); + string argumentString = ", " + joinHumanReadable(argumentStrings); solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); @@ -1214,8 +1214,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall( // (which we would have to subtract from the gas left) // We could also just use MLOAD; POP right before the gas calculation, but the optimizer // would remove that, so we use MSTORE here. - if (!funType.gasSet() && retSize > 0) - m_code << "mstore(add(" << fetchFreeMem() << ", " << to_string(retSize) << "), 0)\n"; + if (!funType.gasSet() && estimatedReturnSize > 0) + m_code << "mstore(add(" << freeMemory() << ", " << to_string(estimatedReturnSize) << "), 0)\n"; } ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); @@ -1226,26 +1226,29 @@ void IRGeneratorForStatements::appendExternalFunctionCall( if iszero(extcodesize(
)) { revert(0, 0) } - let := + let := + mstore(, ()) let := (add(, 4) ) - let := (,
, , , sub(, ), , ) - if iszero() { } + let := (,
, , , sub(, ), , ) + if iszero() { () } returndatacopy(, 0, returndatasize()) - - mstore(, add(, and(add(, 0x1f), not(0x1f)))) - let := (, ) + + mstore(, add(, and(add(, 0x1f), not(0x1f)))) + let := (, add(, )) )"); templ("pos", m_context.newYulVariable()); templ("end", m_context.newYulVariable()); templ("result", m_context.newYulVariable()); - templ("freeMem", fetchFreeMem()); + templ("freeMemory", freeMemory()); + templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)); templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4))); templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name()); + templ("address", IRVariable(_functionCall.expression()).part("address").name()); // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place. // Move arguments to memory, will not update the free memory pointer (but will update the memory @@ -1257,23 +1260,19 @@ void IRGeneratorForStatements::appendExternalFunctionCall( encodeInPlace = false; bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; solUnimplementedAssert(!encodeInPlace, ""); - solUnimplementedAssert(!funType.padArguments(), ""); + solUnimplementedAssert(funType.padArguments(), ""); templ("encodeArgs", abi.tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall)); templ("argumentString", argumentString); // Output data will replace input data, unless we have ECRecover (then, output // area will be 32 bytes just before input area). - templ("retSize", to_string(retSize)); solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); - if (isDelegateCall) - solAssert(!funType.valueSet(), "Value set for delegatecall"); - else if (useStaticCall) - solAssert(!funType.valueSet(), "Value set for staticcall"); - else if (funType.valueSet()) - templ("value", IRVariable(_functionCall.expression()).part("value").name()); - else - templ("value", "0"); + solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall"); + solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall"); + + templ("hasValue", !isDelegateCall && !useStaticCall); + templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); // Check that the target contract exists (has code) for non-low-level calls. bool checkExistence = (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall); @@ -1315,13 +1314,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall( if (haveReturndatacopy) templ("returnSize", "returndatasize()"); else - templ("returnSize", to_string(retSize)); + templ("returnSize", to_string(estimatedReturnSize)); + + templ("reservedReturnSize", dynamicReturnSize ? "0" : to_string(estimatedReturnSize)); + templ("abiDecode", abi.tupleDecoder(returnTypes, true)); templ("returns", !returnTypes.empty()); templ("retVars", IRVariable(_functionCall).commaSeparatedList()); + + m_code << templ.render(); } -string IRGeneratorForStatements::fetchFreeMem() const +string IRGeneratorForStatements::freeMemory() { return "mload(" + to_string(CompilerUtils::freeMemoryPointer) + ")"; } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 60861e5be85c..785b02e098e6 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -75,7 +75,9 @@ class IRGeneratorForStatements: public ASTConstVisitor std::vector> const& _arguments ); - std::string fetchFreeMem() const; + /// @returns code that evaluates to the first unused memory slot (which does not have to + /// be empty). + static std::string freeMemory(); /// Generates the required conversion code and @returns an IRVariable referring to the value of @a _variable /// converted to type @a _to. diff --git a/test/libsolidity/semanticTests/array/memory.sol b/test/libsolidity/semanticTests/array/memory.sol new file mode 100644 index 000000000000..dba62a3365c8 --- /dev/null +++ b/test/libsolidity/semanticTests/array/memory.sol @@ -0,0 +1,17 @@ +pragma solidity >= 0.6.0; + +contract C { + function h(uint[4] memory n) public pure returns (uint) { + return n[0] + n[1] + n[2] + n[3]; + } + + function i(uint[4] memory n) public view returns (uint) { + return this.h(n) * 2; + } +} + +// ==== +// compileViaYul: also +// ---- +// h(uint256[4]): 1, 2, 3, 4 -> 10 +// i(uint256[4]): 1, 2, 3, 4 -> 20 diff --git a/test/libsolidity/semanticTests/functionCall/external_call.sol b/test/libsolidity/semanticTests/functionCall/external_call.sol new file mode 100644 index 000000000000..b732f5e957a5 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/external_call.sol @@ -0,0 +1,17 @@ +pragma solidity >= 0.6.0; + +contract C { + function g(uint n) external pure returns (uint) { + return n + 1; + } + + function f(uint n) public view returns (uint) { + return this.g(2 * n); + } +} + +// ==== +// compileViaYul: also +// ---- +// g(uint256): 4 -> 5 +// f(uint256): 2 -> 5 diff --git a/test/libsolidity/semanticTests/functionCall/external_call_dynamic_returndata.sol b/test/libsolidity/semanticTests/functionCall/external_call_dynamic_returndata.sol new file mode 100644 index 000000000000..47ccc8dfa4ef --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/external_call_dynamic_returndata.sol @@ -0,0 +1,24 @@ +pragma solidity >= 0.6.0; + +contract C { + function d(uint n) external pure returns (uint[] memory) { + uint[] memory data = new uint[](n); + for (uint i = 0; i < data.length; ++i) + data[i] = i; + return data; + } + + function dt(uint n) public view returns (uint) { + uint[] memory data = this.d(n); + uint sum = 0; + for (uint i = 0; i < data.length; ++i) + sum += data[i]; + return sum; + } +} + +// ==== +// compileViaYul: also +// EVMVersion: >=byzantium +// ---- +// dt(uint256): 4 -> 6 From 3633557f8a0c9238fcf3380da7ee31da1a4d5915 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Fri, 14 Feb 2020 12:28:55 +0100 Subject: [PATCH 130/160] Fixes command line tests on MacOS. --- scripts/ASTImportTest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index 7b34b14b02f3..800c5a5143af 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -79,7 +79,7 @@ for solfile in $(find $SYNTAXTESTS_DIR -name *.sol) do echo -n "." # create a temporary sub-directory - FILETMP=$(mktemp -d -p $WORKINGDIR) + FILETMP=$(mktemp -d) cd $FILETMP OUTPUT=$($SPLITSOURCES $solfile) From 9f094d59b92dd561b330d4486bc98089dfe0315d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 7 Feb 2020 12:44:52 +0100 Subject: [PATCH 131/160] Mark function selector accesses as pure for pure expressions and mark function accesses via contract name as pure. --- libsolidity/analysis/TypeChecker.cpp | 22 +++++++++++++++++++ libsolidity/codegen/ExpressionCompiler.cpp | 2 ++ .../members/base_contract.sol | 6 ++--- .../function_selector_via_contract_name.sol | 4 ++-- .../function_selector_via_interface_name.sol | 4 ++-- .../function_via_contract_name_public.sol | 4 ++-- .../function_definition_expression.sol | 11 ++++++++++ .../selector/function_selector_pure.sol | 14 ++++++++++++ .../local_variable_selector_not_pure.sol | 8 +++++++ .../state_variable_selector_contract_name.sol | 9 ++++++++ .../state_variable_selector_not_pure.sol | 17 ++++++++++++++ .../state_variable_selector_super.sol | 9 ++++++++ 12 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 test/libsolidity/syntaxTests/types/function_types/function_definition_expression.sol create mode 100644 test/libsolidity/syntaxTests/types/function_types/selector/function_selector_pure.sol create mode 100644 test/libsolidity/syntaxTests/types/function_types/selector/local_variable_selector_not_pure.sol create mode 100644 test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_contract_name.sol create mode 100644 test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_not_pure.sol create mode 100644 test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_super.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 46490297173b..e41425c6e594 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2527,7 +2527,15 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) else if (TypeType const* typeType = dynamic_cast(exprType)) { if (ContractType const* contractType = dynamic_cast(typeType->actualType())) + { annotation.isLValue = annotation.referencedDeclaration->isLValue(); + if ( + auto const* functionType = dynamic_cast(annotation.type); + functionType && + functionType->kind() == FunctionType::Kind::Declaration + ) + annotation.isPure = _memberAccess.expression().annotation().isPure; + } } // TODO some members might be pure, but for example `address(0x123).balance` is not pure @@ -2535,6 +2543,20 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (auto tt = dynamic_cast(exprType)) if (tt->actualType()->category() == Type::Category::Enum) annotation.isPure = true; + if ( + auto const* functionType = dynamic_cast(exprType); + functionType && + functionType->hasDeclaration() && + dynamic_cast(&functionType->declaration()) && + memberName == "selector" + ) + if (auto const* parentAccess = dynamic_cast(&_memberAccess.expression())) + { + annotation.isPure = parentAccess->expression().annotation().isPure; + if (auto const* exprInt = dynamic_cast(&parentAccess->expression())) + if (exprInt->name() == "this" || exprInt->name() == "super") + annotation.isPure = true; + } if (auto magicType = dynamic_cast(exprType)) { if (magicType->kind() == MagicType::Kind::ABI) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c8ded907874d..0fc24d919e68 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1303,6 +1303,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { switch (funType->kind()) { + case FunctionType::Kind::Declaration: + break; case FunctionType::Kind::Internal: // We do not visit the expression here on purpose, because in the case of an // internal library function call, this would push the library address forcing diff --git a/test/libsolidity/syntaxTests/types/contractTypeType/members/base_contract.sol b/test/libsolidity/syntaxTests/types/contractTypeType/members/base_contract.sol index 154a57d189a3..609d21b7bba7 100644 --- a/test/libsolidity/syntaxTests/types/contractTypeType/members/base_contract.sol +++ b/test/libsolidity/syntaxTests/types/contractTypeType/members/base_contract.sol @@ -3,9 +3,9 @@ contract B { function g() public {} } contract C is B { - function h() public { - B.f.selector; - B.g.selector; + function h() public returns (bytes4 fs, bytes4 gs) { + fs = B.f.selector; + gs = B.g.selector; B.g(); } } diff --git a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_contract_name.sol b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_contract_name.sol index ec53a0f601ce..63320917e6ca 100644 --- a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_contract_name.sol +++ b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_contract_name.sol @@ -3,7 +3,7 @@ contract A { } contract B { - function g() external pure { - A.f.selector; + function g() external pure returns(bytes4) { + return A.f.selector; } } diff --git a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_interface_name.sol b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_interface_name.sol index e733b670169a..b721a5a698f9 100644 --- a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_interface_name.sol +++ b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_selector_via_interface_name.sol @@ -3,7 +3,7 @@ interface I { } contract B { - function g() external pure { - I.f.selector; + function g() external pure returns(bytes4) { + return I.f.selector; } } diff --git a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_via_contract_name_public.sol b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_via_contract_name_public.sol index 28d1ff6d172a..533f30279ff4 100644 --- a/test/libsolidity/syntaxTests/types/contractTypeType/members/function_via_contract_name_public.sol +++ b/test/libsolidity/syntaxTests/types/contractTypeType/members/function_via_contract_name_public.sol @@ -3,7 +3,7 @@ contract A { } contract B { - function g() external pure { - A.f.selector; + function g() external pure returns(bytes4) { + return A.f.selector; } } diff --git a/test/libsolidity/syntaxTests/types/function_types/function_definition_expression.sol b/test/libsolidity/syntaxTests/types/function_types/function_definition_expression.sol new file mode 100644 index 000000000000..8afe07f573ad --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/function_definition_expression.sol @@ -0,0 +1,11 @@ +interface Banana { + function transfer(address,uint256) external returns(bool); +} + +contract Apple { + function f() public pure { + Banana.transfer; + } +} +// ---- +// Warning: (141-156): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/function_selector_pure.sol b/test/libsolidity/syntaxTests/types/function_types/selector/function_selector_pure.sol new file mode 100644 index 000000000000..cb7ad8c56f33 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/function_selector_pure.sol @@ -0,0 +1,14 @@ +interface A { + function f() external; +} +contract B { + function g() public {} +} + +contract C is B { + function h() external {} + bytes4 constant s1 = A.f.selector; + bytes4 constant s2 = B.g.selector; + bytes4 constant s3 = this.h.selector; + bytes4 constant s4 = super.g.selector; +} diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/local_variable_selector_not_pure.sol b/test/libsolidity/syntaxTests/types/function_types/selector/local_variable_selector_not_pure.sol new file mode 100644 index 000000000000..481683af3227 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/local_variable_selector_not_pure.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (bytes4) { + function() external g; + // Make sure g.selector is not considered pure: + // If it was considered pure, this would emit a warning "Statement has no effect". + g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_contract_name.sol b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_contract_name.sol new file mode 100644 index 000000000000..79d74893bcf5 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_contract_name.sol @@ -0,0 +1,9 @@ +contract A { + function() external public f; +} + +contract C { + bytes4 constant s4 = A.f.selector; +} +// ---- +// TypeError: (88-91): Member "f" not found or not visible after argument-dependent lookup in type(contract A). diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_not_pure.sol b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_not_pure.sol new file mode 100644 index 000000000000..2bd5a2a31b07 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_not_pure.sol @@ -0,0 +1,17 @@ +contract A { + function() external public f; +} +contract B { + function() external public g; +} + +contract C is B { + function() external public h; + bytes4 constant s1 = h.selector; + bytes4 constant s2 = B.g.selector; + bytes4 constant s3 = this.h.selector; +} +// ---- +// TypeError: (176-186): Initial value for constant variable has to be compile-time constant. +// TypeError: (213-225): Initial value for constant variable has to be compile-time constant. +// TypeError: (252-267): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_super.sol b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_super.sol new file mode 100644 index 000000000000..c6d658d1dc95 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/selector/state_variable_selector_super.sol @@ -0,0 +1,9 @@ +contract B { + function() external public g; +} + +contract C is B { + bytes4 constant s4 = super.g.selector; +} +// ---- +// TypeError: (93-100): Member "g" not found or not visible after argument-dependent lookup in contract super C. From 2f8683510d23fc2dcdb46195f1526352377a9c6f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 14 Feb 2020 12:48:21 +0100 Subject: [PATCH 132/160] Add readme. --- scripts/wasm-rebuild/README | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 scripts/wasm-rebuild/README diff --git a/scripts/wasm-rebuild/README b/scripts/wasm-rebuild/README new file mode 100644 index 000000000000..8de79453a89e --- /dev/null +++ b/scripts/wasm-rebuild/README @@ -0,0 +1,4 @@ +This directory contains scripts that we used to rebuild historic releases to webassembly. +The rebuild took place at the time of Solidity version 0.6.1 and was used to rebuild all versions from 0.3.6 to 0.6.1. +The scripts are not actively tested or maintained, but we keep them for future reference in case we want to rebuild +historic releases again at a later point. From e3a235a4927f7f3aa4a437fd52671a5181b807ce Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 14 Feb 2020 14:22:11 +0100 Subject: [PATCH 133/160] Add missing changelog entries. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 7fcfc08b6099..683e2928da62 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Language Features: * Allow contract types and enums as keys for mappings. + * Allow function selectors to be used as compile-time constants. Compiler Features: @@ -13,6 +14,7 @@ Compiler Features: Bugfixes: * Parser: Fix an internal error for ``abstract`` without ``contract``. + * Type Checker: Make invalid calls to uncallable types fatal errors. From b43751d65ec31f73aad70f9b609f935aba7ba966 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Thu, 23 Jan 2020 12:20:44 +0100 Subject: [PATCH 134/160] Reports source location for structured documentation errors. --- liblangutil/ErrorReporter.cpp | 9 +++++++++ liblangutil/ErrorReporter.h | 1 + libsolidity/analysis/DocStringAnalyser.cpp | 23 +++++++++++++++------- libsolidity/analysis/DocStringAnalyser.h | 3 ++- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/liblangutil/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index 312f159c0247..c6c059bc7f17 100644 --- a/liblangutil/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -244,3 +244,12 @@ void ErrorReporter::docstringParsingError(string const& _description) _description ); } + +void ErrorReporter::docstringParsingError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::DocstringParsingError, + _location, + _description + ); +} diff --git a/liblangutil/ErrorReporter.h b/liblangutil/ErrorReporter.h index 3acf5afee38c..5aca4beb9232 100644 --- a/liblangutil/ErrorReporter.h +++ b/liblangutil/ErrorReporter.h @@ -107,6 +107,7 @@ class ErrorReporter void fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description); void docstringParsingError(std::string const& _description); + void docstringParsingError(SourceLocation const& _location, std::string const& _description); ErrorList const& errors() const; diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index cadd12fc3c4e..e802c4204638 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -73,6 +73,7 @@ bool DocStringAnalyser::visit(EventDefinition const& _event) void DocStringAnalyser::checkParameters( CallableDeclaration const& _callable, + StructurallyDocumented const& _node, StructurallyDocumentedAnnotation& _annotation ) { @@ -86,6 +87,7 @@ void DocStringAnalyser::checkParameters( for (auto i = paramRange.first; i != paramRange.second; ++i) if (!validParams.count(i->second.paramName)) appendError( + _node.documentation()->location(), "Documented parameter \"" + i->second.paramName + "\" not found in the parameter list of the function." @@ -101,7 +103,7 @@ void DocStringAnalyser::handleConstructor( { static set const validTags = set{"author", "dev", "notice", "param"}; parseDocStrings(_node, _annotation, validTags, "constructor"); - checkParameters(_callable, _annotation); + checkParameters(_callable, _node, _annotation); } void DocStringAnalyser::handleCallable( @@ -112,7 +114,7 @@ void DocStringAnalyser::handleCallable( { static set const validTags = set{"author", "dev", "notice", "return", "param"}; parseDocStrings(_node, _annotation, validTags, "functions"); - checkParameters(_callable, _annotation); + checkParameters(_callable, _node, _annotation); } void DocStringAnalyser::parseDocStrings( @@ -134,7 +136,10 @@ void DocStringAnalyser::parseDocStrings( for (auto const& docTag: _annotation.docTags) { if (!_validTags.count(docTag.first)) - appendError("Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."); + appendError( + _node.documentation()->location(), + "Documentation tag @" + docTag.first + " not valid for " + _nodeName + "." + ); else if (docTag.first == "return") { @@ -145,14 +150,18 @@ void DocStringAnalyser::parseDocStrings( string firstWord = content.substr(0, content.find_first_of(" \t")); if (returnTagsVisited > function->returnParameters().size()) - appendError("Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + + appendError( + _node.documentation()->location(), + "Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + " exceedes the number of return parameters." ); else { auto parameter = function->returnParameters().at(returnTagsVisited - 1); if (!parameter->name().empty() && parameter->name() != firstWord) - appendError("Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + + appendError( + _node.documentation()->location(), + "Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + " does not contain the name of its return parameter." ); } @@ -161,8 +170,8 @@ void DocStringAnalyser::parseDocStrings( } } -void DocStringAnalyser::appendError(string const& _description) +void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description) { m_errorOccured = true; - m_errorReporter.docstringParsingError(_description); + m_errorReporter.docstringParsingError(_location, _description); } diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index 2b9e231958ab..b9f816854eb3 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -51,6 +51,7 @@ class DocStringAnalyser: private ASTConstVisitor void checkParameters( CallableDeclaration const& _callable, + StructurallyDocumented const& _node, StructurallyDocumentedAnnotation& _annotation ); @@ -73,7 +74,7 @@ class DocStringAnalyser: private ASTConstVisitor std::string const& _nodeName ); - void appendError(std::string const& _description); + void appendError(langutil::SourceLocation const& _location, std::string const& _description); bool m_errorOccured = false; langutil::ErrorReporter& m_errorReporter; From cfc70ede5ca94972fb7afe3b2e6d2d5ff24755f4 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Sat, 25 Jan 2020 17:53:48 +0100 Subject: [PATCH 135/160] Corrects comment literal that span too long. --- liblangutil/Scanner.cpp | 19 +++++++++++-------- liblangutil/Scanner.h | 3 ++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 9b9ad8847733..9d748c570fa3 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -306,19 +306,24 @@ bool Scanner::tryScanEndOfLine() return false; } -Token Scanner::scanSingleLineDocComment() +int Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); + int endPosition = m_source->position(); advance(); //consume the last '/' at /// skipWhitespaceExceptUnicodeLinebreak(); while (!isSourcePastEndOfInput()) { + endPosition = m_source->position(); if (tryScanEndOfLine()) { - // check if next line is also a documentation comment - skipWhitespace(); + // Check if next line is also a single-line comment. + // If any whitespaces were skipped, use source position before. + if (!skipWhitespace()) + endPosition = m_source->position(); + if (!m_source->isPastEndOfInput(3) && m_source->get(0) == '/' && m_source->get(1) == '/' && @@ -338,7 +343,7 @@ Token Scanner::scanSingleLineDocComment() advance(); } literal.complete(); - return Token::CommentLiteral; + return endPosition; } Token Scanner::skipMultiLineComment() @@ -426,12 +431,10 @@ Token Scanner::scanSlash() else if (m_char == '/') { // doxygen style /// comment - Token comment; m_skippedComments[NextNext].location.start = firstSlashPosition; m_skippedComments[NextNext].location.source = m_source; - comment = scanSingleLineDocComment(); - m_skippedComments[NextNext].location.end = sourcePos(); - m_skippedComments[NextNext].token = comment; + m_skippedComments[NextNext].token = Token::CommentLiteral; + m_skippedComments[NextNext].location.end = scanSingleLineDocComment(); return Token::Whitespace; } else diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 1e08e0b6e813..8f6cdab875e1 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -229,7 +229,8 @@ class Scanner Token scanString(); Token scanHexString(); - Token scanSingleLineDocComment(); + /// Scans a single line comment and returns its corrected end position. + int scanSingleLineDocComment(); Token scanMultiLineDocComment(); /// Scans a slash '/' and depending on the characters returns the appropriate token Token scanSlash(); From 85fc0b597b102ba9ce7b4d0dbd39f11d933e915a Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Sat, 25 Jan 2020 17:54:41 +0100 Subject: [PATCH 136/160] Adjusts tests to source locations for structured documentation errors. --- .../syntaxTests/natspec/invalid/docstring_parameter.sol | 4 ++-- .../invalid/dosctring_named_return_param_mismatch.sol | 2 +- .../natspec/invalid/dosctring_return_size_mismatch.sol | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_parameter.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_parameter.sol index d0a39af65c88..3e6dc0885c3e 100644 --- a/test/libsolidity/syntaxTests/natspec/invalid/docstring_parameter.sol +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_parameter.sol @@ -7,5 +7,5 @@ contract C { } } // ---- -// DocstringParsingError: Documented parameter "" not found in the parameter list of the function. -// DocstringParsingError: Documented parameter "_" not found in the parameter list of the function. +// DocstringParsingError: (17-101): Documented parameter "" not found in the parameter list of the function. +// DocstringParsingError: (17-101): Documented parameter "_" not found in the parameter list of the function. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol b/test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol index 23f4828e4a1c..c2f29c0387ba 100644 --- a/test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol +++ b/test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol @@ -4,4 +4,4 @@ abstract contract C { function vote(uint id) public virtual returns (uint value); } // ---- -// DocstringParsingError: Documentation tag "@return No value returned" does not contain the name of its return parameter. +// DocstringParsingError: (26-89): Documentation tag "@return No value returned" does not contain the name of its return parameter. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol b/test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol index 23f4828e4a1c..6e95d788d0e8 100644 --- a/test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol +++ b/test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol @@ -2,6 +2,10 @@ abstract contract C { /// @param id Some identifier /// @return No value returned function vote(uint id) public virtual returns (uint value); + + /// @return No value returned + function unvote(uint id) public virtual returns (uint value); } // ---- -// DocstringParsingError: Documentation tag "@return No value returned" does not contain the name of its return parameter. +// DocstringParsingError: (26-89): Documentation tag "@return No value returned" does not contain the name of its return parameter. +// DocstringParsingError: (159-188): Documentation tag "@return No value returned" does not contain the name of its return parameter. From 125d9f6648a5f3fb224f826c844faa832af4ed4b Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Thu, 30 Jan 2020 21:08:43 +0100 Subject: [PATCH 137/160] Adds changelog for locations of structured doc errors. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 683e2928da62..94d9a6b8df0c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,8 @@ Language Features: * Allow contract types and enums as keys for mappings. * Allow function selectors to be used as compile-time constants. + * Report source locations for structured documentation errors. + Compiler Features: From 1d2e1c4f0ad564bd8fc676ccac0602cd31ff1b50 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Thu, 30 Jan 2020 21:49:32 +0100 Subject: [PATCH 138/160] Adds command line test for structured doc errors. --- .../structured_documentation_source_location/err | 11 +++++++++++ .../structured_documentation_source_location/exit | 1 + .../input.sol | 9 +++++++++ 3 files changed, 21 insertions(+) create mode 100644 test/cmdlineTests/structured_documentation_source_location/err create mode 100644 test/cmdlineTests/structured_documentation_source_location/exit create mode 100644 test/cmdlineTests/structured_documentation_source_location/input.sol diff --git a/test/cmdlineTests/structured_documentation_source_location/err b/test/cmdlineTests/structured_documentation_source_location/err new file mode 100644 index 000000000000..1081fdaa89a4 --- /dev/null +++ b/test/cmdlineTests/structured_documentation_source_location/err @@ -0,0 +1,11 @@ +Error: Documentation tag "@return No value returned" does not contain the name of its return parameter. + --> structured_documentation_source_location/input.sol:3:5: + | +3 | /// @param id Some identifier + | ^ (Relevant source part starts here and spans across multiple lines). + +Error: Documentation tag "@return No value returned" does not contain the name of its return parameter. + --> structured_documentation_source_location/input.sol:7:5: + | +7 | /// @return No value returned + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/structured_documentation_source_location/exit b/test/cmdlineTests/structured_documentation_source_location/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/structured_documentation_source_location/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/structured_documentation_source_location/input.sol b/test/cmdlineTests/structured_documentation_source_location/input.sol new file mode 100644 index 000000000000..ceec1354731c --- /dev/null +++ b/test/cmdlineTests/structured_documentation_source_location/input.sol @@ -0,0 +1,9 @@ +pragma solidity >= 0.0; +abstract contract C { + /// @param id Some identifier + /// @return No value returned + function vote(uint id) public virtual returns (uint value); + + /// @return No value returned + function unvote(uint id) public virtual returns (uint value); +} \ No newline at end of file From 017d33cfbe7f38cdb487cf2b1cd580401b7db8c4 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Fri, 14 Feb 2020 14:33:23 +0100 Subject: [PATCH 139/160] Fix typos in filenames within docstring syntax tests. --- ..._return_parameter.sol => docstring_named_return_parameter.sol} | 0 ...ram_mismatch.sol => docstring_named_return_param_mismatch.sol} | 0 ...eturn_size_mismatch.sol => docstring_return_size_mismatch.sol} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename test/libsolidity/syntaxTests/natspec/{dosctring_named_return_parameter.sol => docstring_named_return_parameter.sol} (100%) rename test/libsolidity/syntaxTests/natspec/invalid/{dosctring_named_return_param_mismatch.sol => docstring_named_return_param_mismatch.sol} (100%) rename test/libsolidity/syntaxTests/natspec/invalid/{dosctring_return_size_mismatch.sol => docstring_return_size_mismatch.sol} (100%) diff --git a/test/libsolidity/syntaxTests/natspec/dosctring_named_return_parameter.sol b/test/libsolidity/syntaxTests/natspec/docstring_named_return_parameter.sol similarity index 100% rename from test/libsolidity/syntaxTests/natspec/dosctring_named_return_parameter.sol rename to test/libsolidity/syntaxTests/natspec/docstring_named_return_parameter.sol diff --git a/test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_named_return_param_mismatch.sol similarity index 100% rename from test/libsolidity/syntaxTests/natspec/invalid/dosctring_named_return_param_mismatch.sol rename to test/libsolidity/syntaxTests/natspec/invalid/docstring_named_return_param_mismatch.sol diff --git a/test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_return_size_mismatch.sol similarity index 100% rename from test/libsolidity/syntaxTests/natspec/invalid/dosctring_return_size_mismatch.sol rename to test/libsolidity/syntaxTests/natspec/invalid/docstring_return_size_mismatch.sol From 50adb2943a35f480f55277a201e1d87a911b590d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 14 Feb 2020 17:03:48 +0100 Subject: [PATCH 140/160] Prevent windows line endings in bytecode report. --- scripts/bytecodecompare/prepare_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index 7dbe85965153..74178c758213 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -6,7 +6,7 @@ import json SOLC_BIN = sys.argv[1] -REPORT_FILE = open("report.txt", mode="w", encoding='utf8') +REPORT_FILE = open("report.txt", mode="w", encoding='utf8', newline='\n') for optimize in [False, True]: for f in sorted(glob.glob("*.sol")): From 40b086774ae803a8c49c501b32063b9a5ed33e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 13 Feb 2020 02:28:07 +0100 Subject: [PATCH 141/160] [yul-phaser] Random: Fix tests allocating more counters than necessary - The tests weren't broken, just less efficient. --- test/yulPhaser/Random.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/yulPhaser/Random.cpp b/test/yulPhaser/Random.cpp index c69b91055177..9125e7e56454 100644 --- a/test/yulPhaser/Random.cpp +++ b/test/yulPhaser/Random.cpp @@ -42,8 +42,8 @@ BOOST_AUTO_TEST_CASE(uniformRandomInt_returns_different_values_when_called_multi samples2.push_back(uniformRandomInt(0, numOutcomes - 1)); } - vector counts1(numSamples, 0); - vector counts2(numSamples, 0); + vector counts1(numOutcomes, 0); + vector counts2(numOutcomes, 0); for (uint32_t i = 0; i < numSamples; ++i) { ++counts1[samples1[i]]; @@ -77,8 +77,8 @@ BOOST_AUTO_TEST_CASE(binomialRandomInt_returns_different_values_when_called_mult samples2.push_back(binomialRandomInt(numTrials, successProbability)); } - vector counts1(numSamples, 0); - vector counts2(numSamples, 0); + vector counts1(numTrials, 0); + vector counts2(numTrials, 0); for (uint32_t i = 0; i < numSamples; ++i) { ++counts1[samples1[i]]; From 94538efc0e1e6d24cd1989f457050029e68dba9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 13 Feb 2020 21:33:37 +0100 Subject: [PATCH 142/160] [yul-phaser] Add a file for small utilities used in yul-phaser's tests --- test/CMakeLists.txt | 3 +++ test/yulPhaser/Common.cpp | 18 ++++++++++++++++++ test/yulPhaser/Common.h | 29 +++++++++++++++++++++++++++++ test/yulPhaser/CommonTest.cpp | 29 +++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 test/yulPhaser/Common.cpp create mode 100644 test/yulPhaser/Common.h create mode 100644 test/yulPhaser/CommonTest.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7cf466e0bfd4..0f2801694ba1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -139,6 +139,9 @@ set(libyul_sources detect_stray_source_files("${libyul_sources}" "libyul/") set(yul_phaser_sources + yulPhaser/Common.h + yulPhaser/Common.cpp + yulPhaser/CommonTest.cpp yulPhaser/Chromosome.cpp yulPhaser/Population.cpp yulPhaser/Program.cpp diff --git a/test/yulPhaser/Common.cpp b/test/yulPhaser/Common.cpp new file mode 100644 index 000000000000..bdc99a530ed7 --- /dev/null +++ b/test/yulPhaser/Common.cpp @@ -0,0 +1,18 @@ +/* + 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 . +*/ + +#include diff --git a/test/yulPhaser/Common.h b/test/yulPhaser/Common.h new file mode 100644 index 000000000000..8a7bc9afecdc --- /dev/null +++ b/test/yulPhaser/Common.h @@ -0,0 +1,29 @@ +/* + 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 . +*/ +/** + * Miscellaneous utilities for use in yul-phaser's test cases. + * + * - Generic code that's only used in these particular tests. + * - Convenience functions and wrappers to make tests more concise. + * - Mocks and dummy objects/functions used in multiple test suites. + * + * Note that the code included here may be not as generic, robust and/or complete as it could be + * since it's not meant for production use. If some utility seems useful enough to be moved to + * the normal code base, you should review its implementation before doing so. + */ + +#pragma once diff --git a/test/yulPhaser/CommonTest.cpp b/test/yulPhaser/CommonTest.cpp new file mode 100644 index 000000000000..55ddc4633ad0 --- /dev/null +++ b/test/yulPhaser/CommonTest.cpp @@ -0,0 +1,29 @@ +/* + 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 . +*/ + +#include + +namespace solidity::phaser::test +{ + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(CommonTest) + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} From 957ca005886fb29489de483068761a87e0c9c6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 14 Feb 2020 23:42:01 +0100 Subject: [PATCH 143/160] [yul-phaser] Common: Add mean() and meanSquaredError() --- test/yulPhaser/Common.h | 51 +++++++++++++++++++++++++++++++++++ test/yulPhaser/CommonTest.cpp | 29 ++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/test/yulPhaser/Common.h b/test/yulPhaser/Common.h index 8a7bc9afecdc..a5881d3295d4 100644 --- a/test/yulPhaser/Common.h +++ b/test/yulPhaser/Common.h @@ -27,3 +27,54 @@ */ #pragma once + +#include +#include + +namespace solidity::phaser::test +{ + +// STATISTICAL UTILITIES + +/// Calculates the mean value of a series of samples given in a vector. +/// +/// Supports any integer and real type as a convenience but the precision of the result is limited +/// to the precision of type @a double as all the values are internally converted to it. +/// +/// This is a very simple, naive implementation that's more than enough for tests where we usually +/// deal with relatively short sequences of small, positive integers. It's not numerically stable +/// in more complicated cases. Don't use in production. +template +double mean(std::vector const& _samples) +{ + assert(_samples.size() > 0); + + double sum = 0; + for (T const& sample: _samples) + sum += static_cast(sample); + + return sum / _samples.size(); +} + +/// Calculates the sum of squared differences between @a _expectedValue and the values of a series +/// of samples given in a vector. +/// +/// Supports any integer and real type as a convenience but the precision of the result is limited +/// to the precision of type @a double as all the values are internally converted to it. +/// +/// This is a very simple, naive implementation that's more than enough for tests where we usually +/// deal with relatively short sequences of small, positive integers. It's not numerically stable +/// in more complicated cases. Don't use in production. +template +double meanSquaredError(std::vector const& _samples, double _expectedValue) +{ + assert(_samples.size() > 0); + + double sumOfSquaredDifferences = 0; + for (T const& sample: _samples) + sumOfSquaredDifferences += (sample - _expectedValue) * (sample - _expectedValue); + + return sumOfSquaredDifferences / _samples.size(); +} + +} diff --git a/test/yulPhaser/CommonTest.cpp b/test/yulPhaser/CommonTest.cpp index 55ddc4633ad0..f86d963bd9fc 100644 --- a/test/yulPhaser/CommonTest.cpp +++ b/test/yulPhaser/CommonTest.cpp @@ -15,14 +15,43 @@ along with solidity. If not, see . */ +#include + #include +using namespace boost::test_tools; + namespace solidity::phaser::test { BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(CommonTest) +BOOST_AUTO_TEST_CASE(mean_should_calculate_statistical_mean) +{ + BOOST_TEST(mean({0}) == 0.0); + BOOST_TEST(mean({0, 0, 0, 0}) == 0.0); + BOOST_TEST(mean({5, 5, 5, 5}) == 5.0); + BOOST_TEST(mean({0, 1, 2, 3}) == 1.5); + BOOST_TEST(mean({-4, -3, -2, -1, 0, 1, 2, 3}) == -0.5); + + BOOST_TEST(mean({1.3, 1.1, 0.0, 1.5, 1.1, 2.0, 1.5, 1.5}) == 1.25); +} + +BOOST_AUTO_TEST_CASE(meanSquaredError_should_calculate_average_squared_difference_between_samples_and_expected_value) +{ + BOOST_TEST(meanSquaredError({0}, 0.0) == 0.0); + BOOST_TEST(meanSquaredError({0}, 1.0) == 1.0); + BOOST_TEST(meanSquaredError({0, 0, 0, 0}, 0.0) == 0.0); + BOOST_TEST(meanSquaredError({0, 0, 0, 0}, 1.0) == 1.0); + BOOST_TEST(meanSquaredError({0, 0, 0, 0}, 2.0) == 4.0); + BOOST_TEST(meanSquaredError({5, 5, 5, 5}, 1.0) == 16.0); + BOOST_TEST(meanSquaredError({0, 1, 2, 3}, 2.0) == 1.5); + BOOST_TEST(meanSquaredError({-4, -3, -2, -1, 0, 1, 2, 3}, -4.0) == 17.5); + + BOOST_TEST(meanSquaredError({1.3, 1.1, 0.0, 1.5, 1.1, 2.0, 1.5, 1.5}, 1.0) == 0.3575, tolerance(0.0001)); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() From 46d69d09412c754f1578dc6ba5300b42b8c18d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 14 Feb 2020 19:37:43 +0100 Subject: [PATCH 144/160] [yul-phaser] Common: Add enumerateOptimisationSteps() --- test/yulPhaser/Common.cpp | 16 ++++++++++++++++ test/yulPhaser/Common.h | 8 ++++++++ test/yulPhaser/CommonTest.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/test/yulPhaser/Common.cpp b/test/yulPhaser/Common.cpp index bdc99a530ed7..3cef4ff2bc85 100644 --- a/test/yulPhaser/Common.cpp +++ b/test/yulPhaser/Common.cpp @@ -16,3 +16,19 @@ */ #include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::yul; + +map phaser::test::enumerateOptmisationSteps() +{ + map stepIndices; + size_t i = 0; + for (auto const& nameAndAbbreviation: OptimiserSuite::stepNameToAbbreviationMap()) + stepIndices.insert({nameAndAbbreviation.first, i++}); + + return stepIndices; +} diff --git a/test/yulPhaser/Common.h b/test/yulPhaser/Common.h index a5881d3295d4..a3dd24bce4e5 100644 --- a/test/yulPhaser/Common.h +++ b/test/yulPhaser/Common.h @@ -29,11 +29,19 @@ #pragma once #include +#include +#include #include namespace solidity::phaser::test { +// CHROMOSOME AND POPULATION HELPERS +/// Assigns indices from 0 to N to all optimisation steps available in the OptimiserSuite. +/// This is a convenience helper to make it easier to test their distribution with tools made for +/// integers. +std::map enumerateOptmisationSteps(); + // STATISTICAL UTILITIES /// Calculates the mean value of a series of samples given in a vector. diff --git a/test/yulPhaser/CommonTest.cpp b/test/yulPhaser/CommonTest.cpp index f86d963bd9fc..d7268c669d72 100644 --- a/test/yulPhaser/CommonTest.cpp +++ b/test/yulPhaser/CommonTest.cpp @@ -17,8 +17,14 @@ #include +#include + #include +#include + +using namespace std; +using namespace solidity::yul; using namespace boost::test_tools; namespace solidity::phaser::test @@ -27,6 +33,24 @@ namespace solidity::phaser::test BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(CommonTest) +BOOST_AUTO_TEST_CASE(enumerateOptimisationSteps_should_assing_indices_to_all_available_optimisation_steps) +{ + map stepsAndAbbreviations = OptimiserSuite::stepNameToAbbreviationMap(); + map stepsAndIndices = enumerateOptmisationSteps(); + BOOST_TEST(stepsAndIndices.size() == stepsAndAbbreviations.size()); + + set stepsSoFar; + for (auto& [name, index]: stepsAndIndices) + { + BOOST_TEST(index >= 0); + BOOST_TEST(index <= stepsAndAbbreviations.size()); + BOOST_TEST(stepsAndAbbreviations.count(name) == 1); + BOOST_TEST(stepsSoFar.count(name) == 0); + + stepsSoFar.insert(name); + } +} + BOOST_AUTO_TEST_CASE(mean_should_calculate_statistical_mean) { BOOST_TEST(mean({0}) == 0.0); From 4ee9174454d9e08669ea930cc54edd94bdea42e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Feb 2020 00:49:11 +0100 Subject: [PATCH 145/160] [yul-phaser] Chromosome: Make randomOptimisationStep() public - And add tests now that it's public. --- test/yulPhaser/Chromosome.cpp | 21 +++++++++++++++++++++ tools/yulPhaser/Chromosome.h | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index bd07e1b2e9f0..e8dbbe6c35e4 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -15,7 +15,10 @@ along with solidity. If not, see . */ +#include + #include +#include #include #include @@ -95,6 +98,24 @@ BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_strin BOOST_TEST(toString(chromosome) == "flcCUnDvejsxIOoighTLMrmVatud"); } +BOOST_AUTO_TEST_CASE(randomOptimisationStep_should_return_each_step_with_same_probability) +{ + SimulationRNG::reset(1); + constexpr int samplesPerStep = 100; + constexpr double relativeTolerance = 0.01; + + map stepIndices = enumerateOptmisationSteps(); + vector samples; + for (size_t i = 0; i <= stepIndices.size() * samplesPerStep; ++i) + samples.push_back(stepIndices.at(Chromosome::randomOptimisationStep())); + + const double expectedValue = (stepIndices.size() - 1) / 2.0; + const double variance = (stepIndices.size() * stepIndices.size() - 1) / 12.0; + + BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/Chromosome.h b/tools/yulPhaser/Chromosome.h index 6db99dc4e009..bf91dbd305fc 100644 --- a/tools/yulPhaser/Chromosome.h +++ b/tools/yulPhaser/Chromosome.h @@ -52,9 +52,10 @@ class Chromosome bool operator==(Chromosome const& _other) const { return m_optimisationSteps == _other.m_optimisationSteps; } bool operator!=(Chromosome const& _other) const { return !(*this == _other); } + static std::string const& randomOptimisationStep(); + private: static std::vector allStepNames(); - static std::string const& randomOptimisationStep(); std::vector m_optimisationSteps; }; From b01766c4db46a882e840f308594f83a4321cf06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 12 Feb 2020 11:39:00 +0100 Subject: [PATCH 146/160] [yul-phaser] Gather functions from Random into SimulationRNG class --- test/yulPhaser/Random.cpp | 14 +++++++------- tools/yulPhaser/Chromosome.cpp | 2 +- tools/yulPhaser/Population.h | 2 +- tools/yulPhaser/Random.cpp | 8 ++++---- tools/yulPhaser/Random.h | 16 ++++++++++++++-- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/test/yulPhaser/Random.cpp b/test/yulPhaser/Random.cpp index 9125e7e56454..b9070303ff42 100644 --- a/test/yulPhaser/Random.cpp +++ b/test/yulPhaser/Random.cpp @@ -29,7 +29,7 @@ namespace solidity::phaser::test BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(RandomTest) -BOOST_AUTO_TEST_CASE(uniformRandomInt_returns_different_values_when_called_multiple_times) +BOOST_AUTO_TEST_CASE(uniformInt_returns_different_values_when_called_multiple_times) { constexpr uint32_t numSamples = 1000; constexpr uint32_t numOutcomes = 100; @@ -38,8 +38,8 @@ BOOST_AUTO_TEST_CASE(uniformRandomInt_returns_different_values_when_called_multi vector samples2; for (uint32_t i = 0; i < numSamples; ++i) { - samples1.push_back(uniformRandomInt(0, numOutcomes - 1)); - samples2.push_back(uniformRandomInt(0, numOutcomes - 1)); + samples1.push_back(SimulationRNG::uniformInt(0, numOutcomes - 1)); + samples2.push_back(SimulationRNG::uniformInt(0, numOutcomes - 1)); } vector counts1(numOutcomes, 0); @@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(uniformRandomInt_returns_different_values_when_called_multi BOOST_TEST(counts1 != counts2); } -BOOST_AUTO_TEST_CASE(binomialRandomInt_returns_different_values_when_called_multiple_times) +BOOST_AUTO_TEST_CASE(binomialInt_returns_different_values_when_called_multiple_times) { constexpr uint32_t numSamples = 1000; constexpr uint32_t numTrials = 100; @@ -73,8 +73,8 @@ BOOST_AUTO_TEST_CASE(binomialRandomInt_returns_different_values_when_called_mult vector samples2; for (uint32_t i = 0; i < numSamples; ++i) { - samples1.push_back(binomialRandomInt(numTrials, successProbability)); - samples2.push_back(binomialRandomInt(numTrials, successProbability)); + samples1.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); + samples2.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); } vector counts1(numTrials, 0); @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(binomialRandomInt_returns_different_values_when_called_mult ++counts2[samples2[i]]; } - // See remark for uniformRandomInt() above. Same applies here. + // See remark for uniformInt() above. Same applies here. BOOST_TEST(counts1 != counts2); } diff --git a/tools/yulPhaser/Chromosome.cpp b/tools/yulPhaser/Chromosome.cpp index b332266ba354..c75ad7612358 100644 --- a/tools/yulPhaser/Chromosome.cpp +++ b/tools/yulPhaser/Chromosome.cpp @@ -66,5 +66,5 @@ string const& Chromosome::randomOptimisationStep() { static vector stepNames = allStepNames(); - return stepNames[uniformRandomInt(0, stepNames.size() - 1)]; + return stepNames[SimulationRNG::uniformInt(0, stepNames.size() - 1)]; } diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index e8a38dc14970..4c8eff9fead7 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -61,7 +61,7 @@ class Population std::vector const& individuals() const { return m_individuals; } - static size_t randomChromosomeLength() { return binomialRandomInt(MaxChromosomeLength, 0.5); } + static size_t randomChromosomeLength() { return SimulationRNG::binomialInt(MaxChromosomeLength, 0.5); } static size_t measureFitness(Chromosome const& _chromosome, Program const& _program); friend std::ostream& operator<<(std::ostream& _stream, Population const& _population); diff --git a/tools/yulPhaser/Random.cpp b/tools/yulPhaser/Random.cpp index 645afb39539e..0e4118712506 100644 --- a/tools/yulPhaser/Random.cpp +++ b/tools/yulPhaser/Random.cpp @@ -17,15 +17,15 @@ #include -#include -#include #include +#include #include using namespace solidity; +using namespace solidity::phaser; -uint32_t phaser::uniformRandomInt(uint32_t _min, uint32_t _max) +uint32_t SimulationRNG::uniformInt(uint32_t _min, uint32_t _max) { // TODO: Seed must be configurable static boost::random::mt19937 generator(time(0)); @@ -34,7 +34,7 @@ uint32_t phaser::uniformRandomInt(uint32_t _min, uint32_t _max) return distribution(generator); } -uint32_t phaser::binomialRandomInt(uint32_t _numTrials, double _successProbability) +uint32_t SimulationRNG::binomialInt(uint32_t _numTrials, double _successProbability) { // TODO: Seed must be configurable static boost::random::mt19937 generator(time(0)); diff --git a/tools/yulPhaser/Random.h b/tools/yulPhaser/Random.h index 25091ddef7ec..850cbb01f08c 100644 --- a/tools/yulPhaser/Random.h +++ b/tools/yulPhaser/Random.h @@ -17,12 +17,24 @@ #pragma once +#include + #include namespace solidity::phaser { -uint32_t uniformRandomInt(uint32_t _min, uint32_t _max); -uint32_t binomialRandomInt(uint32_t _numTrials, double _successProbability); +/** + * A class that provides functions for generating random numbers good enough for simulation purposes. + * + * The numbers are not cryptographically secure so do not use this for anything that requires + * them to be truly unpredictable. + */ +class SimulationRNG +{ +public: + static uint32_t uniformInt(uint32_t _min, uint32_t _max); + static uint32_t binomialInt(uint32_t _numTrials, double _successProbability); +}; } From 0d28d5a2870ccfe3c35adfef1d9439180009a112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 15 Feb 2020 03:01:24 +0100 Subject: [PATCH 147/160] [yul-phaser] Rename Random to SimulationRNG (file move) --- test/yulPhaser/{Random.cpp => SimulationRNG.cpp} | 0 tools/yulPhaser/{Random.cpp => SimulationRNG.cpp} | 0 tools/yulPhaser/{Random.h => SimulationRNG.h} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename test/yulPhaser/{Random.cpp => SimulationRNG.cpp} (100%) rename tools/yulPhaser/{Random.cpp => SimulationRNG.cpp} (100%) rename tools/yulPhaser/{Random.h => SimulationRNG.h} (100%) diff --git a/test/yulPhaser/Random.cpp b/test/yulPhaser/SimulationRNG.cpp similarity index 100% rename from test/yulPhaser/Random.cpp rename to test/yulPhaser/SimulationRNG.cpp diff --git a/tools/yulPhaser/Random.cpp b/tools/yulPhaser/SimulationRNG.cpp similarity index 100% rename from tools/yulPhaser/Random.cpp rename to tools/yulPhaser/SimulationRNG.cpp diff --git a/tools/yulPhaser/Random.h b/tools/yulPhaser/SimulationRNG.h similarity index 100% rename from tools/yulPhaser/Random.h rename to tools/yulPhaser/SimulationRNG.h From 342a4e5dee1c36c7cce066bd978686c404b25bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 15 Feb 2020 03:03:22 +0100 Subject: [PATCH 148/160] [yul-phaser] Rename Random to SimulationRNG (fix references) --- test/CMakeLists.txt | 4 ++-- test/yulPhaser/Chromosome.cpp | 2 +- test/yulPhaser/SimulationRNG.cpp | 2 +- tools/CMakeLists.txt | 4 ++-- tools/yulPhaser/Chromosome.cpp | 2 +- tools/yulPhaser/Population.h | 2 +- tools/yulPhaser/SimulationRNG.cpp | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0f2801694ba1..37a78287d51a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -145,7 +145,7 @@ set(yul_phaser_sources yulPhaser/Chromosome.cpp yulPhaser/Population.cpp yulPhaser/Program.cpp - yulPhaser/Random.cpp + yulPhaser/SimulationRNG.cpp # FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries(). # My current workaround is just to include its source files here but this introduces @@ -153,7 +153,7 @@ set(yul_phaser_sources ../tools/yulPhaser/Chromosome.cpp ../tools/yulPhaser/Population.cpp ../tools/yulPhaser/Program.cpp - ../tools/yulPhaser/Random.cpp + ../tools/yulPhaser/SimulationRNG.cpp ) detect_stray_source_files("${yul_phaser_sources}" "yulPhaser/") diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index e8dbbe6c35e4..13f2f6811175 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index b9070303ff42..4722fcb659f7 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index a2aa807af414..cd33c4006069 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -21,8 +21,8 @@ add_executable(yul-phaser yulPhaser/Chromosome.cpp yulPhaser/Program.h yulPhaser/Program.cpp - yulPhaser/Random.h - yulPhaser/Random.cpp + yulPhaser/SimulationRNG.h + yulPhaser/SimulationRNG.cpp ) target_link_libraries(yul-phaser PRIVATE solidity Boost::program_options) diff --git a/tools/yulPhaser/Chromosome.cpp b/tools/yulPhaser/Chromosome.cpp index c75ad7612358..ae30a87e9582 100644 --- a/tools/yulPhaser/Chromosome.cpp +++ b/tools/yulPhaser/Chromosome.cpp @@ -17,7 +17,7 @@ #include -#include +#include #include #include diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index 4c8eff9fead7..276db83ffcd8 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 0e4118712506..8f78c8a6adc0 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include #include From db140a667ab6226d1949eabe5686d753f2057d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 15 Feb 2020 00:23:32 +0100 Subject: [PATCH 149/160] [yul-phaser] SimulationRNG: Use a single, shared and seedable generator --- test/yulPhaser/SimulationRNG.cpp | 66 +++++++++++++++++++++++++++++++ tools/yulPhaser/SimulationRNG.cpp | 18 +++++---- tools/yulPhaser/SimulationRNG.h | 16 ++++++++ 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index 4722fcb659f7..e9b7084a9b98 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -63,6 +63,39 @@ BOOST_AUTO_TEST_CASE(uniformInt_returns_different_values_when_called_multiple_ti BOOST_TEST(counts1 != counts2); } +BOOST_AUTO_TEST_CASE(uniformInt_can_be_reset) +{ + constexpr size_t numSamples = 10; + constexpr uint32_t minValue = 50; + constexpr uint32_t maxValue = 80; + + SimulationRNG::reset(1); + vector samples1; + for (uint32_t i = 0; i < numSamples; ++i) + samples1.push_back(SimulationRNG::uniformInt(minValue, maxValue)); + + vector samples2; + for (uint32_t i = 0; i < numSamples; ++i) + samples2.push_back(SimulationRNG::uniformInt(minValue, maxValue)); + + SimulationRNG::reset(1); + vector samples3; + for (uint32_t i = 0; i < numSamples; ++i) + samples3.push_back(SimulationRNG::uniformInt(minValue, maxValue)); + + SimulationRNG::reset(2); + vector samples4; + for (uint32_t i = 0; i < numSamples; ++i) + samples4.push_back(SimulationRNG::uniformInt(minValue, maxValue)); + + BOOST_TEST(samples1 != samples2); + BOOST_TEST(samples1 == samples3); + BOOST_TEST(samples1 != samples4); + BOOST_TEST(samples2 != samples3); + BOOST_TEST(samples2 != samples4); + BOOST_TEST(samples3 != samples4); +} + BOOST_AUTO_TEST_CASE(binomialInt_returns_different_values_when_called_multiple_times) { constexpr uint32_t numSamples = 1000; @@ -89,6 +122,39 @@ BOOST_AUTO_TEST_CASE(binomialInt_returns_different_values_when_called_multiple_t BOOST_TEST(counts1 != counts2); } +BOOST_AUTO_TEST_CASE(binomialInt_can_be_reset) +{ + constexpr size_t numSamples = 10; + constexpr uint32_t numTrials = 10; + constexpr double successProbability = 0.6; + + SimulationRNG::reset(1); + vector samples1; + for (uint32_t i = 0; i < numSamples; ++i) + samples1.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); + + vector samples2; + for (uint32_t i = 0; i < numSamples; ++i) + samples2.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); + + SimulationRNG::reset(1); + vector samples3; + for (uint32_t i = 0; i < numSamples; ++i) + samples3.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); + + SimulationRNG::reset(2); + vector samples4; + for (uint32_t i = 0; i < numSamples; ++i) + samples4.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); + + BOOST_TEST(samples1 != samples2); + BOOST_TEST(samples1 == samples3); + BOOST_TEST(samples1 != samples4); + BOOST_TEST(samples2 != samples3); + BOOST_TEST(samples2 != samples4); + BOOST_TEST(samples3 != samples4); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 8f78c8a6adc0..6432d5e5bf5d 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -25,20 +25,24 @@ using namespace solidity; using namespace solidity::phaser; +thread_local boost::random::mt19937 SimulationRNG::s_generator(SimulationRNG::generateSeed()); + uint32_t SimulationRNG::uniformInt(uint32_t _min, uint32_t _max) { - // TODO: Seed must be configurable - static boost::random::mt19937 generator(time(0)); boost::random::uniform_int_distribution<> distribution(_min, _max); - - return distribution(generator); + return distribution(s_generator); } uint32_t SimulationRNG::binomialInt(uint32_t _numTrials, double _successProbability) { - // TODO: Seed must be configurable - static boost::random::mt19937 generator(time(0)); boost::random::binomial_distribution<> distribution(_numTrials, _successProbability); + return distribution(s_generator); +} - return distribution(generator); +uint32_t SimulationRNG::generateSeed() +{ + // This is not a secure way to seed the generator but it's good enough for simulation purposes. + // The only thing that matters for us is that the sequence is different on each run and that + // it fits the expected distribution. It does not have to be 100% unpredictable. + return time(0); } diff --git a/tools/yulPhaser/SimulationRNG.h b/tools/yulPhaser/SimulationRNG.h index 850cbb01f08c..e884b4f36899 100644 --- a/tools/yulPhaser/SimulationRNG.h +++ b/tools/yulPhaser/SimulationRNG.h @@ -27,6 +27,10 @@ namespace solidity::phaser /** * A class that provides functions for generating random numbers good enough for simulation purposes. * + * The functions share a common instance of the generator which can be reset with a known seed + * to deterministically generate a given sequence of numbers. Initially the generator is seeded with + * a value from @a generateSeed() which is different on each run. + * * The numbers are not cryptographically secure so do not use this for anything that requires * them to be truly unpredictable. */ @@ -35,6 +39,18 @@ class SimulationRNG public: static uint32_t uniformInt(uint32_t _min, uint32_t _max); static uint32_t binomialInt(uint32_t _numTrials, double _successProbability); + + /// Resets generator to a known state given by the @a seed. Given the same seed, a fixed + /// sequence of calls to the members generating random values is guaranteed to produce the + /// same results. + static void reset(uint32_t seed) { s_generator = boost::random::mt19937(seed); } + + /// Generates a seed that's different on each run of the program. + /// Does **not** use the generator and is not affected by @a reset(). + static uint32_t generateSeed(); + +private: + thread_local static boost::random::mt19937 s_generator; }; } From a8fa332a9c4e65f9fa5432943802804f6b5c5e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 14 Feb 2020 14:53:37 +0100 Subject: [PATCH 150/160] [yul-phaser] SimulationRNG: Rewrite uniformInt() and binomialInt() tests to rely on variance and expected value --- test/yulPhaser/SimulationRNG.cpp | 72 ++++++++++++-------------------- 1 file changed, 27 insertions(+), 45 deletions(-) diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index e9b7084a9b98..3c3098f25f21 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -15,6 +15,8 @@ along with solidity. If not, see . */ +#include + #include #include @@ -31,36 +33,22 @@ BOOST_AUTO_TEST_SUITE(RandomTest) BOOST_AUTO_TEST_CASE(uniformInt_returns_different_values_when_called_multiple_times) { - constexpr uint32_t numSamples = 1000; - constexpr uint32_t numOutcomes = 100; + SimulationRNG::reset(1); + constexpr size_t numSamples = 1000; + constexpr uint32_t minValue = 50; + constexpr uint32_t maxValue = 80; + constexpr double relativeTolerance = 0.05; - vector samples1; - vector samples2; - for (uint32_t i = 0; i < numSamples; ++i) - { - samples1.push_back(SimulationRNG::uniformInt(0, numOutcomes - 1)); - samples2.push_back(SimulationRNG::uniformInt(0, numOutcomes - 1)); - } + // For uniform distribution from range a..b: EX = (a + b) / 2, VarX = ((b - a + 1)^2 - 1) / 12 + constexpr double expectedValue = (minValue + maxValue) / 2.0; + constexpr double variance = ((maxValue - minValue + 1) * (maxValue - minValue + 1) - 1) / 12.0; - vector counts1(numOutcomes, 0); - vector counts2(numOutcomes, 0); + vector samples; for (uint32_t i = 0; i < numSamples; ++i) - { - ++counts1[samples1[i]]; - ++counts2[samples2[i]]; - } - - // This test rules out not only the possibility that the two sequences are the same but also - // that they're just different permutations of the same values. The test is probabilistic so - // it's technically possible for it to fail even if generator is good but the probability is - // so low that it would happen on average once very 10^125 billion years if you repeated it - // every second. The chance is much lower than 1 in 1000^100 / 100!. - // - // This does not really guarantee that the generated numbers have the right distribution or - // or that they don't come in long, repeating sequences but the implementation is very simple - // (it just calls a generator from boost) so our goal here is just to make sure it's used - // properly and we're not getting something totally non-random, e.g. the same number every time. - BOOST_TEST(counts1 != counts2); + samples.push_back(SimulationRNG::uniformInt(minValue, maxValue)); + + BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(uniformInt_can_be_reset) @@ -96,30 +84,24 @@ BOOST_AUTO_TEST_CASE(uniformInt_can_be_reset) BOOST_TEST(samples3 != samples4); } -BOOST_AUTO_TEST_CASE(binomialInt_returns_different_values_when_called_multiple_times) +BOOST_AUTO_TEST_CASE(binomialInt_should_produce_samples_with_right_expected_value_and_variance) { - constexpr uint32_t numSamples = 1000; + SimulationRNG::reset(1); + constexpr size_t numSamples = 1000; constexpr uint32_t numTrials = 100; - constexpr double successProbability = 0.6; + constexpr double successProbability = 0.2; + constexpr double relativeTolerance = 0.05; - vector samples1; - vector samples2; - for (uint32_t i = 0; i < numSamples; ++i) - { - samples1.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); - samples2.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); - } + // For binomial distribution with n trials and success probability p: EX = np, VarX = np(1 - p) + constexpr double expectedValue = numTrials * successProbability; + constexpr double variance = numTrials * successProbability * (1 - successProbability); - vector counts1(numTrials, 0); - vector counts2(numTrials, 0); + vector samples; for (uint32_t i = 0; i < numSamples; ++i) - { - ++counts1[samples1[i]]; - ++counts2[samples2[i]]; - } + samples.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); - // See remark for uniformInt() above. Same applies here. - BOOST_TEST(counts1 != counts2); + BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(binomialInt_can_be_reset) From f29d3655f7ed5bf2aafc7acc860b50277109b9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Feb 2020 00:48:16 +0100 Subject: [PATCH 151/160] [yul-phaser] SimulationRNG: Add bernoulliTrial() --- test/yulPhaser/SimulationRNG.cpp | 51 +++++++++++++++++++++++++++++++ tools/yulPhaser/SimulationRNG.cpp | 8 +++++ tools/yulPhaser/SimulationRNG.h | 1 + 3 files changed, 60 insertions(+) diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index 3c3098f25f21..f158fcbf6aa4 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -31,6 +31,57 @@ namespace solidity::phaser::test BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(RandomTest) +BOOST_AUTO_TEST_CASE(bernoulliTrial_should_produce_samples_with_right_expected_value_and_variance) +{ + SimulationRNG::reset(1); + constexpr size_t numSamples = 10000; + constexpr double successProbability = 0.4; + constexpr double relativeTolerance = 0.05; + + // For bernoulli distribution with success probability p: EX = p, VarX = p(1 - p) + constexpr double expectedValue = successProbability; + constexpr double variance = successProbability * (1 - successProbability); + + vector samples; + for (uint32_t i = 0; i < numSamples; ++i) + samples.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); +} + +BOOST_AUTO_TEST_CASE(bernoulliTrial_can_be_reset) +{ + constexpr size_t numSamples = 10; + constexpr double successProbability = 0.4; + + SimulationRNG::reset(1); + vector samples1; + for (uint32_t i = 0; i < numSamples; ++i) + samples1.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + vector samples2; + for (uint32_t i = 0; i < numSamples; ++i) + samples2.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + SimulationRNG::reset(1); + vector samples3; + for (uint32_t i = 0; i < numSamples; ++i) + samples3.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + SimulationRNG::reset(2); + vector samples4; + for (uint32_t i = 0; i < numSamples; ++i) + samples4.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); + + BOOST_TEST(samples1 != samples2); + BOOST_TEST(samples1 == samples3); + BOOST_TEST(samples1 != samples4); + BOOST_TEST(samples2 != samples3); + BOOST_TEST(samples2 != samples4); + BOOST_TEST(samples3 != samples4); +} + BOOST_AUTO_TEST_CASE(uniformInt_returns_different_values_when_called_multiple_times) { SimulationRNG::reset(1); diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 6432d5e5bf5d..1103db5a8bc5 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -17,6 +17,7 @@ #include +#include #include #include @@ -27,6 +28,13 @@ using namespace solidity::phaser; thread_local boost::random::mt19937 SimulationRNG::s_generator(SimulationRNG::generateSeed()); +bool SimulationRNG::bernoulliTrial(double _successProbability) +{ + boost::random::bernoulli_distribution<> distribution(_successProbability); + + return static_cast(distribution(s_generator)); +} + uint32_t SimulationRNG::uniformInt(uint32_t _min, uint32_t _max) { boost::random::uniform_int_distribution<> distribution(_min, _max); diff --git a/tools/yulPhaser/SimulationRNG.h b/tools/yulPhaser/SimulationRNG.h index e884b4f36899..1e8a1da18b11 100644 --- a/tools/yulPhaser/SimulationRNG.h +++ b/tools/yulPhaser/SimulationRNG.h @@ -37,6 +37,7 @@ namespace solidity::phaser class SimulationRNG { public: + static bool bernoulliTrial(double _successProbability); static uint32_t uniformInt(uint32_t _min, uint32_t _max); static uint32_t binomialInt(uint32_t _numTrials, double _successProbability); From 2f67302ffc1e5af543fd5940ebde5d8fad02e82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 12 Feb 2020 11:49:09 +0100 Subject: [PATCH 152/160] [yul-phaser] main: Add --seed option --- tools/yulPhaser/main.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index bb11b9fcf6ad..b5c522ce7414 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,19 @@ struct CommandLineParsingResult po::variables_map arguments; }; + +void initializeRNG(po::variables_map const& arguments) +{ + uint32_t seed; + if (arguments.count("seed") > 0) + seed = arguments["seed"].as(); + else + seed = SimulationRNG::generateSeed(); + + SimulationRNG::reset(seed); + cout << "Random seed: " << seed << endl; +} + CharStream loadSource(string const& _sourcePath) { assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); @@ -79,6 +93,7 @@ CommandLineParsingResult parseCommandLine(int argc, char** argv) description.add_options() ("help", "Show help message and exit.") ("input-file", po::value()->required(), "Input file") + ("seed", po::value(), "Seed for the random number generator") ; po::positional_options_description positionalDescription; @@ -121,6 +136,8 @@ int main(int argc, char** argv) if (parsingResult.exitCode != 0) return parsingResult.exitCode; + initializeRNG(parsingResult.arguments); + try { runAlgorithm(parsingResult.arguments["input-file"].as()); From 29e5cd89be6e815a42de5abd50ea725b3cf6deb4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 16 Jan 2020 18:56:05 +0100 Subject: [PATCH 153/160] Remove stack counting from Asm Analysis. --- libyul/AsmAnalysis.cpp | 423 ++++++++++------------------ libyul/AsmAnalysis.h | 44 +-- test/libsolidity/InlineAssembly.cpp | 6 +- 3 files changed, 181 insertions(+), 292 deletions(-) diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 32b06b08d37d..89db52d8d4bd 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -45,14 +45,14 @@ using namespace solidity::langutil; bool AsmAnalyzer::analyze(Block const& _block) { - bool success = false; + m_success = true; try { if (!(ScopeFiller(m_info, m_errorReporter))(_block)) return false; - success = (*this)(_block); - if (!success) + (*this)(_block); + if (!m_success) yulAssert(m_errorReporter.hasErrors(), "No success but no error."); } catch (FatalError const&) @@ -60,7 +60,7 @@ bool AsmAnalyzer::analyze(Block const& _block) // This FatalError con occur if the errorReporter has too many errors. yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); } - return success && !m_errorReporter.hasErrors(); + return m_success && !m_errorReporter.hasErrors(); } AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, Object const& _object) @@ -79,127 +79,112 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, return analysisInfo; } -bool AsmAnalyzer::operator()(Literal const& _literal) +vector AsmAnalyzer::operator()(Literal const& _literal) { expectValidType(_literal.type, _literal.location); - ++m_stackHeight; + if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32) - { - m_errorReporter.typeError( + typeError( _literal.location, "String literal too long (" + to_string(_literal.value.str().size()) + " > 32)" ); - return false; - } else if (_literal.kind == LiteralKind::Number && bigint(_literal.value.str()) > u256(-1)) - { - m_errorReporter.typeError( - _literal.location, - "Number literal too large (> 256 bits)" - ); - return false; - } + typeError(_literal.location, "Number literal too large (> 256 bits)"); else if (_literal.kind == LiteralKind::Boolean) yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); - return true; + + return {_literal.type}; } -bool AsmAnalyzer::operator()(Identifier const& _identifier) +vector AsmAnalyzer::operator()(Identifier const& _identifier) { yulAssert(!_identifier.name.empty(), ""); size_t numErrorsBefore = m_errorReporter.errors().size(); - bool success = true; + YulString type = m_dialect.defaultType; + if (m_currentScope->lookup(_identifier.name, GenericVisitor{ [&](Scope::Variable const& _var) { if (!m_activeVariables.count(&_var)) - { - m_errorReporter.declarationError( + declarationError( _identifier.location, "Variable " + _identifier.name.str() + " used before it was declared." ); - success = false; - } - ++m_stackHeight; + type = _var.type; }, [&](Scope::Function const&) { - m_errorReporter.typeError( + typeError( _identifier.location, "Function " + _identifier.name.str() + " used without being called." ); - success = false; } })) { } else { - size_t stackSize(-1); + bool found = false; if (m_resolver) { bool insideFunction = m_currentScope->insideFunction(); - stackSize = m_resolver(_identifier, yul::IdentifierContext::RValue, insideFunction); + size_t stackSize = m_resolver(_identifier, yul::IdentifierContext::RValue, insideFunction); + if (stackSize != size_t(-1)) + { + found = true; + yulAssert(stackSize == 1, "Invalid stack size of external reference."); + } } - if (stackSize == size_t(-1)) + if (!found) { // Only add an error message if the callback did not do it. if (numErrorsBefore == m_errorReporter.errors().size()) - m_errorReporter.declarationError(_identifier.location, "Identifier not found."); - success = false; + declarationError(_identifier.location, "Identifier not found."); + m_success = false; } - m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize; } - return success; + + return {type}; } -bool AsmAnalyzer::operator()(ExpressionStatement const& _statement) +void AsmAnalyzer::operator()(ExpressionStatement const& _statement) { - int initialStackHeight = m_stackHeight; - bool success = std::visit(*this, _statement.expression); - if (success && m_stackHeight != initialStackHeight) - { - string msg = + vector types = std::visit(*this, _statement.expression); + if (m_success && !types.empty()) + typeError(_statement.location, "Top-level expressions are not supposed to return values (this expression returns " + - to_string(m_stackHeight - initialStackHeight) + + to_string(types.size()) + " value" + - (m_stackHeight - initialStackHeight == 1 ? "" : "s") + - "). Use ``pop()`` or assign them."; - m_errorReporter.error(Error::Type::TypeError, _statement.location, msg); - success = false; - } - return success; + (types.size() == 1 ? "" : "s") + + "). Use ``pop()`` or assign them." + ); } -bool AsmAnalyzer::operator()(Assignment const& _assignment) +void AsmAnalyzer::operator()(Assignment const& _assignment) { yulAssert(_assignment.value, ""); - int const expectedItems = _assignment.variableNames.size(); - yulAssert(expectedItems >= 1, ""); - int const stackHeight = m_stackHeight; - bool success = std::visit(*this, *_assignment.value); - if ((m_stackHeight - stackHeight) != expectedItems) - { - m_errorReporter.declarationError( + size_t const numVariables = _assignment.variableNames.size(); + yulAssert(numVariables >= 1, ""); + + vector types = std::visit(*this, *_assignment.value); + + if (types.size() != numVariables) + declarationError( _assignment.location, "Variable count does not match number of values (" + - to_string(expectedItems) + + to_string(numVariables) + " vs. " + - to_string(m_stackHeight - stackHeight) + + to_string(types.size()) + ")" ); - return false; - } - for (auto const& variableName: _assignment.variableNames) - if (!checkAssignment(variableName, 1)) - success = false; - return success; + + for (size_t i = 0; i < numVariables; ++i) + checkAssignment(_assignment.variableNames[i]); } -bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) +void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) { - bool success = true; - int const numVariables = _varDecl.variables.size(); + size_t const numVariables = _varDecl.variables.size(); if (m_resolver) for (auto const& variable: _varDecl.variables) // Call the resolver for variable declarations to allow it to raise errors on shadowing. @@ -210,35 +195,27 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) ); if (_varDecl.value) { - int const stackHeight = m_stackHeight; - success = std::visit(*this, *_varDecl.value); - int numValues = m_stackHeight - stackHeight; - if (numValues != numVariables) - { - m_errorReporter.declarationError(_varDecl.location, + vector types = std::visit(*this, *_varDecl.value); + if (types.size() != numVariables) + declarationError(_varDecl.location, "Variable count mismatch: " + to_string(numVariables) + " variables and " + - to_string(numValues) + + to_string(types.size()) + " values." ); - // Adjust stack height to avoid misleading additional errors. - m_stackHeight += numVariables - numValues; - return false; - } } - else - m_stackHeight += numVariables; - for (auto const& variable: _varDecl.variables) + for (TypedName const& variable: _varDecl.variables) { expectValidType(variable.type, variable.location); - m_activeVariables.insert(&std::get(m_currentScope->identifiers.at(variable.name))); + m_activeVariables.insert(&std::get( + m_currentScope->identifiers.at(variable.name)) + ); } - return success; } -bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) +void AsmAnalyzer::operator()(FunctionDefinition const& _funDef) { yulAssert(!_funDef.name.empty(), ""); Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); @@ -250,112 +227,95 @@ bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) m_activeVariables.insert(&std::get(varScope.identifiers.at(var.name))); } - int const stackHeight = m_stackHeight; - m_stackHeight = _funDef.parameters.size() + _funDef.returnVariables.size(); - - bool success = (*this)(_funDef.body); - - m_stackHeight = stackHeight; - return success; + (*this)(_funDef.body); } -bool AsmAnalyzer::operator()(FunctionCall const& _funCall) +vector AsmAnalyzer::operator()(FunctionCall const& _funCall) { yulAssert(!_funCall.functionName.name.empty(), ""); - bool success = true; - size_t parameters = 0; - size_t returns = 0; + vector const* parameterTypes = nullptr; + vector const* returnTypes = nullptr; bool needsLiteralArguments = false; + if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name)) { - // TODO: compare types, too - parameters = f->parameters.size(); - returns = f->returns.size(); + parameterTypes = &f->parameters; + returnTypes = &f->returns; if (f->literalArguments) needsLiteralArguments = true; } else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{ [&](Scope::Variable const&) { - m_errorReporter.typeError( + typeError( _funCall.functionName.location, "Attempt to call variable instead of function." ); - success = false; }, [&](Scope::Function const& _fun) { - /// TODO: compare types too - parameters = _fun.arguments.size(); - returns = _fun.returns.size(); + parameterTypes = &_fun.arguments; + returnTypes = &_fun.returns; } })) { if (!warnOnInstructions(_funCall.functionName.name.str(), _funCall.functionName.location)) - m_errorReporter.declarationError(_funCall.functionName.location, "Function not found."); - success = false; + declarationError(_funCall.functionName.location, "Function not found."); + m_success = false; } - if (success) - if (_funCall.arguments.size() != parameters) - { - m_errorReporter.typeError( - _funCall.functionName.location, - "Function expects " + - to_string(parameters) + - " arguments but got " + - to_string(_funCall.arguments.size()) + "." - ); - success = false; - } + if (parameterTypes && _funCall.arguments.size() != parameterTypes->size()) + typeError( + _funCall.functionName.location, + "Function expects " + + to_string(parameterTypes->size()) + + " arguments but got " + + to_string(_funCall.arguments.size()) + "." + ); + vector argTypes; for (auto const& arg: _funCall.arguments | boost::adaptors::reversed) { - if (!expectExpression(arg)) - success = false; - else if (needsLiteralArguments) + argTypes.emplace_back(expectExpression(arg)); + + if (needsLiteralArguments) { if (!holds_alternative(arg)) - m_errorReporter.typeError( + typeError( _funCall.functionName.location, "Function expects direct literals as arguments." ); else if (!m_dataNames.count(std::get(arg).value)) - m_errorReporter.typeError( + typeError( _funCall.functionName.location, "Unknown data object \"" + std::get(arg).value.str() + "\"." ); } } - // Use argument size instead of parameter count to avoid misleading errors. - m_stackHeight += int(returns) - int(_funCall.arguments.size()); - return success; + + if (m_success) + { + yulAssert(parameterTypes && parameterTypes->size() == argTypes.size(), ""); + yulAssert(returnTypes, ""); + return *returnTypes; + } + else if (returnTypes) + return vector(returnTypes->size(), m_dialect.defaultType); + else + return {}; } -bool AsmAnalyzer::operator()(If const& _if) +void AsmAnalyzer::operator()(If const& _if) { - bool success = true; - - int const initialHeight = m_stackHeight; - if (!expectExpression(*_if.condition)) - success = false; - - m_stackHeight = initialHeight; - - if (!(*this)(_if.body)) - success = false; + expectExpression(*_if.condition); - return success; + (*this)(_if.body); } -bool AsmAnalyzer::operator()(Switch const& _switch) +void AsmAnalyzer::operator()(Switch const& _switch) { yulAssert(_switch.expression, ""); - bool success = true; - - int const initialHeight = m_stackHeight; - if (!expectExpression(*_switch.expression)) - success = false; + expectExpression(*_switch.expression); YulString caseType; bool mismatchingTypes = false; @@ -370,7 +330,6 @@ bool AsmAnalyzer::operator()(Switch const& _switch) break; } } - if (mismatchingTypes) m_errorReporter.typeError( _switch.location, @@ -382,191 +341,103 @@ bool AsmAnalyzer::operator()(Switch const& _switch) { if (_case.value) { - int const initialStackHeight = m_stackHeight; - bool isCaseValueValid = true; - // We cannot use "expectExpression" here because *_case.value is not a - // Statement and would be converted to a Statement otherwise. - if (!(*this)(*_case.value)) - { - isCaseValueValid = false; - success = false; - } - expectDeposit(1, initialStackHeight, _case.value->location); - m_stackHeight--; + // We cannot use "expectExpression" here because *_case.value is not an + // Expression and would be converted to an Expression otherwise. + (*this)(*_case.value); - // If the case value is not valid, we should not insert it into cases. - yulAssert(isCaseValueValid || m_errorReporter.hasErrors(), "Invalid case value."); /// Note: the parser ensures there is only one default case - if (isCaseValueValid && !cases.insert(valueOfLiteral(*_case.value)).second) - { - m_errorReporter.declarationError( - _case.location, - "Duplicate case defined." - ); - success = false; - } + if (m_success && !cases.insert(valueOfLiteral(*_case.value)).second) + declarationError(_case.location, "Duplicate case defined."); } - if (!(*this)(_case.body)) - success = false; + (*this)(_case.body); } - - m_stackHeight = initialHeight; - - return success; } -bool AsmAnalyzer::operator()(ForLoop const& _for) +void AsmAnalyzer::operator()(ForLoop const& _for) { yulAssert(_for.condition, ""); Scope* outerScope = m_currentScope; - int const initialHeight = m_stackHeight; + (*this)(_for.pre); - bool success = true; - if (!(*this)(_for.pre)) - success = false; // The block was closed already, but we re-open it again and stuff the // condition, the body and the post part inside. - m_stackHeight += scope(&_for.pre).numberOfVariables(); m_currentScope = &scope(&_for.pre); - if (!expectExpression(*_for.condition)) - success = false; - - m_stackHeight--; + expectExpression(*_for.condition); // backup outer for-loop & create new state auto outerForLoop = m_currentForLoop; m_currentForLoop = &_for; - if (!(*this)(_for.body)) - success = false; - - if (!(*this)(_for.post)) - success = false; + (*this)(_for.body); + (*this)(_for.post); - m_stackHeight = initialHeight; m_currentScope = outerScope; m_currentForLoop = outerForLoop; - - return success; } -bool AsmAnalyzer::operator()(Block const& _block) +void AsmAnalyzer::operator()(Block const& _block) { - bool success = true; auto previousScope = m_currentScope; m_currentScope = &scope(&_block); - int const initialStackHeight = m_stackHeight; - for (auto const& s: _block.statements) - if (!std::visit(*this, s)) - success = false; - - m_stackHeight -= scope(&_block).numberOfVariables(); - - int const stackDiff = m_stackHeight - initialStackHeight; - if (success && stackDiff != 0) - { - m_errorReporter.declarationError( - _block.location, - "Unbalanced stack at the end of a block: " + - ( - stackDiff > 0 ? - to_string(stackDiff) + string(" surplus item(s).") : - to_string(-stackDiff) + string(" missing item(s).") - ) - ); - success = false; - } + std::visit(*this, s); m_currentScope = previousScope; - return success; } -bool AsmAnalyzer::expectExpression(Expression const& _expr) +YulString AsmAnalyzer::expectExpression(Expression const& _expr) { - bool success = true; - int const initialHeight = m_stackHeight; - if (!std::visit(*this, _expr)) - success = false; - if (success && !expectDeposit(1, initialHeight, locationOf(_expr))) - success = false; - return success; -} - -bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location) -{ - if (m_stackHeight - _oldHeight != _deposit) - { - m_errorReporter.typeError( - _location, - "Expected expression to return one item to the stack, but did return " + - to_string(m_stackHeight - _oldHeight) + - " items." + vector types = std::visit(*this, _expr); + if (types.size() != 1) + typeError( + locationOf(_expr), + "Expected expression to evaluate to one value, but got " + + to_string(types.size()) + + " values instead." ); - return false; - } - return true; + return types.empty() ? m_dialect.defaultType : types.front(); } -bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize) +void AsmAnalyzer::checkAssignment(Identifier const& _variable) { yulAssert(!_variable.name.empty(), ""); - bool success = true; size_t numErrorsBefore = m_errorReporter.errors().size(); - size_t variableSize(-1); + bool found = false; if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) { // Check that it is a variable if (!holds_alternative(*var)) - { - m_errorReporter.typeError(_variable.location, "Assignment requires variable."); - success = false; - } + typeError(_variable.location, "Assignment requires variable."); else if (!m_activeVariables.count(&std::get(*var))) - { - m_errorReporter.declarationError( + declarationError( _variable.location, "Variable " + _variable.name.str() + " used before it was declared." ); - success = false; - } - variableSize = 1; + found = true; } else if (m_resolver) { bool insideFunction = m_currentScope->insideFunction(); - variableSize = m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction); + size_t variableSize = m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction); + if (variableSize != size_t(-1)) + { + found = true; + yulAssert(variableSize == 1, "Invalid stack size of external reference."); + } } - if (variableSize == size_t(-1)) + + if (!found) { + m_success = false; // Only add message if the callback did not. if (numErrorsBefore == m_errorReporter.errors().size()) - m_errorReporter.declarationError(_variable.location, "Variable not found or variable not lvalue."); - success = false; - } - if (_valueSize == size_t(-1)) - _valueSize = variableSize == size_t(-1) ? 1 : variableSize; - - m_stackHeight -= _valueSize; - - if (_valueSize != variableSize && variableSize != size_t(-1)) - { - m_errorReporter.typeError( - _variable.location, - "Variable size (" + - to_string(variableSize) + - ") and value size (" + - to_string(_valueSize) + - ") do not match." - ); - success = false; + declarationError(_variable.location, "Variable not found or variable not lvalue."); } - return success; } Scope& AsmAnalyzer::scope(Block const* _block) @@ -603,7 +474,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); auto errorForVM = [=](string const& vmKindMessage) { - m_errorReporter.typeError( + typeError( _location, "The \"" + boost::to_lower_copy(instructionInfo(_instr).name) @@ -664,9 +535,23 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation "incorrect stack access. Because of that they are disallowed in strict assembly. " "Use functions, \"switch\", \"if\" or \"for\" statements instead." ); + m_success = false; } else return false; return true; } + +void AsmAnalyzer::typeError(SourceLocation const& _location, string const& _description) +{ + m_errorReporter.typeError(_location, _description); + m_success = false; +} + +void AsmAnalyzer::declarationError(SourceLocation const& _location, string const& _description) +{ + m_errorReporter.declarationError(_location, _description); + m_success = false; +} + diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index bd99b50c210a..dded6d5ab2be 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -77,36 +77,40 @@ class AsmAnalyzer /// Asserts on failure. static AsmAnalysisInfo analyzeStrictAssertCorrect(Dialect const& _dialect, Object const& _object); - bool operator()(Literal const& _literal); - bool operator()(Identifier const&); - bool operator()(ExpressionStatement const&); - bool operator()(Assignment const& _assignment); - bool operator()(VariableDeclaration const& _variableDeclaration); - bool operator()(FunctionDefinition const& _functionDefinition); - bool operator()(FunctionCall const& _functionCall); - bool operator()(If const& _if); - bool operator()(Switch const& _switch); - bool operator()(ForLoop const& _forLoop); - bool operator()(Break const&) { return true; } - bool operator()(Continue const&) { return true; } - bool operator()(Leave const&) { return true; } - bool operator()(Block const& _block); + std::vector operator()(Literal const& _literal); + std::vector operator()(Identifier const&); + void operator()(ExpressionStatement const&); + void operator()(Assignment const& _assignment); + void operator()(VariableDeclaration const& _variableDeclaration); + void operator()(FunctionDefinition const& _functionDefinition); + std::vector operator()(FunctionCall const& _functionCall); + void operator()(If const& _if); + void operator()(Switch const& _switch); + void operator()(ForLoop const& _forLoop); + void operator()(Break const&) { } + void operator()(Continue const&) { } + void operator()(Leave const&) { } + void operator()(Block const& _block); private: - /// Visits the statement and expects it to deposit one item onto the stack. - bool expectExpression(Expression const& _expr); + /// Visits the expression, expects that it evaluates to exactly one value and + /// returns the type. Reports errors on errors and returns the default type. + YulString expectExpression(Expression const& _expr); bool expectDeposit(int _deposit, int _oldHeight, langutil::SourceLocation const& _location); - /// Verifies that a variable to be assigned to exists and has the same size - /// as the value, @a _valueSize, unless that is equal to -1. - bool checkAssignment(Identifier const& _assignment, size_t _valueSize = size_t(-1)); + /// Verifies that a variable to be assigned to exists and can be assigned to. + void checkAssignment(Identifier const& _variable); Scope& scope(Block const* _block); void expectValidType(YulString _type, langutil::SourceLocation const& _location); bool warnOnInstructions(evmasm::Instruction _instr, langutil::SourceLocation const& _location); bool warnOnInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location); - int m_stackHeight = 0; + void typeError(langutil::SourceLocation const& _location, std::string const& _description); + void declarationError(langutil::SourceLocation const& _location, std::string const& _description); + + /// Success-flag, can be set to false at any time. + bool m_success = true; yul::ExternalIdentifierAccess::Resolver m_resolver; Scope* m_currentScope = nullptr; /// Variables that are active at the current point in assembly (as opposed to diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 4ca7130545fb..08e296830be0 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(if_statement_invalid) { CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected '(' but got '{'"); BOOST_CHECK("{ if calldatasize() {}"); - CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items"); + CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead."); CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected '{' but got reserved keyword 'let'"); } @@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(switch_invalid_expression) { CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal or identifier expected."); CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' but got reserved keyword 'default'"); - CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items"); + CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead."); } BOOST_AUTO_TEST_CASE(switch_default_before_case) @@ -352,7 +352,7 @@ BOOST_AUTO_TEST_CASE(for_invalid_expression) CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected '{' but got 'Number'"); CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected '{' but got 'Number'"); CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' but got '{'"); - CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items"); + CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead."); } BOOST_AUTO_TEST_CASE(for_visibility) From c50631d4e252b223be6216f32c52ec57cafd7652 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 17 Feb 2020 12:56:44 +0100 Subject: [PATCH 154/160] Update tests. --- test/libsolidity/InlineAssembly.cpp | 6 +++--- test/libsolidity/SolidityNameAndTypeResolution.cpp | 9 +++++++++ test/libsolidity/ViewPureChecker.cpp | 4 +--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 08e296830be0..2a1b72e500da 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -732,9 +732,9 @@ BOOST_AUTO_TEST_CASE(shift_constantinople_warning) { if (solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; - CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", TypeError, "The \"shl\" instruction is only available for Constantinople-compatible VMs"); - CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", TypeError, "The \"shr\" instruction is only available for Constantinople-compatible VMs"); - CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", TypeError, "The \"sar\" instruction is only available for Constantinople-compatible VMs"); + CHECK_PARSE_WARNING("{ shl(10, 32) }", TypeError, "The \"shl\" instruction is only available for Constantinople-compatible VMs"); + CHECK_PARSE_WARNING("{ shr(10, 32) }", TypeError, "The \"shr\" instruction is only available for Constantinople-compatible VMs"); + CHECK_PARSE_WARNING("{ sar(10, 32) }", TypeError, "The \"sar\" instruction is only available for Constantinople-compatible VMs"); } BOOST_AUTO_TEST_CASE(jump_error) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b19644a4cf63..b66b5aad6274 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -387,7 +387,10 @@ BOOST_AUTO_TEST_CASE(returndatasize_as_variable) {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} }); if (!solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) + { expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible VMs"))); + expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("Expected expression to evaluate to one value, but got 0 values instead."))); + } CHECK_ALLOW_MULTI(text, expectations); } @@ -402,7 +405,10 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} }); if (!solidity::test::CommonOptions::get().evmVersion().hasCreate2()) + { expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("\"create2\" instruction is only available for Constantinople-compatible VMs"))); + expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("Expected expression to evaluate to one value, but got 0 values instead."))); + } CHECK_ALLOW_MULTI(text, expectations); } @@ -417,7 +423,10 @@ BOOST_AUTO_TEST_CASE(extcodehash_as_variable) {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} }); if (!solidity::test::CommonOptions::get().evmVersion().hasExtCodeHash()) + { expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("\"extcodehash\" instruction is only available for Constantinople-compatible VMs"))); + expectations.emplace_back(make_pair(Error::Type::TypeError, std::string("Expected expression to evaluate to one value, but got 0 values instead."))); + } CHECK_ALLOW_MULTI(text, expectations); } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 3bde18d27a77..050fa573d546 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -121,9 +121,7 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) } } )"; - if (!solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) - CHECK_ERROR(text, TypeError, "\"staticcall\" instruction is only available for Byzantium-compatible"); - else + if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) CHECK_SUCCESS_NO_WARNINGS(text); } From 6db0d5009489d869e4d8510952a537ab16956fd2 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Mon, 17 Feb 2020 12:44:39 -0500 Subject: [PATCH 155/160] Don't use identifiers starting with an underscore followed by an uppercase letter --- libevmasm/CommonSubexpressionEliminator.h | 12 ++--- libevmasm/KnownState.cpp | 2 +- libsolidity/analysis/NameAndTypeResolver.cpp | 22 ++++----- libsolidity/analysis/NameAndTypeResolver.h | 4 +- libsolidity/ast/AST.h | 12 ++--- libsolidity/codegen/ABIFunctions.h | 2 +- libsolidity/codegen/ExpressionCompiler.h | 10 ++-- libsolutil/Assertions.h | 4 +- libsolutil/CommonData.h | 8 +-- libsolutil/CommonIO.cpp | 8 +-- libsolutil/CommonIO.h | 4 +- libsolutil/vector_ref.h | 52 ++++++++++---------- libyul/optimiser/BlockHasher.cpp | 4 +- test/ExecutionFramework.h | 4 +- 14 files changed, 74 insertions(+), 74 deletions(-) diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index 0e6f6ee36d80..ce9c898ee4e0 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -69,8 +69,8 @@ class CommonSubexpressionEliminator /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// item that must be fed into a new instance of the eliminator. /// @param _msizeImportant if false, do not consider modification of MSIZE a side-effect - template - _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end, bool _msizeImportant); + template + AssemblyItemIterator feedItems(AssemblyItemIterator _iterator, AssemblyItemIterator _end, bool _msizeImportant); /// @returns the resulting items after optimization. AssemblyItems getOptimizedItems(); @@ -169,10 +169,10 @@ class CSECodeGenerator std::map m_targetStack; }; -template -_AssemblyItemIterator CommonSubexpressionEliminator::feedItems( - _AssemblyItemIterator _iterator, - _AssemblyItemIterator _end, +template +AssemblyItemIterator CommonSubexpressionEliminator::feedItems( + AssemblyItemIterator _iterator, + AssemblyItemIterator _end, bool _msizeImportant ) { diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index b6198e994423..7e447a9a1fd6 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -179,7 +179,7 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool /// Helper function for KnownState::reduceToCommonKnowledge, removes everything from /// _this which is not in or not equal to the value in _other. -template void intersect(_Mapping& _this, _Mapping const& _other) +template void intersect(Mapping& _this, Mapping const& _other) { for (auto it = _this.begin(); it != _this.end();) if (_other.count(it->first) && _other.at(it->first) == it->second) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 76c388f3f878..3643c64019b8 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -405,13 +405,13 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) _contract.annotation().contractDependencies.insert(result.begin() + 1, result.end()); } -template -vector<_T const*> NameAndTypeResolver::cThreeMerge(list>& _toMerge) +template +vector NameAndTypeResolver::cThreeMerge(list>& _toMerge) { // returns true iff _candidate appears only as last element of the lists - auto appearsOnlyAtHead = [&](_T const* _candidate) -> bool + auto appearsOnlyAtHead = [&](T const* _candidate) -> bool { - for (list<_T const*> const& bases: _toMerge) + for (list const& bases: _toMerge) { solAssert(!bases.empty(), ""); if (find(++bases.begin(), bases.end(), _candidate) != bases.end()) @@ -420,9 +420,9 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list>& _toMer return true; }; // returns the next candidate to append to the linearized list or nullptr on failure - auto nextCandidate = [&]() -> _T const* + auto nextCandidate = [&]() -> T const* { - for (list<_T const*> const& bases: _toMerge) + for (list const& bases: _toMerge) { solAssert(!bases.empty(), ""); if (appearsOnlyAtHead(bases.front())) @@ -431,7 +431,7 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list>& _toMer return nullptr; }; // removes the given contract from all lists - auto removeCandidate = [&](_T const* _candidate) + auto removeCandidate = [&](T const* _candidate) { for (auto it = _toMerge.begin(); it != _toMerge.end();) { @@ -443,13 +443,13 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list>& _toMer } }; - _toMerge.remove_if([](list<_T const*> const& _bases) { return _bases.empty(); }); - vector<_T const*> result; + _toMerge.remove_if([](list const& _bases) { return _bases.empty(); }); + vector result; while (!_toMerge.empty()) { - _T const* candidate = nextCandidate(); + T const* candidate = nextCandidate(); if (!candidate) - return vector<_T const*>(); + return vector(); result.push_back(candidate); removeCandidate(candidate); } diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index b8ae2fefb423..dc4d32bb9dfc 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -122,8 +122,8 @@ class NameAndTypeResolver: private boost::noncopyable void linearizeBaseContracts(ContractDefinition& _contract); /// Computes the C3-merge of the given list of lists of bases. /// @returns the linearized vector or an empty vector if linearization is not possible. - template - static std::vector<_T const*> cThreeMerge(std::list>& _toMerge); + template + static std::vector cThreeMerge(std::list>& _toMerge); /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, /// where nullptr denotes the global scope. Note that structs are not scope since they do diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index c0c29be7d198..336c221c6767 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -88,8 +88,8 @@ class ASTNode: private boost::noncopyable } /// @returns a copy of the vector containing only the nodes which derive from T. - template - static std::vector<_T const*> filteredNodes(std::vector> const& _nodes); + template + static std::vector filteredNodes(std::vector> const& _nodes); /// Returns the source code location of this node. SourceLocation const& location() const { return m_location; } @@ -121,12 +121,12 @@ class ASTNode: private boost::noncopyable SourceLocation m_location; }; -template -std::vector<_T const*> ASTNode::filteredNodes(std::vector> const& _nodes) +template +std::vector ASTNode::filteredNodes(std::vector> const& _nodes) { - std::vector<_T const*> ret; + std::vector ret; for (auto const& n: _nodes) - if (auto const* nt = dynamic_cast<_T const*>(n.get())) + if (auto const* nt = dynamic_cast(n.get())) ret.push_back(nt); return ret; } diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index a0b8333df9e5..ce3efe6eac37 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -204,7 +204,7 @@ class ABIFunctions /// @param _fromMemory if decoding from memory instead of from calldata /// @param _forUseOnStack if the decoded value is stored on stack or in memory. std::string abiDecodingFunction( - Type const& _Type, + Type const& _type, bool _fromMemory, bool _forUseOnStack ); diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 33fb2696fe90..f75fbc8ab75b 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -125,8 +125,8 @@ class ExpressionCompiler: private ASTConstVisitor void setLValueToStorageItem(Expression const& _expression); /// Sets the current LValue to a new LValue constructed from the arguments. /// Also retrieves the value if it was not requested by @a _expression. - template - void setLValue(Expression const& _expression, _Arguments const&... _arguments); + template + void setLValue(Expression const& _expression, Arguments const&... _arguments); /// @returns true if the operator applied to the given type requires a cleanup prior to the /// operation. @@ -143,11 +143,11 @@ class ExpressionCompiler: private ASTConstVisitor }; -template -void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments const&... _arguments) +template +void ExpressionCompiler::setLValue(Expression const& _expression, Arguments const&... _arguments) { solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one."); - std::unique_ptr<_LValueType> lvalue = std::make_unique<_LValueType>(m_context, _arguments...); + std::unique_ptr lvalue = std::make_unique(m_context, _arguments...); if (_expression.annotation().lValueRequested) m_currentLValue = move(lvalue); else diff --git a/libsolutil/Assertions.h b/libsolutil/Assertions.h index c0c330835aa9..1c5a14008a93 100644 --- a/libsolutil/Assertions.h +++ b/libsolutil/Assertions.h @@ -40,12 +40,12 @@ namespace solidity::util /// Assertion that throws an exception containing the given description if it is not met. /// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong."); /// Do NOT supply an exception object as the second parameter. -#define assertThrow(_condition, _ExceptionType, _description) \ +#define assertThrow(_condition, _exceptionType, _description) \ do \ { \ if (!(_condition)) \ ::boost::throw_exception( \ - _ExceptionType() << \ + _exceptionType() << \ ::solidity::util::errinfo_comment(_description) << \ ::boost::throw_function(ETH_FUNC) << \ ::boost::throw_file(__FILE__) << \ diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index 5afb654705e3..f969ad2127d7 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -249,14 +249,14 @@ inline void toBigEndian(T _val, Out& o_out) } /// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. -/// @a _In will typically be either std::string or bytes. +/// @a In will typically be either std::string or bytes. /// @a T will typically by unsigned, u160, u256 or bigint. -template -inline T fromBigEndian(_In const& _bytes) +template +inline T fromBigEndian(In const& _bytes) { T ret = (T)0; for (auto i: _bytes) - ret = (T)((ret << 8) | (uint8_t)(typename std::make_unsigned::type)i); + ret = (T)((ret << 8) | (uint8_t)(typename std::make_unsigned::type)i); return ret; } inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } diff --git a/libsolutil/CommonIO.cpp b/libsolutil/CommonIO.cpp index 0eca857203f2..ec6c33538f61 100644 --- a/libsolutil/CommonIO.cpp +++ b/libsolutil/CommonIO.cpp @@ -40,11 +40,11 @@ using namespace solidity::util; namespace { -template -inline _T readFile(std::string const& _file) +template +inline T readFile(std::string const& _file) { - _T ret; - size_t const c_elementSize = sizeof(typename _T::value_type); + T ret; + size_t const c_elementSize = sizeof(typename T::value_type); std::ifstream is(_file, std::ifstream::binary); if (!is) return ret; diff --git a/libsolutil/CommonIO.h b/libsolutil/CommonIO.h index 15291df337aa..be763c42f2c1 100644 --- a/libsolutil/CommonIO.h +++ b/libsolutil/CommonIO.h @@ -42,8 +42,8 @@ std::string readStandardInput(); int readStandardInputChar(); /// Converts arbitrary value to string representation using std::stringstream. -template -std::string toString(_T const& _t) +template +std::string toString(T const& _t) { std::ostringstream o; o << _t; diff --git a/libsolutil/vector_ref.h b/libsolutil/vector_ref.h index 2c98bce250ef..3bb71f35e8de 100644 --- a/libsolutil/vector_ref.h +++ b/libsolutil/vector_ref.h @@ -16,63 +16,63 @@ namespace solidity::util /** * A modifiable reference to an existing object or vector in memory. */ -template +template class vector_ref { public: - using value_type = _T; - using element_type = _T; - using mutable_value_type = typename std::conditional::value, typename std::remove_const<_T>::type, _T>::type; - using string_type = typename std::conditional::value, std::string const, std::string>::type; - using vector_type = typename std::conditional::value, std::vector::type> const, std::vector<_T>>::type; - using iterator = _T*; - using const_iterator = _T const*; + using value_type = T; + using element_type = T; + using mutable_value_type = typename std::conditional::value, typename std::remove_const::type, T>::type; + using string_type = typename std::conditional::value, std::string const, std::string>::type; + using vector_type = typename std::conditional::value, std::vector::type> const, std::vector>::type; + using iterator = T*; + using const_iterator = T const*; static_assert(std::is_pod::value, "vector_ref can only be used with PODs due to its low-level treatment of data."); vector_ref(): m_data(nullptr), m_count(0) {} /// Creates a new vector_ref to point to @a _count elements starting at @a _data. - vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} + vector_ref(T* _data, size_t _count): m_data(_data), m_count(_count) {} /// Creates a new vector_ref pointing to the data part of a string (given as pointer). - vector_ref(string_type* _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {} + vector_ref(string_type* _data): m_data(reinterpret_cast(_data->data())), m_count(_data->size() / sizeof(T)) {} /// Creates a new vector_ref pointing to the data part of a string (given as reference). vector_ref(string_type& _data): vector_ref(&_data) {} /// Creates a new vector_ref pointing to the data part of a vector (given as pointer). vector_ref(vector_type* _data): m_data(_data->data()), m_count(_data->size()) {} explicit operator bool() const { return m_data && m_count; } - std::vector toBytes() const { return std::vector(reinterpret_cast(m_data), reinterpret_cast(m_data) + m_count * sizeof(_T)); } - std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } + std::vector toBytes() const { return std::vector(reinterpret_cast(m_data), reinterpret_cast(m_data) + m_count * sizeof(T)); } + std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(T)); } - template explicit operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>(reinterpret_cast<_T2*>(m_data), m_count * sizeof(_T) / sizeof(_T2)); } - operator vector_ref<_T const>() const { return vector_ref<_T const>(m_data, m_count); } + template explicit operator vector_ref() const { assert(m_count * sizeof(T) / sizeof(T2) * sizeof(T2) / sizeof(T) == m_count); return vector_ref(reinterpret_cast(m_data), m_count * sizeof(T) / sizeof(T2)); } + operator vector_ref() const { return vector_ref(m_data, m_count); } - _T* data() const { return m_data; } + T* data() const { return m_data; } /// @returns the number of elements referenced (not necessarily number of bytes). size_t size() const { return m_count; } bool empty() const { return !m_count; } /// @returns a new vector_ref which is a shifted and shortened view of the original data. /// If this goes out of bounds in any way, returns an empty vector_ref. /// If @a _count is ~size_t(0), extends the view to the end of the data. - vector_ref<_T> cropped(size_t _begin, size_t _count) const { if (m_data && _begin <= m_count && _count <= m_count && _begin + _count <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } + vector_ref cropped(size_t _begin, size_t _count) const { if (m_data && _begin <= m_count && _count <= m_count && _begin + _count <= m_count) return vector_ref(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref(); } /// @returns a new vector_ref which is a shifted view of the original data (not going beyond it). - vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); } + vector_ref cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref(m_data + _begin, m_count - _begin); else return vector_ref(); } - _T* begin() { return m_data; } - _T* end() { return m_data + m_count; } - _T const* begin() const { return m_data; } - _T const* end() const { return m_data + m_count; } + T* begin() { return m_data; } + T* end() { return m_data + m_count; } + T const* begin() const { return m_data; } + T const* end() const { return m_data + m_count; } - _T& operator[](size_t _i) { assert(m_data); assert(_i < m_count); return m_data[_i]; } - _T const& operator[](size_t _i) const { assert(m_data); assert(_i < m_count); return m_data[_i]; } + T& operator[](size_t _i) { assert(m_data); assert(_i < m_count); return m_data[_i]; } + T const& operator[](size_t _i) const { assert(m_data); assert(_i < m_count); return m_data[_i]; } - bool operator==(vector_ref<_T> const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; } - bool operator!=(vector_ref<_T> const& _cmp) const { return !operator==(_cmp); } + bool operator==(vector_ref const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; } + bool operator!=(vector_ref const& _cmp) const { return !operator==(_cmp); } void reset() { m_data = nullptr; m_count = 0; } private: - _T* m_data = nullptr; + T* m_data = nullptr; size_t m_count = 0; }; diff --git a/libyul/optimiser/BlockHasher.cpp b/libyul/optimiser/BlockHasher.cpp index a878db6015f2..53d263f944ab 100644 --- a/libyul/optimiser/BlockHasher.cpp +++ b/libyul/optimiser/BlockHasher.cpp @@ -30,9 +30,9 @@ using namespace solidity::util; namespace { -static constexpr uint64_t compileTimeLiteralHash(char const* _literal, size_t _N) +static constexpr uint64_t compileTimeLiteralHash(char const* _literal, size_t _n) { - return (_N == 0) ? BlockHasher::fnvEmptyHash : (static_cast(_literal[0]) * BlockHasher::fnvPrime) ^ compileTimeLiteralHash(_literal + 1, _N - 1); + return (_n == 0) ? BlockHasher::fnvEmptyHash : (static_cast(_literal[0]) * BlockHasher::fnvPrime) ^ compileTimeLiteralHash(_literal + 1, _n - 1); } template diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 40e12da941cb..6a3ffc0fbc75 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -179,8 +179,8 @@ class ExecutionFramework return _padLeft ? padding + _value : _value + padding; } static bytes encode(std::string const& _value) { return encode(util::asBytes(_value), false); } - template - static bytes encode(std::vector<_T> const& _value) + template + static bytes encode(std::vector const& _value) { bytes ret; for (auto const& v: _value) From 786c63ec3cc25f3b54c042723326b3f75f5fed24 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 17 Feb 2020 22:44:29 +0530 Subject: [PATCH 156/160] Use explicit type for variant constructor that is part of direct list initializer --- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index dfb95cdd3e61..38d16eb69ca9 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -968,7 +968,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) *_indexAccess.annotation().type, IRLValue::Storage{ slot, - 0 + 0u } }); } From 5d5bb0071638aeb1b90edc76a712bfe46f68353d Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 18 Feb 2020 12:48:21 +0530 Subject: [PATCH 157/160] Fix script used for fuzzer nightly test --- .circleci/config.yml | 1 + scripts/regressions.py | 38 +++++++++++++++----------------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 954579033de1..1009d8217c1c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -413,6 +413,7 @@ jobs: - run: name: Regression tests command: | + git clone https://github.com/ethereum/solidity-fuzzing-corpus /tmp/solidity-fuzzing-corpus mkdir -p test_results scripts/regressions.py -o test_results - run: *gitter_notify_failure diff --git a/scripts/regressions.py b/scripts/regressions.py index 22a9f1c86511..405333e02487 100755 --- a/scripts/regressions.py +++ b/scripts/regressions.py @@ -31,7 +31,7 @@ def run(self): time.sleep(self.interval) class regressor(): - _re_sanitizer_log = re.compile(r"""(.*runtime error: (?P\w+).*|std::exception::what: (?P\w+).*)""") + _re_sanitizer_log = re.compile(r"""ERROR: (libFuzzer|UndefinedBehaviorSanitizer)""") def __init__(self, description, args): self._description = description @@ -44,7 +44,7 @@ def __init__(self, description, args): def parseCmdLine(self, description, args): argParser = ArgumentParser(description) argParser.add_argument('-o', '--out-dir', required=True, type=str, - help="""Directory where test results will be written""") + help="""Directory where test results will be written""") return argParser.parse_args(args) @staticmethod @@ -89,8 +89,7 @@ def process_log(self, logfile): ## Log may contain non ASCII characters, so we simply stringify them ## since they don't matter for regular expression matching rawtext = str(open(logfile, 'rb').read()) - list = re.findall(self._re_sanitizer_log, rawtext) - return len(list) == 0 + return not re.search(self._re_sanitizer_log, rawtext) def run(self): """ @@ -106,29 +105,22 @@ def run(self): logfile = os.path.join(self._logpath, "{}.log".format(basename)) corpus_dir = "/tmp/solidity-fuzzing-corpus/{0}_seed_corpus" \ .format(basename) - cmd = "find {0} -type f | xargs -n1 {1}".format(corpus_dir, fuzzer) - cmd_status = self.run_cmd(cmd, logfile=logfile) - if cmd_status: - ret, numLeaks = self.process_log(logfile) - if not ret: - print( - "\t[-] libFuzzer reported failure for {0}. " - "Failure logged to test_results".format( - basename)) - testStatus.append(cmd_status) - else: - print("\t[+] {0} passed regression tests but leaked " - "memory.".format(basename)) - print("\t\t[+] Suppressed {0} memory leak reports".format( - numLeaks)) - testStatus.append(0) + cmd = "find {0} -type f | xargs -n1 sh -c '{1} $0 || exit 255'".format(corpus_dir, fuzzer) + self.run_cmd(cmd, logfile=logfile) + ret = self.process_log(logfile) + if not ret: + print( + "\t[-] libFuzzer reported failure for {0}. " + "Failure logged to test_results".format( + basename)) + testStatus.append(False) else: print("\t[+] {0} passed regression tests.".format(basename)) - testStatus.append(cmd_status) - return any(testStatus) + testStatus.append(True) + return all(testStatus) if __name__ == '__main__': dotprinter = PrintDotsThread() tool = regressor(DESCRIPTION, sys.argv[1:]) - sys.exit(tool.run()) + sys.exit(not tool.run()) From 18dea6b69c8c17ec50d28d86c1691cc348626a39 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Tue, 18 Feb 2020 10:22:34 +0100 Subject: [PATCH 158/160] Assembly: Added missing source field to legacy assembly json output to complete the source reference --- .gitignore | 1 + Changelog.md | 1 + libevmasm/Assembly.cpp | 47 ++++++++++++++-------- libevmasm/Assembly.h | 11 ++++- libsolidity/codegen/Compiler.h | 4 +- libsolidity/codegen/CompilerContext.h | 4 +- libsolidity/interface/CompilerStack.cpp | 4 +- libsolidity/interface/CompilerStack.h | 2 +- libsolidity/interface/StandardCompiler.cpp | 2 +- solc/CommandLineInterface.cpp | 4 +- test/libevmasm/Assembler.cpp | 37 +++++++++-------- test/libsolidity/StandardCompiler.cpp | 42 +++++++++---------- 12 files changed, 94 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index bbdf9d68dbe9..f6eeba75fffa 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ docs/utils/*.pyc /deps/downloads/ deps/install deps/cache +cmake-build-debug/ # vim stuff [._]*.sw[a-p] diff --git a/Changelog.md b/Changelog.md index 683e2928da62..0432babb51c9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,7 @@ Compiler Features: Bugfixes: * Parser: Fix an internal error for ``abstract`` without ``contract``. * Type Checker: Make invalid calls to uncallable types fatal errors. + * Assembly: Added missing `source` field to legacy assembly json output to complete the source reference. diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 10503c484dd0..7ed44d236ab0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -197,10 +197,11 @@ string Assembly::assemblyString(StringMap const& _sourceCodes) const return tmp.str(); } -Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) +Json::Value Assembly::createJsonValue(string _name, int _source, int _begin, int _end, string _value, string _jumpType) { Json::Value value; value["name"] = _name; + value["source"] = _source; value["begin"] = _begin; value["end"] = _end; if (!_value.empty()) @@ -217,65 +218,79 @@ string Assembly::toStringInHex(u256 _value) return hexStr.str(); } -Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const +Json::Value Assembly::assemblyJSON(map const& _sourceIndices) const { Json::Value root; Json::Value& collection = root[".code"] = Json::arrayValue; for (AssemblyItem const& i: m_items) { + unsigned sourceIndex = unsigned(-1); + if (i.location().source) + { + auto iter = _sourceIndices.find(i.location().source->name()); + if (iter != _sourceIndices.end()) + sourceIndex = iter->second; + } + switch (i.type()) { case Operation: collection.append( - createJsonValue(instructionInfo(i.instruction()).name, i.location().start, i.location().end, i.getJumpTypeAsString())); + createJsonValue( + instructionInfo(i.instruction()).name, + sourceIndex, + i.location().start, + i.location().end, + i.getJumpTypeAsString()) + ); break; case Push: collection.append( - createJsonValue("PUSH", i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString())); + createJsonValue("PUSH", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString())); break; case PushString: collection.append( - createJsonValue("PUSH tag", i.location().start, i.location().end, m_strings.at((h256)i.data()))); + createJsonValue("PUSH tag", sourceIndex, i.location().start, i.location().end, m_strings.at((h256)i.data()))); break; case PushTag: if (i.data() == 0) collection.append( - createJsonValue("PUSH [ErrorTag]", i.location().start, i.location().end, "")); + createJsonValue("PUSH [ErrorTag]", sourceIndex, i.location().start, i.location().end, "")); else collection.append( - createJsonValue("PUSH [tag]", i.location().start, i.location().end, toString(i.data()))); + createJsonValue("PUSH [tag]", sourceIndex, i.location().start, i.location().end, toString(i.data()))); break; case PushSub: collection.append( - createJsonValue("PUSH [$]", i.location().start, i.location().end, toString(h256(i.data())))); + createJsonValue("PUSH [$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data())))); break; case PushSubSize: collection.append( - createJsonValue("PUSH #[$]", i.location().start, i.location().end, toString(h256(i.data())))); + createJsonValue("PUSH #[$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data())))); break; case PushProgramSize: collection.append( - createJsonValue("PUSHSIZE", i.location().start, i.location().end)); + createJsonValue("PUSHSIZE", sourceIndex, i.location().start, i.location().end)); break; case PushLibraryAddress: collection.append( - createJsonValue("PUSHLIB", i.location().start, i.location().end, m_libraries.at(h256(i.data()))) + createJsonValue("PUSHLIB", sourceIndex, i.location().start, i.location().end, m_libraries.at(h256(i.data()))) ); break; case PushDeployTimeAddress: collection.append( - createJsonValue("PUSHDEPLOYADDRESS", i.location().start, i.location().end) + createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end) ); break; case Tag: collection.append( - createJsonValue("tag", i.location().start, i.location().end, toString(i.data()))); + createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data()))); collection.append( - createJsonValue("JUMPDEST", i.location().start, i.location().end)); + createJsonValue("JUMPDEST", sourceIndex, i.location().start, i.location().end)); break; case PushData: - collection.append(createJsonValue("PUSH data", i.location().start, i.location().end, toStringInHex(i.data()))); + collection.append(createJsonValue("PUSH data", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()))); break; default: assertThrow(false, InvalidOpcode, ""); @@ -293,7 +308,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const { std::stringstream hexStr; hexStr << hex << i; - data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceCodes); + data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices); } } diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index e859951bd89f..a76538375c9b 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -133,7 +133,7 @@ class Assembly /// Create a JSON representation of the assembly. Json::Value assemblyJSON( - StringMap const& _sourceCodes = StringMap() + std::map const& _sourceIndices = std::map() ) const; protected: @@ -145,7 +145,14 @@ class Assembly unsigned bytesRequired(unsigned subTagSize) const; private: - static Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()); + static Json::Value createJsonValue( + std::string _name, + int _source, + int _begin, + int _end, + std::string _value = std::string(), + std::string _jumpType = std::string() + ); static std::string toStringInHex(u256 _value); protected: diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 8bd21e586446..e12332015ff1 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -64,9 +64,9 @@ class Compiler return m_context.assemblyString(_sourceCodes); } /// @arg _sourceCodes is the map of input files to source code strings - Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const + Json::Value assemblyJSON(std::map const& _indices = std::map()) const { - return m_context.assemblyJSON(_sourceCodes); + return m_context.assemblyJSON(_indices); } /// @returns Assembly items of the normal compiler context evmasm::AssemblyItems const& assemblyItems() const { return m_context.assembly().items(); } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 0ee379f5aec7..c37afbfa5234 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -262,9 +262,9 @@ class CompilerContext } /// @arg _sourceCodes is the map of input files to source code strings - Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const + Json::Value assemblyJSON(std::map const& _indicies = std::map()) const { - return m_asm->assemblyJSON(_sourceCodes); + return m_asm->assemblyJSON(_indicies); } evmasm::LinkerObject const& assembledObject() const { return m_asm->assemble(); } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 572b3bbbff94..89ab13818192 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -670,14 +670,14 @@ string CompilerStack::assemblyString(string const& _contractName, StringMap _sou } /// TODO: cache the JSON -Json::Value CompilerStack::assemblyJSON(string const& _contractName, StringMap const& _sourceCodes) const +Json::Value CompilerStack::assemblyJSON(string const& _contractName) const { if (m_stackState != CompilationSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); Contract const& currentContract = contract(_contractName); if (currentContract.compiler) - return currentContract.compiler->assemblyJSON(_sourceCodes); + return currentContract.compiler->assemblyJSON(sourceIndices()); else return Json::Value(); } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 42279a49998f..099375879497 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -287,7 +287,7 @@ class CompilerStack: boost::noncopyable /// @returns a JSON representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings /// Prerequisite: Successful compilation. - Json::Value assemblyJSON(std::string const& _contractName, StringMap const& _sourceCodes = StringMap()) const; + Json::Value assemblyJSON(std::string const& _contractName) const; /// @returns a JSON representing the contract ABI. /// Prerequisite: Successful call to parse or compile. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 1b09b2f2c8f7..9f837a996a51 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -967,7 +967,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental)) evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList); if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesExperimental)) - evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName, sourceList); + evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName); if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesExperimental)) evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName); if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesExperimental)) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 243d6dd72f99..ccb192ba1bdc 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1258,7 +1258,7 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strOpcodes) && m_compiler->compilationSuccessful()) contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) - contractData[g_strAsm] = m_compiler->assemblyJSON(contractName, m_sourceCodes); + contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) { auto map = m_compiler->sourceMapping(contractName); @@ -1612,7 +1612,7 @@ void CommandLineInterface::outputCompilationResults() { string ret; if (m_args.count(g_argAsmJson)) - ret = jsonPrettyPrint(m_compiler->assemblyJSON(contract, m_sourceCodes)); + ret = jsonPrettyPrint(m_compiler->assemblyJSON(contract)); else ret = m_compiler->assemblyString(contract, m_sourceCodes); diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index eac83e1bbe03..2d7057f7c050 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -50,6 +50,10 @@ BOOST_AUTO_TEST_SUITE(Assembler) BOOST_AUTO_TEST_CASE(all_assembly_items) { + map indices = { + { "root.asm", 0 }, + { "sub.asm", 1 } + }; Assembly _assembly; auto root_asm = make_shared("lorem ipsum", "root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); @@ -119,22 +123,23 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) "auxdata: 0x4266eeaa\n" ); BOOST_CHECK_EQUAL( - util::jsonCompactPrint(_assembly.assemblyJSON()), - "{\".auxdata\":\"4266eeaa\",\".code\":[{\"begin\":1,\"end\":3,\"name\":\"tag\",\"value\":\"1\"}," - "{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"value\":\"1\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"value\":\"2\"}," - "{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"value\":\"someLibrary\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"value\":\"1\"}," - "{\"begin\":1,\"end\":3,\"name\":\"JUMP\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," - "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\"}," - "{\"begin\":1,\"end\":3,\"name\":\"STOP\"}]," - "\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\"}]}," + util::jsonCompactPrint(_assembly.assemblyJSON(indices)), + "{\".auxdata\":\"4266eeaa\",\".code\":[" + "{\"begin\":1,\"end\":3,\"name\":\"tag\",\"source\":0,\"value\":\"1\"}," + "{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"1\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"}," + "{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"source\":0,\"value\":\"someLibrary\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"}," + "{\"begin\":1,\"end\":3,\"name\":\"JUMP\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"source\":0,\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}" + "],\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}]}," "\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" ); } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 6ade0849fc48..df2aecbf5d7b 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -394,27 +394,27 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["legacyAssembly"][".code"].isArray()); BOOST_CHECK_EQUAL( util::jsonCompactPrint(contract["evm"]["legacyAssembly"][".code"]), - "[{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"80\"}," - "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"40\"}," - "{\"begin\":0,\"end\":14,\"name\":\"MSTORE\"}," - "{\"begin\":0,\"end\":14,\"name\":\"CALLVALUE\"}," - "{\"begin\":5,\"end\":14,\"name\":\"DUP1\"}," - "{\"begin\":2,\"end\":4,\"name\":\"ISZERO\"}," - "{\"begin\":2,\"end\":4,\"name\":\"PUSH [tag]\",\"value\":\"1\"}," - "{\"begin\":2,\"end\":4,\"name\":\"JUMPI\"}," - "{\"begin\":27,\"end\":28,\"name\":\"PUSH\",\"value\":\"0\"}," - "{\"begin\":24,\"end\":25,\"name\":\"DUP1\"}," - "{\"begin\":17,\"end\":29,\"name\":\"REVERT\"}," - "{\"begin\":2,\"end\":4,\"name\":\"tag\",\"value\":\"1\"}," - "{\"begin\":2,\"end\":4,\"name\":\"JUMPDEST\"}," - "{\"begin\":0,\"end\":14,\"name\":\"POP\"}," - "{\"begin\":0,\"end\":14,\"name\":\"PUSH #[$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," - "{\"begin\":0,\"end\":14,\"name\":\"DUP1\"}," - "{\"begin\":0,\"end\":14,\"name\":\"PUSH [$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," - "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"0\"}," - "{\"begin\":0,\"end\":14,\"name\":\"CODECOPY\"}," - "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"0\"}," - "{\"begin\":0,\"end\":14,\"name\":\"RETURN\"}]" + "[{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"80\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"40\"}," + "{\"begin\":0,\"end\":14,\"name\":\"MSTORE\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"CALLVALUE\",\"source\":0}," + "{\"begin\":5,\"end\":14,\"name\":\"DUP1\",\"source\":-1}," + "{\"begin\":2,\"end\":4,\"name\":\"ISZERO\",\"source\":-1}," + "{\"begin\":2,\"end\":4,\"name\":\"PUSH [tag]\",\"source\":-1,\"value\":\"1\"}," + "{\"begin\":2,\"end\":4,\"name\":\"JUMPI\",\"source\":-1}," + "{\"begin\":27,\"end\":28,\"name\":\"PUSH\",\"source\":-1,\"value\":\"0\"}," + "{\"begin\":24,\"end\":25,\"name\":\"DUP1\",\"source\":-1}," + "{\"begin\":17,\"end\":29,\"name\":\"REVERT\",\"source\":-1}," + "{\"begin\":2,\"end\":4,\"name\":\"tag\",\"source\":-1,\"value\":\"1\"}," + "{\"begin\":2,\"end\":4,\"name\":\"JUMPDEST\",\"source\":-1}," + "{\"begin\":0,\"end\":14,\"name\":\"POP\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":0,\"end\":14,\"name\":\"DUP1\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"}," + "{\"begin\":0,\"end\":14,\"name\":\"CODECOPY\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"}," + "{\"begin\":0,\"end\":14,\"name\":\"RETURN\",\"source\":0}]" ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(solidity::test::isValidMetadata(contract["metadata"].asString())); From a7baa25b3c510692ae9d8f335ea01259b1f066b5 Mon Sep 17 00:00:00 2001 From: "Mathias L. Baumann" Date: Tue, 18 Feb 2020 12:03:17 +0100 Subject: [PATCH 159/160] Make gitter chat more visible in main readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4729c95c067b..c0a98aefa7c2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # The Solidity Contract-Oriented Programming Language -[![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +You can talk to us on [![solidity at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge). Questions, feedback and suggestions are welcome! Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. From 41155533445b62274a2e475590be6aea0968f835 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 18 Feb 2020 12:41:00 +0100 Subject: [PATCH 160/160] Prepare changelog for 0.6.3 release. --- Changelog.md | 8 ++++---- docs/bugs_by_version.json | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0432babb51c9..3c9d7969490c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.6.3 (unreleased) +### 0.6.3 (2020-02-18) Language Features: * Allow contract types and enums as keys for mappings. @@ -6,16 +6,16 @@ Language Features: Compiler Features: - * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. * AST: Add a new node for doxygen-style, structured documentation that can be received by contract, function, event and modifier definitions. + * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. * Debug: Provide reason strings for compiler-generated internal reverts when using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting on ``debug`` mode. * Yul Optimizer: Prune functions that call each other but are otherwise unreferenced. Bugfixes: - * Parser: Fix an internal error for ``abstract`` without ``contract``. - * Type Checker: Make invalid calls to uncallable types fatal errors. * Assembly: Added missing `source` field to legacy assembly json output to complete the source reference. + * Parser: Fix an internal error for ``abstract`` without ``contract``. + * Type Checker: Make invalid calls to uncallable types fatal errors instead of regular. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 5be7152024e3..3bdc7d9e5427 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -884,5 +884,9 @@ "0.6.2": { "bugs": [], "released": "2020-01-27" + }, + "0.6.3": { + "bugs": [], + "released": "2020-02-18" } } \ No newline at end of file