From b57a36ddae0b84d5643f23dc14a9f1a01c7c9112 Mon Sep 17 00:00:00 2001 From: Paweł Bylica Date: Mon, 28 Aug 2017 17:04:41 +0200 Subject: CMake: Use toolchain files --- CMakeLists.txt | 2 ++ cmake/EthCompilerSettings.cmake | 8 +------- cmake/EthToolchains.cmake | 8 ++++++++ cmake/toolchains/default.cmake | 4 ++++ cmake/toolchains/emscripten.cmake | 2 ++ scripts/travis-emscripten/build_emscripten.sh | 3 +-- 6 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 cmake/EthToolchains.cmake create mode 100644 cmake/toolchains/default.cmake create mode 100644 cmake/toolchains/emscripten.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 399dd9df..cc59c6f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.0.0) set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" CACHE PATH "The the path to the cmake directory") list(APPEND CMAKE_MODULE_PATH ${ETH_CMAKE_DIR}) +include(EthToolchains) + # Set cmake_policies include(EthPolicy) eth_policy() diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 70505fdb..19cf0694 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -24,10 +24,6 @@ endif() eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) - - # Use ISO C++14 standard language. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") - # Enables all the warnings about constructions that some users consider questionable, # and that are easy to avoid. Also enable some extra warning flags that are not # enabled by -Wall. Finally, treat at warnings-as-errors, which forces developers @@ -78,10 +74,8 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # into errors, which makes sense. # http://stackoverflow.com/questions/21617158/how-to-silence-unused-command-line-argument-error-with-clang-without-disabling-i add_compile_options(-Qunused-arguments) - endif() - if (EMSCRIPTEN) - # Do not emit a separate memory initialiser file + elseif(EMSCRIPTEN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0") # Leave only exported symbols as public and aggressively remove others set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden") diff --git a/cmake/EthToolchains.cmake b/cmake/EthToolchains.cmake new file mode 100644 index 00000000..a4263b7d --- /dev/null +++ b/cmake/EthToolchains.cmake @@ -0,0 +1,8 @@ +if(NOT CMAKE_TOOLCHAIN_FILE) + # Use default toolchain file if none is provided. + set( + CMAKE_TOOLCHAIN_FILE + "${CMAKE_CURRENT_LIST_DIR}/toolchains/default.cmake" + CACHE FILEPATH "The CMake toolchain file" + ) +endif() diff --git a/cmake/toolchains/default.cmake b/cmake/toolchains/default.cmake new file mode 100644 index 00000000..baf859b7 --- /dev/null +++ b/cmake/toolchains/default.cmake @@ -0,0 +1,4 @@ +# Require C++14. +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/cmake/toolchains/emscripten.cmake b/cmake/toolchains/emscripten.cmake new file mode 100644 index 00000000..6c29074f --- /dev/null +++ b/cmake/toolchains/emscripten.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/default.cmake") +include("$ENV{EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake") diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 32899903..d1978a7c 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -79,9 +79,8 @@ cd $WORKSPACE mkdir -p build cd build cmake \ - -DCMAKE_TOOLCHAIN_FILE=$EMSCRIPTEN/cmake/Modules/Platform/Emscripten.cmake \ + -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/emscripten.cmake \ -DCMAKE_BUILD_TYPE=Release \ - -DEMSCRIPTEN=1 \ -DBoost_FOUND=1 \ -DBoost_USE_STATIC_LIBS=1 \ -DBoost_USE_STATIC_RUNTIME=1 \ -- cgit From 43d072df7fa21247232672bf9b1e927a64ed6788 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 19 Dec 2018 16:38:31 +0100 Subject: Remove Structs item from FAQ --- docs/frequently-asked-questions.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index d263e0c6..e17f8a76 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -16,11 +16,6 @@ Enums are not supported by the ABI, they are just supported by Solidity. You have to do the mapping yourself for now, we might provide some help later. -How do structs work? -==================== - -See `struct_and_for_loop_tester.sol `_. - What are some examples of basic string manipulation (``substring``, ``indexOf``, ``charAt``, etc)? ================================================================================================== -- cgit From ca31a9f9b999e2a58921009be297261a725af72d Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 19 Dec 2018 17:45:02 +0100 Subject: Remove comments FAQ item --- docs/frequently-asked-questions.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index d263e0c6..2cdcf819 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -71,12 +71,6 @@ arguments for you. See `ping.sol `_ and `pong.sol `_. -Are comments included with deployed contracts and do they increase deployment gas? -================================================================================== - -No, everything that is not needed for execution is removed during compilation. -This includes, among others, comments, variable names and type names. - What happens if you send ether along with a function call to a contract? ======================================================================== -- cgit From 687558f046faf2617f8961c3dada39b9c1322f43 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 07:23:08 +0100 Subject: Remove real username from source tarballs. --- scripts/create_source_tarball.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index 632c1daa..8e219ff3 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -33,6 +33,6 @@ REPO_ROOT="$(dirname "$0")"/.. mkdir -p "$SOLDIR/deps/downloads/" 2>/dev/null || true wget -O "$SOLDIR/deps/downloads/jsoncpp-1.8.4.tar.gz" https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.tar.gz mkdir -p "$REPO_ROOT/upload" - tar czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring" + tar --owner user:1000 --group user:1000 -czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring" rm -r "$TEMPDIR" ) -- cgit From ef8a67919f4f36c522e14f22209290c5858e3f3f Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 07:25:52 +0100 Subject: Set version to 0.5.3 --- CMakeLists.txt | 2 +- Changelog.md | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 399dd9df..2d1e2017 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.5.2") +set(PROJECT_VERSION "0.5.3") project(solidity VERSION ${PROJECT_VERSION}) option(LLL "Build LLL" OFF) diff --git a/Changelog.md b/Changelog.md index 57c611a3..3d298f6f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,17 @@ +### 0.5.3 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + +Build System: + + ### 0.5.2 (2018-12-19) Language Features: -- cgit From 639622c6d7b3c449252962cc16cf5274e4fcbafc Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 19 Dec 2018 17:28:05 +0100 Subject: cmake: Do not depend on a C compiler to be present by explicitly stating that this is a C++ project. CMake defaults to C *and* C++ toolchain, in case nothing has been specified. This means that cmake always checks for both, which is more than needed. This PR cuts off C toolchain requirement from /CMakeLists.txt and ensures that we don't pass along any `..._C_...` variables in EthCompilerSettings.cake nor jsoncpp.cmake. --- CMakeLists.txt | 2 +- cmake/EthCompilerSettings.cmake | 3 +-- cmake/jsoncpp.cmake | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d1e2017..745ec597 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ eth_policy() # project name and version should be set after cmake_policy CMP0048 set(PROJECT_VERSION "0.5.3") -project(solidity VERSION ${PROJECT_VERSION}) +project(solidity VERSION ${PROJECT_VERSION} LANGUAGES CXX) option(LLL "Build LLL" OFF) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 70505fdb..b5cfd09d 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -166,9 +166,8 @@ option(USE_CVC4 "Allow compiling with CVC4 SMT solver integration" ON) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) option(USE_LD_GOLD "Use GNU gold linker" ON) if (USE_LD_GOLD) - execute_process(COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION) + execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION) if ("${LD_VERSION}" MATCHES "GNU gold") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=gold") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=gold") endif () endif () diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake index 4db7b9c3..4ca8581d 100644 --- a/cmake/jsoncpp.cmake +++ b/cmake/jsoncpp.cmake @@ -35,7 +35,6 @@ ExternalProject_Add(jsoncpp-project URL_HASH SHA256=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6 CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= - -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_INSTALL_LIBDIR=lib # Build static lib but suitable to be included in a shared lib. -- cgit From 239026b3418a462a6c84297f06ea6dad4af4558b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 09:40:06 +0100 Subject: Update release checklist. - create contributor list earlier - fix name of bytecode comparison directory - add instructions to upload the source tarball --- ReleaseChecklist.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 04734544..c10db742 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -4,11 +4,13 @@ Checklist for making a release: - [ ] Check that all issues and pull requests from the Github project to be released are merged to ``develop``. - [ ] Create a commit in ``develop`` that updates the ``Changelog`` to include a release date (run ``./scripts/tests.sh`` to update the bug list). Sort the changelog entries alphabetically and correct any errors you notice. - [ ] Create a pull request and wait for the tests, merge it. + - [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``). - [ ] Create a pull request from ``develop`` to ``release``, wait for the tests, then merge it. - - [ ] Make a final check that there are no platform-dependency issues in the ``solc-test-bytecode`` repository. + - [ ] Make a final check that there are no platform-dependency issues in the ``solidity-test-bytecode`` repository. - [ ] Wait for the tests for the commit on ``release``, create a release in Github, creating the tag. - - [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``). - [ ] Wait for the CI runs on the tag itself (they should push artifacts onto the Github release page). + - [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. + - [ ] Upload the source tarball (in the upload directory) to the release page. - [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key). - [ ] Once the ``~ethereum/ubuntu/ethereum-static`` PPA build is finished and published for all platforms (make sure not to do this earlier), copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` while selecting ``Copy existing binaries``. - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``). -- cgit From 8690898ac14d42efe1b3ee9e0ed6172e25acaba4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 12:08:42 +0100 Subject: Formatting. --- test/cmdlineTests.sh | 109 ++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 9d2ffa5f..ba352015 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -100,17 +100,19 @@ printTask "Testing unknown options..." failed=$? set -e - if [ "$output" == "unrecognised option '--allow=test'" ] && [ $failed -ne 0 ] ; then - echo "Passed" + if [ "$output" == "unrecognised option '--allow=test'" ] && [ $failed -ne 0 ] + then + echo "Passed" else - printError "Incorrect response to unknown options: $STDERR" - exit 1 + printError "Incorrect response to unknown options: $STDERR" + exit 1 fi ) # General helper function for testing SOLC behaviour, based on file name, compile opts, exit code, stdout and stderr. # An failure is expected. -test_solc_behaviour() { +function test_solc_behaviour() +{ local filename="${1}" local solc_args="${2}" local solc_stdin="${3}" @@ -122,7 +124,8 @@ test_solc_behaviour() { if [[ "$exit_code_expected" = "" ]]; then exit_code_expected="0"; fi set +e - if [[ "$solc_stdin" = "" ]]; then + if [[ "$solc_stdin" = "" ]] + then "$SOLC" "${filename}" ${solc_args} 1>$stdout_path 2>$stderr_path else "$SOLC" "${filename}" ${solc_args} <$solc_stdin 1>$stdout_path 2>$stderr_path @@ -130,7 +133,8 @@ test_solc_behaviour() { exitCode=$? set -e - if [[ "$solc_args" == *"--standard-json"* ]]; then + if [[ "$solc_args" == *"--standard-json"* ]] + then sed -i -e 's/{[^{]*Warning: This is a pre-release compiler version[^}]*},\{0,1\}//' "$stdout_path" sed -i -e 's/"errors":\[\],\{0,1\}//' "$stdout_path" else @@ -138,13 +142,15 @@ test_solc_behaviour() { sed -i -e 's/ Consider adding "pragma .*$//' "$stderr_path" fi - if [[ $exitCode -ne "$exit_code_expected" ]]; then + if [[ $exitCode -ne "$exit_code_expected" ]] + then printError "Incorrect exit code. Expected $exit_code_expected but got $exitCode." rm -f $stdout_path $stderr_path exit 1 fi - if [[ "$(cat $stdout_path)" != "${stdout_expected}" ]]; then + if [[ "$(cat $stdout_path)" != "${stdout_expected}" ]] + then printError "Incorrect output on stdout received. Expected:" echo -e "${stdout_expected}" @@ -154,7 +160,8 @@ test_solc_behaviour() { exit 1 fi - if [[ "$(cat $stderr_path)" != "${stderr_expected}" ]]; then + if [[ "$(cat $stderr_path)" != "${stderr_expected}" ]] + then printError "Incorrect output on stderr received. Expected:" echo -e "${stderr_expected}" @@ -179,46 +186,46 @@ test_solc_behaviour "${0}" "ctx:=/some/remapping/target" "" "" 1 "Invalid remapp printTask "Running standard JSON commandline tests..." ( -cd "$REPO_ROOT"/test/cmdlineTests/ -for file in *.json -do - args="--standard-json" - stdin="$REPO_ROOT/test/cmdlineTests/$file" - stdout=$(cat $file.stdout 2>/dev/null || true) - exitCode=$(cat $file.exit 2>/dev/null || true) - err=$(cat $file.err 2>/dev/null || true) - printTask " - $file" - test_solc_behaviour "" "$args" "$stdin" "$stdout" "$exitCode" "$err" -done + cd "$REPO_ROOT"/test/cmdlineTests/ + for file in *.json + do + args="--standard-json" + stdin="$REPO_ROOT/test/cmdlineTests/$file" + stdout=$(cat $file.stdout 2>/dev/null || true) + exitCode=$(cat $file.exit 2>/dev/null || true) + err=$(cat $file.err 2>/dev/null || true) + printTask " - $file" + test_solc_behaviour "" "$args" "$stdin" "$stdout" "$exitCode" "$err" + done ) printTask "Running general commandline tests..." ( -cd "$REPO_ROOT"/test/cmdlineTests/ -for file in *.sol -do - args=$(cat $file.args 2>/dev/null || true) - stdout=$(cat $file.stdout 2>/dev/null || true) - exitCode=$(cat $file.exit 2>/dev/null || true) - err=$(cat $file.err 2>/dev/null || true) - printTask " - $file" - test_solc_behaviour "$file" "$args" "" "$stdout" "$exitCode" "$err" -done + cd "$REPO_ROOT"/test/cmdlineTests/ + for file in *.sol + do + args=$(cat $file.args 2>/dev/null || true) + stdout=$(cat $file.stdout 2>/dev/null || true) + exitCode=$(cat $file.exit 2>/dev/null || true) + err=$(cat $file.err 2>/dev/null || true) + printTask " - $file" + test_solc_behaviour "$file" "$args" "" "$stdout" "$exitCode" "$err" + done ) printTask "Compiling various other contracts and libraries..." ( -cd "$REPO_ROOT"/test/compilationTests/ -for dir in * -do - if [ "$dir" != "README.md" ] - then - echo " - $dir" - cd "$dir" - compileFull -w *.sol */*.sol - cd .. - fi -done + cd "$REPO_ROOT"/test/compilationTests/ + for dir in * + do + if [ "$dir" != "README.md" ] + then + echo " - $dir" + cd "$dir" + compileFull -w *.sol */*.sol + cd .. + fi + done ) printTask "Compiling all examples from the documentation..." @@ -293,7 +300,8 @@ SOLTMPDIR=$(mktemp -d) ) rm -rf "$SOLTMPDIR" -test_solc_assembly_output() { +function test_solc_assembly_output() +{ local input="${1}" local expected="${2}" local solc_args="${3}" @@ -342,7 +350,8 @@ SOLTMPDIR=$(mktemp -d) set -e # This should fail - if [[ !("$output" =~ "No input files given") || ($result == 0) ]] ; then + if [[ !("$output" =~ "No input files given") || ($result == 0) ]] + then printError "Incorrect response to empty input arg list: $STDERR" exit 1 fi @@ -353,7 +362,8 @@ SOLTMPDIR=$(mktemp -d) set -e # The contract should be compiled - if [[ "$result" != 0 ]] ; then + if [[ "$result" != 0 ]] + then exit 1 fi @@ -361,7 +371,8 @@ SOLTMPDIR=$(mktemp -d) set +e output=$(echo '' | "$SOLC" --ast - 2>/dev/null) set -e - if [[ $? != 0 ]] ; then + if [[ $? != 0 ]] + then exit 1 fi ) @@ -379,14 +390,16 @@ SOLTMPDIR=$(mktemp -d) do set +e "$REPO_ROOT"/build/test/tools/solfuzzer --quiet < "$f" - if [ $? -ne 0 ]; then + if [ $? -ne 0 ] + then printError "Fuzzer failed on:" cat "$f" exit 1 fi "$REPO_ROOT"/build/test/tools/solfuzzer --without-optimizer --quiet < "$f" - if [ $? -ne 0 ]; then + if [ $? -ne 0 ] + then printError "Fuzzer (without optimizer) failed on:" cat "$f" exit 1 -- cgit From 4487567629b74098fda23b56858a7aca89fe9fd3 Mon Sep 17 00:00:00 2001 From: androlo Date: Thu, 20 Dec 2018 12:12:53 +0100 Subject: Only select directories to skip the README. --- test/cmdlineTests.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index ba352015..fc84d8a0 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -216,15 +216,12 @@ printTask "Running general commandline tests..." printTask "Compiling various other contracts and libraries..." ( cd "$REPO_ROOT"/test/compilationTests/ - for dir in * + for dir in */ do - if [ "$dir" != "README.md" ] - then - echo " - $dir" - cd "$dir" - compileFull -w *.sol */*.sol - cd .. - fi + echo " - $dir" + cd "$dir" + compileFull -w *.sol */*.sol + cd .. done ) -- cgit From 09feb9cf529d4c7da69e42b1c5fd94b7c7396005 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 12:52:14 +0100 Subject: Print command. --- test/cmdlineTests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 9d2ffa5f..bdd60863 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -150,6 +150,8 @@ test_solc_behaviour() { printError "But got:" cat $stdout_path + printError "When running $SOLC ${filename} ${solc_args} <$solc_stdin" + rm -f $stdout_path $stderr_path exit 1 fi @@ -160,6 +162,8 @@ test_solc_behaviour() { printError "But got:" cat $stderr_path + printError "When running $SOLC ${filename} ${solc_args} <$solc_stdin" + rm -f $stdout_path $stderr_path exit 1 fi -- cgit From ac6f80b4e973d793097d49e295a7d4b6d88a8851 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Dec 2018 17:34:39 +0100 Subject: [DOC] More details about packed encoding. --- docs/abi-spec.rst | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 0f4a16b6..3c9dfcf9 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -609,7 +609,9 @@ Through ``abi.encodePacked()``, Solidity supports a non-standard packed mode whe - types shorter than 32 bytes are neither zero padded nor sign extended and - dynamic types are encoded in-place and without the length. -As an example encoding ``int8, bytes1, uint16, string`` with values ``-1, 0x42, 0x2424, "Hello, world!"`` results in: +This packed mode is mainly used for indexed event parameters. + +As an example, the encoding of ``int8(-1), bytes1(0x42), uint16(0x2424), string("Hello, world!")`` results in: .. code-block:: none @@ -619,12 +621,18 @@ As an example encoding ``int8, bytes1, uint16, string`` with values ``-1, 0x42, ^^^^ uint16(0x2424) ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field -More specifically, each statically-sized type takes as many bytes as its range has -and dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded without -their length field. This means that the encoding is ambiguous as soon as there are two -dynamically-sized elements. +More specifically: + - Each value type takes as many bytes as its range has. + - The encoding of a struct or fixed-size array is the concatenation of the + encoding of its members/elements without any separator or padding. + - Mapping members of structs are ignored as usual. + - Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded without + their length field. + +In general, the encoding is ambiguous as soon as there are two dynamically-sized elements, +because of the missing length field. If padding is needed, explicit type conversions can be used: ``abi.encodePacked(uint16(0x12)) == hex"0012"``. Since packed encoding is not used when calling functions, there is no special support -for prepending a function selector. +for prepending a function selector. Since the encoding is ambiguous, there is no decoding function. -- cgit From 88c7975ca5fc46fd6c0ea5cc0267d55f2fe97ff8 Mon Sep 17 00:00:00 2001 From: androlo Date: Thu, 20 Dec 2018 12:17:33 +0100 Subject: Move functions to the top. --- test/cmdlineTests.sh | 86 ++++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index fc84d8a0..40b49daf 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -28,14 +28,14 @@ set -e +## GLOBAL VARIABLES + REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) -echo $REPO_ROOT SOLC="$REPO_ROOT/build/solc/solc" FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" -echo "Checking that the bug list is up to date..." -"$REPO_ROOT"/scripts/update_bugs_by_version.py +## FUNCTIONS if [ "$CIRCLECI" ] then @@ -93,22 +93,6 @@ function compileFull() fi } -printTask "Testing unknown options..." -( - set +e - output=$("$SOLC" --allow=test 2>&1) - failed=$? - set -e - - if [ "$output" == "unrecognised option '--allow=test'" ] && [ $failed -ne 0 ] - then - echo "Passed" - else - printError "Incorrect response to unknown options: $STDERR" - exit 1 - fi -) - # General helper function for testing SOLC behaviour, based on file name, compile opts, exit code, stdout and stderr. # An failure is expected. function test_solc_behaviour() @@ -174,6 +158,49 @@ function test_solc_behaviour() rm -f $stdout_path $stderr_path } + +function test_solc_assembly_output() +{ + local input="${1}" + local expected="${2}" + local solc_args="${3}" + + local expected_object="object \"object\" { code "${expected}" }" + + output=$(echo "${input}" | "$SOLC" - ${solc_args} 2>/dev/null) + empty=$(echo $output | sed -ne '/'"${expected_object}"'/p') + if [ -z "$empty" ] + then + printError "Incorrect assembly output. Expected: " + echo -e ${expected} + printError "with arguments ${solc_args}, but got:" + echo "${output}" + exit 1 + fi +} + +## RUN + +echo "Checking that the bug list is up to date..." +"$REPO_ROOT"/scripts/update_bugs_by_version.py + +printTask "Testing unknown options..." +( + set +e + output=$("$SOLC" --allow=test 2>&1) + failed=$? + set -e + + if [ "$output" == "unrecognised option '--allow=test'" ] && [ $failed -ne 0 ] + then + echo "Passed" + else + printError "Incorrect response to unknown options: $STDERR" + exit 1 + fi +) + + printTask "Testing passing files that are not found..." test_solc_behaviour "file_not_found.sol" "" "" "" 1 "\"file_not_found.sol\" is not found." @@ -297,26 +324,6 @@ SOLTMPDIR=$(mktemp -d) ) rm -rf "$SOLTMPDIR" -function test_solc_assembly_output() -{ - local input="${1}" - local expected="${2}" - local solc_args="${3}" - - local expected_object="object \"object\" { code "${expected}" }" - - output=$(echo "${input}" | "$SOLC" - ${solc_args} 2>/dev/null) - empty=$(echo $output | sed -ne '/'"${expected_object}"'/p') - if [ -z "$empty" ] - then - printError "Incorrect assembly output. Expected: " - echo -e ${expected} - printError "with arguments ${solc_args}, but got:" - echo "${output}" - exit 1 - fi -} - printTask "Testing assemble, yul, strict-assembly and optimize..." ( echo '{}' | "$SOLC" - --assemble &>/dev/null @@ -405,4 +412,5 @@ SOLTMPDIR=$(mktemp -d) done ) rm -rf "$SOLTMPDIR" + echo "Commandline tests successful." -- cgit From bf93665ad88d49fc6ff5b58ce5f03d59ba6d6131 Mon Sep 17 00:00:00 2001 From: poiresel Date: Fri, 21 Dec 2018 10:49:11 -0500 Subject: resolve spelling issues in the docs folder --- docs/050-breaking-changes.rst | 2 +- docs/abi-spec.rst | 2 +- docs/bugs.json | 2 +- docs/contributing.rst | 2 +- docs/yul.rst | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 48112cd9..32241776 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -171,7 +171,7 @@ Command Line and JSON Interfaces the first 36 hex characters of the keccak256 hash of the fully qualified library name, surrounded by ``$...$``. Previously, just the fully qualified library name was used. - This recudes the chances of collisions, especially when long paths are used. + This reduces the chances of collisions, especially when long paths are used. Binary files now also contain a list of mappings from these placeholders to the fully qualified names. diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 0f4a16b6..367bb965 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -597,7 +597,7 @@ Strict encoding mode is the mode that leads to exactly the same encoding as defi This means offsets have to be as small as possible while still not creating overlaps in the data areas and thus no gaps are allowed. -Usually, ABI decoders are written in a straigthforward way just following offset pointers, but some decoders +Usually, ABI decoders are written in a straightforward way just following offset pointers, but some decoders might enforce strict mode. The Solidity ABI decoder currently does not enforce strict mode, but the encoder always creates data in strict mode. diff --git a/docs/bugs.json b/docs/bugs.json index 28c0fe62..41ebce7b 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -43,7 +43,7 @@ { "name": "DelegateCallReturnValue", "summary": "The low-level .delegatecall() does not return the execution outcome, but converts the value returned by the functioned called to a boolean instead.", - "description": "The return value of the low-level .delegatecall() function is taken from a position in memory, where the call data or the return data resides. This value is interpreted as a boolean and put onto the stack. This means if the called function returns at least 32 zero bytes, .delegatecall() returns false even if the call was successuful.", + "description": "The return value of the low-level .delegatecall() function is taken from a position in memory, where the call data or the return data resides. This value is interpreted as a boolean and put onto the stack. This means if the called function returns at least 32 zero bytes, .delegatecall() returns false even if the call was successful.", "introduced": "0.3.0", "fixed": "0.4.15", "severity": "low" diff --git a/docs/contributing.rst b/docs/contributing.rst index 85816766..5768d944 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -112,7 +112,7 @@ For example, you could run the following command in your ``build`` folder: cmake -DCMAKE_BUILD_TYPE=Debug .. make -This will create symbols such that when you debug a test using the ``--debug`` flag, you will have acecess to functions and varialbes in which you can break or print with. +This will create symbols such that when you debug a test using the ``--debug`` flag, you will have access to functions and variables in which you can break or print with. The script ``./scripts/tests.sh`` also runs commandline tests and compilation tests diff --git a/docs/yul.rst b/docs/yul.rst index 9e50f126..31555742 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -171,7 +171,7 @@ As an exception, identifiers defined in the "init" part of the for-loop (the first block) are visible in all other parts of the for-loop (but not outside of the loop). Identifiers declared in the other parts of the for loop respect the regular -syntatical scoping rules. +syntactical scoping rules. The parameters and return parameters of functions are visible in the function body and their names cannot overlap. -- cgit From 444464f8cfd54bb03d608941e8f50c593997f106 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sun, 23 Dec 2018 22:18:01 +0100 Subject: Fix examples in type conversion docs --- docs/types.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/types.rst b/docs/types.rst index 08fbd7b3..4eb1f926 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -1289,7 +1289,7 @@ cut off:: uint16 b = uint16(a); // b will be 0x5678 now If an integer is explicitly converted to a larger type, it is padded on the left (i.e. at the higher order end). -The result of the conversion will compare equal to the original integer. +The result of the conversion will compare equal to the original integer:: uint16 a = 0x1234; uint32 b = uint32(a); // b will be 0x00001234 now @@ -1321,7 +1321,7 @@ rules explicit:: uint32 b = uint16(a); // b will be 0x00001234 uint32 c = uint32(bytes4(a)); // c will be 0x12340000 uint8 d = uint8(uint16(a)); // d will be 0x34 - uint8 e = uint8(bytes1(a)); // d will be 0x12 + uint8 e = uint8(bytes1(a)); // e will be 0x12 .. _types-conversion-literals: -- cgit From cc400dbeca64691f2409b2dbaad507412bdac984 Mon Sep 17 00:00:00 2001 From: spmvg <13852721+spmvg@users.noreply.github.com> Date: Sun, 30 Dec 2018 22:23:20 +0100 Subject: fix typo in docs --- docs/contracts.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 682cb378..746f6e00 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -585,9 +585,8 @@ return variables and then using ``return;`` to leave the function. Returning Multiple Values ------------------------- -When a function has multiple return types, the statement ``return (v0, v1, ..., vn) can be used to return multiple values. -vn)`` can return multiple values. The number of components must be -the same as the number of return types. +When a function has multiple return types, the statement ``return (v0, v1, ..., vn)`` can be used to return multiple values. +The number of components must be the same as the number of return types. .. index:: ! view function, function;view -- cgit From fdd24d12f3b66b7cc7183f96d39aafca45534bb0 Mon Sep 17 00:00:00 2001 From: chandan kumar mandal Date: Wed, 2 Jan 2019 02:54:43 +0530 Subject: copyright year updated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Happy New Year !! 🎇 Copyright year udated to 2019. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 233ff7b6..08a5a045 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -53,7 +53,7 @@ master_doc = 'index' # General information about the project. project = 'Solidity' -copyright = '2016-2018, Ethereum' +copyright = '2016-2019, 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 -- cgit From 3d64b0b0ec27f74b68599b28dee8babf04f9872b Mon Sep 17 00:00:00 2001 From: Asher <34049754+HaoXuan40404@users.noreply.github.com> Date: Thu, 3 Jan 2019 10:05:08 +0800 Subject: Update install_deps.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In install_deps.sh, line 59 (sed -n -e 's/^NAME="?([^"])"?$/\1/p' /etc/os-release) will get CentOS Linux instead of CentOS, so it cant match CentOS. I suggest that in line 355, can modified " CentOS)" to " CentOS*)" --- scripts/install_deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 09d5a249..0d1620c4 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -352,7 +352,7 @@ case $(uname -s) in # CentOS needs some more testing. This is the general idea of packages # needed, but some tweaking/improvements can definitely happen #------------------------------------------------------------------------------ - CentOS) + CentOS*) read -p "This script will heavily modify your system in order to allow for compilation of Solidity. Are you sure? [Y/N]" -n 1 -r if [[ $REPLY =~ ^[Yy]$ ]]; then # Make Sure we have the EPEL repos -- cgit From 12d00d38bd63d1722f4efe702df5ec21b8c01ecd Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Thu, 3 Jan 2019 00:35:06 -0600 Subject: clarify antecedents in deprecation notices --- docs/units-and-global-variables.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 336aaf77..59acfcff 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -103,11 +103,11 @@ Block and Transaction Properties values will be zero. .. note:: - The function ``blockhash`` was previously known as ``block.blockhash``. It was deprecated in + The function ``blockhash`` was previously known as ``block.blockhash``, which was deprecated in version 0.4.22 and removed in version 0.5.0. .. note:: - The function ``gasleft`` was previously known as ``msg.gas``. It was deprecated in + The function ``gasleft`` was previously known as ``msg.gas``, which was deprecated in version 0.4.21 and removed in version 0.5.0. .. index:: abi, encoding, packed -- cgit From c7074a365e3ff503b20c2a2f8638f5d3b7a8616b Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 7 Jan 2019 11:33:14 +0100 Subject: Fixes SourceLocation extraction on multiline locations with a too long first line. --- liblangutil/SourceReferenceExtractor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/liblangutil/SourceReferenceExtractor.cpp b/liblangutil/SourceReferenceExtractor.cpp index 4502bb23..1a6dbdb3 100644 --- a/liblangutil/SourceReferenceExtractor.cpp +++ b/liblangutil/SourceReferenceExtractor.cpp @@ -58,7 +58,9 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio int locationLength = isMultiline ? line.length() - start.column : end.column - start.column; if (locationLength > 150) { - line = line.substr(0, start.column + 35) + " ... " + line.substr(end.column - 35); + int const lhs = start.column + 35; + int const rhs = (isMultiline ? line.length() : end.column) - 35; + line = line.substr(0, lhs) + " ... " + line.substr(rhs); end.column = start.column + 75; locationLength = 75; } -- cgit From 9e61dbad07c690c8f43528d193e1576a59f8558c Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 7 Jan 2019 11:56:43 +0100 Subject: Adds new test for SourceReferenceExtractor to extract multiline location with a too long first line. --- test/cmdlineTests/too_long_line_multiline.sol | 13 +++++++++++++ test/cmdlineTests/too_long_line_multiline.sol.err | 6 ++++++ test/cmdlineTests/too_long_line_multiline.sol.exit | 1 + 3 files changed, 20 insertions(+) create mode 100644 test/cmdlineTests/too_long_line_multiline.sol create mode 100644 test/cmdlineTests/too_long_line_multiline.sol.err create mode 100644 test/cmdlineTests/too_long_line_multiline.sol.exit diff --git a/test/cmdlineTests/too_long_line_multiline.sol b/test/cmdlineTests/too_long_line_multiline.sol new file mode 100644 index 00000000..6609e125 --- /dev/null +++ b/test/cmdlineTests/too_long_line_multiline.sol @@ -0,0 +1,13 @@ +contract C { + function f() returns (byte _b, bytes2 _b2, bytes3 _b3, bytes memory _blit, bytes5 _b5, bytes6 _b6, string memory _str, bytes7 _b7, bytes22 _b22, bytes32 _b32) { + _b = 0x12; + _b2 = 0x1223; + _b5 = hex"043245"; + _b6 = hex"2345532532"; + _b7 = hex"03252353253253"; + _b22 = hex"325235235325325325235325"; + _b32 = hex"032523532532523532523532523532"; + _blit = hex"123498"; + _str = "heidy"; + } +} diff --git a/test/cmdlineTests/too_long_line_multiline.sol.err b/test/cmdlineTests/too_long_line_multiline.sol.err new file mode 100644 index 00000000..d7412ffe --- /dev/null +++ b/test/cmdlineTests/too_long_line_multiline.sol.err @@ -0,0 +1,6 @@ +too_long_line_multiline.sol:2:5: Error: No visibility specified. Did you intend to add "public"? + function f() returns (byte _b, byte ... _b7, bytes22 _b22, bytes32 _b32) { + ^ (Relevant source part starts here and spans across multiple lines). +too_long_line_multiline.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineTests/too_long_line_multiline.sol.exit b/test/cmdlineTests/too_long_line_multiline.sol.exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/cmdlineTests/too_long_line_multiline.sol.exit @@ -0,0 +1 @@ +1 -- cgit From ae107bde3c56e17c3cf8bce4e8446e2a0bd10361 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 11:58:11 +0100 Subject: Split Visibility and Getters doc --- docs/contracts.rst | 199 +----------------------------- docs/contracts/visibility-and-getters.rst | 198 +++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 198 deletions(-) create mode 100644 docs/contracts/visibility-and-getters.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 682cb378..79c2308d 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -130,204 +130,7 @@ This means that cyclic creation dependencies are impossible. } } -.. index:: ! visibility, external, public, private, internal - -.. _visibility-and-getters: - -********************** -Visibility and Getters -********************** - -Since Solidity knows two kinds of function calls (internal -ones that do not create an actual EVM call (also called -a "message call") and external -ones that do), there are four types of visibilities for -functions and state variables. - -Functions have to be specified as being ``external``, -``public``, ``internal`` or ``private``. -For state variables, ``external`` is not possible. - -``external``: - External functions are part of the contract interface, - which means they can be called from other contracts and - via transactions. An external function ``f`` cannot be called - internally (i.e. ``f()`` does not work, but ``this.f()`` works). - External functions are sometimes more efficient when - they receive large arrays of data. - -``public``: - Public functions are part of the contract interface - and can be either called internally or via - messages. For public state variables, an automatic getter - function (see below) is generated. - -``internal``: - Those functions and state variables can only be - accessed internally (i.e. from within the current contract - or contracts deriving from it), without using ``this``. - -``private``: - Private functions and state variables are only - visible for the contract they are defined in and not in - derived contracts. - -.. note:: - Everything that is inside a contract is visible to - all observers external to the blockchain. Making something ``private`` - only prevents other contracts from accessing and modifying - the information, but it will still be visible to the - whole world outside of the blockchain. - -The visibility specifier is given after the type for -state variables and between parameter list and -return parameter list for functions. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract C { - function f(uint a) private pure returns (uint b) { return a + 1; } - function setData(uint a) internal { data = a; } - uint public data; - } - -In the following example, ``D``, can call ``c.getData()`` to retrieve the value of -``data`` in state storage, but is not able to call ``f``. Contract ``E`` is derived from -``C`` and, thus, can call ``compute``. - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract C { - uint private data; - - function f(uint a) private pure returns(uint b) { return a + 1; } - function setData(uint a) public { data = a; } - function getData() public view returns(uint) { return data; } - function compute(uint a, uint b) internal pure returns (uint) { return a + b; } - } - - // This will not compile - contract D { - function readData() public { - C c = new C(); - uint local = c.f(7); // error: member `f` is not visible - c.setData(3); - local = c.getData(); - local = c.compute(3, 5); // error: member `compute` is not visible - } - } - - contract E is C { - function g() public { - C c = new C(); - uint val = compute(3, 5); // access to internal member (from derived to parent contract) - } - } - -.. index:: ! getter;function, ! function;getter -.. _getter-functions: - -Getter Functions -================ - -The compiler automatically creates getter functions for -all **public** state variables. For the contract given below, the compiler will -generate a function called ``data`` that does not take any -arguments and returns a ``uint``, the value of the state -variable ``data``. State variables can be initialized -when they are declared. - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract C { - uint public data = 42; - } - - contract Caller { - C c = new C(); - function f() public view returns (uint) { - return c.data(); - } - } - -The getter functions have external visibility. If the -symbol is accessed internally (i.e. without ``this.``), -it evaluates to a state variable. If it is accessed externally -(i.e. with ``this.``), it evaluates to a function. - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract C { - uint public data; - function x() public returns (uint) { - data = 3; // internal access - return this.data(); // external access - } - } - -If you have a ``public`` state variable of array type, then you can only retrieve -single elements of the array via the generated getter function. This mechanism -exists to avoid high gas costs when returning an entire array. You can use -arguments to specify which individual element to return, for example -``data(0)``. If you want to return an entire array in one call, then you need -to write a function, for example: - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract arrayExample { - // public state variable - uint[] public myArray; - - // Getter function generated by the compiler - /* - function myArray(uint i) returns (uint) { - return myArray[i]; - } - */ - - // function that returns entire array - function getArray() returns (uint[] memory) { - return myArray; - } - } - -Now you can use ``getArray()`` to retrieve the entire array, instead of -``myArray(i)``, which returns a single element per call. - -The next example is more complex: - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract Complex { - struct Data { - uint a; - bytes3 b; - mapping (uint => uint) map; - } - mapping (uint => mapping(bool => Data[])) public data; - } - -It generates a function of the following form. The mapping in the struct is omitted -because there is no good way to provide the key for the mapping: - -:: - - function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) { - a = data[arg1][arg2][arg3].a; - b = data[arg1][arg2][arg3].b; - } +.. include:: contracts/visibility-and-getters.rst .. index:: ! function;modifier diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst new file mode 100644 index 00000000..e78c9674 --- /dev/null +++ b/docs/contracts/visibility-and-getters.rst @@ -0,0 +1,198 @@ +.. index:: ! visibility, external, public, private, internal + +.. _visibility-and-getters: + +********************** +Visibility and Getters +********************** + +Since Solidity knows two kinds of function calls (internal +ones that do not create an actual EVM call (also called +a "message call") and external +ones that do), there are four types of visibilities for +functions and state variables. + +Functions have to be specified as being ``external``, +``public``, ``internal`` or ``private``. +For state variables, ``external`` is not possible. + +``external``: + External functions are part of the contract interface, + which means they can be called from other contracts and + via transactions. An external function ``f`` cannot be called + internally (i.e. ``f()`` does not work, but ``this.f()`` works). + External functions are sometimes more efficient when + they receive large arrays of data. + +``public``: + Public functions are part of the contract interface + and can be either called internally or via + messages. For public state variables, an automatic getter + function (see below) is generated. + +``internal``: + Those functions and state variables can only be + accessed internally (i.e. from within the current contract + or contracts deriving from it), without using ``this``. + +``private``: + Private functions and state variables are only + visible for the contract they are defined in and not in + derived contracts. + +.. note:: + Everything that is inside a contract is visible to + all observers external to the blockchain. Making something ``private`` + only prevents other contracts from accessing and modifying + the information, but it will still be visible to the + whole world outside of the blockchain. + +The visibility specifier is given after the type for +state variables and between parameter list and +return parameter list for functions. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract C { + function f(uint a) private pure returns (uint b) { return a + 1; } + function setData(uint a) internal { data = a; } + uint public data; + } + +In the following example, ``D``, can call ``c.getData()`` to retrieve the value of +``data`` in state storage, but is not able to call ``f``. Contract ``E`` is derived from +``C`` and, thus, can call ``compute``. + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract C { + uint private data; + + function f(uint a) private pure returns(uint b) { return a + 1; } + function setData(uint a) public { data = a; } + function getData() public view returns(uint) { return data; } + function compute(uint a, uint b) internal pure returns (uint) { return a + b; } + } + + // This will not compile + contract D { + function readData() public { + C c = new C(); + uint local = c.f(7); // error: member `f` is not visible + c.setData(3); + local = c.getData(); + local = c.compute(3, 5); // error: member `compute` is not visible + } + } + + contract E is C { + function g() public { + C c = new C(); + uint val = compute(3, 5); // access to internal member (from derived to parent contract) + } + } + +.. index:: ! getter;function, ! function;getter +.. _getter-functions: + +Getter Functions +================ + +The compiler automatically creates getter functions for +all **public** state variables. For the contract given below, the compiler will +generate a function called ``data`` that does not take any +arguments and returns a ``uint``, the value of the state +variable ``data``. State variables can be initialized +when they are declared. + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract C { + uint public data = 42; + } + + contract Caller { + C c = new C(); + function f() public view returns (uint) { + return c.data(); + } + } + +The getter functions have external visibility. If the +symbol is accessed internally (i.e. without ``this.``), +it evaluates to a state variable. If it is accessed externally +(i.e. with ``this.``), it evaluates to a function. + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract C { + uint public data; + function x() public returns (uint) { + data = 3; // internal access + return this.data(); // external access + } + } + +If you have a ``public`` state variable of array type, then you can only retrieve +single elements of the array via the generated getter function. This mechanism +exists to avoid high gas costs when returning an entire array. You can use +arguments to specify which individual element to return, for example +``data(0)``. If you want to return an entire array in one call, then you need +to write a function, for example: + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract arrayExample { + // public state variable + uint[] public myArray; + + // Getter function generated by the compiler + /* + function myArray(uint i) returns (uint) { + return myArray[i]; + } + */ + + // function that returns entire array + function getArray() returns (uint[] memory) { + return myArray; + } + } + +Now you can use ``getArray()`` to retrieve the entire array, instead of +``myArray(i)``, which returns a single element per call. + +The next example is more complex: + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract Complex { + struct Data { + uint a; + bytes3 b; + mapping (uint => uint) map; + } + mapping (uint => mapping(bool => Data[])) public data; + } + +It generates a function of the following form. The mapping in the struct is omitted +because there is no good way to provide the key for the mapping: + +:: + + function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) { + a = data[arg1][arg2][arg3].a; + b = data[arg1][arg2][arg3].b; + } -- cgit From c265bc4c0d6a94ba28149a5f42ab28eaa2f4688d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 7 Jan 2019 10:55:52 +0100 Subject: Update to boost 1.68 for emscripten builds. --- .circleci/config.yml | 2 +- .travis.yml | 2 +- scripts/travis-emscripten/build_emscripten.sh | 14 +++++++------- scripts/travis-emscripten/install_deps.sh | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cec69e31..5c85007e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,7 +54,7 @@ jobs: name: Save Boost build key: *boost-cache-key paths: - - boost_1_67_0 + - boost_1_68_0 - store_artifacts: path: build/libsolc/soljson.js destination: soljson.js diff --git a/.travis.yml b/.travis.yml index 8da17c45..2036f03e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -177,7 +177,7 @@ git: cache: ccache: true directories: - - boost_1_67_0 + - boost_1_68_0 - $HOME/.local install: diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index d1978a7c..32677460 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -58,7 +58,7 @@ fi # Boost echo -en 'travis_fold:start:compiling_boost\\r' -cd "$WORKSPACE"/boost_1_67_0 +cd "$WORKSPACE"/boost_1_68_0 # if b2 exists, it is a fresh checkout, otherwise it comes from the cache # and is already compiled test -e b2 && ( @@ -84,12 +84,12 @@ cmake \ -DBoost_FOUND=1 \ -DBoost_USE_STATIC_LIBS=1 \ -DBoost_USE_STATIC_RUNTIME=1 \ - -DBoost_INCLUDE_DIR="$WORKSPACE"/boost_1_67_0/ \ - -DBoost_FILESYSTEM_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_filesystem.a \ - -DBoost_PROGRAM_OPTIONS_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_program_options.a \ - -DBoost_REGEX_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_regex.a \ - -DBoost_SYSTEM_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_system.a \ - -DBoost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE="$WORKSPACE"/boost_1_67_0/libboost_unit_test_framework.a \ + -DBoost_INCLUDE_DIR="$WORKSPACE"/boost_1_68_0/ \ + -DBoost_FILESYSTEM_LIBRARY_RELEASE="$WORKSPACE"/boost_1_68_0/libboost_filesystem.a \ + -DBoost_PROGRAM_OPTIONS_LIBRARY_RELEASE="$WORKSPACE"/boost_1_68_0/libboost_program_options.a \ + -DBoost_REGEX_LIBRARY_RELEASE="$WORKSPACE"/boost_1_68_0/libboost_regex.a \ + -DBoost_SYSTEM_LIBRARY_RELEASE="$WORKSPACE"/boost_1_68_0/libboost_system.a \ + -DBoost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE="$WORKSPACE"/boost_1_68_0/libboost_unit_test_framework.a \ -DTESTS=0 \ .. make -j 4 diff --git a/scripts/travis-emscripten/install_deps.sh b/scripts/travis-emscripten/install_deps.sh index 155506e4..5959ef12 100755 --- a/scripts/travis-emscripten/install_deps.sh +++ b/scripts/travis-emscripten/install_deps.sh @@ -30,15 +30,15 @@ set -ev echo -en 'travis_fold:start:installing_dependencies\\r' -test -e boost_1_67_0 -a -e boost_1_67_0/boost || ( -rm -rf boost_1_67_0 +test -e boost_1_68_0 -a -e boost_1_68_0/boost || ( +rm -rf boost_1_68_0 rm -f boost.tar.xz -wget -q 'https://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.gz/download'\ +wget -q 'https://sourceforge.net/projects/boost/files/boost/1.68.0/boost_1_68_0.tar.gz/download'\ -O boost.tar.xz -test "$(shasum boost.tar.xz)" = "77e73c9fd7bf85b14067767b9e8fdc39b49ee0f2 boost.tar.xz" +test "$(shasum boost.tar.xz)" = "a78cf6ebb111a48385dd0c135e145a6819a8c856 boost.tar.xz" tar -xzf boost.tar.xz rm boost.tar.xz -cd boost_1_67_0 +cd boost_1_68_0 ./bootstrap.sh wget -q 'https://raw.githubusercontent.com/tee3/boost-build-emscripten/master/emscripten.jam' test "$(shasum emscripten.jam)" = "a7e13fc2c1e53b0e079ef440622f879aa6da3049 emscripten.jam" -- cgit From 17e781f8d218baf38d0a1b17fe271c6a1573fb1a Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 7 Jan 2019 13:02:01 +0100 Subject: Change uid and gid for source tarball to 0:0. --- scripts/create_source_tarball.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index 8e219ff3..7e3c238d 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -33,6 +33,6 @@ REPO_ROOT="$(dirname "$0")"/.. mkdir -p "$SOLDIR/deps/downloads/" 2>/dev/null || true wget -O "$SOLDIR/deps/downloads/jsoncpp-1.8.4.tar.gz" https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.tar.gz mkdir -p "$REPO_ROOT/upload" - tar --owner user:1000 --group user:1000 -czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring" + tar --owner 0 --group 0 -czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring" rm -r "$TEMPDIR" ) -- cgit From 2e9c70add0490a5157f393c78e29bb86f67111d7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 5 Dec 2018 23:42:55 +0100 Subject: Use rematerializer if variable is unreferenced or value is "cheap". --- libyul/optimiser/Metrics.cpp | 64 ++++++++++++++++++ libyul/optimiser/Metrics.h | 22 +++++++ libyul/optimiser/Rematerialiser.cpp | 26 +++++++- libyul/optimiser/Rematerialiser.h | 13 +++- libyul/optimiser/Suite.cpp | 3 + test/libyul/YulOptimizerTest.cpp | 2 +- test/libyul/objectCompiler/nested_optimizer.yul | 18 ++--- test/libyul/objectCompiler/simple_optimizer.yul | 11 +--- .../yulOptimizerTests/fullSuite/abi_example1.yul | 76 +++++++++------------- test/libyul/yulOptimizerTests/fullSuite/medium.yul | 5 +- .../rematerialiser/branches_for1.yul | 12 ++-- .../rematerialiser/branches_for2.yul | 14 ++-- .../branches_for_declared_in_init1.yul | 12 ++-- .../branches_for_declared_in_init2.yul | 6 +- .../rematerialiser/branches_if.yul | 14 ++-- .../rematerialiser/cheap_caller.yul | 16 +++++ .../do_not_remat_large_amounts_of_code1.yul | 10 --- .../do_not_remat_large_amounts_of_code2.yul | 10 --- ...do_remat_large_amounts_of_code_if_used_once.yul | 10 +++ .../rematerialiser/expression.yul | 10 --- .../rematerialiser/large_constant.yul | 12 ++++ .../rematerialiser/large_constant_used_once.yul | 13 ++++ .../rematerialiser/medium_sized_constant.yul | 25 +++++++ test/tools/yulopti.cpp | 2 +- 24 files changed, 270 insertions(+), 136 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul delete mode 100644 test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul delete mode 100644 test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul create mode 100644 test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul delete mode 100644 test/libyul/yulOptimizerTests/rematerialiser/expression.yul create mode 100644 test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul create mode 100644 test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul create mode 100644 test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index 8fc9476e..a351f1d1 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -21,6 +21,9 @@ #include #include +#include + +#include using namespace dev; using namespace yul; @@ -60,3 +63,64 @@ void CodeSize::visit(Expression const& _expression) ++m_size; ASTWalker::visit(_expression); } + + +size_t CodeCost::codeCost(Expression const& _expr) +{ + CodeCost cc; + cc.visit(_expr); + return cc.m_cost; +} + + +void CodeCost::operator()(FunctionCall const& _funCall) +{ + yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression)."); + m_cost += 49; + ASTWalker::operator()(_funCall); +} + +void CodeCost::operator()(FunctionalInstruction const& _instr) +{ + using namespace dev::solidity; + yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression)."); + Tier gasPriceTier = instructionInfo(_instr.instruction).gasPriceTier; + if (gasPriceTier < Tier::VeryLow) + m_cost -= 1; + else if (gasPriceTier < Tier::High) + m_cost += 1; + else + m_cost += 49; + ASTWalker::operator()(_instr); +} +void CodeCost::operator()(Literal const& _literal) +{ + yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression)."); + size_t cost = 0; + switch (_literal.kind) + { + case LiteralKind::Boolean: + break; + case LiteralKind::Number: + for (u256 n = u256(_literal.value.str()); n >= 0x100; n >>= 8) + cost++; + break; + case LiteralKind::String: + cost = _literal.value.str().size(); + break; + } + + m_cost += cost; +} + +void CodeCost::visit(Statement const& _statement) +{ + ++m_cost; + ASTWalker::visit(_statement); +} + +void CodeCost::visit(Expression const& _expression) +{ + ++m_cost; + ASTWalker::visit(_expression); +} diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h index d26ecbd9..d8a1b279 100644 --- a/libyul/optimiser/Metrics.h +++ b/libyul/optimiser/Metrics.h @@ -46,4 +46,26 @@ private: size_t m_size = 0; }; +/** + * Very rough cost that takes the size and execution cost of code into account. + * The cost per AST element is one, except for literals where it is the byte size. + * Function calls cost 50. Instructions cost 0 for 3 or less gas (same as DUP), + * 2 for up to 10 and 50 otherwise. + */ +class CodeCost: public ASTWalker +{ +public: + static size_t codeCost(Expression const& _expression); + +private: + void operator()(FunctionCall const& _funCall) override; + void operator()(FunctionalInstruction const& _instr) override; + void operator()(Literal const& _literal) override; + void visit(Statement const& _statement) override; + void visit(Expression const& _expression) override; + +private: + size_t m_cost = 0; +}; + } diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index 4180bfc3..247defda 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -29,6 +30,16 @@ using namespace std; using namespace dev; using namespace yul; +void Rematerialiser::run(Block& _ast) +{ + Rematerialiser{_ast}(_ast); +} + +Rematerialiser::Rematerialiser(Block& _ast): + m_referenceCounts(ReferencesCounter::countReferences(_ast)) +{ +} + void Rematerialiser::visit(Expression& _e) { if (_e.type() == typeid(Identifier)) @@ -37,12 +48,21 @@ void Rematerialiser::visit(Expression& _e) if (m_value.count(identifier.name)) { YulString name = identifier.name; - for (auto const& ref: m_references[name]) - assertThrow(inScope(ref), OptimizerException, ""); assertThrow(m_value.at(name), OptimizerException, ""); auto const& value = *m_value.at(name); - if (CodeSize::codeSize(value) <= 7) + size_t refs = m_referenceCounts[name]; + size_t cost = CodeCost::codeCost(value); + if (refs <= 1 || cost == 0 || (refs <= 5 && cost <= 1)) + { + assertThrow(m_referenceCounts[name] > 0, OptimizerException, ""); + for (auto const& ref: m_references[name]) + assertThrow(inScope(ref), OptimizerException, ""); + // update reference counts + m_referenceCounts[name]--; + for (auto const& ref: ReferencesCounter::countReferences(value)) + m_referenceCounts[ref.first] += ref.second; _e = (ASTCopier{}).translate(value); + } } } DataFlowAnalyzer::visit(_e); diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index b3841519..c34353de 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -26,16 +26,27 @@ namespace yul { /** - * Optimisation stage that replaces variables by their most recently assigned expressions. + * Optimisation stage that replaces variables by their most recently assigned expressions, + * but only if the expression is movable and one of the following holds: + * - the variable is referenced exactly once + * - the value is extremely cheap ("cost" of zero like ``caller()``) + * - the variable is referenced at most 5 times and the value is rather cheap + * ("cost" of at most 1 like a constant up to 0xff) * * Prerequisite: Disambiguator */ class Rematerialiser: public DataFlowAnalyzer { +public: + static void run(Block& _ast); + protected: + Rematerialiser(Block& _ast); + using ASTModifier::visit; void visit(Expression& _e) override; + std::map m_referenceCounts; }; } diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index bfba8dfc..c0fd15a2 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -105,14 +105,17 @@ void OptimiserSuite::run( RedundantAssignEliminator::run(ast); RedundantAssignEliminator::run(ast); UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); + CommonSubexpressionEliminator{}(ast); } ExpressionJoiner::run(ast); + Rematerialiser::run(ast); UnusedPruner::runUntilStabilised(ast); ExpressionJoiner::run(ast); UnusedPruner::runUntilStabilised(ast); ExpressionJoiner::run(ast); UnusedPruner::runUntilStabilised(ast); ExpressionJoiner::run(ast); + Rematerialiser::run(ast); UnusedPruner::runUntilStabilised(ast); _ast = std::move(ast); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 9643a1e9..0c782b17 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -171,7 +171,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con else if (m_optimizerStep == "rematerialiser") { disambiguate(); - (Rematerialiser{})(*m_ast); + Rematerialiser::run(*m_ast); } else if (m_optimizerStep == "expressionSimplifier") { diff --git a/test/libyul/objectCompiler/nested_optimizer.yul b/test/libyul/objectCompiler/nested_optimizer.yul index 7739ce61..2775c346 100644 --- a/test/libyul/objectCompiler/nested_optimizer.yul +++ b/test/libyul/objectCompiler/nested_optimizer.yul @@ -19,31 +19,21 @@ object "a" { // Assembly: // /* "source":60:61 */ // 0x00 -// /* "source":137:138 */ -// dup1 -// /* "source":60:61 */ -// dup2 +// 0x00 // /* "source":47:62 */ // calldataload // /* "source":119:139 */ // sstore -// /* "source":32:143 */ -// pop // stop // // sub_0: assembly { // /* "source":200:201 */ // 0x00 -// /* "source":283:284 */ -// dup1 -// /* "source":200:201 */ -// dup2 +// 0x00 // /* "source":187:202 */ // calldataload // /* "source":265:285 */ // sstore -// /* "source":170:291 */ -// pop // } -// Bytecode: 60008081355550fe -// Opcodes: PUSH1 0x0 DUP1 DUP2 CALLDATALOAD SSTORE POP INVALID +// Bytecode: 600060003555fe +// Opcodes: PUSH1 0x0 PUSH1 0x0 CALLDATALOAD SSTORE INVALID diff --git a/test/libyul/objectCompiler/simple_optimizer.yul b/test/libyul/objectCompiler/simple_optimizer.yul index 43b33553..c757dee7 100644 --- a/test/libyul/objectCompiler/simple_optimizer.yul +++ b/test/libyul/objectCompiler/simple_optimizer.yul @@ -9,15 +9,10 @@ // Assembly: // /* "source":38:39 */ // 0x00 -// /* "source":109:110 */ -// dup1 -// /* "source":38:39 */ -// dup2 +// 0x00 // /* "source":25:40 */ // calldataload // /* "source":91:111 */ // sstore -// /* "source":12:113 */ -// pop -// Bytecode: 60008081355550 -// Opcodes: PUSH1 0x0 DUP1 DUP2 CALLDATALOAD SSTORE POP +// Bytecode: 600060003555 +// Opcodes: PUSH1 0x0 PUSH1 0x0 CALLDATALOAD SSTORE diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index efb846f2..b261b5bc 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -460,14 +460,13 @@ // { // { // let _1 := 0x20 -// let _2 := 0 -// let _485 := mload(_2) +// let _485 := mload(0) // let abi_encode_pos := _1 // let abi_encode_length_68 := mload(_485) // mstore(_1, abi_encode_length_68) // abi_encode_pos := 64 // let abi_encode_srcPtr := add(_485, _1) -// let abi_encode_i_69 := _2 +// let abi_encode_i_69 := 0 // for { // } // lt(abi_encode_i_69, abi_encode_length_68) @@ -477,12 +476,11 @@ // { // let _863 := mload(abi_encode_srcPtr) // let abi_encode_pos_71_971 := abi_encode_pos -// let abi_encode_length_72_972 := 0x3 // let abi_encode_srcPtr_73_973 := _863 -// let abi_encode_i_74_974 := _2 +// let abi_encode_i_74_974 := 0 // for { // } -// lt(abi_encode_i_74_974, abi_encode_length_72_972) +// lt(abi_encode_i_74_974, 0x3) // { // abi_encode_i_74_974 := add(abi_encode_i_74_974, 1) // } @@ -497,28 +495,24 @@ // let a, b, c, d := abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(mload(_1), mload(0x40)) // sstore(a, b) // sstore(c, d) -// sstore(_2, abi_encode_pos) +// sstore(0, abi_encode_pos) // } // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5 // { // if iszero(slt(add(offset_3, 0x1f), end_4)) // { -// revert(array_5, array_5) +// revert(0, 0) // } // let length_6 := calldataload(offset_3) // let array_5_254 := allocateMemory(array_allocation_size_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(length_6)) // array_5 := array_5_254 // let dst_7 := array_5_254 // mstore(array_5_254, length_6) -// let _36 := 0x20 -// let offset_3_256 := add(offset_3, _36) -// dst_7 := add(array_5_254, _36) -// let src_8 := offset_3_256 -// let _38 := 0x40 -// if gt(add(add(offset_3, mul(length_6, _38)), _36), end_4) +// dst_7 := add(array_5_254, 0x20) +// let src_8 := add(offset_3, 0x20) +// if gt(add(add(offset_3, mul(length_6, 0x40)), 0x20), end_4) // { -// let _42 := 0 -// revert(_42, _42) +// revert(0, 0) // } // let i_9 := 0 // for { @@ -529,46 +523,42 @@ // } // { // mstore(dst_7, abi_decode_t_array$_t_uint256_$2_memory(src_8, end_4)) -// dst_7 := add(dst_7, _36) -// src_8 := add(src_8, _38) +// dst_7 := add(dst_7, 0x20) +// src_8 := add(src_8, 0x40) // } // } // function abi_decode_t_array$_t_uint256_$2_memory(offset_11, end_12) -> array_13 // { // if iszero(slt(add(offset_11, 0x1f), end_12)) // { -// revert(array_13, array_13) +// revert(0, 0) // } -// let length_14 := 0x2 -// let array_allo__559 := 0x20 -// let array_allo_size_95_605 := 64 -// let array_13_263 := allocateMemory(array_allo_size_95_605) +// let array_13_263 := allocateMemory(64) // array_13 := array_13_263 // let dst_15 := array_13_263 // let src_16 := offset_11 -// if gt(add(offset_11, array_allo_size_95_605), end_12) +// if gt(add(offset_11, 64), end_12) // { -// let _59 := 0 -// revert(_59, _59) +// revert(0, 0) // } // let i_17 := 0 // for { // } -// lt(i_17, length_14) +// lt(i_17, 0x2) // { // i_17 := add(i_17, 1) // } // { // mstore(dst_15, calldataload(src_16)) -// dst_15 := add(dst_15, array_allo__559) -// src_16 := add(src_16, array_allo__559) +// dst_15 := add(dst_15, 0x20) +// src_16 := add(src_16, 0x20) // } // } // function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29 // { // if iszero(slt(add(offset_27, 0x1f), end_28)) // { -// revert(array_29, array_29) +// revert(0, 0) // } // let length_30 := calldataload(offset_27) // let array_29_279 := allocateMemory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length_30)) @@ -576,13 +566,11 @@ // let dst_31 := array_29_279 // mstore(array_29_279, length_30) // let _91 := 0x20 -// let offset_27_281 := add(offset_27, _91) // dst_31 := add(array_29_279, _91) -// let src_32 := offset_27_281 +// let src_32 := add(offset_27, _91) // if gt(add(add(offset_27, mul(length_30, _91)), _91), end_28) // { -// let _97 := 0 -// revert(_97, _97) +// revert(0, 0) // } // let i_33 := 0 // for { @@ -621,40 +609,36 @@ // let offset_65 := calldataload(add(headStart_58, 96)) // if gt(offset_65, 0xffffffffffffffff) // { -// revert(value3, value3) +// revert(0, 0) // } // value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(headStart_58, offset_65), dataEnd_59) // } // } // function allocateMemory(size) -> memPtr // { -// let _199 := 64 -// let memPtr_315 := mload(_199) +// let memPtr_315 := mload(64) // memPtr := memPtr_315 // let newFreePtr := add(memPtr_315, size) // if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_315)) // { -// let _204 := 0 -// revert(_204, _204) +// revert(0, 0) // } -// mstore(_199, newFreePtr) +// mstore(64, newFreePtr) // } // function array_allocation_size_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(length_92) -> size_93 // { // if gt(length_92, 0xffffffffffffffff) // { -// revert(size_93, size_93) +// revert(0, 0) // } -// let _217 := 0x20 -// size_93 := add(mul(length_92, _217), _217) +// size_93 := add(mul(length_92, 0x20), 0x20) // } // function array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length_98) -> size_99 // { // if gt(length_98, 0xffffffffffffffff) // { -// revert(size_99, size_99) +// revert(0, 0) // } -// let _234 := 0x20 -// size_99 := add(mul(length_98, _234), _234) +// size_99 := add(mul(length_98, 0x20), 0x20) // } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul index fbe243d4..5578452a 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -20,12 +20,11 @@ // fullSuite // { // { -// let _1 := 0x20 // let allocate__19 := 0x40 -// mstore(allocate__19, add(mload(allocate__19), _1)) +// mstore(allocate__19, add(mload(allocate__19), 0x20)) // let allocate_p_24_41 := mload(allocate__19) // mstore(allocate__19, add(allocate_p_24_41, allocate__19)) // mstore(add(allocate_p_24_41, 96), 2) -// mstore(allocate__19, _1) +// mstore(allocate__19, 0x20) // } // } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul index dbd1ee63..3160381f 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul @@ -1,5 +1,5 @@ { - let a := 1 + let a := caller() for { pop(a) } a { pop(a) } { pop(a) } @@ -7,15 +7,15 @@ // ---- // rematerialiser // { -// let a := 1 +// let a := caller() // for { -// pop(1) +// pop(caller()) // } -// 1 +// caller() // { -// pop(1) +// pop(caller()) // } // { -// pop(1) +// pop(caller()) // } // } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul index 6a52e045..eb092e95 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul @@ -1,7 +1,7 @@ { - let a := 1 + let a := caller() for { pop(a) } a { pop(a) } { - a := 7 + a := address() let c := a } let x := a @@ -9,17 +9,17 @@ // ---- // rematerialiser // { -// let a := 1 +// let a := caller() // for { -// pop(1) +// pop(caller()) // } // a // { -// pop(7) +// pop(address()) // } // { -// a := 7 -// let c := 7 +// a := address() +// let c := address() // } // let x := a // } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul index fc816419..e7c689ca 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul @@ -1,6 +1,6 @@ { let b := 0 - for { let a := 1 pop(a) } a { pop(a) } { + for { let a := caller() pop(a) } a { pop(a) } { b := 1 pop(a) } } @@ -9,15 +9,15 @@ // { // let b := 0 // for { -// let a := 1 -// pop(1) +// let a := caller() +// pop(caller()) // } -// 1 +// caller() // { -// pop(1) +// pop(caller()) // } // { // b := 1 -// pop(1) +// pop(caller()) // } // } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul index 3d916890..80ee9233 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul @@ -1,6 +1,6 @@ { let b := 0 - for { let a := 1 pop(a) } lt(a, 0) { pop(a) a := add(a, 3) } { + for { let a := caller() pop(a) } lt(a, 0) { pop(a) a := add(a, 3) } { b := 1 pop(a) } } @@ -9,8 +9,8 @@ // { // let b := 0 // for { -// let a := 1 -// pop(1) +// let a := caller() +// pop(caller()) // } // lt(a, 0) // { diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul index c148c2f2..2aff06d4 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul @@ -1,18 +1,18 @@ { - let a := 1 - let b := 2 + let a := caller() + let b := address() if b { pop(b) b := a } let c := b } // ---- // rematerialiser // { -// let a := 1 -// let b := 2 -// if 2 +// let a := caller() +// let b := address() +// if address() // { -// pop(2) -// b := 1 +// pop(address()) +// b := caller() // } // let c := b // } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul b/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul new file mode 100644 index 00000000..7e99e428 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul @@ -0,0 +1,16 @@ +{ + // The caller opcode is cheap, so inline it, + // no matter how often it is used + let a := caller() + mstore(a, a) + mstore(add(a, a), mload(a)) + sstore(a, sload(a)) +} +// ---- +// rematerialiser +// { +// let a := caller() +// mstore(caller(), caller()) +// mstore(add(caller(), caller()), mload(caller())) +// sstore(caller(), sload(caller())) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul deleted file mode 100644 index 016fa0d7..00000000 --- a/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul +++ /dev/null @@ -1,10 +0,0 @@ -{ - let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) - let b := x -} -// ---- -// rematerialiser -// { -// let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) -// let b := x -// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul deleted file mode 100644 index d95dc1fc..00000000 --- a/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul +++ /dev/null @@ -1,10 +0,0 @@ -{ - let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) - let b := x -} -// ---- -// rematerialiser -// { -// let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) -// let b := add(mul(calldataload(2), calldataload(4)), calldatasize()) -// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul new file mode 100644 index 00000000..e464d404 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul @@ -0,0 +1,10 @@ +{ + let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) + let b := x +} +// ---- +// rematerialiser +// { +// let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) +// let b := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/expression.yul b/test/libyul/yulOptimizerTests/rematerialiser/expression.yul deleted file mode 100644 index a801677d..00000000 --- a/test/libyul/yulOptimizerTests/rematerialiser/expression.yul +++ /dev/null @@ -1,10 +0,0 @@ -{ - let a := add(mul(calldatasize(), 2), number()) - let b := add(a, a) -} -// ---- -// rematerialiser -// { -// let a := add(mul(calldatasize(), 2), number()) -// let b := add(add(mul(calldatasize(), 2), number()), add(mul(calldatasize(), 2), number())) -// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul b/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul new file mode 100644 index 00000000..9c7c66f1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul @@ -0,0 +1,12 @@ +{ + // Constants cost depending on their magnitude. + // Do not rematerialize large constants. + let a := 0xffffffffffffffffffffff + mstore(a, a) +} +// ---- +// rematerialiser +// { +// let a := 0xffffffffffffffffffffff +// mstore(a, a) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul b/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul new file mode 100644 index 00000000..b8a861aa --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul @@ -0,0 +1,13 @@ +{ + // Constants cost depending on their magnitude. + // Do not rematerialize large constants, + // unless they are used exactly once. + let a := 0xffffffffffffffffffffff + mstore(0, a) +} +// ---- +// rematerialiser +// { +// let a := 0xffffffffffffffffffffff +// mstore(0, 0xffffffffffffffffffffff) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul b/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul new file mode 100644 index 00000000..98cdbd09 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul @@ -0,0 +1,25 @@ +{ + // Constants cost depending on their magnitude. + // Rematerialize small constants only if they are + // not used too often. + // b is used 5 times + let b := 2 + mstore(b, b) + mstore(add(b, b), b) + // a is used 7 times + let a := 1 + mstore(a, a) + mstore(add(a, a), a) + mstore(a, mload(a)) +} +// ---- +// rematerialiser +// { +// let b := 2 +// mstore(2, 2) +// mstore(add(2, 2), 2) +// let a := 1 +// mstore(a, a) +// mstore(add(a, a), a) +// mstore(a, mload(a)) +// } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index fcbe308f..e88b0538 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -182,7 +182,7 @@ public: RedundantAssignEliminator::run(*m_ast); break; case 'm': - Rematerialiser{}(*m_ast); + Rematerialiser::run(*m_ast); break; default: cout << "Unknown option." << endl; -- cgit From 1da2c1f7e4a11f44a1a106088e2e99696658849a Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 14:14:26 +0100 Subject: Split Constant State Variables doc --- docs/contracts.rst | 36 +---------------------------- docs/contracts/constant-state-variables.rst | 35 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 35 deletions(-) create mode 100644 docs/contracts/constant-state-variables.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 746f6e00..c9adb03b 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -441,41 +441,7 @@ all symbols visible from the function are visible in the modifier. Symbols introduced in the modifier are not visible in the function (as they might change by overriding). -.. index:: ! constant - -************************ -Constant State Variables -************************ - -State variables can be declared as ``constant``. In this case, they have to be -assigned from an expression which is a constant at compile time. Any expression -that accesses storage, blockchain data (e.g. ``now``, ``address(this).balance`` or -``block.number``) or -execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions -that might have a side-effect on memory allocation are allowed, but those that -might have a side-effect on other memory objects are not. The built-in functions -``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod`` -are allowed (even though, with the exception of ``keccak256``, they do call external contracts). - -The reason behind allowing side-effects on the memory allocator is that it -should be possible to construct complex objects like e.g. lookup-tables. -This feature is not yet fully usable. - -The compiler does not reserve a storage slot for these variables, and every occurrence is -replaced by the respective constant expression (which might be computed to a single value by the optimizer). - -Not all types for constants are implemented at this time. The only supported types are -value types and strings. - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract C { - uint constant x = 32**22 + 8; - string constant text = "abc"; - bytes32 constant myHash = keccak256("abc"); - } +.. include:: contracts/constant-state-variables.rst .. index:: ! functions diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst new file mode 100644 index 00000000..3e615ed0 --- /dev/null +++ b/docs/contracts/constant-state-variables.rst @@ -0,0 +1,35 @@ +.. index:: ! constant + +************************ +Constant State Variables +************************ + +State variables can be declared as ``constant``. In this case, they have to be +assigned from an expression which is a constant at compile time. Any expression +that accesses storage, blockchain data (e.g. ``now``, ``address(this).balance`` or +``block.number``) or +execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions +that might have a side-effect on memory allocation are allowed, but those that +might have a side-effect on other memory objects are not. The built-in functions +``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod`` +are allowed (even though, with the exception of ``keccak256``, they do call external contracts). + +The reason behind allowing side-effects on the memory allocator is that it +should be possible to construct complex objects like e.g. lookup-tables. +This feature is not yet fully usable. + +The compiler does not reserve a storage slot for these variables, and every occurrence is +replaced by the respective constant expression (which might be computed to a single value by the optimizer). + +Not all types for constants are implemented at this time. The only supported types are +value types and strings. + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract C { + uint constant x = 32**22 + 8; + string constant text = "abc"; + bytes32 constant myHash = keccak256("abc"); + } -- cgit From cefc6c433e1722364a1461a144b077e57e2ecafe Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 2 Jan 2019 17:25:16 +0100 Subject: Update non-existant pragma 0.4.99 to 0.5.0 Use correct syntax for pragma Change pragma syntax One more change to pragma syntax --- docs/050-breaking-changes.rst | 8 ++++---- docs/abi-spec.rst | 2 +- docs/common-patterns.rst | 4 ++-- docs/contracts.rst | 14 +++++++------- docs/control-structures.rst | 12 ++++++------ docs/frequently-asked-questions.rst | 2 +- docs/introduction-to-smart-contracts.rst | 2 +- docs/security-considerations.rst | 4 ++-- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 32241776..01d21c8c 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -308,7 +308,7 @@ This will no longer compile with Solidity v0.5.0. However, you can define a comp :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; interface OldContract { function someOldFunction(uint8 a) external; function anotherOldFunction() external returns (bool); @@ -325,7 +325,7 @@ Given the interface defined above, you can now easily use the already deployed p :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; interface OldContract { function someOldFunction(uint8 a) external; @@ -345,7 +345,7 @@ commandline compiler for linking): :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; library OldLibrary { function someFunction(uint8 a) public returns(bool); @@ -430,7 +430,7 @@ New version: :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract OtherContract { uint x; diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 2c01c4a1..68ca8ec4 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -471,7 +471,7 @@ For example, :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract Test { constructor() public { b = hex"12345678901234567890123456789012"; } diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 84c18936..3704b730 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -28,7 +28,7 @@ become the new richest. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract WithdrawalContract { address public richest; @@ -65,7 +65,7 @@ This is as opposed to the more intuitive sending pattern: :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract SendContract { address payable public richest; diff --git a/docs/contracts.rst b/docs/contracts.rst index 746f6e00..24477a62 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -343,7 +343,7 @@ inheritable properties of contracts and may be overridden by derived contracts. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract owned { constructor() public { owner = msg.sender; } @@ -619,7 +619,7 @@ The following statements are considered modifying the state: :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract C { function f(uint a, uint b) public view returns (uint) { @@ -664,7 +664,7 @@ In addition to the list of state modifying statements explained above, the follo :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract C { function f(uint a, uint b) public pure returns (uint) { @@ -758,7 +758,7 @@ Like any function, the fallback function can execute complex operations as long :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract Test { // This function is called for all messages sent to @@ -1063,7 +1063,7 @@ Details are given in the following example. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract owned { constructor() public { owner = msg.sender; } @@ -1228,7 +1228,7 @@ equivalent to ``constructor() public {}``. For example: :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract A { uint public a; @@ -1407,7 +1407,7 @@ Interfaces are denoted by their own keyword: :: - pragma solidity >=0.5.0 <0.6.0; + pragma solidity ^0.5.0; interface Token { enum TokenType { Fungible, NonFungible } diff --git a/docs/control-structures.rst b/docs/control-structures.rst index f8016806..f32e7879 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -168,7 +168,7 @@ is compiled so recursive creation-dependencies are not possible. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract D { uint public x; @@ -291,7 +291,7 @@ the two variables have the same name but disjoint scopes. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract C { function minimalScoping() pure public { { @@ -312,7 +312,7 @@ In any case, you will get a warning about the outer variable being shadowed. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; // This will report a warning contract C { function f() pure public returns (uint) { @@ -332,7 +332,7 @@ In any case, you will get a warning about the outer variable being shadowed. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; // This will not compile contract C { function f() pure public returns (uint) { @@ -379,7 +379,7 @@ a message string for ``require``, but not for ``assert``. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract Sharer { function sendHalf(address payable addr) public payable returns (uint balance) { @@ -425,7 +425,7 @@ The following example shows how an error string can be used together with revert :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract VendingMachine { function buy(uint amount) public payable { diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index ae25b935..2cc082b4 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -120,7 +120,7 @@ In the case of a ``contract A`` calling a new instance of ``contract B``, parent You will need to make sure that you have both contracts aware of each other's presence and that ``contract B`` has a ``payable`` constructor. In this example:: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract B { constructor() public payable {} diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 7daae06a..0cce690b 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -81,7 +81,7 @@ registering with username and password — all you need is an Ethereum keypair. :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; contract Coin { // The keyword "public" makes those variables diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index bd06276b..d83302a0 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -183,7 +183,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract TxUserWallet { @@ -203,7 +203,7 @@ Now someone tricks you into sending ether to the address of this attack wallet: :: - pragma solidity >0.4.99 <0.6.0; + pragma solidity ^0.5.0; interface TxUserWallet { function transferTo(address payable dest, uint amount) external; -- cgit From b18c8a60ffc59e8664a298cc595e79951b54ebd9 Mon Sep 17 00:00:00 2001 From: androlo Date: Thu, 20 Dec 2018 12:28:42 +0100 Subject: Use directories for tests. --- test/cmdlineTests.sh | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 3beee552..f8cafc52 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -217,30 +217,29 @@ test_solc_behaviour "${0}" "ctx:=/some/remapping/target" "" "" 1 "Invalid remapp printTask "Running standard JSON commandline tests..." ( - cd "$REPO_ROOT"/test/cmdlineTests/ - for file in *.json + cd "$REPO_ROOT"/test/cmdlineTests/standard_json/ + for tdir in */ do - args="--standard-json" - stdin="$REPO_ROOT/test/cmdlineTests/$file" - stdout=$(cat $file.stdout 2>/dev/null || true) - exitCode=$(cat $file.exit 2>/dev/null || true) - err=$(cat $file.err 2>/dev/null || true) - printTask " - $file" - test_solc_behaviour "" "$args" "$stdin" "$stdout" "$exitCode" "$err" + stdin="${tdir}/input.json" + stdout=$(cat ${tdir}/output.json 2>/dev/null || true) + exitCode=$(cat ${tdir}/exit 2>/dev/null || true) + err=$(cat ${tdir}/err 2>/dev/null || true) + printTask " - ${tdir}" + test_solc_behaviour "" "--standard-json" "$stdin" "$stdout" "$exitCode" "$err" done ) printTask "Running general commandline tests..." ( - cd "$REPO_ROOT"/test/cmdlineTests/ - for file in *.sol + cd "$REPO_ROOT"/test/cmdlineTests/regular_sol + for tdir in */ do - args=$(cat $file.args 2>/dev/null || true) - stdout=$(cat $file.stdout 2>/dev/null || true) - exitCode=$(cat $file.exit 2>/dev/null || true) - err=$(cat $file.err 2>/dev/null || true) - printTask " - $file" - test_solc_behaviour "$file" "$args" "" "$stdout" "$exitCode" "$err" + args=$(cat ${tdir}/args 2>/dev/null || true) + stdout=$(cat ${tdir}/output 2>/dev/null || true) + exitCode=$(cat ${tdir}/exit 2>/dev/null || true) + err=$(cat ${tdir}/err 2>/dev/null || true) + printTask " - ${tdir}" + test_solc_behaviour "${tdir}contracts.sol" "$args" "" "$stdout" "$exitCode" "$err" done ) -- cgit From 6e626ad95502549d6b156c3b294990a450c590e1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 12:32:53 +0100 Subject: Unify json and non-json commandline tests. --- test/cmdlineTests.sh | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index f8cafc52..1ef1b320 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -215,31 +215,27 @@ printTask "Testing passing empty remappings..." test_solc_behaviour "${0}" "=/some/remapping/target" "" "" 1 "Invalid remapping: \"=/some/remapping/target\"." test_solc_behaviour "${0}" "ctx:=/some/remapping/target" "" "" 1 "Invalid remapping: \"ctx:=/some/remapping/target\"." -printTask "Running standard JSON commandline tests..." -( - cd "$REPO_ROOT"/test/cmdlineTests/standard_json/ - for tdir in */ - do - stdin="${tdir}/input.json" - stdout=$(cat ${tdir}/output.json 2>/dev/null || true) - exitCode=$(cat ${tdir}/exit 2>/dev/null || true) - err=$(cat ${tdir}/err 2>/dev/null || true) - printTask " - ${tdir}" - test_solc_behaviour "" "--standard-json" "$stdin" "$stdout" "$exitCode" "$err" - done -) - printTask "Running general commandline tests..." ( - cd "$REPO_ROOT"/test/cmdlineTests/regular_sol + cd "$REPO_ROOT"/test/cmdlineTests/ for tdir in */ do - args=$(cat ${tdir}/args 2>/dev/null || true) - stdout=$(cat ${tdir}/output 2>/dev/null || true) + if [ -e "${tdir}/input.json" ] + then + inputFile="" + stdin="${tdir}/input.json" + stdout=$(cat ${tdir}/output.json 2>/dev/null || true) + args="--standard-json "$(cat ${tdir}/args 2>/dev/null || true) + else + inputFile="${tdir}input.sol" + stdin="" + stdout=$(cat ${tdir}/output 2>/dev/null || true) + args=$(cat ${tdir}/args 2>/dev/null || true) + fi exitCode=$(cat ${tdir}/exit 2>/dev/null || true) err=$(cat ${tdir}/err 2>/dev/null || true) printTask " - ${tdir}" - test_solc_behaviour "${tdir}contracts.sol" "$args" "" "$stdout" "$exitCode" "$err" + test_solc_behaviour "$inputFile" "$args" "$stdin" "$stdout" "$exitCode" "$err" done ) -- cgit From d2105be57d62e5113a086b555a747ac7b4601b41 Mon Sep 17 00:00:00 2001 From: androlo Date: Tue, 18 Dec 2018 20:48:56 +0100 Subject: Move actual test files. --- test/cmdlineTests/data_storage.sol | 15 ------ test/cmdlineTests/data_storage.sol.args | 1 - test/cmdlineTests/data_storage.sol.stdout | 7 --- test/cmdlineTests/data_storage/args | 1 + test/cmdlineTests/data_storage/input.sol | 15 ++++++ test/cmdlineTests/data_storage/output | 7 +++ test/cmdlineTests/gas_test_dispatch.sol | 43 ------------------ test/cmdlineTests/gas_test_dispatch.sol.args | 1 - test/cmdlineTests/gas_test_dispatch.sol.stdout | 53 ---------------------- test/cmdlineTests/gas_test_dispatch/args | 1 + test/cmdlineTests/gas_test_dispatch/input.sol | 43 ++++++++++++++++++ test/cmdlineTests/gas_test_dispatch/output | 53 ++++++++++++++++++++++ test/cmdlineTests/gas_test_dispatch_optimize.sol | 43 ------------------ .../gas_test_dispatch_optimize.sol.args | 1 - .../gas_test_dispatch_optimize.sol.stdout | 53 ---------------------- test/cmdlineTests/gas_test_dispatch_optimize/args | 1 + .../gas_test_dispatch_optimize/input.sol | 43 ++++++++++++++++++ .../cmdlineTests/gas_test_dispatch_optimize/output | 53 ++++++++++++++++++++++ test/cmdlineTests/standard.json | 10 ---- test/cmdlineTests/standard.json.exit | 1 - test/cmdlineTests/standard.json.stdout | 1 - test/cmdlineTests/standard_binaries_requested.json | 17 ------- .../standard_binaries_requested.json.stdout | 1 - .../standard_binaries_requested/input.json | 17 +++++++ .../standard_binaries_requested/output.json | 1 + test/cmdlineTests/standard_default_success/exit | 1 + .../standard_default_success/input.json | 10 ++++ .../standard_default_success/output.json | 1 + .../standard_methodIdentifiersRequested.json | 17 ------- ...standard_methodIdentifiersRequested.json.stdout | 1 - .../input.json | 17 +++++++ .../output.json | 1 + test/cmdlineTests/standard_only_ast_requested.json | 17 ------- .../standard_only_ast_requested.json.stdout | 1 - .../standard_only_ast_requested/input.json | 17 +++++++ .../standard_only_ast_requested/output.json | 1 + .../standard_wrong_key_auxiliary_input.json | 14 ------ .../standard_wrong_key_auxiliary_input.json.exit | 1 - .../standard_wrong_key_auxiliary_input.json.stdout | 1 - .../standard_wrong_key_auxiliary_input/exit | 1 + .../standard_wrong_key_auxiliary_input/input.json | 14 ++++++ .../standard_wrong_key_auxiliary_input/output.json | 1 + test/cmdlineTests/standard_wrong_key_metadata.json | 22 --------- .../standard_wrong_key_metadata.json.exit | 1 - .../standard_wrong_key_metadata.json.stdout | 1 - test/cmdlineTests/standard_wrong_key_metadata/exit | 1 + .../standard_wrong_key_metadata/input.json | 22 +++++++++ .../standard_wrong_key_metadata/output.json | 1 + .../cmdlineTests/standard_wrong_key_optimizer.json | 22 --------- .../standard_wrong_key_optimizer.json.exit | 1 - .../standard_wrong_key_optimizer.json.stdout | 1 - .../cmdlineTests/standard_wrong_key_optimizer/exit | 1 + .../standard_wrong_key_optimizer/input.json | 22 +++++++++ .../standard_wrong_key_optimizer/output.json | 1 + test/cmdlineTests/standard_wrong_key_root.json | 11 ----- .../cmdlineTests/standard_wrong_key_root.json.exit | 1 - .../standard_wrong_key_root.json.stdout | 1 - test/cmdlineTests/standard_wrong_key_root/exit | 1 + .../standard_wrong_key_root/input.json | 11 +++++ .../standard_wrong_key_root/output.json | 1 + test/cmdlineTests/standard_wrong_key_settings.json | 22 --------- .../standard_wrong_key_settings.json.exit | 1 - .../standard_wrong_key_settings.json.stdout | 1 - test/cmdlineTests/standard_wrong_key_settings/exit | 1 + .../standard_wrong_key_settings/input.json | 22 +++++++++ .../standard_wrong_key_settings/output.json | 1 + test/cmdlineTests/standard_wrong_key_source.json | 11 ----- .../standard_wrong_key_source.json.exit | 1 - .../standard_wrong_key_source.json.stdout | 1 - test/cmdlineTests/standard_wrong_key_source/exit | 1 + .../standard_wrong_key_source/input.json | 11 +++++ .../standard_wrong_key_source/output.json | 1 + .../standard_wrong_type_auxiliary_input.json | 11 ----- .../standard_wrong_type_auxiliary_input.json.exit | 1 - ...standard_wrong_type_auxiliary_input.json.stdout | 2 - .../standard_wrong_type_auxiliary_input/exit | 1 + .../standard_wrong_type_auxiliary_input/input.json | 11 +++++ .../output.json | 2 + ...rong_type_auxiliary_input_smtlib2responses.json | 20 -------- ...type_auxiliary_input_smtlib2responses.json.exit | 1 - ...pe_auxiliary_input_smtlib2responses.json.stdout | 1 - .../exit | 1 + .../input.json | 20 ++++++++ .../output.json | 1 + ...pe_auxiliary_input_smtlib2responses_member.json | 23 ---------- ...xiliary_input_smtlib2responses_member.json.exit | 1 - ...liary_input_smtlib2responses_member.json.stdout | 1 - .../exit | 1 + .../input.json | 23 ++++++++++ .../output.json | 1 + .../cmdlineTests/standard_wrong_type_metadata.json | 19 -------- .../standard_wrong_type_metadata.json.exit | 1 - .../standard_wrong_type_metadata.json.stdout | 1 - .../cmdlineTests/standard_wrong_type_metadata/exit | 1 + .../standard_wrong_type_metadata/input.json | 19 ++++++++ .../standard_wrong_type_metadata/output.json | 1 + .../standard_wrong_type_optimizer.json | 18 -------- .../standard_wrong_type_optimizer.json.exit | 1 - .../standard_wrong_type_optimizer.json.stdout | 1 - .../standard_wrong_type_optimizer/exit | 1 + .../standard_wrong_type_optimizer/input.json | 18 ++++++++ .../standard_wrong_type_optimizer/output.json | 1 + .../standard_wrong_type_output_selection.json | 11 ----- .../standard_wrong_type_output_selection.json.exit | 1 - ...tandard_wrong_type_output_selection.json.stdout | 1 - .../standard_wrong_type_output_selection/exit | 1 + .../input.json | 11 +++++ .../output.json | 1 + ...ndard_wrong_type_output_selection_contract.json | 16 ------- ..._wrong_type_output_selection_contract.json.exit | 1 - ...rong_type_output_selection_contract.json.stdout | 1 - .../exit | 1 + .../input.json | 16 +++++++ .../output.json | 1 + .../standard_wrong_type_output_selection_file.json | 13 ------ ...dard_wrong_type_output_selection_file.json.exit | 1 - ...rd_wrong_type_output_selection_file.json.stdout | 1 - .../standard_wrong_type_output_selection_file/exit | 1 + .../input.json | 13 ++++++ .../output.json | 1 + ...tandard_wrong_type_output_selection_output.json | 16 ------- ...rd_wrong_type_output_selection_output.json.exit | 1 - ..._wrong_type_output_selection_output.json.stdout | 1 - .../exit | 1 + .../input.json | 16 +++++++ .../output.json | 1 + .../standard_wrong_type_remappings.json | 17 ------- .../standard_wrong_type_remappings.json.exit | 1 - .../standard_wrong_type_remappings.json.stdout | 1 - .../standard_wrong_type_remappings/exit | 1 + .../standard_wrong_type_remappings/input.json | 17 +++++++ .../standard_wrong_type_remappings/output.json | 1 + .../standard_wrong_type_remappings_entry.json | 17 ------- .../standard_wrong_type_remappings_entry.json.exit | 1 - ...tandard_wrong_type_remappings_entry.json.stdout | 1 - .../standard_wrong_type_remappings_entry/exit | 1 + .../input.json | 17 +++++++ .../output.json | 1 + test/cmdlineTests/standard_wrong_type_root.json | 1 - .../standard_wrong_type_root.json.exit | 1 - .../standard_wrong_type_root.json.stdout | 1 - test/cmdlineTests/standard_wrong_type_root/exit | 1 + .../standard_wrong_type_root/input.json | 1 + .../standard_wrong_type_root/output.json | 1 + .../cmdlineTests/standard_wrong_type_settings.json | 23 ---------- .../standard_wrong_type_settings.json.exit | 1 - .../standard_wrong_type_settings.json.stdout | 1 - .../cmdlineTests/standard_wrong_type_settings/exit | 1 + .../standard_wrong_type_settings/input.json | 23 ++++++++++ .../standard_wrong_type_settings/output.json | 1 + test/cmdlineTests/standard_wrong_type_source.json | 12 ----- .../standard_wrong_type_source.json.exit | 1 - .../standard_wrong_type_source.json.stdout | 1 - test/cmdlineTests/standard_wrong_type_source/exit | 1 + .../standard_wrong_type_source/input.json | 12 +++++ .../standard_wrong_type_source/output.json | 1 + test/cmdlineTests/standard_wrong_type_sources.json | 4 -- .../standard_wrong_type_sources.json.exit | 1 - .../standard_wrong_type_sources.json.stdout | 1 - test/cmdlineTests/standard_wrong_type_sources/exit | 1 + .../standard_wrong_type_sources/input.json | 4 ++ .../standard_wrong_type_sources/output.json | 1 + test/cmdlineTests/too_long_line.sol | 4 -- test/cmdlineTests/too_long_line.sol.err | 6 --- test/cmdlineTests/too_long_line.sol.exit | 1 - test/cmdlineTests/too_long_line/err | 6 +++ test/cmdlineTests/too_long_line/exit | 1 + test/cmdlineTests/too_long_line/input.sol | 4 ++ .../too_long_line_both_sides_short.sol | 5 -- .../too_long_line_both_sides_short.sol.err | 6 --- .../too_long_line_both_sides_short.sol.exit | 1 - .../too_long_line_both_sides_short/err | 6 +++ .../too_long_line_both_sides_short/exit | 1 + .../too_long_line_both_sides_short/input.sol | 5 ++ test/cmdlineTests/too_long_line_edge_in.sol | 4 -- test/cmdlineTests/too_long_line_edge_in.sol.err | 6 --- test/cmdlineTests/too_long_line_edge_in.sol.exit | 1 - test/cmdlineTests/too_long_line_edge_in/err | 6 +++ test/cmdlineTests/too_long_line_edge_in/exit | 1 + test/cmdlineTests/too_long_line_edge_in/input.sol | 4 ++ test/cmdlineTests/too_long_line_edge_out.sol | 4 -- test/cmdlineTests/too_long_line_edge_out.sol.err | 6 --- test/cmdlineTests/too_long_line_edge_out.sol.exit | 1 - test/cmdlineTests/too_long_line_edge_out/err | 6 +++ test/cmdlineTests/too_long_line_edge_out/exit | 1 + test/cmdlineTests/too_long_line_edge_out/input.sol | 4 ++ test/cmdlineTests/too_long_line_left_short.sol | 4 -- test/cmdlineTests/too_long_line_left_short.sol.err | 6 --- .../cmdlineTests/too_long_line_left_short.sol.exit | 1 - test/cmdlineTests/too_long_line_left_short/err | 6 +++ test/cmdlineTests/too_long_line_left_short/exit | 1 + .../too_long_line_left_short/input.sol | 4 ++ test/cmdlineTests/too_long_line_right_short.sol | 5 -- .../cmdlineTests/too_long_line_right_short.sol.err | 6 --- .../too_long_line_right_short.sol.exit | 1 - test/cmdlineTests/too_long_line_right_short/err | 6 +++ test/cmdlineTests/too_long_line_right_short/exit | 1 + .../too_long_line_right_short/input.sol | 5 ++ 198 files changed, 717 insertions(+), 717 deletions(-) delete mode 100644 test/cmdlineTests/data_storage.sol delete mode 100644 test/cmdlineTests/data_storage.sol.args delete mode 100644 test/cmdlineTests/data_storage.sol.stdout create mode 100644 test/cmdlineTests/data_storage/args create mode 100644 test/cmdlineTests/data_storage/input.sol create mode 100644 test/cmdlineTests/data_storage/output delete mode 100644 test/cmdlineTests/gas_test_dispatch.sol delete mode 100644 test/cmdlineTests/gas_test_dispatch.sol.args delete mode 100644 test/cmdlineTests/gas_test_dispatch.sol.stdout create mode 100644 test/cmdlineTests/gas_test_dispatch/args create mode 100644 test/cmdlineTests/gas_test_dispatch/input.sol create mode 100644 test/cmdlineTests/gas_test_dispatch/output delete mode 100644 test/cmdlineTests/gas_test_dispatch_optimize.sol delete mode 100644 test/cmdlineTests/gas_test_dispatch_optimize.sol.args delete mode 100644 test/cmdlineTests/gas_test_dispatch_optimize.sol.stdout create mode 100644 test/cmdlineTests/gas_test_dispatch_optimize/args create mode 100644 test/cmdlineTests/gas_test_dispatch_optimize/input.sol create mode 100644 test/cmdlineTests/gas_test_dispatch_optimize/output delete mode 100644 test/cmdlineTests/standard.json delete mode 100644 test/cmdlineTests/standard.json.exit delete mode 100644 test/cmdlineTests/standard.json.stdout delete mode 100644 test/cmdlineTests/standard_binaries_requested.json delete mode 100644 test/cmdlineTests/standard_binaries_requested.json.stdout create mode 100644 test/cmdlineTests/standard_binaries_requested/input.json create mode 100644 test/cmdlineTests/standard_binaries_requested/output.json create mode 100644 test/cmdlineTests/standard_default_success/exit create mode 100644 test/cmdlineTests/standard_default_success/input.json create mode 100644 test/cmdlineTests/standard_default_success/output.json delete mode 100644 test/cmdlineTests/standard_methodIdentifiersRequested.json delete mode 100644 test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout create mode 100644 test/cmdlineTests/standard_method_identifiers_requested/input.json create mode 100644 test/cmdlineTests/standard_method_identifiers_requested/output.json delete mode 100644 test/cmdlineTests/standard_only_ast_requested.json delete mode 100644 test/cmdlineTests/standard_only_ast_requested.json.stdout create mode 100644 test/cmdlineTests/standard_only_ast_requested/input.json create mode 100644 test/cmdlineTests/standard_only_ast_requested/output.json delete mode 100644 test/cmdlineTests/standard_wrong_key_auxiliary_input.json delete mode 100644 test/cmdlineTests/standard_wrong_key_auxiliary_input.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_key_auxiliary_input.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_key_auxiliary_input/exit create mode 100644 test/cmdlineTests/standard_wrong_key_auxiliary_input/input.json create mode 100644 test/cmdlineTests/standard_wrong_key_auxiliary_input/output.json delete mode 100644 test/cmdlineTests/standard_wrong_key_metadata.json delete mode 100644 test/cmdlineTests/standard_wrong_key_metadata.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_key_metadata.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_key_metadata/exit create mode 100644 test/cmdlineTests/standard_wrong_key_metadata/input.json create mode 100644 test/cmdlineTests/standard_wrong_key_metadata/output.json delete mode 100644 test/cmdlineTests/standard_wrong_key_optimizer.json delete mode 100644 test/cmdlineTests/standard_wrong_key_optimizer.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_key_optimizer.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_key_optimizer/exit create mode 100644 test/cmdlineTests/standard_wrong_key_optimizer/input.json create mode 100644 test/cmdlineTests/standard_wrong_key_optimizer/output.json delete mode 100644 test/cmdlineTests/standard_wrong_key_root.json delete mode 100644 test/cmdlineTests/standard_wrong_key_root.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_key_root.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_key_root/exit create mode 100644 test/cmdlineTests/standard_wrong_key_root/input.json create mode 100644 test/cmdlineTests/standard_wrong_key_root/output.json delete mode 100644 test/cmdlineTests/standard_wrong_key_settings.json delete mode 100644 test/cmdlineTests/standard_wrong_key_settings.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_key_settings.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_key_settings/exit create mode 100644 test/cmdlineTests/standard_wrong_key_settings/input.json create mode 100644 test/cmdlineTests/standard_wrong_key_settings/output.json delete mode 100644 test/cmdlineTests/standard_wrong_key_source.json delete mode 100644 test/cmdlineTests/standard_wrong_key_source.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_key_source.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_key_source/exit create mode 100644 test/cmdlineTests/standard_wrong_key_source/input.json create mode 100644 test/cmdlineTests/standard_wrong_key_source/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input.json delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input/exit create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/exit create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/exit create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_metadata.json delete mode 100644 test/cmdlineTests/standard_wrong_type_metadata.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_metadata.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_metadata/exit create mode 100644 test/cmdlineTests/standard_wrong_type_metadata/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_metadata/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_optimizer.json delete mode 100644 test/cmdlineTests/standard_wrong_type_optimizer.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_optimizer.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_optimizer/exit create mode 100644 test/cmdlineTests/standard_wrong_type_optimizer/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_optimizer/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection.json delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection/exit create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_contract.json delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_contract.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_contract.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_contract/exit create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_contract/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_contract/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_file.json delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_file.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_file.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_file/exit create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_file/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_file/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_output.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_output.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_output/exit create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_output/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_output_selection_output/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_remappings.json delete mode 100644 test/cmdlineTests/standard_wrong_type_remappings.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_remappings.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_remappings/exit create mode 100644 test/cmdlineTests/standard_wrong_type_remappings/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_remappings/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_remappings_entry.json delete mode 100644 test/cmdlineTests/standard_wrong_type_remappings_entry.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_remappings_entry.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_remappings_entry/exit create mode 100644 test/cmdlineTests/standard_wrong_type_remappings_entry/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_remappings_entry/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_root.json delete mode 100644 test/cmdlineTests/standard_wrong_type_root.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_root.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_root/exit create mode 100644 test/cmdlineTests/standard_wrong_type_root/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_root/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_settings.json delete mode 100644 test/cmdlineTests/standard_wrong_type_settings.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_settings.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_settings/exit create mode 100644 test/cmdlineTests/standard_wrong_type_settings/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_settings/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_source.json delete mode 100644 test/cmdlineTests/standard_wrong_type_source.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_source.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_source/exit create mode 100644 test/cmdlineTests/standard_wrong_type_source/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_source/output.json delete mode 100644 test/cmdlineTests/standard_wrong_type_sources.json delete mode 100644 test/cmdlineTests/standard_wrong_type_sources.json.exit delete mode 100644 test/cmdlineTests/standard_wrong_type_sources.json.stdout create mode 100644 test/cmdlineTests/standard_wrong_type_sources/exit create mode 100644 test/cmdlineTests/standard_wrong_type_sources/input.json create mode 100644 test/cmdlineTests/standard_wrong_type_sources/output.json delete mode 100644 test/cmdlineTests/too_long_line.sol delete mode 100644 test/cmdlineTests/too_long_line.sol.err delete mode 100644 test/cmdlineTests/too_long_line.sol.exit create mode 100644 test/cmdlineTests/too_long_line/err create mode 100644 test/cmdlineTests/too_long_line/exit create mode 100644 test/cmdlineTests/too_long_line/input.sol delete mode 100644 test/cmdlineTests/too_long_line_both_sides_short.sol delete mode 100644 test/cmdlineTests/too_long_line_both_sides_short.sol.err delete mode 100644 test/cmdlineTests/too_long_line_both_sides_short.sol.exit create mode 100644 test/cmdlineTests/too_long_line_both_sides_short/err create mode 100644 test/cmdlineTests/too_long_line_both_sides_short/exit create mode 100644 test/cmdlineTests/too_long_line_both_sides_short/input.sol delete mode 100644 test/cmdlineTests/too_long_line_edge_in.sol delete mode 100644 test/cmdlineTests/too_long_line_edge_in.sol.err delete mode 100644 test/cmdlineTests/too_long_line_edge_in.sol.exit create mode 100644 test/cmdlineTests/too_long_line_edge_in/err create mode 100644 test/cmdlineTests/too_long_line_edge_in/exit create mode 100644 test/cmdlineTests/too_long_line_edge_in/input.sol delete mode 100644 test/cmdlineTests/too_long_line_edge_out.sol delete mode 100644 test/cmdlineTests/too_long_line_edge_out.sol.err delete mode 100644 test/cmdlineTests/too_long_line_edge_out.sol.exit create mode 100644 test/cmdlineTests/too_long_line_edge_out/err create mode 100644 test/cmdlineTests/too_long_line_edge_out/exit create mode 100644 test/cmdlineTests/too_long_line_edge_out/input.sol delete mode 100644 test/cmdlineTests/too_long_line_left_short.sol delete mode 100644 test/cmdlineTests/too_long_line_left_short.sol.err delete mode 100644 test/cmdlineTests/too_long_line_left_short.sol.exit create mode 100644 test/cmdlineTests/too_long_line_left_short/err create mode 100644 test/cmdlineTests/too_long_line_left_short/exit create mode 100644 test/cmdlineTests/too_long_line_left_short/input.sol delete mode 100644 test/cmdlineTests/too_long_line_right_short.sol delete mode 100644 test/cmdlineTests/too_long_line_right_short.sol.err delete mode 100644 test/cmdlineTests/too_long_line_right_short.sol.exit create mode 100644 test/cmdlineTests/too_long_line_right_short/err create mode 100644 test/cmdlineTests/too_long_line_right_short/exit create mode 100644 test/cmdlineTests/too_long_line_right_short/input.sol diff --git a/test/cmdlineTests/data_storage.sol b/test/cmdlineTests/data_storage.sol deleted file mode 100644 index cc602cc9..00000000 --- a/test/cmdlineTests/data_storage.sol +++ /dev/null @@ -1,15 +0,0 @@ -pragma solidity >=0.0; - -contract C { - function f() pure public { - require(false, "1234567890123456789012345678901"); - require(false, "12345678901234567890123456789012"); - require(false, "123456789012345678901234567890123"); - require(false, "1234567890123456789012345678901234"); - require(false, "12345678901234567890123456789012345"); - require(false, "123456789012345678901234567890123456"); - require(false, "123456789012345678901234567890121234567890123456789012345678901"); - require(false, "1234567890123456789012345678901212345678901234567890123456789012"); - require(false, "12345678901234567890123456789012123456789012345678901234567890123"); - } -} diff --git a/test/cmdlineTests/data_storage.sol.args b/test/cmdlineTests/data_storage.sol.args deleted file mode 100644 index 3684987e..00000000 --- a/test/cmdlineTests/data_storage.sol.args +++ /dev/null @@ -1 +0,0 @@ ---gas diff --git a/test/cmdlineTests/data_storage.sol.stdout b/test/cmdlineTests/data_storage.sol.stdout deleted file mode 100644 index 4a5250f7..00000000 --- a/test/cmdlineTests/data_storage.sol.stdout +++ /dev/null @@ -1,7 +0,0 @@ - -======= data_storage.sol:C ======= -Gas estimation: -construction: - 306 + 264400 = 264706 -external: - f(): 263 diff --git a/test/cmdlineTests/data_storage/args b/test/cmdlineTests/data_storage/args new file mode 100644 index 00000000..3684987e --- /dev/null +++ b/test/cmdlineTests/data_storage/args @@ -0,0 +1 @@ +--gas diff --git a/test/cmdlineTests/data_storage/input.sol b/test/cmdlineTests/data_storage/input.sol new file mode 100644 index 00000000..cc602cc9 --- /dev/null +++ b/test/cmdlineTests/data_storage/input.sol @@ -0,0 +1,15 @@ +pragma solidity >=0.0; + +contract C { + function f() pure public { + require(false, "1234567890123456789012345678901"); + require(false, "12345678901234567890123456789012"); + require(false, "123456789012345678901234567890123"); + require(false, "1234567890123456789012345678901234"); + require(false, "12345678901234567890123456789012345"); + require(false, "123456789012345678901234567890123456"); + require(false, "123456789012345678901234567890121234567890123456789012345678901"); + require(false, "1234567890123456789012345678901212345678901234567890123456789012"); + require(false, "12345678901234567890123456789012123456789012345678901234567890123"); + } +} diff --git a/test/cmdlineTests/data_storage/output b/test/cmdlineTests/data_storage/output new file mode 100644 index 00000000..e0dae4bd --- /dev/null +++ b/test/cmdlineTests/data_storage/output @@ -0,0 +1,7 @@ + +======= data_storage/input.sol:C ======= +Gas estimation: +construction: + 306 + 264400 = 264706 +external: + f(): 263 diff --git a/test/cmdlineTests/gas_test_dispatch.sol b/test/cmdlineTests/gas_test_dispatch.sol deleted file mode 100644 index a5ca9e7d..00000000 --- a/test/cmdlineTests/gas_test_dispatch.sol +++ /dev/null @@ -1,43 +0,0 @@ -pragma solidity >=0.0; - -contract Large { - uint public a; - uint[] public b; - function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } - function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; } - function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; } - function f4(uint x) public returns (uint) { b[uint8(msg.data[3])] = x; } - function f5(uint x) public returns (uint) { b[uint8(msg.data[4])] = x; } - function f6(uint x) public returns (uint) { b[uint8(msg.data[5])] = x; } - function f7(uint x) public returns (uint) { b[uint8(msg.data[6])] = x; } - function f8(uint x) public returns (uint) { b[uint8(msg.data[7])] = x; } - function f9(uint x) public returns (uint) { b[uint8(msg.data[8])] = x; } - function f0(uint x) public pure returns (uint) { require(x > 10); } - function g1(uint x) public payable returns (uint) { a = x; b[uint8(msg.data[0])] = x; } - function g2(uint x) public payable returns (uint) { b[uint8(msg.data[1])] = x; } - function g3(uint x) public payable returns (uint) { b[uint8(msg.data[2])] = x; } - function g4(uint x) public payable returns (uint) { b[uint8(msg.data[3])] = x; } - function g5(uint x) public payable returns (uint) { b[uint8(msg.data[4])] = x; } - function g6(uint x) public payable returns (uint) { b[uint8(msg.data[5])] = x; } - function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; } - function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; } - function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; } - function g0(uint x) public payable returns (uint) { require(x > 10); } -} -contract Medium { - uint public a; - uint[] public b; - function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } - function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; } - function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; } - function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; } - function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; } - function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; } - function g0(uint x) public payable returns (uint) { require(x > 10); } -} -contract Small { - uint public a; - uint[] public b; - function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } - function () external payable {} -} diff --git a/test/cmdlineTests/gas_test_dispatch.sol.args b/test/cmdlineTests/gas_test_dispatch.sol.args deleted file mode 100644 index 3684987e..00000000 --- a/test/cmdlineTests/gas_test_dispatch.sol.args +++ /dev/null @@ -1 +0,0 @@ ---gas diff --git a/test/cmdlineTests/gas_test_dispatch.sol.stdout b/test/cmdlineTests/gas_test_dispatch.sol.stdout deleted file mode 100644 index 117affad..00000000 --- a/test/cmdlineTests/gas_test_dispatch.sol.stdout +++ /dev/null @@ -1,53 +0,0 @@ - -======= gas_test_dispatch.sol:Large ======= -Gas estimation: -construction: - 1034 + 998400 = 999434 -external: - a(): 456 - b(uint256): 857 - f0(uint256): 438 - f1(uint256): 40781 - f2(uint256): 20722 - f3(uint256): 20810 - f4(uint256): 20788 - f5(uint256): 20766 - f6(uint256): 20789 - f7(uint256): 20701 - f8(uint256): 20701 - f9(uint256): 20723 - g0(uint256): 324 - g1(uint256): 40736 - g2(uint256): 20699 - g3(uint256): 20787 - g4(uint256): 20765 - g5(uint256): 20721 - g6(uint256): 20744 - g7(uint256): 20743 - g8(uint256): 20721 - g9(uint256): 20678 - -======= gas_test_dispatch.sol:Medium ======= -Gas estimation: -construction: - 411 + 376600 = 377011 -external: - a(): 433 - b(uint256): 857 - f1(uint256): 40692 - f2(uint256): 20722 - f3(uint256): 20766 - g0(uint256): 324 - g7(uint256): 20721 - g8(uint256): 20699 - g9(uint256): 20655 - -======= gas_test_dispatch.sol:Small ======= -Gas estimation: -construction: - 153 + 107800 = 107953 -external: - fallback: 123 - a(): 388 - b(uint256): 813 - f1(uint256): 40692 diff --git a/test/cmdlineTests/gas_test_dispatch/args b/test/cmdlineTests/gas_test_dispatch/args new file mode 100644 index 00000000..3684987e --- /dev/null +++ b/test/cmdlineTests/gas_test_dispatch/args @@ -0,0 +1 @@ +--gas diff --git a/test/cmdlineTests/gas_test_dispatch/input.sol b/test/cmdlineTests/gas_test_dispatch/input.sol new file mode 100644 index 00000000..a5ca9e7d --- /dev/null +++ b/test/cmdlineTests/gas_test_dispatch/input.sol @@ -0,0 +1,43 @@ +pragma solidity >=0.0; + +contract Large { + uint public a; + uint[] public b; + function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } + function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; } + function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; } + function f4(uint x) public returns (uint) { b[uint8(msg.data[3])] = x; } + function f5(uint x) public returns (uint) { b[uint8(msg.data[4])] = x; } + function f6(uint x) public returns (uint) { b[uint8(msg.data[5])] = x; } + function f7(uint x) public returns (uint) { b[uint8(msg.data[6])] = x; } + function f8(uint x) public returns (uint) { b[uint8(msg.data[7])] = x; } + function f9(uint x) public returns (uint) { b[uint8(msg.data[8])] = x; } + function f0(uint x) public pure returns (uint) { require(x > 10); } + function g1(uint x) public payable returns (uint) { a = x; b[uint8(msg.data[0])] = x; } + function g2(uint x) public payable returns (uint) { b[uint8(msg.data[1])] = x; } + function g3(uint x) public payable returns (uint) { b[uint8(msg.data[2])] = x; } + function g4(uint x) public payable returns (uint) { b[uint8(msg.data[3])] = x; } + function g5(uint x) public payable returns (uint) { b[uint8(msg.data[4])] = x; } + function g6(uint x) public payable returns (uint) { b[uint8(msg.data[5])] = x; } + function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; } + function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; } + function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; } + function g0(uint x) public payable returns (uint) { require(x > 10); } +} +contract Medium { + uint public a; + uint[] public b; + function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } + function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; } + function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; } + function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; } + function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; } + function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; } + function g0(uint x) public payable returns (uint) { require(x > 10); } +} +contract Small { + uint public a; + uint[] public b; + function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } + function () external payable {} +} diff --git a/test/cmdlineTests/gas_test_dispatch/output b/test/cmdlineTests/gas_test_dispatch/output new file mode 100644 index 00000000..d817e85c --- /dev/null +++ b/test/cmdlineTests/gas_test_dispatch/output @@ -0,0 +1,53 @@ + +======= gas_test_dispatch/input.sol:Large ======= +Gas estimation: +construction: + 1034 + 998400 = 999434 +external: + a(): 456 + b(uint256): 857 + f0(uint256): 438 + f1(uint256): 40781 + f2(uint256): 20722 + f3(uint256): 20810 + f4(uint256): 20788 + f5(uint256): 20766 + f6(uint256): 20789 + f7(uint256): 20701 + f8(uint256): 20701 + f9(uint256): 20723 + g0(uint256): 324 + g1(uint256): 40736 + g2(uint256): 20699 + g3(uint256): 20787 + g4(uint256): 20765 + g5(uint256): 20721 + g6(uint256): 20744 + g7(uint256): 20743 + g8(uint256): 20721 + g9(uint256): 20678 + +======= gas_test_dispatch/input.sol:Medium ======= +Gas estimation: +construction: + 411 + 376600 = 377011 +external: + a(): 433 + b(uint256): 857 + f1(uint256): 40692 + f2(uint256): 20722 + f3(uint256): 20766 + g0(uint256): 324 + g7(uint256): 20721 + g8(uint256): 20699 + g9(uint256): 20655 + +======= gas_test_dispatch/input.sol:Small ======= +Gas estimation: +construction: + 153 + 107800 = 107953 +external: + fallback: 123 + a(): 388 + b(uint256): 813 + f1(uint256): 40692 diff --git a/test/cmdlineTests/gas_test_dispatch_optimize.sol b/test/cmdlineTests/gas_test_dispatch_optimize.sol deleted file mode 100644 index a5ca9e7d..00000000 --- a/test/cmdlineTests/gas_test_dispatch_optimize.sol +++ /dev/null @@ -1,43 +0,0 @@ -pragma solidity >=0.0; - -contract Large { - uint public a; - uint[] public b; - function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } - function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; } - function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; } - function f4(uint x) public returns (uint) { b[uint8(msg.data[3])] = x; } - function f5(uint x) public returns (uint) { b[uint8(msg.data[4])] = x; } - function f6(uint x) public returns (uint) { b[uint8(msg.data[5])] = x; } - function f7(uint x) public returns (uint) { b[uint8(msg.data[6])] = x; } - function f8(uint x) public returns (uint) { b[uint8(msg.data[7])] = x; } - function f9(uint x) public returns (uint) { b[uint8(msg.data[8])] = x; } - function f0(uint x) public pure returns (uint) { require(x > 10); } - function g1(uint x) public payable returns (uint) { a = x; b[uint8(msg.data[0])] = x; } - function g2(uint x) public payable returns (uint) { b[uint8(msg.data[1])] = x; } - function g3(uint x) public payable returns (uint) { b[uint8(msg.data[2])] = x; } - function g4(uint x) public payable returns (uint) { b[uint8(msg.data[3])] = x; } - function g5(uint x) public payable returns (uint) { b[uint8(msg.data[4])] = x; } - function g6(uint x) public payable returns (uint) { b[uint8(msg.data[5])] = x; } - function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; } - function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; } - function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; } - function g0(uint x) public payable returns (uint) { require(x > 10); } -} -contract Medium { - uint public a; - uint[] public b; - function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } - function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; } - function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; } - function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; } - function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; } - function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; } - function g0(uint x) public payable returns (uint) { require(x > 10); } -} -contract Small { - uint public a; - uint[] public b; - function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } - function () external payable {} -} diff --git a/test/cmdlineTests/gas_test_dispatch_optimize.sol.args b/test/cmdlineTests/gas_test_dispatch_optimize.sol.args deleted file mode 100644 index 814e0591..00000000 --- a/test/cmdlineTests/gas_test_dispatch_optimize.sol.args +++ /dev/null @@ -1 +0,0 @@ ---optimize --optimize-runs 2 --gas diff --git a/test/cmdlineTests/gas_test_dispatch_optimize.sol.stdout b/test/cmdlineTests/gas_test_dispatch_optimize.sol.stdout deleted file mode 100644 index 76fa086e..00000000 --- a/test/cmdlineTests/gas_test_dispatch_optimize.sol.stdout +++ /dev/null @@ -1,53 +0,0 @@ - -======= gas_test_dispatch_optimize.sol:Large ======= -Gas estimation: -construction: - 300 + 262000 = 262300 -external: - a(): 463 - b(uint256): 1173 - f0(uint256): 399 - f1(uint256): 41164 - f2(uint256): 21224 - f3(uint256): 21312 - f4(uint256): 21290 - f5(uint256): 21268 - f6(uint256): 21180 - f7(uint256): 20960 - f8(uint256): 21092 - f9(uint256): 21114 - g0(uint256): 639 - g1(uint256): 40876 - g2(uint256): 20958 - g3(uint256): 21046 - g4(uint256): 21024 - g5(uint256): 21112 - g6(uint256): 20892 - g7(uint256): 21002 - g8(uint256): 20980 - g9(uint256): 20826 - -======= gas_test_dispatch_optimize.sol:Medium ======= -Gas estimation: -construction: - 190 + 143000 = 143190 -external: - a(): 463 - b(uint256): 931 - f1(uint256): 40944 - f2(uint256): 20982 - f3(uint256): 21026 - g0(uint256): 397 - g7(uint256): 20892 - g8(uint256): 20870 - g9(uint256): 20826 - -======= gas_test_dispatch_optimize.sol:Small ======= -Gas estimation: -construction: - 117 + 67400 = 67517 -external: - fallback: 183 - a(): 441 - b(uint256): 821 - f1(uint256): 40878 diff --git a/test/cmdlineTests/gas_test_dispatch_optimize/args b/test/cmdlineTests/gas_test_dispatch_optimize/args new file mode 100644 index 00000000..814e0591 --- /dev/null +++ b/test/cmdlineTests/gas_test_dispatch_optimize/args @@ -0,0 +1 @@ +--optimize --optimize-runs 2 --gas diff --git a/test/cmdlineTests/gas_test_dispatch_optimize/input.sol b/test/cmdlineTests/gas_test_dispatch_optimize/input.sol new file mode 100644 index 00000000..a5ca9e7d --- /dev/null +++ b/test/cmdlineTests/gas_test_dispatch_optimize/input.sol @@ -0,0 +1,43 @@ +pragma solidity >=0.0; + +contract Large { + uint public a; + uint[] public b; + function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } + function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; } + function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; } + function f4(uint x) public returns (uint) { b[uint8(msg.data[3])] = x; } + function f5(uint x) public returns (uint) { b[uint8(msg.data[4])] = x; } + function f6(uint x) public returns (uint) { b[uint8(msg.data[5])] = x; } + function f7(uint x) public returns (uint) { b[uint8(msg.data[6])] = x; } + function f8(uint x) public returns (uint) { b[uint8(msg.data[7])] = x; } + function f9(uint x) public returns (uint) { b[uint8(msg.data[8])] = x; } + function f0(uint x) public pure returns (uint) { require(x > 10); } + function g1(uint x) public payable returns (uint) { a = x; b[uint8(msg.data[0])] = x; } + function g2(uint x) public payable returns (uint) { b[uint8(msg.data[1])] = x; } + function g3(uint x) public payable returns (uint) { b[uint8(msg.data[2])] = x; } + function g4(uint x) public payable returns (uint) { b[uint8(msg.data[3])] = x; } + function g5(uint x) public payable returns (uint) { b[uint8(msg.data[4])] = x; } + function g6(uint x) public payable returns (uint) { b[uint8(msg.data[5])] = x; } + function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; } + function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; } + function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; } + function g0(uint x) public payable returns (uint) { require(x > 10); } +} +contract Medium { + uint public a; + uint[] public b; + function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } + function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; } + function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; } + function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; } + function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; } + function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; } + function g0(uint x) public payable returns (uint) { require(x > 10); } +} +contract Small { + uint public a; + uint[] public b; + function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; } + function () external payable {} +} diff --git a/test/cmdlineTests/gas_test_dispatch_optimize/output b/test/cmdlineTests/gas_test_dispatch_optimize/output new file mode 100644 index 00000000..fd8e9e93 --- /dev/null +++ b/test/cmdlineTests/gas_test_dispatch_optimize/output @@ -0,0 +1,53 @@ + +======= gas_test_dispatch_optimize/input.sol:Large ======= +Gas estimation: +construction: + 300 + 262000 = 262300 +external: + a(): 463 + b(uint256): 1173 + f0(uint256): 399 + f1(uint256): 41164 + f2(uint256): 21224 + f3(uint256): 21312 + f4(uint256): 21290 + f5(uint256): 21268 + f6(uint256): 21180 + f7(uint256): 20960 + f8(uint256): 21092 + f9(uint256): 21114 + g0(uint256): 639 + g1(uint256): 40876 + g2(uint256): 20958 + g3(uint256): 21046 + g4(uint256): 21024 + g5(uint256): 21112 + g6(uint256): 20892 + g7(uint256): 21002 + g8(uint256): 20980 + g9(uint256): 20826 + +======= gas_test_dispatch_optimize/input.sol:Medium ======= +Gas estimation: +construction: + 190 + 143000 = 143190 +external: + a(): 463 + b(uint256): 931 + f1(uint256): 40944 + f2(uint256): 20982 + f3(uint256): 21026 + g0(uint256): 397 + g7(uint256): 20892 + g8(uint256): 20870 + g9(uint256): 20826 + +======= gas_test_dispatch_optimize/input.sol:Small ======= +Gas estimation: +construction: + 117 + 67400 = 67517 +external: + fallback: 183 + a(): 441 + b(uint256): 821 + f1(uint256): 40878 diff --git a/test/cmdlineTests/standard.json b/test/cmdlineTests/standard.json deleted file mode 100644 index 826253b8..00000000 --- a/test/cmdlineTests/standard.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - } -} diff --git a/test/cmdlineTests/standard.json.exit b/test/cmdlineTests/standard.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard.json.stdout b/test/cmdlineTests/standard.json.stdout deleted file mode 100644 index 59b90c8c..00000000 --- a/test/cmdlineTests/standard.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_binaries_requested.json b/test/cmdlineTests/standard_binaries_requested.json deleted file mode 100644 index 65f19543..00000000 --- a/test/cmdlineTests/standard_binaries_requested.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - { - "outputSelection": - { - "*": { "*": ["evm.gasEstimates"] } - } - } -} diff --git a/test/cmdlineTests/standard_binaries_requested.json.stdout b/test/cmdlineTests/standard_binaries_requested.json.stdout deleted file mode 100644 index 2baef22a..00000000 --- a/test/cmdlineTests/standard_binaries_requested.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"contracts":{"A":{"C":{"evm":{"gasEstimates":{"creation":{"codeDepositCost":"25600","executionCost":"75","totalCost":"25675"},"external":{"f()":"127"}}}}}},"sources":{"A":{"id":0}}} \ No newline at end of file diff --git a/test/cmdlineTests/standard_binaries_requested/input.json b/test/cmdlineTests/standard_binaries_requested/input.json new file mode 100644 index 00000000..65f19543 --- /dev/null +++ b/test/cmdlineTests/standard_binaries_requested/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["evm.gasEstimates"] } + } + } +} diff --git a/test/cmdlineTests/standard_binaries_requested/output.json b/test/cmdlineTests/standard_binaries_requested/output.json new file mode 100644 index 00000000..2baef22a --- /dev/null +++ b/test/cmdlineTests/standard_binaries_requested/output.json @@ -0,0 +1 @@ +{"contracts":{"A":{"C":{"evm":{"gasEstimates":{"creation":{"codeDepositCost":"25600","executionCost":"75","totalCost":"25675"},"external":{"f()":"127"}}}}}},"sources":{"A":{"id":0}}} \ No newline at end of file diff --git a/test/cmdlineTests/standard_default_success/exit b/test/cmdlineTests/standard_default_success/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_default_success/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_default_success/input.json b/test/cmdlineTests/standard_default_success/input.json new file mode 100644 index 00000000..826253b8 --- /dev/null +++ b/test/cmdlineTests/standard_default_success/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + } +} diff --git a/test/cmdlineTests/standard_default_success/output.json b/test/cmdlineTests/standard_default_success/output.json new file mode 100644 index 00000000..59b90c8c --- /dev/null +++ b/test/cmdlineTests/standard_default_success/output.json @@ -0,0 +1 @@ +{"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_methodIdentifiersRequested.json b/test/cmdlineTests/standard_methodIdentifiersRequested.json deleted file mode 100644 index 79a3c75d..00000000 --- a/test/cmdlineTests/standard_methodIdentifiersRequested.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - { - "outputSelection": - { - "*": { "*": ["evm.methodIdentifiers"] } - } - } -} diff --git a/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout b/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout deleted file mode 100644 index 7e3f139f..00000000 --- a/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"contracts":{"A":{"C":{"evm":{"methodIdentifiers":{"f()":"26121ff0"}}}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_method_identifiers_requested/input.json b/test/cmdlineTests/standard_method_identifiers_requested/input.json new file mode 100644 index 00000000..79a3c75d --- /dev/null +++ b/test/cmdlineTests/standard_method_identifiers_requested/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["evm.methodIdentifiers"] } + } + } +} diff --git a/test/cmdlineTests/standard_method_identifiers_requested/output.json b/test/cmdlineTests/standard_method_identifiers_requested/output.json new file mode 100644 index 00000000..7e3f139f --- /dev/null +++ b/test/cmdlineTests/standard_method_identifiers_requested/output.json @@ -0,0 +1 @@ +{"contracts":{"A":{"C":{"evm":{"methodIdentifiers":{"f()":"26121ff0"}}}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_only_ast_requested.json b/test/cmdlineTests/standard_only_ast_requested.json deleted file mode 100644 index 7abd6da5..00000000 --- a/test/cmdlineTests/standard_only_ast_requested.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - { - "outputSelection": - { - "*": { "": ["ast"] } - } - } -} diff --git a/test/cmdlineTests/standard_only_ast_requested.json.stdout b/test/cmdlineTests/standard_only_ast_requested.json.stdout deleted file mode 100644 index b884ab7d..00000000 --- a/test/cmdlineTests/standard_only_ast_requested.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"0:22:0"},{"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"61:2:0","statements":[]},"documentation":null,"id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"46:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"61:0:0"},"scope":6,"src":"36:27:0","stateMutability":"pure","superFunction":null,"visibility":"public"}],"scope":7,"src":"23:42:0"}],"src":"0:65:0"},"id":0}}} diff --git a/test/cmdlineTests/standard_only_ast_requested/input.json b/test/cmdlineTests/standard_only_ast_requested/input.json new file mode 100644 index 00000000..7abd6da5 --- /dev/null +++ b/test/cmdlineTests/standard_only_ast_requested/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "": ["ast"] } + } + } +} diff --git a/test/cmdlineTests/standard_only_ast_requested/output.json b/test/cmdlineTests/standard_only_ast_requested/output.json new file mode 100644 index 00000000..b884ab7d --- /dev/null +++ b/test/cmdlineTests/standard_only_ast_requested/output.json @@ -0,0 +1 @@ +{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"0:22:0"},{"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"61:2:0","statements":[]},"documentation":null,"id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"46:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"61:0:0"},"scope":6,"src":"36:27:0","stateMutability":"pure","superFunction":null,"visibility":"public"}],"scope":7,"src":"23:42:0"}],"src":"0:65:0"},"id":0}}} diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json deleted file mode 100644 index 51dbce41..00000000 --- a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "auxiliaryInput": - { - "key1": "test" - } -} diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.exit b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.stdout b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.stdout deleted file mode 100644 index 077ac47e..00000000 --- a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input/exit b/test/cmdlineTests/standard_wrong_key_auxiliary_input/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_auxiliary_input/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input/input.json b/test/cmdlineTests/standard_wrong_key_auxiliary_input/input.json new file mode 100644 index 00000000..51dbce41 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_auxiliary_input/input.json @@ -0,0 +1,14 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "auxiliaryInput": + { + "key1": "test" + } +} diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input/output.json b/test/cmdlineTests/standard_wrong_key_auxiliary_input/output.json new file mode 100644 index 00000000..077ac47e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_auxiliary_input/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_metadata.json b/test/cmdlineTests/standard_wrong_key_metadata.json deleted file mode 100644 index 490e489a..00000000 --- a/test/cmdlineTests/standard_wrong_key_metadata.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "evmVersion": "byzantium", - "metadata": { - "key1": "test", - "useLiteralContent": true - } - } -} diff --git a/test/cmdlineTests/standard_wrong_key_metadata.json.exit b/test/cmdlineTests/standard_wrong_key_metadata.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_key_metadata.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_key_metadata.json.stdout b/test/cmdlineTests/standard_wrong_key_metadata.json.stdout deleted file mode 100644 index 077ac47e..00000000 --- a/test/cmdlineTests/standard_wrong_key_metadata.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_metadata/exit b/test/cmdlineTests/standard_wrong_key_metadata/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_metadata/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_key_metadata/input.json b/test/cmdlineTests/standard_wrong_key_metadata/input.json new file mode 100644 index 00000000..490e489a --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_metadata/input.json @@ -0,0 +1,22 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "byzantium", + "metadata": { + "key1": "test", + "useLiteralContent": true + } + } +} diff --git a/test/cmdlineTests/standard_wrong_key_metadata/output.json b/test/cmdlineTests/standard_wrong_key_metadata/output.json new file mode 100644 index 00000000..077ac47e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_metadata/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_optimizer.json b/test/cmdlineTests/standard_wrong_key_optimizer.json deleted file mode 100644 index c28c3a92..00000000 --- a/test/cmdlineTests/standard_wrong_key_optimizer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - { - "optimizer": { - "key1": "test", - "enabled": true, - "runs": 200 - }, - "evmVersion": "byzantium", - "metadata": { - "useLiteralContent": true - } - } -} diff --git a/test/cmdlineTests/standard_wrong_key_optimizer.json.exit b/test/cmdlineTests/standard_wrong_key_optimizer.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_key_optimizer.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_key_optimizer.json.stdout b/test/cmdlineTests/standard_wrong_key_optimizer.json.stdout deleted file mode 100644 index 077ac47e..00000000 --- a/test/cmdlineTests/standard_wrong_key_optimizer.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_optimizer/exit b/test/cmdlineTests/standard_wrong_key_optimizer/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_optimizer/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_key_optimizer/input.json b/test/cmdlineTests/standard_wrong_key_optimizer/input.json new file mode 100644 index 00000000..c28c3a92 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_optimizer/input.json @@ -0,0 +1,22 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "optimizer": { + "key1": "test", + "enabled": true, + "runs": 200 + }, + "evmVersion": "byzantium", + "metadata": { + "useLiteralContent": true + } + } +} diff --git a/test/cmdlineTests/standard_wrong_key_optimizer/output.json b/test/cmdlineTests/standard_wrong_key_optimizer/output.json new file mode 100644 index 00000000..077ac47e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_optimizer/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_root.json b/test/cmdlineTests/standard_wrong_key_root.json deleted file mode 100644 index 4689c50c..00000000 --- a/test/cmdlineTests/standard_wrong_key_root.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "language": "Solidity", - "key1": "test", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - } -} diff --git a/test/cmdlineTests/standard_wrong_key_root.json.exit b/test/cmdlineTests/standard_wrong_key_root.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_key_root.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_key_root.json.stdout b/test/cmdlineTests/standard_wrong_key_root.json.stdout deleted file mode 100644 index 077ac47e..00000000 --- a/test/cmdlineTests/standard_wrong_key_root.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_root/exit b/test/cmdlineTests/standard_wrong_key_root/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_root/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_key_root/input.json b/test/cmdlineTests/standard_wrong_key_root/input.json new file mode 100644 index 00000000..4689c50c --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_root/input.json @@ -0,0 +1,11 @@ +{ + "language": "Solidity", + "key1": "test", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + } +} diff --git a/test/cmdlineTests/standard_wrong_key_root/output.json b/test/cmdlineTests/standard_wrong_key_root/output.json new file mode 100644 index 00000000..077ac47e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_root/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_settings.json b/test/cmdlineTests/standard_wrong_key_settings.json deleted file mode 100644 index d7809b1c..00000000 --- a/test/cmdlineTests/standard_wrong_key_settings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "evmVersion": "byzantium", - "metadata": { - "useLiteralContent": true - }, - "key1": "test" - } -} diff --git a/test/cmdlineTests/standard_wrong_key_settings.json.exit b/test/cmdlineTests/standard_wrong_key_settings.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_key_settings.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_key_settings.json.stdout b/test/cmdlineTests/standard_wrong_key_settings.json.stdout deleted file mode 100644 index 077ac47e..00000000 --- a/test/cmdlineTests/standard_wrong_key_settings.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_settings/exit b/test/cmdlineTests/standard_wrong_key_settings/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_settings/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_key_settings/input.json b/test/cmdlineTests/standard_wrong_key_settings/input.json new file mode 100644 index 00000000..d7809b1c --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_settings/input.json @@ -0,0 +1,22 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "byzantium", + "metadata": { + "useLiteralContent": true + }, + "key1": "test" + } +} diff --git a/test/cmdlineTests/standard_wrong_key_settings/output.json b/test/cmdlineTests/standard_wrong_key_settings/output.json new file mode 100644 index 00000000..077ac47e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_settings/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_source.json b/test/cmdlineTests/standard_wrong_key_source.json deleted file mode 100644 index d8a8aa16..00000000 --- a/test/cmdlineTests/standard_wrong_key_source.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "key1": "test", - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - } -} diff --git a/test/cmdlineTests/standard_wrong_key_source.json.exit b/test/cmdlineTests/standard_wrong_key_source.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_key_source.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_key_source.json.stdout b/test/cmdlineTests/standard_wrong_key_source.json.stdout deleted file mode 100644 index 077ac47e..00000000 --- a/test/cmdlineTests/standard_wrong_key_source.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_key_source/exit b/test/cmdlineTests/standard_wrong_key_source/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_source/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_key_source/input.json b/test/cmdlineTests/standard_wrong_key_source/input.json new file mode 100644 index 00000000..d8a8aa16 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_source/input.json @@ -0,0 +1,11 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "key1": "test", + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + } +} diff --git a/test/cmdlineTests/standard_wrong_key_source/output.json b/test/cmdlineTests/standard_wrong_key_source/output.json new file mode 100644 index 00000000..077ac47e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_key_source/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json deleted file mode 100644 index 8d2c7593..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "auxiliaryInput": [1, 2, 3] -} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.stdout b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.stdout deleted file mode 100644 index 046cb6d9..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.stdout +++ /dev/null @@ -1,2 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput\" must be an object","message":"\"auxiliaryInput\" must be an object","severity":"error","type":"JSONError"}]} - diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input/exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input/input.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input/input.json new file mode 100644 index 00000000..8d2c7593 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input/input.json @@ -0,0 +1,11 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "auxiliaryInput": [1, 2, 3] +} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input/output.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input/output.json new file mode 100644 index 00000000..046cb6d9 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input/output.json @@ -0,0 +1,2 @@ +{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput\" must be an object","message":"\"auxiliaryInput\" must be an object","severity":"error","type":"JSONError"}]} + diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json deleted file mode 100644 index 9175050f..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - }, - "settings": { - "outputSelection": { - "fileA": { - "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] - } - } - }, - "auxiliaryInput": - { - "smtlib2responses": "not an object" - } -} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.stdout b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.stdout deleted file mode 100644 index 3efaea20..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput.smtlib2responses\" must be an object.","message":"\"auxiliaryInput.smtlib2responses\" must be an object.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/input.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/input.json new file mode 100644 index 00000000..9175050f --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/input.json @@ -0,0 +1,20 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], + "": [ "legacyAST" ] + } + } + }, + "auxiliaryInput": + { + "smtlib2responses": "not an object" + } +} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/output.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/output.json new file mode 100644 index 00000000..3efaea20 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput.smtlib2responses\" must be an object.","message":"\"auxiliaryInput.smtlib2responses\" must be an object.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json deleted file mode 100644 index aa7d451b..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - }, - "settings": { - "outputSelection": { - "fileA": { - "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] - } - } - }, - "auxiliaryInput": - { - "smtlib2responses": - { - "abc": ["not a string"] - } - } -} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.stdout b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.stdout deleted file mode 100644 index a05176be..00000000 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"smtlib2Responses.abc\" must be a string.","message":"\"smtlib2Responses.abc\" must be a string.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/input.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/input.json new file mode 100644 index 00000000..aa7d451b --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/input.json @@ -0,0 +1,23 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], + "": [ "legacyAST" ] + } + } + }, + "auxiliaryInput": + { + "smtlib2responses": + { + "abc": ["not a string"] + } + } +} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/output.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/output.json new file mode 100644 index 00000000..a05176be --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"smtlib2Responses.abc\" must be a string.","message":"\"smtlib2Responses.abc\" must be a string.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_metadata.json b/test/cmdlineTests/standard_wrong_type_metadata.json deleted file mode 100644 index d4dd06a1..00000000 --- a/test/cmdlineTests/standard_wrong_type_metadata.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "evmVersion": "byzantium", - "metadata": ["meta1", "meta2", "meta3"] - } -} diff --git a/test/cmdlineTests/standard_wrong_type_metadata.json.exit b/test/cmdlineTests/standard_wrong_type_metadata.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_metadata.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_metadata.json.stdout b/test/cmdlineTests/standard_wrong_type_metadata.json.stdout deleted file mode 100644 index 7b997cec..00000000 --- a/test/cmdlineTests/standard_wrong_type_metadata.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings.metadata\" must be an object","message":"\"settings.metadata\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_metadata/exit b/test/cmdlineTests/standard_wrong_type_metadata/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_metadata/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_metadata/input.json b/test/cmdlineTests/standard_wrong_type_metadata/input.json new file mode 100644 index 00000000..d4dd06a1 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_metadata/input.json @@ -0,0 +1,19 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "byzantium", + "metadata": ["meta1", "meta2", "meta3"] + } +} diff --git a/test/cmdlineTests/standard_wrong_type_metadata/output.json b/test/cmdlineTests/standard_wrong_type_metadata/output.json new file mode 100644 index 00000000..7b997cec --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_metadata/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings.metadata\" must be an object","message":"\"settings.metadata\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_optimizer.json b/test/cmdlineTests/standard_wrong_type_optimizer.json deleted file mode 100644 index b42ca550..00000000 --- a/test/cmdlineTests/standard_wrong_type_optimizer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - { - "optimizer": 1, - "evmVersion": "byzantium", - "metadata": { - "useLiteralContent": true - } - } -} diff --git a/test/cmdlineTests/standard_wrong_type_optimizer.json.exit b/test/cmdlineTests/standard_wrong_type_optimizer.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_optimizer.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_optimizer.json.stdout b/test/cmdlineTests/standard_wrong_type_optimizer.json.stdout deleted file mode 100644 index d43b6470..00000000 --- a/test/cmdlineTests/standard_wrong_type_optimizer.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer\" must be an object","message":"\"settings.optimizer\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_optimizer/exit b/test/cmdlineTests/standard_wrong_type_optimizer/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_optimizer/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_optimizer/input.json b/test/cmdlineTests/standard_wrong_type_optimizer/input.json new file mode 100644 index 00000000..b42ca550 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_optimizer/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "optimizer": 1, + "evmVersion": "byzantium", + "metadata": { + "useLiteralContent": true + } + } +} diff --git a/test/cmdlineTests/standard_wrong_type_optimizer/output.json b/test/cmdlineTests/standard_wrong_type_optimizer/output.json new file mode 100644 index 00000000..d43b6470 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_optimizer/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer\" must be an object","message":"\"settings.optimizer\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection.json b/test/cmdlineTests/standard_wrong_type_output_selection.json deleted file mode 100644 index a7b615d1..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - }, - "settings": { - "outputSelection": "not an object" - } -} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection.json.exit b/test/cmdlineTests/standard_wrong_type_output_selection.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_output_selection.json.stdout b/test/cmdlineTests/standard_wrong_type_output_selection.json.stdout deleted file mode 100644 index 39e74882..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection\" must be an object","message":"\"settings.outputSelection\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection/exit b/test/cmdlineTests/standard_wrong_type_output_selection/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_output_selection/input.json b/test/cmdlineTests/standard_wrong_type_output_selection/input.json new file mode 100644 index 00000000..a7b615d1 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection/input.json @@ -0,0 +1,11 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": "not an object" + } +} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection/output.json b/test/cmdlineTests/standard_wrong_type_output_selection/output.json new file mode 100644 index 00000000..39e74882 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection\" must be an object","message":"\"settings.outputSelection\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json deleted file mode 100644 index 9840a97e..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - }, - "settings": { - "outputSelection": { - "fileA": { - "A": "it's a contract, but not an array!", - "": [ "legacyAST" ] - } - } - } -} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.exit b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.stdout b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.stdout deleted file mode 100644 index a4ba320e..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA.A\" must be a string array","message":"\"settings.outputSelection.fileA.A\" must be a string array","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract/exit b/test/cmdlineTests/standard_wrong_type_output_selection_contract/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_contract/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract/input.json b/test/cmdlineTests/standard_wrong_type_output_selection_contract/input.json new file mode 100644 index 00000000..9840a97e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_contract/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": "it's a contract, but not an array!", + "": [ "legacyAST" ] + } + } + } +} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract/output.json b/test/cmdlineTests/standard_wrong_type_output_selection_contract/output.json new file mode 100644 index 00000000..a4ba320e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_contract/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA.A\" must be a string array","message":"\"settings.outputSelection.fileA.A\" must be a string array","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file.json b/test/cmdlineTests/standard_wrong_type_output_selection_file.json deleted file mode 100644 index 7ab12ba8..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_file.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - }, - "settings": { - "outputSelection": { - "fileA": "awesome file!" - } - } -} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file.json.exit b/test/cmdlineTests/standard_wrong_type_output_selection_file.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_file.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file.json.stdout b/test/cmdlineTests/standard_wrong_type_output_selection_file.json.stdout deleted file mode 100644 index 8874e636..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_file.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA\" must be an object","message":"\"settings.outputSelection.fileA\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file/exit b/test/cmdlineTests/standard_wrong_type_output_selection_file/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_file/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file/input.json b/test/cmdlineTests/standard_wrong_type_output_selection_file/input.json new file mode 100644 index 00000000..7ab12ba8 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_file/input.json @@ -0,0 +1,13 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": "awesome file!" + } + } +} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file/output.json b/test/cmdlineTests/standard_wrong_type_output_selection_file/output.json new file mode 100644 index 00000000..8874e636 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_file/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA\" must be an object","message":"\"settings.outputSelection.fileA\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output.json b/test/cmdlineTests/standard_wrong_type_output_selection_output.json deleted file mode 100644 index 3e5cd661..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_output.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - }, - "settings": { - "outputSelection": { - "fileA": { - "A": [ 1, 2, 3 ,4], - "": [ "legacyAST" ] - } - } - } -} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output.json.exit b/test/cmdlineTests/standard_wrong_type_output_selection_output.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_output.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output.json.stdout b/test/cmdlineTests/standard_wrong_type_output_selection_output.json.stdout deleted file mode 100644 index a4ba320e..00000000 --- a/test/cmdlineTests/standard_wrong_type_output_selection_output.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA.A\" must be a string array","message":"\"settings.outputSelection.fileA.A\" must be a string array","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output/exit b/test/cmdlineTests/standard_wrong_type_output_selection_output/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_output/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output/input.json b/test/cmdlineTests/standard_wrong_type_output_selection_output/input.json new file mode 100644 index 00000000..3e5cd661 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_output/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ 1, 2, 3 ,4], + "": [ "legacyAST" ] + } + } + } +} diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output/output.json b/test/cmdlineTests/standard_wrong_type_output_selection_output/output.json new file mode 100644 index 00000000..a4ba320e --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_output_selection_output/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA.A\" must be a string array","message":"\"settings.outputSelection.fileA.A\" must be a string array","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_remappings.json b/test/cmdlineTests/standard_wrong_type_remappings.json deleted file mode 100644 index 1436e014..00000000 --- a/test/cmdlineTests/standard_wrong_type_remappings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - }, - "settings": { - "outputSelection": { - "fileA": { - "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] - } - }, - "remappings": "not an object" - } -} diff --git a/test/cmdlineTests/standard_wrong_type_remappings.json.exit b/test/cmdlineTests/standard_wrong_type_remappings.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_remappings.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_remappings.json.stdout b/test/cmdlineTests/standard_wrong_type_remappings.json.stdout deleted file mode 100644 index b5e4ea5c..00000000 --- a/test/cmdlineTests/standard_wrong_type_remappings.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings.remappings\" must be an array of strings.","message":"\"settings.remappings\" must be an array of strings.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_remappings/exit b/test/cmdlineTests/standard_wrong_type_remappings/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_remappings/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_remappings/input.json b/test/cmdlineTests/standard_wrong_type_remappings/input.json new file mode 100644 index 00000000..1436e014 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_remappings/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], + "": [ "legacyAST" ] + } + }, + "remappings": "not an object" + } +} diff --git a/test/cmdlineTests/standard_wrong_type_remappings/output.json b/test/cmdlineTests/standard_wrong_type_remappings/output.json new file mode 100644 index 00000000..b5e4ea5c --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_remappings/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings.remappings\" must be an array of strings.","message":"\"settings.remappings\" must be an array of strings.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry.json b/test/cmdlineTests/standard_wrong_type_remappings_entry.json deleted file mode 100644 index c96611f3..00000000 --- a/test/cmdlineTests/standard_wrong_type_remappings_entry.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - }, - "settings": { - "outputSelection": { - "fileA": { - "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] - } - }, - "remappings": [1, 2 ,3 ,4] - } -} diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry.json.exit b/test/cmdlineTests/standard_wrong_type_remappings_entry.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_remappings_entry.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry.json.stdout b/test/cmdlineTests/standard_wrong_type_remappings_entry.json.stdout deleted file mode 100644 index 0fc71ded..00000000 --- a/test/cmdlineTests/standard_wrong_type_remappings_entry.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings.remappings\" must be an array of strings","message":"\"settings.remappings\" must be an array of strings","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry/exit b/test/cmdlineTests/standard_wrong_type_remappings_entry/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_remappings_entry/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry/input.json b/test/cmdlineTests/standard_wrong_type_remappings_entry/input.json new file mode 100644 index 00000000..c96611f3 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_remappings_entry/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], + "": [ "legacyAST" ] + } + }, + "remappings": [1, 2 ,3 ,4] + } +} diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry/output.json b/test/cmdlineTests/standard_wrong_type_remappings_entry/output.json new file mode 100644 index 00000000..0fc71ded --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_remappings_entry/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings.remappings\" must be an array of strings","message":"\"settings.remappings\" must be an array of strings","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_root.json b/test/cmdlineTests/standard_wrong_type_root.json deleted file mode 100644 index 4763607a..00000000 --- a/test/cmdlineTests/standard_wrong_type_root.json +++ /dev/null @@ -1 +0,0 @@ -["abc"] diff --git a/test/cmdlineTests/standard_wrong_type_root.json.exit b/test/cmdlineTests/standard_wrong_type_root.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_root.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_root.json.stdout b/test/cmdlineTests/standard_wrong_type_root.json.stdout deleted file mode 100644 index 15c12e77..00000000 --- a/test/cmdlineTests/standard_wrong_type_root.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"Input is not a JSON object.","message":"Input is not a JSON object.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_root/exit b/test/cmdlineTests/standard_wrong_type_root/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_root/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_root/input.json b/test/cmdlineTests/standard_wrong_type_root/input.json new file mode 100644 index 00000000..4763607a --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_root/input.json @@ -0,0 +1 @@ +["abc"] diff --git a/test/cmdlineTests/standard_wrong_type_root/output.json b/test/cmdlineTests/standard_wrong_type_root/output.json new file mode 100644 index 00000000..15c12e77 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_root/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"Input is not a JSON object.","message":"Input is not a JSON object.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_settings.json b/test/cmdlineTests/standard_wrong_type_settings.json deleted file mode 100644 index 7cdb0881..00000000 --- a/test/cmdlineTests/standard_wrong_type_settings.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - }, - "settings": - [ - { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "evmVersion": "byzantium", - "metadata": { - "useLiteralContent": true - } - } - ] -} diff --git a/test/cmdlineTests/standard_wrong_type_settings.json.exit b/test/cmdlineTests/standard_wrong_type_settings.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_settings.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_settings.json.stdout b/test/cmdlineTests/standard_wrong_type_settings.json.stdout deleted file mode 100644 index c78c6086..00000000 --- a/test/cmdlineTests/standard_wrong_type_settings.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"settings\" must be an object","message":"\"settings\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_settings/exit b/test/cmdlineTests/standard_wrong_type_settings/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_settings/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_settings/input.json b/test/cmdlineTests/standard_wrong_type_settings/input.json new file mode 100644 index 00000000..7cdb0881 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_settings/input.json @@ -0,0 +1,23 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + [ + { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "byzantium", + "metadata": { + "useLiteralContent": true + } + } + ] +} diff --git a/test/cmdlineTests/standard_wrong_type_settings/output.json b/test/cmdlineTests/standard_wrong_type_settings/output.json new file mode 100644 index 00000000..c78c6086 --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_settings/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"settings\" must be an object","message":"\"settings\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_source.json b/test/cmdlineTests/standard_wrong_type_source.json deleted file mode 100644 index d58504fe..00000000 --- a/test/cmdlineTests/standard_wrong_type_source.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "A": "not an object :o", - "B": [1, 2, 3], - "C": - { - "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" - } - } -} diff --git a/test/cmdlineTests/standard_wrong_type_source.json.exit b/test/cmdlineTests/standard_wrong_type_source.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_source.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_source.json.stdout b/test/cmdlineTests/standard_wrong_type_source.json.stdout deleted file mode 100644 index 98fe32fd..00000000 --- a/test/cmdlineTests/standard_wrong_type_source.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"sources.A\" must be an object","message":"\"sources.A\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_source/exit b/test/cmdlineTests/standard_wrong_type_source/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_source/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_source/input.json b/test/cmdlineTests/standard_wrong_type_source/input.json new file mode 100644 index 00000000..d58504fe --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_source/input.json @@ -0,0 +1,12 @@ +{ + "language": "Solidity", + "sources": + { + "A": "not an object :o", + "B": [1, 2, 3], + "C": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + } +} diff --git a/test/cmdlineTests/standard_wrong_type_source/output.json b/test/cmdlineTests/standard_wrong_type_source/output.json new file mode 100644 index 00000000..98fe32fd --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_source/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"sources.A\" must be an object","message":"\"sources.A\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_sources.json b/test/cmdlineTests/standard_wrong_type_sources.json deleted file mode 100644 index 76e1ae7d..00000000 --- a/test/cmdlineTests/standard_wrong_type_sources.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "language": "Solidity", - "sources": ["source1", "source2", "source3"] -} diff --git a/test/cmdlineTests/standard_wrong_type_sources.json.exit b/test/cmdlineTests/standard_wrong_type_sources.json.exit deleted file mode 100644 index 573541ac..00000000 --- a/test/cmdlineTests/standard_wrong_type_sources.json.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/test/cmdlineTests/standard_wrong_type_sources.json.stdout b/test/cmdlineTests/standard_wrong_type_sources.json.stdout deleted file mode 100644 index ac6c613f..00000000 --- a/test/cmdlineTests/standard_wrong_type_sources.json.stdout +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"component":"general","formattedMessage":"\"sources\" is not a JSON object.","message":"\"sources\" is not a JSON object.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_wrong_type_sources/exit b/test/cmdlineTests/standard_wrong_type_sources/exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_sources/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_wrong_type_sources/input.json b/test/cmdlineTests/standard_wrong_type_sources/input.json new file mode 100644 index 00000000..76e1ae7d --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_sources/input.json @@ -0,0 +1,4 @@ +{ + "language": "Solidity", + "sources": ["source1", "source2", "source3"] +} diff --git a/test/cmdlineTests/standard_wrong_type_sources/output.json b/test/cmdlineTests/standard_wrong_type_sources/output.json new file mode 100644 index 00000000..ac6c613f --- /dev/null +++ b/test/cmdlineTests/standard_wrong_type_sources/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"sources\" is not a JSON object.","message":"\"sources\" is not a JSON object.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/too_long_line.sol b/test/cmdlineTests/too_long_line.sol deleted file mode 100644 index 7df1057a..00000000 --- a/test/cmdlineTests/too_long_line.sol +++ /dev/null @@ -1,4 +0,0 @@ -contract C { - function ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { -} -} diff --git a/test/cmdlineTests/too_long_line.sol.err b/test/cmdlineTests/too_long_line.sol.err deleted file mode 100644 index 55cd1935..00000000 --- a/test/cmdlineTests/too_long_line.sol.err +++ /dev/null @@ -1,6 +0,0 @@ -too_long_line.sol:1:1: Warning: Source file does not specify required compiler version! -contract C { -^ (Relevant source part starts here and spans across multiple lines). -too_long_line.sol:2:164: Error: Identifier not found or not unique. - ... ffffffffffffffffffffffffffffffffff(announcementType Type, string Announcement, string ... - ^--------------^ diff --git a/test/cmdlineTests/too_long_line.sol.exit b/test/cmdlineTests/too_long_line.sol.exit deleted file mode 100644 index d00491fd..00000000 --- a/test/cmdlineTests/too_long_line.sol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/test/cmdlineTests/too_long_line/err b/test/cmdlineTests/too_long_line/err new file mode 100644 index 00000000..bfbc8e1e --- /dev/null +++ b/test/cmdlineTests/too_long_line/err @@ -0,0 +1,6 @@ +too_long_line/input.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line/input.sol:2:164: Error: Identifier not found or not unique. + ... ffffffffffffffffffffffffffffffffff(announcementType Type, string Announcement, string ... + ^--------------^ diff --git a/test/cmdlineTests/too_long_line/exit b/test/cmdlineTests/too_long_line/exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/cmdlineTests/too_long_line/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/too_long_line/input.sol b/test/cmdlineTests/too_long_line/input.sol new file mode 100644 index 00000000..7df1057a --- /dev/null +++ b/test/cmdlineTests/too_long_line/input.sol @@ -0,0 +1,4 @@ +contract C { + function ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { +} +} diff --git a/test/cmdlineTests/too_long_line_both_sides_short.sol b/test/cmdlineTests/too_long_line_both_sides_short.sol deleted file mode 100644 index 062f0292..00000000 --- a/test/cmdlineTests/too_long_line_both_sides_short.sol +++ /dev/null @@ -1,5 +0,0 @@ -contract C { - function f(announcementTypeXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Type, - string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { -} -} diff --git a/test/cmdlineTests/too_long_line_both_sides_short.sol.err b/test/cmdlineTests/too_long_line_both_sides_short.sol.err deleted file mode 100644 index 9a5ebfba..00000000 --- a/test/cmdlineTests/too_long_line_both_sides_short.sol.err +++ /dev/null @@ -1,6 +0,0 @@ -too_long_line_both_sides_short.sol:1:1: Warning: Source file does not specify required compiler version! -contract C { -^ (Relevant source part starts here and spans across multiple lines). -too_long_line_both_sides_short.sol:2:15: Error: Identifier not found or not unique. - function f(announcementTypeXXXXXXXXXXXXXXXXXXX ... XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Type, - ^-------------------------------------------------------------------------^ diff --git a/test/cmdlineTests/too_long_line_both_sides_short.sol.exit b/test/cmdlineTests/too_long_line_both_sides_short.sol.exit deleted file mode 100644 index d00491fd..00000000 --- a/test/cmdlineTests/too_long_line_both_sides_short.sol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/test/cmdlineTests/too_long_line_both_sides_short/err b/test/cmdlineTests/too_long_line_both_sides_short/err new file mode 100644 index 00000000..2868fcb1 --- /dev/null +++ b/test/cmdlineTests/too_long_line_both_sides_short/err @@ -0,0 +1,6 @@ +too_long_line_both_sides_short/input.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_both_sides_short/input.sol:2:15: Error: Identifier not found or not unique. + function f(announcementTypeXXXXXXXXXXXXXXXXXXX ... XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Type, + ^-------------------------------------------------------------------------^ diff --git a/test/cmdlineTests/too_long_line_both_sides_short/exit b/test/cmdlineTests/too_long_line_both_sides_short/exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/cmdlineTests/too_long_line_both_sides_short/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/too_long_line_both_sides_short/input.sol b/test/cmdlineTests/too_long_line_both_sides_short/input.sol new file mode 100644 index 00000000..062f0292 --- /dev/null +++ b/test/cmdlineTests/too_long_line_both_sides_short/input.sol @@ -0,0 +1,5 @@ +contract C { + function f(announcementTypeXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Type, + string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { +} +} diff --git a/test/cmdlineTests/too_long_line_edge_in.sol b/test/cmdlineTests/too_long_line_edge_in.sol deleted file mode 100644 index 6f181c83..00000000 --- a/test/cmdlineTests/too_long_line_edge_in.sol +++ /dev/null @@ -1,4 +0,0 @@ -contract C { - function ffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Ty, string A) onlyOwner external { -} -} diff --git a/test/cmdlineTests/too_long_line_edge_in.sol.err b/test/cmdlineTests/too_long_line_edge_in.sol.err deleted file mode 100644 index ad3b7805..00000000 --- a/test/cmdlineTests/too_long_line_edge_in.sol.err +++ /dev/null @@ -1,6 +0,0 @@ -too_long_line_edge_in.sol:1:1: Warning: Source file does not specify required compiler version! -contract C { -^ (Relevant source part starts here and spans across multiple lines). -too_long_line_edge_in.sol:2:36: Error: Identifier not found or not unique. - function ffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Ty, string A) onlyOwner external { - ^----------------------------------------------------------------------------------------------^ diff --git a/test/cmdlineTests/too_long_line_edge_in.sol.exit b/test/cmdlineTests/too_long_line_edge_in.sol.exit deleted file mode 100644 index d00491fd..00000000 --- a/test/cmdlineTests/too_long_line_edge_in.sol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/test/cmdlineTests/too_long_line_edge_in/err b/test/cmdlineTests/too_long_line_edge_in/err new file mode 100644 index 00000000..626451e1 --- /dev/null +++ b/test/cmdlineTests/too_long_line_edge_in/err @@ -0,0 +1,6 @@ +too_long_line_edge_in/input.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_edge_in/input.sol:2:36: Error: Identifier not found or not unique. + function ffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Ty, string A) onlyOwner external { + ^----------------------------------------------------------------------------------------------^ diff --git a/test/cmdlineTests/too_long_line_edge_in/exit b/test/cmdlineTests/too_long_line_edge_in/exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/cmdlineTests/too_long_line_edge_in/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/too_long_line_edge_in/input.sol b/test/cmdlineTests/too_long_line_edge_in/input.sol new file mode 100644 index 00000000..6f181c83 --- /dev/null +++ b/test/cmdlineTests/too_long_line_edge_in/input.sol @@ -0,0 +1,4 @@ +contract C { + function ffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Ty, string A) onlyOwner external { +} +} diff --git a/test/cmdlineTests/too_long_line_edge_out.sol b/test/cmdlineTests/too_long_line_edge_out.sol deleted file mode 100644 index 29d3cee6..00000000 --- a/test/cmdlineTests/too_long_line_edge_out.sol +++ /dev/null @@ -1,4 +0,0 @@ -contract C { - function fffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Typ, string A) onlyOwner external { -} -} diff --git a/test/cmdlineTests/too_long_line_edge_out.sol.err b/test/cmdlineTests/too_long_line_edge_out.sol.err deleted file mode 100644 index d8495c11..00000000 --- a/test/cmdlineTests/too_long_line_edge_out.sol.err +++ /dev/null @@ -1,6 +0,0 @@ -too_long_line_edge_out.sol:1:1: Warning: Source file does not specify required compiler version! -contract C { -^ (Relevant source part starts here and spans across multiple lines). -too_long_line_edge_out.sol:2:37: Error: Identifier not found or not unique. - ... function fffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Typ, string A) onlyOwner external ... - ^----------------------------------------------------------------------------------------------^ diff --git a/test/cmdlineTests/too_long_line_edge_out.sol.exit b/test/cmdlineTests/too_long_line_edge_out.sol.exit deleted file mode 100644 index d00491fd..00000000 --- a/test/cmdlineTests/too_long_line_edge_out.sol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/test/cmdlineTests/too_long_line_edge_out/err b/test/cmdlineTests/too_long_line_edge_out/err new file mode 100644 index 00000000..7a4f935a --- /dev/null +++ b/test/cmdlineTests/too_long_line_edge_out/err @@ -0,0 +1,6 @@ +too_long_line_edge_out/input.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_edge_out/input.sol:2:37: Error: Identifier not found or not unique. + ... function fffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Typ, string A) onlyOwner external ... + ^----------------------------------------------------------------------------------------------^ diff --git a/test/cmdlineTests/too_long_line_edge_out/exit b/test/cmdlineTests/too_long_line_edge_out/exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/cmdlineTests/too_long_line_edge_out/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/too_long_line_edge_out/input.sol b/test/cmdlineTests/too_long_line_edge_out/input.sol new file mode 100644 index 00000000..29d3cee6 --- /dev/null +++ b/test/cmdlineTests/too_long_line_edge_out/input.sol @@ -0,0 +1,4 @@ +contract C { + function fffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Typ, string A) onlyOwner external { +} +} diff --git a/test/cmdlineTests/too_long_line_left_short.sol b/test/cmdlineTests/too_long_line_left_short.sol deleted file mode 100644 index 2accfcce..00000000 --- a/test/cmdlineTests/too_long_line_left_short.sol +++ /dev/null @@ -1,4 +0,0 @@ -contract C { - function f(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { -} -} diff --git a/test/cmdlineTests/too_long_line_left_short.sol.err b/test/cmdlineTests/too_long_line_left_short.sol.err deleted file mode 100644 index 00b6be5c..00000000 --- a/test/cmdlineTests/too_long_line_left_short.sol.err +++ /dev/null @@ -1,6 +0,0 @@ -too_long_line_left_short.sol:1:1: Warning: Source file does not specify required compiler version! -contract C { -^ (Relevant source part starts here and spans across multiple lines). -too_long_line_left_short.sol:2:15: Error: Identifier not found or not unique. - function f(announcementType Type, string Announcement, string ... - ^--------------^ diff --git a/test/cmdlineTests/too_long_line_left_short.sol.exit b/test/cmdlineTests/too_long_line_left_short.sol.exit deleted file mode 100644 index d00491fd..00000000 --- a/test/cmdlineTests/too_long_line_left_short.sol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/test/cmdlineTests/too_long_line_left_short/err b/test/cmdlineTests/too_long_line_left_short/err new file mode 100644 index 00000000..4aa830b6 --- /dev/null +++ b/test/cmdlineTests/too_long_line_left_short/err @@ -0,0 +1,6 @@ +too_long_line_left_short/input.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_left_short/input.sol:2:15: Error: Identifier not found or not unique. + function f(announcementType Type, string Announcement, string ... + ^--------------^ diff --git a/test/cmdlineTests/too_long_line_left_short/exit b/test/cmdlineTests/too_long_line_left_short/exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/cmdlineTests/too_long_line_left_short/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/too_long_line_left_short/input.sol b/test/cmdlineTests/too_long_line_left_short/input.sol new file mode 100644 index 00000000..2accfcce --- /dev/null +++ b/test/cmdlineTests/too_long_line_left_short/input.sol @@ -0,0 +1,4 @@ +contract C { + function f(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { +} +} diff --git a/test/cmdlineTests/too_long_line_right_short.sol b/test/cmdlineTests/too_long_line_right_short.sol deleted file mode 100644 index 936b3961..00000000 --- a/test/cmdlineTests/too_long_line_right_short.sol +++ /dev/null @@ -1,5 +0,0 @@ -contract C { - function ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(announcementType Type, - string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { -} -} diff --git a/test/cmdlineTests/too_long_line_right_short.sol.err b/test/cmdlineTests/too_long_line_right_short.sol.err deleted file mode 100644 index 88072d95..00000000 --- a/test/cmdlineTests/too_long_line_right_short.sol.err +++ /dev/null @@ -1,6 +0,0 @@ -too_long_line_right_short.sol:1:1: Warning: Source file does not specify required compiler version! -contract C { -^ (Relevant source part starts here and spans across multiple lines). -too_long_line_right_short.sol:2:164: Error: Identifier not found or not unique. - ... ffffffffffffffffffffffffffffffffff(announcementType Type, - ^--------------^ diff --git a/test/cmdlineTests/too_long_line_right_short.sol.exit b/test/cmdlineTests/too_long_line_right_short.sol.exit deleted file mode 100644 index d00491fd..00000000 --- a/test/cmdlineTests/too_long_line_right_short.sol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/test/cmdlineTests/too_long_line_right_short/err b/test/cmdlineTests/too_long_line_right_short/err new file mode 100644 index 00000000..ed992565 --- /dev/null +++ b/test/cmdlineTests/too_long_line_right_short/err @@ -0,0 +1,6 @@ +too_long_line_right_short/input.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_right_short/input.sol:2:164: Error: Identifier not found or not unique. + ... ffffffffffffffffffffffffffffffffff(announcementType Type, + ^--------------^ diff --git a/test/cmdlineTests/too_long_line_right_short/exit b/test/cmdlineTests/too_long_line_right_short/exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/cmdlineTests/too_long_line_right_short/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/too_long_line_right_short/input.sol b/test/cmdlineTests/too_long_line_right_short/input.sol new file mode 100644 index 00000000..936b3961 --- /dev/null +++ b/test/cmdlineTests/too_long_line_right_short/input.sol @@ -0,0 +1,5 @@ +contract C { + function ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(announcementType Type, + string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { +} +} -- cgit From 8b8ccd1e1a304aa1ada090613d2753cf5e634b50 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 15:13:47 +0100 Subject: Exclude included files --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 08a5a045..342aefa9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -81,7 +81,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ['_build', 'contracts'] # The reST default role (used for this markup: `text`) to use for all # documents. -- cgit From b604b87272f9d18a8a4114f3343ab3dd6ffe81c3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 14:57:59 +0100 Subject: Add AZTEK code as test. --- test/libyul/yulOptimizerTests/fullSuite/aztec.yul | 416 ++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 test/libyul/yulOptimizerTests/fullSuite/aztec.yul diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul new file mode 100644 index 00000000..e43a066c --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -0,0 +1,416 @@ +/** + * @title Library to validate AZTEC zero-knowledge proofs + * @author Zachary Williamson, AZTEC + * @dev Don't include this as an internal library. This contract uses a static memory table to cache elliptic curve primitives and hashes. + * Calling this internally from another function will lead to memory mutation and undefined behaviour. + * The intended use case is to call this externally via `staticcall`. External calls to OptimizedAZTEC can be treated as pure functions as this contract contains no storage and makes no external calls (other than to precompiles) + * Copyright Spilbury Holdings Ltd 2018. All rights reserved. + * We will be releasing AZTEC as an open-source protocol that provides efficient transaction privacy for Ethereum. + * This will include our bespoke AZTEC decentralized exchange, allowing for cross-asset transfers with full transaction privacy + * and interopability with public decentralized exchanges. + * Stay tuned for updates! + * + * Permission to use as test case in the Solidity compiler granted by the author: + * https://github.com/ethereum/solidity/pull/5713#issuecomment-449042830 +**/ +{ + validateJoinSplit() + // should not get here + mstore(0x00, 404) + revert(0x00, 0x20) + + + function validateJoinSplit() { + mstore(0x80, 7673901602397024137095011250362199966051872585513276903826533215767972925880) // h_x + mstore(0xa0, 8489654445897228341090914135473290831551238522473825886865492707826370766375) // h_y + let notes := add(0x04, calldataload(0x04)) + let m := calldataload(0x24) + let n := calldataload(notes) + let gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 + let challenge := mod(calldataload(0x44), gen_order) + + // validate m <= n + if gt(m, n) { mstore(0x00, 404) revert(0x00, 0x20) } + + // recover k_{public} and calculate k_{public} + let kn := calldataload(sub(calldatasize(), 0xc0)) + + // add kn and m to final hash table + mstore(0x2a0, caller()) + mstore(0x2c0, kn) + mstore(0x2e0, m) + kn := mulmod(sub(gen_order, kn), challenge, gen_order) // we actually want c*k_{public} + hashCommitments(notes, n) + let b := add(0x300, mul(n, 0x80)) + + // Iterate over every note and calculate the blinding factor B_i = \gamma_i^{kBar}h^{aBar}\sigma_i^{-c}. + // We use the AZTEC protocol pairing optimization to reduce the number of pairing comparisons to 1, which adds some minor alterations + for { let i := 0 } lt(i, n) { i := add(i, 0x01) } { + + // Get the calldata index of this note + let noteIndex := add(add(notes, 0x20), mul(i, 0xc0)) + + + let k + let a := calldataload(add(noteIndex, 0x20)) + let c := challenge + + switch eq(add(i, 0x01), n) + case 1 { + k := kn + + // if all notes are input notes, invert k + if eq(m, n) { + k := sub(gen_order, k) + } + } + case 0 { k := calldataload(noteIndex) } + + // Check this commitment is well formed... + validateCommitment(noteIndex, k, a) + + // If i > m then this is an output note. + // Set k = kx_j, a = ax_j, c = cx_j, where j = i - (m+1) + switch gt(add(i, 0x01), m) + case 1 { + + // before we update k, update kn = \sum_{i=0}^{m-1}k_i - \sum_{i=m}^{n-1}k_i + kn := addmod(kn, sub(gen_order, k), gen_order) + let x := mod(mload(0x00), gen_order) + k := mulmod(k, x, gen_order) + a := mulmod(a, x, gen_order) + c := mulmod(challenge, x, gen_order) + + // calculate x_{j+1} + mstore(0x00, keccak256(0x00, 0x20)) + } + case 0 { + + // nothing to do here except update kn = \sum_{i=0}^{m-1}k_i - \sum_{i=m}^{n-1}k_i + kn := addmod(kn, k, gen_order) + } + + calldatacopy(0xe0, add(noteIndex, 0x80), 0x40) + calldatacopy(0x20, add(noteIndex, 0x40), 0x40) + mstore(0x120, sub(gen_order, c)) + mstore(0x60, k) + mstore(0xc0, a) + + // Using call instead of staticcall here to make it work on all targets. + let result := call(gas(), 7, 0, 0xe0, 0x60, 0x1a0, 0x40) + result := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x120, 0x40)) + result := and(result, call(gas(), 7, 0, 0x80, 0x60, 0x160, 0x40)) + + result := and(result, call(gas(), 6, 0, 0x120, 0x80, 0x160, 0x40)) + + result := and(result, call(gas(), 6, 0, 0x160, 0x80, b, 0x40)) + + if eq(i, m) { + mstore(0x260, mload(0x20)) + mstore(0x280, mload(0x40)) + mstore(0x1e0, mload(0xe0)) + mstore(0x200, sub(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47, mload(0x100))) + } + + if gt(i, m) { + mstore(0x60, c) + result := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x220, 0x40)) + + result := and(result, call(gas(), 6, 0, 0x220, 0x80, 0x260, 0x40)) + result := and(result, call(gas(), 6, 0, 0x1a0, 0x80, 0x1e0, 0x40)) + } + + if iszero(result) { mstore(0x00, 400) revert(0x00, 0x20) } + b := add(b, 0x40) // increase B pointer by 2 words + } + + if lt(m, n) { + validatePairing(0x64) + } + + let expected := mod(keccak256(0x2a0, sub(b, 0x2a0)), gen_order) + if iszero(eq(expected, challenge)) { + + // No! Bad! No soup for you! + mstore(0x00, 404) + revert(0x00, 0x20) + } + + // Great! All done. This is a valid proof so return ```true``` + mstore(0x00, 0x01) + return(0x00, 0x20) + } + + function validatePairing(t2) { + let field_order := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 + let t2_x_1 := calldataload(t2) + let t2_x_2 := calldataload(add(t2, 0x20)) + let t2_y_1 := calldataload(add(t2, 0x40)) + let t2_y_2 := calldataload(add(t2, 0x60)) + + // check provided setup pubkey is not zero or g2 + if or(or(or(or(or(or(or( + iszero(t2_x_1), + iszero(t2_x_2)), + iszero(t2_y_1)), + iszero(t2_y_2)), + eq(t2_x_1, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed)), + eq(t2_x_2, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2)), + eq(t2_y_1, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)), + eq(t2_y_2, 0x90689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b)) + { + mstore(0x00, 400) + revert(0x00, 0x20) + } + + mstore(0x20, mload(0x1e0)) // sigma accumulator x + mstore(0x40, mload(0x200)) // sigma accumulator y + mstore(0x80, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) + mstore(0x60, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) + mstore(0xc0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa) + mstore(0xa0, 0x90689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) + mstore(0xe0, mload(0x260)) // gamma accumulator x + mstore(0x100, mload(0x280)) // gamma accumulator y + mstore(0x140, t2_x_1) + mstore(0x120, t2_x_2) + mstore(0x180, t2_y_1) + mstore(0x160, t2_y_2) + + let success := call(gas(), 8, 0, 0x20, 0x180, 0x20, 0x20) + + if or(iszero(success), iszero(mload(0x20))) { + mstore(0x00, 400) + revert(0x00, 0x20) + } + } + + function validateCommitment(note, k, a) { + let gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 + let field_order := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 + let gammaX := calldataload(add(note, 0x40)) + let gammaY := calldataload(add(note, 0x60)) + let sigmaX := calldataload(add(note, 0x80)) + let sigmaY := calldataload(add(note, 0xa0)) + if iszero( + and( + and( + and( + eq(mod(a, gen_order), a), // a is modulo generator order? + gt(a, 1) // can't be 0 or 1 either! + ), + and( + eq(mod(k, gen_order), k), // k is modulo generator order? + gt(k, 1) // and not 0 or 1 + ) + ), + and( + eq( // y^2 ?= x^3 + 3 + addmod(mulmod(mulmod(sigmaX, sigmaX, field_order), sigmaX, field_order), 3, field_order), + mulmod(sigmaY, sigmaY, field_order) + ), + eq( // y^2 ?= x^3 + 3 + addmod(mulmod(mulmod(gammaX, gammaX, field_order), gammaX, field_order), 3, field_order), + mulmod(gammaY, gammaY, field_order) + ) + ) + ) + ) { + mstore(0x00, 400) + revert(0x00, 0x20) + } + } + + function hashCommitments(notes, n) { + for { let i := 0 } lt(i, n) { i := add(i, 0x01) } { + let index := add(add(notes, mul(i, 0xc0)), 0x60) + calldatacopy(add(0x300, mul(i, 0x80)), index, 0x80) + } + mstore(0x00, keccak256(0x300, mul(n, 0x80))) + } +} +// ---- +// fullSuite +// { +// { +// let validateJo__6 := 0x80 +// mstore(validateJo__6, 7673901602397024137095011250362199966051872585513276903826533215767972925880) +// mstore(0xa0, 8489654445897228341090914135473290831551238522473825886865492707826370766375) +// let validateJo__10 := calldataload(0x04) +// let validateJo_notes := add(0x04, validateJo__10) +// let validateJo_m := calldataload(0x24) +// let validateJo_n := calldataload(validateJo_notes) +// let validateJo_gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +// let validateJo_challenge := mod(calldataload(0x44), validateJo_gen_order) +// if gt(validateJo_m, validateJo_n) +// { +// mstore(0x00, 404) +// revert(0x00, 0x20) +// } +// let validateJo_kn_287 := calldataload(add(calldatasize(), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40)) +// let validateJo_kn := validateJo_kn_287 +// let validateJo__24 := 0x2a0 +// mstore(validateJo__24, caller()) +// mstore(0x2c0, validateJo_kn_287) +// mstore(0x2e0, validateJo_m) +// validateJo_kn := mulmod(sub(validateJo_gen_order, validateJo_kn_287), validateJo_challenge, validateJo_gen_order) +// hashCommitments(validateJo_notes, validateJo_n) +// let validateJo_b := add(0x300, mul(validateJo_n, validateJo__6)) +// let validateJo_i_290 := 0 +// let validateJo_i := validateJo_i_290 +// for { +// } +// lt(validateJo_i, validateJo_n) +// { +// validateJo_i := add(validateJo_i, 0x01) +// } +// { +// let validateJo__34 := 0x20 +// let validateJo__376 := add(validateJo__10, mul(validateJo_i, 0xc0)) +// let validateJo_noteIndex := add(validateJo__376, 36) +// let validateJo_k := validateJo_i_290 +// let validateJo_a_292 := calldataload(add(validateJo__376, 68)) +// let validateJo_a := validateJo_a_292 +// let validateJo_c := validateJo_challenge +// let validateJo__39 := add(validateJo_i, 0x01) +// switch eq(validateJo__39, validateJo_n) +// case 1 { +// validateJo_k := validateJo_kn +// if eq(validateJo_m, validateJo_n) +// { +// validateJo_k := sub(validateJo_gen_order, validateJo_kn) +// } +// } +// case 0 { +// validateJo_k := calldataload(validateJo_noteIndex) +// } +// validateCommitment(validateJo_noteIndex, validateJo_k, validateJo_a_292) +// switch gt(validateJo__39, validateJo_m) +// case 1 { +// validateJo_kn := addmod(validateJo_kn, sub(validateJo_gen_order, validateJo_k), validateJo_gen_order) +// let validateJo_x := mod(mload(0x00), validateJo_gen_order) +// validateJo_k := mulmod(validateJo_k, validateJo_x, validateJo_gen_order) +// validateJo_a := mulmod(validateJo_a_292, validateJo_x, validateJo_gen_order) +// validateJo_c := mulmod(validateJo_challenge, validateJo_x, validateJo_gen_order) +// mstore(0x00, keccak256(0x00, validateJo__34)) +// } +// case 0 { +// validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) +// } +// let validateJo__52 := 0x40 +// calldatacopy(0xe0, add(validateJo__376, 164), validateJo__52) +// calldatacopy(validateJo__34, add(validateJo__376, 100), validateJo__52) +// let validateJo__61 := 0x120 +// mstore(validateJo__61, sub(validateJo_gen_order, validateJo_c)) +// let validateJo__62 := 0x60 +// mstore(validateJo__62, validateJo_k) +// mstore(0xc0, validateJo_a) +// let validateJo__65 := 0x1a0 +// let validateJo_result_302 := call(gas(), 7, validateJo_i_290, 0xe0, validateJo__62, validateJo__65, validateJo__52) +// let validateJo_result := validateJo_result_302 +// let validateJo_result_303 := and(validateJo_result_302, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__61, validateJo__52)) +// let validateJo__80 := 0x160 +// let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_290, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) +// let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_290, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) +// let validateJo_result_306 := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) +// validateJo_result := validateJo_result_306 +// if eq(validateJo_i, validateJo_m) +// { +// mstore(0x260, mload(validateJo__34)) +// mstore(0x280, mload(validateJo__52)) +// mstore(0x1e0, mload(0xe0)) +// mstore(0x200, sub(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47, mload(0x100))) +// } +// if gt(validateJo_i, validateJo_m) +// { +// mstore(validateJo__62, validateJo_c) +// let validateJo__120 := 0x220 +// let validateJo_result_307 := and(validateJo_result_306, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) +// let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_290, validateJo__120, validateJo__6, 0x260, validateJo__52)) +// validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_290, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) +// } +// if iszero(validateJo_result) +// { +// mstore(0x00, 400) +// revert(0x00, validateJo__34) +// } +// validateJo_b := add(validateJo_b, validateJo__52) +// } +// if lt(validateJo_m, validateJo_n) +// { +// validatePairing(0x64) +// } +// if iszero(eq(mod(keccak256(validateJo__24, add(validateJo_b, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60)), validateJo_gen_order), validateJo_challenge)) +// { +// mstore(0x00, 404) +// revert(0x00, 0x20) +// } +// mstore(0x00, 0x01) +// return(0x00, 0x20) +// mstore(0x00, 404) +// revert(0x00, 0x20) +// } +// function validatePairing(t2) +// { +// let t2_x_1 := calldataload(t2) +// let _165 := 0x20 +// let t2_x_2 := calldataload(add(t2, _165)) +// let t2_y_1 := calldataload(add(t2, 0x40)) +// let t2_y_2 := calldataload(add(t2, 0x60)) +// let _171 := 0x90689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b +// let _173 := 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa +// let _175 := 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 +// let _177 := 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed +// if or(or(or(or(or(or(or(iszero(t2_x_1), iszero(t2_x_2)), iszero(t2_y_1)), iszero(t2_y_2)), eq(t2_x_1, _177)), eq(t2_x_2, _175)), eq(t2_y_1, _173)), eq(t2_y_2, _171)) +// { +// mstore(0x00, 400) +// revert(0x00, _165) +// } +// mstore(_165, mload(0x1e0)) +// mstore(0x40, mload(0x200)) +// mstore(0x80, _177) +// mstore(0x60, _175) +// mstore(0xc0, _173) +// mstore(0xa0, _171) +// mstore(0xe0, mload(0x260)) +// mstore(0x100, mload(0x280)) +// mstore(0x140, t2_x_1) +// mstore(0x120, t2_x_2) +// let _216 := 0x180 +// mstore(_216, t2_y_1) +// mstore(0x160, t2_y_2) +// let success := call(gas(), 8, 0, _165, _216, _165, _165) +// if or(iszero(success), iszero(mload(_165))) +// { +// mstore(0x00, 400) +// revert(0x00, _165) +// } +// } +// function validateCommitment(note, k_1, a_2) +// { +// let gen_order_3 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +// let field_order_4 := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 +// let gammaX := calldataload(add(note, 0x40)) +// let gammaY := calldataload(add(note, 0x60)) +// let sigmaX := calldataload(add(note, 0x80)) +// let sigmaY := calldataload(add(note, 0xa0)) +// if iszero(and(and(and(eq(mod(a_2, gen_order_3), a_2), gt(a_2, 1)), and(eq(mod(k_1, gen_order_3), k_1), gt(k_1, 1))), and(eq(addmod(mulmod(mulmod(sigmaX, sigmaX, field_order_4), sigmaX, field_order_4), 3, field_order_4), mulmod(sigmaY, sigmaY, field_order_4)), eq(addmod(mulmod(mulmod(gammaX, gammaX, field_order_4), gammaX, field_order_4), 3, field_order_4), mulmod(gammaY, gammaY, field_order_4))))) +// { +// mstore(0x00, 400) +// revert(0x00, 0x20) +// } +// } +// function hashCommitments(notes_5, n_6) +// { +// let i_7 := 0 +// for { +// } +// lt(i_7, n_6) +// { +// i_7 := add(i_7, 0x01) +// } +// { +// calldatacopy(add(0x300, mul(i_7, 0x80)), add(add(notes_5, mul(i_7, 0xc0)), 0x60), 0x80) +// } +// mstore(0x00, keccak256(0x300, mul(n_6, 0x80))) +// } +// } -- cgit From 9222eff08fad8e6a0ea7df8b299880f90c68c8b3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 18:55:32 +0100 Subject: Make the dialect available everywhere. --- libsolidity/interface/AssemblyStack.cpp | 2 +- libyul/optimiser/CommonSubexpressionEliminator.h | 3 + libyul/optimiser/DataFlowAnalyzer.cpp | 2 +- libyul/optimiser/DataFlowAnalyzer.h | 4 ++ libyul/optimiser/Disambiguator.cpp | 3 +- libyul/optimiser/Disambiguator.h | 8 ++- libyul/optimiser/ExpressionInliner.cpp | 2 +- libyul/optimiser/ExpressionInliner.h | 6 +- libyul/optimiser/ExpressionSimplifier.cpp | 8 +-- libyul/optimiser/ExpressionSimplifier.h | 8 ++- libyul/optimiser/NameDispenser.cpp | 10 ++-- libyul/optimiser/NameDispenser.h | 6 +- libyul/optimiser/RedundantAssignEliminator.cpp | 6 +- libyul/optimiser/RedundantAssignEliminator.h | 5 +- libyul/optimiser/Rematerialiser.cpp | 7 ++- libyul/optimiser/Rematerialiser.h | 4 +- libyul/optimiser/Semantics.cpp | 17 +++++- libyul/optimiser/Semantics.h | 6 +- libyul/optimiser/SimplificationRules.cpp | 13 +++-- libyul/optimiser/SimplificationRules.h | 9 ++- libyul/optimiser/StructuralSimplifier.h | 2 + libyul/optimiser/Suite.cpp | 71 ++++++++++++------------ libyul/optimiser/Suite.h | 2 + libyul/optimiser/UnusedPruner.cpp | 15 +++-- libyul/optimiser/UnusedPruner.h | 14 ++++- test/libyul/Common.cpp | 12 +++- test/libyul/YulOptimizerTest.cpp | 44 +++++++-------- test/libyul/YulOptimizerTest.h | 2 + test/tools/yulopti.cpp | 23 ++++---- 29 files changed, 198 insertions(+), 116 deletions(-) diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 69bceefc..6d30f5f7 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -134,7 +134,7 @@ void AssemblyStack::optimize(yul::Object& _object) for (auto& subNode: _object.subObjects) if (auto subObject = dynamic_cast(subNode.get())) optimize(*subObject); - yul::OptimiserSuite::run(*_object.code, *_object.analysisInfo); + yul::OptimiserSuite::run(*languageToDialect(m_language), *_object.code, *_object.analysisInfo); } MachineAssemblyObject AssemblyStack::assemble(Machine _machine, bool _optimize) const diff --git a/libyul/optimiser/CommonSubexpressionEliminator.h b/libyul/optimiser/CommonSubexpressionEliminator.h index ac1ebe3a..4b7973e2 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.h +++ b/libyul/optimiser/CommonSubexpressionEliminator.h @@ -34,6 +34,9 @@ namespace yul */ class CommonSubexpressionEliminator: public DataFlowAnalyzer { +public: + CommonSubexpressionEliminator(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} + protected: using ASTModifier::visit; void visit(Expression& _e) override; diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index c8d236dc..e97bcdc4 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -142,7 +142,7 @@ void DataFlowAnalyzer::handleAssignment(set const& _variables, Expres static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}}; clearValues(_variables); - MovableChecker movableChecker; + MovableChecker movableChecker{m_dialect}; if (_value) movableChecker.visit(*_value); else diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index 4f12ff6a..5fb5db95 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -30,6 +30,7 @@ namespace yul { +struct Dialect; /** * Base class to perform data flow analysis during AST walks. @@ -43,6 +44,8 @@ namespace yul class DataFlowAnalyzer: public ASTModifier { public: + explicit DataFlowAnalyzer(Dialect const& _dialect): m_dialect(_dialect) {} + using ASTModifier::operator(); void operator()(Assignment& _assignment) override; void operator()(VariableDeclaration& _varDecl) override; @@ -84,6 +87,7 @@ protected: }; /// List of scopes. std::vector m_variableScopes; + Dialect const& m_dialect; }; } diff --git a/libyul/optimiser/Disambiguator.cpp b/libyul/optimiser/Disambiguator.cpp index fda5895b..cb56ee99 100644 --- a/libyul/optimiser/Disambiguator.cpp +++ b/libyul/optimiser/Disambiguator.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace std; using namespace dev; @@ -31,7 +32,7 @@ using namespace dev::solidity; YulString Disambiguator::translateIdentifier(YulString _originalName) { - if ((m_externallyUsedIdentifiers.count(_originalName))) + if (m_dialect.builtin(_originalName) || m_externallyUsedIdentifiers.count(_originalName)) return _originalName; assertThrow(!m_scopes.empty() && m_scopes.back(), OptimizerException, ""); diff --git a/libyul/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h index bb83417b..ec6a0879 100644 --- a/libyul/optimiser/Disambiguator.h +++ b/libyul/optimiser/Disambiguator.h @@ -32,6 +32,7 @@ namespace yul { +struct Dialect; /** * Creates a copy of a Yul AST replacing all identifiers by unique names. @@ -40,10 +41,14 @@ class Disambiguator: public ASTCopier { public: explicit Disambiguator( + Dialect const& _dialect, AsmAnalysisInfo const& _analysisInfo, std::set const& _externallyUsedIdentifiers = {} ): - m_info(_analysisInfo), m_externallyUsedIdentifiers(_externallyUsedIdentifiers), m_nameDispenser(m_externallyUsedIdentifiers) + m_info(_analysisInfo), + m_dialect(_dialect), + m_externallyUsedIdentifiers(_externallyUsedIdentifiers), + m_nameDispenser(_dialect, m_externallyUsedIdentifiers) { } @@ -58,6 +63,7 @@ protected: void leaveScopeInternal(Scope& _scope); AsmAnalysisInfo const& m_info; + Dialect const& m_dialect; std::set const& m_externallyUsedIdentifiers; std::vector m_scopes; diff --git a/libyul/optimiser/ExpressionInliner.cpp b/libyul/optimiser/ExpressionInliner.cpp index 27d43ac0..858c8733 100644 --- a/libyul/optimiser/ExpressionInliner.cpp +++ b/libyul/optimiser/ExpressionInliner.cpp @@ -56,7 +56,7 @@ void ExpressionInliner::visit(Expression& _expression) bool movable = boost::algorithm::all_of( funCall.arguments, - [=](Expression const& _arg) { return MovableChecker(_arg).movable(); } + [=](Expression const& _arg) { return MovableChecker(m_dialect, _arg).movable(); } ); if (m_inlinableFunctions.count(funCall.functionName.name) && movable) { diff --git a/libyul/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h index 14e80c0a..e6e710f8 100644 --- a/libyul/optimiser/ExpressionInliner.h +++ b/libyul/optimiser/ExpressionInliner.h @@ -29,6 +29,7 @@ namespace yul { +struct Dialect; /** * Optimiser component that modifies an AST in place, inlining functions that can be @@ -44,8 +45,8 @@ namespace yul class ExpressionInliner: public ASTModifier { public: - ExpressionInliner(Block& _block): - m_block(_block) + ExpressionInliner(Dialect const& _dialect, Block& _block): + m_block(_block), m_dialect(_dialect) {} void run(); @@ -62,6 +63,7 @@ private: std::set m_currentFunctions; Block& m_block; + Dialect const& m_dialect; }; diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index cda44e8e..b26e4245 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -36,7 +36,7 @@ using namespace dev::solidity; void ExpressionSimplifier::visit(Expression& _expression) { ASTModifier::visit(_expression); - while (auto match = SimplificationRules::findFirstMatch(_expression, m_ssaValues)) + while (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_ssaValues)) { // Do not apply the rule if it removes non-constant parts of the expression. // TODO: The check could actually be less strict than "movable". @@ -45,15 +45,15 @@ void ExpressionSimplifier::visit(Expression& _expression) // so if the value of the variable is not movable, the expression that references // the variable still is. - if (match->removesNonConstants && !MovableChecker(_expression).movable()) + if (match->removesNonConstants && !MovableChecker(m_dialect, _expression).movable()) return; _expression = match->action().toExpression(locationOf(_expression)); } } -void ExpressionSimplifier::run(Block& _ast) +void ExpressionSimplifier::run(Dialect const& _dialect, Block& _ast) { SSAValueTracker ssaValues; ssaValues(_ast); - ExpressionSimplifier{ssaValues.values()}(_ast); + ExpressionSimplifier{_dialect, ssaValues.values()}(_ast); } diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index fe3507f8..b1122e91 100644 --- a/libyul/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -26,6 +26,7 @@ namespace yul { +struct Dialect; /** * Applies simplification rules to all expressions. @@ -40,12 +41,13 @@ public: using ASTModifier::operator(); virtual void visit(Expression& _expression); - static void run(Block& _ast); + static void run(Dialect const& _dialect, Block& _ast); private: - explicit ExpressionSimplifier(std::map _ssaValues): - m_ssaValues(std::move(_ssaValues)) + explicit ExpressionSimplifier(Dialect const& _dialect, std::map _ssaValues): + m_dialect(_dialect), m_ssaValues(std::move(_ssaValues)) {} + Dialect const& m_dialect; std::map m_ssaValues; }; diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index e7cdc60f..172e6907 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -22,17 +22,19 @@ #include #include +#include using namespace std; using namespace dev; using namespace yul; -NameDispenser::NameDispenser(Block const& _ast): - NameDispenser(NameCollector(_ast).names()) +NameDispenser::NameDispenser(Dialect const& _dialect, Block const& _ast): + NameDispenser(_dialect, NameCollector(_ast).names()) { } -NameDispenser::NameDispenser(set _usedNames): +NameDispenser::NameDispenser(Dialect const& _dialect, set _usedNames): + m_dialect(_dialect), m_usedNames(std::move(_usedNames)) { } @@ -51,7 +53,7 @@ YulString NameDispenser::newName(YulString _nameHint, YulString _context) YulString NameDispenser::newNameInternal(YulString _nameHint) { YulString name = _nameHint; - while (name.empty() || m_usedNames.count(name)) + while (name.empty() || m_usedNames.count(name) || m_dialect.builtin(name)) { m_counter++; name = YulString(_nameHint.str() + "_" + to_string(m_counter)); diff --git a/libyul/optimiser/NameDispenser.h b/libyul/optimiser/NameDispenser.h index 664a5265..719743e6 100644 --- a/libyul/optimiser/NameDispenser.h +++ b/libyul/optimiser/NameDispenser.h @@ -27,6 +27,7 @@ namespace yul { +struct Dialect; /** * Optimizer component that can be used to generate new names that @@ -38,9 +39,9 @@ class NameDispenser { public: /// Initialize the name dispenser with all the names used in the given AST. - explicit NameDispenser(Block const& _ast); + explicit NameDispenser(Dialect const& _dialect, Block const& _ast); /// Initialize the name dispenser with the given used names. - explicit NameDispenser(std::set _usedNames); + explicit NameDispenser(Dialect const& _dialect, std::set _usedNames); /// @returns a currently unused name that should be similar to _nameHint /// and prefixed by _context if present. @@ -51,6 +52,7 @@ public: private: YulString newNameInternal(YulString _nameHint); + Dialect const& m_dialect; std::set m_usedNames; size_t m_counter = 0; }; diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp index 7b18e8ca..f6d7676f 100644 --- a/libyul/optimiser/RedundantAssignEliminator.cpp +++ b/libyul/optimiser/RedundantAssignEliminator.cpp @@ -150,9 +150,9 @@ void RedundantAssignEliminator::operator()(Block const& _block) ASTWalker::operator()(_block); } -void RedundantAssignEliminator::run(Block& _ast) +void RedundantAssignEliminator::run(Dialect const& _dialect, Block& _ast) { - RedundantAssignEliminator rae; + RedundantAssignEliminator rae{_dialect}; rae(_ast); AssignmentRemover remover{rae.m_assignmentsToRemove}; @@ -211,7 +211,7 @@ void RedundantAssignEliminator::finalize(YulString _variable) for (auto& assignment: m_assignments[_variable]) { assertThrow(assignment.second != State::Undecided, OptimizerException, ""); - if (assignment.second == State{State::Unused} && MovableChecker{*assignment.first->value}.movable()) + if (assignment.second == State{State::Unused} && MovableChecker{*m_dialect, *assignment.first->value}.movable()) // TODO the only point where we actually need this // to be a set is for the for loop m_assignmentsToRemove.insert(assignment.first); diff --git a/libyul/optimiser/RedundantAssignEliminator.h b/libyul/optimiser/RedundantAssignEliminator.h index 4f82e7a2..2f4dd380 100644 --- a/libyul/optimiser/RedundantAssignEliminator.h +++ b/libyul/optimiser/RedundantAssignEliminator.h @@ -28,6 +28,7 @@ namespace yul { +struct Dialect; /** * Optimiser component that removes assignments to variables that are not used @@ -98,6 +99,7 @@ namespace yul class RedundantAssignEliminator: public ASTWalker { public: + explicit RedundantAssignEliminator(Dialect const& _dialect): m_dialect(&_dialect) {} RedundantAssignEliminator(RedundantAssignEliminator const&) = default; RedundantAssignEliminator& operator=(RedundantAssignEliminator const&) = default; RedundantAssignEliminator(RedundantAssignEliminator&&) = default; @@ -112,7 +114,7 @@ public: void operator()(ForLoop const&) override; void operator()(Block const& _block) override; - static void run(Block& _ast); + static void run(Dialect const& _dialect, Block& _ast); private: RedundantAssignEliminator() = default; @@ -167,6 +169,7 @@ private: void changeUndecidedTo(YulString _variable, State _newState); void finalize(YulString _variable); + Dialect const* m_dialect; std::set m_declaredVariables; // TODO check that this does not cause nondeterminism! // This could also be a pseudo-map from state to assignment. diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index 247defda..56f6e99c 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -30,12 +30,13 @@ using namespace std; using namespace dev; using namespace yul; -void Rematerialiser::run(Block& _ast) +void Rematerialiser::run(Dialect const& _dialect, Block& _ast) { - Rematerialiser{_ast}(_ast); + Rematerialiser{_dialect, _ast}(_ast); } -Rematerialiser::Rematerialiser(Block& _ast): +Rematerialiser::Rematerialiser(Dialect const& _dialect, Block& _ast): + DataFlowAnalyzer(_dialect), m_referenceCounts(ReferencesCounter::countReferences(_ast)) { } diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index c34353de..731697c8 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -38,10 +38,10 @@ namespace yul class Rematerialiser: public DataFlowAnalyzer { public: - static void run(Block& _ast); + static void run(Dialect const& _dialect, Block& _ast); protected: - Rematerialiser(Block& _ast); + Rematerialiser(Dialect const& _dialect, Block& _ast); using ASTModifier::visit; void visit(Expression& _e) override; diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 91bb2709..7edf97d7 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -22,6 +22,7 @@ #include #include +#include #include @@ -31,7 +32,13 @@ using namespace std; using namespace dev; using namespace yul; -MovableChecker::MovableChecker(Expression const& _expression) +MovableChecker::MovableChecker(Dialect const& _dialect): + m_dialect(_dialect) +{ +} + +MovableChecker::MovableChecker(Dialect const& _dialect, Expression const& _expression): + MovableChecker(_dialect) { visit(_expression); } @@ -50,8 +57,14 @@ void MovableChecker::operator()(FunctionalInstruction const& _instr) ASTWalker::operator()(_instr); } -void MovableChecker::operator()(FunctionCall const&) +void MovableChecker::operator()(FunctionCall const& _functionCall) { + if (BuiltinFunction const* f = m_dialect.builtin(_functionCall.functionName.name)) + if (f->movable) + { + ASTWalker::operator()(_functionCall); + return; + } m_movable = false; } diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 70c50806..a81a489f 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -26,6 +26,7 @@ namespace yul { +struct Dialect; /** * Specific AST walker that determines whether an expression is movable. @@ -33,8 +34,8 @@ namespace yul class MovableChecker: public ASTWalker { public: - MovableChecker() = default; - explicit MovableChecker(Expression const& _expression); + explicit MovableChecker(Dialect const& _dialect); + MovableChecker(Dialect const& _dialect, Expression const& _expression); void operator()(Identifier const& _identifier) override; void operator()(FunctionalInstruction const& _functionalInstruction) override; @@ -48,6 +49,7 @@ public: std::set const& referencedVariables() const { return m_variableReferences; } private: + Dialect const& m_dialect; /// Which variables the current expression references. std::set m_variableReferences; /// Is the current expression movable or not. diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 45b0ca2c..da9c7d9d 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -36,6 +36,7 @@ using namespace yul; SimplificationRule const* SimplificationRules::findFirstMatch( Expression const& _expr, + Dialect const& _dialect, map const& _ssaValues ) { @@ -49,7 +50,7 @@ SimplificationRule const* SimplificationRules::findFirstMatch( for (auto const& rule: rules.m_rules[uint8_t(instruction.instruction)]) { rules.resetMatchGroups(); - if (rule.pattern.matches(_expr, _ssaValues)) + if (rule.pattern.matches(_expr, _dialect, _ssaValues)) return &rule; } return nullptr; @@ -104,7 +105,11 @@ void Pattern::setMatchGroup(unsigned _group, map& _ m_matchGroups = &_matchGroups; } -bool Pattern::matches(Expression const& _expr, map const& _ssaValues) const +bool Pattern::matches( + Expression const& _expr, + Dialect const& _dialect, + map const& _ssaValues +) const { Expression const* expr = &_expr; @@ -139,7 +144,7 @@ bool Pattern::matches(Expression const& _expr, map return false; assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, ""); for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i].matches(instr.arguments.at(i), _ssaValues)) + if (!m_arguments[i].matches(instr.arguments.at(i), _dialect, _ssaValues)) return false; } else @@ -167,7 +172,7 @@ bool Pattern::matches(Expression const& _expr, map assertThrow(firstMatch, OptimizerException, "Match set but to null."); return SyntacticalEqualityChecker::equal(*firstMatch, _expr) && - MovableChecker(_expr).movable(); + MovableChecker(_dialect, _expr).movable(); } else if (m_kind == PatternKind::Any) (*m_matchGroups)[m_matchGroup] = &_expr; diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index 16aaba04..8213a185 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -33,7 +33,7 @@ namespace yul { - +struct Dialect; class Pattern; /** @@ -49,6 +49,7 @@ public: /// @param _ssaValues values of variables that are assigned exactly once. static SimplificationRule const* findFirstMatch( Expression const& _expr, + Dialect const& _dialect, std::map const& _ssaValues ); @@ -93,7 +94,11 @@ public: /// same expression equivalence class. void setMatchGroup(unsigned _group, std::map& _matchGroups); unsigned matchGroup() const { return m_matchGroup; } - bool matches(Expression const& _expr, std::map const& _ssaValues) const; + bool matches( + Expression const& _expr, + Dialect const& _dialect, + std::map const& _ssaValues + ) const; std::vector arguments() const { return m_arguments; } diff --git a/libyul/optimiser/StructuralSimplifier.h b/libyul/optimiser/StructuralSimplifier.h index bbd8e005..d68a9620 100644 --- a/libyul/optimiser/StructuralSimplifier.h +++ b/libyul/optimiser/StructuralSimplifier.h @@ -38,6 +38,8 @@ namespace yul class StructuralSimplifier: public DataFlowAnalyzer { public: + explicit StructuralSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} + using DataFlowAnalyzer::operator(); void operator()(Block& _block) override; private: diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index c0fd15a2..9b6e1337 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -47,6 +47,7 @@ using namespace dev; using namespace yul; void OptimiserSuite::run( + Dialect const& _dialect, Block& _ast, AsmAnalysisInfo const& _analysisInfo, set const& _externallyUsedIdentifiers @@ -54,69 +55,69 @@ void OptimiserSuite::run( { set reservedIdentifiers = _externallyUsedIdentifiers; - Block ast = boost::get(Disambiguator(_analysisInfo, reservedIdentifiers)(_ast)); + Block ast = boost::get(Disambiguator(_dialect, _analysisInfo, reservedIdentifiers)(_ast)); (VarDeclInitializer{})(ast); (FunctionHoister{})(ast); (FunctionGrouper{})(ast); (ForLoopInitRewriter{})(ast); - StructuralSimplifier{}(ast); + StructuralSimplifier{_dialect}(ast); - NameDispenser dispenser{ast}; + NameDispenser dispenser{_dialect, ast}; for (size_t i = 0; i < 4; i++) { ExpressionSplitter{dispenser}(ast); SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(ast); - RedundantAssignEliminator::run(ast); + RedundantAssignEliminator::run(_dialect, ast); + RedundantAssignEliminator::run(_dialect, ast); - CommonSubexpressionEliminator{}(ast); - ExpressionSimplifier::run(ast); - StructuralSimplifier{}(ast); + CommonSubexpressionEliminator{_dialect}(ast); + ExpressionSimplifier::run(_dialect, ast); + StructuralSimplifier{_dialect}(ast); SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(ast); - RedundantAssignEliminator::run(ast); - UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); - CommonSubexpressionEliminator{}(ast); - UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); + RedundantAssignEliminator::run(_dialect, ast); + RedundantAssignEliminator::run(_dialect, ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); + CommonSubexpressionEliminator{_dialect}(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(ast); - RedundantAssignEliminator::run(ast); + RedundantAssignEliminator::run(_dialect, ast); + RedundantAssignEliminator::run(_dialect, ast); ExpressionJoiner::run(ast); ExpressionJoiner::run(ast); - ExpressionInliner(ast).run(); - UnusedPruner::runUntilStabilised(ast); + ExpressionInliner(_dialect, ast).run(); + UnusedPruner::runUntilStabilised(_dialect, ast); ExpressionSplitter{dispenser}(ast); SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(ast); - RedundantAssignEliminator::run(ast); - CommonSubexpressionEliminator{}(ast); + RedundantAssignEliminator::run(_dialect, ast); + RedundantAssignEliminator::run(_dialect, ast); + CommonSubexpressionEliminator{_dialect}(ast); FullInliner{ast, dispenser}.run(); SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(ast); - RedundantAssignEliminator::run(ast); - ExpressionSimplifier::run(ast); - StructuralSimplifier{}(ast); - CommonSubexpressionEliminator{}(ast); + RedundantAssignEliminator::run(_dialect, ast); + RedundantAssignEliminator::run(_dialect, ast); + ExpressionSimplifier::run(_dialect, ast); + StructuralSimplifier{_dialect}(ast); + CommonSubexpressionEliminator{_dialect}(ast); SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(ast); - RedundantAssignEliminator::run(ast); - UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); - CommonSubexpressionEliminator{}(ast); + RedundantAssignEliminator::run(_dialect, ast); + RedundantAssignEliminator::run(_dialect, ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); + CommonSubexpressionEliminator{_dialect}(ast); } ExpressionJoiner::run(ast); - Rematerialiser::run(ast); - UnusedPruner::runUntilStabilised(ast); + Rematerialiser::run(_dialect, ast); + UnusedPruner::runUntilStabilised(_dialect, ast); ExpressionJoiner::run(ast); - UnusedPruner::runUntilStabilised(ast); + UnusedPruner::runUntilStabilised(_dialect, ast); ExpressionJoiner::run(ast); - UnusedPruner::runUntilStabilised(ast); + UnusedPruner::runUntilStabilised(_dialect, ast); ExpressionJoiner::run(ast); - Rematerialiser::run(ast); - UnusedPruner::runUntilStabilised(ast); + Rematerialiser::run(_dialect, ast); + UnusedPruner::runUntilStabilised(_dialect, ast); _ast = std::move(ast); } diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index 795326b4..ce40b204 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -29,6 +29,7 @@ namespace yul { struct AsmAnalysisInfo; +struct Dialect; /** * Optimiser suite that combines all steps and also provides the settings for the heuristics @@ -37,6 +38,7 @@ class OptimiserSuite { public: static void run( + Dialect const& _dialect, Block& _ast, AsmAnalysisInfo const& _analysisInfo, diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index 31aead82..53c412e3 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -32,7 +32,8 @@ using namespace std; using namespace dev; using namespace yul; -UnusedPruner::UnusedPruner(Block& _ast, set const& _externallyUsedFunctions) +UnusedPruner::UnusedPruner(Dialect const& _dialect, Block& _ast, set const& _externallyUsedFunctions): + m_dialect(_dialect) { ReferencesCounter counter; counter(_ast); @@ -69,7 +70,7 @@ void UnusedPruner::operator()(Block& _block) { if (!varDecl.value) statement = Block{std::move(varDecl.location), {}}; - else if (MovableChecker(*varDecl.value).movable()) + else if (MovableChecker(m_dialect, *varDecl.value).movable()) { subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); statement = Block{std::move(varDecl.location), {}}; @@ -87,7 +88,7 @@ void UnusedPruner::operator()(Block& _block) else if (statement.type() == typeid(ExpressionStatement)) { ExpressionStatement& exprStmt = boost::get(statement); - if (MovableChecker(exprStmt.expression).movable()) + if (MovableChecker(m_dialect, exprStmt.expression).movable()) { // pop(x) should be movable! subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); @@ -100,11 +101,15 @@ void UnusedPruner::operator()(Block& _block) ASTModifier::operator()(_block); } -void UnusedPruner::runUntilStabilised(Block& _ast, set const& _externallyUsedFunctions) +void UnusedPruner::runUntilStabilised( + Dialect const& _dialect, + Block& _ast, + set const& _externallyUsedFunctions +) { while (true) { - UnusedPruner pruner(_ast, _externallyUsedFunctions); + UnusedPruner pruner(_dialect, _ast, _externallyUsedFunctions); pruner(_ast); if (!pruner.shouldRunAgain()) return; diff --git a/libyul/optimiser/UnusedPruner.h b/libyul/optimiser/UnusedPruner.h index 64e02b35..72279d4a 100644 --- a/libyul/optimiser/UnusedPruner.h +++ b/libyul/optimiser/UnusedPruner.h @@ -28,6 +28,7 @@ namespace yul { +struct Dialect; /** * Optimisation stage that removes unused variables and functions and also @@ -40,7 +41,11 @@ namespace yul class UnusedPruner: public ASTModifier { public: - explicit UnusedPruner(Block& _ast, std::set const& _externallyUsedFunctions = {}); + UnusedPruner( + Dialect const& _dialect, + Block& _ast, + std::set const& _externallyUsedFunctions = {} + ); using ASTModifier::operator(); void operator()(Block& _block) override; @@ -49,12 +54,17 @@ public: bool shouldRunAgain() const { return m_shouldRunAgain; } // Run the pruner until the code does not change anymore. - static void runUntilStabilised(Block& _ast, std::set const& _externallyUsedFunctions = {}); + static void runUntilStabilised( + Dialect const& _dialect, + Block& _ast, + std::set const& _externallyUsedFunctions = {} + ); private: bool used(YulString _name) const; void subtractReferences(std::map const& _subtrahend); + Dialect const& m_dialect; bool m_shouldRunAgain = false; std::map m_references; }; diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index a337fa8d..ea4d0208 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -41,6 +41,14 @@ using namespace langutil; using namespace yul; using namespace dev::solidity; +namespace +{ +shared_ptr defaultDialect(bool _yul) +{ + return _yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(); +} +} + void yul::test::printErrors(ErrorList const& _errors) { SourceReferenceFormatter formatter(cout); @@ -55,7 +63,7 @@ void yul::test::printErrors(ErrorList const& _errors) pair, shared_ptr> yul::test::parse(string const& _source, bool _yul) { - shared_ptr dialect = _yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(); + shared_ptr dialect = defaultDialect(_yul); ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(CharStream(_source, "")); @@ -87,7 +95,7 @@ pair, shared_ptr> yul::test::parse(strin yul::Block yul::test::disambiguate(string const& _source, bool _yul) { auto result = parse(_source, _yul); - return boost::get(Disambiguator(*result.second, {})(*result.first)); + return boost::get(Disambiguator(*defaultDialect(_yul), *result.second, {})(*result.first)); } string yul::test::format(string const& _source, bool _yul) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 0c782b17..1b392e0f 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -117,11 +117,11 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con else if (m_optimizerStep == "commonSubexpressionEliminator") { disambiguate(); - (CommonSubexpressionEliminator{})(*m_ast); + (CommonSubexpressionEliminator{*m_dialect})(*m_ast); } else if (m_optimizerStep == "expressionSplitter") { - NameDispenser nameDispenser(*m_ast); + NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter{nameDispenser}(*m_ast); } else if (m_optimizerStep == "expressionJoiner") @@ -132,7 +132,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con else if (m_optimizerStep == "splitJoin") { disambiguate(); - NameDispenser nameDispenser(*m_ast); + NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter{nameDispenser}(*m_ast); ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast); @@ -150,14 +150,14 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con else if (m_optimizerStep == "expressionInliner") { disambiguate(); - ExpressionInliner(*m_ast).run(); + ExpressionInliner(*m_dialect, *m_ast).run(); } else if (m_optimizerStep == "fullInliner") { disambiguate(); (FunctionHoister{})(*m_ast); (FunctionGrouper{})(*m_ast); - NameDispenser nameDispenser(*m_ast); + NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter{nameDispenser}(*m_ast); FullInliner(*m_ast, nameDispenser).run(); ExpressionJoiner::run(*m_ast); @@ -171,54 +171,54 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con else if (m_optimizerStep == "rematerialiser") { disambiguate(); - Rematerialiser::run(*m_ast); + Rematerialiser::run(*m_dialect, *m_ast); } else if (m_optimizerStep == "expressionSimplifier") { disambiguate(); - ExpressionSimplifier::run(*m_ast); + ExpressionSimplifier::run(*m_dialect, *m_ast); } else if (m_optimizerStep == "fullSimplify") { disambiguate(); - NameDispenser nameDispenser(*m_ast); + NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter{nameDispenser}(*m_ast); - CommonSubexpressionEliminator{}(*m_ast); - ExpressionSimplifier::run(*m_ast); - UnusedPruner::runUntilStabilised(*m_ast); + CommonSubexpressionEliminator{*m_dialect}(*m_ast); + ExpressionSimplifier::run(*m_dialect, *m_ast); + UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast); } else if (m_optimizerStep == "unusedPruner") { disambiguate(); - UnusedPruner::runUntilStabilised(*m_ast); + UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); } else if (m_optimizerStep == "ssaTransform") { disambiguate(); - NameDispenser nameDispenser(*m_ast); + NameDispenser nameDispenser{*m_dialect, *m_ast}; SSATransform::run(*m_ast, nameDispenser); } else if (m_optimizerStep == "redundantAssignEliminator") { disambiguate(); - RedundantAssignEliminator::run(*m_ast); + RedundantAssignEliminator::run(*m_dialect, *m_ast); } else if (m_optimizerStep == "ssaPlusCleanup") { disambiguate(); - NameDispenser nameDispenser(*m_ast); + NameDispenser nameDispenser{*m_dialect, *m_ast}; SSATransform::run(*m_ast, nameDispenser); - RedundantAssignEliminator::run(*m_ast); + RedundantAssignEliminator::run(*m_dialect, *m_ast); } else if (m_optimizerStep == "structuralSimplifier") { disambiguate(); - StructuralSimplifier{}(*m_ast); + StructuralSimplifier{*m_dialect}(*m_ast); } else if (m_optimizerStep == "fullSuite") - OptimiserSuite::run(*m_ast, *m_analysisInfo); + OptimiserSuite::run(*m_dialect, *m_ast, *m_analysisInfo); else { FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl; @@ -260,11 +260,11 @@ void YulOptimizerTest::printIndented(ostream& _stream, string const& _output, st bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - shared_ptr dialect = m_yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(); + m_dialect = m_yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(); ErrorList errors; ErrorReporter errorReporter(errors); shared_ptr scanner = make_shared(CharStream(m_source, "")); - m_ast = yul::Parser(errorReporter, dialect).parse(scanner, false); + m_ast = yul::Parser(errorReporter, m_dialect).parse(scanner, false); if (!m_ast || !errorReporter.errors().empty()) { FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; @@ -277,7 +277,7 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c errorReporter, dev::test::Options::get().evmVersion(), boost::none, - dialect + m_dialect ); if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) { @@ -290,7 +290,7 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c void YulOptimizerTest::disambiguate() { - *m_ast = boost::get(Disambiguator(*m_analysisInfo)(*m_ast)); + *m_ast = boost::get(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast)); m_analysisInfo.reset(); } diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index 5648e995..5009b82c 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -30,6 +30,7 @@ namespace yul { struct AsmAnalysisInfo; struct Block; +struct Dialect; } namespace yul @@ -64,6 +65,7 @@ private: std::string m_optimizerStep; std::string m_expectation; + std::shared_ptr m_dialect; std::shared_ptr m_ast; std::shared_ptr m_analysisInfo; std::string m_obtainedResult; diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index e88b0538..8ef56ffe 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -85,7 +85,7 @@ public: { ErrorReporter errorReporter(m_errors); shared_ptr scanner = make_shared(CharStream(_input, "")); - m_ast = yul::Parser(errorReporter, yul::EVMDialect::strictAssemblyForEVM()).parse(scanner, false); + m_ast = yul::Parser(errorReporter, m_dialect).parse(scanner, false); if (!m_ast || !errorReporter.errors().empty()) { cout << "Error parsing source." << endl; @@ -98,7 +98,7 @@ public: errorReporter, EVMVersion::byzantium(), langutil::Error::Type::SyntaxError, - EVMDialect::strictAssemblyForEVM() + m_dialect ); if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) { @@ -120,9 +120,9 @@ public: return; if (!disambiguated) { - *m_ast = boost::get(Disambiguator(*m_analysisInfo)(*m_ast)); + *m_ast = boost::get(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast)); m_analysisInfo.reset(); - m_nameDispenser = make_shared(*m_ast); + m_nameDispenser = make_shared(*m_dialect, *m_ast); disambiguated = true; } cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; @@ -143,7 +143,7 @@ public: ForLoopInitRewriter{}(*m_ast); break; case 'c': - (CommonSubexpressionEliminator{})(*m_ast); + (CommonSubexpressionEliminator{*m_dialect})(*m_ast); break; case 'd': (VarDeclInitializer{})(*m_ast); @@ -161,28 +161,28 @@ public: (FunctionHoister{})(*m_ast); break; case 'e': - ExpressionInliner{*m_ast}.run(); + ExpressionInliner{*m_dialect, *m_ast}.run(); break; case 'i': FullInliner(*m_ast, *m_nameDispenser).run(); break; case 's': - ExpressionSimplifier::run(*m_ast); + ExpressionSimplifier::run(*m_dialect, *m_ast); break; case 't': - (StructuralSimplifier{})(*m_ast); + (StructuralSimplifier{*m_dialect})(*m_ast); break; case 'u': - UnusedPruner::runUntilStabilised(*m_ast); + UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); break; case 'a': SSATransform::run(*m_ast, *m_nameDispenser); break; case 'r': - RedundantAssignEliminator::run(*m_ast); + RedundantAssignEliminator::run(*m_dialect, *m_ast); break; case 'm': - Rematerialiser::run(*m_ast); + Rematerialiser::run(*m_dialect, *m_ast); break; default: cout << "Unknown option." << endl; @@ -194,6 +194,7 @@ public: private: ErrorList m_errors; shared_ptr m_ast; + shared_ptr m_dialect{EVMDialect::strictAssemblyForEVMObjects()}; shared_ptr m_analysisInfo; shared_ptr m_nameDispenser; }; -- cgit From 5b73c2ae3bce09442572b5401a7bcccc2ffe7590 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 17:22:17 +0100 Subject: Take special functions that require literals into account. --- libyul/AsmAnalysis.cpp | 10 ++++++++++ libyul/Dialect.h | 8 +++++++- libyul/backends/evm/EVMDialect.cpp | 8 +++++--- libyul/backends/evm/EVMDialect.h | 1 + libyul/optimiser/CommonSubexpressionEliminator.cpp | 15 ++++++++++++++- libyul/optimiser/CommonSubexpressionEliminator.h | 2 ++ libyul/optimiser/ExpressionSplitter.cpp | 6 ++++++ libyul/optimiser/ExpressionSplitter.h | 6 ++++-- libyul/optimiser/Suite.cpp | 4 ++-- libyul/optimiser/Suite.h | 1 - test/libyul/Parser.cpp | 2 +- test/libyul/YulOptimizerTest.cpp | 10 +++++----- test/tools/yulopti.cpp | 2 +- 13 files changed, 58 insertions(+), 17 deletions(-) diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 0ecc5a30..6a7b2b61 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -299,11 +299,14 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) bool success = true; size_t parameters = 0; size_t returns = 0; + 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(); + if (f->literalArguments) + needsLiteralArguments = true; } else if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor( [&](Scope::Variable const&) @@ -347,8 +350,15 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) } for (auto const& arg: _funCall.arguments | boost::adaptors::reversed) + { if (!expectExpression(arg)) success = false; + else if (needsLiteralArguments && arg.type() != typeid(Literal)) + m_errorReporter.typeError( + _funCall.functionName.location, + "Function expects direct literals as arguments." + ); + } // 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; diff --git a/libyul/Dialect.h b/libyul/Dialect.h index 01fd98df..e06a6826 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -44,7 +44,13 @@ struct BuiltinFunction YulString name; std::vector parameters; std::vector returns; - bool movable; + /// If true, calls to this function can be freely moved and copied (as long as their + /// arguments are either variables or also movable) without altering the semantics. + /// This means the function cannot depend on storage or memory, cannot have any side-effects, + /// but it can depend on state that is constant across an EVM-call. + bool movable = false; + /// If true, can only accept literals as arguments and they cannot be moved to voriables. + bool literalArguments = false; }; struct Dialect: boost::noncopyable diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 935f05c6..18502117 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -44,7 +44,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess): if (!m_objectAccess) return; - addFunction("datasize", 1, 1, true, [this]( + addFunction("datasize", 1, 1, true, true, [this]( FunctionCall const& _call, AbstractAssembly& _assembly, std::function @@ -58,7 +58,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess): else _assembly.appendDataSize(m_subIDs.at(dataName)); }); - addFunction("dataoffset", 1, 1, true, [this]( + addFunction("dataoffset", 1, 1, true, true, [this]( FunctionCall const& _call, AbstractAssembly& _assembly, std::function @@ -72,7 +72,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess): else _assembly.appendDataOffset(m_subIDs.at(dataName)); }); - addFunction("datacopy", 3, 0, false, []( + addFunction("datacopy", 3, 0, false, false, []( FunctionCall const&, AbstractAssembly& _assembly, std::function _visitArguments @@ -128,6 +128,7 @@ void EVMDialect::addFunction( size_t _params, size_t _returns, bool _movable, + bool _literalArguments, std::function)> _generateCode ) { @@ -137,5 +138,6 @@ void EVMDialect::addFunction( f.parameters.resize(_params); f.returns.resize(_returns); f.movable = _movable; + f.literalArguments = _literalArguments; f.generateCode = std::move(_generateCode); } diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index feb00b03..c23833bc 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -72,6 +72,7 @@ private: size_t _params, size_t _returns, bool _movable, + bool _literalArguments, std::function)> _generateCode ); diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 9b851333..8ce003e7 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -25,6 +25,7 @@ #include #include #include +#include using namespace std; using namespace dev; @@ -32,12 +33,24 @@ using namespace yul; void CommonSubexpressionEliminator::visit(Expression& _e) { + bool descend = true; + // If this is a function call to a function that requires literal arguments, + // do not try to simplify there. + if (_e.type() == typeid(FunctionCall)) + if (BuiltinFunction const* builtin = m_dialect.builtin(boost::get(_e).functionName.name)) + if (builtin->literalArguments) + // We should not modify function arguments that have to be literals + // Note that replacing the function call entirely is fine, + // if the function call is movable. + descend = false; + // We visit the inner expression first to first simplify inner expressions, // which hopefully allows more matches. // Note that the DataFlowAnalyzer itself only has code for visiting Statements, // so this basically invokes the AST walker directly and thus post-visiting // is also fine with regards to data flow analysis. - DataFlowAnalyzer::visit(_e); + if (descend) + DataFlowAnalyzer::visit(_e); if (_e.type() == typeid(Identifier)) { diff --git a/libyul/optimiser/CommonSubexpressionEliminator.h b/libyul/optimiser/CommonSubexpressionEliminator.h index 4b7973e2..9f416d9f 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.h +++ b/libyul/optimiser/CommonSubexpressionEliminator.h @@ -26,6 +26,8 @@ namespace yul { +struct Dialect; + /** * Optimisation stage that replaces expressions known to be the current value of a variable * in scope by a reference to that variable. diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index a3b2dc11..334e33f4 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -24,6 +24,7 @@ #include #include +#include #include @@ -43,6 +44,11 @@ void ExpressionSplitter::operator()(FunctionalInstruction& _instruction) void ExpressionSplitter::operator()(FunctionCall& _funCall) { + if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name)) + if (builtin->literalArguments) + // We cannot outline function arguments that have to be literals + return; + for (auto& arg: _funCall.arguments | boost::adaptors::reversed) outlineExpression(arg); } diff --git a/libyul/optimiser/ExpressionSplitter.h b/libyul/optimiser/ExpressionSplitter.h index d4d2b3f6..a72d14ba 100644 --- a/libyul/optimiser/ExpressionSplitter.h +++ b/libyul/optimiser/ExpressionSplitter.h @@ -31,6 +31,7 @@ namespace yul { class NameCollector; +struct Dialect; /** @@ -57,8 +58,8 @@ class NameCollector; class ExpressionSplitter: public ASTModifier { public: - explicit ExpressionSplitter(NameDispenser& _nameDispenser): - m_nameDispenser(_nameDispenser) + explicit ExpressionSplitter(Dialect const& _dialect, NameDispenser& _nameDispenser): + m_dialect(_dialect), m_nameDispenser(_nameDispenser) { } void operator()(FunctionalInstruction&) override; @@ -77,6 +78,7 @@ private: /// List of statements that should go in front of the currently visited AST element, /// at the statement level. std::vector m_statementsToPrefix; + Dialect const& m_dialect; NameDispenser& m_nameDispenser; }; diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 9b6e1337..e10916ea 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -67,7 +67,7 @@ void OptimiserSuite::run( for (size_t i = 0; i < 4; i++) { - ExpressionSplitter{dispenser}(ast); + ExpressionSplitter{_dialect, dispenser}(ast); SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast); @@ -90,7 +90,7 @@ void OptimiserSuite::run( ExpressionInliner(_dialect, ast).run(); UnusedPruner::runUntilStabilised(_dialect, ast); - ExpressionSplitter{dispenser}(ast); + ExpressionSplitter{_dialect, dispenser}(ast); SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast); diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index ce40b204..376e9889 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -41,7 +41,6 @@ public: Dialect const& _dialect, Block& _ast, AsmAnalysisInfo const& _analysisInfo, - std::set const& _externallyUsedIdentifiers = {} ); }; diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index df7e32a1..897f18ae 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -331,7 +331,7 @@ BOOST_AUTO_TEST_CASE(builtins_analysis) { return _name == "builtin"_yulstring ? &f : nullptr; } - BuiltinFunction f{"builtin"_yulstring, vector(2), vector(3), false}; + BuiltinFunction f{"builtin"_yulstring, vector(2), vector(3), false, false}; }; shared_ptr dialect = make_shared(); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 1b392e0f..0e3203cb 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -122,7 +122,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con else if (m_optimizerStep == "expressionSplitter") { NameDispenser nameDispenser{*m_dialect, *m_ast}; - ExpressionSplitter{nameDispenser}(*m_ast); + ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); } else if (m_optimizerStep == "expressionJoiner") { @@ -133,7 +133,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con { disambiguate(); NameDispenser nameDispenser{*m_dialect, *m_ast}; - ExpressionSplitter{nameDispenser}(*m_ast); + ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast); } @@ -158,7 +158,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con (FunctionHoister{})(*m_ast); (FunctionGrouper{})(*m_ast); NameDispenser nameDispenser{*m_dialect, *m_ast}; - ExpressionSplitter{nameDispenser}(*m_ast); + ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); FullInliner(*m_ast, nameDispenser).run(); ExpressionJoiner::run(*m_ast); } @@ -182,7 +182,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con { disambiguate(); NameDispenser nameDispenser{*m_dialect, *m_ast}; - ExpressionSplitter{nameDispenser}(*m_ast); + ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); CommonSubexpressionEliminator{*m_dialect}(*m_ast); ExpressionSimplifier::run(*m_dialect, *m_ast); UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); @@ -260,7 +260,7 @@ void YulOptimizerTest::printIndented(ostream& _stream, string const& _output, st bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - m_dialect = m_yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(); + m_dialect = m_yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVMObjects(); ErrorList errors; ErrorReporter errorReporter(errors); shared_ptr scanner = make_shared(CharStream(m_source, "")); diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 8ef56ffe..ac21fd91 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -149,7 +149,7 @@ public: (VarDeclInitializer{})(*m_ast); break; case 'x': - ExpressionSplitter{*m_nameDispenser}(*m_ast); + ExpressionSplitter{*m_dialect, *m_nameDispenser}(*m_ast); break; case 'j': ExpressionJoiner::run(*m_ast); -- cgit From ace601b8f6ebe8097b9c587c96494058d51b3e8f Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 20 Dec 2018 17:39:52 +0100 Subject: Tests. --- test/libyul/ObjectParser.cpp | 30 ++++++++++++++++++++++ .../object_access.yul | 23 +++++++++++++++++ .../expressionSplitter/object_access.yul | 21 +++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul create mode 100644 test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index bb88e4da..13a95788 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -250,6 +250,36 @@ BOOST_AUTO_TEST_CASE(to_string) BOOST_CHECK_EQUAL(asmStack.print(), expectation); } +BOOST_AUTO_TEST_CASE(arg_to_dataoffset_must_be_literal) +{ + string code = R"( + object "outer" { + code { let x := "outer" let y := dataoffset(x) } + } + )"; + CHECK_ERROR(code, TypeError, "Function expects direct literals as arguments."); +} + +BOOST_AUTO_TEST_CASE(arg_to_datasize_must_be_literal) +{ + string code = R"( + object "outer" { + code { let x := "outer" let y := datasize(x) } + } + )"; + CHECK_ERROR(code, TypeError, "Function expects direct literals as arguments."); +} + +BOOST_AUTO_TEST_CASE(args_to_datacopy_are_arbitrary) +{ + string code = R"( + object "outer" { + code { let x := 0 let y := 2 let s := 3 datacopy(x, y, s) } + } + )"; + BOOST_CHECK(successParse(code)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul new file mode 100644 index 00000000..5cfa3e6e --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul @@ -0,0 +1,23 @@ +{ + // Arguments to ``datasize`` and ``dataoffset`` need to be + // literals. We cannot simplify their arguments, but we can + // simplify them as a full expression. + // ``datacopy`` does not have this restriction. + let r := "abc" + let a := datasize("abc") + let x := dataoffset("abc") + // should be replaced by a + let y := datasize("abc") + datacopy("abc", x, y) + mstore(a, x) +} +// ---- +// commonSubexpressionEliminator +// { +// let r := "abc" +// let a := datasize("abc") +// let x := dataoffset("abc") +// let y := a +// datacopy(r, x, a) +// mstore(a, x) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul new file mode 100644 index 00000000..2689ab6f --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul @@ -0,0 +1,21 @@ +{ + // We should never split arguments to ``dataoffset`` + // or ``datasize`` because they need to be literals + let x := dataoffset("abc") + let y := datasize("abc") + // datacopy is fine, though + datacopy(mload(0), mload(1), mload(2)) +} +// ---- +// expressionSplitter +// { +// let x := dataoffset("abc") +// let y := datasize("abc") +// let _1 := 2 +// let _2 := mload(_1) +// let _3 := 1 +// let _4 := mload(_3) +// let _5 := 0 +// let _6 := mload(_5) +// datacopy(_6, _4, _2) +// } -- cgit From 937fc8538eed632ee114f48d5cecd1c63dd05f87 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Jan 2019 16:54:44 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 3d298f6f..23e394c6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: Bugfixes: + * Yul: Check that arguments to ``dataoffset`` and ``datasize`` are literals at parse time and properly take this into account in the optimizer. Build System: -- cgit From 26d47c7213ac8bb26fabd8d761567d940c55cfe3 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 11:04:31 +0100 Subject: Split creating contracts --- docs/contracts.rst | 118 +--------------------------------- docs/contracts/creating-contracts.rst | 117 +++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 117 deletions(-) create mode 100644 docs/contracts/creating-contracts.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 6d31ad82..bdf6902d 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -12,123 +12,7 @@ variables. Calling a function on a different contract (instance) will perform an EVM function call and thus switch the context such that state variables are inaccessible. -.. index:: ! contract;creation, constructor - -****************** -Creating Contracts -****************** - -Contracts can be created "from outside" via Ethereum transactions or from within Solidity contracts. - -IDEs, such as `Remix `_, make the creation process seamless using UI elements. - -Creating contracts programmatically on Ethereum is best done via using the JavaScript API `web3.js `_. -It has a function called `web3.eth.Contract `_ -to facilitate contract creation. - -When a contract is created, its constructor_ (a function declared with the ``constructor`` keyword) is executed once. - -A constructor is optional. Only one constructor is allowed, which means -overloading is not supported. - -After the constructor has executed, the final code of the contract is deployed to the -blockchain. This code includes all public and external functions and all functions -that are reachable from there through function calls. The deployed code does not -include the constructor code or internal functions only called from the constructor. - -.. index:: constructor;arguments - -Internally, constructor arguments are passed :ref:`ABI encoded ` after the code of -the contract itself, but you do not have to care about this if you use ``web3.js``. - -If a contract wants to create another contract, the source code -(and the binary) of the created contract has to be known to the creator. -This means that cyclic creation dependencies are impossible. - -:: - - pragma solidity >=0.4.22 <0.6.0; - - contract OwnedToken { - // `TokenCreator` is a contract type that is defined below. - // It is fine to reference it as long as it is not used - // to create a new contract. - TokenCreator creator; - address owner; - bytes32 name; - - // This is the constructor which registers the - // creator and the assigned name. - constructor(bytes32 _name) public { - // State variables are accessed via their name - // and not via e.g. `this.owner`. Functions can - // be accessed directly or through `this.f`, - // but the latter provides an external view - // to the function. Especially in the constructor, - // you should not access functions externally, - // because the function does not exist yet. - // See the next section for details. - owner = msg.sender; - - // We do an explicit type conversion from `address` - // to `TokenCreator` and assume that the type of - // the calling contract is `TokenCreator`, there is - // no real way to check that. - creator = TokenCreator(msg.sender); - name = _name; - } - - function changeName(bytes32 newName) public { - // Only the creator can alter the name -- - // the comparison is possible since contracts - // are explicitly convertible to addresses. - if (msg.sender == address(creator)) - name = newName; - } - - function transfer(address newOwner) public { - // Only the current owner can transfer the token. - if (msg.sender != owner) return; - - // We ask the creator contract if the transfer - // should proceed by using a function of the - // `TokenCreator` contract defined below. If - // the call fails (e.g. due to out-of-gas), - // the execution also fails here. - if (creator.isTokenTransferOK(owner, newOwner)) - owner = newOwner; - } - } - - contract TokenCreator { - function createToken(bytes32 name) - public - returns (OwnedToken tokenAddress) - { - // Create a new `Token` contract and return its address. - // From the JavaScript side, the return type is - // `address`, as this is the closest type available in - // the ABI. - return new OwnedToken(name); - } - - function changeName(OwnedToken tokenAddress, bytes32 name) public { - // Again, the external type of `tokenAddress` is - // simply `address`. - tokenAddress.changeName(name); - } - - // Perform checks to determine if transferring a token to the - // `OwnedToken` contract should proceed - function isTokenTransferOK(address currentOwner, address newOwner) - public - pure - returns (bool ok) - { - // Check an arbitrary condition to see if transfer should proceed - return keccak256(abi.encodePacked(currentOwner, newOwner))[0] == 0x7f; - } - } +.. include:: contracts/creating-contracts.rst .. index:: ! visibility, external, public, private, internal diff --git a/docs/contracts/creating-contracts.rst b/docs/contracts/creating-contracts.rst new file mode 100644 index 00000000..981243b1 --- /dev/null +++ b/docs/contracts/creating-contracts.rst @@ -0,0 +1,117 @@ +.. index:: ! contract;creation, constructor + +****************** +Creating Contracts +****************** + +Contracts can be created "from outside" via Ethereum transactions or from within Solidity contracts. + +IDEs, such as `Remix `_, make the creation process seamless using UI elements. + +Creating contracts programmatically on Ethereum is best done via using the JavaScript API `web3.js `_. +It has a function called `web3.eth.Contract `_ +to facilitate contract creation. + +When a contract is created, its :ref:`constructor ` (a function declared with the ``constructor`` keyword) is executed once. + +A constructor is optional. Only one constructor is allowed, which means +overloading is not supported. + +After the constructor has executed, the final code of the contract is deployed to the +blockchain. This code includes all public and external functions and all functions +that are reachable from there through function calls. The deployed code does not +include the constructor code or internal functions only called from the constructor. + +.. index:: constructor;arguments + +Internally, constructor arguments are passed :ref:`ABI encoded ` after the code of +the contract itself, but you do not have to care about this if you use ``web3.js``. + +If a contract wants to create another contract, the source code +(and the binary) of the created contract has to be known to the creator. +This means that cyclic creation dependencies are impossible. + +:: + + pragma solidity >=0.4.22 <0.6.0; + + contract OwnedToken { + // `TokenCreator` is a contract type that is defined below. + // It is fine to reference it as long as it is not used + // to create a new contract. + TokenCreator creator; + address owner; + bytes32 name; + + // This is the constructor which registers the + // creator and the assigned name. + constructor(bytes32 _name) public { + // State variables are accessed via their name + // and not via e.g. `this.owner`. Functions can + // be accessed directly or through `this.f`, + // but the latter provides an external view + // to the function. Especially in the constructor, + // you should not access functions externally, + // because the function does not exist yet. + // See the next section for details. + owner = msg.sender; + + // We do an explicit type conversion from `address` + // to `TokenCreator` and assume that the type of + // the calling contract is `TokenCreator`, there is + // no real way to check that. + creator = TokenCreator(msg.sender); + name = _name; + } + + function changeName(bytes32 newName) public { + // Only the creator can alter the name -- + // the comparison is possible since contracts + // are explicitly convertible to addresses. + if (msg.sender == address(creator)) + name = newName; + } + + function transfer(address newOwner) public { + // Only the current owner can transfer the token. + if (msg.sender != owner) return; + + // We ask the creator contract if the transfer + // should proceed by using a function of the + // `TokenCreator` contract defined below. If + // the call fails (e.g. due to out-of-gas), + // the execution also fails here. + if (creator.isTokenTransferOK(owner, newOwner)) + owner = newOwner; + } + } + + contract TokenCreator { + function createToken(bytes32 name) + public + returns (OwnedToken tokenAddress) + { + // Create a new `Token` contract and return its address. + // From the JavaScript side, the return type is + // `address`, as this is the closest type available in + // the ABI. + return new OwnedToken(name); + } + + function changeName(OwnedToken tokenAddress, bytes32 name) public { + // Again, the external type of `tokenAddress` is + // simply `address`. + tokenAddress.changeName(name); + } + + // Perform checks to determine if transferring a token to the + // `OwnedToken` contract should proceed + function isTokenTransferOK(address currentOwner, address newOwner) + public + pure + returns (bool ok) + { + // Check an arbitrary condition to see if transfer should proceed + return keccak256(abi.encodePacked(currentOwner, newOwner))[0] == 0x7f; + } + } -- cgit From 3503f3809d3fac509914234029021bd33127516e Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Jan 2019 17:52:31 +0100 Subject: Do not compile the contracts subdirectory. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 08a5a045..342aefa9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -81,7 +81,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ['_build', 'contracts'] # The reST default role (used for this markup: `text`) to use for all # documents. -- cgit From ee5eac4b3edcb36f092b9140323a6e1f8ffa11be Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 18:08:00 +0100 Subject: Split using for into new file --- docs/contracts.rst | 120 +------------------------------------------ docs/contracts/using-for.rst | 119 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 119 deletions(-) create mode 100644 docs/contracts/using-for.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 3c91b2f8..ac1d2cac 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1304,122 +1304,4 @@ constant to be pushed onto the stack and the dispatcher code compares the current address against this constant for any non-view and non-pure function. -.. index:: ! using for, library - -.. _using-for: - -********* -Using For -********* - -The directive ``using A for B;`` can be used to attach library -functions (from the library ``A``) to any type (``B``). -These functions will receive the object they are called on -as their first parameter (like the ``self`` variable in Python). - -The effect of ``using A for *;`` is that the functions from -the library ``A`` are attached to *any* type. - -In both situations, *all* functions in the library are attached, -even those where the type of the first parameter does not -match the type of the object. The type is checked at the -point the function is called and function overload -resolution is performed. - -The ``using A for B;`` directive is active only within the current -contract, including within all of its functions, and has no effect -outside of the contract in which it is used. The directive -may only be used inside a contract, not inside any of its functions. - -By including a library, its data types including library functions are -available without having to add further code. - -Let us rewrite the set example from the -:ref:`libraries` in this way:: - - pragma solidity >=0.4.16 <0.6.0; - - // This is the same code as before, just without comments - library Set { - struct Data { mapping(uint => bool) flags; } - - function insert(Data storage self, uint value) - public - returns (bool) - { - if (self.flags[value]) - return false; // already there - self.flags[value] = true; - return true; - } - - function remove(Data storage self, uint value) - public - returns (bool) - { - if (!self.flags[value]) - return false; // not there - self.flags[value] = false; - return true; - } - - function contains(Data storage self, uint value) - public - view - returns (bool) - { - return self.flags[value]; - } - } - - contract C { - using Set for Set.Data; // this is the crucial change - Set.Data knownValues; - - function register(uint value) public { - // Here, all variables of type Set.Data have - // corresponding member functions. - // The following function call is identical to - // `Set.insert(knownValues, value)` - require(knownValues.insert(value)); - } - } - -It is also possible to extend elementary types in that way:: - - pragma solidity >=0.4.16 <0.6.0; - - library Search { - function indexOf(uint[] storage self, uint value) - public - view - returns (uint) - { - for (uint i = 0; i < self.length; i++) - if (self[i] == value) return i; - return uint(-1); - } - } - - contract C { - using Search for uint[]; - uint[] data; - - function append(uint value) public { - data.push(value); - } - - function replace(uint _old, uint _new) public { - // This performs the library function call - uint index = data.indexOf(_old); - if (index == uint(-1)) - data.push(_new); - else - data[index] = _new; - } - } - -Note that all library calls are actual EVM function calls. This means that -if you pass memory or value types, a copy will be performed, even of the -``self`` variable. The only situation where no copy will be performed -is when storage reference variables are used. +.. include:: contracts/using-for.rst \ No newline at end of file diff --git a/docs/contracts/using-for.rst b/docs/contracts/using-for.rst new file mode 100644 index 00000000..ef456ff4 --- /dev/null +++ b/docs/contracts/using-for.rst @@ -0,0 +1,119 @@ +.. index:: ! using for, library + +.. _using-for: + +********* +Using For +********* + +The directive ``using A for B;`` can be used to attach library +functions (from the library ``A``) to any type (``B``). +These functions will receive the object they are called on +as their first parameter (like the ``self`` variable in Python). + +The effect of ``using A for *;`` is that the functions from +the library ``A`` are attached to *any* type. + +In both situations, *all* functions in the library are attached, +even those where the type of the first parameter does not +match the type of the object. The type is checked at the +point the function is called and function overload +resolution is performed. + +The ``using A for B;`` directive is active only within the current +contract, including within all of its functions, and has no effect +outside of the contract in which it is used. The directive +may only be used inside a contract, not inside any of its functions. + +By including a library, its data types including library functions are +available without having to add further code. + +Let us rewrite the set example from the +:ref:`libraries` in this way:: + + pragma solidity >=0.4.16 <0.6.0; + + // This is the same code as before, just without comments + library Set { + struct Data { mapping(uint => bool) flags; } + + function insert(Data storage self, uint value) + public + returns (bool) + { + if (self.flags[value]) + return false; // already there + self.flags[value] = true; + return true; + } + + function remove(Data storage self, uint value) + public + returns (bool) + { + if (!self.flags[value]) + return false; // not there + self.flags[value] = false; + return true; + } + + function contains(Data storage self, uint value) + public + view + returns (bool) + { + return self.flags[value]; + } + } + + contract C { + using Set for Set.Data; // this is the crucial change + Set.Data knownValues; + + function register(uint value) public { + // Here, all variables of type Set.Data have + // corresponding member functions. + // The following function call is identical to + // `Set.insert(knownValues, value)` + require(knownValues.insert(value)); + } + } + +It is also possible to extend elementary types in that way:: + + pragma solidity >=0.4.16 <0.6.0; + + library Search { + function indexOf(uint[] storage self, uint value) + public + view + returns (uint) + { + for (uint i = 0; i < self.length; i++) + if (self[i] == value) return i; + return uint(-1); + } + } + + contract C { + using Search for uint[]; + uint[] data; + + function append(uint value) public { + data.push(value); + } + + function replace(uint _old, uint _new) public { + // This performs the library function call + uint index = data.indexOf(_old); + if (index == uint(-1)) + data.push(_new); + else + data[index] = _new; + } + } + +Note that all library calls are actual EVM function calls. This means that +if you pass memory or value types, a copy will be performed, even of the +``self`` variable. The only situation where no copy will be performed +is when storage reference variables are used. -- cgit From c9b2e5da8f1218248284b14dc40cfe1c6b96e229 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 13:52:17 +0100 Subject: Split Function modifiers doc into smaller file --- docs/contracts.rst | 112 +--------------------------------- docs/contracts/function-modifiers.rst | 111 +++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 111 deletions(-) create mode 100644 docs/contracts/function-modifiers.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 3c91b2f8..0dbbbfdc 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -16,117 +16,7 @@ inaccessible. .. include:: contracts/visibility-and-getters.rst -.. index:: ! function;modifier - -.. _modifiers: - -****************** -Function Modifiers -****************** - -Modifiers can be used to easily change the behaviour of functions. For example, -they can automatically check a condition prior to executing the function. Modifiers are -inheritable properties of contracts and may be overridden by derived contracts. - -:: - - pragma solidity ^0.5.0; - - contract owned { - constructor() public { owner = msg.sender; } - address payable owner; - - // This contract only defines a modifier but does not use - // it: it will be used in derived contracts. - // The function body is inserted where the special symbol - // `_;` in the definition of a modifier appears. - // This means that if the owner calls this function, the - // function is executed and otherwise, an exception is - // thrown. - modifier onlyOwner { - require( - msg.sender == owner, - "Only owner can call this function." - ); - _; - } - } - - contract mortal is owned { - // This contract inherits the `onlyOwner` modifier from - // `owned` and applies it to the `close` function, which - // causes that calls to `close` only have an effect if - // they are made by the stored owner. - function close() public onlyOwner { - selfdestruct(owner); - } - } - - contract priced { - // Modifiers can receive arguments: - modifier costs(uint price) { - if (msg.value >= price) { - _; - } - } - } - - contract Register is priced, owned { - mapping (address => bool) registeredAddresses; - uint price; - - constructor(uint initialPrice) public { price = initialPrice; } - - // It is important to also provide the - // `payable` keyword here, otherwise the function will - // automatically reject all Ether sent to it. - function register() public payable costs(price) { - registeredAddresses[msg.sender] = true; - } - - function changePrice(uint _price) public onlyOwner { - price = _price; - } - } - - contract Mutex { - bool locked; - modifier noReentrancy() { - require( - !locked, - "Reentrant call." - ); - locked = true; - _; - locked = false; - } - - /// This function is protected by a mutex, which means that - /// reentrant calls from within `msg.sender.call` cannot call `f` again. - /// The `return 7` statement assigns 7 to the return value but still - /// executes the statement `locked = false` in the modifier. - function f() public noReentrancy returns (uint) { - (bool success,) = msg.sender.call(""); - require(success); - return 7; - } - } - -Multiple modifiers are applied to a function by specifying them in a -whitespace-separated list and are evaluated in the order presented. - -.. warning:: - In an earlier version of Solidity, ``return`` statements in functions - having modifiers behaved differently. - -Explicit returns from a modifier or function body only leave the current -modifier or function body. Return variables are assigned and -control flow continues after the "_" in the preceding modifier. - -Arbitrary expressions are allowed for modifier arguments and in this context, -all symbols visible from the function are visible in the modifier. Symbols -introduced in the modifier are not visible in the function (as they might -change by overriding). +.. include:: contracts/function-modifiers.rst .. include:: contracts/constant-state-variables.rst diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst new file mode 100644 index 00000000..376cd9fa --- /dev/null +++ b/docs/contracts/function-modifiers.rst @@ -0,0 +1,111 @@ +.. index:: ! function;modifier + +.. _modifiers: + +****************** +Function Modifiers +****************** + +Modifiers can be used to easily change the behaviour of functions. For example, +they can automatically check a condition prior to executing the function. Modifiers are +inheritable properties of contracts and may be overridden by derived contracts. + +:: + + pragma solidity ^0.5.0; + + contract owned { + constructor() public { owner = msg.sender; } + address payable owner; + + // This contract only defines a modifier but does not use + // it: it will be used in derived contracts. + // The function body is inserted where the special symbol + // `_;` in the definition of a modifier appears. + // This means that if the owner calls this function, the + // function is executed and otherwise, an exception is + // thrown. + modifier onlyOwner { + require( + msg.sender == owner, + "Only owner can call this function." + ); + _; + } + } + + contract mortal is owned { + // This contract inherits the `onlyOwner` modifier from + // `owned` and applies it to the `close` function, which + // causes that calls to `close` only have an effect if + // they are made by the stored owner. + function close() public onlyOwner { + selfdestruct(owner); + } + } + + contract priced { + // Modifiers can receive arguments: + modifier costs(uint price) { + if (msg.value >= price) { + _; + } + } + } + + contract Register is priced, owned { + mapping (address => bool) registeredAddresses; + uint price; + + constructor(uint initialPrice) public { price = initialPrice; } + + // It is important to also provide the + // `payable` keyword here, otherwise the function will + // automatically reject all Ether sent to it. + function register() public payable costs(price) { + registeredAddresses[msg.sender] = true; + } + + function changePrice(uint _price) public onlyOwner { + price = _price; + } + } + + contract Mutex { + bool locked; + modifier noReentrancy() { + require( + !locked, + "Reentrant call." + ); + locked = true; + _; + locked = false; + } + + /// This function is protected by a mutex, which means that + /// reentrant calls from within `msg.sender.call` cannot call `f` again. + /// The `return 7` statement assigns 7 to the return value but still + /// executes the statement `locked = false` in the modifier. + function f() public noReentrancy returns (uint) { + (bool success,) = msg.sender.call(""); + require(success); + return 7; + } + } + +Multiple modifiers are applied to a function by specifying them in a +whitespace-separated list and are evaluated in the order presented. + +.. warning:: + In an earlier version of Solidity, ``return`` statements in functions + having modifiers behaved differently. + +Explicit returns from a modifier or function body only leave the current +modifier or function body. Return variables are assigned and +control flow continues after the "_" in the preceding modifier. + +Arbitrary expressions are allowed for modifier arguments and in this context, +all symbols visible from the function are visible in the modifier. Symbols +introduced in the modifier are not visible in the function (as they might +change by overriding). -- cgit From 13cd96136aa499303a4c2ed92848366ebd0b9343 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 18:05:27 +0100 Subject: Split libraries into new doc --- docs/contracts.rst | 231 +------------------------------------------ docs/contracts/libraries.rst | 230 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+), 230 deletions(-) create mode 100644 docs/contracts/libraries.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 3c91b2f8..dc1e71e9 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1073,236 +1073,7 @@ Contracts can inherit interfaces as they would inherit other contracts. Types defined inside interfaces and other contract-like structures can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``. -.. index:: ! library, callcode, delegatecall - -.. _libraries: - -********* -Libraries -********* - -Libraries are similar to contracts, but their purpose is that they are deployed -only once at a specific address and their code is reused using the ``DELEGATECALL`` -(``CALLCODE`` until Homestead) -feature of the EVM. This means that if library functions are called, their code -is executed in the context of the calling contract, i.e. ``this`` points to the -calling contract, and especially the storage from the calling contract can be -accessed. As a library is an isolated piece of source code, it can only access -state variables of the calling contract if they are explicitly supplied (it -would have no way to name them, otherwise). Library functions can only be -called directly (i.e. without the use of ``DELEGATECALL``) if they do not modify -the state (i.e. if they are ``view`` or ``pure`` functions), -because libraries are assumed to be stateless. In particular, it is -not possible to destroy a library. - -.. note:: - Until version 0.4.20, it was possible to destroy libraries by - circumventing Solidity's type system. Starting from that version, - libraries contain a :ref:`mechanism` that - disallows state-modifying functions - to be called directly (i.e. without ``DELEGATECALL``). - -Libraries can be seen as implicit base contracts of the contracts that use them. -They will not be explicitly visible in the inheritance hierarchy, but calls -to library functions look just like calls to functions of explicit base -contracts (``L.f()`` if ``L`` is the name of the library). Furthermore, -``internal`` functions of libraries are visible in all contracts, just as -if the library were a base contract. Of course, calls to internal functions -use the internal calling convention, which means that all internal types -can be passed and types :ref:`stored in memory ` will be passed by reference and not copied. -To realize this in the EVM, code of internal library functions -and all functions called from therein will at compile time be pulled into the calling -contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``. - -.. index:: using for, set - -The following example illustrates how to use libraries (but manual method -be sure to check out :ref:`using for ` for a -more advanced example to implement a set). - -:: - - pragma solidity >=0.4.22 <0.6.0; - - library Set { - // We define a new struct datatype that will be used to - // hold its data in the calling contract. - struct Data { mapping(uint => bool) flags; } - - // Note that the first parameter is of type "storage - // reference" and thus only its storage address and not - // its contents is passed as part of the call. This is a - // special feature of library functions. It is idiomatic - // to call the first parameter `self`, if the function can - // be seen as a method of that object. - function insert(Data storage self, uint value) - public - returns (bool) - { - if (self.flags[value]) - return false; // already there - self.flags[value] = true; - return true; - } - - function remove(Data storage self, uint value) - public - returns (bool) - { - if (!self.flags[value]) - return false; // not there - self.flags[value] = false; - return true; - } - - function contains(Data storage self, uint value) - public - view - returns (bool) - { - return self.flags[value]; - } - } - - contract C { - Set.Data knownValues; - - function register(uint value) public { - // The library functions can be called without a - // specific instance of the library, since the - // "instance" will be the current contract. - require(Set.insert(knownValues, value)); - } - // In this contract, we can also directly access knownValues.flags, if we want. - } - -Of course, you do not have to follow this way to use -libraries: they can also be used without defining struct -data types. Functions also work without any storage -reference parameters, and they can have multiple storage reference -parameters and in any position. - -The calls to ``Set.contains``, ``Set.insert`` and ``Set.remove`` -are all compiled as calls (``DELEGATECALL``) to an external -contract/library. If you use libraries, be aware that an -actual external function call is performed. -``msg.sender``, ``msg.value`` and ``this`` will retain their values -in this call, though (prior to Homestead, because of the use of ``CALLCODE``, ``msg.sender`` and -``msg.value`` changed, though). - -The following example shows how to use :ref:`types stored in memory ` and -internal functions in libraries in order to implement -custom types without the overhead of external function calls: - -:: - - pragma solidity >=0.4.16 <0.6.0; - - library BigInt { - struct bigint { - uint[] limbs; - } - - function fromUint(uint x) internal pure returns (bigint memory r) { - r.limbs = new uint[](1); - r.limbs[0] = x; - } - - function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) { - r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length)); - uint carry = 0; - for (uint i = 0; i < r.limbs.length; ++i) { - uint a = limb(_a, i); - uint b = limb(_b, i); - r.limbs[i] = a + b + carry; - if (a + b < a || (a + b == uint(-1) && carry > 0)) - carry = 1; - else - carry = 0; - } - if (carry > 0) { - // too bad, we have to add a limb - uint[] memory newLimbs = new uint[](r.limbs.length + 1); - uint i; - for (i = 0; i < r.limbs.length; ++i) - newLimbs[i] = r.limbs[i]; - newLimbs[i] = carry; - r.limbs = newLimbs; - } - } - - function limb(bigint memory _a, uint _limb) internal pure returns (uint) { - return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; - } - - function max(uint a, uint b) private pure returns (uint) { - return a > b ? a : b; - } - } - - contract C { - using BigInt for BigInt.bigint; - - function f() public pure { - BigInt.bigint memory x = BigInt.fromUint(7); - BigInt.bigint memory y = BigInt.fromUint(uint(-1)); - BigInt.bigint memory z = x.add(y); - assert(z.limb(1) > 0); - } - } - -As the compiler cannot know where the library will be -deployed at, these addresses have to be filled into the -final bytecode by a linker -(see :ref:`commandline-compiler` for how to use the -commandline compiler for linking). If the addresses are not -given as arguments to the compiler, the compiled hex code -will contain placeholders of the form ``__Set______`` (where -``Set`` is the name of the library). The address can be filled -manually by replacing all those 40 symbols by the hex -encoding of the address of the library contract. - -.. note:: - Manually linking libraries on the generated bytecode is discouraged, because - it is restricted to 36 characters. - You should ask the compiler to link the libraries at the time - a contract is compiled by either using - the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use - the standard-JSON interface to the compiler. - -Restrictions for libraries in comparison to contracts: - -- No state variables -- Cannot inherit nor be inherited -- Cannot receive Ether - -(These might be lifted at a later point.) - -.. _call-protection: - -Call Protection For Libraries -============================= - -As mentioned in the introduction, if a library's code is executed -using a ``CALL`` instead of a ``DELEGATECALL`` or ``CALLCODE``, -it will revert unless a ``view`` or ``pure`` function is called. - -The EVM does not provide a direct way for a contract to detect -whether it was called using ``CALL`` or not, but a contract -can use the ``ADDRESS`` opcode to find out "where" it is -currently running. The generated code compares this address -to the address used at construction time to determine the mode -of calling. - -More specifically, the runtime code of a library always starts -with a push instruction, which is a zero of 20 bytes at -compilation time. When the deploy code runs, this constant -is replaced in memory by the current address and this -modified code is stored in the contract. At runtime, -this causes the deploy time address to be the first -constant to be pushed onto the stack and the dispatcher -code compares the current address against this constant -for any non-view and non-pure function. +.. include:: contracts/libraries.rst .. index:: ! using for, library diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst new file mode 100644 index 00000000..0cabe18a --- /dev/null +++ b/docs/contracts/libraries.rst @@ -0,0 +1,230 @@ +.. index:: ! library, callcode, delegatecall + +.. _libraries: + +********* +Libraries +********* + +Libraries are similar to contracts, but their purpose is that they are deployed +only once at a specific address and their code is reused using the ``DELEGATECALL`` +(``CALLCODE`` until Homestead) +feature of the EVM. This means that if library functions are called, their code +is executed in the context of the calling contract, i.e. ``this`` points to the +calling contract, and especially the storage from the calling contract can be +accessed. As a library is an isolated piece of source code, it can only access +state variables of the calling contract if they are explicitly supplied (it +would have no way to name them, otherwise). Library functions can only be +called directly (i.e. without the use of ``DELEGATECALL``) if they do not modify +the state (i.e. if they are ``view`` or ``pure`` functions), +because libraries are assumed to be stateless. In particular, it is +not possible to destroy a library. + +.. note:: + Until version 0.4.20, it was possible to destroy libraries by + circumventing Solidity's type system. Starting from that version, + libraries contain a :ref:`mechanism` that + disallows state-modifying functions + to be called directly (i.e. without ``DELEGATECALL``). + +Libraries can be seen as implicit base contracts of the contracts that use them. +They will not be explicitly visible in the inheritance hierarchy, but calls +to library functions look just like calls to functions of explicit base +contracts (``L.f()`` if ``L`` is the name of the library). Furthermore, +``internal`` functions of libraries are visible in all contracts, just as +if the library were a base contract. Of course, calls to internal functions +use the internal calling convention, which means that all internal types +can be passed and types :ref:`stored in memory ` will be passed by reference and not copied. +To realize this in the EVM, code of internal library functions +and all functions called from therein will at compile time be pulled into the calling +contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``. + +.. index:: using for, set + +The following example illustrates how to use libraries (but manual method +be sure to check out :ref:`using for ` for a +more advanced example to implement a set). + +:: + + pragma solidity >=0.4.22 <0.6.0; + + library Set { + // We define a new struct datatype that will be used to + // hold its data in the calling contract. + struct Data { mapping(uint => bool) flags; } + + // Note that the first parameter is of type "storage + // reference" and thus only its storage address and not + // its contents is passed as part of the call. This is a + // special feature of library functions. It is idiomatic + // to call the first parameter `self`, if the function can + // be seen as a method of that object. + function insert(Data storage self, uint value) + public + returns (bool) + { + if (self.flags[value]) + return false; // already there + self.flags[value] = true; + return true; + } + + function remove(Data storage self, uint value) + public + returns (bool) + { + if (!self.flags[value]) + return false; // not there + self.flags[value] = false; + return true; + } + + function contains(Data storage self, uint value) + public + view + returns (bool) + { + return self.flags[value]; + } + } + + contract C { + Set.Data knownValues; + + function register(uint value) public { + // The library functions can be called without a + // specific instance of the library, since the + // "instance" will be the current contract. + require(Set.insert(knownValues, value)); + } + // In this contract, we can also directly access knownValues.flags, if we want. + } + +Of course, you do not have to follow this way to use +libraries: they can also be used without defining struct +data types. Functions also work without any storage +reference parameters, and they can have multiple storage reference +parameters and in any position. + +The calls to ``Set.contains``, ``Set.insert`` and ``Set.remove`` +are all compiled as calls (``DELEGATECALL``) to an external +contract/library. If you use libraries, be aware that an +actual external function call is performed. +``msg.sender``, ``msg.value`` and ``this`` will retain their values +in this call, though (prior to Homestead, because of the use of ``CALLCODE``, ``msg.sender`` and +``msg.value`` changed, though). + +The following example shows how to use :ref:`types stored in memory ` and +internal functions in libraries in order to implement +custom types without the overhead of external function calls: + +:: + + pragma solidity >=0.4.16 <0.6.0; + + library BigInt { + struct bigint { + uint[] limbs; + } + + function fromUint(uint x) internal pure returns (bigint memory r) { + r.limbs = new uint[](1); + r.limbs[0] = x; + } + + function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) { + r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length)); + uint carry = 0; + for (uint i = 0; i < r.limbs.length; ++i) { + uint a = limb(_a, i); + uint b = limb(_b, i); + r.limbs[i] = a + b + carry; + if (a + b < a || (a + b == uint(-1) && carry > 0)) + carry = 1; + else + carry = 0; + } + if (carry > 0) { + // too bad, we have to add a limb + uint[] memory newLimbs = new uint[](r.limbs.length + 1); + uint i; + for (i = 0; i < r.limbs.length; ++i) + newLimbs[i] = r.limbs[i]; + newLimbs[i] = carry; + r.limbs = newLimbs; + } + } + + function limb(bigint memory _a, uint _limb) internal pure returns (uint) { + return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; + } + + function max(uint a, uint b) private pure returns (uint) { + return a > b ? a : b; + } + } + + contract C { + using BigInt for BigInt.bigint; + + function f() public pure { + BigInt.bigint memory x = BigInt.fromUint(7); + BigInt.bigint memory y = BigInt.fromUint(uint(-1)); + BigInt.bigint memory z = x.add(y); + assert(z.limb(1) > 0); + } + } + +As the compiler cannot know where the library will be +deployed at, these addresses have to be filled into the +final bytecode by a linker +(see :ref:`commandline-compiler` for how to use the +commandline compiler for linking). If the addresses are not +given as arguments to the compiler, the compiled hex code +will contain placeholders of the form ``__Set______`` (where +``Set`` is the name of the library). The address can be filled +manually by replacing all those 40 symbols by the hex +encoding of the address of the library contract. + +.. note:: + Manually linking libraries on the generated bytecode is discouraged, because + it is restricted to 36 characters. + You should ask the compiler to link the libraries at the time + a contract is compiled by either using + the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use + the standard-JSON interface to the compiler. + +Restrictions for libraries in comparison to contracts: + +- No state variables +- Cannot inherit nor be inherited +- Cannot receive Ether + +(These might be lifted at a later point.) + +.. _call-protection: + +Call Protection For Libraries +============================= + +As mentioned in the introduction, if a library's code is executed +using a ``CALL`` instead of a ``DELEGATECALL`` or ``CALLCODE``, +it will revert unless a ``view`` or ``pure`` function is called. + +The EVM does not provide a direct way for a contract to detect +whether it was called using ``CALL`` or not, but a contract +can use the ``ADDRESS`` opcode to find out "where" it is +currently running. The generated code compares this address +to the address used at construction time to determine the mode +of calling. + +More specifically, the runtime code of a library always starts +with a push instruction, which is a zero of 20 bytes at +compilation time. When the deploy code runs, this constant +is replaced in memory by the current address and this +modified code is stored in the contract. At runtime, +this causes the deploy time address to be the first +constant to be pushed onto the stack and the dispatcher +code compares the current address against this constant +for any non-view and non-pure function. -- cgit From f69af050cb54710d527a2601a376da11c9444755 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 15:45:39 +0100 Subject: Split Abstract Contracts docs to new file --- docs/contracts.rst | 45 +---------------------------------- docs/contracts/abstract-contracts.rst | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 44 deletions(-) create mode 100644 docs/contracts/abstract-contracts.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 3c91b2f8..214a876f 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -991,50 +991,7 @@ When the inheritance results in a contract with a function and a modifier of the This error is produced also by an event and a modifier of the same name, and a function and an event of the same name. As an exception, a state variable getter can override a public function. -.. index:: ! contract;abstract, ! abstract contract - -.. _abstract-contract: - -****************** -Abstract Contracts -****************** - -Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``):: - - pragma solidity >=0.4.0 <0.6.0; - - contract Feline { - function utterance() public returns (bytes32); - } - -Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts:: - - pragma solidity >=0.4.0 <0.6.0; - - contract Feline { - function utterance() public returns (bytes32); - } - - contract Cat is Feline { - function utterance() public returns (bytes32) { return "miaow"; } - } - -If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. - -Note that a function without implementation is different from a :ref:`Function Type ` even though their syntax looks very similar. - -Example of function without implementation (a function declaration):: - - function foo(address) external returns (address); - -Example of a Function Type (a variable declaration, where the variable is of type ``function``):: - - function(address) external returns (address) foo; - -Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and -facilitating patterns like the `Template method `_ and removing code duplication. -Abstract contracts are useful in the same way that defining methods in an interface is useful. It is a way for the designer of the abstract contract to say "any child of mine must implement this method". - +.. include:: contracts/abstract-contracts.rst .. index:: ! contract;interface, ! interface contract diff --git a/docs/contracts/abstract-contracts.rst b/docs/contracts/abstract-contracts.rst new file mode 100644 index 00000000..87340733 --- /dev/null +++ b/docs/contracts/abstract-contracts.rst @@ -0,0 +1,43 @@ +.. index:: ! contract;abstract, ! abstract contract + +.. _abstract-contract: + +****************** +Abstract Contracts +****************** + +Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``):: + + pragma solidity >=0.4.0 <0.6.0; + + contract Feline { + function utterance() public returns (bytes32); + } + +Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts:: + + pragma solidity >=0.4.0 <0.6.0; + + contract Feline { + function utterance() public returns (bytes32); + } + + contract Cat is Feline { + function utterance() public returns (bytes32) { return "miaow"; } + } + +If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. + +Note that a function without implementation is different from a :ref:`Function Type ` even though their syntax looks very similar. + +Example of function without implementation (a function declaration):: + + function foo(address) external returns (address); + +Example of a Function Type (a variable declaration, where the variable is of type ``function``):: + + function(address) external returns (address) foo; + +Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and +facilitating patterns like the `Template method `_ and removing code duplication. +Abstract contracts are useful in the same way that defining methods in an interface is useful. It is a way for the designer of the abstract contract to say "any child of mine must implement this method". -- cgit From da1d70e939f11523dccc87009219b22b39b48f15 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 14:50:21 +0100 Subject: Split events docs into file --- docs/contracts.rst | 162 +--------------------------------------------- docs/contracts/events.rst | 161 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 161 deletions(-) create mode 100644 docs/contracts/events.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 3c91b2f8..f181aa5d 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -529,167 +529,7 @@ Calling ``f(50)`` would create a type error since ``50`` can be implicitly conve and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly converted to ``uint8``. -.. index:: ! event - -.. _events: - -****** -Events -****** - -Solidity events give an abstraction on top of the EVM's logging functionality. -Applications can subscribe and listen to these events through the RPC interface of an Ethereum client. - -Events are inheritable members of contracts. When you call them, they cause the -arguments to be stored in the transaction's log - a special data structure -in the blockchain. These logs are associated with the address of the contract, -are incorporated into the blockchain, and stay there as long as a block is -accessible (forever as of the Frontier and Homestead releases, but this might -change with Serenity). The Log and its event data is not accessible from within -contracts (not even from the contract that created them). - -It is possible to request a simple payment verification (SPV) for logs, so if -an external entity supplies a contract with such a verification, it can check -that the log actually exists inside the blockchain. You have to supply block headers -because the contract can only see the last 256 block hashes. - -You can add the attribute ``indexed`` to up to three parameters which adds them -to a special data structure known as :ref:`"topics" ` instead of -the data part of the log. If you use arrays (including ``string`` and ``bytes``) -as indexed arguments, its Keccak-256 hash is stored as a topic instead, this is -because a topic can only hold a single word (32 bytes). - -All parameters without the ``indexed`` attribute are :ref:`ABI-encoded ` -into the data part of the log. - -Topics allow you to search for events, for example when filtering a sequence of -blocks for certain events. You can also filter events by the address of the -contract that emitted the event. - -For example, the code below uses the web3.js ``subscribe("logs")`` -`method `_ to filter -logs that match a topic with a certain address value: - -.. code-block:: javascript - - var options = { - fromBlock: 0, - address: web3.eth.defaultAccount, - topics: ["0x0000000000000000000000000000000000000000000000000000000000000000", null, null] - }; - web3.eth.subscribe('logs', options, function (error, result) { - if (!error) - console.log(result); - }) - .on("data", function (log) { - console.log(log); - }) - .on("changed", function (log) { - }); - - -The hash of the signature of the event is one of the topics, except if you -declared the event with the ``anonymous`` specifier. This means that it is -not possible to filter for specific anonymous events by name. - -:: - - pragma solidity >=0.4.21 <0.6.0; - - contract ClientReceipt { - event Deposit( - address indexed _from, - bytes32 indexed _id, - uint _value - ); - - function deposit(bytes32 _id) public payable { - // Events are emitted using `emit`, followed by - // the name of the event and the arguments - // (if any) in parentheses. Any such invocation - // (even deeply nested) can be detected from - // the JavaScript API by filtering for `Deposit`. - emit Deposit(msg.sender, _id, msg.value); - } - } - -The use in the JavaScript API is as follows: - -:: - - var abi = /* abi as generated by the compiler */; - var ClientReceipt = web3.eth.contract(abi); - var clientReceipt = ClientReceipt.at("0x1234...ab67" /* address */); - - var event = clientReceipt.Deposit(); - - // watch for changes - event.watch(function(error, result){ - // result contains non-indexed arguments and topics - // given to the `Deposit` call. - if (!error) - console.log(result); - }); - - - // Or pass a callback to start watching immediately - var event = clientReceipt.Deposit(function(error, result) { - if (!error) - console.log(result); - }); - -The output of the above looks like the following (trimmed): - -.. code-block:: json - - { - "returnValues": { - "_from": "0x1111…FFFFCCCC", - "_id": "0x50…sd5adb20", - "_value": "0x420042" - }, - "raw": { - "data": "0x7f…91385", - "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] - } - } - -.. index:: ! log - -Low-Level Interface to Logs -=========================== - -It is also possible to access the low-level interface to the logging -mechanism via the functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``. -``logi`` takes ``i + 1`` parameter of type ``bytes32``, where the first -argument will be used for the data part of the log and the others -as topics. The event call above can be performed in the same way as - -:: - - pragma solidity >=0.4.10 <0.6.0; - - contract C { - function f() public payable { - uint256 _id = 0x420042; - log3( - bytes32(msg.value), - bytes32(0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20), - bytes32(uint256(msg.sender)), - bytes32(_id) - ); - } - } - -where the long hexadecimal number is equal to -``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event. - -Additional Resources for Understanding Events -============================================== - -- `Javascript documentation `_ -- `Example usage of events `_ -- `How to access them in js `_ +.. include:: contracts/events.rst .. index:: ! inheritance, ! base class, ! contract;base, ! deriving diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst new file mode 100644 index 00000000..ecb0a87f --- /dev/null +++ b/docs/contracts/events.rst @@ -0,0 +1,161 @@ +.. index:: ! event + +.. _events: + +****** +Events +****** + +Solidity events give an abstraction on top of the EVM's logging functionality. +Applications can subscribe and listen to these events through the RPC interface of an Ethereum client. + +Events are inheritable members of contracts. When you call them, they cause the +arguments to be stored in the transaction's log - a special data structure +in the blockchain. These logs are associated with the address of the contract, +are incorporated into the blockchain, and stay there as long as a block is +accessible (forever as of the Frontier and Homestead releases, but this might +change with Serenity). The Log and its event data is not accessible from within +contracts (not even from the contract that created them). + +It is possible to request a simple payment verification (SPV) for logs, so if +an external entity supplies a contract with such a verification, it can check +that the log actually exists inside the blockchain. You have to supply block headers +because the contract can only see the last 256 block hashes. + +You can add the attribute ``indexed`` to up to three parameters which adds them +to a special data structure known as :ref:`"topics" ` instead of +the data part of the log. If you use arrays (including ``string`` and ``bytes``) +as indexed arguments, its Keccak-256 hash is stored as a topic instead, this is +because a topic can only hold a single word (32 bytes). + +All parameters without the ``indexed`` attribute are :ref:`ABI-encoded ` +into the data part of the log. + +Topics allow you to search for events, for example when filtering a sequence of +blocks for certain events. You can also filter events by the address of the +contract that emitted the event. + +For example, the code below uses the web3.js ``subscribe("logs")`` +`method `_ to filter +logs that match a topic with a certain address value: + +.. code-block:: javascript + + var options = { + fromBlock: 0, + address: web3.eth.defaultAccount, + topics: ["0x0000000000000000000000000000000000000000000000000000000000000000", null, null] + }; + web3.eth.subscribe('logs', options, function (error, result) { + if (!error) + console.log(result); + }) + .on("data", function (log) { + console.log(log); + }) + .on("changed", function (log) { + }); + + +The hash of the signature of the event is one of the topics, except if you +declared the event with the ``anonymous`` specifier. This means that it is +not possible to filter for specific anonymous events by name. + +:: + + pragma solidity >=0.4.21 <0.6.0; + + contract ClientReceipt { + event Deposit( + address indexed _from, + bytes32 indexed _id, + uint _value + ); + + function deposit(bytes32 _id) public payable { + // Events are emitted using `emit`, followed by + // the name of the event and the arguments + // (if any) in parentheses. Any such invocation + // (even deeply nested) can be detected from + // the JavaScript API by filtering for `Deposit`. + emit Deposit(msg.sender, _id, msg.value); + } + } + +The use in the JavaScript API is as follows: + +:: + + var abi = /* abi as generated by the compiler */; + var ClientReceipt = web3.eth.contract(abi); + var clientReceipt = ClientReceipt.at("0x1234...ab67" /* address */); + + var event = clientReceipt.Deposit(); + + // watch for changes + event.watch(function(error, result){ + // result contains non-indexed arguments and topics + // given to the `Deposit` call. + if (!error) + console.log(result); + }); + + + // Or pass a callback to start watching immediately + var event = clientReceipt.Deposit(function(error, result) { + if (!error) + console.log(result); + }); + +The output of the above looks like the following (trimmed): + +.. code-block:: json + + { + "returnValues": { + "_from": "0x1111…FFFFCCCC", + "_id": "0x50…sd5adb20", + "_value": "0x420042" + }, + "raw": { + "data": "0x7f…91385", + "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] + } + } + +.. index:: ! log + +Low-Level Interface to Logs +=========================== + +It is also possible to access the low-level interface to the logging +mechanism via the functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``. +``logi`` takes ``i + 1`` parameter of type ``bytes32``, where the first +argument will be used for the data part of the log and the others +as topics. The event call above can be performed in the same way as + +:: + + pragma solidity >=0.4.10 <0.6.0; + + contract C { + function f() public payable { + uint256 _id = 0x420042; + log3( + bytes32(msg.value), + bytes32(0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20), + bytes32(uint256(msg.sender)), + bytes32(_id) + ); + } + } + +where the long hexadecimal number is equal to +``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event. + +Additional Resources for Understanding Events +============================================== + +- `Javascript documentation `_ +- `Example usage of events `_ +- `How to access them in js `_ -- cgit From 5ca509a4eaf93b4cde420547f3094c529f56bc6b Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 15:37:33 +0100 Subject: Split inheritance into new doc --- docs/contracts.rst | 300 +---------------------------------------- docs/contracts/inheritance.rst | 299 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+), 299 deletions(-) create mode 100644 docs/contracts/inheritance.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 3c91b2f8..ee93f7b6 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -691,305 +691,7 @@ Additional Resources for Understanding Events - `Example usage of events `_ - `How to access them in js `_ -.. index:: ! inheritance, ! base class, ! contract;base, ! deriving - -*********** -Inheritance -*********** - -Solidity supports multiple inheritance including polymorphism. - -All function calls are virtual, which means that the most derived function -is called, except when the contract name is explicitly given or the -``super`` keyword is used. - -When a contract inherits from other contracts, only a single -contract is created on the blockchain, and the code from all the base contracts -is compiled into the created contract. - -The general inheritance system is very similar to -`Python's `_, -especially concerning multiple inheritance, but there are also -some :ref:`differences `. - -Details are given in the following example. - -:: - - pragma solidity ^0.5.0; - - contract owned { - constructor() public { owner = msg.sender; } - address payable owner; - } - - // Use `is` to derive from another contract. Derived - // contracts can access all non-private members including - // internal functions and state variables. These cannot be - // accessed externally via `this`, though. - contract mortal is owned { - function kill() public { - if (msg.sender == owner) selfdestruct(owner); - } - } - - // These abstract contracts are only provided to make the - // interface known to the compiler. Note the function - // without body. If a contract does not implement all - // functions it can only be used as an interface. - contract Config { - function lookup(uint id) public returns (address adr); - } - - contract NameReg { - function register(bytes32 name) public; - function unregister() public; - } - - // Multiple inheritance is possible. Note that `owned` is - // also a base class of `mortal`, yet there is only a single - // instance of `owned` (as for virtual inheritance in C++). - contract named is owned, mortal { - constructor(bytes32 name) public { - Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); - NameReg(config.lookup(1)).register(name); - } - - // Functions can be overridden by another function with the same name and - // the same number/types of inputs. If the overriding function has different - // types of output parameters, that causes an error. - // Both local and message-based function calls take these overrides - // into account. - function kill() public { - if (msg.sender == owner) { - Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); - NameReg(config.lookup(1)).unregister(); - // It is still possible to call a specific - // overridden function. - mortal.kill(); - } - } - } - - // If a constructor takes an argument, it needs to be - // provided in the header (or modifier-invocation-style at - // the constructor of the derived contract (see below)). - contract PriceFeed is owned, mortal, named("GoldFeed") { - function updateInfo(uint newInfo) public { - if (msg.sender == owner) info = newInfo; - } - - function get() public view returns(uint r) { return info; } - - uint info; - } - -Note that above, we call ``mortal.kill()`` to "forward" the -destruction request. The way this is done is problematic, as -seen in the following example:: - - pragma solidity >=0.4.22 <0.6.0; - - contract owned { - constructor() public { owner = msg.sender; } - address payable owner; - } - - contract mortal is owned { - function kill() public { - if (msg.sender == owner) selfdestruct(owner); - } - } - - contract Base1 is mortal { - function kill() public { /* do cleanup 1 */ mortal.kill(); } - } - - contract Base2 is mortal { - function kill() public { /* do cleanup 2 */ mortal.kill(); } - } - - contract Final is Base1, Base2 { - } - -A call to ``Final.kill()`` will call ``Base2.kill`` as the most -derived override, but this function will bypass -``Base1.kill``, basically because it does not even know about -``Base1``. The way around this is to use ``super``:: - - pragma solidity >=0.4.22 <0.6.0; - - contract owned { - constructor() public { owner = msg.sender; } - address payable owner; - } - - contract mortal is owned { - function kill() public { - if (msg.sender == owner) selfdestruct(owner); - } - } - - contract Base1 is mortal { - function kill() public { /* do cleanup 1 */ super.kill(); } - } - - - contract Base2 is mortal { - function kill() public { /* do cleanup 2 */ super.kill(); } - } - - contract Final is Base1, Base2 { - } - -If ``Base2`` calls a function of ``super``, it does not simply -call this function on one of its base contracts. Rather, it -calls this function on the next base contract in the final -inheritance graph, so it will call ``Base1.kill()`` (note that -the final inheritance sequence is -- starting with the most -derived contract: Final, Base2, Base1, mortal, owned). -The actual function that is called when using super is -not known in the context of the class where it is used, -although its type is known. This is similar for ordinary -virtual method lookup. - -.. index:: ! constructor - -.. _constructor: - -Constructors -============ - -A constructor is an optional function declared with the ``constructor`` keyword -which is executed upon contract creation, and where you can run contract -initialisation code. - -Before the constructor code is executed, state variables are initialised to -their specified value if you initialise them inline, or zero if you do not. - -After the constructor has run, the final code of the contract is deployed -to the blockchain. The deployment of -the code costs additional gas linear to the length of the code. -This code includes all functions that are part of the public interface -and all functions that are reachable from there through function calls. -It does not include the constructor code or internal functions that are -only called from the constructor. - -Constructor functions can be either ``public`` or ``internal``. If there is no -constructor, the contract will assume the default constructor, which is -equivalent to ``constructor() public {}``. For example: - -:: - - pragma solidity ^0.5.0; - - contract A { - uint public a; - - constructor(uint _a) internal { - a = _a; - } - } - - contract B is A(1) { - constructor() public {} - } - -A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. - -.. warning :: - Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. - This syntax was deprecated and is not allowed anymore in version 0.5.0. - - -.. index:: ! base;constructor - -Arguments for Base Constructors -=============================== - -The constructors of all the base contracts will be called following the -linearization rules explained below. If the base constructors have arguments, -derived contracts need to specify all of them. This can be done in two ways:: - - pragma solidity >=0.4.22 <0.6.0; - - contract Base { - uint x; - constructor(uint _x) public { x = _x; } - } - - // Either directly specify in the inheritance list... - contract Derived1 is Base(7) { - constructor() public {} - } - - // or through a "modifier" of the derived constructor. - contract Derived2 is Base { - constructor(uint _y) Base(_y * _y) public {} - } - -One way is directly in the inheritance list (``is Base(7)``). The other is in -the way a modifier is invoked as part of -the derived constructor (``Base(_y * _y)``). The first way to -do it is more convenient if the constructor argument is a -constant and defines the behaviour of the contract or -describes it. The second way has to be used if the -constructor arguments of the base depend on those of the -derived contract. Arguments have to be given either in the -inheritance list or in modifier-style in the derived constructor. -Specifying arguments in both places is an error. - -If a derived contract does not specify the arguments to all of its base -contracts' constructors, it will be abstract. - -.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization - -.. _multi-inheritance: - -Multiple Inheritance and Linearization -====================================== - -Languages that allow multiple inheritance have to deal with -several problems. One is the `Diamond Problem `_. -Solidity is similar to Python in that it uses "`C3 Linearization `_" -to force a specific order in the directed acyclic graph (DAG) of base classes. This -results in the desirable property of monotonicity but -disallows some inheritance graphs. Especially, the order in -which the base classes are given in the ``is`` directive is -important: You have to list the direct base contracts -in the order from "most base-like" to "most derived". -Note that this order is the reverse of the one used in Python. - -Another simplifying way to explain this is that when a function is called that -is defined multiple times in different contracts, the given bases -are searched from right to left (left to right in Python) in a depth-first manner, -stopping at the first match. If a base contract has already been searched, it is skipped. - -In the following code, Solidity will give the -error "Linearization of inheritance graph impossible". - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract X {} - contract A is X {} - // This will not compile - contract C is A, X {} - -The reason for this is that ``C`` requests ``X`` to override ``A`` -(by specifying ``A, X`` in this order), but ``A`` itself -requests to override ``X``, which is a contradiction that -cannot be resolved. - - - -Inheriting Different Kinds of Members of the Same Name -====================================================== - -When the inheritance results in a contract with a function and a modifier of the same name, it is considered as an error. -This error is produced also by an event and a modifier of the same name, and a function and an event of the same name. -As an exception, a state variable getter can override a public function. +.. include:: contracts/inheritance.rst .. index:: ! contract;abstract, ! abstract contract diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst new file mode 100644 index 00000000..2e94c2f9 --- /dev/null +++ b/docs/contracts/inheritance.rst @@ -0,0 +1,299 @@ +.. index:: ! inheritance, ! base class, ! contract;base, ! deriving + +*********** +Inheritance +*********** + +Solidity supports multiple inheritance including polymorphism. + +All function calls are virtual, which means that the most derived function +is called, except when the contract name is explicitly given or the +``super`` keyword is used. + +When a contract inherits from other contracts, only a single +contract is created on the blockchain, and the code from all the base contracts +is compiled into the created contract. + +The general inheritance system is very similar to +`Python's `_, +especially concerning multiple inheritance, but there are also +some :ref:`differences `. + +Details are given in the following example. + +:: + + pragma solidity ^0.5.0; + + contract owned { + constructor() public { owner = msg.sender; } + address payable owner; + } + + // Use `is` to derive from another contract. Derived + // contracts can access all non-private members including + // internal functions and state variables. These cannot be + // accessed externally via `this`, though. + contract mortal is owned { + function kill() public { + if (msg.sender == owner) selfdestruct(owner); + } + } + + // These abstract contracts are only provided to make the + // interface known to the compiler. Note the function + // without body. If a contract does not implement all + // functions it can only be used as an interface. + contract Config { + function lookup(uint id) public returns (address adr); + } + + contract NameReg { + function register(bytes32 name) public; + function unregister() public; + } + + // Multiple inheritance is possible. Note that `owned` is + // also a base class of `mortal`, yet there is only a single + // instance of `owned` (as for virtual inheritance in C++). + contract named is owned, mortal { + constructor(bytes32 name) public { + Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); + NameReg(config.lookup(1)).register(name); + } + + // Functions can be overridden by another function with the same name and + // the same number/types of inputs. If the overriding function has different + // types of output parameters, that causes an error. + // Both local and message-based function calls take these overrides + // into account. + function kill() public { + if (msg.sender == owner) { + Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); + NameReg(config.lookup(1)).unregister(); + // It is still possible to call a specific + // overridden function. + mortal.kill(); + } + } + } + + // If a constructor takes an argument, it needs to be + // provided in the header (or modifier-invocation-style at + // the constructor of the derived contract (see below)). + contract PriceFeed is owned, mortal, named("GoldFeed") { + function updateInfo(uint newInfo) public { + if (msg.sender == owner) info = newInfo; + } + + function get() public view returns(uint r) { return info; } + + uint info; + } + +Note that above, we call ``mortal.kill()`` to "forward" the +destruction request. The way this is done is problematic, as +seen in the following example:: + + pragma solidity >=0.4.22 <0.6.0; + + contract owned { + constructor() public { owner = msg.sender; } + address payable owner; + } + + contract mortal is owned { + function kill() public { + if (msg.sender == owner) selfdestruct(owner); + } + } + + contract Base1 is mortal { + function kill() public { /* do cleanup 1 */ mortal.kill(); } + } + + contract Base2 is mortal { + function kill() public { /* do cleanup 2 */ mortal.kill(); } + } + + contract Final is Base1, Base2 { + } + +A call to ``Final.kill()`` will call ``Base2.kill`` as the most +derived override, but this function will bypass +``Base1.kill``, basically because it does not even know about +``Base1``. The way around this is to use ``super``:: + + pragma solidity >=0.4.22 <0.6.0; + + contract owned { + constructor() public { owner = msg.sender; } + address payable owner; + } + + contract mortal is owned { + function kill() public { + if (msg.sender == owner) selfdestruct(owner); + } + } + + contract Base1 is mortal { + function kill() public { /* do cleanup 1 */ super.kill(); } + } + + + contract Base2 is mortal { + function kill() public { /* do cleanup 2 */ super.kill(); } + } + + contract Final is Base1, Base2 { + } + +If ``Base2`` calls a function of ``super``, it does not simply +call this function on one of its base contracts. Rather, it +calls this function on the next base contract in the final +inheritance graph, so it will call ``Base1.kill()`` (note that +the final inheritance sequence is -- starting with the most +derived contract: Final, Base2, Base1, mortal, owned). +The actual function that is called when using super is +not known in the context of the class where it is used, +although its type is known. This is similar for ordinary +virtual method lookup. + +.. index:: ! constructor + +.. _constructor: + +Constructors +============ + +A constructor is an optional function declared with the ``constructor`` keyword +which is executed upon contract creation, and where you can run contract +initialisation code. + +Before the constructor code is executed, state variables are initialised to +their specified value if you initialise them inline, or zero if you do not. + +After the constructor has run, the final code of the contract is deployed +to the blockchain. The deployment of +the code costs additional gas linear to the length of the code. +This code includes all functions that are part of the public interface +and all functions that are reachable from there through function calls. +It does not include the constructor code or internal functions that are +only called from the constructor. + +Constructor functions can be either ``public`` or ``internal``. If there is no +constructor, the contract will assume the default constructor, which is +equivalent to ``constructor() public {}``. For example: + +:: + + pragma solidity ^0.5.0; + + contract A { + uint public a; + + constructor(uint _a) internal { + a = _a; + } + } + + contract B is A(1) { + constructor() public {} + } + +A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. + +.. warning :: + Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. + This syntax was deprecated and is not allowed anymore in version 0.5.0. + + +.. index:: ! base;constructor + +Arguments for Base Constructors +=============================== + +The constructors of all the base contracts will be called following the +linearization rules explained below. If the base constructors have arguments, +derived contracts need to specify all of them. This can be done in two ways:: + + pragma solidity >=0.4.22 <0.6.0; + + contract Base { + uint x; + constructor(uint _x) public { x = _x; } + } + + // Either directly specify in the inheritance list... + contract Derived1 is Base(7) { + constructor() public {} + } + + // or through a "modifier" of the derived constructor. + contract Derived2 is Base { + constructor(uint _y) Base(_y * _y) public {} + } + +One way is directly in the inheritance list (``is Base(7)``). The other is in +the way a modifier is invoked as part of +the derived constructor (``Base(_y * _y)``). The first way to +do it is more convenient if the constructor argument is a +constant and defines the behaviour of the contract or +describes it. The second way has to be used if the +constructor arguments of the base depend on those of the +derived contract. Arguments have to be given either in the +inheritance list or in modifier-style in the derived constructor. +Specifying arguments in both places is an error. + +If a derived contract does not specify the arguments to all of its base +contracts' constructors, it will be abstract. + +.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization + +.. _multi-inheritance: + +Multiple Inheritance and Linearization +====================================== + +Languages that allow multiple inheritance have to deal with +several problems. One is the `Diamond Problem `_. +Solidity is similar to Python in that it uses "`C3 Linearization `_" +to force a specific order in the directed acyclic graph (DAG) of base classes. This +results in the desirable property of monotonicity but +disallows some inheritance graphs. Especially, the order in +which the base classes are given in the ``is`` directive is +important: You have to list the direct base contracts +in the order from "most base-like" to "most derived". +Note that this order is the reverse of the one used in Python. + +Another simplifying way to explain this is that when a function is called that +is defined multiple times in different contracts, the given bases +are searched from right to left (left to right in Python) in a depth-first manner, +stopping at the first match. If a base contract has already been searched, it is skipped. + +In the following code, Solidity will give the +error "Linearization of inheritance graph impossible". + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract X {} + contract A is X {} + // This will not compile + contract C is A, X {} + +The reason for this is that ``C`` requests ``X`` to override ``A`` +(by specifying ``A, X`` in this order), but ``A`` itself +requests to override ``X``, which is a contradiction that +cannot be resolved. + + + +Inheriting Different Kinds of Members of the Same Name +====================================================== + +When the inheritance results in a contract with a function and a modifier of the same name, it is considered as an error. +This error is produced also by an event and a modifier of the same name, and a function and an event of the same name. +As an exception, a state variable getter can override a public function. -- cgit From cc99d636650d20a35a446d16f2b81d1fa8a2dabd Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 17:49:34 +0100 Subject: Split interfaces into new file --- docs/contracts.rst | 38 +------------------------------------- docs/contracts/interfaces.rst | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 docs/contracts/interfaces.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 7f7d821a..2a397f6d 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -534,43 +534,7 @@ converted to ``uint8``. .. include:: contracts/inheritance.rst .. include:: contracts/abstract-contracts.rst - -.. index:: ! contract;interface, ! interface contract - -.. _interfaces: - -********** -Interfaces -********** - -Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions: - -- They cannot inherit other contracts or interfaces. -- All declared functions must be external. -- They cannot declare a constructor. -- They cannot declare state variables. - -Some of these restrictions might be lifted in the future. - -Interfaces are basically limited to what the Contract ABI can represent, and the conversion between the ABI and -an interface should be possible without any information loss. - -Interfaces are denoted by their own keyword: - -:: - - pragma solidity ^0.5.0; - - interface Token { - enum TokenType { Fungible, NonFungible } - struct Coin { string obverse; string reverse; } - function transfer(address recipient, uint amount) external; - } - -Contracts can inherit interfaces as they would inherit other contracts. - -Types defined inside interfaces and other contract-like structures -can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``. +.. include:: contracts/interfaces.rst .. index:: ! library, callcode, delegatecall diff --git a/docs/contracts/interfaces.rst b/docs/contracts/interfaces.rst new file mode 100644 index 00000000..b551b518 --- /dev/null +++ b/docs/contracts/interfaces.rst @@ -0,0 +1,36 @@ +.. index:: ! contract;interface, ! interface contract + +.. _interfaces: + +********** +Interfaces +********** + +Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions: + +- They cannot inherit other contracts or interfaces. +- All declared functions must be external. +- They cannot declare a constructor. +- They cannot declare state variables. + +Some of these restrictions might be lifted in the future. + +Interfaces are basically limited to what the Contract ABI can represent, and the conversion between the ABI and +an interface should be possible without any information loss. + +Interfaces are denoted by their own keyword: + +:: + + pragma solidity ^0.5.0; + + interface Token { + enum TokenType { Fungible, NonFungible } + struct Coin { string obverse; string reverse; } + function transfer(address recipient, uint amount) external; + } + +Contracts can inherit interfaces as they would inherit other contracts. + +Types defined inside interfaces and other contract-like structures +can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``. -- cgit From f5b24a38b8006b66f5e5bed37e8041d87998eddf Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 8 Jan 2019 15:03:40 +0100 Subject: Make function grouper idempotent. --- libyul/optimiser/FunctionGrouper.cpp | 15 +++++++++++++++ libyul/optimiser/FunctionGrouper.h | 3 +++ .../fullInliner/multi_fun_callback.yul | 12 +++++------- .../functionGrouper/already_grouped.yul | 17 +++++++++++++++++ .../functionGrouper/grouped_but_not_ordered.yul | 19 +++++++++++++++++++ 5 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul create mode 100644 test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul diff --git a/libyul/optimiser/FunctionGrouper.cpp b/libyul/optimiser/FunctionGrouper.cpp index 02ce22cd..b9852fcd 100644 --- a/libyul/optimiser/FunctionGrouper.cpp +++ b/libyul/optimiser/FunctionGrouper.cpp @@ -33,6 +33,9 @@ using namespace dev::solidity; void FunctionGrouper::operator()(Block& _block) { + if (alreadyGrouped(_block)) + return; + vector reordered; reordered.emplace_back(Block{_block.location, {}}); @@ -45,3 +48,15 @@ void FunctionGrouper::operator()(Block& _block) } _block.statements = std::move(reordered); } + +bool FunctionGrouper::alreadyGrouped(Block const& _block) +{ + if (_block.statements.empty()) + return false; + if (_block.statements.front().type() != typeid(Block)) + return false; + for (size_t i = 1; i < _block.statements.size(); ++i) + if (_block.statements.at(i).type() != typeid(FunctionDefinition)) + return false; + return true; +} diff --git a/libyul/optimiser/FunctionGrouper.h b/libyul/optimiser/FunctionGrouper.h index 3b3f48a7..4b6abf76 100644 --- a/libyul/optimiser/FunctionGrouper.h +++ b/libyul/optimiser/FunctionGrouper.h @@ -38,6 +38,9 @@ class FunctionGrouper { public: void operator()(Block& _block); + +private: + bool alreadyGrouped(Block const& _block); }; } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul index 6e4acb97..dbbc5422 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul @@ -26,13 +26,11 @@ // fullInliner // { // { -// { -// let f_x := 100 -// mstore(0, f_x) -// mstore(7, h()) -// g(10) -// mstore(1, f_x) -// } +// let f_x := 100 +// mstore(0, f_x) +// mstore(7, h()) +// g(10) +// mstore(1, f_x) // } // function f(x) // { diff --git a/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul b/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul new file mode 100644 index 00000000..42e8a48e --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul @@ -0,0 +1,17 @@ +{ + { + let x := 2 + } + function f() -> y { y := 8 } +} +// ---- +// functionGrouper +// { +// { +// let x := 2 +// } +// function f() -> y +// { +// y := 8 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul b/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul new file mode 100644 index 00000000..0abb5d87 --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul @@ -0,0 +1,19 @@ +{ + function f() -> y { y := 8 } + { + let x := 2 + } +} +// ---- +// functionGrouper +// { +// { +// { +// let x := 2 +// } +// } +// function f() -> y +// { +// y := 8 +// } +// } -- cgit From 2d19903d1af69e517f0362730a77ad3e768522d5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 8 Jan 2019 14:44:06 +0100 Subject: Another ABI test case for the optimizer. --- test/libyul/yulOptimizerTests/fullSuite/abi2.yul | 1136 ++++++++++++++++++++++ 1 file changed, 1136 insertions(+) create mode 100644 test/libyul/yulOptimizerTests/fullSuite/abi2.yul diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul new file mode 100644 index 00000000..d61980ba --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -0,0 +1,1136 @@ +{ + // This ignores many of the encoding / decoding functions. Over time, + // we should add them all here. + + let a, b := abi_decode_tuple_t_contract$_Module_$1038t_contract$_Module_$1038(mload(0), mload(1)) + sstore(0, a) + let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) + sstore(1, x0) + sstore(1, x1) + sstore(1, x2) + sstore(1, x3) + sstore(1, x4) + function abi_decode_t_address(offset, end) -> value + { + value := cleanup_revert_t_address(calldataload(offset)) + } + function abi_decode_t_address_payable(offset_1, end_2) -> value_3 + { + value_3 := cleanup_revert_t_address_payable(calldataload(offset_1)) + } + function abi_decode_t_array$_t_address_$dyn_calldata_ptr(offset_4, end_5) -> arrayPos, length + { + if iszero(slt(add(offset_4, 0x1f), end_5)) + { + revert(0, 0) + } + length := calldataload(offset_4) + if gt(length, 0xffffffffffffffff) + { + revert(0, 0) + } + arrayPos := add(offset_4, 0x20) + if gt(add(arrayPos, mul(length, 0x20)), end_5) + { + revert(0, 0) + } + } + function abi_decode_t_bool_fromMemory(offset_6, end_7) -> value_8 + { + value_8 := cleanup_revert_t_bool(mload(offset_6)) + } + function abi_decode_t_bytes32(offset_9, end_10) -> value_11 + { + value_11 := cleanup_revert_t_bytes32(calldataload(offset_9)) + } + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + length_15 := calldataload(offset_12) + if gt(length_15, 0xffffffffffffffff) + { + revert(0, 0) + } + arrayPos_14 := add(offset_12, 0x20) + if gt(add(arrayPos_14, mul(length_15, 0x1)), end_13) + { + revert(0, 0) + } + } + function abi_decode_t_bytes_memory_ptr(offset_16, end_17) -> array + { + if iszero(slt(add(offset_16, 0x1f), end_17)) + { + revert(0, 0) + } + let length_18 := calldataload(offset_16) + array := allocateMemory(array_allocation_size_t_bytes_memory_ptr(length_18)) + mstore(array, length_18) + let src := add(offset_16, 0x20) + let dst := add(array, 0x20) + if gt(add(src, length_18), end_17) + { + revert(0, 0) + } + copy_calldata_to_memory(src, dst, length_18) + } + function abi_decode_t_contract$_Module_$1038(offset_19, end_20) -> value_21 + { + value_21 := cleanup_revert_t_contract$_Module_$1038(calldataload(offset_19)) + } + function abi_decode_t_enum$_Operation_$1949(offset_22, end_23) -> value_24 + { + value_24 := cleanup_revert_t_enum$_Operation_$1949(calldataload(offset_22)) + } + function abi_decode_t_uint256(offset_25, end_26) -> value_27 + { + value_27 := cleanup_revert_t_uint256(calldataload(offset_25)) + } + function abi_decode_tuple_t_address(headStart, dataEnd) -> value0 + { + if slt(sub(dataEnd, headStart), 32) + { + revert(0, 0) + } + { + let offset_28 := 0 + value0 := abi_decode_t_address(add(headStart, offset_28), dataEnd) + } + } + function abi_decode_tuple_t_addresst_addresst_address(headStart_29, dataEnd_30) -> value0_31, value1, value2 + { + if slt(sub(dataEnd_30, headStart_29), 96) + { + revert(0, 0) + } + { + let offset_32 := 0 + value0_31 := abi_decode_t_address(add(headStart_29, offset_32), dataEnd_30) + } + { + let offset_33 := 32 + value1 := abi_decode_t_address(add(headStart_29, offset_33), dataEnd_30) + } + { + let offset_34 := 64 + value2 := abi_decode_t_address(add(headStart_29, offset_34), dataEnd_30) + } + } + function abi_decode_tuple_t_addresst_addresst_uint256(headStart_35, dataEnd_36) -> value0_37, value1_38, value2_39 + { + if slt(sub(dataEnd_36, headStart_35), 96) + { + revert(0, 0) + } + { + let offset_40 := 0 + value0_37 := abi_decode_t_address(add(headStart_35, offset_40), dataEnd_36) + } + { + let offset_41 := 32 + value1_38 := abi_decode_t_address(add(headStart_35, offset_41), dataEnd_36) + } + { + let offset_42 := 64 + value2_39 := abi_decode_t_uint256(add(headStart_35, offset_42), dataEnd_36) + } + } + function abi_decode_tuple_t_addresst_bytes32(headStart_43, dataEnd_44) -> value0_45, value1_46 + { + if slt(sub(dataEnd_44, headStart_43), 64) + { + revert(0, 0) + } + { + let offset_47 := 0 + value0_45 := abi_decode_t_address(add(headStart_43, offset_47), dataEnd_44) + } + { + let offset_48 := 32 + value1_46 := abi_decode_t_bytes32(add(headStart_43, offset_48), dataEnd_44) + } + } + function abi_decode_tuple_t_addresst_uint256(headStart_49, dataEnd_50) -> value0_51, value1_52 + { + if slt(sub(dataEnd_50, headStart_49), 64) + { + revert(0, 0) + } + { + let offset_53 := 0 + value0_51 := abi_decode_t_address(add(headStart_49, offset_53), dataEnd_50) + } + { + let offset_54 := 32 + value1_52 := abi_decode_t_uint256(add(headStart_49, offset_54), dataEnd_50) + } + } + function abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(headStart_55, dataEnd_56) -> value0_57, value1_58, value2_59, value3, value4 + { + if slt(sub(dataEnd_56, headStart_55), 128) + { + revert(0, 0) + } + { + let offset_60 := 0 + value0_57 := abi_decode_t_address(add(headStart_55, offset_60), dataEnd_56) + } + { + let offset_61 := 32 + value1_58 := abi_decode_t_uint256(add(headStart_55, offset_61), dataEnd_56) + } + { + let offset_62 := calldataload(add(headStart_55, 64)) + if gt(offset_62, 0xffffffffffffffff) + { + revert(0, 0) + } + value2_59, value3 := abi_decode_t_bytes_calldata_ptr(add(headStart_55, offset_62), dataEnd_56) + } + { + let offset_63 := 96 + value4 := abi_decode_t_enum$_Operation_$1949(add(headStart_55, offset_63), dataEnd_56) + } + } + function abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949t_uint256t_uint256t_uint256t_addresst_address_payablet_bytes_calldata_ptr(headStart_64, dataEnd_65) -> value0_66, value1_67, value2_68, value3_69, value4_70, value5, value6, value7, value8, value9, value10, value11 + { + if slt(sub(dataEnd_65, headStart_64), 320) + { + revert(0, 0) + } + { + let offset_71 := 0 + value0_66 := abi_decode_t_address(add(headStart_64, offset_71), dataEnd_65) + } + { + let offset_72 := 32 + value1_67 := abi_decode_t_uint256(add(headStart_64, offset_72), dataEnd_65) + } + { + let offset_73 := calldataload(add(headStart_64, 64)) + if gt(offset_73, 0xffffffffffffffff) + { + revert(0, 0) + } + value2_68, value3_69 := abi_decode_t_bytes_calldata_ptr(add(headStart_64, offset_73), dataEnd_65) + } + { + let offset_74 := 96 + value4_70 := abi_decode_t_enum$_Operation_$1949(add(headStart_64, offset_74), dataEnd_65) + } + { + let offset_75 := 128 + value5 := abi_decode_t_uint256(add(headStart_64, offset_75), dataEnd_65) + } + { + let offset_76 := 160 + value6 := abi_decode_t_uint256(add(headStart_64, offset_76), dataEnd_65) + } + { + let offset_77 := 192 + value7 := abi_decode_t_uint256(add(headStart_64, offset_77), dataEnd_65) + } + { + let offset_78 := 224 + value8 := abi_decode_t_address(add(headStart_64, offset_78), dataEnd_65) + } + { + let offset_79 := 256 + value9 := abi_decode_t_address_payable(add(headStart_64, offset_79), dataEnd_65) + } + { + let offset_80 := calldataload(add(headStart_64, 288)) + if gt(offset_80, 0xffffffffffffffff) + { + revert(0, 0) + } + value10, value11 := abi_decode_t_bytes_calldata_ptr(add(headStart_64, offset_80), dataEnd_65) + } + } + function abi_decode_tuple_t_addresst_uint256t_bytes_memory_ptrt_enum$_Operation_$1949(headStart_81, dataEnd_82) -> value0_83, value1_84, value2_85, value3_86 + { + if slt(sub(dataEnd_82, headStart_81), 128) + { + revert(0, 0) + } + { + let offset_87 := 0 + value0_83 := abi_decode_t_address(add(headStart_81, offset_87), dataEnd_82) + } + { + let offset_88 := 32 + value1_84 := abi_decode_t_uint256(add(headStart_81, offset_88), dataEnd_82) + } + { + let offset_89 := calldataload(add(headStart_81, 64)) + if gt(offset_89, 0xffffffffffffffff) + { + revert(0, 0) + } + value2_85 := abi_decode_t_bytes_memory_ptr(add(headStart_81, offset_89), dataEnd_82) + } + { + let offset_90 := 96 + value3_86 := abi_decode_t_enum$_Operation_$1949(add(headStart_81, offset_90), dataEnd_82) + } + } + function abi_decode_tuple_t_addresst_uint256t_bytes_memory_ptrt_enum$_Operation_$1949t_uint256t_uint256t_uint256t_addresst_addresst_uint256(headStart_91, dataEnd_92) -> value0_93, value1_94, value2_95, value3_96, value4_97, value5_98, value6_99, value7_100, value8_101, value9_102 + { + if slt(sub(dataEnd_92, headStart_91), 320) + { + revert(0, 0) + } + { + let offset_103 := 0 + value0_93 := abi_decode_t_address(add(headStart_91, offset_103), dataEnd_92) + } + { + let offset_104 := 32 + value1_94 := abi_decode_t_uint256(add(headStart_91, offset_104), dataEnd_92) + } + { + let offset_105 := calldataload(add(headStart_91, 64)) + if gt(offset_105, 0xffffffffffffffff) + { + revert(0, 0) + } + value2_95 := abi_decode_t_bytes_memory_ptr(add(headStart_91, offset_105), dataEnd_92) + } + { + let offset_106 := 96 + value3_96 := abi_decode_t_enum$_Operation_$1949(add(headStart_91, offset_106), dataEnd_92) + } + { + let offset_107 := 128 + value4_97 := abi_decode_t_uint256(add(headStart_91, offset_107), dataEnd_92) + } + { + let offset_108 := 160 + value5_98 := abi_decode_t_uint256(add(headStart_91, offset_108), dataEnd_92) + } + { + let offset_109 := 192 + value6_99 := abi_decode_t_uint256(add(headStart_91, offset_109), dataEnd_92) + } + { + let offset_110 := 224 + value7_100 := abi_decode_t_address(add(headStart_91, offset_110), dataEnd_92) + } + { + let offset_111 := 256 + value8_101 := abi_decode_t_address(add(headStart_91, offset_111), dataEnd_92) + } + { + let offset_112 := 288 + value9_102 := abi_decode_t_uint256(add(headStart_91, offset_112), dataEnd_92) + } + } + function abi_decode_tuple_t_array$_t_address_$dyn_calldata_ptrt_uint256t_addresst_bytes_calldata_ptr(headStart_113, dataEnd_114) -> value0_115, value1_116, value2_117, value3_118, value4_119, value5_120 + { + if slt(sub(dataEnd_114, headStart_113), 128) + { + revert(0, 0) + } + { + let offset_121 := calldataload(add(headStart_113, 0)) + if gt(offset_121, 0xffffffffffffffff) + { + revert(0, 0) + } + value0_115, value1_116 := abi_decode_t_array$_t_address_$dyn_calldata_ptr(add(headStart_113, offset_121), dataEnd_114) + } + { + let offset_122 := 32 + value2_117 := abi_decode_t_uint256(add(headStart_113, offset_122), dataEnd_114) + } + { + let offset_123 := 64 + value3_118 := abi_decode_t_address(add(headStart_113, offset_123), dataEnd_114) + } + { + let offset_124 := calldataload(add(headStart_113, 96)) + if gt(offset_124, 0xffffffffffffffff) + { + revert(0, 0) + } + value4_119, value5_120 := abi_decode_t_bytes_calldata_ptr(add(headStart_113, offset_124), dataEnd_114) + } + } + function abi_decode_tuple_t_bool_fromMemory(headStart_125, dataEnd_126) -> value0_127 + { + if slt(sub(dataEnd_126, headStart_125), 32) + { + revert(0, 0) + } + { + let offset_128 := 0 + value0_127 := abi_decode_t_bool_fromMemory(add(headStart_125, offset_128), dataEnd_126) + } + } + function abi_decode_tuple_t_bytes32(headStart_129, dataEnd_130) -> value0_131 + { + if slt(sub(dataEnd_130, headStart_129), 32) + { + revert(0, 0) + } + { + let offset_132 := 0 + value0_131 := abi_decode_t_bytes32(add(headStart_129, offset_132), dataEnd_130) + } + } + function abi_decode_tuple_t_bytes_calldata_ptr(headStart_133, dataEnd_134) -> value0_135, value1_136 + { + if slt(sub(dataEnd_134, headStart_133), 32) + { + revert(0, 0) + } + { + let offset_137 := calldataload(add(headStart_133, 0)) + if gt(offset_137, 0xffffffffffffffff) + { + revert(0, 0) + } + value0_135, value1_136 := abi_decode_t_bytes_calldata_ptr(add(headStart_133, offset_137), dataEnd_134) + } + } + function abi_decode_tuple_t_bytes_calldata_ptrt_bytes_calldata_ptr(headStart_138, dataEnd_139) -> value0_140, value1_141, value2_142, value3_143 + { + if slt(sub(dataEnd_139, headStart_138), 64) + { + revert(0, 0) + } + { + let offset_144 := calldataload(add(headStart_138, 0)) + if gt(offset_144, 0xffffffffffffffff) + { + revert(0, 0) + } + value0_140, value1_141 := abi_decode_t_bytes_calldata_ptr(add(headStart_138, offset_144), dataEnd_139) + } + { + let offset_145 := calldataload(add(headStart_138, 32)) + if gt(offset_145, 0xffffffffffffffff) + { + revert(0, 0) + } + value2_142, value3_143 := abi_decode_t_bytes_calldata_ptr(add(headStart_138, offset_145), dataEnd_139) + } + } + function abi_decode_tuple_t_bytes_memory_ptr(headStart_146, dataEnd_147) -> value0_148 + { + if slt(sub(dataEnd_147, headStart_146), 32) + { + revert(0, 0) + } + { + let offset_149 := calldataload(add(headStart_146, 0)) + if gt(offset_149, 0xffffffffffffffff) + { + revert(0, 0) + } + value0_148 := abi_decode_t_bytes_memory_ptr(add(headStart_146, offset_149), dataEnd_147) + } + } + function abi_decode_tuple_t_contract$_Module_$1038(headStart_150, dataEnd_151) -> value0_152 + { + if slt(sub(dataEnd_151, headStart_150), 32) + { + revert(0, 0) + } + { + let offset_153 := 0 + value0_152 := abi_decode_t_contract$_Module_$1038(add(headStart_150, offset_153), dataEnd_151) + } + } + function abi_decode_tuple_t_contract$_Module_$1038t_contract$_Module_$1038(headStart_154, dataEnd_155) -> value0_156, value1_157 + { + if slt(sub(dataEnd_155, headStart_154), 64) + { + revert(0, 0) + } + { + let offset_158 := 0 + value0_156 := abi_decode_t_contract$_Module_$1038(add(headStart_154, offset_158), dataEnd_155) + } + { + let offset_159 := 32 + value1_157 := abi_decode_t_contract$_Module_$1038(add(headStart_154, offset_159), dataEnd_155) + } + } + function abi_decode_tuple_t_uint256(headStart_160, dataEnd_161) -> value0_162 + { + if slt(sub(dataEnd_161, headStart_160), 32) + { + revert(0, 0) + } + { + let offset_163 := 0 + value0_162 := abi_decode_t_uint256(add(headStart_160, offset_163), dataEnd_161) + } + } + function abi_encode_t_address_to_t_address(value_164, pos) + { + mstore(pos, cleanup_assert_t_address(value_164)) + } + function abi_encode_t_array$_t_address_$dyn_memory_ptr_to_t_array$_t_address_$dyn_memory_ptr(value_165, pos_166) -> end_167 + { + let length_168 := array_length_t_array$_t_address_$dyn_memory_ptr(value_165) + mstore(pos_166, length_168) + pos_166 := add(pos_166, 0x20) + let srcPtr := array_dataslot_t_array$_t_address_$dyn_memory_ptr(value_165) + for { + let i := 0 + } + lt(i, length_168) + { + i := add(i, 1) + } + { + abi_encode_t_address_to_t_address(mload(srcPtr), pos_166) + srcPtr := array_nextElement_t_array$_t_address_$dyn_memory_ptr(srcPtr) + pos_166 := add(pos_166, 0x20) + } + end_167 := pos_166 + } + function abi_encode_t_bool_to_t_bool(value_169, pos_170) + { + mstore(pos_170, cleanup_assert_t_bool(value_169)) + } + function abi_encode_t_bytes32_to_t_bytes32(value_171, pos_172) + { + mstore(pos_172, cleanup_assert_t_bytes32(value_171)) + } + function abi_encode_t_bytes_memory_ptr_to_t_bytes_memory_ptr(value_173, pos_174) -> end_175 + { + let length_176 := array_length_t_bytes_memory_ptr(value_173) + mstore(pos_174, length_176) + copy_memory_to_memory(add(value_173, 0x20), add(pos_174, 0x20), length_176) + end_175 := add(add(pos_174, 0x20), round_up_to_mul_of_32(length_176)) + } + function abi_encode_t_contract$_GnosisSafe_$710_to_t_address_payable(value_177, pos_178) + { + mstore(pos_178, convert_t_contract$_GnosisSafe_$710_to_t_address_payable(value_177)) + } + function abi_encode_t_contract$_Module_$1038_to_t_address(value_179, pos_180) + { + mstore(pos_180, convert_t_contract$_Module_$1038_to_t_address(value_179)) + } + function abi_encode_t_enum$_Operation_$1949_to_t_uint8(value_181, pos_182) + { + mstore(pos_182, convert_t_enum$_Operation_$1949_to_t_uint8(value_181)) + } + function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr(value_183, pos_184) -> end_185 + { + let length_186 := array_length_t_string_memory_ptr(value_183) + mstore(pos_184, length_186) + copy_memory_to_memory(add(value_183, 0x20), add(pos_184, 0x20), length_186) + end_185 := add(add(pos_184, 0x20), round_up_to_mul_of_32(length_186)) + } + function abi_encode_t_string_memory_to_t_string_memory_ptr(value_187, pos_188) -> end_189 + { + let length_190 := array_length_t_string_memory(value_187) + mstore(pos_188, length_190) + copy_memory_to_memory(add(value_187, 0x20), add(pos_188, 0x20), length_190) + end_189 := add(add(pos_188, 0x20), round_up_to_mul_of_32(length_190)) + } + function abi_encode_t_stringliteral_108d84599042957b954e89d43b52f80be89321dfc114a37800028eba58dafc87_to_t_string_memory_ptr(pos_191) -> end_192 + { + mstore(pos_191, 36) + mstore(add(pos_191, 32), 0x496e76616c6964206d617374657220636f707920616464726573732070726f76) + mstore(add(pos_191, 64), 0x6964656400000000000000000000000000000000000000000000000000000000) + end_192 := add(pos_191, 96) + } + function abi_encode_t_stringliteral_1e0428ffa69bff65645154a36d5017c238f946ddaf89430d30eec813f30bdd77_to_t_string_memory_ptr(pos_193) -> end_194 + { + mstore(pos_193, 37) + mstore(add(pos_193, 32), 0x4d6f64756c6573206861766520616c7265616479206265656e20696e69746961) + mstore(add(pos_193, 64), 0x6c697a6564000000000000000000000000000000000000000000000000000000) + end_194 := add(pos_193, 96) + } + function abi_encode_t_stringliteral_21a1cd38818adb750881fbf07c26ce7223dde608fdd9dadd31a0d41afeca2094_to_t_string_memory_ptr(pos_195) -> end_196 + { + mstore(pos_195, 30) + mstore(add(pos_195, 32), 0x496e76616c6964206f776e657220616464726573732070726f76696465640000) + end_196 := add(pos_195, 64) + } + function abi_encode_t_stringliteral_5caa315f9c5cf61be71c182eef2dc9ef7b6ce6b42c320d36694e1d23e09c287e_to_t_string_memory_ptr(pos_197) -> end_198 + { + mstore(pos_197, 40) + mstore(add(pos_197, 32), 0x496e76616c696420707265764d6f64756c652c206d6f64756c65207061697220) + mstore(add(pos_197, 64), 0x70726f7669646564000000000000000000000000000000000000000000000000) + end_198 := add(pos_197, 96) + } + function abi_encode_t_stringliteral_60f21058f4a7689ef29853b3c9c17c9bf69856a949794649bb68878f00552475_to_t_string_memory_ptr(pos_199) -> end_200 + { + mstore(pos_199, 30) + mstore(add(pos_199, 32), 0x4f6e6c79206f776e6572732063616e20617070726f7665206120686173680000) + end_200 := add(pos_199, 64) + } + function abi_encode_t_stringliteral_63d26a9feb8568677e5c255c04e4da88e86a25137d5152a9a089790b7e710e86_to_t_string_memory_ptr(pos_201) -> end_202 + { + mstore(pos_201, 35) + mstore(add(pos_201, 32), 0x5468726573686f6c642063616e6e6f7420657863656564206f776e657220636f) + mstore(add(pos_201, 64), 0x756e740000000000000000000000000000000000000000000000000000000000) + end_202 := add(pos_201, 96) + } + function abi_encode_t_stringliteral_7913a3f9168bf3e458e3f42eb08db5c4b33f44228d345660887090b75e24c6aa_to_t_string_memory_ptr(pos_203) -> end_204 + { + mstore(pos_203, 31) + mstore(add(pos_203, 32), 0x436f756c64206e6f742066696e69736820696e697469616c697a6174696f6e00) + end_204 := add(pos_203, 64) + } + function abi_encode_t_stringliteral_839b4c4db845de24ec74ef067d85431087d6987a4c904418ee4f6ec699c02482_to_t_string_memory_ptr(pos_205) -> end_206 + { + mstore(pos_205, 53) + mstore(add(pos_205, 32), 0x4e6577206f776e657220636f756e74206e6565647320746f206265206c617267) + mstore(add(pos_205, 64), 0x6572207468616e206e6577207468726573686f6c640000000000000000000000) + end_206 := add(pos_205, 96) + } + function abi_encode_t_stringliteral_8560a13547eca5648355c8db1a9f8653b6f657d31d476c36bca25e47b45b08f4_to_t_string_memory_ptr(pos_207) -> end_208 + { + mstore(pos_207, 34) + mstore(add(pos_207, 32), 0x436f756c64206e6f74207061792067617320636f737473207769746820746f6b) + mstore(add(pos_207, 64), 0x656e000000000000000000000000000000000000000000000000000000000000) + end_208 := add(pos_207, 96) + } + function abi_encode_t_stringliteral_85bcea44c930431ef19052d068cc504a81260341ae6c5ee84bb5a38ec55acf05_to_t_string_memory_ptr(pos_209) -> end_210 + { + mstore(pos_209, 27) + mstore(add(pos_209, 32), 0x496e76616c6964207369676e6174757265732070726f76696465640000000000) + end_210 := add(pos_209, 64) + } + function abi_encode_t_stringliteral_8c2199b479423c52a835dfe8b0f2e9eb4c1ec1069ba198ccc38077a4a88a5c00_to_t_string_memory_ptr(pos_211) -> end_212 + { + mstore(pos_211, 31) + mstore(add(pos_211, 32), 0x496e76616c6964206d6f64756c6520616464726573732070726f766964656400) + end_212 := add(pos_211, 64) + } + function abi_encode_t_stringliteral_960698caed81fce71c9b7d572ab2e035b6014a5b812b51df8462ea9817fc4ebc_to_t_string_memory_ptr(pos_213) -> end_214 + { + mstore(pos_213, 38) + mstore(add(pos_213, 32), 0x496e76616c696420707265764f776e65722c206f776e65722070616972207072) + mstore(add(pos_213, 64), 0x6f76696465640000000000000000000000000000000000000000000000000000) + end_214 := add(pos_213, 96) + } + function abi_encode_t_stringliteral_9a45ae898fbe2bd07a0b33b5a6c421f76198e9deb66843b8d827b0b9e4a16f86_to_t_string_memory_ptr(pos_215) -> end_216 + { + mstore(pos_215, 30) + mstore(add(pos_215, 32), 0x4f776e657273206861766520616c7265616479206265656e2073657475700000) + end_216 := add(pos_215, 64) + } + function abi_encode_t_stringliteral_9d461d71e19b25cd406798d062d7e61f961ad52541d3077a543e857810427d47_to_t_string_memory_ptr(pos_217) -> end_218 + { + mstore(pos_217, 27) + mstore(add(pos_217, 32), 0x4164647265737320697320616c726561647920616e206f776e65720000000000) + end_218 := add(pos_217, 64) + } + function abi_encode_t_stringliteral_a2e1f2db9cd32eaa6a2caa3d6caa726a30dc0417d866440bfe13d6a6d030e5e2_to_t_string_memory_ptr(pos_219) -> end_220 + { + mstore(pos_219, 29) + mstore(add(pos_219, 32), 0x446f6d61696e20536570617261746f7220616c72656164792073657421000000) + end_220 := add(pos_219, 64) + } + function abi_encode_t_stringliteral_a803fa289679098e38a7f1f6fe43056918c5ab5af07441cb8db77b949c165ca1_to_t_string_memory_ptr(pos_221) -> end_222 + { + mstore(pos_221, 32) + mstore(add(pos_221, 32), 0x4475706c6963617465206f776e657220616464726573732070726f7669646564) + end_222 := add(pos_221, 64) + } + function abi_encode_t_stringliteral_ae2b4ea52eaf6de3fb2d8a64b7555be2dfd285b837a62821bf24e7dc6f329450_to_t_string_memory_ptr(pos_223) -> end_224 + { + mstore(pos_223, 29) + mstore(add(pos_223, 32), 0x4d6f64756c652068617320616c7265616479206265656e206164646564000000) + end_224 := add(pos_223, 64) + } + function abi_encode_t_stringliteral_b995394ed6031392a784e6dd5e04285cca83077a8dc3873d2fb7fcb090297ab4_to_t_string_memory_ptr(pos_225) -> end_226 + { + mstore(pos_225, 36) + mstore(add(pos_225, 32), 0x5468726573686f6c64206e6565647320746f2062652067726561746572207468) + mstore(add(pos_225, 64), 0x616e203000000000000000000000000000000000000000000000000000000000) + end_226 := add(pos_225, 96) + } + function abi_encode_t_stringliteral_c4780ef0a1d41d59bac8c510cf9ada421bccf2b90f75a8e4ba2e8c09e8d72733_to_t_string_memory_ptr(pos_227) -> end_228 + { + mstore(pos_227, 44) + mstore(add(pos_227, 32), 0x4d6574686f642063616e206f6e6c792062652063616c6c65642066726f6d2074) + mstore(add(pos_227, 64), 0x68697320636f6e74726163740000000000000000000000000000000000000000) + end_228 := add(pos_227, 96) + } + function abi_encode_t_stringliteral_cd36462b17a97c5a3df33333c859d5933a4fb7f5e1a0750f5def8eb51f3272e4_to_t_string_memory_ptr(pos_229) -> end_230 + { + mstore(pos_229, 48) + mstore(add(pos_229, 32), 0x4d6574686f642063616e206f6e6c792062652063616c6c65642066726f6d2061) + mstore(add(pos_229, 64), 0x6e20656e61626c6564206d6f64756c6500000000000000000000000000000000) + end_230 := add(pos_229, 96) + } + function abi_encode_t_stringliteral_e2a11e15f7be1214c1340779ad55027af8aa33aee6cb521776a28a0a44aea377_to_t_string_memory_ptr(pos_231) -> end_232 + { + mstore(pos_231, 34) + mstore(add(pos_231, 32), 0x436f756c64206e6f74207061792067617320636f737473207769746820657468) + mstore(add(pos_231, 64), 0x6572000000000000000000000000000000000000000000000000000000000000) + end_232 := add(pos_231, 96) + } + function abi_encode_t_stringliteral_e7ccb05a0f2c66d12451cdfc6bbab488c38ab704d0f6af9ad18763542e9e5f18_to_t_string_memory_ptr(pos_233) -> end_234 + { + mstore(pos_233, 42) + mstore(add(pos_233, 32), 0x4e6f7420656e6f7567682067617320746f206578656375746520736166652074) + mstore(add(pos_233, 64), 0x72616e73616374696f6e00000000000000000000000000000000000000000000) + end_234 := add(pos_233, 96) + } + function abi_encode_t_uint256_to_t_uint256(value_235, pos_236) + { + mstore(pos_236, cleanup_assert_t_uint256(value_235)) + } + function abi_encode_tuple_t_address__to_t_address_(headStart_237, value0_238) -> tail + { + tail := add(headStart_237, 32) + abi_encode_t_address_to_t_address(value0_238, add(headStart_237, 0)) + } + function abi_encode_tuple_t_address_t_uint256__to_t_address_t_uint256_(headStart_239, value1_240, value0_241) -> tail_242 + { + tail_242 := add(headStart_239, 64) + abi_encode_t_address_to_t_address(value0_241, add(headStart_239, 0)) + abi_encode_t_uint256_to_t_uint256(value1_240, add(headStart_239, 32)) + } + function abi_encode_tuple_t_array$_t_address_$dyn_memory_ptr__to_t_array$_t_address_$dyn_memory_ptr_(headStart_243, value0_244) -> tail_245 + { + tail_245 := add(headStart_243, 32) + mstore(add(headStart_243, 0), sub(tail_245, headStart_243)) + tail_245 := abi_encode_t_array$_t_address_$dyn_memory_ptr_to_t_array$_t_address_$dyn_memory_ptr(value0_244, tail_245) + } + function abi_encode_tuple_t_bool__to_t_bool_(headStart_246, value0_247) -> tail_248 + { + tail_248 := add(headStart_246, 32) + abi_encode_t_bool_to_t_bool(value0_247, add(headStart_246, 0)) + } + function abi_encode_tuple_t_bytes32__to_t_bytes32_(headStart_249, value0_250) -> tail_251 + { + tail_251 := add(headStart_249, 32) + abi_encode_t_bytes32_to_t_bytes32(value0_250, add(headStart_249, 0)) + } + function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart_252, value10_253, value9_254, value8_255, value7_256, value6_257, value5_258, value4_259, value3_260, value2_261, value1_262, value0_263) -> tail_264 + { + tail_264 := add(headStart_252, 352) + abi_encode_t_bytes32_to_t_bytes32(value0_263, add(headStart_252, 0)) + abi_encode_t_address_to_t_address(value1_262, add(headStart_252, 32)) + abi_encode_t_uint256_to_t_uint256(value2_261, add(headStart_252, 64)) + abi_encode_t_bytes32_to_t_bytes32(value3_260, add(headStart_252, 96)) + abi_encode_t_enum$_Operation_$1949_to_t_uint8(value4_259, add(headStart_252, 128)) + abi_encode_t_uint256_to_t_uint256(value5_258, add(headStart_252, 160)) + abi_encode_t_uint256_to_t_uint256(value6_257, add(headStart_252, 192)) + abi_encode_t_uint256_to_t_uint256(value7_256, add(headStart_252, 224)) + abi_encode_t_address_to_t_address(value8_255, add(headStart_252, 256)) + abi_encode_t_address_to_t_address(value9_254, add(headStart_252, 288)) + abi_encode_t_uint256_to_t_uint256(value10_253, add(headStart_252, 320)) + } + function abi_encode_tuple_t_bytes32_t_bytes32__to_t_bytes32_t_bytes32_(headStart_265, value1_266, value0_267) -> tail_268 + { + tail_268 := add(headStart_265, 64) + abi_encode_t_bytes32_to_t_bytes32(value0_267, add(headStart_265, 0)) + abi_encode_t_bytes32_to_t_bytes32(value1_266, add(headStart_265, 32)) + } + function abi_encode_tuple_t_bytes32_t_contract$_GnosisSafe_$710__to_t_bytes32_t_address_payable_(headStart_269, value1_270, value0_271) -> tail_272 + { + tail_272 := add(headStart_269, 64) + abi_encode_t_bytes32_to_t_bytes32(value0_271, add(headStart_269, 0)) + abi_encode_t_contract$_GnosisSafe_$710_to_t_address_payable(value1_270, add(headStart_269, 32)) + } + function abi_encode_tuple_t_bytes_memory_ptr__to_t_bytes_memory_ptr_(headStart_273, value0_274) -> tail_275 + { + tail_275 := add(headStart_273, 32) + mstore(add(headStart_273, 0), sub(tail_275, headStart_273)) + tail_275 := abi_encode_t_bytes_memory_ptr_to_t_bytes_memory_ptr(value0_274, tail_275) + } + function abi_encode_tuple_t_bytes_memory_ptr_t_bytes_memory_ptr__to_t_bytes_memory_ptr_t_bytes_memory_ptr_(headStart_276, value1_277, value0_278) -> tail_279 + { + tail_279 := add(headStart_276, 64) + mstore(add(headStart_276, 0), sub(tail_279, headStart_276)) + tail_279 := abi_encode_t_bytes_memory_ptr_to_t_bytes_memory_ptr(value0_278, tail_279) + mstore(add(headStart_276, 32), sub(tail_279, headStart_276)) + tail_279 := abi_encode_t_bytes_memory_ptr_to_t_bytes_memory_ptr(value1_277, tail_279) + } + function abi_encode_tuple_t_contract$_Module_$1038__to_t_address_(headStart_280, value0_281) -> tail_282 + { + tail_282 := add(headStart_280, 32) + abi_encode_t_contract$_Module_$1038_to_t_address(value0_281, add(headStart_280, 0)) + } + function abi_encode_tuple_t_string_memory__to_t_string_memory_ptr_(headStart_283, value0_284) -> tail_285 + { + tail_285 := add(headStart_283, 32) + mstore(add(headStart_283, 0), sub(tail_285, headStart_283)) + tail_285 := abi_encode_t_string_memory_to_t_string_memory_ptr(value0_284, tail_285) + } + function abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr_(headStart_286, value0_287) -> tail_288 + { + tail_288 := add(headStart_286, 32) + mstore(add(headStart_286, 0), sub(tail_288, headStart_286)) + tail_288 := abi_encode_t_string_memory_ptr_to_t_string_memory_ptr(value0_287, tail_288) + } + function abi_encode_tuple_t_stringliteral_108d84599042957b954e89d43b52f80be89321dfc114a37800028eba58dafc87__to_t_string_memory_ptr_(headStart_289) -> tail_290 + { + tail_290 := add(headStart_289, 32) + mstore(add(headStart_289, 0), sub(tail_290, headStart_289)) + tail_290 := abi_encode_t_stringliteral_108d84599042957b954e89d43b52f80be89321dfc114a37800028eba58dafc87_to_t_string_memory_ptr(tail_290) + } + function abi_encode_tuple_t_stringliteral_1e0428ffa69bff65645154a36d5017c238f946ddaf89430d30eec813f30bdd77__to_t_string_memory_ptr_(headStart_291) -> tail_292 + { + tail_292 := add(headStart_291, 32) + mstore(add(headStart_291, 0), sub(tail_292, headStart_291)) + tail_292 := abi_encode_t_stringliteral_1e0428ffa69bff65645154a36d5017c238f946ddaf89430d30eec813f30bdd77_to_t_string_memory_ptr(tail_292) + } + function abi_encode_tuple_t_stringliteral_21a1cd38818adb750881fbf07c26ce7223dde608fdd9dadd31a0d41afeca2094__to_t_string_memory_ptr_(headStart_293) -> tail_294 + { + tail_294 := add(headStart_293, 32) + mstore(add(headStart_293, 0), sub(tail_294, headStart_293)) + tail_294 := abi_encode_t_stringliteral_21a1cd38818adb750881fbf07c26ce7223dde608fdd9dadd31a0d41afeca2094_to_t_string_memory_ptr(tail_294) + } + function abi_encode_tuple_t_stringliteral_5caa315f9c5cf61be71c182eef2dc9ef7b6ce6b42c320d36694e1d23e09c287e__to_t_string_memory_ptr_(headStart_295) -> tail_296 + { + tail_296 := add(headStart_295, 32) + mstore(add(headStart_295, 0), sub(tail_296, headStart_295)) + tail_296 := abi_encode_t_stringliteral_5caa315f9c5cf61be71c182eef2dc9ef7b6ce6b42c320d36694e1d23e09c287e_to_t_string_memory_ptr(tail_296) + } + function abi_encode_tuple_t_stringliteral_60f21058f4a7689ef29853b3c9c17c9bf69856a949794649bb68878f00552475__to_t_string_memory_ptr_(headStart_297) -> tail_298 + { + tail_298 := add(headStart_297, 32) + mstore(add(headStart_297, 0), sub(tail_298, headStart_297)) + tail_298 := abi_encode_t_stringliteral_60f21058f4a7689ef29853b3c9c17c9bf69856a949794649bb68878f00552475_to_t_string_memory_ptr(tail_298) + } + function abi_encode_tuple_t_stringliteral_63d26a9feb8568677e5c255c04e4da88e86a25137d5152a9a089790b7e710e86__to_t_string_memory_ptr_(headStart_299) -> tail_300 + { + tail_300 := add(headStart_299, 32) + mstore(add(headStart_299, 0), sub(tail_300, headStart_299)) + tail_300 := abi_encode_t_stringliteral_63d26a9feb8568677e5c255c04e4da88e86a25137d5152a9a089790b7e710e86_to_t_string_memory_ptr(tail_300) + } + function abi_encode_tuple_t_stringliteral_7913a3f9168bf3e458e3f42eb08db5c4b33f44228d345660887090b75e24c6aa__to_t_string_memory_ptr_(headStart_301) -> tail_302 + { + tail_302 := add(headStart_301, 32) + mstore(add(headStart_301, 0), sub(tail_302, headStart_301)) + tail_302 := abi_encode_t_stringliteral_7913a3f9168bf3e458e3f42eb08db5c4b33f44228d345660887090b75e24c6aa_to_t_string_memory_ptr(tail_302) + } + function abi_encode_tuple_t_stringliteral_839b4c4db845de24ec74ef067d85431087d6987a4c904418ee4f6ec699c02482__to_t_string_memory_ptr_(headStart_303) -> tail_304 + { + tail_304 := add(headStart_303, 32) + mstore(add(headStart_303, 0), sub(tail_304, headStart_303)) + tail_304 := abi_encode_t_stringliteral_839b4c4db845de24ec74ef067d85431087d6987a4c904418ee4f6ec699c02482_to_t_string_memory_ptr(tail_304) + } + function abi_encode_tuple_t_stringliteral_8560a13547eca5648355c8db1a9f8653b6f657d31d476c36bca25e47b45b08f4__to_t_string_memory_ptr_(headStart_305) -> tail_306 + { + tail_306 := add(headStart_305, 32) + mstore(add(headStart_305, 0), sub(tail_306, headStart_305)) + tail_306 := abi_encode_t_stringliteral_8560a13547eca5648355c8db1a9f8653b6f657d31d476c36bca25e47b45b08f4_to_t_string_memory_ptr(tail_306) + } + function abi_encode_tuple_t_stringliteral_85bcea44c930431ef19052d068cc504a81260341ae6c5ee84bb5a38ec55acf05__to_t_string_memory_ptr_(headStart_307) -> tail_308 + { + tail_308 := add(headStart_307, 32) + mstore(add(headStart_307, 0), sub(tail_308, headStart_307)) + tail_308 := abi_encode_t_stringliteral_85bcea44c930431ef19052d068cc504a81260341ae6c5ee84bb5a38ec55acf05_to_t_string_memory_ptr(tail_308) + } + function abi_encode_tuple_t_stringliteral_8c2199b479423c52a835dfe8b0f2e9eb4c1ec1069ba198ccc38077a4a88a5c00__to_t_string_memory_ptr_(headStart_309) -> tail_310 + { + tail_310 := add(headStart_309, 32) + mstore(add(headStart_309, 0), sub(tail_310, headStart_309)) + tail_310 := abi_encode_t_stringliteral_8c2199b479423c52a835dfe8b0f2e9eb4c1ec1069ba198ccc38077a4a88a5c00_to_t_string_memory_ptr(tail_310) + } + function abi_encode_tuple_t_stringliteral_960698caed81fce71c9b7d572ab2e035b6014a5b812b51df8462ea9817fc4ebc__to_t_string_memory_ptr_(headStart_311) -> tail_312 + { + tail_312 := add(headStart_311, 32) + mstore(add(headStart_311, 0), sub(tail_312, headStart_311)) + tail_312 := abi_encode_t_stringliteral_960698caed81fce71c9b7d572ab2e035b6014a5b812b51df8462ea9817fc4ebc_to_t_string_memory_ptr(tail_312) + } + function abi_encode_tuple_t_stringliteral_9a45ae898fbe2bd07a0b33b5a6c421f76198e9deb66843b8d827b0b9e4a16f86__to_t_string_memory_ptr_(headStart_313) -> tail_314 + { + tail_314 := add(headStart_313, 32) + mstore(add(headStart_313, 0), sub(tail_314, headStart_313)) + tail_314 := abi_encode_t_stringliteral_9a45ae898fbe2bd07a0b33b5a6c421f76198e9deb66843b8d827b0b9e4a16f86_to_t_string_memory_ptr(tail_314) + } + function abi_encode_tuple_t_stringliteral_9d461d71e19b25cd406798d062d7e61f961ad52541d3077a543e857810427d47__to_t_string_memory_ptr_(headStart_315) -> tail_316 + { + tail_316 := add(headStart_315, 32) + mstore(add(headStart_315, 0), sub(tail_316, headStart_315)) + tail_316 := abi_encode_t_stringliteral_9d461d71e19b25cd406798d062d7e61f961ad52541d3077a543e857810427d47_to_t_string_memory_ptr(tail_316) + } + function abi_encode_tuple_t_stringliteral_a2e1f2db9cd32eaa6a2caa3d6caa726a30dc0417d866440bfe13d6a6d030e5e2__to_t_string_memory_ptr_(headStart_317) -> tail_318 + { + tail_318 := add(headStart_317, 32) + mstore(add(headStart_317, 0), sub(tail_318, headStart_317)) + tail_318 := abi_encode_t_stringliteral_a2e1f2db9cd32eaa6a2caa3d6caa726a30dc0417d866440bfe13d6a6d030e5e2_to_t_string_memory_ptr(tail_318) + } + function abi_encode_tuple_t_stringliteral_a803fa289679098e38a7f1f6fe43056918c5ab5af07441cb8db77b949c165ca1__to_t_string_memory_ptr_(headStart_319) -> tail_320 + { + tail_320 := add(headStart_319, 32) + mstore(add(headStart_319, 0), sub(tail_320, headStart_319)) + tail_320 := abi_encode_t_stringliteral_a803fa289679098e38a7f1f6fe43056918c5ab5af07441cb8db77b949c165ca1_to_t_string_memory_ptr(tail_320) + } + function abi_encode_tuple_t_stringliteral_ae2b4ea52eaf6de3fb2d8a64b7555be2dfd285b837a62821bf24e7dc6f329450__to_t_string_memory_ptr_(headStart_321) -> tail_322 + { + tail_322 := add(headStart_321, 32) + mstore(add(headStart_321, 0), sub(tail_322, headStart_321)) + tail_322 := abi_encode_t_stringliteral_ae2b4ea52eaf6de3fb2d8a64b7555be2dfd285b837a62821bf24e7dc6f329450_to_t_string_memory_ptr(tail_322) + } + function abi_encode_tuple_t_stringliteral_b995394ed6031392a784e6dd5e04285cca83077a8dc3873d2fb7fcb090297ab4__to_t_string_memory_ptr_(headStart_323) -> tail_324 + { + tail_324 := add(headStart_323, 32) + mstore(add(headStart_323, 0), sub(tail_324, headStart_323)) + tail_324 := abi_encode_t_stringliteral_b995394ed6031392a784e6dd5e04285cca83077a8dc3873d2fb7fcb090297ab4_to_t_string_memory_ptr(tail_324) + } + function abi_encode_tuple_t_stringliteral_c4780ef0a1d41d59bac8c510cf9ada421bccf2b90f75a8e4ba2e8c09e8d72733__to_t_string_memory_ptr_(headStart_325) -> tail_326 + { + tail_326 := add(headStart_325, 32) + mstore(add(headStart_325, 0), sub(tail_326, headStart_325)) + tail_326 := abi_encode_t_stringliteral_c4780ef0a1d41d59bac8c510cf9ada421bccf2b90f75a8e4ba2e8c09e8d72733_to_t_string_memory_ptr(tail_326) + } + function abi_encode_tuple_t_stringliteral_cd36462b17a97c5a3df33333c859d5933a4fb7f5e1a0750f5def8eb51f3272e4__to_t_string_memory_ptr_(headStart_327) -> tail_328 + { + tail_328 := add(headStart_327, 32) + mstore(add(headStart_327, 0), sub(tail_328, headStart_327)) + tail_328 := abi_encode_t_stringliteral_cd36462b17a97c5a3df33333c859d5933a4fb7f5e1a0750f5def8eb51f3272e4_to_t_string_memory_ptr(tail_328) + } + function abi_encode_tuple_t_stringliteral_e2a11e15f7be1214c1340779ad55027af8aa33aee6cb521776a28a0a44aea377__to_t_string_memory_ptr_(headStart_329) -> tail_330 + { + tail_330 := add(headStart_329, 32) + mstore(add(headStart_329, 0), sub(tail_330, headStart_329)) + tail_330 := abi_encode_t_stringliteral_e2a11e15f7be1214c1340779ad55027af8aa33aee6cb521776a28a0a44aea377_to_t_string_memory_ptr(tail_330) + } + function abi_encode_tuple_t_stringliteral_e7ccb05a0f2c66d12451cdfc6bbab488c38ab704d0f6af9ad18763542e9e5f18__to_t_string_memory_ptr_(headStart_331) -> tail_332 + { + tail_332 := add(headStart_331, 32) + mstore(add(headStart_331, 0), sub(tail_332, headStart_331)) + tail_332 := abi_encode_t_stringliteral_e7ccb05a0f2c66d12451cdfc6bbab488c38ab704d0f6af9ad18763542e9e5f18_to_t_string_memory_ptr(tail_332) + } + function abi_encode_tuple_t_uint256__to_t_uint256_(headStart_333, value0_334) -> tail_335 + { + tail_335 := add(headStart_333, 32) + abi_encode_t_uint256_to_t_uint256(value0_334, add(headStart_333, 0)) + } + function allocateMemory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) + { + revert(0, 0) + } + mstore(64, newFreePtr) + } + function array_allocation_size_t_bytes_memory_ptr(length_336) -> size_337 + { + if gt(length_336, 0xffffffffffffffff) + { + revert(0, 0) + } + size_337 := and(add(length_336, 0x1f), not(0x1f)) + size_337 := add(size_337, 0x20) + } + function array_dataslot_t_array$_t_address_$dyn_memory_ptr(memPtr_338) -> dataPtr + { + dataPtr := add(memPtr_338, 0x20) + } + function array_length_t_array$_t_address_$dyn_memory_ptr(value_339) -> length_340 + { + length_340 := mload(value_339) + } + function array_length_t_bytes_memory_ptr(value_341) -> length_342 + { + length_342 := mload(value_341) + } + function array_length_t_string_memory(value_343) -> length_344 + { + length_344 := mload(value_343) + } + function array_length_t_string_memory_ptr(value_345) -> length_346 + { + length_346 := mload(value_345) + } + function array_nextElement_t_array$_t_address_$dyn_memory_ptr(memPtr_347) -> nextPtr + { + nextPtr := add(memPtr_347, 0x20) + } + function cleanup_assert_t_address(value_348) -> cleaned + { + cleaned := cleanup_assert_t_uint160(value_348) + } + function cleanup_assert_t_bool(value_349) -> cleaned_350 + { + cleaned_350 := iszero(iszero(value_349)) + } + function cleanup_assert_t_bytes32(value_351) -> cleaned_352 + { + cleaned_352 := value_351 + } + function cleanup_assert_t_enum$_Operation_$1949(value_353) -> cleaned_354 + { + if iszero(lt(value_353, 3)) + { + invalid() + } + cleaned_354 := value_353 + } + function cleanup_assert_t_uint160(value_355) -> cleaned_356 + { + cleaned_356 := and(value_355, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + } + function cleanup_assert_t_uint256(value_357) -> cleaned_358 + { + cleaned_358 := value_357 + } + function cleanup_revert_t_address(value_359) -> cleaned_360 + { + cleaned_360 := cleanup_assert_t_uint160(value_359) + } + function cleanup_revert_t_address_payable(value_361) -> cleaned_362 + { + cleaned_362 := cleanup_assert_t_uint160(value_361) + } + function cleanup_revert_t_bool(value_363) -> cleaned_364 + { + cleaned_364 := iszero(iszero(value_363)) + } + function cleanup_revert_t_bytes32(value_365) -> cleaned_366 + { + cleaned_366 := value_365 + } + function cleanup_revert_t_contract$_Module_$1038(value_367) -> cleaned_368 + { + cleaned_368 := cleanup_assert_t_address(value_367) + } + function cleanup_revert_t_enum$_Operation_$1949(value_369) -> cleaned_370 + { + if iszero(lt(value_369, 3)) + { + revert(0, 0) + } + cleaned_370 := value_369 + } + function cleanup_revert_t_uint256(value_371) -> cleaned_372 + { + cleaned_372 := value_371 + } + function convert_t_contract$_GnosisSafe_$710_to_t_address_payable(value_373) -> converted + { + converted := convert_t_contract$_GnosisSafe_$710_to_t_uint160(value_373) + } + function convert_t_contract$_GnosisSafe_$710_to_t_uint160(value_374) -> converted_375 + { + converted_375 := cleanup_assert_t_uint160(value_374) + } + function convert_t_contract$_Module_$1038_to_t_address(value_376) -> converted_377 + { + converted_377 := convert_t_contract$_Module_$1038_to_t_uint160(value_376) + } + function convert_t_contract$_Module_$1038_to_t_uint160(value_378) -> converted_379 + { + converted_379 := cleanup_assert_t_uint160(value_378) + } + function convert_t_enum$_Operation_$1949_to_t_uint8(value_380) -> converted_381 + { + converted_381 := cleanup_assert_t_enum$_Operation_$1949(value_380) + } + function copy_calldata_to_memory(src_382, dst_383, length_384) + { + calldatacopy(dst_383, src_382, length_384) + mstore(add(dst_383, length_384), 0) + } + function copy_memory_to_memory(src_385, dst_386, length_387) + { + let i_388 := 0 + for { + } + lt(i_388, length_387) + { + i_388 := add(i_388, 32) + } + { + mstore(add(dst_386, i_388), mload(add(src_385, i_388))) + } + if gt(i_388, length_387) + { + mstore(add(dst_386, length_387), 0) + } + } + function round_up_to_mul_of_32(value_389) -> result + { + result := and(add(value_389, 31), not(31)) + } +} +// ---- +// fullSuite +// { +// { +// let _1 := 1 +// let _2 := mload(_1) +// let _3 := 0 +// let _1017 := mload(_3) +// let abi_decode_value0_156 := _3 +// if slt(sub(_2, _1017), 64) +// { +// revert(_3, _3) +// } +// { +// abi_decode_value0_156 := and(calldataload(_1017), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) +// } +// sstore(_3, abi_decode_value0_156) +// let _1019 := mload(8) +// let _1021 := mload(7) +// let abi_decode_value0_57 := _3 +// let abi_decode_value1_58 := _3 +// let abi_decode_value2_59 := _3 +// let abi_decode_value3 := _3 +// let abi_decode_value4 := _3 +// if slt(sub(_1019, _1021), 128) +// { +// revert(_3, _3) +// } +// { +// abi_decode_value0_57 := and(calldataload(_1021), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) +// } +// { +// abi_decode_value1_58 := calldataload(add(_1021, 32)) +// } +// { +// let abi_decode_offset_62 := calldataload(add(_1021, 64)) +// if gt(abi_decode_offset_62, 0xffffffffffffffff) +// { +// revert(_3, _3) +// } +// abi_decode_value2_59, abi_decode_value3 := abi_decode_t_bytes_calldata_ptr(add(_1021, abi_decode_offset_62), _1019) +// } +// { +// abi_decode_value4 := cleanup_revert_t_enum$_Operation_$1949(calldataload(add(_1021, 96))) +// } +// sstore(_1, abi_decode_value0_57) +// sstore(_1, abi_decode_value1_58) +// sstore(_1, _3) +// sstore(_1, _3) +// sstore(_1, abi_decode_value4) +// } +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(0, 0) +// } +// let length_15_669 := calldataload(offset_12) +// length_15 := length_15_669 +// if gt(length_15_669, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// arrayPos_14 := add(offset_12, 0x20) +// if gt(add(add(offset_12, length_15_669), 0x20), end_13) +// { +// revert(0, 0) +// } +// } +// function cleanup_revert_t_enum$_Operation_$1949(value_369) -> cleaned_370 +// { +// if iszero(lt(value_369, 3)) +// { +// revert(0, 0) +// } +// cleaned_370 := value_369 +// } +// } -- cgit From 5b8b016fae761866a4652285fb193123a0f5f11e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 8 Jan 2019 15:19:35 +0100 Subject: Use the block flattener. --- libyul/optimiser/Suite.cpp | 8 + test/libyul/yulOptimizerTests/fullSuite/abi2.yul | 74 +++----- .../yulOptimizerTests/fullSuite/abi_example1.yul | 134 ++++++------- test/libyul/yulOptimizerTests/fullSuite/aztec.yul | 208 ++++++++++----------- test/libyul/yulOptimizerTests/fullSuite/medium.yul | 14 +- 5 files changed, 206 insertions(+), 232 deletions(-) diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index e10916ea..48914cf8 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -59,8 +60,10 @@ void OptimiserSuite::run( (VarDeclInitializer{})(ast); (FunctionHoister{})(ast); + (BlockFlattener{})(ast); (FunctionGrouper{})(ast); (ForLoopInitRewriter{})(ast); + (BlockFlattener{})(ast); StructuralSimplifier{_dialect}(ast); NameDispenser dispenser{_dialect, ast}; @@ -75,6 +78,7 @@ void OptimiserSuite::run( CommonSubexpressionEliminator{_dialect}(ast); ExpressionSimplifier::run(_dialect, ast); StructuralSimplifier{_dialect}(ast); + (BlockFlattener{})(ast); SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast); @@ -95,12 +99,16 @@ void OptimiserSuite::run( RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast); CommonSubexpressionEliminator{_dialect}(ast); + + (FunctionGrouper{})(ast); FullInliner{ast, dispenser}.run(); + SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast); ExpressionSimplifier::run(_dialect, ast); StructuralSimplifier{_dialect}(ast); + (BlockFlattener{})(ast); CommonSubexpressionEliminator{_dialect}(ast); SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(_dialect, ast); diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index d61980ba..092acfbe 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1059,54 +1059,36 @@ // ---- // fullSuite // { +// let _1 := 1 +// let _2 := mload(_1) +// let _3 := 0 +// let _1017 := mload(_3) +// if slt(sub(_2, _1017), 64) // { -// let _1 := 1 -// let _2 := mload(_1) -// let _3 := 0 -// let _1017 := mload(_3) -// let abi_decode_value0_156 := _3 -// if slt(sub(_2, _1017), 64) -// { -// revert(_3, _3) -// } -// { -// abi_decode_value0_156 := and(calldataload(_1017), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) -// } -// sstore(_3, abi_decode_value0_156) -// let _1019 := mload(8) -// let _1021 := mload(7) -// let abi_decode_value0_57 := _3 -// let abi_decode_value1_58 := _3 -// let abi_decode_value2_59 := _3 -// let abi_decode_value3 := _3 -// let abi_decode_value4 := _3 -// if slt(sub(_1019, _1021), 128) -// { -// revert(_3, _3) -// } -// { -// abi_decode_value0_57 := and(calldataload(_1021), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) -// } -// { -// abi_decode_value1_58 := calldataload(add(_1021, 32)) -// } -// { -// let abi_decode_offset_62 := calldataload(add(_1021, 64)) -// if gt(abi_decode_offset_62, 0xffffffffffffffff) -// { -// revert(_3, _3) -// } -// abi_decode_value2_59, abi_decode_value3 := abi_decode_t_bytes_calldata_ptr(add(_1021, abi_decode_offset_62), _1019) -// } -// { -// abi_decode_value4 := cleanup_revert_t_enum$_Operation_$1949(calldataload(add(_1021, 96))) -// } -// sstore(_1, abi_decode_value0_57) -// sstore(_1, abi_decode_value1_58) -// sstore(_1, _3) -// sstore(_1, _3) -// sstore(_1, abi_decode_value4) +// revert(_3, _3) +// } +// let _1145 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// sstore(_3, and(calldataload(_1017), _1145)) +// let _1019 := mload(8) +// let _1021 := mload(7) +// let abi_decode_value2_59 := _3 +// let abi_decode_value3 := _3 +// if slt(sub(_1019, _1021), 128) +// { +// revert(_3, _3) +// } +// let abi_decode_offset_62 := calldataload(add(_1021, 64)) +// if gt(abi_decode_offset_62, 0xffffffffffffffff) +// { +// revert(_3, _3) // } +// abi_decode_value2_59, abi_decode_value3 := abi_decode_t_bytes_calldata_ptr(add(_1021, abi_decode_offset_62), _1019) +// let abi_decode_value4_1063 := cleanup_revert_t_enum$_Operation_$1949(calldataload(add(_1021, 96))) +// sstore(_1, and(calldataload(_1021), _1145)) +// sstore(_1, calldataload(add(_1021, 32))) +// sstore(_1, _3) +// sstore(_1, _3) +// sstore(_1, abi_decode_value4_1063) // function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 // { // if iszero(slt(add(offset_12, 0x1f), end_13)) diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index b261b5bc..c8acf465 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -458,45 +458,43 @@ // ---- // fullSuite // { +// let _1 := 0x20 +// let _485 := mload(0) +// let abi_encode_pos := _1 +// let abi_encode_length_68 := mload(_485) +// mstore(_1, abi_encode_length_68) +// abi_encode_pos := 64 +// let abi_encode_srcPtr := add(_485, _1) +// let abi_encode_i_69 := 0 +// for { +// } +// lt(abi_encode_i_69, abi_encode_length_68) +// { +// abi_encode_i_69 := add(abi_encode_i_69, 1) +// } // { -// let _1 := 0x20 -// let _485 := mload(0) -// let abi_encode_pos := _1 -// let abi_encode_length_68 := mload(_485) -// mstore(_1, abi_encode_length_68) -// abi_encode_pos := 64 -// let abi_encode_srcPtr := add(_485, _1) -// let abi_encode_i_69 := 0 +// let _874 := mload(abi_encode_srcPtr) +// let abi_encode_pos_71_978 := abi_encode_pos +// let abi_encode_srcPtr_73_980 := _874 +// let abi_encode_i_74_981 := 0 // for { // } -// lt(abi_encode_i_69, abi_encode_length_68) +// lt(abi_encode_i_74_981, 0x3) // { -// abi_encode_i_69 := add(abi_encode_i_69, 1) +// abi_encode_i_74_981 := add(abi_encode_i_74_981, 1) // } // { -// let _863 := mload(abi_encode_srcPtr) -// let abi_encode_pos_71_971 := abi_encode_pos -// let abi_encode_srcPtr_73_973 := _863 -// let abi_encode_i_74_974 := 0 -// for { -// } -// lt(abi_encode_i_74_974, 0x3) -// { -// abi_encode_i_74_974 := add(abi_encode_i_74_974, 1) -// } -// { -// mstore(abi_encode_pos_71_971, and(mload(abi_encode_srcPtr_73_973), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) -// abi_encode_srcPtr_73_973 := add(abi_encode_srcPtr_73_973, _1) -// abi_encode_pos_71_971 := add(abi_encode_pos_71_971, _1) -// } -// abi_encode_srcPtr := add(abi_encode_srcPtr, _1) -// abi_encode_pos := add(abi_encode_pos, 0x60) +// mstore(abi_encode_pos_71_978, and(mload(abi_encode_srcPtr_73_980), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// abi_encode_srcPtr_73_980 := add(abi_encode_srcPtr_73_980, _1) +// abi_encode_pos_71_978 := add(abi_encode_pos_71_978, _1) // } -// let a, b, c, d := abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(mload(_1), mload(0x40)) -// sstore(a, b) -// sstore(c, d) -// sstore(0, abi_encode_pos) +// abi_encode_srcPtr := add(abi_encode_srcPtr, _1) +// abi_encode_pos := add(abi_encode_pos, 0x60) // } +// let a, b, c, d := abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(mload(_1), mload(0x40)) +// sstore(a, b) +// sstore(c, d) +// sstore(0, abi_encode_pos) // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5 // { // if iszero(slt(add(offset_3, 0x1f), end_4)) @@ -554,65 +552,55 @@ // src_16 := add(src_16, 0x20) // } // } -// function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29 +// function abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(headStart_58, dataEnd_59) -> value0_60, value1_61, value2, value3 // { -// if iszero(slt(add(offset_27, 0x1f), end_28)) -// { -// revert(0, 0) -// } -// let length_30 := calldataload(offset_27) -// let array_29_279 := allocateMemory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length_30)) -// array_29 := array_29_279 -// let dst_31 := array_29_279 -// mstore(array_29_279, length_30) -// let _91 := 0x20 -// dst_31 := add(array_29_279, _91) -// let src_32 := add(offset_27, _91) -// if gt(add(add(offset_27, mul(length_30, _91)), _91), end_28) +// if slt(sub(dataEnd_59, headStart_58), 128) // { -// revert(0, 0) -// } -// let i_33 := 0 -// for { +// revert(value2, value2) // } -// lt(i_33, length_30) +// value0_60 := calldataload(add(headStart_58, value2)) +// value1_61 := calldataload(add(headStart_58, 32)) +// let offset_64 := calldataload(add(headStart_58, 64)) +// let _165 := 0xffffffffffffffff +// if gt(offset_64, _165) // { -// i_33 := add(i_33, 1) +// revert(value2, value2) // } +// let _532 := add(headStart_58, offset_64) +// if iszero(slt(add(_532, 0x1f), dataEnd_59)) // { -// mstore(dst_31, calldataload(src_32)) -// dst_31 := add(dst_31, _91) -// src_32 := add(src_32, _91) +// revert(value2, value2) // } -// } -// function abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(headStart_58, dataEnd_59) -> value0_60, value1_61, value2, value3 -// { -// if slt(sub(dataEnd_59, headStart_58), 128) +// let abi_decode_length_30 := calldataload(_532) +// let abi_decode_array_29_279 := allocateMemory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(abi_decode_length_30)) +// let abi_decode_dst_31 := abi_decode_array_29_279 +// mstore(abi_decode_array_29_279, abi_decode_length_30) +// let abi_decode__91 := 0x20 +// abi_decode_dst_31 := add(abi_decode_array_29_279, abi_decode__91) +// let abi_decode_src_32 := add(_532, abi_decode__91) +// if gt(add(add(_532, mul(abi_decode_length_30, abi_decode__91)), abi_decode__91), dataEnd_59) // { // revert(value2, value2) // } -// { -// value0_60 := calldataload(add(headStart_58, value2)) +// let abi_decode_i_33 := value2 +// for { // } +// lt(abi_decode_i_33, abi_decode_length_30) // { -// value1_61 := calldataload(add(headStart_58, 32)) +// abi_decode_i_33 := add(abi_decode_i_33, 1) // } // { -// let offset_64 := calldataload(add(headStart_58, 64)) -// if gt(offset_64, 0xffffffffffffffff) -// { -// revert(value2, value2) -// } -// value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(headStart_58, offset_64), dataEnd_59) +// mstore(abi_decode_dst_31, calldataload(abi_decode_src_32)) +// abi_decode_dst_31 := add(abi_decode_dst_31, abi_decode__91) +// abi_decode_src_32 := add(abi_decode_src_32, abi_decode__91) // } +// value2 := abi_decode_array_29_279 +// let offset_65 := calldataload(add(headStart_58, 96)) +// if gt(offset_65, _165) // { -// let offset_65 := calldataload(add(headStart_58, 96)) -// if gt(offset_65, 0xffffffffffffffff) -// { -// revert(0, 0) -// } -// value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(headStart_58, offset_65), dataEnd_59) +// revert(0, 0) // } +// value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(headStart_58, offset_65), dataEnd_59) // } // function allocateMemory(size) -> memPtr // { diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index e43a066c..4fcdaaf1 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -231,124 +231,122 @@ // ---- // fullSuite // { +// let validateJo__6 := 0x80 +// mstore(validateJo__6, 7673901602397024137095011250362199966051872585513276903826533215767972925880) +// mstore(0xa0, 8489654445897228341090914135473290831551238522473825886865492707826370766375) +// let validateJo__10 := calldataload(0x04) +// let validateJo_notes := add(0x04, validateJo__10) +// let validateJo_m := calldataload(0x24) +// let validateJo_n := calldataload(validateJo_notes) +// let validateJo_gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +// let validateJo_challenge := mod(calldataload(0x44), validateJo_gen_order) +// if gt(validateJo_m, validateJo_n) // { -// let validateJo__6 := 0x80 -// mstore(validateJo__6, 7673901602397024137095011250362199966051872585513276903826533215767972925880) -// mstore(0xa0, 8489654445897228341090914135473290831551238522473825886865492707826370766375) -// let validateJo__10 := calldataload(0x04) -// let validateJo_notes := add(0x04, validateJo__10) -// let validateJo_m := calldataload(0x24) -// let validateJo_n := calldataload(validateJo_notes) -// let validateJo_gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 -// let validateJo_challenge := mod(calldataload(0x44), validateJo_gen_order) -// if gt(validateJo_m, validateJo_n) -// { -// mstore(0x00, 404) -// revert(0x00, 0x20) +// mstore(0x00, 404) +// revert(0x00, 0x20) +// } +// let validateJo_kn_287 := calldataload(add(calldatasize(), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40)) +// let validateJo_kn := validateJo_kn_287 +// let validateJo__24 := 0x2a0 +// mstore(validateJo__24, caller()) +// mstore(0x2c0, validateJo_kn_287) +// mstore(0x2e0, validateJo_m) +// validateJo_kn := mulmod(sub(validateJo_gen_order, validateJo_kn_287), validateJo_challenge, validateJo_gen_order) +// hashCommitments(validateJo_notes, validateJo_n) +// let validateJo_b := add(0x300, mul(validateJo_n, validateJo__6)) +// let validateJo_i_290 := 0 +// let validateJo_i := validateJo_i_290 +// for { +// } +// lt(validateJo_i, validateJo_n) +// { +// validateJo_i := add(validateJo_i, 0x01) +// } +// { +// let validateJo__34 := 0x20 +// let validateJo__376 := add(validateJo__10, mul(validateJo_i, 0xc0)) +// let validateJo_noteIndex := add(validateJo__376, 36) +// let validateJo_k := validateJo_i_290 +// let validateJo_a_292 := calldataload(add(validateJo__376, 68)) +// let validateJo_a := validateJo_a_292 +// let validateJo_c := validateJo_challenge +// let validateJo__39 := add(validateJo_i, 0x01) +// switch eq(validateJo__39, validateJo_n) +// case 1 { +// validateJo_k := validateJo_kn +// if eq(validateJo_m, validateJo_n) +// { +// validateJo_k := sub(validateJo_gen_order, validateJo_kn) +// } // } -// let validateJo_kn_287 := calldataload(add(calldatasize(), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40)) -// let validateJo_kn := validateJo_kn_287 -// let validateJo__24 := 0x2a0 -// mstore(validateJo__24, caller()) -// mstore(0x2c0, validateJo_kn_287) -// mstore(0x2e0, validateJo_m) -// validateJo_kn := mulmod(sub(validateJo_gen_order, validateJo_kn_287), validateJo_challenge, validateJo_gen_order) -// hashCommitments(validateJo_notes, validateJo_n) -// let validateJo_b := add(0x300, mul(validateJo_n, validateJo__6)) -// let validateJo_i_290 := 0 -// let validateJo_i := validateJo_i_290 -// for { +// case 0 { +// validateJo_k := calldataload(validateJo_noteIndex) // } -// lt(validateJo_i, validateJo_n) -// { -// validateJo_i := add(validateJo_i, 0x01) +// validateCommitment(validateJo_noteIndex, validateJo_k, validateJo_a_292) +// switch gt(validateJo__39, validateJo_m) +// case 1 { +// validateJo_kn := addmod(validateJo_kn, sub(validateJo_gen_order, validateJo_k), validateJo_gen_order) +// let validateJo_x := mod(mload(0x00), validateJo_gen_order) +// validateJo_k := mulmod(validateJo_k, validateJo_x, validateJo_gen_order) +// validateJo_a := mulmod(validateJo_a_292, validateJo_x, validateJo_gen_order) +// validateJo_c := mulmod(validateJo_challenge, validateJo_x, validateJo_gen_order) +// mstore(0x00, keccak256(0x00, validateJo__34)) +// } +// case 0 { +// validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) // } +// let validateJo__52 := 0x40 +// calldatacopy(0xe0, add(validateJo__376, 164), validateJo__52) +// calldatacopy(validateJo__34, add(validateJo__376, 100), validateJo__52) +// let validateJo__61 := 0x120 +// mstore(validateJo__61, sub(validateJo_gen_order, validateJo_c)) +// let validateJo__62 := 0x60 +// mstore(validateJo__62, validateJo_k) +// mstore(0xc0, validateJo_a) +// let validateJo__65 := 0x1a0 +// let validateJo_result_302 := call(gas(), 7, validateJo_i_290, 0xe0, validateJo__62, validateJo__65, validateJo__52) +// let validateJo_result := validateJo_result_302 +// let validateJo_result_303 := and(validateJo_result_302, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__61, validateJo__52)) +// let validateJo__80 := 0x160 +// let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_290, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) +// let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_290, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) +// let validateJo_result_306 := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) +// validateJo_result := validateJo_result_306 +// if eq(validateJo_i, validateJo_m) // { -// let validateJo__34 := 0x20 -// let validateJo__376 := add(validateJo__10, mul(validateJo_i, 0xc0)) -// let validateJo_noteIndex := add(validateJo__376, 36) -// let validateJo_k := validateJo_i_290 -// let validateJo_a_292 := calldataload(add(validateJo__376, 68)) -// let validateJo_a := validateJo_a_292 -// let validateJo_c := validateJo_challenge -// let validateJo__39 := add(validateJo_i, 0x01) -// switch eq(validateJo__39, validateJo_n) -// case 1 { -// validateJo_k := validateJo_kn -// if eq(validateJo_m, validateJo_n) -// { -// validateJo_k := sub(validateJo_gen_order, validateJo_kn) -// } -// } -// case 0 { -// validateJo_k := calldataload(validateJo_noteIndex) -// } -// validateCommitment(validateJo_noteIndex, validateJo_k, validateJo_a_292) -// switch gt(validateJo__39, validateJo_m) -// case 1 { -// validateJo_kn := addmod(validateJo_kn, sub(validateJo_gen_order, validateJo_k), validateJo_gen_order) -// let validateJo_x := mod(mload(0x00), validateJo_gen_order) -// validateJo_k := mulmod(validateJo_k, validateJo_x, validateJo_gen_order) -// validateJo_a := mulmod(validateJo_a_292, validateJo_x, validateJo_gen_order) -// validateJo_c := mulmod(validateJo_challenge, validateJo_x, validateJo_gen_order) -// mstore(0x00, keccak256(0x00, validateJo__34)) -// } -// case 0 { -// validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) -// } -// let validateJo__52 := 0x40 -// calldatacopy(0xe0, add(validateJo__376, 164), validateJo__52) -// calldatacopy(validateJo__34, add(validateJo__376, 100), validateJo__52) -// let validateJo__61 := 0x120 -// mstore(validateJo__61, sub(validateJo_gen_order, validateJo_c)) -// let validateJo__62 := 0x60 -// mstore(validateJo__62, validateJo_k) -// mstore(0xc0, validateJo_a) -// let validateJo__65 := 0x1a0 -// let validateJo_result_302 := call(gas(), 7, validateJo_i_290, 0xe0, validateJo__62, validateJo__65, validateJo__52) -// let validateJo_result := validateJo_result_302 -// let validateJo_result_303 := and(validateJo_result_302, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__61, validateJo__52)) -// let validateJo__80 := 0x160 -// let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_290, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) -// let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_290, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) -// let validateJo_result_306 := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) -// validateJo_result := validateJo_result_306 -// if eq(validateJo_i, validateJo_m) -// { -// mstore(0x260, mload(validateJo__34)) -// mstore(0x280, mload(validateJo__52)) -// mstore(0x1e0, mload(0xe0)) -// mstore(0x200, sub(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47, mload(0x100))) -// } -// if gt(validateJo_i, validateJo_m) -// { -// mstore(validateJo__62, validateJo_c) -// let validateJo__120 := 0x220 -// let validateJo_result_307 := and(validateJo_result_306, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) -// let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_290, validateJo__120, validateJo__6, 0x260, validateJo__52)) -// validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_290, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) -// } -// if iszero(validateJo_result) -// { -// mstore(0x00, 400) -// revert(0x00, validateJo__34) -// } -// validateJo_b := add(validateJo_b, validateJo__52) +// mstore(0x260, mload(validateJo__34)) +// mstore(0x280, mload(validateJo__52)) +// mstore(0x1e0, mload(0xe0)) +// mstore(0x200, sub(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47, mload(0x100))) // } -// if lt(validateJo_m, validateJo_n) +// if gt(validateJo_i, validateJo_m) // { -// validatePairing(0x64) +// mstore(validateJo__62, validateJo_c) +// let validateJo__120 := 0x220 +// let validateJo_result_307 := and(validateJo_result_306, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) +// let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_290, validateJo__120, validateJo__6, 0x260, validateJo__52)) +// validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_290, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) // } -// if iszero(eq(mod(keccak256(validateJo__24, add(validateJo_b, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60)), validateJo_gen_order), validateJo_challenge)) +// if iszero(validateJo_result) // { -// mstore(0x00, 404) -// revert(0x00, 0x20) +// mstore(0x00, 400) +// revert(0x00, validateJo__34) // } -// mstore(0x00, 0x01) -// return(0x00, 0x20) +// validateJo_b := add(validateJo_b, validateJo__52) +// } +// if lt(validateJo_m, validateJo_n) +// { +// validatePairing(0x64) +// } +// if iszero(eq(mod(keccak256(validateJo__24, add(validateJo_b, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60)), validateJo_gen_order), validateJo_challenge)) +// { // mstore(0x00, 404) // revert(0x00, 0x20) // } +// mstore(0x00, 0x01) +// return(0x00, 0x20) +// mstore(0x00, 404) +// revert(0x00, 0x20) // function validatePairing(t2) // { // let t2_x_1 := calldataload(t2) diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul index 5578452a..b10c6c69 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -19,12 +19,10 @@ // ---- // fullSuite // { -// { -// let allocate__19 := 0x40 -// mstore(allocate__19, add(mload(allocate__19), 0x20)) -// let allocate_p_24_41 := mload(allocate__19) -// mstore(allocate__19, add(allocate_p_24_41, allocate__19)) -// mstore(add(allocate_p_24_41, 96), 2) -// mstore(allocate__19, 0x20) -// } +// let allocate__19 := 0x40 +// mstore(allocate__19, add(mload(allocate__19), 0x20)) +// let allocate_p_24_41 := mload(allocate__19) +// mstore(allocate__19, add(allocate_p_24_41, allocate__19)) +// mstore(add(allocate_p_24_41, 96), 2) +// mstore(allocate__19, 0x20) // } -- cgit From 9244b8ea5ab6e1185a67c8120bf8b78e072bf66d Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 8 Jan 2019 18:07:14 +0100 Subject: More code for optimizer tuning. --- test/libyul/yulOptimizerTests/fullSuite/abi2.yul | 133 ++++++++++++++++------- 1 file changed, 91 insertions(+), 42 deletions(-) diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index 092acfbe..fc9b44a3 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -5,11 +5,24 @@ let a, b := abi_decode_tuple_t_contract$_Module_$1038t_contract$_Module_$1038(mload(0), mload(1)) sstore(0, a) let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) - sstore(1, x0) - sstore(1, x1) - sstore(1, x2) - sstore(1, x3) + sstore(x1, x0) + sstore(x3, x2) sstore(1, x4) + let r := abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_( + mload(30), + mload(31), + mload(32), + mload(33), + mload(34), + mload(35), + mload(36), + mload(37), + mload(38), + mload(39), + mload(40), + mload(41) + ) + function abi_decode_t_address(offset, end) -> value { value := cleanup_revert_t_address(calldataload(offset)) @@ -1059,53 +1072,89 @@ // ---- // fullSuite // { -// let _1 := 1 -// let _2 := mload(_1) -// let _3 := 0 -// let _1017 := mload(_3) -// if slt(sub(_2, _1017), 64) +// let a, b := abi_decode_tuple_t_contract$_Module_$1038t_contract$_Module_$1038(mload(0), mload(1)) +// sstore(0, a) +// let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) +// sstore(x1, x0) +// sstore(x3, x2) +// sstore(1, x4) +// pop(abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(mload(30), mload(31), mload(32), mload(33), mload(34), mload(35), mload(36), mload(37), mload(38), mload(39), mload(40), mload(41))) +// function abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(headStart_55, dataEnd_56) -> value0_57, value1_58, value2_59, value3, value4 // { -// revert(_3, _3) +// if slt(sub(dataEnd_56, headStart_55), 128) +// { +// revert(value4, value4) +// } +// value0_57 := and(calldataload(add(headStart_55, value4)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) +// value1_58 := calldataload(add(headStart_55, 32)) +// let offset_62 := calldataload(add(headStart_55, 64)) +// let _1090 := 0xffffffffffffffff +// if gt(offset_62, _1090) +// { +// revert(value4, value4) +// } +// let _1092 := add(headStart_55, offset_62) +// if iszero(slt(add(_1092, 0x1f), dataEnd_56)) +// { +// revert(value4, value4) +// } +// let abi_decode_length_15_689 := calldataload(_1092) +// if gt(abi_decode_length_15_689, _1090) +// { +// revert(value4, value4) +// } +// if gt(add(add(_1092, abi_decode_length_15_689), 0x20), dataEnd_56) +// { +// revert(value4, value4) +// } +// value2_59 := add(_1092, 0x20) +// value3 := abi_decode_length_15_689 +// value4 := cleanup_revert_t_enum$_Operation_$1949(calldataload(add(headStart_55, 96))) // } -// let _1145 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// sstore(_3, and(calldataload(_1017), _1145)) -// let _1019 := mload(8) -// let _1021 := mload(7) -// let abi_decode_value2_59 := _3 -// let abi_decode_value3 := _3 -// if slt(sub(_1019, _1021), 128) +// function abi_decode_tuple_t_contract$_Module_$1038t_contract$_Module_$1038(headStart_154, dataEnd_155) -> value0_156, value1_157 // { -// revert(_3, _3) +// if slt(sub(dataEnd_155, headStart_154), 64) +// { +// revert(0, 0) +// } +// let cleanup_as__1127 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// value0_156 := and(calldataload(add(headStart_154, 0)), cleanup_as__1127) +// value1_157 := and(calldataload(add(headStart_154, 32)), cleanup_as__1127) // } -// let abi_decode_offset_62 := calldataload(add(_1021, 64)) -// if gt(abi_decode_offset_62, 0xffffffffffffffff) +// function abi_encode_t_address_to_t_address(value_164, pos) // { -// revert(_3, _3) +// mstore(pos, and(value_164, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) // } -// abi_decode_value2_59, abi_decode_value3 := abi_decode_t_bytes_calldata_ptr(add(_1021, abi_decode_offset_62), _1019) -// let abi_decode_value4_1063 := cleanup_revert_t_enum$_Operation_$1949(calldataload(add(_1021, 96))) -// sstore(_1, and(calldataload(_1021), _1145)) -// sstore(_1, calldataload(add(_1021, 32))) -// sstore(_1, _3) -// sstore(_1, _3) -// sstore(_1, abi_decode_value4_1063) -// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// function abi_encode_t_bytes32_to_t_bytes32(value_171, pos_172) // { -// if iszero(slt(add(offset_12, 0x1f), end_13)) -// { -// revert(0, 0) -// } -// let length_15_669 := calldataload(offset_12) -// length_15 := length_15_669 -// if gt(length_15_669, 0xffffffffffffffff) -// { -// revert(0, 0) -// } -// arrayPos_14 := add(offset_12, 0x20) -// if gt(add(add(offset_12, length_15_669), 0x20), end_13) +// mstore(pos_172, value_171) +// } +// function abi_encode_t_enum$_Operation_$1949_to_t_uint8(value_181, pos_182) +// { +// if iszero(lt(value_181, 3)) // { -// revert(0, 0) +// invalid() // } +// mstore(pos_182, value_181) +// } +// function abi_encode_t_uint256_to_t_uint256(value_235, pos_236) +// { +// mstore(pos_236, value_235) +// } +// function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart_252, value10_253, value9_254, value8_255, value7_256, value6_257, value5_258, value4_259, value3_260, value2_261, value1_262, value0_263) -> tail_264 +// { +// tail_264 := add(headStart_252, 352) +// abi_encode_t_bytes32_to_t_bytes32(value0_263, headStart_252) +// abi_encode_t_address_to_t_address(value1_262, add(headStart_252, 32)) +// abi_encode_t_uint256_to_t_uint256(value2_261, add(headStart_252, 64)) +// abi_encode_t_bytes32_to_t_bytes32(value3_260, add(headStart_252, 96)) +// abi_encode_t_enum$_Operation_$1949_to_t_uint8(value4_259, add(headStart_252, 128)) +// abi_encode_t_uint256_to_t_uint256(value5_258, add(headStart_252, 160)) +// abi_encode_t_uint256_to_t_uint256(value6_257, add(headStart_252, 192)) +// abi_encode_t_uint256_to_t_uint256(value7_256, add(headStart_252, 224)) +// abi_encode_t_address_to_t_address(value8_255, add(headStart_252, 256)) +// abi_encode_t_address_to_t_address(value9_254, add(headStart_252, 288)) +// abi_encode_t_uint256_to_t_uint256(value10_253, add(headStart_252, 320)) // } // function cleanup_revert_t_enum$_Operation_$1949(value_369) -> cleaned_370 // { -- cgit From 874174bd4742303d16dd76a85eeda264caaa6e22 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 9 Jan 2019 12:06:37 +0100 Subject: Split Value Types docs --- docs/conf.py | 2 +- docs/types.rst | 709 +-------------------------------------------- docs/types/value-types.rst | 708 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 710 insertions(+), 709 deletions(-) create mode 100644 docs/types/value-types.rst diff --git a/docs/conf.py b/docs/conf.py index 342aefa9..d08a5191 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -81,7 +81,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts'] +exclude_patterns = ['_build', 'contracts', 'types'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/types.rst b/docs/types.rst index 4eb1f926..d3d70ebd 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -18,714 +18,7 @@ declared variables always have a :ref:`default value` dependent on its type. To handle any unexpected values, you should use the :ref:`revert function` to revert the whole transaction, or return a tuple with a second `bool` value denoting success. -.. index:: ! value type, ! type;value -.. _value-types: - -Value Types -=========== - -The following types are also called value types because variables of these -types will always be passed by value, i.e. they are always copied when they -are used as function arguments or in assignments. - -.. index:: ! bool, ! true, ! false - -Booleans --------- - -``bool``: The possible values are constants ``true`` and ``false``. - -Operators: - -* ``!`` (logical negation) -* ``&&`` (logical conjunction, "and") -* ``||`` (logical disjunction, "or") -* ``==`` (equality) -* ``!=`` (inequality) - -The operators ``||`` and ``&&`` apply the common short-circuiting rules. This means that in the expression ``f(x) || g(y)``, if ``f(x)`` evaluates to ``true``, ``g(y)`` will not be evaluated even if it may have side-effects. - -.. index:: ! uint, ! int, ! integer - -Integers --------- - -``int`` / ``uint``: Signed and unsigned integers of various sizes. Keywords ``uint8`` to ``uint256`` in steps of ``8`` (unsigned of 8 up to 256 bits) and ``int8`` to ``int256``. ``uint`` and ``int`` are aliases for ``uint256`` and ``int256``, respectively. - -Operators: - -* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) -* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) -* Shift operators: ``<<`` (left shift), ``>>`` (right shift) -* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) - - -Comparisons -^^^^^^^^^^^ - -The value of a comparison is the one obtained by comparing the integer value. - -Bit operations -^^^^^^^^^^^^^^ - -Bit operations are performed on the two's complement representation of the number. -This means that, for example ``~int256(0) == int256(-1)``. - -Shifts -^^^^^^ - -The result of a shift operation has the type of the left operand. The -expression ``x << y`` is equivalent to ``x * 2**y``, and, for positive integers, -``x >> y`` is equivalent to ``x / 2**y``. For negative ``x``, ``x >> y`` -is equivalent to dividing by a power of ``2`` while rounding down (towards negative infinity). -Shifting by a negative amount throws a runtime exception. - -.. warning:: - Before version ``0.5.0`` a right shift ``x >> y`` for negative ``x`` was equivalent to ``x / 2**y``, - i.e. right shifts used rounding towards zero instead of rounding towards negative infinity. - -Addition, Subtraction and Multiplication -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Addition, subtraction and multiplication have the usual semantics. -They wrap in two's complement representation, meaning that -for example ``uint256(0) - uint256(1) == 2**256 - 1``. You have to take these overflows -into account when designing safe smart contracts. - -The expression ``-x`` is equivalent to ``(T(0) - x)`` where -``T`` is the type of ``x``. This means that ``-x`` will not be negative -if the type of ``x`` is an unsigned integer type. Also, ``-x`` can be -positive if ``x`` is negative. There is another caveat also resulting -from two's complement representation:: - - int x = -2**255; - assert(-x == x); - -This means that even if a number is negative, you cannot assume that -its negation will be positive. - - -Division -^^^^^^^^ - -Since the type of the result of an operation is always the type of one of -the operands, division on integers always results in an integer. -In Solidity, division rounds towards zero. This mean that ``int256(-5) / int256(2) == int256(-2)``. - -Note that in contrast, division on :ref:`literals` results in fractional values -of arbitrary precision. - -.. note:: - Division by zero causes a failing assert. - -Modulo -^^^^^^ - -The modulo operation ``a % n`` yields the remainder ``r`` after the division of the operand ``a`` -by the operand ``n``, where ``q = int(a / n)`` and ``r = a - (n * q)``. This means that modulo -results in the same sign as its left operand (or zero) and ``a % n == -(abs(a) % n)`` holds for negative ``a``: - - * ``int256(5) % int256(2) == int256(1)`` - * ``int256(5) % int256(-2) == int256(1)`` - * ``int256(-5) % int256(2) == int256(-1)`` - * ``int256(-5) % int256(-2) == int256(-1)`` - -.. note:: - Modulo with zero causes a failing assert. - -Exponentiation -^^^^^^^^^^^^^^ - -Exponentiation is only available for unsigned types. Please take care that the types -you are using are large enough to hold the result and prepare for potential wrapping behaviour. - -.. note:: - Note that ``0**0`` is defined by the EVM as ``1``. - -.. index:: ! ufixed, ! fixed, ! fixed point number - -Fixed Point Numbers -------------------- - -.. warning:: - Fixed point numbers are not fully supported by Solidity yet. They can be declared, but - cannot be assigned to or from. - -``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represents the number of bits taken by -the type and ``N`` represents how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive. -``ufixed`` and ``fixed`` are aliases for ``ufixed128x18`` and ``fixed128x18``, respectively. - -Operators: - -* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) -* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo) - -.. note:: - The main difference between floating point (``float`` and ``double`` in many languages, more precisely IEEE 754 numbers) and fixed point numbers is - that the number of bits used for the integer and the fractional part (the part after the decimal dot) is flexible in the former, while it is strictly - defined in the latter. Generally, in floating point almost the entire space is used to represent the number, while only a small number of bits define - where the decimal point is. - -.. index:: address, balance, send, call, callcode, delegatecall, staticcall, transfer - -.. _address: - -Address -------- - -The address type comes in two flavours, which are largely identical: - - - ``address``: Holds a 20 byte value (size of an Ethereum address). - - ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. - -The idea behind this distinction is that ``address payable`` is an address you can send Ether to, -while a plain ``address`` cannot be sent Ether. - -Type conversions: - -Implicit conversions from ``address payable`` to ``address`` are allowed, whereas conversions from ``address`` to ``address payable`` are -not possible (the only way to perform such a conversion is by using an intermediate conversion to ``uint160``). - -:ref:`Address literals` can be implicitly converted to ``address payable``. - -Explicit conversions to and from ``address`` are allowed for integers, integer literals, ``bytes20`` and contract types with the following -caveat: -Conversions of the form ``address payable(x)`` are not allowed. Instead the result of a conversion of the form ``address(x)`` -has the type ``address payable``, if ``x`` is of integer or fixed bytes type, a literal or a contract with a payable fallback function. -If ``x`` is a contract without payable fallback function, then ``address(x)`` will be of type ``address``. -In external function signatures ``address`` is used for both the ``address`` and the ``address payable`` type. - -.. note:: - It might very well be that you do not need to care about the distinction between ``address`` - and ``address payable`` and just use ``address`` everywhere. For example, - if you are using the :ref:`withdrawal pattern`, you can (and should) store the - address itself as ``address``, because you invoke the ``transfer`` function on - ``msg.sender``, which is an ``address payable``. - -Operators: - -* ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>`` - -.. warning:: - If you convert a type that uses a larger byte size to an ``address``, for example ``bytes32``, then the ``address`` is truncated. - To reduce conversion ambiguity version 0.4.24 and higher of the compiler force you make the truncation explicit in the conversion. - Take for example the address ``0x111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC``. - - You can use ``address(uint160(bytes20(b)))``, which results in ``0x111122223333444455556666777788889999aAaa``, - or you can use ``address(uint160(uint256(b)))``, which results in ``0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc``. - -.. note:: - The distinction between ``address`` and ``address payable`` was introduced with version 0.5.0. - Also starting from that version, contracts do not derive from the address type, but can still be explicitly converted to - ``address`` or to ``address payable``, if they have a payable fallback function. - -.. _members-of-addresses: - -Members of Addresses -^^^^^^^^^^^^^^^^^^^^ - -For a quick reference of all members of address, see :ref:`address_related`. - -* ``balance`` and ``transfer`` - -It is possible to query the balance of an address using the property ``balance`` -and to send Ether (in units of wei) to a payable address using the ``transfer`` function: - -:: - - address payable x = address(0x123); - address myAddress = address(this); - if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); - -The ``transfer`` function fails if the balance of the current contract is not large enough -or if the Ether transfer is rejected by the receiving account. The ``transfer`` function -reverts on failure. - -.. note:: - If ``x`` is a contract address, its code (more specifically: its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception. - -* ``send`` - -Send is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``. - -.. warning:: - There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 - (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order - to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: - use a pattern where the recipient withdraws the money. - -* ``call``, ``delegatecall`` and ``staticcall`` - -In order to interface with contracts that do not adhere to the ABI, -or to get more direct control over the encoding, -the functions ``call``, ``delegatecall`` and ``staticcall`` are provided. -They all take a single ``bytes memory`` parameter and -return the success condition (as a ``bool``) and the returned data -(``bytes memory``). -The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector`` -and ``abi.encodeWithSignature`` can be used to encode structured data. - -Example:: - - bytes memory payload = abi.encodeWithSignature("register(string)", "MyName"); - (bool success, bytes memory returnData) = address(nameReg).call(payload); - require(success); - -.. warning:: - All these functions are low-level functions and should be used with care. - Specifically, any unknown contract might be malicious and if you call it, you - hand over control to that contract which could in turn call back into - your contract, so be prepared for changes to your state variables - when the call returns. The regular way to interact with other contracts - is to call a function on a contract object (``x.f()``). - -.. note:: - Previous versions of Solidity allowed these functions to receive - arbitrary arguments and would also handle a first argument of type - ``bytes4`` differently. These edge cases were removed in version 0.5.0. - -It is possible to adjust the supplied gas with the ``.gas()`` modifier:: - - address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName")); - -Similarly, the supplied Ether value can be controlled too:: - - address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); - -Lastly, these modifiers can be combined. Their order does not matter:: - - address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); - -In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. - -.. note:: - Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. This function was removed in version 0.5.0. - -Since byzantium ``staticcall`` can be used as well. This is basically the same as ``call``, but will revert if the called function modifies the state in any way. - -All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. - -The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``. - -.. note:: - All contracts can be converted to ``address`` type, so it is possible to query the balance of the - current contract using ``address(this).balance``. - -.. index:: ! contract type, ! type; contract - -.. _contract_types: - -Contract Types --------------- - -Every :ref:`contract` defines its own type. -You can implicitly convert contracts to contracts they inherit from. -Contracts can be explicitly converted to and from all other contract types -and the ``address`` type. - -Explicit conversion to and from the ``address payable`` type -is only possible if the contract type has a payable fallback function. -The conversion is still performed using ``address(x)`` and not -using ``address payable(x)``. You can find more information in the section about -the :ref:`address type
`. - -.. note:: - Before version 0.5.0, contracts directly derived from the address type - and there was no distinction between ``address`` and ``address payable``. - -If you declare a local variable of contract type (`MyContract c`), you can call -functions on that contract. Take care to assign it from somewhere that is the -same contract type. - -You can also instantiate contracts (which means they are newly created). You -can find more details in the :ref:`'Contracts via new'` -section. - -The data representation of a contract is identical to that of the ``address`` -type and this type is also used in the :ref:`ABI`. - -Contracts do not support any operators. - -The members of contract types are the external functions of the contract -including public state variables. - -.. index:: byte array, bytes32 - -Fixed-size byte arrays ----------------------- - -The value types ``bytes1``, ``bytes2``, ``bytes3``, ..., ``bytes32`` -hold a sequence of bytes from one to up to 32. -``byte`` is an alias for ``bytes1``. - -Operators: - -* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) -* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) -* Shift operators: ``<<`` (left shift), ``>>`` (right shift) -* Index access: If ``x`` is of type ``bytesI``, then ``x[k]`` for ``0 <= k < I`` returns the ``k`` th byte (read-only). - -The shifting operator works with any integer type as right operand (but -returns the type of the left operand), which denotes the number of bits to shift by. -Shifting by a negative amount causes a runtime exception. - -Members: - -* ``.length`` yields the fixed length of the byte array (read-only). - -.. note:: - The type ``byte[]`` is an array of bytes, but due to padding rules, it wastes - 31 bytes of space for each element (except in storage). It is better to use the ``bytes`` - type instead. - -Dynamically-sized byte array ----------------------------- - -``bytes``: - Dynamically-sized byte array, see :ref:`arrays`. Not a value-type! -``string``: - Dynamically-sized UTF-8-encoded string, see :ref:`arrays`. Not a value-type! - -.. index:: address, literal;address - -.. _address_literals: - -Address Literals ----------------- - -Hexadecimal literals that pass the address checksum test, for example -``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address payable`` type. -Hexadecimal literals that are between 39 and 41 digits -long and do not pass the checksum test produce -a warning and are treated as regular rational number literals. - -.. note:: - The mixed-case address checksum format is defined in `EIP-55 `_. - -.. index:: literal, literal;rational - -.. _rational_literals: - -Rational and Integer Literals ------------------------------ - -Integer literals are formed from a sequence of numbers in the range 0-9. -They are interpreted as decimals. For example, ``69`` means sixty nine. -Octal literals do not exist in Solidity and leading zeros are invalid. - -Decimal fraction literals are formed by a ``.`` with at least one number on -one side. Examples include ``1.``, ``.1`` and ``1.3``. - -Scientific notation is also supported, where the base can have fractions, while the exponent cannot. -Examples include ``2e10``, ``-2e10``, ``2e-10``, ``2.5e1``. - -Underscores can be used to separate the digits of a numeric literal to aid readability. -For example, decimal ``123_000``, hexadecimal ``0x2eff_abde``, scientific decimal notation ``1_2e345_678`` are all valid. -Underscores are only allowed between two digits and only one consecutive underscore is allowed. -There is no additional semantic meaning added to a number literal containing underscores, -the underscores are ignored. - -Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by -using them together with a non-literal expression or by explicit conversion). -This means that computations do not overflow and divisions do not truncate -in number literal expressions. - -For example, ``(2**800 + 1) - 2**800`` results in the constant ``1`` (of type ``uint8``) -although intermediate results would not even fit the machine word size. Furthermore, ``.5 * 8`` results -in the integer ``4`` (although non-integers were used in between). - -Any operator that can be applied to integers can also be applied to number literal expressions as -long as the operands are integers. If any of the two is fractional, bit operations are disallowed -and exponentiation is disallowed if the exponent is fractional (because that might result in -a non-rational number). - -.. note:: - Solidity has a number literal type for each rational number. - Integer literals and rational number literals belong to number literal types. - Moreover, all number literal expressions (i.e. the expressions that - contain only number literals and operators) belong to number literal - types. So the number literal expressions ``1 + 2`` and ``2 + 1`` both - belong to the same number literal type for the rational number three. - -.. warning:: - Division on integer literals used to truncate in Solidity prior to version 0.4.0, but it now converts into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``. - -.. note:: - Number literal expressions are converted into a non-literal type as soon as they are used with non-literal - expressions. Disregarding types, the value of the expression assigned to ``b`` - below evaluates to an integer. Because ``a`` is of type ``uint128``, the - expression ``2.5 + a`` has to have a proper type, though. Since there is no common type - for the type of ``2.5`` and ``uint128``, the Solidity compiler does not accept - this code. - -:: - - uint128 a = 1; - uint128 b = 2.5 + a + 0.5; - -.. index:: literal, literal;string, string -.. _string_literals: - -String Literals and Types -------------------------- - -String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. - -For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. - -String literals support the following escape characters: - - - ``\`` (escapes an actual newline) - - ``\\`` (backslash) - - ``\'`` (single quote) - - ``\"`` (double quote) - - ``\b`` (backspace) - - ``\f`` (form feed) - - ``\n`` (newline) - - ``\r`` (carriage return) - - ``\t`` (tab) - - ``\v`` (vertical tab) - - ``\xNN`` (hex escape, see below) - - ``\uNNNN`` (unicode escape, see below) - -``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. - -The string in the following example has a length of ten bytes. -It starts with a newline byte, followed by a double quote, a single -quote a backslash character and then (without separator) the -character sequence ``abcdef``. - -:: - - "\n\"\'\\abc\ - def" - -Any unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to -terminate the string literal. Newline only terminates the string literal if it is not preceded by a ``\``. - -.. index:: literal, bytes - -Hexadecimal Literals --------------------- - -Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. - -Hexadecimal literals behave like :ref:`string literals ` and have the same convertibility restrictions. - -.. index:: enum - -.. _enums: - -Enums ------ - -Enums are one way to create a user-defined type in Solidity. They are explicitly convertible -to and from all integer types but implicit conversion is not allowed. The explicit conversion -from integer checks at runtime that the value lies inside the range of the enum and causes a failing assert otherwise. -Enums needs at least one member. - -The data representation is the same as for enums in C: The options are represented by -subsequent unsigned integer values starting from ``0``. - - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } - ActionChoices choice; - ActionChoices constant defaultChoice = ActionChoices.GoStraight; - - function setGoStraight() public { - choice = ActionChoices.GoStraight; - } - - // Since enum types are not part of the ABI, the signature of "getChoice" - // will automatically be changed to "getChoice() returns (uint8)" - // for all matters external to Solidity. The integer type used is just - // large enough to hold all enum values, i.e. if you have more than 256 values, - // `uint16` will be used and so on. - function getChoice() public view returns (ActionChoices) { - return choice; - } - - function getDefaultChoice() public pure returns (uint) { - return uint(defaultChoice); - } - } - -.. index:: ! function type, ! type; function - -.. _function_types: - -Function Types --------------- - -Function types are the types of functions. Variables of function type -can be assigned from functions and function parameters of function type -can be used to pass functions to and return functions from function calls. -Function types come in two flavours - *internal* and *external* functions: - -Internal functions can only be called inside the current contract (more specifically, -inside the current code unit, which also includes internal library functions -and inherited functions) because they cannot be executed outside of the -context of the current contract. Calling an internal function is realized -by jumping to its entry label, just like when calling a function of the current -contract internally. - -External functions consist of an address and a function signature and they can -be passed via and returned from external function calls. - -Function types are notated as follows:: - - function () {internal|external} [pure|view|payable] [returns ()] - -In contrast to the parameter types, the return types cannot be empty - if the -function type should not return anything, the whole ``returns ()`` -part has to be omitted. - -By default, function types are internal, so the ``internal`` keyword can be -omitted. Note that this only applies to function types. Visibility has -to be specified explicitly for functions defined in contracts, they -do not have a default. - -Conversions: - -A value of external function type can be explicitly converted to ``address`` -resulting in the address of the contract of the function. - -A function type ``A`` is implicitly convertible to a function type ``B`` if and only if -their parameter types are identical, their return types are identical, -their internal/external property is identical and the state mutability of ``A`` -is not more restrictive than the state mutability of ``B``. In particular: - - - ``pure`` functions can be converted to ``view`` and ``non-payable`` functions - - ``view`` functions can be converted to ``non-payable`` functions - - ``payable`` functions can be converted to ``non-payable`` functions - -No other conversions between function types are possible. - -The rule about ``payable`` and ``non-payable`` might be a little -confusing, but in essence, if a function is ``payable``, this means that it -also accepts a payment of zero Ether, so it also is ``non-payable``. -On the other hand, a ``non-payable`` function will reject Ether sent to it, -so ``non-payable`` functions cannot be converted to ``payable`` functions. - -If a function type variable is not initialised, calling it results -in a failed assertion. The same happens if you call a function after using ``delete`` -on it. - -If external function types are used outside of the context of Solidity, -they are treated as the ``function`` type, which encodes the address -followed by the function identifier together in a single ``bytes24`` type. - -Note that public functions of the current contract can be used both as an -internal and as an external function. To use ``f`` as an internal function, -just use ``f``, if you want to use its external form, use ``this.f``. - -Members: - -Public (or external) functions also have a special member called ``selector``, -which returns the :ref:`ABI function selector `:: - - pragma solidity >=0.4.16 <0.6.0; - - contract Selector { - function f() public pure returns (bytes4) { - return this.f.selector; - } - } - -Example that shows how to use internal function types:: - - pragma solidity >=0.4.16 <0.6.0; - - library ArrayUtils { - // internal functions can be used in internal library functions because - // they will be part of the same code context - function map(uint[] memory self, function (uint) pure returns (uint) f) - internal - pure - returns (uint[] memory r) - { - r = new uint[](self.length); - for (uint i = 0; i < self.length; i++) { - r[i] = f(self[i]); - } - } - function reduce( - uint[] memory self, - function (uint, uint) pure returns (uint) f - ) - internal - pure - returns (uint r) - { - r = self[0]; - for (uint i = 1; i < self.length; i++) { - r = f(r, self[i]); - } - } - function range(uint length) internal pure returns (uint[] memory r) { - r = new uint[](length); - for (uint i = 0; i < r.length; i++) { - r[i] = i; - } - } - } - - contract Pyramid { - using ArrayUtils for *; - function pyramid(uint l) public pure returns (uint) { - return ArrayUtils.range(l).map(square).reduce(sum); - } - function square(uint x) internal pure returns (uint) { - return x * x; - } - function sum(uint x, uint y) internal pure returns (uint) { - return x + y; - } - } - -Another example that uses external function types:: - - pragma solidity >=0.4.22 <0.6.0; - - contract Oracle { - struct Request { - bytes data; - function(uint) external callback; - } - Request[] requests; - event NewRequest(uint); - function query(bytes memory data, function(uint) external callback) public { - requests.push(Request(data, callback)); - emit NewRequest(requests.length - 1); - } - function reply(uint requestID, uint response) public { - // Here goes the check that the reply comes from a trusted source - requests[requestID].callback(response); - } - } - - contract OracleUser { - Oracle constant oracle = Oracle(0x1234567); // known contract - uint exchangeRate; - function buySomething() public { - oracle.query("USD", this.oracleResponse); - } - function oracleResponse(uint response) public { - require( - msg.sender == address(oracle), - "Only oracle can call this." - ); - exchangeRate = response; - } - } - -.. note:: - Lambda or inline functions are planned but not yet supported. +.. include:: types/value-types.rst .. index:: ! type;reference, ! reference type, storage, memory, location, array, struct diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst new file mode 100644 index 00000000..077587dd --- /dev/null +++ b/docs/types/value-types.rst @@ -0,0 +1,708 @@ +.. index:: ! value type, ! type;value +.. _value-types: + +Value Types +=========== + +The following types are also called value types because variables of these +types will always be passed by value, i.e. they are always copied when they +are used as function arguments or in assignments. + +.. index:: ! bool, ! true, ! false + +Booleans +-------- + +``bool``: The possible values are constants ``true`` and ``false``. + +Operators: + +* ``!`` (logical negation) +* ``&&`` (logical conjunction, "and") +* ``||`` (logical disjunction, "or") +* ``==`` (equality) +* ``!=`` (inequality) + +The operators ``||`` and ``&&`` apply the common short-circuiting rules. This means that in the expression ``f(x) || g(y)``, if ``f(x)`` evaluates to ``true``, ``g(y)`` will not be evaluated even if it may have side-effects. + +.. index:: ! uint, ! int, ! integer + +Integers +-------- + +``int`` / ``uint``: Signed and unsigned integers of various sizes. Keywords ``uint8`` to ``uint256`` in steps of ``8`` (unsigned of 8 up to 256 bits) and ``int8`` to ``int256``. ``uint`` and ``int`` are aliases for ``uint256`` and ``int256``, respectively. + +Operators: + +* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) +* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) +* Shift operators: ``<<`` (left shift), ``>>`` (right shift) +* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) + + +Comparisons +^^^^^^^^^^^ + +The value of a comparison is the one obtained by comparing the integer value. + +Bit operations +^^^^^^^^^^^^^^ + +Bit operations are performed on the two's complement representation of the number. +This means that, for example ``~int256(0) == int256(-1)``. + +Shifts +^^^^^^ + +The result of a shift operation has the type of the left operand. The +expression ``x << y`` is equivalent to ``x * 2**y``, and, for positive integers, +``x >> y`` is equivalent to ``x / 2**y``. For negative ``x``, ``x >> y`` +is equivalent to dividing by a power of ``2`` while rounding down (towards negative infinity). +Shifting by a negative amount throws a runtime exception. + +.. warning:: + Before version ``0.5.0`` a right shift ``x >> y`` for negative ``x`` was equivalent to ``x / 2**y``, + i.e. right shifts used rounding towards zero instead of rounding towards negative infinity. + +Addition, Subtraction and Multiplication +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Addition, subtraction and multiplication have the usual semantics. +They wrap in two's complement representation, meaning that +for example ``uint256(0) - uint256(1) == 2**256 - 1``. You have to take these overflows +into account when designing safe smart contracts. + +The expression ``-x`` is equivalent to ``(T(0) - x)`` where +``T`` is the type of ``x``. This means that ``-x`` will not be negative +if the type of ``x`` is an unsigned integer type. Also, ``-x`` can be +positive if ``x`` is negative. There is another caveat also resulting +from two's complement representation:: + + int x = -2**255; + assert(-x == x); + +This means that even if a number is negative, you cannot assume that +its negation will be positive. + + +Division +^^^^^^^^ + +Since the type of the result of an operation is always the type of one of +the operands, division on integers always results in an integer. +In Solidity, division rounds towards zero. This mean that ``int256(-5) / int256(2) == int256(-2)``. + +Note that in contrast, division on :ref:`literals` results in fractional values +of arbitrary precision. + +.. note:: + Division by zero causes a failing assert. + +Modulo +^^^^^^ + +The modulo operation ``a % n`` yields the remainder ``r`` after the division of the operand ``a`` +by the operand ``n``, where ``q = int(a / n)`` and ``r = a - (n * q)``. This means that modulo +results in the same sign as its left operand (or zero) and ``a % n == -(abs(a) % n)`` holds for negative ``a``: + + * ``int256(5) % int256(2) == int256(1)`` + * ``int256(5) % int256(-2) == int256(1)`` + * ``int256(-5) % int256(2) == int256(-1)`` + * ``int256(-5) % int256(-2) == int256(-1)`` + +.. note:: + Modulo with zero causes a failing assert. + +Exponentiation +^^^^^^^^^^^^^^ + +Exponentiation is only available for unsigned types. Please take care that the types +you are using are large enough to hold the result and prepare for potential wrapping behaviour. + +.. note:: + Note that ``0**0`` is defined by the EVM as ``1``. + +.. index:: ! ufixed, ! fixed, ! fixed point number + +Fixed Point Numbers +------------------- + +.. warning:: + Fixed point numbers are not fully supported by Solidity yet. They can be declared, but + cannot be assigned to or from. + +``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represents the number of bits taken by +the type and ``N`` represents how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive. +``ufixed`` and ``fixed`` are aliases for ``ufixed128x18`` and ``fixed128x18``, respectively. + +Operators: + +* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) +* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo) + +.. note:: + The main difference between floating point (``float`` and ``double`` in many languages, more precisely IEEE 754 numbers) and fixed point numbers is + that the number of bits used for the integer and the fractional part (the part after the decimal dot) is flexible in the former, while it is strictly + defined in the latter. Generally, in floating point almost the entire space is used to represent the number, while only a small number of bits define + where the decimal point is. + +.. index:: address, balance, send, call, callcode, delegatecall, staticcall, transfer + +.. _address: + +Address +------- + +The address type comes in two flavours, which are largely identical: + + - ``address``: Holds a 20 byte value (size of an Ethereum address). + - ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. + +The idea behind this distinction is that ``address payable`` is an address you can send Ether to, +while a plain ``address`` cannot be sent Ether. + +Type conversions: + +Implicit conversions from ``address payable`` to ``address`` are allowed, whereas conversions from ``address`` to ``address payable`` are +not possible (the only way to perform such a conversion is by using an intermediate conversion to ``uint160``). + +:ref:`Address literals` can be implicitly converted to ``address payable``. + +Explicit conversions to and from ``address`` are allowed for integers, integer literals, ``bytes20`` and contract types with the following +caveat: +Conversions of the form ``address payable(x)`` are not allowed. Instead the result of a conversion of the form ``address(x)`` +has the type ``address payable``, if ``x`` is of integer or fixed bytes type, a literal or a contract with a payable fallback function. +If ``x`` is a contract without payable fallback function, then ``address(x)`` will be of type ``address``. +In external function signatures ``address`` is used for both the ``address`` and the ``address payable`` type. + +.. note:: + It might very well be that you do not need to care about the distinction between ``address`` + and ``address payable`` and just use ``address`` everywhere. For example, + if you are using the :ref:`withdrawal pattern`, you can (and should) store the + address itself as ``address``, because you invoke the ``transfer`` function on + ``msg.sender``, which is an ``address payable``. + +Operators: + +* ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>`` + +.. warning:: + If you convert a type that uses a larger byte size to an ``address``, for example ``bytes32``, then the ``address`` is truncated. + To reduce conversion ambiguity version 0.4.24 and higher of the compiler force you make the truncation explicit in the conversion. + Take for example the address ``0x111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC``. + + You can use ``address(uint160(bytes20(b)))``, which results in ``0x111122223333444455556666777788889999aAaa``, + or you can use ``address(uint160(uint256(b)))``, which results in ``0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc``. + +.. note:: + The distinction between ``address`` and ``address payable`` was introduced with version 0.5.0. + Also starting from that version, contracts do not derive from the address type, but can still be explicitly converted to + ``address`` or to ``address payable``, if they have a payable fallback function. + +.. _members-of-addresses: + +Members of Addresses +^^^^^^^^^^^^^^^^^^^^ + +For a quick reference of all members of address, see :ref:`address_related`. + +* ``balance`` and ``transfer`` + +It is possible to query the balance of an address using the property ``balance`` +and to send Ether (in units of wei) to a payable address using the ``transfer`` function: + +:: + + address payable x = address(0x123); + address myAddress = address(this); + if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); + +The ``transfer`` function fails if the balance of the current contract is not large enough +or if the Ether transfer is rejected by the receiving account. The ``transfer`` function +reverts on failure. + +.. note:: + If ``x`` is a contract address, its code (more specifically: its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception. + +* ``send`` + +Send is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``. + +.. warning:: + There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 + (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order + to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: + use a pattern where the recipient withdraws the money. + +* ``call``, ``delegatecall`` and ``staticcall`` + +In order to interface with contracts that do not adhere to the ABI, +or to get more direct control over the encoding, +the functions ``call``, ``delegatecall`` and ``staticcall`` are provided. +They all take a single ``bytes memory`` parameter and +return the success condition (as a ``bool``) and the returned data +(``bytes memory``). +The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector`` +and ``abi.encodeWithSignature`` can be used to encode structured data. + +Example:: + + bytes memory payload = abi.encodeWithSignature("register(string)", "MyName"); + (bool success, bytes memory returnData) = address(nameReg).call(payload); + require(success); + +.. warning:: + All these functions are low-level functions and should be used with care. + Specifically, any unknown contract might be malicious and if you call it, you + hand over control to that contract which could in turn call back into + your contract, so be prepared for changes to your state variables + when the call returns. The regular way to interact with other contracts + is to call a function on a contract object (``x.f()``). + +.. note:: + Previous versions of Solidity allowed these functions to receive + arbitrary arguments and would also handle a first argument of type + ``bytes4`` differently. These edge cases were removed in version 0.5.0. + +It is possible to adjust the supplied gas with the ``.gas()`` modifier:: + + address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName")); + +Similarly, the supplied Ether value can be controlled too:: + + address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); + +Lastly, these modifiers can be combined. Their order does not matter:: + + address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); + +In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. + +.. note:: + Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. This function was removed in version 0.5.0. + +Since byzantium ``staticcall`` can be used as well. This is basically the same as ``call``, but will revert if the called function modifies the state in any way. + +All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. + +The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``. + +.. note:: + All contracts can be converted to ``address`` type, so it is possible to query the balance of the + current contract using ``address(this).balance``. + +.. index:: ! contract type, ! type; contract + +.. _contract_types: + +Contract Types +-------------- + +Every :ref:`contract` defines its own type. +You can implicitly convert contracts to contracts they inherit from. +Contracts can be explicitly converted to and from all other contract types +and the ``address`` type. + +Explicit conversion to and from the ``address payable`` type +is only possible if the contract type has a payable fallback function. +The conversion is still performed using ``address(x)`` and not +using ``address payable(x)``. You can find more information in the section about +the :ref:`address type
`. + +.. note:: + Before version 0.5.0, contracts directly derived from the address type + and there was no distinction between ``address`` and ``address payable``. + +If you declare a local variable of contract type (`MyContract c`), you can call +functions on that contract. Take care to assign it from somewhere that is the +same contract type. + +You can also instantiate contracts (which means they are newly created). You +can find more details in the :ref:`'Contracts via new'` +section. + +The data representation of a contract is identical to that of the ``address`` +type and this type is also used in the :ref:`ABI`. + +Contracts do not support any operators. + +The members of contract types are the external functions of the contract +including public state variables. + +.. index:: byte array, bytes32 + +Fixed-size byte arrays +---------------------- + +The value types ``bytes1``, ``bytes2``, ``bytes3``, ..., ``bytes32`` +hold a sequence of bytes from one to up to 32. +``byte`` is an alias for ``bytes1``. + +Operators: + +* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) +* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) +* Shift operators: ``<<`` (left shift), ``>>`` (right shift) +* Index access: If ``x`` is of type ``bytesI``, then ``x[k]`` for ``0 <= k < I`` returns the ``k`` th byte (read-only). + +The shifting operator works with any integer type as right operand (but +returns the type of the left operand), which denotes the number of bits to shift by. +Shifting by a negative amount causes a runtime exception. + +Members: + +* ``.length`` yields the fixed length of the byte array (read-only). + +.. note:: + The type ``byte[]`` is an array of bytes, but due to padding rules, it wastes + 31 bytes of space for each element (except in storage). It is better to use the ``bytes`` + type instead. + +Dynamically-sized byte array +---------------------------- + +``bytes``: + Dynamically-sized byte array, see :ref:`arrays`. Not a value-type! +``string``: + Dynamically-sized UTF-8-encoded string, see :ref:`arrays`. Not a value-type! + +.. index:: address, literal;address + +.. _address_literals: + +Address Literals +---------------- + +Hexadecimal literals that pass the address checksum test, for example +``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address payable`` type. +Hexadecimal literals that are between 39 and 41 digits +long and do not pass the checksum test produce +a warning and are treated as regular rational number literals. + +.. note:: + The mixed-case address checksum format is defined in `EIP-55 `_. + +.. index:: literal, literal;rational + +.. _rational_literals: + +Rational and Integer Literals +----------------------------- + +Integer literals are formed from a sequence of numbers in the range 0-9. +They are interpreted as decimals. For example, ``69`` means sixty nine. +Octal literals do not exist in Solidity and leading zeros are invalid. + +Decimal fraction literals are formed by a ``.`` with at least one number on +one side. Examples include ``1.``, ``.1`` and ``1.3``. + +Scientific notation is also supported, where the base can have fractions, while the exponent cannot. +Examples include ``2e10``, ``-2e10``, ``2e-10``, ``2.5e1``. + +Underscores can be used to separate the digits of a numeric literal to aid readability. +For example, decimal ``123_000``, hexadecimal ``0x2eff_abde``, scientific decimal notation ``1_2e345_678`` are all valid. +Underscores are only allowed between two digits and only one consecutive underscore is allowed. +There is no additional semantic meaning added to a number literal containing underscores, +the underscores are ignored. + +Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by +using them together with a non-literal expression or by explicit conversion). +This means that computations do not overflow and divisions do not truncate +in number literal expressions. + +For example, ``(2**800 + 1) - 2**800`` results in the constant ``1`` (of type ``uint8``) +although intermediate results would not even fit the machine word size. Furthermore, ``.5 * 8`` results +in the integer ``4`` (although non-integers were used in between). + +Any operator that can be applied to integers can also be applied to number literal expressions as +long as the operands are integers. If any of the two is fractional, bit operations are disallowed +and exponentiation is disallowed if the exponent is fractional (because that might result in +a non-rational number). + +.. note:: + Solidity has a number literal type for each rational number. + Integer literals and rational number literals belong to number literal types. + Moreover, all number literal expressions (i.e. the expressions that + contain only number literals and operators) belong to number literal + types. So the number literal expressions ``1 + 2`` and ``2 + 1`` both + belong to the same number literal type for the rational number three. + +.. warning:: + Division on integer literals used to truncate in Solidity prior to version 0.4.0, but it now converts into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``. + +.. note:: + Number literal expressions are converted into a non-literal type as soon as they are used with non-literal + expressions. Disregarding types, the value of the expression assigned to ``b`` + below evaluates to an integer. Because ``a`` is of type ``uint128``, the + expression ``2.5 + a`` has to have a proper type, though. Since there is no common type + for the type of ``2.5`` and ``uint128``, the Solidity compiler does not accept + this code. + +:: + + uint128 a = 1; + uint128 b = 2.5 + a + 0.5; + +.. index:: literal, literal;string, string +.. _string_literals: + +String Literals and Types +------------------------- + +String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. + +For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. + +String literals support the following escape characters: + + - ``\`` (escapes an actual newline) + - ``\\`` (backslash) + - ``\'`` (single quote) + - ``\"`` (double quote) + - ``\b`` (backspace) + - ``\f`` (form feed) + - ``\n`` (newline) + - ``\r`` (carriage return) + - ``\t`` (tab) + - ``\v`` (vertical tab) + - ``\xNN`` (hex escape, see below) + - ``\uNNNN`` (unicode escape, see below) + +``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. + +The string in the following example has a length of ten bytes. +It starts with a newline byte, followed by a double quote, a single +quote a backslash character and then (without separator) the +character sequence ``abcdef``. + +:: + + "\n\"\'\\abc\ + def" + +Any unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to +terminate the string literal. Newline only terminates the string literal if it is not preceded by a ``\``. + +.. index:: literal, bytes + +Hexadecimal Literals +-------------------- + +Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. + +Hexadecimal literals behave like :ref:`string literals ` and have the same convertibility restrictions. + +.. index:: enum + +.. _enums: + +Enums +----- + +Enums are one way to create a user-defined type in Solidity. They are explicitly convertible +to and from all integer types but implicit conversion is not allowed. The explicit conversion +from integer checks at runtime that the value lies inside the range of the enum and causes a failing assert otherwise. +Enums needs at least one member. + +The data representation is the same as for enums in C: The options are represented by +subsequent unsigned integer values starting from ``0``. + + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } + ActionChoices choice; + ActionChoices constant defaultChoice = ActionChoices.GoStraight; + + function setGoStraight() public { + choice = ActionChoices.GoStraight; + } + + // Since enum types are not part of the ABI, the signature of "getChoice" + // will automatically be changed to "getChoice() returns (uint8)" + // for all matters external to Solidity. The integer type used is just + // large enough to hold all enum values, i.e. if you have more than 256 values, + // `uint16` will be used and so on. + function getChoice() public view returns (ActionChoices) { + return choice; + } + + function getDefaultChoice() public pure returns (uint) { + return uint(defaultChoice); + } + } + +.. index:: ! function type, ! type; function + +.. _function_types: + +Function Types +-------------- + +Function types are the types of functions. Variables of function type +can be assigned from functions and function parameters of function type +can be used to pass functions to and return functions from function calls. +Function types come in two flavours - *internal* and *external* functions: + +Internal functions can only be called inside the current contract (more specifically, +inside the current code unit, which also includes internal library functions +and inherited functions) because they cannot be executed outside of the +context of the current contract. Calling an internal function is realized +by jumping to its entry label, just like when calling a function of the current +contract internally. + +External functions consist of an address and a function signature and they can +be passed via and returned from external function calls. + +Function types are notated as follows:: + + function () {internal|external} [pure|view|payable] [returns ()] + +In contrast to the parameter types, the return types cannot be empty - if the +function type should not return anything, the whole ``returns ()`` +part has to be omitted. + +By default, function types are internal, so the ``internal`` keyword can be +omitted. Note that this only applies to function types. Visibility has +to be specified explicitly for functions defined in contracts, they +do not have a default. + +Conversions: + +A value of external function type can be explicitly converted to ``address`` +resulting in the address of the contract of the function. + +A function type ``A`` is implicitly convertible to a function type ``B`` if and only if +their parameter types are identical, their return types are identical, +their internal/external property is identical and the state mutability of ``A`` +is not more restrictive than the state mutability of ``B``. In particular: + + - ``pure`` functions can be converted to ``view`` and ``non-payable`` functions + - ``view`` functions can be converted to ``non-payable`` functions + - ``payable`` functions can be converted to ``non-payable`` functions + +No other conversions between function types are possible. + +The rule about ``payable`` and ``non-payable`` might be a little +confusing, but in essence, if a function is ``payable``, this means that it +also accepts a payment of zero Ether, so it also is ``non-payable``. +On the other hand, a ``non-payable`` function will reject Ether sent to it, +so ``non-payable`` functions cannot be converted to ``payable`` functions. + +If a function type variable is not initialised, calling it results +in a failed assertion. The same happens if you call a function after using ``delete`` +on it. + +If external function types are used outside of the context of Solidity, +they are treated as the ``function`` type, which encodes the address +followed by the function identifier together in a single ``bytes24`` type. + +Note that public functions of the current contract can be used both as an +internal and as an external function. To use ``f`` as an internal function, +just use ``f``, if you want to use its external form, use ``this.f``. + +Members: + +Public (or external) functions also have a special member called ``selector``, +which returns the :ref:`ABI function selector `:: + + pragma solidity >=0.4.16 <0.6.0; + + contract Selector { + function f() public pure returns (bytes4) { + return this.f.selector; + } + } + +Example that shows how to use internal function types:: + + pragma solidity >=0.4.16 <0.6.0; + + library ArrayUtils { + // internal functions can be used in internal library functions because + // they will be part of the same code context + function map(uint[] memory self, function (uint) pure returns (uint) f) + internal + pure + returns (uint[] memory r) + { + r = new uint[](self.length); + for (uint i = 0; i < self.length; i++) { + r[i] = f(self[i]); + } + } + function reduce( + uint[] memory self, + function (uint, uint) pure returns (uint) f + ) + internal + pure + returns (uint r) + { + r = self[0]; + for (uint i = 1; i < self.length; i++) { + r = f(r, self[i]); + } + } + function range(uint length) internal pure returns (uint[] memory r) { + r = new uint[](length); + for (uint i = 0; i < r.length; i++) { + r[i] = i; + } + } + } + + contract Pyramid { + using ArrayUtils for *; + function pyramid(uint l) public pure returns (uint) { + return ArrayUtils.range(l).map(square).reduce(sum); + } + function square(uint x) internal pure returns (uint) { + return x * x; + } + function sum(uint x, uint y) internal pure returns (uint) { + return x + y; + } + } + +Another example that uses external function types:: + + pragma solidity >=0.4.22 <0.6.0; + + contract Oracle { + struct Request { + bytes data; + function(uint) external callback; + } + Request[] requests; + event NewRequest(uint); + function query(bytes memory data, function(uint) external callback) public { + requests.push(Request(data, callback)); + emit NewRequest(requests.length - 1); + } + function reply(uint requestID, uint response) public { + // Here goes the check that the reply comes from a trusted source + requests[requestID].callback(response); + } + } + + contract OracleUser { + Oracle constant oracle = Oracle(0x1234567); // known contract + uint exchangeRate; + function buySomething() public { + oracle.query("USD", this.oracleResponse); + } + function oracleResponse(uint response) public { + require( + msg.sender == address(oracle), + "Only oracle can call this." + ); + exchangeRate = response; + } + } + +.. note:: + Lambda or inline functions are planned but not yet supported. \ No newline at end of file -- cgit From 47399a6e2bcd5504d79c0fcf7ac9b89287bfac69 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 9 Jan 2019 12:15:58 +0100 Subject: Split Reference types doc into new file --- docs/conf.py | 2 +- docs/types.rst | 410 +---------------------------------------- docs/types/reference-types.rst | 409 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 411 insertions(+), 410 deletions(-) create mode 100644 docs/types/reference-types.rst diff --git a/docs/conf.py b/docs/conf.py index 342aefa9..d08a5191 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -81,7 +81,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts'] +exclude_patterns = ['_build', 'contracts', 'types'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/types.rst b/docs/types.rst index 4eb1f926..cdb424d1 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -727,415 +727,7 @@ Another example that uses external function types:: .. note:: Lambda or inline functions are planned but not yet supported. -.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct - -.. _reference-types: - -Reference Types -=============== - -Values of reference type can be modified through multiple different names. -Contrast this with value types where you get an independent copy whenever -a variable of value type is used. Because of that, reference types have to be handled -more carefully than value types. Currently, reference types comprise structs, -arrays and mappings. If you use a reference type, you always have to explicitly -provide the data area where the type is stored: ``memory`` (whose lifetime is limited -to a function call), ``storage`` (the location where the state variables are stored) -or ``calldata`` (special data location that contains the function arguments, -only available for external function call parameters). - -An assignment or type conversion that changes the data location will always incur an automatic copy operation, -while assignments inside the same data location only copy in some cases for storage types. - -.. _data-location: - -Data location -------------- - -Every reference type, i.e. *arrays* and *structs*, has an additional -annotation, the "data location", about where it is stored. There are three data locations: -``memory``, ``storage`` and ``calldata``. Calldata is only valid for parameters of external contract -functions and is required for this type of parameter. Calldata is a non-modifiable, -non-persistent area where function arguments are stored, and behaves mostly like memory. - - -.. note:: - Prior to version 0.5.0 the data location could be omitted, and would default to different locations - depending on the kind of variable, function type, etc., but all complex types must now give an explicit - data location. - -.. _data-location-assignment: - -Data location and assignment behaviour -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Data locations are not only relevant for persistency of data, but also for the semantics of assignments: - -* Assignments between ``storage`` and ``memory`` (or from ``calldata``) always create an independent copy. -* Assignments from ``memory`` to ``memory`` only create references. This means that changes to one memory variable are also visible in all other memory variables that refer to the same data. -* Assignments from ``storage`` to a local storage variable also only assign a reference. -* All other assignments to ``storage`` always copy. Examples for this case are assignments to state variables or to members of local variables of storage struct type, even if the local variable itself is just a reference. - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract C { - uint[] x; // the data location of x is storage - - // the data location of memoryArray is memory - function f(uint[] memory memoryArray) public { - x = memoryArray; // works, copies the whole array to storage - uint[] storage y = x; // works, assigns a pointer, data location of y is storage - y[7]; // fine, returns the 8th element - y.length = 2; // fine, modifies x through y - delete x; // fine, clears the array, also modifies y - // The following does not work; it would need to create a new temporary / - // unnamed array in storage, but storage is "statically" allocated: - // y = memoryArray; - // This does not work either, since it would "reset" the pointer, but there - // is no sensible location it could point to. - // delete y; - g(x); // calls g, handing over a reference to x - h(x); // calls h and creates an independent, temporary copy in memory - } - - function g(uint[] storage) internal pure {} - function h(uint[] memory) public pure {} - } - -.. index:: ! array - -.. _arrays: - -Arrays ------- - -Arrays can have a compile-time fixed size, or they can have a dynamic size. - -The type of an array of fixed size ``k`` and element type ``T`` is written as ``T[k]``, -and an array of dynamic size as ``T[]``. - -For example, an array of 5 dynamic arrays of ``uint`` is written as -``uint[][5]``. The notation is reversed compared to some other languages. In -Solidity, ``X[3]`` is always an array containing three elements of type ``X``, -even if ``X`` is itself an array. This is not the case in other languages such -as C. - -Indices are zero-based, and access is in the opposite direction of the -declaration. - -For example, if you have a variable ``uint[][5] x memory``, you access the -second ``uint`` in the third dynamic array using ``x[2][1]``, and to access the -third dynamic array, use ``x[2]``. Again, -if you have an array ``T[5] a`` for a type ``T`` that can also be an array, -then ``a[2]`` always has type ``T``. - -Array elements can be of any type, including mapping or struct. The general -restrictions for types apply, in that mappings can only be stored in the -``storage`` data location and publicly-visible functions need parameters that are :ref:`ABI types `. - -Accessing an array past its end causes a failing assertion. You can use the ``.push()`` method to append a new element at the end or assign to the ``.length`` :ref:`member ` to change the size (see below for caveats). -method or increase the ``.length`` :ref:`member ` to add elements. - -Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, -but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow -length or index access. - -You should use ``bytes`` over ``byte[]`` because it is cheaper, since ``byte[]`` adds 31 padding bytes between the elements. As a general rule, -use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length -string (UTF-8) data. If you can limit the length to a certain number of bytes, -always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper. - -.. note:: - If you want to access the byte-representation of a string ``s``, use - ``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind - that you are accessing the low-level bytes of the UTF-8 representation, - and not the individual characters. - -It is possible to mark arrays ``public`` and have Solidity create a :ref:`getter `. -The numeric index becomes a required parameter for the getter. - -.. index:: ! array;allocating, new - -Allocating Memory Arrays -^^^^^^^^^^^^^^^^^^^^^^^^ - -You can use the ``new`` keyword to create arrays with a runtime-dependent length in memory. -As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to -the ``.length`` member). You either have to calculate the required size in advance -or create a new memory array and copy every element. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract C { - function f(uint len) public pure { - uint[] memory a = new uint[](7); - bytes memory b = new bytes(len); - assert(a.length == 7); - assert(b.length == len); - a[6] = 8; - } - } - -.. index:: ! array;literals, ! inline;arrays - -Array Literals -^^^^^^^^^^^^^^ - -An array literal is a comma-separated list of one or more expressions, enclosed -in square brackets (``[...]``). For example ``[1, a, f(3)]``. There must be a -common type all elements can be implicitly converted to. This is the elementary -type of the array. - -Array literals are always statically-sized memory arrays. - -In the example below, the type of ``[1, 2, 3]`` is -``uint8[3] memory``. Because the type of each of these constants is ``uint8``, if you want the result to be a ``uint[3] memory`` type, you need to convert the first element to ``uint``. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract C { - function f() public pure { - g([uint(1), 2, 3]); - } - function g(uint[3] memory) public pure { - // ... - } - } - -Fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible: - -:: - - pragma solidity >=0.4.0 <0.6.0; - - // This will not compile. - contract C { - function f() public { - // The next line creates a type error because uint[3] memory - // cannot be converted to uint[] memory. - uint[] memory x = [uint(1), 3, 4]; - } - } - -It is planned to remove this restriction in the future, but it creates some -complications because of how arrays are passed in the ABI. - -.. index:: ! array;length, length, push, pop, !array;push, !array;pop - -.. _array-members: - -Array Members -^^^^^^^^^^^^^ - -**length**: - Arrays have a ``length`` member that contains their number of elements. - The length of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. - For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array. - Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion. - Increasing the length adds new zero-initialised elements to the array. - Reducing the length performs an implicit :ref:``delete`` on each of the - removed elements. If you try to resize a non-dynamic array that isn't in - storage, you receive a ``Value must be an lvalue`` error. -**push**: - Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length. -**pop**: - Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that you can use to remove an element from the end of the array. This also implicitly calls :ref:``delete`` on the removed element. - -.. warning:: - If you use ``.length--`` on an empty array, it causes an underflow and - thus sets the length to ``2**256-1``. - -.. note:: - Increasing the length of a storage array has constant gas costs because - storage is assumed to be zero-initialised, while decreasing - the length has at least linear cost (but in most cases worse than linear), - because it includes explicitly clearing the removed - elements similar to calling :ref:``delete`` on them. - -.. note:: - It is not yet possible to use arrays of arrays in external functions - (but they are supported in public functions). - -.. note:: - In EVM versions before Byzantium, it was not possible to access - dynamic arrays return from function calls. If you call functions - that return dynamic arrays, make sure to use an EVM that is set to - Byzantium mode. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract ArrayContract { - uint[2**20] m_aLotOfIntegers; - // Note that the following is not a pair of dynamic arrays but a - // dynamic array of pairs (i.e. of fixed size arrays of length two). - // Because of that, T[] is always a dynamic array of T, even if T - // itself is an array. - // Data location for all state variables is storage. - bool[2][] m_pairsOfFlags; - - // newPairs is stored in memory - the only possibility - // for public contract function arguments - function setAllFlagPairs(bool[2][] memory newPairs) public { - // assignment to a storage array performs a copy of ``newPairs`` and - // replaces the complete array ``m_pairsOfFlags``. - m_pairsOfFlags = newPairs; - } - - struct StructType { - uint[] contents; - uint moreInfo; - } - StructType s; - - function f(uint[] memory c) public { - // stores a reference to ``s`` in ``g`` - StructType storage g = s; - // also changes ``s.moreInfo``. - g.moreInfo = 2; - // assigns a copy because ``g.contents`` - // is not a local variable, but a member of - // a local variable. - g.contents = c; - } - - function setFlagPair(uint index, bool flagA, bool flagB) public { - // access to a non-existing index will throw an exception - m_pairsOfFlags[index][0] = flagA; - m_pairsOfFlags[index][1] = flagB; - } - - function changeFlagArraySize(uint newSize) public { - // if the new size is smaller, removed array elements will be cleared - m_pairsOfFlags.length = newSize; - } - - function clear() public { - // these clear the arrays completely - delete m_pairsOfFlags; - delete m_aLotOfIntegers; - // identical effect here - m_pairsOfFlags.length = 0; - } - - bytes m_byteData; - - function byteArrays(bytes memory data) public { - // byte arrays ("bytes") are different as they are stored without padding, - // but can be treated identical to "uint8[]" - m_byteData = data; - m_byteData.length += 7; - m_byteData[3] = 0x08; - delete m_byteData[2]; - } - - function addFlag(bool[2] memory flag) public returns (uint) { - return m_pairsOfFlags.push(flag); - } - - function createMemoryArray(uint size) public pure returns (bytes memory) { - // Dynamic memory arrays are created using `new`: - uint[2][] memory arrayOfPairs = new uint[2][](size); - - // Inline arrays are always statically-sized and if you only - // use literals, you have to provide at least one type. - arrayOfPairs[0] = [uint(1), 2]; - - // Create a dynamic byte array: - bytes memory b = new bytes(200); - for (uint i = 0; i < b.length; i++) - b[i] = byte(uint8(i)); - return b; - } - } - - -.. index:: ! struct, ! type;struct - -.. _structs: - -Structs -------- - -Solidity provides a way to define new types in the form of structs, which is -shown in the following example: - -:: - - pragma solidity >=0.4.11 <0.6.0; - - contract CrowdFunding { - // Defines a new type with two fields. - struct Funder { - address addr; - uint amount; - } - - struct Campaign { - address payable beneficiary; - uint fundingGoal; - uint numFunders; - uint amount; - mapping (uint => Funder) funders; - } - - uint numCampaigns; - mapping (uint => Campaign) campaigns; - - function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { - campaignID = numCampaigns++; // campaignID is return variable - // Creates new struct in memory and copies it to storage. - // We leave out the mapping type, because it is not valid in memory. - // If structs are copied (even from storage to storage), mapping types - // are always omitted, because they cannot be enumerated. - campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); - } - - function contribute(uint campaignID) public payable { - Campaign storage c = campaigns[campaignID]; - // Creates a new temporary memory struct, initialised with the given values - // and copies it over to storage. - // Note that you can also use Funder(msg.sender, msg.value) to initialise. - c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); - c.amount += msg.value; - } - - function checkGoalReached(uint campaignID) public returns (bool reached) { - Campaign storage c = campaigns[campaignID]; - if (c.amount < c.fundingGoal) - return false; - uint amount = c.amount; - c.amount = 0; - c.beneficiary.transfer(amount); - return true; - } - } - -The contract does not provide the full functionality of a crowdfunding -contract, but it contains the basic concepts necessary to understand structs. -Struct types can be used inside mappings and arrays and they can itself -contain mappings and arrays. - -It is not possible for a struct to contain a member of its own type, -although the struct itself can be the value type of a mapping member -or it can contain a dynamically-sized array of its type. -This restriction is necessary, as the size of the struct has to be finite. - -Note how in all the functions, a struct type is assigned to a local variable -with data location ``storage``. -This does not copy the struct but only stores a reference so that assignments to -members of the local variable actually write to the state. - -Of course, you can also directly access the members of the struct without -assigning it to a local variable, as in -``campaigns[campaignID].amount = 0``. +.. include:: types/reference-types.rst .. index:: !mapping .. _mapping-types: diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst new file mode 100644 index 00000000..b133bdf1 --- /dev/null +++ b/docs/types/reference-types.rst @@ -0,0 +1,409 @@ +.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct + +.. _reference-types: + +Reference Types +=============== + +Values of reference type can be modified through multiple different names. +Contrast this with value types where you get an independent copy whenever +a variable of value type is used. Because of that, reference types have to be handled +more carefully than value types. Currently, reference types comprise structs, +arrays and mappings. If you use a reference type, you always have to explicitly +provide the data area where the type is stored: ``memory`` (whose lifetime is limited +to a function call), ``storage`` (the location where the state variables are stored) +or ``calldata`` (special data location that contains the function arguments, +only available for external function call parameters). + +An assignment or type conversion that changes the data location will always incur an automatic copy operation, +while assignments inside the same data location only copy in some cases for storage types. + +.. _data-location: + +Data location +------------- + +Every reference type, i.e. *arrays* and *structs*, has an additional +annotation, the "data location", about where it is stored. There are three data locations: +``memory``, ``storage`` and ``calldata``. Calldata is only valid for parameters of external contract +functions and is required for this type of parameter. Calldata is a non-modifiable, +non-persistent area where function arguments are stored, and behaves mostly like memory. + + +.. note:: + Prior to version 0.5.0 the data location could be omitted, and would default to different locations + depending on the kind of variable, function type, etc., but all complex types must now give an explicit + data location. + +.. _data-location-assignment: + +Data location and assignment behaviour +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Data locations are not only relevant for persistency of data, but also for the semantics of assignments: + +* Assignments between ``storage`` and ``memory`` (or from ``calldata``) always create an independent copy. +* Assignments from ``memory`` to ``memory`` only create references. This means that changes to one memory variable are also visible in all other memory variables that refer to the same data. +* Assignments from ``storage`` to a local storage variable also only assign a reference. +* All other assignments to ``storage`` always copy. Examples for this case are assignments to state variables or to members of local variables of storage struct type, even if the local variable itself is just a reference. + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract C { + uint[] x; // the data location of x is storage + + // the data location of memoryArray is memory + function f(uint[] memory memoryArray) public { + x = memoryArray; // works, copies the whole array to storage + uint[] storage y = x; // works, assigns a pointer, data location of y is storage + y[7]; // fine, returns the 8th element + y.length = 2; // fine, modifies x through y + delete x; // fine, clears the array, also modifies y + // The following does not work; it would need to create a new temporary / + // unnamed array in storage, but storage is "statically" allocated: + // y = memoryArray; + // This does not work either, since it would "reset" the pointer, but there + // is no sensible location it could point to. + // delete y; + g(x); // calls g, handing over a reference to x + h(x); // calls h and creates an independent, temporary copy in memory + } + + function g(uint[] storage) internal pure {} + function h(uint[] memory) public pure {} + } + +.. index:: ! array + +.. _arrays: + +Arrays +------ + +Arrays can have a compile-time fixed size, or they can have a dynamic size. + +The type of an array of fixed size ``k`` and element type ``T`` is written as ``T[k]``, +and an array of dynamic size as ``T[]``. + +For example, an array of 5 dynamic arrays of ``uint`` is written as +``uint[][5]``. The notation is reversed compared to some other languages. In +Solidity, ``X[3]`` is always an array containing three elements of type ``X``, +even if ``X`` is itself an array. This is not the case in other languages such +as C. + +Indices are zero-based, and access is in the opposite direction of the +declaration. + +For example, if you have a variable ``uint[][5] x memory``, you access the +second ``uint`` in the third dynamic array using ``x[2][1]``, and to access the +third dynamic array, use ``x[2]``. Again, +if you have an array ``T[5] a`` for a type ``T`` that can also be an array, +then ``a[2]`` always has type ``T``. + +Array elements can be of any type, including mapping or struct. The general +restrictions for types apply, in that mappings can only be stored in the +``storage`` data location and publicly-visible functions need parameters that are :ref:`ABI types `. + +Accessing an array past its end causes a failing assertion. You can use the ``.push()`` method to append a new element at the end or assign to the ``.length`` :ref:`member ` to change the size (see below for caveats). +method or increase the ``.length`` :ref:`member ` to add elements. + +Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, +but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow +length or index access. + +You should use ``bytes`` over ``byte[]`` because it is cheaper, since ``byte[]`` adds 31 padding bytes between the elements. As a general rule, +use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length +string (UTF-8) data. If you can limit the length to a certain number of bytes, +always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper. + +.. note:: + If you want to access the byte-representation of a string ``s``, use + ``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind + that you are accessing the low-level bytes of the UTF-8 representation, + and not the individual characters. + +It is possible to mark arrays ``public`` and have Solidity create a :ref:`getter `. +The numeric index becomes a required parameter for the getter. + +.. index:: ! array;allocating, new + +Allocating Memory Arrays +^^^^^^^^^^^^^^^^^^^^^^^^ + +You can use the ``new`` keyword to create arrays with a runtime-dependent length in memory. +As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to +the ``.length`` member). You either have to calculate the required size in advance +or create a new memory array and copy every element. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract C { + function f(uint len) public pure { + uint[] memory a = new uint[](7); + bytes memory b = new bytes(len); + assert(a.length == 7); + assert(b.length == len); + a[6] = 8; + } + } + +.. index:: ! array;literals, ! inline;arrays + +Array Literals +^^^^^^^^^^^^^^ + +An array literal is a comma-separated list of one or more expressions, enclosed +in square brackets (``[...]``). For example ``[1, a, f(3)]``. There must be a +common type all elements can be implicitly converted to. This is the elementary +type of the array. + +Array literals are always statically-sized memory arrays. + +In the example below, the type of ``[1, 2, 3]`` is +``uint8[3] memory``. Because the type of each of these constants is ``uint8``, if you want the result to be a ``uint[3] memory`` type, you need to convert the first element to ``uint``. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract C { + function f() public pure { + g([uint(1), 2, 3]); + } + function g(uint[3] memory) public pure { + // ... + } + } + +Fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible: + +:: + + pragma solidity >=0.4.0 <0.6.0; + + // This will not compile. + contract C { + function f() public { + // The next line creates a type error because uint[3] memory + // cannot be converted to uint[] memory. + uint[] memory x = [uint(1), 3, 4]; + } + } + +It is planned to remove this restriction in the future, but it creates some +complications because of how arrays are passed in the ABI. + +.. index:: ! array;length, length, push, pop, !array;push, !array;pop + +.. _array-members: + +Array Members +^^^^^^^^^^^^^ + +**length**: + Arrays have a ``length`` member that contains their number of elements. + The length of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. + For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array. + Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion. + Increasing the length adds new zero-initialised elements to the array. + Reducing the length performs an implicit :ref:``delete`` on each of the + removed elements. If you try to resize a non-dynamic array that isn't in + storage, you receive a ``Value must be an lvalue`` error. +**push**: + Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length. +**pop**: + Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that you can use to remove an element from the end of the array. This also implicitly calls :ref:``delete`` on the removed element. + +.. warning:: + If you use ``.length--`` on an empty array, it causes an underflow and + thus sets the length to ``2**256-1``. + +.. note:: + Increasing the length of a storage array has constant gas costs because + storage is assumed to be zero-initialised, while decreasing + the length has at least linear cost (but in most cases worse than linear), + because it includes explicitly clearing the removed + elements similar to calling :ref:``delete`` on them. + +.. note:: + It is not yet possible to use arrays of arrays in external functions + (but they are supported in public functions). + +.. note:: + In EVM versions before Byzantium, it was not possible to access + dynamic arrays return from function calls. If you call functions + that return dynamic arrays, make sure to use an EVM that is set to + Byzantium mode. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract ArrayContract { + uint[2**20] m_aLotOfIntegers; + // Note that the following is not a pair of dynamic arrays but a + // dynamic array of pairs (i.e. of fixed size arrays of length two). + // Because of that, T[] is always a dynamic array of T, even if T + // itself is an array. + // Data location for all state variables is storage. + bool[2][] m_pairsOfFlags; + + // newPairs is stored in memory - the only possibility + // for public contract function arguments + function setAllFlagPairs(bool[2][] memory newPairs) public { + // assignment to a storage array performs a copy of ``newPairs`` and + // replaces the complete array ``m_pairsOfFlags``. + m_pairsOfFlags = newPairs; + } + + struct StructType { + uint[] contents; + uint moreInfo; + } + StructType s; + + function f(uint[] memory c) public { + // stores a reference to ``s`` in ``g`` + StructType storage g = s; + // also changes ``s.moreInfo``. + g.moreInfo = 2; + // assigns a copy because ``g.contents`` + // is not a local variable, but a member of + // a local variable. + g.contents = c; + } + + function setFlagPair(uint index, bool flagA, bool flagB) public { + // access to a non-existing index will throw an exception + m_pairsOfFlags[index][0] = flagA; + m_pairsOfFlags[index][1] = flagB; + } + + function changeFlagArraySize(uint newSize) public { + // if the new size is smaller, removed array elements will be cleared + m_pairsOfFlags.length = newSize; + } + + function clear() public { + // these clear the arrays completely + delete m_pairsOfFlags; + delete m_aLotOfIntegers; + // identical effect here + m_pairsOfFlags.length = 0; + } + + bytes m_byteData; + + function byteArrays(bytes memory data) public { + // byte arrays ("bytes") are different as they are stored without padding, + // but can be treated identical to "uint8[]" + m_byteData = data; + m_byteData.length += 7; + m_byteData[3] = 0x08; + delete m_byteData[2]; + } + + function addFlag(bool[2] memory flag) public returns (uint) { + return m_pairsOfFlags.push(flag); + } + + function createMemoryArray(uint size) public pure returns (bytes memory) { + // Dynamic memory arrays are created using `new`: + uint[2][] memory arrayOfPairs = new uint[2][](size); + + // Inline arrays are always statically-sized and if you only + // use literals, you have to provide at least one type. + arrayOfPairs[0] = [uint(1), 2]; + + // Create a dynamic byte array: + bytes memory b = new bytes(200); + for (uint i = 0; i < b.length; i++) + b[i] = byte(uint8(i)); + return b; + } + } + + +.. index:: ! struct, ! type;struct + +.. _structs: + +Structs +------- + +Solidity provides a way to define new types in the form of structs, which is +shown in the following example: + +:: + + pragma solidity >=0.4.11 <0.6.0; + + contract CrowdFunding { + // Defines a new type with two fields. + struct Funder { + address addr; + uint amount; + } + + struct Campaign { + address payable beneficiary; + uint fundingGoal; + uint numFunders; + uint amount; + mapping (uint => Funder) funders; + } + + uint numCampaigns; + mapping (uint => Campaign) campaigns; + + function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { + campaignID = numCampaigns++; // campaignID is return variable + // Creates new struct in memory and copies it to storage. + // We leave out the mapping type, because it is not valid in memory. + // If structs are copied (even from storage to storage), mapping types + // are always omitted, because they cannot be enumerated. + campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); + } + + function contribute(uint campaignID) public payable { + Campaign storage c = campaigns[campaignID]; + // Creates a new temporary memory struct, initialised with the given values + // and copies it over to storage. + // Note that you can also use Funder(msg.sender, msg.value) to initialise. + c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); + c.amount += msg.value; + } + + function checkGoalReached(uint campaignID) public returns (bool reached) { + Campaign storage c = campaigns[campaignID]; + if (c.amount < c.fundingGoal) + return false; + uint amount = c.amount; + c.amount = 0; + c.beneficiary.transfer(amount); + return true; + } + } + +The contract does not provide the full functionality of a crowdfunding +contract, but it contains the basic concepts necessary to understand structs. +Struct types can be used inside mappings and arrays and they can itself +contain mappings and arrays. + +It is not possible for a struct to contain a member of its own type, +although the struct itself can be the value type of a mapping member +or it can contain a dynamically-sized array of its type. +This restriction is necessary, as the size of the struct has to be finite. + +Note how in all the functions, a struct type is assigned to a local variable +with data location ``storage``. +This does not copy the struct but only stores a reference so that assignments to +members of the local variable actually write to the state. + +Of course, you can also directly access the members of the struct without +assigning it to a local variable, as in +``campaigns[campaignID].amount = 0``. -- cgit From 4cc102fa61116036e0d7c811f081deecbaaf7cba Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 9 Jan 2019 12:37:20 +0100 Subject: Split operators involving LValues into new doc --- docs/conf.py | 2 +- docs/types.rst | 48 +----------------------------------------------- docs/types/operators.rst | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 48 deletions(-) create mode 100644 docs/types/operators.rst diff --git a/docs/conf.py b/docs/conf.py index 342aefa9..d08a5191 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -81,7 +81,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts'] +exclude_patterns = ['_build', 'contracts', 'types'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/types.rst b/docs/types.rst index 4eb1f926..61b0afc0 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -1196,53 +1196,7 @@ each ``_KeyType``, recursively. For example with a mapping: Mappings are not iterable, but it is possible to implement a data structure on top of them. For an example, see `iterable mapping `_. -.. index:: assignment, ! delete, lvalue - -Operators Involving LValues -=========================== - -If ``a`` is an LValue (i.e. a variable or something that can be assigned to), the following operators are available as shorthands: - -``a += e`` is equivalent to ``a = a + e``. The operators ``-=``, ``*=``, ``/=``, ``%=``, ``|=``, ``&=`` and ``^=`` are defined accordingly. ``a++`` and ``a--`` are equivalent to ``a += 1`` / ``a -= 1`` but the expression itself still has the previous value of ``a``. In contrast, ``--a`` and ``++a`` have the same effect on ``a`` but return the value after the change. - -delete ------- - -``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is -equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic -array of length zero or a static array of the same length with all elements set to their -initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves -all other elements and the length of the array untouched. This especially means that it leaves -a gap in the array. If you plan to remove items, a mapping is probably a better choice. - -For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat: - -``delete`` has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted: If ``a`` is a mapping, then ``delete a[x]`` will delete the value stored at ``x``. - -It is important to note that ``delete a`` really behaves like an assignment to ``a``, i.e. it stores a new object in ``a``. -This distinction is visible when ``a`` is reference variable: It will only reset ``a`` itself, not the -value it referred to previously. - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract DeleteExample { - uint data; - uint[] dataArray; - - function f() public { - uint x = data; - delete x; // sets x to 0, does not affect data - delete data; // sets data to 0, does not affect x - uint[] storage y = dataArray; - delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also - // y is affected which is an alias to the storage object - // On the other hand: "delete y" is not valid, as assignments to local variables - // referencing storage objects can only be made from existing storage objects. - assert(y.length == 0); - } - } +.. include:: types/operators.rst .. index:: ! type;conversion, ! cast diff --git a/docs/types/operators.rst b/docs/types/operators.rst new file mode 100644 index 00000000..9851f214 --- /dev/null +++ b/docs/types/operators.rst @@ -0,0 +1,47 @@ +.. index:: assignment, ! delete, lvalue + +Operators Involving LValues +=========================== + +If ``a`` is an LValue (i.e. a variable or something that can be assigned to), the following operators are available as shorthands: + +``a += e`` is equivalent to ``a = a + e``. The operators ``-=``, ``*=``, ``/=``, ``%=``, ``|=``, ``&=`` and ``^=`` are defined accordingly. ``a++`` and ``a--`` are equivalent to ``a += 1`` / ``a -= 1`` but the expression itself still has the previous value of ``a``. In contrast, ``--a`` and ``++a`` have the same effect on ``a`` but return the value after the change. + +delete +------ + +``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is +equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic +array of length zero or a static array of the same length with all elements set to their +initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves +all other elements and the length of the array untouched. This especially means that it leaves +a gap in the array. If you plan to remove items, a mapping is probably a better choice. + +For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat: + +``delete`` has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted: If ``a`` is a mapping, then ``delete a[x]`` will delete the value stored at ``x``. + +It is important to note that ``delete a`` really behaves like an assignment to ``a``, i.e. it stores a new object in ``a``. +This distinction is visible when ``a`` is reference variable: It will only reset ``a`` itself, not the +value it referred to previously. + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract DeleteExample { + uint data; + uint[] dataArray; + + function f() public { + uint x = data; + delete x; // sets x to 0, does not affect data + delete data; // sets data to 0, does not affect x + uint[] storage y = dataArray; + delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also + // y is affected which is an alias to the storage object + // On the other hand: "delete y" is not valid, as assignments to local variables + // referencing storage objects can only be made from existing storage objects. + assert(y.length == 0); + } + } -- cgit From edda79eec546674c8c1f0128c63939921f4ff1f2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 9 Jan 2019 16:50:41 +0100 Subject: Variables are free with regards to code size. --- libyul/optimiser/Metrics.cpp | 16 ++++-- libyul/optimiser/Metrics.h | 9 ++++ test/libyul/Metrics.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 test/libyul/Metrics.cpp diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index a351f1d1..fb275cc3 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -25,6 +25,9 @@ #include +#include + +using namespace std; using namespace dev; using namespace yul; @@ -45,7 +48,7 @@ size_t CodeSize::codeSize(Expression const& _expression) size_t CodeSize::codeSize(Block const& _block) { CodeSize cs; - cs(_block); + cs.visit(_block); return cs.m_size; } @@ -53,14 +56,21 @@ void CodeSize::visit(Statement const& _statement) { if (_statement.type() == typeid(FunctionDefinition)) return; + else if (!( + _statement.type() == typeid(Block) || + _statement.type() == typeid(ExpressionStatement) || + _statement.type() == typeid(Assignment) || + _statement.type() == typeid(VariableDeclaration) + )) + ++m_size; - ++m_size; ASTWalker::visit(_statement); } void CodeSize::visit(Expression const& _expression) { - ++m_size; + if (_expression.type() != typeid(Identifier)) + ++m_size; ASTWalker::visit(_expression); } diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h index d8a1b279..03e1b62a 100644 --- a/libyul/optimiser/Metrics.h +++ b/libyul/optimiser/Metrics.h @@ -30,6 +30,13 @@ namespace yul * More specifically, the number of AST nodes. * Ignores function definitions while traversing the AST. * If you want to know the size of a function, you have to invoke this on its body. + * + * As an exception, the following AST elements have a cost of zero: + * - expression statement (only the expression inside has a cost) + * - block (only the statements inside have a cost) + * - variable references + * - variable declarations (only the right hand side has a cost) + * - assignments (only the value has a cost) */ class CodeSize: public ASTWalker { @@ -39,6 +46,8 @@ public: static size_t codeSize(Block const& _block); private: + CodeSize() {} + void visit(Statement const& _statement) override; void visit(Expression const& _expression) override; diff --git a/test/libyul/Metrics.cpp b/test/libyul/Metrics.cpp new file mode 100644 index 00000000..185a3755 --- /dev/null +++ b/test/libyul/Metrics.cpp @@ -0,0 +1,116 @@ +/* + 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 . +*/ +/** + * Unit tests for the code metrics. + */ + +#include + +#include + +#include +#include + + +using namespace std; +using namespace langutil; + +namespace yul +{ +namespace test +{ + +namespace +{ + +size_t codeSize(string const& _source) +{ + shared_ptr ast = parse(_source, false).first; + BOOST_REQUIRE(ast); + return CodeSize::codeSize(*ast); +} + +} + +BOOST_AUTO_TEST_SUITE(YulCodeSize) + +BOOST_AUTO_TEST_CASE(empty_code) +{ + BOOST_CHECK_EQUAL(codeSize("{}"), 0); +} + +BOOST_AUTO_TEST_CASE(nested_blocks) +{ + BOOST_CHECK_EQUAL(codeSize("{ {} {} {{ }} }"), 0); +} + +BOOST_AUTO_TEST_CASE(instruction) +{ + BOOST_CHECK_EQUAL(codeSize("{ pop(calldatasize()) }"), 2); +} + +BOOST_AUTO_TEST_CASE(variables_are_free) +{ + BOOST_CHECK_EQUAL(codeSize("{ let x let y let a, b, c }"), 0); +} + +BOOST_AUTO_TEST_CASE(constants_cost_one) +{ + BOOST_CHECK_EQUAL(codeSize("{ let x := 3 }"), 1); +} + +BOOST_AUTO_TEST_CASE(functions_are_skipped) +{ + BOOST_CHECK_EQUAL(codeSize("{ function f(x) -> r { r := mload(x) } }"), 0); +} + +BOOST_AUTO_TEST_CASE(function_with_arguments) +{ + BOOST_CHECK_EQUAL(codeSize("{ function f(x) { sstore(x, 2) } f(2) }"), 2); +} + +BOOST_AUTO_TEST_CASE(function_with_variables_as_arguments) +{ + BOOST_CHECK_EQUAL(codeSize("{ function f(x) { sstore(x, 2) } let y f(y) }"), 1); +} + +BOOST_AUTO_TEST_CASE(function_with_variables_and_constants_as_arguments) +{ + BOOST_CHECK_EQUAL(codeSize( + "{ function f(x, r) -> z { sstore(x, r) z := r } let y let t := f(y, 2) }" + ), 2); +} + +BOOST_AUTO_TEST_CASE(assignment) +{ + BOOST_CHECK_EQUAL(codeSize("{ let a a := 3 }"), 1); +} + +BOOST_AUTO_TEST_CASE(assignments_between_vars_are_free) +{ + BOOST_CHECK_EQUAL(codeSize("{ let a let b := a a := b }"), 0); +} + +BOOST_AUTO_TEST_CASE(assignment_complex) +{ + BOOST_CHECK_EQUAL(codeSize("{ let a let x := mload(a) a := sload(x) }"), 2); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} -- cgit From d9c677366414f926a58ce644670a6006b8cfcb79 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 9 Jan 2019 16:51:09 +0100 Subject: Adjust inlining thresholds. --- libyul/optimiser/FullInliner.cpp | 14 +++++++++----- libyul/optimiser/FullInliner.h | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 95360dc3..1f267f96 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -80,9 +80,9 @@ void FullInliner::run() } } -void FullInliner::updateCodeSize(FunctionDefinition& fun) +void FullInliner::updateCodeSize(FunctionDefinition const& _fun) { - m_functionSizes[fun.name] = CodeSize::codeSize(fun.body); + m_functionSizes[_fun.name] = CodeSize::codeSize(_fun.body); } void FullInliner::handleBlock(YulString _currentFunctionName, Block& _block) @@ -100,8 +100,13 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) if (!calledFunction) return false; + // Inline really, really tiny functions + size_t size = m_functionSizes.at(calledFunction->name); + if (size <= 1) + return true; + // Do not inline into already big functions. - if (m_functionSizes.at(_callSite) > 100) + if (m_functionSizes.at(_callSite) > 45) return false; if (m_singleUse.count(calledFunction->name)) @@ -119,8 +124,7 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) break; } - size_t size = m_functionSizes.at(calledFunction->name); - return (size < 10 || (constantArg && size < 30)); + return (size < 6 || (constantArg && size < 12)); } void FullInliner::tentativelyUpdateCodeSize(YulString _function, YulString _callSite) diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index d2dd3229..32664c96 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -91,7 +91,7 @@ public: void tentativelyUpdateCodeSize(YulString _function, YulString _callSite); private: - void updateCodeSize(FunctionDefinition& fun); + void updateCodeSize(FunctionDefinition const& _fun); void handleBlock(YulString _currentFunctionName, Block& _block); /// The AST to be modified. The root block itself will not be modified, because -- cgit From b7ef8509554768de04d1cf7d11b122e98ab43ae8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 9 Jan 2019 16:51:23 +0100 Subject: Update tests. --- .../fullInliner/large_function_multi_use.yul | 18 +- .../fullInliner/no_inline_into_big_function.yul | 20 ++- .../no_inline_into_big_global_context.yul | 20 ++- test/libyul/yulOptimizerTests/fullSuite/abi2.yul | 73 +++----- .../yulOptimizerTests/fullSuite/abi_example1.yul | 198 ++++++++++----------- 5 files changed, 174 insertions(+), 155 deletions(-) diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul index c00b1163..3a7ee2f3 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul @@ -21,22 +21,28 @@ // { // let a_1 := mload(2) // let a2 := 2 -// let r := f(a_1) -// let f_a := a2 +// let f_a := a_1 // let f_b := 0 // let f_x := mload(f_a) // f_b := sload(f_x) // let f_y := add(f_a, f_x) // sstore(f_y, 10) -// let t := f_b -// let a3 -// let f_a_3 := a3 +// let r := f_b +// let f_a_3 := a2 // let f_b_4 := 0 // let f_x_5 := mload(f_a_3) // f_b_4 := sload(f_x_5) // let f_y_6 := add(f_a_3, f_x_5) // sstore(f_y_6, 10) -// let s := f_b_4 +// let t := f_b_4 +// let a3 +// let f_a_8 := a3 +// let f_b_9 := 0 +// let f_x_10 := mload(f_a_8) +// f_b_9 := sload(f_x_10) +// let f_y_11 := add(f_a_8, f_x_10) +// sstore(f_y_11, 10) +// let s := f_b_9 // } // function f(a) -> b // { diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul index f59e2c11..58922954 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul @@ -39,6 +39,24 @@ // let f_a_35 := f_b_33 // let f_b_36 := 0 // f_b_36 := sload(mload(f_a_35)) -// x_1 := f(f(f(f(f(f(f(f(f(f(f(f(f_b_36)))))))))))) +// let f_a_38 := f_b_36 +// let f_b_39 := 0 +// f_b_39 := sload(mload(f_a_38)) +// let f_a_41 := f_b_39 +// let f_b_42 := 0 +// f_b_42 := sload(mload(f_a_41)) +// let f_a_44 := f_b_42 +// let f_b_45 := 0 +// f_b_45 := sload(mload(f_a_44)) +// let f_a_47 := f_b_45 +// let f_b_48 := 0 +// f_b_48 := sload(mload(f_a_47)) +// let f_a_50 := f_b_48 +// let f_b_51 := 0 +// f_b_51 := sload(mload(f_a_50)) +// let f_a_53 := f_b_51 +// let f_b_54 := 0 +// f_b_54 := sload(mload(f_a_53)) +// x_1 := f(f(f(f(f(f(f_b_54)))))) // } // } diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul index f20b7221..3dc27b2d 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul @@ -32,7 +32,25 @@ // let f_a_35 := f_b_33 // let f_b_36 := 0 // f_b_36 := sload(mload(f_a_35)) -// let x_1 := f(f(f(f(f(f(f(f(f(f(f(f(f_b_36)))))))))))) +// let f_a_38 := f_b_36 +// let f_b_39 := 0 +// f_b_39 := sload(mload(f_a_38)) +// let f_a_41 := f_b_39 +// let f_b_42 := 0 +// f_b_42 := sload(mload(f_a_41)) +// let f_a_44 := f_b_42 +// let f_b_45 := 0 +// f_b_45 := sload(mload(f_a_44)) +// let f_a_47 := f_b_45 +// let f_b_48 := 0 +// f_b_48 := sload(mload(f_a_47)) +// let f_a_50 := f_b_48 +// let f_b_51 := 0 +// f_b_51 := sload(mload(f_a_50)) +// let f_a_53 := f_b_51 +// let f_b_54 := 0 +// f_b_54 := sload(mload(f_a_53)) +// let x_1 := f(f(f(f(f(f(f_b_54)))))) // } // function f(a) -> b // { diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index fc9b44a3..bb60b1fa 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1072,8 +1072,13 @@ // ---- // fullSuite // { -// let a, b := abi_decode_tuple_t_contract$_Module_$1038t_contract$_Module_$1038(mload(0), mload(1)) -// sstore(0, a) +// let _2 := mload(1) +// let _1042 := mload(0) +// if slt(sub(_2, _1042), 64) +// { +// revert(0, 0) +// } +// sstore(0, and(calldataload(_1042), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) // let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) // sstore(x1, x0) // sstore(x3, x2) @@ -1109,59 +1114,31 @@ // } // value2_59 := add(_1092, 0x20) // value3 := abi_decode_length_15_689 -// value4 := cleanup_revert_t_enum$_Operation_$1949(calldataload(add(headStart_55, 96))) -// } -// function abi_decode_tuple_t_contract$_Module_$1038t_contract$_Module_$1038(headStart_154, dataEnd_155) -> value0_156, value1_157 -// { -// if slt(sub(dataEnd_155, headStart_154), 64) +// let _1095 := calldataload(add(headStart_55, 96)) +// if iszero(lt(_1095, 3)) // { -// revert(0, 0) -// } -// let cleanup_as__1127 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// value0_156 := and(calldataload(add(headStart_154, 0)), cleanup_as__1127) -// value1_157 := and(calldataload(add(headStart_154, 32)), cleanup_as__1127) -// } -// function abi_encode_t_address_to_t_address(value_164, pos) -// { -// mstore(pos, and(value_164, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) -// } -// function abi_encode_t_bytes32_to_t_bytes32(value_171, pos_172) -// { -// mstore(pos_172, value_171) -// } -// function abi_encode_t_enum$_Operation_$1949_to_t_uint8(value_181, pos_182) -// { -// if iszero(lt(value_181, 3)) -// { -// invalid() +// revert(value4, value4) // } -// mstore(pos_182, value_181) -// } -// function abi_encode_t_uint256_to_t_uint256(value_235, pos_236) -// { -// mstore(pos_236, value_235) +// value4 := _1095 // } // function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart_252, value10_253, value9_254, value8_255, value7_256, value6_257, value5_258, value4_259, value3_260, value2_261, value1_262, value0_263) -> tail_264 // { // tail_264 := add(headStart_252, 352) -// abi_encode_t_bytes32_to_t_bytes32(value0_263, headStart_252) -// abi_encode_t_address_to_t_address(value1_262, add(headStart_252, 32)) -// abi_encode_t_uint256_to_t_uint256(value2_261, add(headStart_252, 64)) -// abi_encode_t_bytes32_to_t_bytes32(value3_260, add(headStart_252, 96)) -// abi_encode_t_enum$_Operation_$1949_to_t_uint8(value4_259, add(headStart_252, 128)) -// abi_encode_t_uint256_to_t_uint256(value5_258, add(headStart_252, 160)) -// abi_encode_t_uint256_to_t_uint256(value6_257, add(headStart_252, 192)) -// abi_encode_t_uint256_to_t_uint256(value7_256, add(headStart_252, 224)) -// abi_encode_t_address_to_t_address(value8_255, add(headStart_252, 256)) -// abi_encode_t_address_to_t_address(value9_254, add(headStart_252, 288)) -// abi_encode_t_uint256_to_t_uint256(value10_253, add(headStart_252, 320)) -// } -// function cleanup_revert_t_enum$_Operation_$1949(value_369) -> cleaned_370 -// { -// if iszero(lt(value_369, 3)) +// mstore(headStart_252, value0_263) +// let _1307 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// mstore(add(headStart_252, 32), and(value1_262, _1307)) +// mstore(add(headStart_252, 64), value2_261) +// mstore(add(headStart_252, 96), value3_260) +// if iszero(lt(value4_259, 3)) // { -// revert(0, 0) +// invalid() // } -// cleaned_370 := value_369 +// mstore(add(headStart_252, 128), value4_259) +// mstore(add(headStart_252, 160), value5_258) +// mstore(add(headStart_252, 192), value6_257) +// mstore(add(headStart_252, 224), value7_256) +// mstore(add(headStart_252, 256), and(value8_255, _1307)) +// mstore(add(headStart_252, 288), and(value9_254, _1307)) +// mstore(add(headStart_252, 320), value10_253) // } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index c8acf465..c909d39e 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -458,14 +458,14 @@ // ---- // fullSuite // { -// let _1 := 0x20 -// let _485 := mload(0) -// let abi_encode_pos := _1 +// let _2 := 0 +// let _485 := mload(_2) +// let abi_encode_pos := 0x20 // let abi_encode_length_68 := mload(_485) -// mstore(_1, abi_encode_length_68) +// mstore(0x20, abi_encode_length_68) // abi_encode_pos := 64 -// let abi_encode_srcPtr := add(_485, _1) -// let abi_encode_i_69 := 0 +// let abi_encode_srcPtr := add(_485, 0x20) +// let abi_encode_i_69 := _2 // for { // } // lt(abi_encode_i_69, abi_encode_length_68) @@ -473,28 +473,32 @@ // abi_encode_i_69 := add(abi_encode_i_69, 1) // } // { -// let _874 := mload(abi_encode_srcPtr) -// let abi_encode_pos_71_978 := abi_encode_pos -// let abi_encode_srcPtr_73_980 := _874 -// let abi_encode_i_74_981 := 0 -// for { -// } -// lt(abi_encode_i_74_981, 0x3) -// { -// abi_encode_i_74_981 := add(abi_encode_i_74_981, 1) -// } -// { -// mstore(abi_encode_pos_71_978, and(mload(abi_encode_srcPtr_73_980), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) -// abi_encode_srcPtr_73_980 := add(abi_encode_srcPtr_73_980, _1) -// abi_encode_pos_71_978 := add(abi_encode_pos_71_978, _1) -// } -// abi_encode_srcPtr := add(abi_encode_srcPtr, _1) +// abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(abi_encode_srcPtr), abi_encode_pos) +// abi_encode_srcPtr := add(abi_encode_srcPtr, 0x20) // abi_encode_pos := add(abi_encode_pos, 0x60) // } -// let a, b, c, d := abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(mload(_1), mload(0x40)) -// sstore(a, b) -// sstore(c, d) -// sstore(0, abi_encode_pos) +// let _487 := mload(0x40) +// let _488 := mload(0x20) +// if slt(sub(_487, _488), 128) +// { +// revert(_2, _2) +// } +// let abi_decode_offset_64 := calldataload(add(_488, 64)) +// let abi_decode__165 := 0xffffffffffffffff +// if gt(abi_decode_offset_64, abi_decode__165) +// { +// revert(_2, _2) +// } +// let abi_decode_value2_587 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_488, abi_decode_offset_64), _487) +// let abi_decode_offset_65 := calldataload(add(_488, 96)) +// if gt(abi_decode_offset_65, abi_decode__165) +// { +// revert(_2, _2) +// } +// let abi_decode_value3_588 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_488, abi_decode_offset_65), _487) +// sstore(calldataload(_488), calldataload(add(_488, 32))) +// sstore(abi_decode_value2_587, abi_decode_value3_588) +// sstore(_2, abi_encode_pos) // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5 // { // if iszero(slt(add(offset_3, 0x1f), end_4)) @@ -502,17 +506,23 @@ // revert(0, 0) // } // let length_6 := calldataload(offset_3) -// let array_5_254 := allocateMemory(array_allocation_size_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(length_6)) +// if gt(length_6, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// let array_allo__217 := 0x20 +// let array_5_254 := allocateMemory(add(mul(length_6, array_allo__217), array_allo__217)) // array_5 := array_5_254 // let dst_7 := array_5_254 // mstore(array_5_254, length_6) -// dst_7 := add(array_5_254, 0x20) -// let src_8 := add(offset_3, 0x20) -// if gt(add(add(offset_3, mul(length_6, 0x40)), 0x20), end_4) +// dst_7 := add(array_5_254, array_allo__217) +// let src_8 := add(offset_3, array_allo__217) +// if gt(add(add(offset_3, mul(length_6, 0x40)), array_allo__217), end_4) // { // revert(0, 0) // } -// let i_9 := 0 +// let i_9_566 := 0 +// let i_9 := i_9_566 // for { // } // lt(i_9, length_6) @@ -520,87 +530,85 @@ // i_9 := add(i_9, 1) // } // { -// mstore(dst_7, abi_decode_t_array$_t_uint256_$2_memory(src_8, end_4)) -// dst_7 := add(dst_7, 0x20) +// if iszero(slt(add(src_8, 0x1f), end_4)) +// { +// revert(i_9_566, i_9_566) +// } +// let abi_decode_array_13_263 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) +// let abi_decode_dst_15 := abi_decode_array_13_263 +// let abi_decode_src_16 := src_8 +// if gt(add(src_8, 64), end_4) +// { +// revert(i_9_566, i_9_566) +// } +// let abi_decode_i_17 := i_9_566 +// for { +// } +// lt(abi_decode_i_17, 0x2) +// { +// abi_decode_i_17 := add(abi_decode_i_17, 1) +// } +// { +// mstore(abi_decode_dst_15, calldataload(abi_decode_src_16)) +// abi_decode_dst_15 := add(abi_decode_dst_15, array_allo__217) +// abi_decode_src_16 := add(abi_decode_src_16, array_allo__217) +// } +// mstore(dst_7, abi_decode_array_13_263) +// dst_7 := add(dst_7, array_allo__217) // src_8 := add(src_8, 0x40) // } // } -// function abi_decode_t_array$_t_uint256_$2_memory(offset_11, end_12) -> array_13 +// function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29 // { -// if iszero(slt(add(offset_11, 0x1f), end_12)) +// if iszero(slt(add(offset_27, 0x1f), end_28)) +// { +// revert(0, 0) +// } +// let length_30 := calldataload(offset_27) +// if gt(length_30, 0xffffffffffffffff) // { // revert(0, 0) // } -// let array_13_263 := allocateMemory(64) -// array_13 := array_13_263 -// let dst_15 := array_13_263 -// let src_16 := offset_11 -// if gt(add(offset_11, 64), end_12) +// let array_allo__234 := 0x20 +// let array_allo__560 := mul(length_30, array_allo__234) +// let array_29_279 := allocateMemory(add(array_allo__560, array_allo__234)) +// array_29 := array_29_279 +// let dst_31 := array_29_279 +// mstore(array_29_279, length_30) +// dst_31 := add(array_29_279, array_allo__234) +// let src_32 := add(offset_27, array_allo__234) +// if gt(add(add(offset_27, array_allo__560), array_allo__234), end_28) // { // revert(0, 0) // } -// let i_17 := 0 +// let i_33 := 0 // for { // } -// lt(i_17, 0x2) +// lt(i_33, length_30) // { -// i_17 := add(i_17, 1) +// i_33 := add(i_33, 1) // } // { -// mstore(dst_15, calldataload(src_16)) -// dst_15 := add(dst_15, 0x20) -// src_16 := add(src_16, 0x20) +// mstore(dst_31, calldataload(src_32)) +// dst_31 := add(dst_31, array_allo__234) +// src_32 := add(src_32, array_allo__234) // } // } -// function abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(headStart_58, dataEnd_59) -> value0_60, value1_61, value2, value3 +// function abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(value_70, pos_71) // { -// if slt(sub(dataEnd_59, headStart_58), 128) -// { -// revert(value2, value2) -// } -// value0_60 := calldataload(add(headStart_58, value2)) -// value1_61 := calldataload(add(headStart_58, 32)) -// let offset_64 := calldataload(add(headStart_58, 64)) -// let _165 := 0xffffffffffffffff -// if gt(offset_64, _165) -// { -// revert(value2, value2) -// } -// let _532 := add(headStart_58, offset_64) -// if iszero(slt(add(_532, 0x1f), dataEnd_59)) -// { -// revert(value2, value2) -// } -// let abi_decode_length_30 := calldataload(_532) -// let abi_decode_array_29_279 := allocateMemory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(abi_decode_length_30)) -// let abi_decode_dst_31 := abi_decode_array_29_279 -// mstore(abi_decode_array_29_279, abi_decode_length_30) -// let abi_decode__91 := 0x20 -// abi_decode_dst_31 := add(abi_decode_array_29_279, abi_decode__91) -// let abi_decode_src_32 := add(_532, abi_decode__91) -// if gt(add(add(_532, mul(abi_decode_length_30, abi_decode__91)), abi_decode__91), dataEnd_59) -// { -// revert(value2, value2) -// } -// let abi_decode_i_33 := value2 +// let srcPtr_73 := value_70 +// let i_74 := 0 // for { // } -// lt(abi_decode_i_33, abi_decode_length_30) +// lt(i_74, 0x3) // { -// abi_decode_i_33 := add(abi_decode_i_33, 1) +// i_74 := add(i_74, 1) // } // { -// mstore(abi_decode_dst_31, calldataload(abi_decode_src_32)) -// abi_decode_dst_31 := add(abi_decode_dst_31, abi_decode__91) -// abi_decode_src_32 := add(abi_decode_src_32, abi_decode__91) +// mstore(pos_71, and(mload(srcPtr_73), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// srcPtr_73 := add(srcPtr_73, 0x20) +// pos_71 := add(pos_71, 0x20) // } -// value2 := abi_decode_array_29_279 -// let offset_65 := calldataload(add(headStart_58, 96)) -// if gt(offset_65, _165) -// { -// revert(0, 0) -// } -// value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(headStart_58, offset_65), dataEnd_59) // } // function allocateMemory(size) -> memPtr // { @@ -613,20 +621,12 @@ // } // mstore(64, newFreePtr) // } -// function array_allocation_size_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(length_92) -> size_93 -// { -// if gt(length_92, 0xffffffffffffffff) -// { -// revert(0, 0) -// } -// size_93 := add(mul(length_92, 0x20), 0x20) -// } -// function array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length_98) -> size_99 +// function array_allocation_size_t_array$_t_uint256_$2_memory(length_94) -> size_95 // { -// if gt(length_98, 0xffffffffffffffff) +// if gt(length_94, 0xffffffffffffffff) // { // revert(0, 0) // } -// size_99 := add(mul(length_98, 0x20), 0x20) +// size_95 := mul(length_94, 0x20) // } // } -- cgit From 0dfd4a726eb7e6fa8a5e886a0d80bb5bf3d9b7dc Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 8 Jan 2019 19:33:46 +0100 Subject: Warn about unreachable code. --- Changelog.md | 1 + libdevcore/Algorithms.h | 43 ++++++++++++++++++++++ liblangutil/SourceLocation.h | 20 ++++++++++ libsolidity/analysis/ControlFlowAnalyzer.cpp | 34 +++++++++++++++++ libsolidity/analysis/ControlFlowAnalyzer.h | 3 ++ libsolidity/analysis/ControlFlowBuilder.cpp | 37 +++++++++++++++---- libsolidity/analysis/ControlFlowBuilder.h | 8 ++-- libsolidity/analysis/ControlFlowGraph.h | 3 ++ .../controlFlow/storageReturn/dowhile_err.sol | 4 ++ .../controlFlow/storageReturn/dowhile_fine.sol | 1 + .../uninitializedAccess/always_revert.sol | 4 +- .../uninitializedAccess/unreachable.sol | 1 + .../controlFlow/unreachableCode/comment_fine.sol | 6 +++ .../unreachableCode/constant_condition.sol | 8 ++++ .../unreachableCode/do_while_continue.sol | 12 ++++++ .../controlFlow/unreachableCode/double_return.sol | 8 ++++ .../controlFlow/unreachableCode/double_revert.sol | 8 ++++ .../controlFlow/unreachableCode/for_break.sol | 12 ++++++ .../controlFlow/unreachableCode/if_both_return.sol | 12 ++++++ .../controlFlow/unreachableCode/revert.sol | 8 ++++ .../controlFlow/unreachableCode/revert_empty.sol | 8 ++++ .../controlFlow/unreachableCode/while_break.sol | 12 ++++++ .../controlFlow/unreachableCode/while_continue.sol | 11 ++++++ .../parsing/for_loop_simple_initexpr.sol | 2 + .../syntaxTests/parsing/for_loop_simple_noexpr.sol | 1 + .../parsing/for_loop_vardef_initexpr.sol | 2 + .../libsolidity/syntaxTests/parsing/while_loop.sol | 1 + 27 files changed, 258 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/comment_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/constant_condition.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/do_while_continue.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_return.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_revert.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/for_break.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/if_both_return.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert_empty.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_break.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_continue.sol diff --git a/Changelog.md b/Changelog.md index 23e394c6..bb7b191f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * Control Flow Graph: Warn about unreachable code. Bugfixes: diff --git a/libdevcore/Algorithms.h b/libdevcore/Algorithms.h index 7fe2472d..34a4dfe5 100644 --- a/libdevcore/Algorithms.h +++ b/libdevcore/Algorithms.h @@ -75,4 +75,47 @@ private: V const* m_firstCycleVertex = nullptr; }; +/** + * Generic breadth first search. + * + * Example: Gather all (recursive) children in a graph starting at (and including) ``root``: + * + * Node const* root = ...; + * std::set allNodes = BreadthFirstSearch{{root}}.run([](Node const& _node, auto&& _addChild) { + * // Potentially process ``_node``. + * for (Node const& _child: _node.children()) + * // Potentially filter the children to be visited. + * _addChild(_child); + * }).visited; + * + * Note that the order of the traversal is *non-deterministic* (the children are stored in a std::set of pointers). + */ +template +struct BreadthFirstSearch +{ + /// Runs the breadth first search. The verticesToTraverse member of the struct needs to be initialized. + /// @param _forEachChild is a callable of the form [...](V const& _node, auto&& _addChild) { ... } + /// that is called for each visited node and is supposed to call _addChild(childNode) for every child + /// node of _node. + template + BreadthFirstSearch& run(ForEachChild&& _forEachChild) + { + while (!verticesToTraverse.empty()) + { + V const* v = *verticesToTraverse.begin(); + verticesToTraverse.erase(verticesToTraverse.begin()); + visited.insert(v); + + _forEachChild(*v, [this](V const& _vertex) { + if (!visited.count(&_vertex)) + verticesToTraverse.insert(&_vertex); + }); + } + return *this; + } + + std::set verticesToTraverse; + std::set visited{}; +}; + } diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h index 840891c2..c461909f 100644 --- a/liblangutil/SourceLocation.h +++ b/liblangutil/SourceLocation.h @@ -49,6 +49,26 @@ struct SourceLocation bool isEmpty() const { return start == -1 && end == -1; } + /// @returns the smallest SourceLocation that contains both @param _a and @param _b. + /// Assumes that @param _a and @param _b refer to the same source (exception: if the source of either one + /// is unset, the source of the other will be used for the result, even if that is unset as well). + /// Invalid start and end positions (with value of -1) are ignored (if start or end are -1 for both @param _a and + /// @param _b, then start resp. end of the result will be -1 as well). + static SourceLocation smallestCovering(SourceLocation _a, SourceLocation const& _b) + { + if (!_a.source) + _a.source = _b.source; + + if (_a.start < 0) + _a.start = _b.start; + else if (_b.start >= 0 && _b.start < _a.start) + _a.start = _b.start; + if (_b.end > _a.end) + _a.end = _b.end; + + return _a; + } + int start = -1; int end = -1; std::shared_ptr source; diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 3adf6318..b801547f 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -18,6 +18,7 @@ #include #include +#include #include using namespace std; @@ -36,6 +37,7 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function) { auto const& functionFlow = m_cfg.functionFlow(_function); checkUninitializedAccess(functionFlow.entry, functionFlow.exit); + checkUnreachable(functionFlow.entry, functionFlow.exit, functionFlow.revert); } return false; } @@ -145,3 +147,35 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod } } } + +void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert) const +{ + // collect all nodes reachable from the entry point + std::set reachable = BreadthFirstSearch{{_entry}}.run( + [](CFGNode const& _node, auto&& _addChild) { + for (CFGNode const* exit: _node.exits) + _addChild(*exit); + } + ).visited; + + // traverse all paths backwards from exit and revert + // and extract (valid) source locations of unreachable nodes into sorted set + std::set unreachable; + BreadthFirstSearch{{_exit, _revert}}.run( + [&](CFGNode const& _node, auto&& _addChild) { + if (!reachable.count(&_node) && !_node.location.isEmpty()) + unreachable.insert(_node.location); + for (CFGNode const* entry: _node.entries) + _addChild(*entry); + } + ); + + for (auto it = unreachable.begin(); it != unreachable.end();) + { + SourceLocation location = *it++; + // Extend the location, as long as the next location overlaps (unreachable is sorted). + for (; it != unreachable.end() && it->start <= location.end; ++it) + location.end = std::max(location.end, it->end); + m_errorReporter.warning(location, "Unreachable code."); + } +} diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h index 7761817a..e1585740 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.h +++ b/libsolidity/analysis/ControlFlowAnalyzer.h @@ -38,6 +38,9 @@ public: private: /// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit. void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const; + /// Checks for unreachable code, i.e. code ending in @param _exit or @param _revert + /// that can not be reached from @param _entry. + void checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert) const; CFG const& m_cfg; langutil::ErrorReporter& m_errorReporter; diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index 3dab8b16..66664245 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -18,6 +18,7 @@ #include using namespace dev; +using namespace langutil; using namespace solidity; using namespace std; @@ -53,6 +54,7 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation) case Token::Or: case Token::And: { + visitNode(_operation); appendControlFlow(_operation.leftExpression()); auto nodes = splitFlow<2>(); @@ -62,14 +64,14 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation) return false; } default: - break; + return ASTConstVisitor::visit(_operation); } - return ASTConstVisitor::visit(_operation); } bool ControlFlowBuilder::visit(Conditional const& _conditional) { solAssert(!!m_currentNode, ""); + visitNode(_conditional); _conditional.condition().accept(*this); @@ -86,6 +88,7 @@ bool ControlFlowBuilder::visit(Conditional const& _conditional) bool ControlFlowBuilder::visit(IfStatement const& _ifStatement) { solAssert(!!m_currentNode, ""); + visitNode(_ifStatement); _ifStatement.condition().accept(*this); @@ -106,6 +109,7 @@ bool ControlFlowBuilder::visit(IfStatement const& _ifStatement) bool ControlFlowBuilder::visit(ForStatement const& _forStatement) { solAssert(!!m_currentNode, ""); + visitNode(_forStatement); if (_forStatement.initializationExpression()) _forStatement.initializationExpression()->accept(*this); @@ -139,6 +143,7 @@ bool ControlFlowBuilder::visit(ForStatement const& _forStatement) bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement) { solAssert(!!m_currentNode, ""); + visitNode(_whileStatement); if (_whileStatement.isDoWhile()) { @@ -183,28 +188,31 @@ bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement) return false; } -bool ControlFlowBuilder::visit(Break const&) +bool ControlFlowBuilder::visit(Break const& _break) { solAssert(!!m_currentNode, ""); solAssert(!!m_breakJump, ""); + visitNode(_break); connect(m_currentNode, m_breakJump); m_currentNode = newLabel(); return false; } -bool ControlFlowBuilder::visit(Continue const&) +bool ControlFlowBuilder::visit(Continue const& _continue) { solAssert(!!m_currentNode, ""); solAssert(!!m_continueJump, ""); + visitNode(_continue); connect(m_currentNode, m_continueJump); m_currentNode = newLabel(); return false; } -bool ControlFlowBuilder::visit(Throw const&) +bool ControlFlowBuilder::visit(Throw const& _throw) { solAssert(!!m_currentNode, ""); solAssert(!!m_revertNode, ""); + visitNode(_throw); connect(m_currentNode, m_revertNode); m_currentNode = newLabel(); return false; @@ -232,6 +240,7 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall) { case FunctionType::Kind::Revert: solAssert(!!m_revertNode, ""); + visitNode(_functionCall); _functionCall.expression().accept(*this); ASTNode::listAccept(_functionCall.arguments(), *this); connect(m_currentNode, m_revertNode); @@ -241,6 +250,7 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall) case FunctionType::Kind::Assert: { solAssert(!!m_revertNode, ""); + visitNode(_functionCall); _functionCall.expression().accept(*this); ASTNode::listAccept(_functionCall.arguments(), *this); connect(m_currentNode, m_revertNode); @@ -314,6 +324,7 @@ bool ControlFlowBuilder::visit(Return const& _return) { solAssert(!!m_currentNode, ""); solAssert(!!m_returnNode, ""); + visitNode(_return); if (_return.expression()) { appendControlFlow(*_return.expression()); @@ -327,11 +338,12 @@ bool ControlFlowBuilder::visit(Return const& _return) } connect(m_currentNode, m_returnNode); m_currentNode = newLabel(); - return true; + return false; } -bool ControlFlowBuilder::visit(FunctionTypeName const&) +bool ControlFlowBuilder::visit(FunctionTypeName const& _functionTypeName) { + visitNode(_functionTypeName); // Do not visit the parameters and return values of a function type name. // We do not want to consider them as variable declarations for the control flow graph. return false; @@ -340,6 +352,7 @@ bool ControlFlowBuilder::visit(FunctionTypeName const&) bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly) { solAssert(!!m_currentNode, ""); + visitNode(_inlineAssembly); for (auto const& ref: _inlineAssembly.annotation().externalReferences) { if (auto variableDeclaration = dynamic_cast(ref.second.declaration)) @@ -355,6 +368,7 @@ bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly) bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) { solAssert(!!m_currentNode, ""); + visitNode(_variableDeclaration); m_currentNode->variableOccurrences.emplace_back( _variableDeclaration, @@ -382,6 +396,7 @@ bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { solAssert(!!m_currentNode, ""); + visitNode(_variableDeclarationStatement); for (auto const& var: _variableDeclarationStatement.declarations()) if (var) @@ -417,6 +432,7 @@ bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDecl bool ControlFlowBuilder::visit(Identifier const& _identifier) { solAssert(!!m_currentNode, ""); + visitNode(_identifier); if (auto const* variableDeclaration = dynamic_cast(_identifier.annotation().referencedDeclaration)) m_currentNode->variableOccurrences.emplace_back( @@ -430,7 +446,12 @@ bool ControlFlowBuilder::visit(Identifier const& _identifier) return true; } - +bool ControlFlowBuilder::visitNode(ASTNode const& _node) +{ + solAssert(!!m_currentNode, ""); + m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, _node.location()); + return true; +} void ControlFlowBuilder::appendControlFlow(ASTNode const& _node) { diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index f196e5fc..b1748395 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -66,6 +66,11 @@ private: bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; bool visit(Identifier const& _identifier) override; +protected: + bool visitNode(ASTNode const&) override; + +private: + /// Appends the control flow of @a _node to the current control flow. void appendControlFlow(ASTNode const& _node); @@ -77,9 +82,6 @@ private: /// Creates an arc from @a _from to @a _to. static void connect(CFGNode* _from, CFGNode* _to); - -private: - /// Splits the control flow starting at the current node into n paths. /// m_currentNode is set to nullptr and has to be set manually or /// using mergeFlow later. diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h index cc0113d8..a55b96e8 100644 --- a/libsolidity/analysis/ControlFlowGraph.h +++ b/libsolidity/analysis/ControlFlowGraph.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -98,6 +99,8 @@ struct CFGNode /// Variable occurrences in the node. std::vector variableOccurrences; + // Source location of this control flow block. + langutil::SourceLocation location; }; /** Describes the control flow of a function. */ diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol index b868d61d..c7e3eacd 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol @@ -46,7 +46,11 @@ contract C { } // ---- // TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment. +// Warning: (146-151): Unreachable code. +// Warning: (169-174): Unreachable code. // TypeError: (223-234): This variable is of storage pointer type and can be returned without prior assignment. +// Warning: (316-321): Unreachable code. // TypeError: (440-451): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (654-665): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (871-882): This variable is of storage pointer type and can be returned without prior assignment. +// Warning: (933-938): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol index 55c5edd3..5a113668 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol @@ -29,3 +29,4 @@ contract C { } } // ---- +// Warning: (567-572): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol index 96767402..da7f1a90 100644 --- a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol +++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol @@ -5,4 +5,6 @@ contract C { revert(); b; } -} \ No newline at end of file +} +// ---- +// Warning: (125-126): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol index b941ad34..9ebd0321 100644 --- a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol +++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol @@ -8,3 +8,4 @@ contract C { } } // ---- +// Warning: (112-135): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/comment_fine.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/comment_fine.sol new file mode 100644 index 00000000..17119ca6 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/comment_fine.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure { + return; + // unreachable comment + } +} diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/constant_condition.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/constant_condition.sol new file mode 100644 index 00000000..e00398bc --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/constant_condition.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + if (false) { + return; // unreachable, but not yet detected + } + return; + } +} diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/do_while_continue.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/do_while_continue.sol new file mode 100644 index 00000000..363c53e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/do_while_continue.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure { + do { + uint a = 42; a; + continue; + return; // this is unreachable + } while(false); + return; // this is still reachable + } +} +// ---- +// Warning: (119-126): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_return.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_return.sol new file mode 100644 index 00000000..9b755347 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_return.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (uint) { + return 0; + return 0; + } +} +// ---- +// Warning: (85-93): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_revert.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_revert.sol new file mode 100644 index 00000000..a6457e4f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/double_revert.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + revert(); + revert(); + } +} +// ---- +// Warning: (70-78): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/for_break.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/for_break.sol new file mode 100644 index 00000000..496addb2 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/for_break.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure { + for (uint a = 0; a < 1; a++) { + break; + uint b = 42; b; + } + return; + } +} +// ---- +// Warning: (76-79): Unreachable code. +// Warning: (114-128): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/if_both_return.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/if_both_return.sol new file mode 100644 index 00000000..3513b17d --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/if_both_return.sol @@ -0,0 +1,12 @@ +contract C { + function f(bool c) public pure { + if (c) { + return; + } else { + return; + } + return; // unreachable + } +} +// ---- +// Warning: (142-149): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert.sol new file mode 100644 index 00000000..9bb6a41c --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + revert(); + uint a = 0; a; + } +} +// ---- +// Warning: (70-83): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert_empty.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert_empty.sol new file mode 100644 index 00000000..4c80c5ca --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/revert_empty.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + revert(); + for(int i = 0; i < 3; i++) { f(); } + } +} +// ---- +// Warning: (70-105): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_break.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_break.sol new file mode 100644 index 00000000..2d1ddd4f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_break.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure { + uint a = 0; + while (a < 100) { + a++; + break; + a--; + } + } +} +// ---- +// Warning: (138-141): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_continue.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_continue.sol new file mode 100644 index 00000000..55f98f67 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/while_continue.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + while(true) { + continue; + return; + } + return; // this is unreachable as well, but currently undetected (needs to consider constant condition "true") + } +} +// ---- +// Warning: (100-107): Unreachable code. diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol index fce669dd..9fafd706 100644 --- a/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol +++ b/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol @@ -7,6 +7,8 @@ contract test { } } // ---- +// Warning: (103-106): Unreachable code. +// Warning: (144-152): Unreachable code. // Warning: (33-42): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning: (122-131): Unused local variable. // Warning: (20-169): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol index 4adf0948..c36f9c58 100644 --- a/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol +++ b/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol @@ -7,6 +7,7 @@ contract test { } } // ---- +// Warning: (144-152): Unreachable code. // Warning: (37-46): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning: (122-131): Unused local variable. // Warning: (24-177): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol index c22ae42f..c81f611b 100644 --- a/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol +++ b/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol @@ -6,6 +6,8 @@ contract test { } } // ---- +// Warning: (89-92): Unreachable code. +// Warning: (130-138): Unreachable code. // Warning: (33-42): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning: (108-117): Unused local variable. // Warning: (20-155): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/while_loop.sol b/test/libsolidity/syntaxTests/parsing/while_loop.sol index dbb00a69..cdac929f 100644 --- a/test/libsolidity/syntaxTests/parsing/while_loop.sol +++ b/test/libsolidity/syntaxTests/parsing/while_loop.sol @@ -5,3 +5,4 @@ contract test { } } // ---- +// Warning: (105-113): Unreachable code. -- cgit From 52fd20e98c140fbc9d177ebc28d366e7da6c3876 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Mon, 7 Jan 2019 16:29:36 -0500 Subject: Use latest version in code snippets. --- README.md | 4 +++- docs/index.rst | 2 ++ docs/layout-of-source-files.rst | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c7065c09..959fefc2 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ that run on the Ethereum Virtual Machine. Smart contracts are programs that are network where nobody has special authority over the execution and thus they allow to implement tokens of value, ownership, voting and other kinds of logics. +When deploying contracts, you should use the latest released version of Solidity. This is because breaking changes as well as new features and bug fixes are introduced regularly. We currently use a 0.x version number [to indicate this fast pace of change](https://semver.org/#spec-item-4). + ## Build and Install Instructions about how to build and install the Solidity compiler can be found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source) @@ -29,7 +31,7 @@ Instructions about how to build and install the Solidity compiler can be found i A "Hello World" program in Solidity is of even less use than in other languages, but still: ``` -pragma solidity ^0.4.16; +pragma solidity ^0.5.0; contract HelloWorld { function helloWorld() external pure returns (string memory) { diff --git a/docs/index.rst b/docs/index.rst index ed931163..16745c07 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,6 +19,8 @@ user-defined types among other features. With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions, and multi-signature wallets. +When deploying contracts, you should use the latest released version of Solidity. This is because breaking changes as well as new features and bug fixes are introduced regularly. We currently use a 0.x version number [to indicate this fast pace of change](https://semver.org/#spec-item-4). + Language Documentation ---------------------- diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index fa36fc6a..235f4dd4 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -37,12 +37,12 @@ breaking changes, those releases will always have versions of the form The version pragma is used as follows:: - pragma solidity ^0.4.0; + pragma solidity ^0.5.2; -Such a source file will not compile with a compiler earlier than version 0.4.0 -and it will also not work on a compiler starting from version 0.5.0 (this +Such a source file will not compile with a compiler earlier than version 0.5.2 +and it will also not work on a compiler starting from version 0.6.0 (this second condition is added by using ``^``). The idea behind this is that -there will be no breaking changes until version ``0.5.0``, so we can always +there will be no breaking changes until version ``0.6.0``, so we can always be sure that our code will compile the way we intended it to. We do not fix the exact version of the compiler, so that bugfix releases are still possible. -- cgit From 5a99623fec7d03b5bd4158cb3cf58e95fe46359b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 11 Jan 2019 12:47:45 +0100 Subject: Avoid explicit conversion from Block to Statement in CodeSize visit. --- libyul/optimiser/Metrics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index fb275cc3..df919682 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -48,7 +48,7 @@ size_t CodeSize::codeSize(Expression const& _expression) size_t CodeSize::codeSize(Block const& _block) { CodeSize cs; - cs.visit(_block); + cs(_block); return cs.m_size; } -- cgit From 06ca903b370ca0188b20dc568b87d01fae5a7b57 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 14 Jan 2019 09:41:10 +0200 Subject: Split voting example into seperate file --- docs/conf.py | 2 +- docs/examples/voting.rst | 191 ++++++++++++++++++++++++++++++++++++++++++ docs/solidity-by-example.rst | 192 +------------------------------------------ 3 files changed, 193 insertions(+), 192 deletions(-) create mode 100644 docs/examples/voting.rst diff --git a/docs/conf.py b/docs/conf.py index d08a5191..87a6ec03 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -81,7 +81,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts', 'types'] +exclude_patterns = ['_build', 'contracts', 'types', 'examples'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/examples/voting.rst b/docs/examples/voting.rst new file mode 100644 index 00000000..73ace87d --- /dev/null +++ b/docs/examples/voting.rst @@ -0,0 +1,191 @@ +.. index:: voting, ballot + +.. _voting: + +****** +Voting +****** + +The following contract is quite complex, but showcases +a lot of Solidity's features. It implements a voting +contract. Of course, the main problems of electronic +voting is how to assign voting rights to the correct +persons and how to prevent manipulation. We will not +solve all problems here, but at least we will show +how delegated voting can be done so that vote counting +is **automatic and completely transparent** at the +same time. + +The idea is to create one contract per ballot, +providing a short name for each option. +Then the creator of the contract who serves as +chairperson will give the right to vote to each +address individually. + +The persons behind the addresses can then choose +to either vote themselves or to delegate their +vote to a person they trust. + +At the end of the voting time, ``winningProposal()`` +will return the proposal with the largest number +of votes. + +:: + + pragma solidity >=0.4.22 <0.6.0; + + /// @title Voting with delegation. + contract Ballot { + // This declares a new complex type which will + // be used for variables later. + // It will represent a single voter. + struct Voter { + uint weight; // weight is accumulated by delegation + bool voted; // if true, that person already voted + address delegate; // person delegated to + uint vote; // index of the voted proposal + } + + // This is a type for a single proposal. + struct Proposal { + bytes32 name; // short name (up to 32 bytes) + uint voteCount; // number of accumulated votes + } + + address public chairperson; + + // This declares a state variable that + // stores a `Voter` struct for each possible address. + mapping(address => Voter) public voters; + + // A dynamically-sized array of `Proposal` structs. + Proposal[] public proposals; + + /// Create a new ballot to choose one of `proposalNames`. + constructor(bytes32[] memory proposalNames) public { + chairperson = msg.sender; + voters[chairperson].weight = 1; + + // For each of the provided proposal names, + // create a new proposal object and add it + // to the end of the array. + for (uint i = 0; i < proposalNames.length; i++) { + // `Proposal({...})` creates a temporary + // Proposal object and `proposals.push(...)` + // appends it to the end of `proposals`. + proposals.push(Proposal({ + name: proposalNames[i], + voteCount: 0 + })); + } + } + + // Give `voter` the right to vote on this ballot. + // May only be called by `chairperson`. + function giveRightToVote(address voter) public { + // If the first argument of `require` evaluates + // to `false`, execution terminates and all + // changes to the state and to Ether balances + // are reverted. + // This used to consume all gas in old EVM versions, but + // not anymore. + // It is often a good idea to use `require` to check if + // functions are called correctly. + // As a second argument, you can also provide an + // explanation about what went wrong. + require( + msg.sender == chairperson, + "Only chairperson can give right to vote." + ); + require( + !voters[voter].voted, + "The voter already voted." + ); + require(voters[voter].weight == 0); + voters[voter].weight = 1; + } + + /// Delegate your vote to the voter `to`. + function delegate(address to) public { + // assigns reference + Voter storage sender = voters[msg.sender]; + require(!sender.voted, "You already voted."); + + require(to != msg.sender, "Self-delegation is disallowed."); + + // Forward the delegation as long as + // `to` also delegated. + // In general, such loops are very dangerous, + // because if they run too long, they might + // need more gas than is available in a block. + // In this case, the delegation will not be executed, + // but in other situations, such loops might + // cause a contract to get "stuck" completely. + while (voters[to].delegate != address(0)) { + to = voters[to].delegate; + + // We found a loop in the delegation, not allowed. + require(to != msg.sender, "Found loop in delegation."); + } + + // Since `sender` is a reference, this + // modifies `voters[msg.sender].voted` + sender.voted = true; + sender.delegate = to; + Voter storage delegate_ = voters[to]; + if (delegate_.voted) { + // If the delegate already voted, + // directly add to the number of votes + proposals[delegate_.vote].voteCount += sender.weight; + } else { + // If the delegate did not vote yet, + // add to her weight. + delegate_.weight += sender.weight; + } + } + + /// Give your vote (including votes delegated to you) + /// to proposal `proposals[proposal].name`. + function vote(uint proposal) public { + Voter storage sender = voters[msg.sender]; + require(sender.weight != 0, "Has no right to vote"); + require(!sender.voted, "Already voted."); + sender.voted = true; + sender.vote = proposal; + + // If `proposal` is out of the range of the array, + // this will throw automatically and revert all + // changes. + proposals[proposal].voteCount += sender.weight; + } + + /// @dev Computes the winning proposal taking all + /// previous votes into account. + function winningProposal() public view + returns (uint winningProposal_) + { + uint winningVoteCount = 0; + for (uint p = 0; p < proposals.length; p++) { + if (proposals[p].voteCount > winningVoteCount) { + winningVoteCount = proposals[p].voteCount; + winningProposal_ = p; + } + } + } + + // Calls winningProposal() function to get the index + // of the winner contained in the proposals array and then + // returns the name of the winner + function winnerName() public view + returns (bytes32 winnerName_) + { + winnerName_ = proposals[winningProposal()].name; + } + } + + +Possible Improvements +===================== + +Currently, many transactions are needed to assign the rights +to vote to all participants. Can you think of a better way? diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 0041e80c..0e7d507d 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -2,197 +2,7 @@ Solidity by Example ################### -.. index:: voting, ballot - -.. _voting: - -****** -Voting -****** - -The following contract is quite complex, but showcases -a lot of Solidity's features. It implements a voting -contract. Of course, the main problems of electronic -voting is how to assign voting rights to the correct -persons and how to prevent manipulation. We will not -solve all problems here, but at least we will show -how delegated voting can be done so that vote counting -is **automatic and completely transparent** at the -same time. - -The idea is to create one contract per ballot, -providing a short name for each option. -Then the creator of the contract who serves as -chairperson will give the right to vote to each -address individually. - -The persons behind the addresses can then choose -to either vote themselves or to delegate their -vote to a person they trust. - -At the end of the voting time, ``winningProposal()`` -will return the proposal with the largest number -of votes. - -:: - - pragma solidity >=0.4.22 <0.6.0; - - /// @title Voting with delegation. - contract Ballot { - // This declares a new complex type which will - // be used for variables later. - // It will represent a single voter. - struct Voter { - uint weight; // weight is accumulated by delegation - bool voted; // if true, that person already voted - address delegate; // person delegated to - uint vote; // index of the voted proposal - } - - // This is a type for a single proposal. - struct Proposal { - bytes32 name; // short name (up to 32 bytes) - uint voteCount; // number of accumulated votes - } - - address public chairperson; - - // This declares a state variable that - // stores a `Voter` struct for each possible address. - mapping(address => Voter) public voters; - - // A dynamically-sized array of `Proposal` structs. - Proposal[] public proposals; - - /// Create a new ballot to choose one of `proposalNames`. - constructor(bytes32[] memory proposalNames) public { - chairperson = msg.sender; - voters[chairperson].weight = 1; - - // For each of the provided proposal names, - // create a new proposal object and add it - // to the end of the array. - for (uint i = 0; i < proposalNames.length; i++) { - // `Proposal({...})` creates a temporary - // Proposal object and `proposals.push(...)` - // appends it to the end of `proposals`. - proposals.push(Proposal({ - name: proposalNames[i], - voteCount: 0 - })); - } - } - - // Give `voter` the right to vote on this ballot. - // May only be called by `chairperson`. - function giveRightToVote(address voter) public { - // If the first argument of `require` evaluates - // to `false`, execution terminates and all - // changes to the state and to Ether balances - // are reverted. - // This used to consume all gas in old EVM versions, but - // not anymore. - // It is often a good idea to use `require` to check if - // functions are called correctly. - // As a second argument, you can also provide an - // explanation about what went wrong. - require( - msg.sender == chairperson, - "Only chairperson can give right to vote." - ); - require( - !voters[voter].voted, - "The voter already voted." - ); - require(voters[voter].weight == 0); - voters[voter].weight = 1; - } - - /// Delegate your vote to the voter `to`. - function delegate(address to) public { - // assigns reference - Voter storage sender = voters[msg.sender]; - require(!sender.voted, "You already voted."); - - require(to != msg.sender, "Self-delegation is disallowed."); - - // Forward the delegation as long as - // `to` also delegated. - // In general, such loops are very dangerous, - // because if they run too long, they might - // need more gas than is available in a block. - // In this case, the delegation will not be executed, - // but in other situations, such loops might - // cause a contract to get "stuck" completely. - while (voters[to].delegate != address(0)) { - to = voters[to].delegate; - - // We found a loop in the delegation, not allowed. - require(to != msg.sender, "Found loop in delegation."); - } - - // Since `sender` is a reference, this - // modifies `voters[msg.sender].voted` - sender.voted = true; - sender.delegate = to; - Voter storage delegate_ = voters[to]; - if (delegate_.voted) { - // If the delegate already voted, - // directly add to the number of votes - proposals[delegate_.vote].voteCount += sender.weight; - } else { - // If the delegate did not vote yet, - // add to her weight. - delegate_.weight += sender.weight; - } - } - - /// Give your vote (including votes delegated to you) - /// to proposal `proposals[proposal].name`. - function vote(uint proposal) public { - Voter storage sender = voters[msg.sender]; - require(sender.weight != 0, "Has no right to vote"); - require(!sender.voted, "Already voted."); - sender.voted = true; - sender.vote = proposal; - - // If `proposal` is out of the range of the array, - // this will throw automatically and revert all - // changes. - proposals[proposal].voteCount += sender.weight; - } - - /// @dev Computes the winning proposal taking all - /// previous votes into account. - function winningProposal() public view - returns (uint winningProposal_) - { - uint winningVoteCount = 0; - for (uint p = 0; p < proposals.length; p++) { - if (proposals[p].voteCount > winningVoteCount) { - winningVoteCount = proposals[p].voteCount; - winningProposal_ = p; - } - } - } - - // Calls winningProposal() function to get the index - // of the winner contained in the proposals array and then - // returns the name of the winner - function winnerName() public view - returns (bytes32 winnerName_) - { - winnerName_ = proposals[winningProposal()].name; - } - } - - -Possible Improvements -===================== - -Currently, many transactions are needed to assign the rights -to vote to all participants. Can you think of a better way? +.. include:: examples/voting.rst .. index:: auction;blind, auction;open, blind auction, open auction -- cgit From 7bc1f1a4a3824156c3e315d420d5e6a2047b4822 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 14 Jan 2019 10:36:47 +0200 Subject: Split blind auction example into seperate doc --- docs/examples/blind-auction.rst | 339 +++++++++++++++++++++++++++++++++++++++ docs/solidity-by-example.rst | 342 +--------------------------------------- 2 files changed, 340 insertions(+), 341 deletions(-) create mode 100644 docs/examples/blind-auction.rst diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst new file mode 100644 index 00000000..70d95ad6 --- /dev/null +++ b/docs/examples/blind-auction.rst @@ -0,0 +1,339 @@ +.. index:: auction;blind, auction;open, blind auction, open auction + +************* +Blind Auction +************* + +In this section, we will show how easy it is to create a +completely blind auction contract on Ethereum. +We will start with an open auction where everyone +can see the bids that are made and then extend this +contract into a blind auction where it is not +possible to see the actual bid until the bidding +period ends. + +.. _simple_auction: + +Simple Open Auction +=================== + +The general idea of the following simple auction contract +is that everyone can send their bids during +a bidding period. The bids already include sending +money / ether in order to bind the bidders to their +bid. If the highest bid is raised, the previously +highest bidder gets her money back. +After the end of the bidding period, the +contract has to be called manually for the +beneficiary to receive their money - contracts cannot +activate themselves. + +:: + + pragma solidity >=0.4.22 <0.6.0; + + contract SimpleAuction { + // Parameters of the auction. Times are either + // absolute unix timestamps (seconds since 1970-01-01) + // or time periods in seconds. + address payable public beneficiary; + uint public auctionEndTime; + + // Current state of the auction. + address public highestBidder; + uint public highestBid; + + // Allowed withdrawals of previous bids + mapping(address => uint) pendingReturns; + + // Set to true at the end, disallows any change. + // By default initialized to `false`. + bool ended; + + // Events that will be emitted on changes. + event HighestBidIncreased(address bidder, uint amount); + event AuctionEnded(address winner, uint amount); + + // The following is a so-called natspec comment, + // recognizable by the three slashes. + // It will be shown when the user is asked to + // confirm a transaction. + + /// Create a simple auction with `_biddingTime` + /// seconds bidding time on behalf of the + /// beneficiary address `_beneficiary`. + constructor( + uint _biddingTime, + address payable _beneficiary + ) public { + beneficiary = _beneficiary; + auctionEndTime = now + _biddingTime; + } + + /// Bid on the auction with the value sent + /// together with this transaction. + /// The value will only be refunded if the + /// auction is not won. + function bid() public payable { + // No arguments are necessary, all + // information is already part of + // the transaction. The keyword payable + // is required for the function to + // be able to receive Ether. + + // Revert the call if the bidding + // period is over. + require( + now <= auctionEndTime, + "Auction already ended." + ); + + // If the bid is not higher, send the + // money back. + require( + msg.value > highestBid, + "There already is a higher bid." + ); + + if (highestBid != 0) { + // Sending back the money by simply using + // highestBidder.send(highestBid) is a security risk + // because it could execute an untrusted contract. + // It is always safer to let the recipients + // withdraw their money themselves. + pendingReturns[highestBidder] += highestBid; + } + highestBidder = msg.sender; + highestBid = msg.value; + emit HighestBidIncreased(msg.sender, msg.value); + } + + /// Withdraw a bid that was overbid. + function withdraw() public returns (bool) { + uint amount = pendingReturns[msg.sender]; + if (amount > 0) { + // It is important to set this to zero because the recipient + // can call this function again as part of the receiving call + // before `send` returns. + pendingReturns[msg.sender] = 0; + + if (!msg.sender.send(amount)) { + // No need to call throw here, just reset the amount owing + pendingReturns[msg.sender] = amount; + return false; + } + } + return true; + } + + /// End the auction and send the highest bid + /// to the beneficiary. + function auctionEnd() public { + // It is a good guideline to structure functions that interact + // with other contracts (i.e. they call functions or send Ether) + // into three phases: + // 1. checking conditions + // 2. performing actions (potentially changing conditions) + // 3. interacting with other contracts + // If these phases are mixed up, the other contract could call + // back into the current contract and modify the state or cause + // effects (ether payout) to be performed multiple times. + // If functions called internally include interaction with external + // contracts, they also have to be considered interaction with + // external contracts. + + // 1. Conditions + require(now >= auctionEndTime, "Auction not yet ended."); + require(!ended, "auctionEnd has already been called."); + + // 2. Effects + ended = true; + emit AuctionEnded(highestBidder, highestBid); + + // 3. Interaction + beneficiary.transfer(highestBid); + } + } + +Blind Auction +============= + +The previous open auction is extended to a blind auction +in the following. The advantage of a blind auction is +that there is no time pressure towards the end of +the bidding period. Creating a blind auction on a +transparent computing platform might sound like a +contradiction, but cryptography comes to the rescue. + +During the **bidding period**, a bidder does not +actually send her bid, but only a hashed version of it. +Since it is currently considered practically impossible +to find two (sufficiently long) values whose hash +values are equal, the bidder commits to the bid by that. +After the end of the bidding period, the bidders have +to reveal their bids: They send their values +unencrypted and the contract checks that the hash value +is the same as the one provided during the bidding period. + +Another challenge is how to make the auction +**binding and blind** at the same time: The only way to +prevent the bidder from just not sending the money +after they won the auction is to make her send it +together with the bid. Since value transfers cannot +be blinded in Ethereum, anyone can see the value. + +The following contract solves this problem by +accepting any value that is larger than the highest +bid. Since this can of course only be checked during +the reveal phase, some bids might be **invalid**, and +this is on purpose (it even provides an explicit +flag to place invalid bids with high value transfers): +Bidders can confuse competition by placing several +high or low invalid bids. + + +:: + + pragma solidity >0.4.23 <0.6.0; + + contract BlindAuction { + struct Bid { + bytes32 blindedBid; + uint deposit; + } + + address payable public beneficiary; + uint public biddingEnd; + uint public revealEnd; + bool public ended; + + mapping(address => Bid[]) public bids; + + address public highestBidder; + uint public highestBid; + + // Allowed withdrawals of previous bids + mapping(address => uint) pendingReturns; + + event AuctionEnded(address winner, uint highestBid); + + /// Modifiers are a convenient way to validate inputs to + /// functions. `onlyBefore` is applied to `bid` below: + /// The new function body is the modifier's body where + /// `_` is replaced by the old function body. + modifier onlyBefore(uint _time) { require(now < _time); _; } + modifier onlyAfter(uint _time) { require(now > _time); _; } + + constructor( + uint _biddingTime, + uint _revealTime, + address payable _beneficiary + ) public { + beneficiary = _beneficiary; + biddingEnd = now + _biddingTime; + revealEnd = biddingEnd + _revealTime; + } + + /// Place a blinded bid with `_blindedBid` = + /// keccak256(abi.encodePacked(value, fake, secret)). + /// The sent ether is only refunded if the bid is correctly + /// revealed in the revealing phase. The bid is valid if the + /// ether sent together with the bid is at least "value" and + /// "fake" is not true. Setting "fake" to true and sending + /// not the exact amount are ways to hide the real bid but + /// still make the required deposit. The same address can + /// place multiple bids. + function bid(bytes32 _blindedBid) + public + payable + onlyBefore(biddingEnd) + { + bids[msg.sender].push(Bid({ + blindedBid: _blindedBid, + deposit: msg.value + })); + } + + /// Reveal your blinded bids. You will get a refund for all + /// correctly blinded invalid bids and for all bids except for + /// the totally highest. + function reveal( + uint[] memory _values, + bool[] memory _fake, + bytes32[] memory _secret + ) + public + onlyAfter(biddingEnd) + onlyBefore(revealEnd) + { + uint length = bids[msg.sender].length; + require(_values.length == length); + require(_fake.length == length); + require(_secret.length == length); + + uint refund; + for (uint i = 0; i < length; i++) { + Bid storage bidToCheck = bids[msg.sender][i]; + (uint value, bool fake, bytes32 secret) = + (_values[i], _fake[i], _secret[i]); + if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) { + // Bid was not actually revealed. + // Do not refund deposit. + continue; + } + refund += bidToCheck.deposit; + if (!fake && bidToCheck.deposit >= value) { + if (placeBid(msg.sender, value)) + refund -= value; + } + // Make it impossible for the sender to re-claim + // the same deposit. + bidToCheck.blindedBid = bytes32(0); + } + msg.sender.transfer(refund); + } + + // This is an "internal" function which means that it + // can only be called from the contract itself (or from + // derived contracts). + function placeBid(address bidder, uint value) internal + returns (bool success) + { + if (value <= highestBid) { + return false; + } + if (highestBidder != address(0)) { + // Refund the previously highest bidder. + pendingReturns[highestBidder] += highestBid; + } + highestBid = value; + highestBidder = bidder; + return true; + } + + /// Withdraw a bid that was overbid. + function withdraw() public { + uint amount = pendingReturns[msg.sender]; + if (amount > 0) { + // It is important to set this to zero because the recipient + // can call this function again as part of the receiving call + // before `transfer` returns (see the remark above about + // conditions -> effects -> interaction). + pendingReturns[msg.sender] = 0; + + msg.sender.transfer(amount); + } + } + + /// End the auction and send the highest bid + /// to the beneficiary. + function auctionEnd() + public + onlyAfter(revealEnd) + { + require(!ended); + emit AuctionEnded(highestBidder, highestBid); + ended = true; + beneficiary.transfer(highestBid); + } + } \ No newline at end of file diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 0e7d507d..ddead4d5 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -3,347 +3,7 @@ Solidity by Example ################### .. include:: examples/voting.rst - -.. index:: auction;blind, auction;open, blind auction, open auction - -************* -Blind Auction -************* - -In this section, we will show how easy it is to create a -completely blind auction contract on Ethereum. -We will start with an open auction where everyone -can see the bids that are made and then extend this -contract into a blind auction where it is not -possible to see the actual bid until the bidding -period ends. - -.. _simple_auction: - -Simple Open Auction -=================== - -The general idea of the following simple auction contract -is that everyone can send their bids during -a bidding period. The bids already include sending -money / ether in order to bind the bidders to their -bid. If the highest bid is raised, the previously -highest bidder gets her money back. -After the end of the bidding period, the -contract has to be called manually for the -beneficiary to receive their money - contracts cannot -activate themselves. - -:: - - pragma solidity >=0.4.22 <0.6.0; - - contract SimpleAuction { - // Parameters of the auction. Times are either - // absolute unix timestamps (seconds since 1970-01-01) - // or time periods in seconds. - address payable public beneficiary; - uint public auctionEndTime; - - // Current state of the auction. - address public highestBidder; - uint public highestBid; - - // Allowed withdrawals of previous bids - mapping(address => uint) pendingReturns; - - // Set to true at the end, disallows any change. - // By default initialized to `false`. - bool ended; - - // Events that will be emitted on changes. - event HighestBidIncreased(address bidder, uint amount); - event AuctionEnded(address winner, uint amount); - - // The following is a so-called natspec comment, - // recognizable by the three slashes. - // It will be shown when the user is asked to - // confirm a transaction. - - /// Create a simple auction with `_biddingTime` - /// seconds bidding time on behalf of the - /// beneficiary address `_beneficiary`. - constructor( - uint _biddingTime, - address payable _beneficiary - ) public { - beneficiary = _beneficiary; - auctionEndTime = now + _biddingTime; - } - - /// Bid on the auction with the value sent - /// together with this transaction. - /// The value will only be refunded if the - /// auction is not won. - function bid() public payable { - // No arguments are necessary, all - // information is already part of - // the transaction. The keyword payable - // is required for the function to - // be able to receive Ether. - - // Revert the call if the bidding - // period is over. - require( - now <= auctionEndTime, - "Auction already ended." - ); - - // If the bid is not higher, send the - // money back. - require( - msg.value > highestBid, - "There already is a higher bid." - ); - - if (highestBid != 0) { - // Sending back the money by simply using - // highestBidder.send(highestBid) is a security risk - // because it could execute an untrusted contract. - // It is always safer to let the recipients - // withdraw their money themselves. - pendingReturns[highestBidder] += highestBid; - } - highestBidder = msg.sender; - highestBid = msg.value; - emit HighestBidIncreased(msg.sender, msg.value); - } - - /// Withdraw a bid that was overbid. - function withdraw() public returns (bool) { - uint amount = pendingReturns[msg.sender]; - if (amount > 0) { - // It is important to set this to zero because the recipient - // can call this function again as part of the receiving call - // before `send` returns. - pendingReturns[msg.sender] = 0; - - if (!msg.sender.send(amount)) { - // No need to call throw here, just reset the amount owing - pendingReturns[msg.sender] = amount; - return false; - } - } - return true; - } - - /// End the auction and send the highest bid - /// to the beneficiary. - function auctionEnd() public { - // It is a good guideline to structure functions that interact - // with other contracts (i.e. they call functions or send Ether) - // into three phases: - // 1. checking conditions - // 2. performing actions (potentially changing conditions) - // 3. interacting with other contracts - // If these phases are mixed up, the other contract could call - // back into the current contract and modify the state or cause - // effects (ether payout) to be performed multiple times. - // If functions called internally include interaction with external - // contracts, they also have to be considered interaction with - // external contracts. - - // 1. Conditions - require(now >= auctionEndTime, "Auction not yet ended."); - require(!ended, "auctionEnd has already been called."); - - // 2. Effects - ended = true; - emit AuctionEnded(highestBidder, highestBid); - - // 3. Interaction - beneficiary.transfer(highestBid); - } - } - -Blind Auction -============= - -The previous open auction is extended to a blind auction -in the following. The advantage of a blind auction is -that there is no time pressure towards the end of -the bidding period. Creating a blind auction on a -transparent computing platform might sound like a -contradiction, but cryptography comes to the rescue. - -During the **bidding period**, a bidder does not -actually send her bid, but only a hashed version of it. -Since it is currently considered practically impossible -to find two (sufficiently long) values whose hash -values are equal, the bidder commits to the bid by that. -After the end of the bidding period, the bidders have -to reveal their bids: They send their values -unencrypted and the contract checks that the hash value -is the same as the one provided during the bidding period. - -Another challenge is how to make the auction -**binding and blind** at the same time: The only way to -prevent the bidder from just not sending the money -after they won the auction is to make her send it -together with the bid. Since value transfers cannot -be blinded in Ethereum, anyone can see the value. - -The following contract solves this problem by -accepting any value that is larger than the highest -bid. Since this can of course only be checked during -the reveal phase, some bids might be **invalid**, and -this is on purpose (it even provides an explicit -flag to place invalid bids with high value transfers): -Bidders can confuse competition by placing several -high or low invalid bids. - - -:: - - pragma solidity >0.4.23 <0.6.0; - - contract BlindAuction { - struct Bid { - bytes32 blindedBid; - uint deposit; - } - - address payable public beneficiary; - uint public biddingEnd; - uint public revealEnd; - bool public ended; - - mapping(address => Bid[]) public bids; - - address public highestBidder; - uint public highestBid; - - // Allowed withdrawals of previous bids - mapping(address => uint) pendingReturns; - - event AuctionEnded(address winner, uint highestBid); - - /// Modifiers are a convenient way to validate inputs to - /// functions. `onlyBefore` is applied to `bid` below: - /// The new function body is the modifier's body where - /// `_` is replaced by the old function body. - modifier onlyBefore(uint _time) { require(now < _time); _; } - modifier onlyAfter(uint _time) { require(now > _time); _; } - - constructor( - uint _biddingTime, - uint _revealTime, - address payable _beneficiary - ) public { - beneficiary = _beneficiary; - biddingEnd = now + _biddingTime; - revealEnd = biddingEnd + _revealTime; - } - - /// Place a blinded bid with `_blindedBid` = - /// keccak256(abi.encodePacked(value, fake, secret)). - /// The sent ether is only refunded if the bid is correctly - /// revealed in the revealing phase. The bid is valid if the - /// ether sent together with the bid is at least "value" and - /// "fake" is not true. Setting "fake" to true and sending - /// not the exact amount are ways to hide the real bid but - /// still make the required deposit. The same address can - /// place multiple bids. - function bid(bytes32 _blindedBid) - public - payable - onlyBefore(biddingEnd) - { - bids[msg.sender].push(Bid({ - blindedBid: _blindedBid, - deposit: msg.value - })); - } - - /// Reveal your blinded bids. You will get a refund for all - /// correctly blinded invalid bids and for all bids except for - /// the totally highest. - function reveal( - uint[] memory _values, - bool[] memory _fake, - bytes32[] memory _secret - ) - public - onlyAfter(biddingEnd) - onlyBefore(revealEnd) - { - uint length = bids[msg.sender].length; - require(_values.length == length); - require(_fake.length == length); - require(_secret.length == length); - - uint refund; - for (uint i = 0; i < length; i++) { - Bid storage bidToCheck = bids[msg.sender][i]; - (uint value, bool fake, bytes32 secret) = - (_values[i], _fake[i], _secret[i]); - if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) { - // Bid was not actually revealed. - // Do not refund deposit. - continue; - } - refund += bidToCheck.deposit; - if (!fake && bidToCheck.deposit >= value) { - if (placeBid(msg.sender, value)) - refund -= value; - } - // Make it impossible for the sender to re-claim - // the same deposit. - bidToCheck.blindedBid = bytes32(0); - } - msg.sender.transfer(refund); - } - - // This is an "internal" function which means that it - // can only be called from the contract itself (or from - // derived contracts). - function placeBid(address bidder, uint value) internal - returns (bool success) - { - if (value <= highestBid) { - return false; - } - if (highestBidder != address(0)) { - // Refund the previously highest bidder. - pendingReturns[highestBidder] += highestBid; - } - highestBid = value; - highestBidder = bidder; - return true; - } - - /// Withdraw a bid that was overbid. - function withdraw() public { - uint amount = pendingReturns[msg.sender]; - if (amount > 0) { - // It is important to set this to zero because the recipient - // can call this function again as part of the receiving call - // before `transfer` returns (see the remark above about - // conditions -> effects -> interaction). - pendingReturns[msg.sender] = 0; - - msg.sender.transfer(amount); - } - } - - /// End the auction and send the highest bid - /// to the beneficiary. - function auctionEnd() - public - onlyAfter(revealEnd) - { - require(!ended); - emit AuctionEnded(highestBidder, highestBid); - ended = true; - beneficiary.transfer(highestBid); - } - } - +.. include:: examples/blind-auction.rst .. index:: purchase, remote purchase, escrow -- cgit From ea47e382ba43fb4e1a7b0ab788989580379545bd Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 14 Jan 2019 10:45:08 +0200 Subject: Split safe remote example into new file --- docs/examples/safe-remote.rst | 107 +++++++++++++++++++++++++++++++++++++++++ docs/solidity-by-example.rst | 108 +----------------------------------------- 2 files changed, 108 insertions(+), 107 deletions(-) create mode 100644 docs/examples/safe-remote.rst diff --git a/docs/examples/safe-remote.rst b/docs/examples/safe-remote.rst new file mode 100644 index 00000000..cfc63a24 --- /dev/null +++ b/docs/examples/safe-remote.rst @@ -0,0 +1,107 @@ +.. index:: purchase, remote purchase, escrow + +******************** +Safe Remote Purchase +******************** + +:: + + pragma solidity >=0.4.22 <0.6.0; + + contract Purchase { + uint public value; + address payable public seller; + address payable public buyer; + enum State { Created, Locked, Inactive } + State public state; + + // Ensure that `msg.value` is an even number. + // Division will truncate if it is an odd number. + // Check via multiplication that it wasn't an odd number. + constructor() public payable { + seller = msg.sender; + value = msg.value / 2; + require((2 * value) == msg.value, "Value has to be even."); + } + + modifier condition(bool _condition) { + require(_condition); + _; + } + + modifier onlyBuyer() { + require( + msg.sender == buyer, + "Only buyer can call this." + ); + _; + } + + modifier onlySeller() { + require( + msg.sender == seller, + "Only seller can call this." + ); + _; + } + + modifier inState(State _state) { + require( + state == _state, + "Invalid state." + ); + _; + } + + event Aborted(); + event PurchaseConfirmed(); + event ItemReceived(); + + /// Abort the purchase and reclaim the ether. + /// Can only be called by the seller before + /// the contract is locked. + function abort() + public + onlySeller + inState(State.Created) + { + emit Aborted(); + state = State.Inactive; + seller.transfer(address(this).balance); + } + + /// Confirm the purchase as buyer. + /// Transaction has to include `2 * value` ether. + /// The ether will be locked until confirmReceived + /// is called. + function confirmPurchase() + public + inState(State.Created) + condition(msg.value == (2 * value)) + payable + { + emit PurchaseConfirmed(); + buyer = msg.sender; + state = State.Locked; + } + + /// Confirm that you (the buyer) received the item. + /// This will release the locked ether. + function confirmReceived() + public + onlyBuyer + inState(State.Locked) + { + emit ItemReceived(); + // It is important to change the state first because + // otherwise, the contracts called using `send` below + // can call in again here. + state = State.Inactive; + + // NOTE: This actually allows both the buyer and the seller to + // block the refund - the withdraw pattern should be used. + + buyer.transfer(value); + seller.transfer(address(this).balance); + } + } \ No newline at end of file diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 0e7d507d..e94a9c29 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -345,113 +345,7 @@ high or low invalid bids. } -.. index:: purchase, remote purchase, escrow - -******************** -Safe Remote Purchase -******************** - -:: - - pragma solidity >=0.4.22 <0.6.0; - - contract Purchase { - uint public value; - address payable public seller; - address payable public buyer; - enum State { Created, Locked, Inactive } - State public state; - - // Ensure that `msg.value` is an even number. - // Division will truncate if it is an odd number. - // Check via multiplication that it wasn't an odd number. - constructor() public payable { - seller = msg.sender; - value = msg.value / 2; - require((2 * value) == msg.value, "Value has to be even."); - } - - modifier condition(bool _condition) { - require(_condition); - _; - } - - modifier onlyBuyer() { - require( - msg.sender == buyer, - "Only buyer can call this." - ); - _; - } - - modifier onlySeller() { - require( - msg.sender == seller, - "Only seller can call this." - ); - _; - } - - modifier inState(State _state) { - require( - state == _state, - "Invalid state." - ); - _; - } - - event Aborted(); - event PurchaseConfirmed(); - event ItemReceived(); - - /// Abort the purchase and reclaim the ether. - /// Can only be called by the seller before - /// the contract is locked. - function abort() - public - onlySeller - inState(State.Created) - { - emit Aborted(); - state = State.Inactive; - seller.transfer(address(this).balance); - } - - /// Confirm the purchase as buyer. - /// Transaction has to include `2 * value` ether. - /// The ether will be locked until confirmReceived - /// is called. - function confirmPurchase() - public - inState(State.Created) - condition(msg.value == (2 * value)) - payable - { - emit PurchaseConfirmed(); - buyer = msg.sender; - state = State.Locked; - } - - /// Confirm that you (the buyer) received the item. - /// This will release the locked ether. - function confirmReceived() - public - onlyBuyer - inState(State.Locked) - { - emit ItemReceived(); - // It is important to change the state first because - // otherwise, the contracts called using `send` below - // can call in again here. - state = State.Inactive; - - // NOTE: This actually allows both the buyer and the seller to - // block the refund - the withdraw pattern should be used. - - buyer.transfer(value); - seller.transfer(address(this).balance); - } - } +.. include:: examples/safe-remote.rst ******************** Micropayment Channel -- cgit From 29088c4f4852049da5a5b43403b2a70cb4eb9795 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 9 Jan 2019 12:25:45 +0100 Subject: Split mapping types into new file --- docs/types.rst | 59 +------------------------------------------- docs/types/mapping-types.rst | 58 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 58 deletions(-) create mode 100644 docs/types/mapping-types.rst diff --git a/docs/types.rst b/docs/types.rst index ea45b7d7..d5622804 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -22,64 +22,7 @@ tuple with a second `bool` value denoting success. .. include:: types/reference-types.rst -.. index:: !mapping -.. _mapping-types: - -Mapping Types -============= - -You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``. -The ``_KeyType`` can be any elementary type. This means it can be any of -the built-in value types plus ``bytes`` and ``string``. User-defined -or complex types like contract types, enums, mappings, structs and any array type -apart from ``bytes`` and ``string`` are not allowed. -``_ValueType`` can be any type, including mappings. - -You can think of mappings as `hash tables `_, which are virtually initialised -such that every possible key exists and is mapped to a value whose -byte-representation is all zeros, a type's :ref:`default value `. The similarity ends there, the key data is not stored in a -mapping, only its ``keccak256`` hash is used to look up the value. - -Because of this, mappings do not have a length or a concept of a key or -value being set. - -Mappings can only have a data location of ``storage`` and thus -are allowed for state variables, as storage reference types -in functions, or as parameters for library functions. -They cannot be used as parameters or return parameters -of contract functions that are publicly visible. - -You can mark variables of mapping type as ``public`` and Solidity creates a -:ref:`getter ` for you. The ``_KeyType`` becomes a -parameter for the getter. If ``_ValueType`` is a value type or a struct, -the getter returns ``_ValueType``. -If ``_ValueType`` is an array or a mapping, the getter has one parameter for -each ``_KeyType``, recursively. For example with a mapping: - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract MappingExample { - mapping(address => uint) public balances; - - function update(uint newBalance) public { - balances[msg.sender] = newBalance; - } - } - - contract MappingUser { - function f() public returns (uint) { - MappingExample m = new MappingExample(); - m.update(100); - return m.balances(address(this)); - } - } - - -.. note:: - Mappings are not iterable, but it is possible to implement a data structure - on top of them. For an example, see `iterable mapping `_. +.. include:: types/mapping-types.rst .. include:: types/operators.rst diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst new file mode 100644 index 00000000..935ed6b4 --- /dev/null +++ b/docs/types/mapping-types.rst @@ -0,0 +1,58 @@ +.. index:: !mapping +.. _mapping-types: + +Mapping Types +============= + +You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``. +The ``_KeyType`` can be any elementary type. This means it can be any of +the built-in value types plus ``bytes`` and ``string``. User-defined +or complex types like contract types, enums, mappings, structs and any array type +apart from ``bytes`` and ``string`` are not allowed. +``_ValueType`` can be any type, including mappings. + +You can think of mappings as `hash tables `_, which are virtually initialised +such that every possible key exists and is mapped to a value whose +byte-representation is all zeros, a type's :ref:`default value `. The similarity ends there, the key data is not stored in a +mapping, only its ``keccak256`` hash is used to look up the value. + +Because of this, mappings do not have a length or a concept of a key or +value being set. + +Mappings can only have a data location of ``storage`` and thus +are allowed for state variables, as storage reference types +in functions, or as parameters for library functions. +They cannot be used as parameters or return parameters +of contract functions that are publicly visible. + +You can mark variables of mapping type as ``public`` and Solidity creates a +:ref:`getter ` for you. The ``_KeyType`` becomes a +parameter for the getter. If ``_ValueType`` is a value type or a struct, +the getter returns ``_ValueType``. +If ``_ValueType`` is an array or a mapping, the getter has one parameter for +each ``_KeyType``, recursively. For example with a mapping: + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract MappingExample { + mapping(address => uint) public balances; + + function update(uint newBalance) public { + balances[msg.sender] = newBalance; + } + } + + contract MappingUser { + function f() public returns (uint) { + MappingExample m = new MappingExample(); + m.update(100); + return m.balances(address(this)); + } + } + + +.. note:: + Mappings are not iterable, but it is possible to implement a data structure + on top of them. For an example, see `iterable mapping `_. -- cgit From 620cbdc8001dc849f5cf620f8e2a3ed6f7668d4d Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 9 Jan 2019 12:48:36 +0100 Subject: Split conversion sections into new doc --- docs/types.rst | 128 +--------------------------------------------- docs/types/conversion.rst | 127 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 127 deletions(-) create mode 100644 docs/types/conversion.rst diff --git a/docs/types.rst b/docs/types.rst index ea45b7d7..54e6ca7f 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -83,130 +83,4 @@ each ``_KeyType``, recursively. For example with a mapping: .. include:: types/operators.rst -.. index:: ! type;conversion, ! cast - -.. _types-conversion-elementary-types: - -Conversions between Elementary Types -==================================== - -Implicit Conversions --------------------- - -If an operator is applied to different types, the compiler tries to -implicitly convert one of the operands to the type of the other (the same is -true for assignments). In general, an implicit conversion between value-types -is possible if it -makes sense semantically and no information is lost: ``uint8`` is convertible to -``uint16`` and ``int128`` to ``int256``, but ``int8`` is not convertible to ``uint256`` -(because ``uint256`` cannot hold e.g. ``-1``). - -For more details, please consult the sections about the types themselves. - -Explicit Conversions --------------------- - -If the compiler does not allow implicit conversion but you know what you are -doing, an explicit type conversion is sometimes possible. Note that this may -give you some unexpected behaviour and allows you to bypass some security -features of the compiler, so be sure to test that the -result is what you want! Take the following example where you are converting -a negative ``int8`` to a ``uint``: - -:: - - int8 y = -3; - uint x = uint(y); - -At the end of this code snippet, ``x`` will have the value ``0xfffff..fd`` (64 hex -characters), which is -3 in the two's complement representation of 256 bits. - -If an integer is explicitly converted to a smaller type, higher-order bits are -cut off:: - - uint32 a = 0x12345678; - uint16 b = uint16(a); // b will be 0x5678 now - -If an integer is explicitly converted to a larger type, it is padded on the left (i.e. at the higher order end). -The result of the conversion will compare equal to the original integer:: - - uint16 a = 0x1234; - uint32 b = uint32(a); // b will be 0x00001234 now - assert(a == b); - -Fixed-size bytes types behave differently during conversions. They can be thought of as -sequences of individual bytes and converting to a smaller type will cut off the -sequence:: - - bytes2 a = 0x1234; - bytes1 b = bytes1(a); // b will be 0x12 - -If a fixed-size bytes type is explicitly converted to a larger type, it is padded on -the right. Accessing the byte at a fixed index will result in the same value before and -after the conversion (if the index is still in range):: - - bytes2 a = 0x1234; - bytes4 b = bytes4(a); // b will be 0x12340000 - assert(a[0] == b[0]); - assert(a[1] == b[1]); - -Since integers and fixed-size byte arrays behave differently when truncating or -padding, explicit conversions between integers and fixed-size byte arrays are only allowed, -if both have the same size. If you want to convert between integers and fixed-size byte arrays of -different size, you have to use intermediate conversions that make the desired truncation and padding -rules explicit:: - - bytes2 a = 0x1234; - uint32 b = uint16(a); // b will be 0x00001234 - uint32 c = uint32(bytes4(a)); // c will be 0x12340000 - uint8 d = uint8(uint16(a)); // d will be 0x34 - uint8 e = uint8(bytes1(a)); // e will be 0x12 - -.. _types-conversion-literals: - -Conversions between Literals and Elementary Types -================================================= - -Integer Types -------------- - -Decimal and hexadecimal number literals can be implicitly converted to any integer type -that is large enough to represent it without truncation:: - - uint8 a = 12; // fine - uint32 b = 1234; // fine - uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456 - -Fixed-Size Byte Arrays ----------------------- - -Decimal number literals cannot be implicitly converted to fixed-size byte arrays. Hexadecimal -number literals can be, but only if the number of hex digits exactly fits the size of the bytes -type. As an exception both decimal and hexadecimal literals which have a value of zero can be -converted to any fixed-size bytes type:: - - bytes2 a = 54321; // not allowed - bytes2 b = 0x12; // not allowed - bytes2 c = 0x123; // not allowed - bytes2 d = 0x1234; // fine - bytes2 e = 0x0012; // fine - bytes4 f = 0; // fine - bytes4 g = 0x0; // fine - -String literals and hex string literals can be implicitly converted to fixed-size byte arrays, -if their number of characters matches the size of the bytes type:: - - bytes2 a = hex"1234"; // fine - bytes2 b = "xy"; // fine - bytes2 c = hex"12"; // not allowed - bytes2 d = hex"123"; // not allowed - bytes2 e = "x"; // not allowed - bytes2 f = "xyz"; // not allowed - -Addresses ---------- - -As described in :ref:`address_literals`, hex literals of the correct size that pass the checksum -test are of ``address`` type. No other literals can be implicitly converted to the ``address`` type. - -Explicit conversions from ``bytes20`` or any integer type to ``address`` result in ``address payable``. +.. include:: types/conversion.rst \ No newline at end of file diff --git a/docs/types/conversion.rst b/docs/types/conversion.rst new file mode 100644 index 00000000..5a9f84c0 --- /dev/null +++ b/docs/types/conversion.rst @@ -0,0 +1,127 @@ +.. index:: ! type;conversion, ! cast + +.. _types-conversion-elementary-types: + +Conversions between Elementary Types +==================================== + +Implicit Conversions +-------------------- + +If an operator is applied to different types, the compiler tries to +implicitly convert one of the operands to the type of the other (the same is +true for assignments). In general, an implicit conversion between value-types +is possible if it +makes sense semantically and no information is lost: ``uint8`` is convertible to +``uint16`` and ``int128`` to ``int256``, but ``int8`` is not convertible to ``uint256`` +(because ``uint256`` cannot hold e.g. ``-1``). + +For more details, please consult the sections about the types themselves. + +Explicit Conversions +-------------------- + +If the compiler does not allow implicit conversion but you know what you are +doing, an explicit type conversion is sometimes possible. Note that this may +give you some unexpected behaviour and allows you to bypass some security +features of the compiler, so be sure to test that the +result is what you want! Take the following example where you are converting +a negative ``int8`` to a ``uint``: + +:: + + int8 y = -3; + uint x = uint(y); + +At the end of this code snippet, ``x`` will have the value ``0xfffff..fd`` (64 hex +characters), which is -3 in the two's complement representation of 256 bits. + +If an integer is explicitly converted to a smaller type, higher-order bits are +cut off:: + + uint32 a = 0x12345678; + uint16 b = uint16(a); // b will be 0x5678 now + +If an integer is explicitly converted to a larger type, it is padded on the left (i.e. at the higher order end). +The result of the conversion will compare equal to the original integer:: + + uint16 a = 0x1234; + uint32 b = uint32(a); // b will be 0x00001234 now + assert(a == b); + +Fixed-size bytes types behave differently during conversions. They can be thought of as +sequences of individual bytes and converting to a smaller type will cut off the +sequence:: + + bytes2 a = 0x1234; + bytes1 b = bytes1(a); // b will be 0x12 + +If a fixed-size bytes type is explicitly converted to a larger type, it is padded on +the right. Accessing the byte at a fixed index will result in the same value before and +after the conversion (if the index is still in range):: + + bytes2 a = 0x1234; + bytes4 b = bytes4(a); // b will be 0x12340000 + assert(a[0] == b[0]); + assert(a[1] == b[1]); + +Since integers and fixed-size byte arrays behave differently when truncating or +padding, explicit conversions between integers and fixed-size byte arrays are only allowed, +if both have the same size. If you want to convert between integers and fixed-size byte arrays of +different size, you have to use intermediate conversions that make the desired truncation and padding +rules explicit:: + + bytes2 a = 0x1234; + uint32 b = uint16(a); // b will be 0x00001234 + uint32 c = uint32(bytes4(a)); // c will be 0x12340000 + uint8 d = uint8(uint16(a)); // d will be 0x34 + uint8 e = uint8(bytes1(a)); // e will be 0x12 + +.. _types-conversion-literals: + +Conversions between Literals and Elementary Types +================================================= + +Integer Types +------------- + +Decimal and hexadecimal number literals can be implicitly converted to any integer type +that is large enough to represent it without truncation:: + + uint8 a = 12; // fine + uint32 b = 1234; // fine + uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456 + +Fixed-Size Byte Arrays +---------------------- + +Decimal number literals cannot be implicitly converted to fixed-size byte arrays. Hexadecimal +number literals can be, but only if the number of hex digits exactly fits the size of the bytes +type. As an exception both decimal and hexadecimal literals which have a value of zero can be +converted to any fixed-size bytes type:: + + bytes2 a = 54321; // not allowed + bytes2 b = 0x12; // not allowed + bytes2 c = 0x123; // not allowed + bytes2 d = 0x1234; // fine + bytes2 e = 0x0012; // fine + bytes4 f = 0; // fine + bytes4 g = 0x0; // fine + +String literals and hex string literals can be implicitly converted to fixed-size byte arrays, +if their number of characters matches the size of the bytes type:: + + bytes2 a = hex"1234"; // fine + bytes2 b = "xy"; // fine + bytes2 c = hex"12"; // not allowed + bytes2 d = hex"123"; // not allowed + bytes2 e = "x"; // not allowed + bytes2 f = "xyz"; // not allowed + +Addresses +--------- + +As described in :ref:`address_literals`, hex literals of the correct size that pass the checksum +test are of ``address`` type. No other literals can be implicitly converted to the ``address`` type. + +Explicit conversions from ``bytes20`` or any integer type to ``address`` result in ``address payable``. -- cgit From dbe88755af2cf1593b92cd6a08a097fcd8db8c47 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 9 Jan 2019 14:31:51 +0100 Subject: Remove random FAQ item --- docs/frequently-asked-questions.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 2cc082b4..8d7caefe 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -77,15 +77,6 @@ otherwise an exception is thrown. Advanced Questions ****************** -How do you get a random number in a contract? (Implement a self-returning gambling contract.) -============================================================================================= - -Getting randomness right is often the crucial part in a crypto project and -most failures result from bad random number generators. - -If you do not want it to be safe, you build something similar to the `coin flipper `_ -but otherwise, rather use a contract that supplies randomness, like the `RANDAO `_. - Get return value from non-constant function from another contract ================================================================= -- cgit From b49f6781efae5d0fb289a42c303767c406376508 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 9 Jan 2019 14:35:57 +0100 Subject: Remove Multi-dimensional array FAQ --- docs/frequently-asked-questions.rst | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 2cc082b4..653ff483 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -94,23 +94,6 @@ The key point is that the calling contract needs to know about the function it i See `ping.sol `_ and `pong.sol `_. -How do you create 2-dimensional arrays? -======================================= - -See `2D_array.sol `_. - -Note that filling a 10x10 square of ``uint8`` + contract creation took more than ``800,000`` -gas at the time of this writing. 17x17 took ``2,000,000`` gas. With the limit at -3.14 million... well, there’s a pretty low ceiling for what you can create right -now. - -Note that merely "creating" the array is free, the costs are in filling it. - -Note2: Optimizing storage access can pull the gas costs down considerably, because -32 ``uint8`` values can be stored in a single slot. The problem is that these optimizations -currently do not work across loops and also have a problem with bounds checking. -You might get much better results in the future, though. - How do I initialize a contract with only a specific amount of wei? ================================================================== -- cgit From f4ee72494f5e6baa7ccf73bb8309f8e1b59446f6 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 19 Dec 2018 17:51:33 +0100 Subject: Remove explicit conversion FAQ item, covered elsewhere --- docs/frequently-asked-questions.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 2cc082b4..02bf908b 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -178,16 +178,6 @@ does not fit inside this range, it is truncated. These truncations can have above is necessary to avoid certain attacks. -Why are explicit conversions between fixed-size bytes types and integer types failing? -====================================================================================== - -Since version 0.5.0 explicit conversions between fixed-size byte arrays and integers are only allowed, -if both types have the same size. This prevents unexpected behaviour when truncating or padding. -Such conversions are still possible, but intermediate casts are required that make the desired -truncation and padding convention explicit. See :ref:`types-conversion-elementary-types` for a full -explanation and examples. - - Why can number literals not be converted to fixed-size bytes types? =================================================================== -- cgit From b7eaa4f8d3bfd4a1a04899f26bb1292ea1949f64 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 14 Jan 2019 12:21:06 +0100 Subject: Ensuring we use at least boost 1.65, build static on Xenial, and adapt CircleCI accordingly. --- .circleci/config.yml | 10 ++++++---- ReleaseChecklist.md | 2 +- cmake/EthDependencies.cmake | 5 ++++- scripts/install_deps.cmake | 4 ++-- scripts/release_ppa.sh | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5c85007e..66fe6430 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -104,7 +104,7 @@ jobs: test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js build_x86_linux: docker: - - image: buildpack-deps:artful + - image: buildpack-deps:bionic environment: TERM: xterm COVERAGE: "ON" @@ -212,7 +212,7 @@ jobs: test_check_style: docker: - - image: buildpack-deps:artful + - image: buildpack-deps:bionic steps: - checkout - run: @@ -238,7 +238,7 @@ jobs: test_x86_linux: docker: - - image: buildpack-deps:artful + - image: buildpack-deps:bionic environment: TERM: xterm steps: @@ -315,7 +315,9 @@ jobs: docs: docker: - - image: buildpack-deps:artful + - image: buildpack-deps:bionic + environment: + DEBIAN_FRONTEND: noninteractive steps: - checkout - run: diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index c10db742..2610daa2 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -12,7 +12,7 @@ Checklist for making a release: - [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. - [ ] Upload the source tarball (in the upload directory) to the release page. - [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key). - - [ ] Once the ``~ethereum/ubuntu/ethereum-static`` PPA build is finished and published for all platforms (make sure not to do this earlier), copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` while selecting ``Copy existing binaries``. + - [ ] Once the ``~ethereum/ubuntu/ethereum-static`` PPA build is finished and published for all platforms (make sure not to do this earlier), copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``. - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``). - [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash) - [ ] Update the default version on readthedocs. diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index cc2f8711..477a604d 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -5,6 +5,9 @@ function(eth_show_dependency DEP NAME) get_property(DISPLAYED GLOBAL PROPERTY ETH_${DEP}_DISPLAYED) if (NOT DISPLAYED) set_property(GLOBAL PROPERTY ETH_${DEP}_DISPLAYED TRUE) + if (NOT("${${DEP}_VERSION}" STREQUAL "")) + message(STATUS "${NAME} version: ${${DEP}_VERSION}") + endif() message(STATUS "${NAME} headers: ${${DEP}_INCLUDE_DIRS}") message(STATUS "${NAME} lib : ${${DEP}_LIBRARIES}") if (NOT("${${DEP}_DLLS}" STREQUAL "")) @@ -38,6 +41,6 @@ set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts) set(Boost_USE_MULTITHREADED ON) option(Boost_USE_STATIC_LIBS "Link Boost statically" ON) -find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options system) +find_package(Boost 1.65.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options system) eth_show_dependency(Boost boost) diff --git a/scripts/install_deps.cmake b/scripts/install_deps.cmake index d1284b9e..0cb0ed62 100644 --- a/scripts/install_deps.cmake +++ b/scripts/install_deps.cmake @@ -90,10 +90,10 @@ function(prepare_package_source NAME VERSION URL) endfunction() set(INSTALL_DIR "${ROOT_DIR}/install") -set(SERVER "https://github.com/ethereum/cpp-dependencies/releases/download/vc140/") +set(SERVER "https://github.com/ethereum/cpp-dependencies/releases/download/vs2017/") function(download_and_install PACKAGE_NAME) download_and_unpack("${SERVER}${PACKAGE_NAME}.tar.gz" ${INSTALL_DIR}) endfunction(download_and_install) -download_and_install("boost-1.61") +download_and_install("boost-1.67.0") diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 4ba0e644..6cba7dff 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -53,7 +53,7 @@ packagename=solc static_build_distribution=cosmic -for distribution in xenial bionic cosmic STATIC +for distribution in bionic cosmic STATIC do cd /tmp/ rm -rf $distribution -- cgit From 18cceba81f1f0bf45561dc924a68309a1b1878b8 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Jan 2019 14:27:32 +0100 Subject: Split functions docs --- docs/contracts.rst | 400 +------------------------------------------ docs/contracts/functions.rst | 398 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 399 insertions(+), 399 deletions(-) create mode 100644 docs/contracts/functions.rst diff --git a/docs/contracts.rst b/docs/contracts.rst index 859cd9e9..646e4cdc 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -19,405 +19,7 @@ inaccessible. .. include:: contracts/function-modifiers.rst .. include:: contracts/constant-state-variables.rst - -.. index:: ! functions - -.. _functions: - -********* -Functions -********* - -.. _function-parameters-return-variables: - -Function Parameters and Return Variables -======================================== - -As in JavaScript, functions may take parameters as input. Unlike in JavaScript -and C, functions may also return an arbitrary number of values as output. - -Function Parameters -------------------- - -Function parameters are declared the same way as variables, and the name of -unused parameters can be omitted. - -For example, if you want your contract to accept one kind of external call -with two integers, you would use something like:: - - pragma solidity >=0.4.16 <0.6.0; - - contract Simple { - uint sum; - function taker(uint _a, uint _b) public { - sum = _a + _b; - } - } - -Function parameters can be used as any other local variable and they can also be assigned to. - -.. note:: - - An :ref:`external function` cannot accept a - multi-dimensional array as an input - parameter. This functionality is possible if you enable the new - experimental ``ABIEncoderV2`` feature by adding ``pragma experimental ABIEncoderV2;`` to your source file. - - An :ref:`internal function` can accept a - multi-dimensional array without enabling the feature. - -.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct - -Return Variables ----------------- - -Function return variables are declared with the same syntax after the -``returns`` keyword. - -For example, suppose you want to return two results: the sum and the product of -two integers passed as function parameters, then you use something like:: - - pragma solidity >=0.4.16 <0.6.0; - - contract Simple { - function arithmetic(uint _a, uint _b) - public - pure - returns (uint o_sum, uint o_product) - { - o_sum = _a + _b; - o_product = _a * _b; - } - } - -The names of return variables can be omitted. -Return variables can be used as any other local variable and they -are initialized with their :ref:`default value ` and have that value unless explicitly set. - -You can either explicitly assign to return variables and -then leave the function using ``return;``, -or you can provide return values -(either a single or :ref:`multiple ones`) directly with the ``return`` -statement:: - - pragma solidity >=0.4.16 <0.6.0; - - contract Simple { - function arithmetic(uint _a, uint _b) - public - pure - returns (uint o_sum, uint o_product) - { - return (_a + _b, _a * _b); - } - } - -This form is equivalent to first assigning values to the -return variables and then using ``return;`` to leave the function. - -.. note:: - You cannot return some types from non-internal functions, notably - multi-dimensional dynamic arrays and structs. If you enable the - new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental - ABIEncoderV2;`` to your source file then more types are available, but - ``mapping`` types are still limited to inside a single contract and you - cannot transfer them. - -.. _multi-return: - -Returning Multiple Values -------------------------- - -When a function has multiple return types, the statement ``return (v0, v1, ..., vn)`` can be used to return multiple values. -The number of components must be the same as the number of return types. - -.. index:: ! view function, function;view - -.. _view-functions: - -View Functions -============== - -Functions can be declared ``view`` in which case they promise not to modify the state. - -.. note:: - If the compiler's EVM target is Byzantium or newer (default) the opcode - ``STATICCALL`` is used for ``view`` functions which enforces the state - to stay unmodified as part of the EVM execution. For library ``view`` functions - ``DELEGATECALL`` is used, because there is no combined ``DELEGATECALL`` and ``STATICCALL``. - This means library ``view`` functions do not have run-time checks that prevent state - modifications. This should not impact security negatively because library code is - usually known at compile-time and the static checker performs compile-time checks. - -The following statements are considered modifying the state: - -#. Writing to state variables. -#. :ref:`Emitting events `. -#. :ref:`Creating other contracts `. -#. Using ``selfdestruct``. -#. Sending Ether via calls. -#. Calling any function not marked ``view`` or ``pure``. -#. Using low-level calls. -#. Using inline assembly that contains certain opcodes. - -:: - - pragma solidity ^0.5.0; - - contract C { - function f(uint a, uint b) public view returns (uint) { - return a * (b + 42) + now; - } - } - -.. note:: - ``constant`` on functions used to be an alias to ``view``, but this was dropped in version 0.5.0. - -.. note:: - Getter methods are automatically marked ``view``. - -.. note:: - Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode - for ``view`` functions. - This enabled state modifications in ``view`` functions through the use of - invalid explicit type conversions. - By using ``STATICCALL`` for ``view`` functions, modifications to the - state are prevented on the level of the EVM. - -.. index:: ! pure function, function;pure - -.. _pure-functions: - -Pure Functions -============== - -Functions can be declared ``pure`` in which case they promise not to read from or modify the state. - -.. note:: - If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used, - which does not guarantee that the state is not read, but at least that it is not modified. - -In addition to the list of state modifying statements explained above, the following are considered reading from the state: - -#. Reading from state variables. -#. Accessing ``address(this).balance`` or ``
.balance``. -#. Accessing any of the members of ``block``, ``tx``, ``msg`` (with the exception of ``msg.sig`` and ``msg.data``). -#. Calling any function not marked ``pure``. -#. Using inline assembly that contains certain opcodes. - -:: - - pragma solidity ^0.5.0; - - contract C { - function f(uint a, uint b) public pure returns (uint) { - return a * (b + 42); - } - } - -Pure functions are able to use the `revert()` and `require()` functions to revert -potential state changes when an :ref:`error occurs `. - -Reverting a state change is not considered a "state modification", as only changes to the -state made previously in code that did not have the ``view`` or ``pure`` restriction -are reverted and that code has the option to catch the ``revert`` and not pass it on. - -This behaviour is also in line with the ``STATICCALL`` opcode. - -.. warning:: - It is not possible to prevent functions from reading the state at the level - of the EVM, it is only possible to prevent them from writing to the state - (i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not). - -.. note:: - Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode - for ``pure`` functions. - This enabled state modifications in ``pure`` functions through the use of - invalid explicit type conversions. - By using ``STATICCALL`` for ``pure`` functions, modifications to the - state are prevented on the level of the EVM. - -.. note:: - Prior to version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state. - It is a compile-time type check, which can be circumvented doing invalid explicit conversions - between contract types, because the compiler can verify that the type of the contract does - not do state-changing operations, but it cannot check that the contract that will be called - at runtime is actually of that type. - -.. index:: ! fallback function, function;fallback - -.. _fallback-function: - -Fallback Function -================= - -A contract can have exactly one unnamed function. This function cannot have -arguments, cannot return anything and has to have ``external`` visibility. -It is executed on a call to the contract if none of the other -functions match the given function identifier (or if no data was supplied at -all). - -Furthermore, this function is executed whenever the contract receives plain -Ether (without data). Additionally, in order to receive Ether, the fallback function -must be marked ``payable``. If no such function exists, the contract cannot receive -Ether through regular transactions. - -In the worst case, the fallback function can only rely on 2300 gas being -available (for example when `send` or `transfer` is used), leaving little -room to perform other operations except basic logging. The following operations -will consume more gas than the 2300 gas stipend: - -- Writing to storage -- Creating a contract -- Calling an external function which consumes a large amount of gas -- Sending Ether - -Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it. - -.. note:: - Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve - any payload supplied with the call. - -.. warning:: - The fallback function is also executed if the caller meant to call - a function that is not available. If you want to implement the fallback - function only to receive ether, you should add a check - like ``require(msg.data.length == 0)`` to prevent invalid calls. - -.. warning:: - Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``) - but do not define a fallback function - throw an exception, sending back the Ether (this was different - before Solidity v0.4.0). So if you want your contract to receive Ether, - you have to implement a payable fallback function. - -.. warning:: - A contract without a payable fallback function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`) - or as a destination of a ``selfdestruct``. - - A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it. - - It also means that ``address(this).balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the fallback function). - -:: - - pragma solidity ^0.5.0; - - contract Test { - // This function is called for all messages sent to - // this contract (there is no other function). - // Sending Ether to this contract will cause an exception, - // because the fallback function does not have the `payable` - // modifier. - function() external { x = 1; } - uint x; - } - - - // This contract keeps all Ether sent to it with no way - // to get it back. - contract Sink { - function() external payable { } - } - - contract Caller { - function callTest(Test test) public returns (bool) { - (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()")); - require(success); - // results in test.x becoming == 1. - - // address(test) will not allow to call ``send`` directly, since ``test`` has no payable - // fallback function. It has to be converted to the ``address payable`` type via an - // intermediate conversion to ``uint160`` to even allow calling ``send`` on it. - address payable testPayable = address(uint160(address(test))); - - // If someone sends ether to that contract, - // the transfer will fail, i.e. this returns false here. - return testPayable.send(2 ether); - } - } - -.. index:: ! overload - -.. _overload-function: - -Function Overloading -==================== - -A contract can have multiple functions of the same name but with different parameter -types. -This process is called "overloading" and also applies to inherited functions. -The following example shows overloading of the function -``f`` in the scope of contract ``A``. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract A { - function f(uint _in) public pure returns (uint out) { - out = _in; - } - - function f(uint _in, bool _really) public pure returns (uint out) { - if (_really) - out = _in; - } - } - -Overloaded functions are also present in the external interface. It is an error if two -externally visible functions differ by their Solidity types but not by their external types. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - // This will not compile - contract A { - function f(B _in) public pure returns (B out) { - out = _in; - } - - function f(address _in) public pure returns (address out) { - out = _in; - } - } - - contract B { - } - - -Both ``f`` function overloads above end up accepting the address type for the ABI although -they are considered different inside Solidity. - -Overload resolution and Argument matching ------------------------------------------ - -Overloaded functions are selected by matching the function declarations in the current scope -to the arguments supplied in the function call. Functions are selected as overload candidates -if all arguments can be implicitly converted to the expected types. If there is not exactly one -candidate, resolution fails. - -.. note:: - Return parameters are not taken into account for overload resolution. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract A { - function f(uint8 _in) public pure returns (uint8 out) { - out = _in; - } - - function f(uint256 _in) public pure returns (uint256 out) { - out = _in; - } - } - -Calling ``f(50)`` would create a type error since ``50`` can be implicitly converted both to ``uint8`` -and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly -converted to ``uint8``. +.. include:: contracts/functions.rst .. include:: contracts/events.rst diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst new file mode 100644 index 00000000..76245952 --- /dev/null +++ b/docs/contracts/functions.rst @@ -0,0 +1,398 @@ +.. index:: ! functions + +.. _functions: + +********* +Functions +********* + +.. _function-parameters-return-variables: + +Function Parameters and Return Variables +======================================== + +As in JavaScript, functions may take parameters as input. Unlike in JavaScript +and C, functions may also return an arbitrary number of values as output. + +Function Parameters +------------------- + +Function parameters are declared the same way as variables, and the name of +unused parameters can be omitted. + +For example, if you want your contract to accept one kind of external call +with two integers, you would use something like:: + + pragma solidity >=0.4.16 <0.6.0; + + contract Simple { + uint sum; + function taker(uint _a, uint _b) public { + sum = _a + _b; + } + } + +Function parameters can be used as any other local variable and they can also be assigned to. + +.. note:: + + An :ref:`external function` cannot accept a + multi-dimensional array as an input + parameter. This functionality is possible if you enable the new + experimental ``ABIEncoderV2`` feature by adding ``pragma experimental ABIEncoderV2;`` to your source file. + + An :ref:`internal function` can accept a + multi-dimensional array without enabling the feature. + +.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct + +Return Variables +---------------- + +Function return variables are declared with the same syntax after the +``returns`` keyword. + +For example, suppose you want to return two results: the sum and the product of +two integers passed as function parameters, then you use something like:: + + pragma solidity >=0.4.16 <0.6.0; + + contract Simple { + function arithmetic(uint _a, uint _b) + public + pure + returns (uint o_sum, uint o_product) + { + o_sum = _a + _b; + o_product = _a * _b; + } + } + +The names of return variables can be omitted. +Return variables can be used as any other local variable and they +are initialized with their :ref:`default value ` and have that value unless explicitly set. + +You can either explicitly assign to return variables and +then leave the function using ``return;``, +or you can provide return values +(either a single or :ref:`multiple ones`) directly with the ``return`` +statement:: + + pragma solidity >=0.4.16 <0.6.0; + + contract Simple { + function arithmetic(uint _a, uint _b) + public + pure + returns (uint o_sum, uint o_product) + { + return (_a + _b, _a * _b); + } + } + +This form is equivalent to first assigning values to the +return variables and then using ``return;`` to leave the function. + +.. note:: + You cannot return some types from non-internal functions, notably + multi-dimensional dynamic arrays and structs. If you enable the + new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental + ABIEncoderV2;`` to your source file then more types are available, but + ``mapping`` types are still limited to inside a single contract and you + cannot transfer them. + +.. _multi-return: + +Returning Multiple Values +------------------------- + +When a function has multiple return types, the statement ``return (v0, v1, ..., vn)`` can be used to return multiple values. +The number of components must be the same as the number of return types. + +.. index:: ! view function, function;view + +.. _view-functions: + +View Functions +============== + +Functions can be declared ``view`` in which case they promise not to modify the state. + +.. note:: + If the compiler's EVM target is Byzantium or newer (default) the opcode + ``STATICCALL`` is used for ``view`` functions which enforces the state + to stay unmodified as part of the EVM execution. For library ``view`` functions + ``DELEGATECALL`` is used, because there is no combined ``DELEGATECALL`` and ``STATICCALL``. + This means library ``view`` functions do not have run-time checks that prevent state + modifications. This should not impact security negatively because library code is + usually known at compile-time and the static checker performs compile-time checks. + +The following statements are considered modifying the state: + +#. Writing to state variables. +#. :ref:`Emitting events `. +#. :ref:`Creating other contracts `. +#. Using ``selfdestruct``. +#. Sending Ether via calls. +#. Calling any function not marked ``view`` or ``pure``. +#. Using low-level calls. +#. Using inline assembly that contains certain opcodes. + +:: + + pragma solidity ^0.5.0; + + contract C { + function f(uint a, uint b) public view returns (uint) { + return a * (b + 42) + now; + } + } + +.. note:: + ``constant`` on functions used to be an alias to ``view``, but this was dropped in version 0.5.0. + +.. note:: + Getter methods are automatically marked ``view``. + +.. note:: + Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode + for ``view`` functions. + This enabled state modifications in ``view`` functions through the use of + invalid explicit type conversions. + By using ``STATICCALL`` for ``view`` functions, modifications to the + state are prevented on the level of the EVM. + +.. index:: ! pure function, function;pure + +.. _pure-functions: + +Pure Functions +============== + +Functions can be declared ``pure`` in which case they promise not to read from or modify the state. + +.. note:: + If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used, + which does not guarantee that the state is not read, but at least that it is not modified. + +In addition to the list of state modifying statements explained above, the following are considered reading from the state: + +#. Reading from state variables. +#. Accessing ``address(this).balance`` or ``
.balance``. +#. Accessing any of the members of ``block``, ``tx``, ``msg`` (with the exception of ``msg.sig`` and ``msg.data``). +#. Calling any function not marked ``pure``. +#. Using inline assembly that contains certain opcodes. + +:: + + pragma solidity ^0.5.0; + + contract C { + function f(uint a, uint b) public pure returns (uint) { + return a * (b + 42); + } + } + +Pure functions are able to use the `revert()` and `require()` functions to revert +potential state changes when an :ref:`error occurs `. + +Reverting a state change is not considered a "state modification", as only changes to the +state made previously in code that did not have the ``view`` or ``pure`` restriction +are reverted and that code has the option to catch the ``revert`` and not pass it on. + +This behaviour is also in line with the ``STATICCALL`` opcode. + +.. warning:: + It is not possible to prevent functions from reading the state at the level + of the EVM, it is only possible to prevent them from writing to the state + (i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not). + +.. note:: + Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode + for ``pure`` functions. + This enabled state modifications in ``pure`` functions through the use of + invalid explicit type conversions. + By using ``STATICCALL`` for ``pure`` functions, modifications to the + state are prevented on the level of the EVM. + +.. note:: + Prior to version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state. + It is a compile-time type check, which can be circumvented doing invalid explicit conversions + between contract types, because the compiler can verify that the type of the contract does + not do state-changing operations, but it cannot check that the contract that will be called + at runtime is actually of that type. + +.. index:: ! fallback function, function;fallback + +.. _fallback-function: + +Fallback Function +================= + +A contract can have exactly one unnamed function. This function cannot have +arguments, cannot return anything and has to have ``external`` visibility. +It is executed on a call to the contract if none of the other +functions match the given function identifier (or if no data was supplied at +all). + +Furthermore, this function is executed whenever the contract receives plain +Ether (without data). Additionally, in order to receive Ether, the fallback function +must be marked ``payable``. If no such function exists, the contract cannot receive +Ether through regular transactions. + +In the worst case, the fallback function can only rely on 2300 gas being +available (for example when `send` or `transfer` is used), leaving little +room to perform other operations except basic logging. The following operations +will consume more gas than the 2300 gas stipend: + +- Writing to storage +- Creating a contract +- Calling an external function which consumes a large amount of gas +- Sending Ether + +Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it. + +.. note:: + Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve + any payload supplied with the call. + +.. warning:: + The fallback function is also executed if the caller meant to call + a function that is not available. If you want to implement the fallback + function only to receive ether, you should add a check + like ``require(msg.data.length == 0)`` to prevent invalid calls. + +.. warning:: + Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``) + but do not define a fallback function + throw an exception, sending back the Ether (this was different + before Solidity v0.4.0). So if you want your contract to receive Ether, + you have to implement a payable fallback function. + +.. warning:: + A contract without a payable fallback function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`) + or as a destination of a ``selfdestruct``. + + A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it. + + It also means that ``address(this).balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the fallback function). + +:: + + pragma solidity ^0.5.0; + + contract Test { + // This function is called for all messages sent to + // this contract (there is no other function). + // Sending Ether to this contract will cause an exception, + // because the fallback function does not have the `payable` + // modifier. + function() external { x = 1; } + uint x; + } + + + // This contract keeps all Ether sent to it with no way + // to get it back. + contract Sink { + function() external payable { } + } + + contract Caller { + function callTest(Test test) public returns (bool) { + (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()")); + require(success); + // results in test.x becoming == 1. + + // address(test) will not allow to call ``send`` directly, since ``test`` has no payable + // fallback function. It has to be converted to the ``address payable`` type via an + // intermediate conversion to ``uint160`` to even allow calling ``send`` on it. + address payable testPayable = address(uint160(address(test))); + + // If someone sends ether to that contract, + // the transfer will fail, i.e. this returns false here. + return testPayable.send(2 ether); + } + } + +.. index:: ! overload + +.. _overload-function: + +Function Overloading +==================== + +A contract can have multiple functions of the same name but with different parameter +types. +This process is called "overloading" and also applies to inherited functions. +The following example shows overloading of the function +``f`` in the scope of contract ``A``. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract A { + function f(uint _in) public pure returns (uint out) { + out = _in; + } + + function f(uint _in, bool _really) public pure returns (uint out) { + if (_really) + out = _in; + } + } + +Overloaded functions are also present in the external interface. It is an error if two +externally visible functions differ by their Solidity types but not by their external types. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + // This will not compile + contract A { + function f(B _in) public pure returns (B out) { + out = _in; + } + + function f(address _in) public pure returns (address out) { + out = _in; + } + } + + contract B { + } + + +Both ``f`` function overloads above end up accepting the address type for the ABI although +they are considered different inside Solidity. + +Overload resolution and Argument matching +----------------------------------------- + +Overloaded functions are selected by matching the function declarations in the current scope +to the arguments supplied in the function call. Functions are selected as overload candidates +if all arguments can be implicitly converted to the expected types. If there is not exactly one +candidate, resolution fails. + +.. note:: + Return parameters are not taken into account for overload resolution. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract A { + function f(uint8 _in) public pure returns (uint8 out) { + out = _in; + } + + function f(uint256 _in) public pure returns (uint256 out) { + out = _in; + } + } + +Calling ``f(50)`` would create a type error since ``50`` can be implicitly converted both to ``uint8`` +and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly +converted to ``uint8``. -- cgit From 0010371a932a5c9afdc40282d2e10ae2e003accf Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 14 Jan 2019 10:54:07 +0200 Subject: Split micropayments example into seperate file --- docs/examples/micropayment.rst | 429 ++++++++++++++++++++++++++++++++++++++++ docs/solidity-by-example.rst | 430 +---------------------------------------- 2 files changed, 430 insertions(+), 429 deletions(-) create mode 100644 docs/examples/micropayment.rst diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst new file mode 100644 index 00000000..e06ea4a4 --- /dev/null +++ b/docs/examples/micropayment.rst @@ -0,0 +1,429 @@ +******************** +Micropayment Channel +******************** + +In this section we will learn how to build an example implementation +of a payment channel. It uses cryptographic signatures to make +repeated transfers of Ether between the same parties secure, instantaneous, and +without transaction fees. For the example, we need to understand how to +sign and verify signatures, and setup the payment channel. + +Creating and verifying signatures +================================= + +Imagine Alice wants to send a quantity of Ether to Bob, i.e. +Alice is the sender and the Bob is the recipient. + +Alice only needs to send cryptographically signed messages off-chain +(e.g. via email) to Bob and it is similar to writing checks. + +Alice and Bob use signatures to authorise transactions, which is possible with smart contracts on Ethereum. +Alice will build a simple smart contract that lets her transmit Ether, but instead of calling a function herself +to initiate a payment, she will let Bob do that, and therefore pay the transaction fee. + +The contract will work as follows: + + 1. Alice deploys the ``ReceiverPays`` contract, attaching enough Ether to cover the payments that will be made. + 2. Alice authorises a payment by signing a message with their private key. + 3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret + (explained later), and the mechanism for sending it does not matter. + 4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the + authenticity of the message and then releases the funds. + +Creating the signature +---------------------- + +Alice does not need to interact with the Ethereum network to sign the transaction, the process is completely offline. +In this tutorial, we will sign messages in the browser using `web3.js `_ and `MetaMask `_, using the method described in `EIP-762 `_, +as it provides a number of other security benefits. + +:: + /// Hashing first makes things easier + var hash = web3.utils.sha3("message to sign"); + web3.eth.personal.sign(hash, web3.eth.defaultAccount, function () { console.log("Signed"); }); + +.. note:: + The ``web3.eth.personal.sign`` prepends the length of the message to the signed data. Since we hash first, the message will always be exactly 32 bytes long, and thus this length prefix is always the same. + +What to Sign +------------ + +For a contract that fulfils payments, the signed message must include: + + 1. The recipient's address. + 2. The amount to be transferred. + 3. Protection against replay attacks. + +A replay attack is when a signed message is reused to claim authorization for +a second action. +To avoid replay attacks we use the same as in Ethereum transactions +themselves, a so-called nonce, which is the number of transactions sent by an +account. +The smart contract checks if a nonce is used multiple times. + +Another type of replay attack can occur when the owner deploys a ``ReceiverPays`` smart contract, makes some payments, and then destroys the contract. Later, they decide to deploy the ``RecipientPays`` smart contract again, but the new contract does not know the nonces used in the previous deployment, so the attacker can use the old messages again. + +Alice can protect against this attack by including the contract's address in the message, and only messages containing the contract's address itself will be accepted. You can find an example of this in the first two lines of the ``claimPayment()`` function of the full contract at the end of this section. + +Packing arguments +----------------- + +Now that we have identified what information to include in the signed message, +we are ready to put the message together, hash it, and sign it. For simplicity, +we concatenate the data. The `ethereumjs-abi `_ +library provides a function called ``soliditySHA3`` that mimics the behaviour of +Solidity's ``keccak256`` function applied to arguments encoded using ``abi.encodePacked``. +Here is a JavaScript function that creates the proper signature for the ``ReceiverPays`` example: + +:: + + // recipient is the address that should be paid. + // amount, in wei, specifies how much ether should be sent. + // nonce can be any unique number to prevent replay attacks + // contractAddress is used to prevent cross-contract replay attacks + function signPayment(recipient, amount, nonce, contractAddress, callback) { + var hash = "0x" + abi.soliditySHA3( + ["address", "uint256", "uint256", "address"], + [recipient, amount, nonce, contractAddress] + ).toString("hex"); + + web3.eth.personal.sign(hash, web3.eth.defaultAccount, callback); + } + +Recovering the Message Signer in Solidity +----------------------------------------- + +In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. Signatures in Ethereum include a third parameter called ``v``, that you can use to verify which account's private key was used to sign the message, and the transaction's sender. Solidity provides a built-in function `ecrecover `_ that accepts a message along with the ``r``, ``s`` and ``v`` parameters and returns the address that was used to sign the message. + +Extracting the Signature Parameters +----------------------------------- + +Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a mess, so we use `inline assembly `_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section). + +Computing the Message Hash +-------------------------- + +The smart contract needs to know exactly what parameters were signed, and so it +must recreate the message from the parameters and use that for signature verification. +The functions ``prefixed`` and ``recoverSigner`` do this in the ``claimPayment`` function. + +The full contract +----------------- + +:: + + pragma solidity >=0.4.24 <0.6.0; + + contract ReceiverPays { + address owner = msg.sender; + + mapping(uint256 => bool) usedNonces; + + constructor() public payable {} + + function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public { + require(!usedNonces[nonce]); + usedNonces[nonce] = true; + + // this recreates the message that was signed on the client + bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this))); + + require(recoverSigner(message, signature) == owner); + + msg.sender.transfer(amount); + } + + /// destroy the contract and reclaim the leftover funds. + function kill() public { + require(msg.sender == owner); + selfdestruct(msg.sender); + } + + /// signature methods. + function splitSignature(bytes memory sig) + internal + pure + returns (uint8 v, bytes32 r, bytes32 s) + { + require(sig.length == 65); + + assembly { + // first 32 bytes, after the length prefix. + r := mload(add(sig, 32)) + // second 32 bytes. + s := mload(add(sig, 64)) + // final byte (first byte of the next 32 bytes). + v := byte(0, mload(add(sig, 96))) + } + + return (v, r, s); + } + + function recoverSigner(bytes32 message, bytes memory sig) + internal + pure + returns (address) + { + (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); + + return ecrecover(message, v, r, s); + } + + /// builds a prefixed hash to mimic the behavior of eth_sign. + function prefixed(bytes32 hash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } + } + + +Writing a Simple Payment Channel +================================ + +Alice now builds a simple but complete implementation of a payment channel. Payment channels use cryptographic signatures to make repeated transfers of Ether securely, instantaneously, and without transaction fees. + +What is a Payment Channel? +-------------------------- + +Payment channels allow participants to make repeated transfers of Ether without using transactions. This means that you can avoid the delays and fees associated with transactions. We are going to explore a simple unidirectional payment channel between two parties (Alice and Bob). It involves three steps: + + 1. Alice funds a smart contract with Ether. This "opens" the payment channel. + 2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment. + 3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender. + +.. note:: + Only steps 1 and 3 require Ethereum transactions, step 2 means that the sender transmits a cryptographically signed message to the recipient via off chain methods (e.g. email). This means only two transactions are required to support any number of transfers. + +Bob is guaranteed to receive their funds because the smart contract escrows the Ether and honours a valid signed message. The smart contract also enforces a timeout, so Alice is guaranteed to eventually recover their funds even if the recipient refuses to close the channel. It is up to the participants in a payment channel to decide how long to keep it open. For a short-lived transaction, such as paying an internet café for each minute of network access, or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years. + +Opening the Payment Channel +--------------------------- + +To open the payment channel, Alice deploys the smart contract, attaching the Ether to be escrowed and specifying the intended recipient and a maximum duration for the channel to exist. This is the function ``SimplePaymentChannel`` in the contract, at the end of this section. + +Making Payments +--------------- + +Alice makes payments by sending signed messages to Bob. +This step is performed entirely outside of the Ethereum network. +Messages are cryptographically signed by the sender and then transmitted directly to the recipient. + +Each message includes the following information: + + * The smart contract's address, used to prevent cross-contract replay attacks. + * The total amount of Ether that is owed the recipient so far. + +A payment channel is closed just once, at the end of a series of transfers. +Because of this, only one of the messages sent is redeemed. This is why +each message specifies a cumulative total amount of Ether owed, rather than the +amount of the individual micropayment. The recipient will naturally choose to +redeem the most recent message because that is the one with the highest total. +The nonce per-message is not needed anymore, because the smart contract only honors a single message. The address of the smart contract is still used +to prevent a message intended for one payment channel from being used for a different channel. + +Here is the modified JavaScript code to cryptographically sign a message from the previous section: + +:: + + function constructPaymentMessage(contractAddress, amount) { + return abi.soliditySHA3( + ["address", "uint256"], + [contractAddress, amount] + ); + } + + function signMessage(message, callback) { + web3.eth.personal.sign( + "0x" + message.toString("hex"), + web3.eth.defaultAccount, + callback + ); + } + + // contractAddress is used to prevent cross-contract replay attacks. + // amount, in wei, specifies how much Ether should be sent. + + function signPayment(contractAddress, amount, callback) { + var message = constructPaymentMessage(contractAddress, amount); + signMessage(message, callback); + } + + +Closing the Payment Channel +--------------------------- + +When Bob is ready to receive their funds, it is time to +close the payment channel by calling a ``close`` function on the smart contract. +Closing the channel pays the recipient the Ether they are owed and destroys the contract, sending any remaining Ether back to Alice. To close the channel, Bob needs to provide a message signed by Alice. + +The smart contract must verify that the message contains a valid signature from the sender. +The process for doing this verification is the same as the process the recipient uses. +The Solidity functions ``isValidSignature`` and ``recoverSigner`` work just like their +JavaScript counterparts in the previous section, with the latter function borrowed from the ``ReceiverPays`` contract. + +Only the payment channel recipient can call the ``close`` function, +who naturally passes the most recent payment message because that message +carries the highest total owed. If the sender were allowed to call this function, +they could provide a message with a lower amount and cheat the recipient out of what they are owed. + +The function verifies the signed message matches the given parameters. +If everything checks out, the recipient is sent their portion of the Ether, +and the sender is sent the rest via a ``selfdestruct``. +You can see the ``close`` function in the full contract. + +Channel Expiration +------------------- + +Bob can close the payment channel at any time, but if they fail to do so, +Alice needs a way to recover their escrowed funds. An *expiration* time was set +at the time of contract deployment. Once that time is reached, Alice can call +``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the full contract. + +After this function is called, Bob can no longer receive any Ether, +so it is important that Bob closes the channel before the expiration is reached. + +The full contract +----------------- + +:: + + pragma solidity >=0.4.24 <0.6.0; + + contract SimplePaymentChannel { + address payable public sender; // The account sending payments. + address payable public recipient; // The account receiving the payments. + uint256 public expiration; // Timeout in case the recipient never closes. + + constructor (address payable _recipient, uint256 duration) + public + payable + { + sender = msg.sender; + recipient = _recipient; + expiration = now + duration; + } + + function isValidSignature(uint256 amount, bytes memory signature) + internal + view + returns (bool) + { + bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount))); + + // check that the signature is from the payment sender + return recoverSigner(message, signature) == sender; + } + + /// the recipient can close the channel at any time by presenting a + /// signed amount from the sender. the recipient will be sent that amount, + /// and the remainder will go back to the sender + function close(uint256 amount, bytes memory signature) public { + require(msg.sender == recipient); + require(isValidSignature(amount, signature)); + + recipient.transfer(amount); + selfdestruct(sender); + } + + /// the sender can extend the expiration at any time + function extend(uint256 newExpiration) public { + require(msg.sender == sender); + require(newExpiration > expiration); + + expiration = newExpiration; + } + + /// if the timeout is reached without the recipient closing the channel, + /// then the Ether is released back to the sender. + function claimTimeout() public { + require(now >= expiration); + selfdestruct(sender); + } + + /// All functions below this are just taken from the chapter + /// 'creating and verifying signatures' chapter. + + function splitSignature(bytes memory sig) + internal + pure + returns (uint8 v, bytes32 r, bytes32 s) + { + require(sig.length == 65); + + assembly { + // first 32 bytes, after the length prefix + r := mload(add(sig, 32)) + // second 32 bytes + s := mload(add(sig, 64)) + // final byte (first byte of the next 32 bytes) + v := byte(0, mload(add(sig, 96))) + } + + return (v, r, s); + } + + function recoverSigner(bytes32 message, bytes memory sig) + internal + pure + returns (address) + { + (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); + + return ecrecover(message, v, r, s); + } + + /// builds a prefixed hash to mimic the behavior of eth_sign. + function prefixed(bytes32 hash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } + } + + +.. note:: + The function ``splitSignature`` does not use all security + checks. A real implementation should use a more rigorously tested library, + such as openzepplin's `version `_ of this code. + +Verifying Payments +------------------ + +Unlike in the previous section, messages in a payment channel aren't +redeemed right away. The recipient keeps track of the latest message and +redeems it when it's time to close the payment channel. This means it's +critical that the recipient perform their own verification of each message. +Otherwise there is no guarantee that the recipient will be able to get paid +in the end. + +The recipient should verify each message using the following process: + + 1. Verify that the contact address in the message matches the payment channel. + 2. Verify that the new total is the expected amount. + 3. Verify that the new total does not exceed the amount of Ether escrowed. + 4. Verify that the signature is valid and comes from the payment channel sender. + +We'll use the `ethereumjs-util `_ +library to write this verification. The final step can be done a number of ways, +and we use JavaScript. The following code borrows the `constructMessage` function from the signing **JavaScript code** above: + +:: + + // this mimics the prefixing behavior of the eth_sign JSON-RPC method. + function prefixed(hash) { + return ethereumjs.ABI.soliditySHA3( + ["string", "bytes32"], + ["\x19Ethereum Signed Message:\n32", hash] + ); + } + + function recoverSigner(message, signature) { + var split = ethereumjs.Util.fromRpcSig(signature); + var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s); + var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex"); + return signer; + } + + function isValidSignature(contractAddress, amount, signature, expectedSigner) { + var message = prefixed(constructPaymentMessage(contractAddress, amount)); + var signer = recoverSigner(message, signature); + return signer.toLowerCase() == + ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase(); + } diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 933b0765..3e54ec28 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -7,432 +7,4 @@ Solidity by Example .. include:: examples/safe-remote.rst -******************** -Micropayment Channel -******************** - -In this section we will learn how to build an example implementation -of a payment channel. It uses cryptographic signatures to make -repeated transfers of Ether between the same parties secure, instantaneous, and -without transaction fees. For the example, we need to understand how to -sign and verify signatures, and setup the payment channel. - -Creating and verifying signatures -================================= - -Imagine Alice wants to send a quantity of Ether to Bob, i.e. -Alice is the sender and the Bob is the recipient. - -Alice only needs to send cryptographically signed messages off-chain -(e.g. via email) to Bob and it is similar to writing checks. - -Alice and Bob use signatures to authorise transactions, which is possible with smart contracts on Ethereum. -Alice will build a simple smart contract that lets her transmit Ether, but instead of calling a function herself -to initiate a payment, she will let Bob do that, and therefore pay the transaction fee. - -The contract will work as follows: - - 1. Alice deploys the ``ReceiverPays`` contract, attaching enough Ether to cover the payments that will be made. - 2. Alice authorises a payment by signing a message with their private key. - 3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret - (explained later), and the mechanism for sending it does not matter. - 4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the - authenticity of the message and then releases the funds. - -Creating the signature ----------------------- - -Alice does not need to interact with the Ethereum network to sign the transaction, the process is completely offline. -In this tutorial, we will sign messages in the browser using `web3.js `_ and `MetaMask `_, using the method described in `EIP-762 `_, -as it provides a number of other security benefits. - -:: - /// Hashing first makes things easier - var hash = web3.utils.sha3("message to sign"); - web3.eth.personal.sign(hash, web3.eth.defaultAccount, function () { console.log("Signed"); }); - -.. note:: - The ``web3.eth.personal.sign`` prepends the length of the message to the signed data. Since we hash first, the message will always be exactly 32 bytes long, and thus this length prefix is always the same. - -What to Sign ------------- - -For a contract that fulfils payments, the signed message must include: - - 1. The recipient's address. - 2. The amount to be transferred. - 3. Protection against replay attacks. - -A replay attack is when a signed message is reused to claim authorization for -a second action. -To avoid replay attacks we use the same as in Ethereum transactions -themselves, a so-called nonce, which is the number of transactions sent by an -account. -The smart contract checks if a nonce is used multiple times. - -Another type of replay attack can occur when the owner deploys a ``ReceiverPays`` smart contract, makes some payments, and then destroys the contract. Later, they decide to deploy the ``RecipientPays`` smart contract again, but the new contract does not know the nonces used in the previous deployment, so the attacker can use the old messages again. - -Alice can protect against this attack by including the contract's address in the message, and only messages containing the contract's address itself will be accepted. You can find an example of this in the first two lines of the ``claimPayment()`` function of the full contract at the end of this section. - -Packing arguments ------------------ - -Now that we have identified what information to include in the signed message, -we are ready to put the message together, hash it, and sign it. For simplicity, -we concatenate the data. The `ethereumjs-abi `_ -library provides a function called ``soliditySHA3`` that mimics the behaviour of -Solidity's ``keccak256`` function applied to arguments encoded using ``abi.encodePacked``. -Here is a JavaScript function that creates the proper signature for the ``ReceiverPays`` example: - -:: - - // recipient is the address that should be paid. - // amount, in wei, specifies how much ether should be sent. - // nonce can be any unique number to prevent replay attacks - // contractAddress is used to prevent cross-contract replay attacks - function signPayment(recipient, amount, nonce, contractAddress, callback) { - var hash = "0x" + abi.soliditySHA3( - ["address", "uint256", "uint256", "address"], - [recipient, amount, nonce, contractAddress] - ).toString("hex"); - - web3.eth.personal.sign(hash, web3.eth.defaultAccount, callback); - } - -Recovering the Message Signer in Solidity ------------------------------------------ - -In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. Signatures in Ethereum include a third parameter called ``v``, that you can use to verify which account's private key was used to sign the message, and the transaction's sender. Solidity provides a built-in function `ecrecover `_ that accepts a message along with the ``r``, ``s`` and ``v`` parameters and returns the address that was used to sign the message. - -Extracting the Signature Parameters ------------------------------------ - -Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a mess, so we use `inline assembly `_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section). - -Computing the Message Hash --------------------------- - -The smart contract needs to know exactly what parameters were signed, and so it -must recreate the message from the parameters and use that for signature verification. -The functions ``prefixed`` and ``recoverSigner`` do this in the ``claimPayment`` function. - -The full contract ------------------ - -:: - - pragma solidity >=0.4.24 <0.6.0; - - contract ReceiverPays { - address owner = msg.sender; - - mapping(uint256 => bool) usedNonces; - - constructor() public payable {} - - function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public { - require(!usedNonces[nonce]); - usedNonces[nonce] = true; - - // this recreates the message that was signed on the client - bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this))); - - require(recoverSigner(message, signature) == owner); - - msg.sender.transfer(amount); - } - - /// destroy the contract and reclaim the leftover funds. - function kill() public { - require(msg.sender == owner); - selfdestruct(msg.sender); - } - - /// signature methods. - function splitSignature(bytes memory sig) - internal - pure - returns (uint8 v, bytes32 r, bytes32 s) - { - require(sig.length == 65); - - assembly { - // first 32 bytes, after the length prefix. - r := mload(add(sig, 32)) - // second 32 bytes. - s := mload(add(sig, 64)) - // final byte (first byte of the next 32 bytes). - v := byte(0, mload(add(sig, 96))) - } - - return (v, r, s); - } - - function recoverSigner(bytes32 message, bytes memory sig) - internal - pure - returns (address) - { - (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); - - return ecrecover(message, v, r, s); - } - - /// builds a prefixed hash to mimic the behavior of eth_sign. - function prefixed(bytes32 hash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } - } - - -Writing a Simple Payment Channel -================================ - -Alice now builds a simple but complete implementation of a payment channel. Payment channels use cryptographic signatures to make repeated transfers of Ether securely, instantaneously, and without transaction fees. - -What is a Payment Channel? --------------------------- - -Payment channels allow participants to make repeated transfers of Ether without using transactions. This means that you can avoid the delays and fees associated with transactions. We are going to explore a simple unidirectional payment channel between two parties (Alice and Bob). It involves three steps: - - 1. Alice funds a smart contract with Ether. This "opens" the payment channel. - 2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment. - 3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender. - -.. note:: - Only steps 1 and 3 require Ethereum transactions, step 2 means that the sender transmits a cryptographically signed message to the recipient via off chain methods (e.g. email). This means only two transactions are required to support any number of transfers. - -Bob is guaranteed to receive their funds because the smart contract escrows the Ether and honours a valid signed message. The smart contract also enforces a timeout, so Alice is guaranteed to eventually recover their funds even if the recipient refuses to close the channel. It is up to the participants in a payment channel to decide how long to keep it open. For a short-lived transaction, such as paying an internet café for each minute of network access, or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years. - -Opening the Payment Channel ---------------------------- - -To open the payment channel, Alice deploys the smart contract, attaching the Ether to be escrowed and specifying the intended recipient and a maximum duration for the channel to exist. This is the function ``SimplePaymentChannel`` in the contract, at the end of this section. - -Making Payments ---------------- - -Alice makes payments by sending signed messages to Bob. -This step is performed entirely outside of the Ethereum network. -Messages are cryptographically signed by the sender and then transmitted directly to the recipient. - -Each message includes the following information: - - * The smart contract's address, used to prevent cross-contract replay attacks. - * The total amount of Ether that is owed the recipient so far. - -A payment channel is closed just once, at the end of a series of transfers. -Because of this, only one of the messages sent is redeemed. This is why -each message specifies a cumulative total amount of Ether owed, rather than the -amount of the individual micropayment. The recipient will naturally choose to -redeem the most recent message because that is the one with the highest total. -The nonce per-message is not needed anymore, because the smart contract only honors a single message. The address of the smart contract is still used -to prevent a message intended for one payment channel from being used for a different channel. - -Here is the modified JavaScript code to cryptographically sign a message from the previous section: - -:: - - function constructPaymentMessage(contractAddress, amount) { - return abi.soliditySHA3( - ["address", "uint256"], - [contractAddress, amount] - ); - } - - function signMessage(message, callback) { - web3.eth.personal.sign( - "0x" + message.toString("hex"), - web3.eth.defaultAccount, - callback - ); - } - - // contractAddress is used to prevent cross-contract replay attacks. - // amount, in wei, specifies how much Ether should be sent. - - function signPayment(contractAddress, amount, callback) { - var message = constructPaymentMessage(contractAddress, amount); - signMessage(message, callback); - } - - -Closing the Payment Channel ---------------------------- - -When Bob is ready to receive their funds, it is time to -close the payment channel by calling a ``close`` function on the smart contract. -Closing the channel pays the recipient the Ether they are owed and destroys the contract, sending any remaining Ether back to Alice. To close the channel, Bob needs to provide a message signed by Alice. - -The smart contract must verify that the message contains a valid signature from the sender. -The process for doing this verification is the same as the process the recipient uses. -The Solidity functions ``isValidSignature`` and ``recoverSigner`` work just like their -JavaScript counterparts in the previous section, with the latter function borrowed from the ``ReceiverPays`` contract. - -Only the payment channel recipient can call the ``close`` function, -who naturally passes the most recent payment message because that message -carries the highest total owed. If the sender were allowed to call this function, -they could provide a message with a lower amount and cheat the recipient out of what they are owed. - -The function verifies the signed message matches the given parameters. -If everything checks out, the recipient is sent their portion of the Ether, -and the sender is sent the rest via a ``selfdestruct``. -You can see the ``close`` function in the full contract. - -Channel Expiration -------------------- - -Bob can close the payment channel at any time, but if they fail to do so, -Alice needs a way to recover their escrowed funds. An *expiration* time was set -at the time of contract deployment. Once that time is reached, Alice can call -``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the full contract. - -After this function is called, Bob can no longer receive any Ether, -so it is important that Bob closes the channel before the expiration is reached. - -The full contract ------------------ - -:: - - pragma solidity >=0.4.24 <0.6.0; - - contract SimplePaymentChannel { - address payable public sender; // The account sending payments. - address payable public recipient; // The account receiving the payments. - uint256 public expiration; // Timeout in case the recipient never closes. - - constructor (address payable _recipient, uint256 duration) - public - payable - { - sender = msg.sender; - recipient = _recipient; - expiration = now + duration; - } - - function isValidSignature(uint256 amount, bytes memory signature) - internal - view - returns (bool) - { - bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount))); - - // check that the signature is from the payment sender - return recoverSigner(message, signature) == sender; - } - - /// the recipient can close the channel at any time by presenting a - /// signed amount from the sender. the recipient will be sent that amount, - /// and the remainder will go back to the sender - function close(uint256 amount, bytes memory signature) public { - require(msg.sender == recipient); - require(isValidSignature(amount, signature)); - - recipient.transfer(amount); - selfdestruct(sender); - } - - /// the sender can extend the expiration at any time - function extend(uint256 newExpiration) public { - require(msg.sender == sender); - require(newExpiration > expiration); - - expiration = newExpiration; - } - - /// if the timeout is reached without the recipient closing the channel, - /// then the Ether is released back to the sender. - function claimTimeout() public { - require(now >= expiration); - selfdestruct(sender); - } - - /// All functions below this are just taken from the chapter - /// 'creating and verifying signatures' chapter. - - function splitSignature(bytes memory sig) - internal - pure - returns (uint8 v, bytes32 r, bytes32 s) - { - require(sig.length == 65); - - assembly { - // first 32 bytes, after the length prefix - r := mload(add(sig, 32)) - // second 32 bytes - s := mload(add(sig, 64)) - // final byte (first byte of the next 32 bytes) - v := byte(0, mload(add(sig, 96))) - } - - return (v, r, s); - } - - function recoverSigner(bytes32 message, bytes memory sig) - internal - pure - returns (address) - { - (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); - - return ecrecover(message, v, r, s); - } - - /// builds a prefixed hash to mimic the behavior of eth_sign. - function prefixed(bytes32 hash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } - } - - -.. note:: - The function ``splitSignature`` does not use all security - checks. A real implementation should use a more rigorously tested library, - such as openzepplin's `version `_ of this code. - -Verifying Payments ------------------- - -Unlike in the previous section, messages in a payment channel aren't -redeemed right away. The recipient keeps track of the latest message and -redeems it when it's time to close the payment channel. This means it's -critical that the recipient perform their own verification of each message. -Otherwise there is no guarantee that the recipient will be able to get paid -in the end. - -The recipient should verify each message using the following process: - - 1. Verify that the contact address in the message matches the payment channel. - 2. Verify that the new total is the expected amount. - 3. Verify that the new total does not exceed the amount of Ether escrowed. - 4. Verify that the signature is valid and comes from the payment channel sender. - -We'll use the `ethereumjs-util `_ -library to write this verification. The final step can be done a number of ways, -and we use JavaScript. The following code borrows the `constructMessage` function from the signing **JavaScript code** above: - -:: - - // this mimics the prefixing behavior of the eth_sign JSON-RPC method. - function prefixed(hash) { - return ethereumjs.ABI.soliditySHA3( - ["string", "bytes32"], - ["\x19Ethereum Signed Message:\n32", hash] - ); - } - - function recoverSigner(message, signature) { - var split = ethereumjs.Util.fromRpcSig(signature); - var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s); - var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex"); - return signer; - } - - function isValidSignature(contractAddress, amount, signature, expectedSigner) { - var message = prefixed(constructPaymentMessage(contractAddress, amount)); - var signer = recoverSigner(message, signature); - return signer.toLowerCase() == - ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase(); - } +.. include:: examples/micropayment.rst \ No newline at end of file -- cgit From 4c8f8e949143d0c680a8257adbcc768d908fae9a Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 15 Jan 2019 13:40:10 +0100 Subject: Disallow mismatching types in switch cases and detect duplicates by value for number literals. --- docs/yul.rst | 7 +-- libyul/AsmAnalysis.cpp | 30 +++++++++-- libyul/CMakeLists.txt | 6 ++- libyul/Utilities.cpp | 50 ++++++++++++++++++ libyul/Utilities.h | 59 ++++++++++++++++++++++ libyul/optimiser/ExpressionJoiner.cpp | 2 +- libyul/optimiser/FullInliner.cpp | 2 +- libyul/optimiser/FunctionHoister.cpp | 2 +- .../InlinableExpressionFunctionFinder.cpp | 2 +- libyul/optimiser/OptimizerUtilities.cpp | 39 ++++++++++++++ libyul/optimiser/OptimizerUtilities.h | 32 ++++++++++++ libyul/optimiser/SimplificationRules.cpp | 2 +- libyul/optimiser/StructuralSimplifier.cpp | 2 +- libyul/optimiser/UnusedPruner.cpp | 2 +- libyul/optimiser/Utilities.cpp | 48 ------------------ libyul/optimiser/Utilities.h | 34 ------------- test/libsolidity/InlineAssembly.cpp | 2 +- test/libyul/Parser.cpp | 13 +++++ 18 files changed, 235 insertions(+), 99 deletions(-) create mode 100644 libyul/Utilities.cpp create mode 100644 libyul/Utilities.h create mode 100644 libyul/optimiser/OptimizerUtilities.cpp create mode 100644 libyul/optimiser/OptimizerUtilities.h delete mode 100644 libyul/optimiser/Utilities.cpp delete mode 100644 libyul/optimiser/Utilities.h diff --git a/docs/yul.rst b/docs/yul.rst index 31555742..627e6e7c 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -130,9 +130,10 @@ Restrictions on the Grammar --------------------------- Switches must have at least one case (including the default case). -If all possible values of the expression is covered, the default case should -not be allowed (i.e. a switch with a ``bool`` expression and having both a -true and false case should not allow a default case). +If all possible values of the expression are covered, a default case should +not be allowed (i.e. a switch with a ``bool`` expression that has both a +true and a false case should not allow a default case). All case values need to +have the same type. Every expression evaluates to zero or more values. Identifiers and Literals evaluate to exactly diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 6a7b2b61..a5552c51 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -390,7 +391,29 @@ bool AsmAnalyzer::operator()(Switch const& _switch) if (!expectExpression(*_switch.expression)) success = false; - set> cases; + if (m_dialect->flavour == AsmFlavour::Yul) + { + YulString caseType; + bool mismatchingTypes = false; + for (auto const& _case: _switch.cases) + if (_case.value) + { + if (caseType.empty()) + caseType = _case.value->type; + else if (caseType != _case.value->type) + { + mismatchingTypes = true; + break; + } + } + if (mismatchingTypes) + m_errorReporter.typeError( + _switch.location, + "Switch cases have non-matching types." + ); + } + + set> cases; for (auto const& _case: _switch.cases) { if (_case.value) @@ -404,12 +427,11 @@ bool AsmAnalyzer::operator()(Switch const& _switch) m_stackHeight--; /// Note: the parser ensures there is only one default case - auto val = make_tuple(_case.value->kind, _case.value->value); - if (!cases.insert(val).second) + if (!cases.insert(_case.value.get()).second) { m_errorReporter.declarationError( _case.location, - "Duplicate case defined" + "Duplicate case defined." ); success = false; } diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 52c4ac8e..ad9812bd 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -20,6 +20,8 @@ add_library(yul Object.h ObjectParser.cpp ObjectParser.h + Utilities.cpp + Utilities.h YulString.h backends/evm/AbstractAssembly.h backends/evm/EVMAssembly.cpp @@ -68,6 +70,8 @@ add_library(yul optimiser/NameCollector.h optimiser/NameDispenser.cpp optimiser/NameDispenser.h + optimiser/OptimizerUtilities.cpp + optimiser/OptimizerUtilities.h optimiser/RedundantAssignEliminator.cpp optimiser/RedundantAssignEliminator.h optimiser/Rematerialiser.cpp @@ -90,8 +94,6 @@ add_library(yul optimiser/SyntacticalEquality.h optimiser/UnusedPruner.cpp optimiser/UnusedPruner.h - optimiser/Utilities.cpp - optimiser/Utilities.h optimiser/VarDeclInitializer.cpp optimiser/VarDeclInitializer.h ) diff --git a/libyul/Utilities.cpp b/libyul/Utilities.cpp new file mode 100644 index 00000000..e5f4e517 --- /dev/null +++ b/libyul/Utilities.cpp @@ -0,0 +1,50 @@ +/* + 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 . +*/ +/** + * Some useful snippets for the optimiser. + */ + +#include + +#include +#include + +#include + +using namespace std; +using namespace dev; +using namespace yul; + +u256 yul::valueOfNumberLiteral(Literal const& _literal) +{ + assertThrow(_literal.kind == LiteralKind::Number, OptimizerException, ""); + std::string const& literalString = _literal.value.str(); + assertThrow(isValidDecimal(literalString) || isValidHex(literalString), OptimizerException, ""); + return u256(literalString); +} + +template<> +bool Less::operator()(Literal const& _lhs, Literal const& _rhs) const +{ + if (std::make_tuple(_lhs.kind, _lhs.type) != std::make_tuple(_rhs.kind, _rhs.type)) + return std::make_tuple(_lhs.kind, _lhs.type) < std::make_tuple(_rhs.kind, _rhs.type); + + if (_lhs.kind == LiteralKind::Number) + return valueOfNumberLiteral(_lhs) < valueOfNumberLiteral(_rhs); + else + return _lhs.value < _rhs.value; +} diff --git a/libyul/Utilities.h b/libyul/Utilities.h new file mode 100644 index 00000000..288bc6ee --- /dev/null +++ b/libyul/Utilities.h @@ -0,0 +1,59 @@ +/* + 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 . +*/ +/** + * Small useful snippets for the optimiser. + */ + +#pragma once + +#include +#include + +namespace yul +{ + +dev::u256 valueOfNumberLiteral(Literal const& _literal); + +/** + * Linear order on Yul AST nodes. + * + * Defines a linear order on Yul AST nodes to be used in maps and sets. + * Note: the order is total and deterministic, but independent of the semantics, e.g. + * it is not guaranteed that the false Literal is "less" than the true Literal. + */ +template +struct Less +{ + bool operator()(T const& _lhs, T const& _rhs) const; +}; + +template +struct Less +{ + bool operator()(T const* _lhs, T const* _rhs) const + { + if (_lhs && _rhs) + return Less{}(*_lhs, *_rhs); + else + return _lhs < _rhs; + } +}; + +template<> bool Less::operator()(Literal const& _lhs, Literal const& _rhs) const; +extern template struct Less; + +} diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp index de2b5d53..02ac4e45 100644 --- a/libyul/optimiser/ExpressionJoiner.cpp +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 1f267f96..dd969faf 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libyul/optimiser/FunctionHoister.cpp b/libyul/optimiser/FunctionHoister.cpp index bd1c781b..4863b94d 100644 --- a/libyul/optimiser/FunctionHoister.cpp +++ b/libyul/optimiser/FunctionHoister.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp index 662cdf25..f57faa7c 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include using namespace std; diff --git a/libyul/optimiser/OptimizerUtilities.cpp b/libyul/optimiser/OptimizerUtilities.cpp new file mode 100644 index 00000000..f9571a4c --- /dev/null +++ b/libyul/optimiser/OptimizerUtilities.cpp @@ -0,0 +1,39 @@ +/* + 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 . +*/ +/** + * Some useful snippets for the optimiser. + */ + +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace yul; + +void yul::removeEmptyBlocks(Block& _block) +{ + auto isEmptyBlock = [](Statement const& _st) -> bool { + return _st.type() == typeid(Block) && boost::get(_st).statements.empty(); + }; + boost::range::remove_erase_if(_block.statements, isEmptyBlock); +} diff --git a/libyul/optimiser/OptimizerUtilities.h b/libyul/optimiser/OptimizerUtilities.h new file mode 100644 index 00000000..449a1bc0 --- /dev/null +++ b/libyul/optimiser/OptimizerUtilities.h @@ -0,0 +1,32 @@ +/* + 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 . +*/ +/** + * Small useful snippets for the optimiser. + */ + +#pragma once + +#include +#include + +namespace yul +{ + +/// Removes statements that are just empty blocks (non-recursive). +void removeEmptyBlocks(Block& _block); + +} diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index da9c7d9d..037dad97 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -20,11 +20,11 @@ #include -#include #include #include #include #include +#include #include diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index bdf4cb2a..8d7dcd57 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -16,8 +16,8 @@ */ #include #include -#include #include +#include #include #include diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index 53c412e3..365b255c 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/libyul/optimiser/Utilities.cpp b/libyul/optimiser/Utilities.cpp deleted file mode 100644 index b3b580d5..00000000 --- a/libyul/optimiser/Utilities.cpp +++ /dev/null @@ -1,48 +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 . -*/ -/** - * Some useful snippets for the optimiser. - */ - -#include - -#include -#include - -#include - -#include - -using namespace std; -using namespace dev; -using namespace yul; - -void yul::removeEmptyBlocks(Block& _block) -{ - auto isEmptyBlock = [](Statement const& _st) -> bool { - return _st.type() == typeid(Block) && boost::get(_st).statements.empty(); - }; - boost::range::remove_erase_if(_block.statements, isEmptyBlock); -} - -u256 yul::valueOfNumberLiteral(Literal const& _literal) -{ - assertThrow(_literal.kind == LiteralKind::Number, OptimizerException, ""); - std::string const& literalString = _literal.value.str(); - assertThrow(isValidDecimal(literalString) || isValidHex(literalString), OptimizerException, ""); - return u256(literalString); -} diff --git a/libyul/optimiser/Utilities.h b/libyul/optimiser/Utilities.h deleted file mode 100644 index 1cfff62b..00000000 --- a/libyul/optimiser/Utilities.h +++ /dev/null @@ -1,34 +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 . -*/ -/** - * Small useful snippets for the optimiser. - */ - -#pragma once - -#include -#include - -namespace yul -{ - -/// Removes statements that are just empty blocks (non-recursive). -void removeEmptyBlocks(Block& _block); - -dev::u256 valueOfNumberLiteral(Literal const& _literal); - -} diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index b6986041..92e4dcf6 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(switch_no_cases) BOOST_AUTO_TEST_CASE(switch_duplicate_case) { - CHECK_PARSE_ERROR("{ switch 42 case 1 {} case 1 {} default {} }", DeclarationError, "Duplicate case defined"); + CHECK_PARSE_ERROR("{ switch 42 case 1 {} case 1 {} default {} }", DeclarationError, "Duplicate case defined."); } BOOST_AUTO_TEST_CASE(switch_invalid_expression) diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 897f18ae..84f5c14b 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -304,6 +304,19 @@ BOOST_AUTO_TEST_CASE(if_statement_invalid) BOOST_CHECK(successParse("{ if 42:u256 { } }")); } +BOOST_AUTO_TEST_CASE(switch_case_types) +{ + CHECK_ERROR("{ switch 0:u256 case 0:u256 {} case 1:u32 {} }", TypeError, "Switch cases have non-matching types."); + // The following should be an error in the future, but this is not yet detected. + BOOST_CHECK(successParse("{ switch 0:u256 case 0:u32 {} case 1:u32 {} }")); +} + +BOOST_AUTO_TEST_CASE(switch_duplicate_case) +{ + CHECK_ERROR("{ switch 0:u256 case 0:u256 {} case 0x0:u256 {} }", DeclarationError, "Duplicate case defined."); + BOOST_CHECK(successParse("{ switch 0:u256 case 42:u256 {} case 0x42:u256 {} }")); +} + BOOST_AUTO_TEST_CASE(builtins_parser) { struct SimpleDialect: public Dialect -- cgit From 81f24f24e6d827d45b1ae1b22e88388d30db3dd0 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 10 Jan 2019 20:29:30 +0100 Subject: Add equivalent function combiner as Yul optimizer step. --- libdevcore/CommonData.h | 6 + libyul/CMakeLists.txt | 4 + libyul/optimiser/CommonSubexpressionEliminator.cpp | 2 +- libyul/optimiser/EquivalentFunctionCombiner.cpp | 41 +++++ libyul/optimiser/EquivalentFunctionCombiner.h | 49 ++++++ libyul/optimiser/EquivalentFunctionDetector.cpp | 63 +++++++ libyul/optimiser/EquivalentFunctionDetector.h | 71 ++++++++ libyul/optimiser/SimplificationRules.cpp | 2 +- libyul/optimiser/Suite.cpp | 4 + libyul/optimiser/SyntacticalEquality.cpp | 193 +++++++++++++++++---- libyul/optimiser/SyntacticalEquality.h | 60 ++++++- test/libyul/YulOptimizerTest.cpp | 6 + .../multiple_complex.yul | 114 ++++++++++++ .../equivalentFunctionCombiner/simple.yul | 20 +++ .../simple_different_vars.yul | 22 +++ .../switch_case_order.yul | 32 ++++ test/libyul/yulOptimizerTests/fullSuite/abi2.yul | 38 ++-- .../yulOptimizerTests/fullSuite/abi_example1.yul | 153 ++++++++-------- test/libyul/yulOptimizerTests/fullSuite/aztec.yul | 36 ++-- test/tools/yulopti.cpp | 6 +- 20 files changed, 759 insertions(+), 163 deletions(-) create mode 100644 libyul/optimiser/EquivalentFunctionCombiner.cpp create mode 100644 libyul/optimiser/EquivalentFunctionCombiner.h create mode 100644 libyul/optimiser/EquivalentFunctionDetector.cpp create mode 100644 libyul/optimiser/EquivalentFunctionDetector.h create mode 100644 test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul create mode 100644 test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul create mode 100644 test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul create mode 100644 test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 7c59c505..1d668f26 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -275,4 +275,10 @@ std::string getChecksummedAddress(std::string const& _addr); bool isValidHex(std::string const& _string); bool isValidDecimal(std::string const& _string); +template +bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) +{ + return std::equal(std::begin(_lhs), std::end(_lhs), std::begin(_rhs), std::end(_rhs), std::forward(_compare)); +} + } diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index ad9812bd..44af2fc3 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -44,6 +44,10 @@ add_library(yul optimiser/DataFlowAnalyzer.h optimiser/Disambiguator.cpp optimiser/Disambiguator.h + optimiser/EquivalentFunctionDetector.cpp + optimiser/EquivalentFunctionDetector.h + optimiser/EquivalentFunctionCombiner.cpp + optimiser/EquivalentFunctionCombiner.h optimiser/ExpressionInliner.cpp optimiser/ExpressionInliner.h optimiser/ExpressionJoiner.cpp diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 8ce003e7..5f182eba 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -74,7 +74,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e) { assertThrow(var.second, OptimizerException, ""); assertThrow(inScope(var.first), OptimizerException, ""); - if (SyntacticalEqualityChecker::equal(_e, *var.second)) + if (SyntacticallyEqual{}(_e, *var.second)) { _e = Identifier{locationOf(_e), var.first}; break; diff --git a/libyul/optimiser/EquivalentFunctionCombiner.cpp b/libyul/optimiser/EquivalentFunctionCombiner.cpp new file mode 100644 index 00000000..939e63d2 --- /dev/null +++ b/libyul/optimiser/EquivalentFunctionCombiner.cpp @@ -0,0 +1,41 @@ +/* + 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 . +*/ +/** + * Optimiser component that combines syntactically equivalent functions. + */ + +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; +using namespace dev::solidity; + +void EquivalentFunctionCombiner::run(Block& _ast) +{ + EquivalentFunctionCombiner{EquivalentFunctionDetector::run(_ast)}(_ast); +} + +void EquivalentFunctionCombiner::operator()(FunctionCall& _funCall) +{ + auto it = m_duplicates.find(_funCall.functionName.name); + if (it != m_duplicates.end()) + _funCall.functionName.name = it->second->name; + ASTModifier::operator()(_funCall); +} diff --git a/libyul/optimiser/EquivalentFunctionCombiner.h b/libyul/optimiser/EquivalentFunctionCombiner.h new file mode 100644 index 00000000..0c766ded --- /dev/null +++ b/libyul/optimiser/EquivalentFunctionCombiner.h @@ -0,0 +1,49 @@ +/* + 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 . +*/ +/** + * Optimiser component that combines syntactically equivalent functions. + */ +#pragma once + +#include +#include +#include + +namespace yul +{ + +/** + * Optimiser component that detects syntactically equivalent functions and replaces all calls to any of them by calls + * to one particular of them. + * + * Prerequisite: Disambiguator, Function Hoister + */ +class EquivalentFunctionCombiner: public ASTModifier +{ +public: + static void run(Block& _ast); + + using ASTModifier::operator(); + void operator()(FunctionCall& _funCall) override; + +private: + EquivalentFunctionCombiner(std::map _duplicates): m_duplicates(std::move(_duplicates)) {} + std::map m_duplicates; +}; + + +} diff --git a/libyul/optimiser/EquivalentFunctionDetector.cpp b/libyul/optimiser/EquivalentFunctionDetector.cpp new file mode 100644 index 00000000..d3a697bd --- /dev/null +++ b/libyul/optimiser/EquivalentFunctionDetector.cpp @@ -0,0 +1,63 @@ +/* + 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 . +*/ +/** + * Optimiser component that combines syntactically equivalent functions. + */ + +#include +#include + +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; +using namespace solidity; + +void EquivalentFunctionDetector::operator()(FunctionDefinition const& _fun) +{ + RoughHeuristic heuristic(_fun); + auto& candidates = m_candidates[heuristic]; + for (auto const& candidate: candidates) + if (SyntacticallyEqual{}.statementEqual(_fun, *candidate)) + { + m_duplicates[_fun.name] = candidate; + return; + } + candidates.push_back(&_fun); +} + +bool EquivalentFunctionDetector::RoughHeuristic::operator<(EquivalentFunctionDetector::RoughHeuristic const& _rhs) const +{ + if ( + std::make_tuple(m_fun.parameters.size(), m_fun.returnVariables.size()) == + std::make_tuple(_rhs.m_fun.parameters.size(), _rhs.m_fun.returnVariables.size()) + ) + return codeSize() < _rhs.codeSize(); + else + return + std::make_tuple(m_fun.parameters.size(), m_fun.returnVariables.size()) < + std::make_tuple(_rhs.m_fun.parameters.size(), _rhs.m_fun.returnVariables.size()); +} + +size_t EquivalentFunctionDetector::RoughHeuristic::codeSize() const +{ + if (!m_codeSize) + m_codeSize = CodeSize::codeSize(m_fun.body); + return *m_codeSize; +} diff --git a/libyul/optimiser/EquivalentFunctionDetector.h b/libyul/optimiser/EquivalentFunctionDetector.h new file mode 100644 index 00000000..329fd385 --- /dev/null +++ b/libyul/optimiser/EquivalentFunctionDetector.h @@ -0,0 +1,71 @@ +/* + 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 . +*/ +/** + * Optimiser component that combines syntactically equivalent functions. + */ +#pragma once + +#include +#include + +namespace yul +{ + +/** + * Optimiser component that detects syntactically equivalent functions. + * + * Prerequisite: Disambiguator + */ +class EquivalentFunctionDetector: public ASTWalker +{ +public: + static std::map run(Block& _block) + { + EquivalentFunctionDetector detector{}; + detector(_block); + return std::move(detector.m_duplicates); + } + + using ASTWalker::operator(); + void operator()(FunctionDefinition const& _fun) override; + +private: + EquivalentFunctionDetector() = default; + /** + * Fast heuristic to detect distinct, resp. potentially equal functions. + * + * Defines a partial order on function definitions. If two functions + * are comparable (one is "less" than the other), they are distinct. + * If not (neither is "less" than the other), they are *potentially* equal. + */ + class RoughHeuristic + { + public: + RoughHeuristic(FunctionDefinition const& _fun): m_fun(_fun) {} + bool operator<(RoughHeuristic const& _rhs) const; + private: + std::size_t codeSize() const; + FunctionDefinition const& m_fun; + mutable boost::optional m_codeSize; + // In case the heuristic doesn't turn out to be good enough, we might want to define a hash function for code blocks. + }; + std::map> m_candidates; + std::map m_duplicates; +}; + + +} diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 037dad97..1b620b64 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -171,7 +171,7 @@ bool Pattern::matches( Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; assertThrow(firstMatch, OptimizerException, "Match set but to null."); return - SyntacticalEqualityChecker::equal(*firstMatch, _expr) && + SyntacticallyEqual{}(*firstMatch, _expr) && MovableChecker(_dialect, _expr).movable(); } else if (m_kind == PatternKind::Any) diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 48914cf8..38c0bf49 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,8 @@ void OptimiserSuite::run( (FunctionHoister{})(ast); (BlockFlattener{})(ast); (FunctionGrouper{})(ast); + EquivalentFunctionCombiner::run(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); (ForLoopInitRewriter{})(ast); (BlockFlattener{})(ast); StructuralSimplifier{_dialect}(ast); @@ -101,6 +104,7 @@ void OptimiserSuite::run( CommonSubexpressionEliminator{_dialect}(ast); (FunctionGrouper{})(ast); + EquivalentFunctionCombiner::run(ast); FullInliner{ast, dispenser}.run(); SSATransform::run(ast, dispenser); diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index 99ce06e5..ba8cc793 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -22,6 +22,7 @@ #include #include +#include #include @@ -29,48 +30,166 @@ using namespace std; using namespace dev; using namespace yul; -bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& _e2) +bool SyntacticallyEqual::operator()(Expression const& _lhs, Expression const& _rhs) { - if (_e1.type() != _e2.type()) + return boost::apply_visitor([this](auto&& _lhsExpr, auto&& _rhsExpr) -> bool { + // ``this->`` is redundant, but required to work around a bug present in gcc 6.x. + return this->expressionEqual(_lhsExpr, _rhsExpr); + }, _lhs, _rhs); +} + +bool SyntacticallyEqual::operator()(Statement const& _lhs, Statement const& _rhs) +{ + return boost::apply_visitor([this](auto&& _lhsStmt, auto&& _rhsStmt) -> bool { + // ``this->`` is redundant, but required to work around a bug present in gcc 6.x. + return this->statementEqual(_lhsStmt, _rhsStmt); + }, _lhs, _rhs); +} + +bool SyntacticallyEqual::expressionEqual(FunctionalInstruction const& _lhs, FunctionalInstruction const& _rhs) +{ + return + _lhs.instruction == _rhs.instruction && + containerEqual(_lhs.arguments, _rhs.arguments, [this](Expression const& _lhsExpr, Expression const& _rhsExpr) -> bool { + return (*this)(_lhsExpr, _rhsExpr); + }); +} + +bool SyntacticallyEqual::expressionEqual(FunctionCall const& _lhs, FunctionCall const& _rhs) +{ + return + expressionEqual(_lhs.functionName, _rhs.functionName) && + containerEqual(_lhs.arguments, _rhs.arguments, [this](Expression const& _lhsExpr, Expression const& _rhsExpr) -> bool { + return (*this)(_lhsExpr, _rhsExpr); + }); +} + +bool SyntacticallyEqual::expressionEqual(Identifier const& _lhs, Identifier const& _rhs) +{ + auto lhsIt = m_identifiersLHS.find(_lhs.name); + auto rhsIt = m_identifiersRHS.find(_rhs.name); + return + (lhsIt == m_identifiersLHS.end() && rhsIt == m_identifiersRHS.end() && _lhs.name == _rhs.name) || + (lhsIt != m_identifiersLHS.end() && rhsIt != m_identifiersRHS.end() && lhsIt->second == rhsIt->second); +} +bool SyntacticallyEqual::expressionEqual(Literal const& _lhs, Literal const& _rhs) +{ + if (_lhs.kind != _rhs.kind || _lhs.type != _rhs.type) return false; - // TODO This somehow calls strcmp - WHERE? - - // TODO This should be replaced by some kind of AST walker as soon as it gets - // more complex. - if (_e1.type() == typeid(FunctionalInstruction)) - { - auto const& e1 = boost::get(_e1); - auto const& e2 = boost::get(_e2); - return - e1.instruction == e2.instruction && - equalVector(e1.arguments, e2.arguments); - } - else if (_e1.type() == typeid(FunctionCall)) - { - auto const& e1 = boost::get(_e1); - auto const& e2 = boost::get(_e2); - return - equal(e1.functionName, e2.functionName) && - equalVector(e1.arguments, e2.arguments); - } - else if (_e1.type() == typeid(Identifier)) - return boost::get(_e1).name == boost::get(_e2).name; - else if (_e1.type() == typeid(Literal)) - { - auto const& e1 = boost::get(_e1); - auto const& e2 = boost::get(_e2); - return e1.kind == e2.kind && e1.value == e2.value && e1.type == e2.type; - } + if (_lhs.kind == LiteralKind::Number) + return valueOfNumberLiteral(_lhs) == valueOfNumberLiteral(_rhs); else - { - assertThrow(false, OptimizerException, "Invalid expression"); - } - return false; + return _lhs.value == _rhs.value; } -bool SyntacticalEqualityChecker::equalVector(vector const& _e1, vector const& _e2) +bool SyntacticallyEqual::statementEqual(ExpressionStatement const& _lhs, ExpressionStatement const& _rhs) { - return _e1.size() == _e2.size() && - std::equal(begin(_e1), end(_e1), begin(_e2), SyntacticalEqualityChecker::equal); + return (*this)(_lhs.expression, _rhs.expression); +} +bool SyntacticallyEqual::statementEqual(Assignment const& _lhs, Assignment const& _rhs) +{ + return containerEqual( + _lhs.variableNames, + _rhs.variableNames, + [this](Identifier const& _lhsVarName, Identifier const& _rhsVarName) -> bool { + return this->expressionEqual(_lhsVarName, _rhsVarName); + } + ) && (*this)(*_lhs.value, *_rhs.value); +} + +bool SyntacticallyEqual::statementEqual(VariableDeclaration const& _lhs, VariableDeclaration const& _rhs) +{ + // first visit expression, then variable declarations + if (!compareSharedPtr(_lhs.value, _rhs.value)) + return false; + return containerEqual(_lhs.variables, _rhs.variables, [this](TypedName const& _lhsVarName, TypedName const& _rhsVarName) -> bool { + return this->visitDeclaration(_lhsVarName, _rhsVarName); + }); +} +bool SyntacticallyEqual::statementEqual(FunctionDefinition const& _lhs, FunctionDefinition const& _rhs) +{ + auto compare = [this](TypedName const& _lhsVarName, TypedName const& _rhsVarName) -> bool { + return this->visitDeclaration(_lhsVarName, _rhsVarName); + }; + // first visit parameter declarations, then body + if (!containerEqual(_lhs.parameters, _rhs.parameters, compare)) + return false; + if (!containerEqual(_lhs.returnVariables, _rhs.returnVariables, compare)) + return false; + return statementEqual(_lhs.body, _rhs.body); +} + +bool SyntacticallyEqual::statementEqual(If const& _lhs, If const& _rhs) +{ + return + compareSharedPtr(_lhs.condition, _rhs.condition) && + statementEqual(_lhs.body, _rhs.body); +} + +bool SyntacticallyEqual::statementEqual(Switch const& _lhs, Switch const& _rhs) +{ + static auto const sortCasesByValue = [](Case const* _lhsCase, Case const* _rhsCase) -> bool { + return Less{}(_lhsCase->value.get(), _rhsCase->value.get()); + }; + std::set lhsCases(sortCasesByValue); + std::set rhsCases(sortCasesByValue); + for (auto const& lhsCase: _lhs.cases) + lhsCases.insert(&lhsCase); + for (auto const& rhsCase: _rhs.cases) + rhsCases.insert(&rhsCase); + return + compareSharedPtr(_lhs.expression, _rhs.expression) && + containerEqual(lhsCases, rhsCases, [this](Case const* _lhsCase, Case const* _rhsCase) -> bool { + return this->switchCaseEqual(*_lhsCase, *_rhsCase); + }); +} + + +bool SyntacticallyEqual::switchCaseEqual(Case const& _lhs, Case const& _rhs) +{ + return + compareSharedPtr(_lhs.value, _rhs.value) && + statementEqual(_lhs.body, _rhs.body); +} + +bool SyntacticallyEqual::statementEqual(ForLoop const& _lhs, ForLoop const& _rhs) +{ + return + statementEqual(_lhs.pre, _rhs.pre) && + compareSharedPtr(_lhs.condition, _rhs.condition) && + statementEqual(_lhs.body, _rhs.body) && + statementEqual(_lhs.post, _rhs.post); +} + +bool SyntacticallyEqual::statementEqual(Instruction const&, Instruction const&) +{ + assertThrow(false, OptimizerException, ""); +} + +bool SyntacticallyEqual::statementEqual(Label const&, Label const&) +{ + assertThrow(false, OptimizerException, ""); +} + +bool SyntacticallyEqual::statementEqual(StackAssignment const&, StackAssignment const&) +{ + assertThrow(false, OptimizerException, ""); +} + +bool SyntacticallyEqual::statementEqual(Block const& _lhs, Block const& _rhs) +{ + return containerEqual(_lhs.statements, _rhs.statements, [this](Statement const& _lhsStmt, Statement const& _rhsStmt) -> bool { + return (*this)(_lhsStmt, _rhsStmt); + }); +} + +bool SyntacticallyEqual::visitDeclaration(TypedName const& _lhs, TypedName const& _rhs) +{ + if (_lhs.type != _rhs.type) + return false; + std::size_t id = m_idsUsed++; + m_identifiersLHS[_lhs.name] = id; + m_identifiersRHS[_rhs.name] = id; + return true; } diff --git a/libyul/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h index 63c51b4f..d4630d95 100644 --- a/libyul/optimiser/SyntacticalEquality.h +++ b/libyul/optimiser/SyntacticalEquality.h @@ -21,27 +21,69 @@ #pragma once #include +#include -#include +#include +#include namespace yul { + /** * Component that can compare ASTs for equality on a syntactic basis. - * Ignores source locations but requires exact matches otherwise. + * Ignores source locations and allows for different variable names but requires exact matches otherwise. * - * TODO: Only implemented for Expressions for now. - * A future version might also recognize renamed variables and thus could be used to - * remove duplicate functions. + * Prerequisite: Disambiguator (unless only expressions are compared) */ -class SyntacticalEqualityChecker +class SyntacticallyEqual { public: - static bool equal(Expression const& _e1, Expression const& _e2); + bool operator()(Expression const& _lhs, Expression const& _rhs); + bool operator()(Statement const& _lhs, Statement const& _rhs); + + bool expressionEqual(FunctionalInstruction const& _lhs, FunctionalInstruction const& _rhs); + bool expressionEqual(FunctionCall const& _lhs, FunctionCall const& _rhs); + bool expressionEqual(Identifier const& _lhs, Identifier const& _rhs); + bool expressionEqual(Literal const& _lhs, Literal const& _rhs); + + bool statementEqual(ExpressionStatement const& _lhs, ExpressionStatement const& _rhs); + bool statementEqual(Assignment const& _lhs, Assignment const& _rhs); + bool statementEqual(VariableDeclaration const& _lhs, VariableDeclaration const& _rhs); + bool statementEqual(FunctionDefinition const& _lhs, FunctionDefinition const& _rhs); + bool statementEqual(If const& _lhs, If const& _rhs); + bool statementEqual(Switch const& _lhs, Switch const& _rhs); + bool switchCaseEqual(Case const& _lhs, Case const& _rhs); + bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs); + bool statementEqual(Block const& _lhs, Block const& _rhs); +private: + bool statementEqual(Instruction const& _lhs, Instruction const& _rhs); + bool statementEqual(Label const& _lhs, Label const& _rhs); + bool statementEqual(StackAssignment const& _lhs, StackAssignment const& _rhs); + + bool visitDeclaration(TypedName const& _lhs, TypedName const& _rhs); + + template + bool expressionEqual(U const&, V const&, std::enable_if_t::value>* = nullptr) + { + return false; + } + + template + bool statementEqual(U const&, V const&, std::enable_if_t::value>* = nullptr) + { + return false; + } + + template + bool compareSharedPtr(std::shared_ptr const& _lhs, std::shared_ptr const& _rhs) + { + return (_lhs == _rhs) || (_lhs && _rhs && (this->*CompareMember)(*_lhs, *_rhs)); + } -protected: - static bool equalVector(std::vector const& _e1, std::vector const& _e2); + std::size_t m_idsUsed = 0; + std::map m_identifiersLHS; + std::map m_identifiersRHS; }; } diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 0e3203cb..59cde4fe 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,11 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con disambiguate(); StructuralSimplifier{*m_dialect}(*m_ast); } + else if (m_optimizerStep == "equivalentFunctionCombiner") + { + disambiguate(); + EquivalentFunctionCombiner::run(*m_ast); + } else if (m_optimizerStep == "fullSuite") OptimiserSuite::run(*m_dialect, *m_ast, *m_analysisInfo); else diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul new file mode 100644 index 00000000..380f9f03 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul @@ -0,0 +1,114 @@ +{ + pop(f(1,2,3)) + pop(g(4,5,6)) + pop(h(7,8,9)) + function f(f1, f2, f3) -> rf + { + switch f1 + case 0 { + if f2 + { + rf := f3 + } + if not(f2) + { + rf := f1 + } + } + default { + rf := 3 + } + } + function g(g1, g2, g3) -> rg + { + switch g1 + case 0 { + if g2 + { + rg := g3 + } + if not(g2) + { + rg := g1 + } + } + default { + rg := 3 + } + } + function h(h1, h2, h3) -> rh + { + switch h1 + case 1 { + if h2 + { + rh := h3 + } + if not(h2) + { + rh := h1 + } + } + default { + rh := 3 + } + } +} +// ---- +// equivalentFunctionCombiner +// { +// pop(f(1, 2, 3)) +// pop(f(4, 5, 6)) +// pop(h(7, 8, 9)) +// function f(f1, f2, f3) -> rf +// { +// switch f1 +// case 0 { +// if f2 +// { +// rf := f3 +// } +// if not(f2) +// { +// rf := f1 +// } +// } +// default { +// rf := 3 +// } +// } +// function g(g1, g2, g3) -> rg +// { +// switch g1 +// case 0 { +// if g2 +// { +// rg := g3 +// } +// if not(g2) +// { +// rg := g1 +// } +// } +// default { +// rg := 3 +// } +// } +// function h(h1, h2, h3) -> rh +// { +// switch h1 +// case 1 { +// if h2 +// { +// rh := h3 +// } +// if not(h2) +// { +// rh := h1 +// } +// } +// default { +// rh := 3 +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul new file mode 100644 index 00000000..2d5b3ef8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul @@ -0,0 +1,20 @@ +{ + f() + g() + function f() { mstore(1, mload(0)) } + function g() { mstore(1, mload(0)) } +} +// ---- +// equivalentFunctionCombiner +// { +// f() +// f() +// function f() +// { +// mstore(1, mload(0)) +// } +// function g() +// { +// mstore(1, mload(0)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul new file mode 100644 index 00000000..d38a3d2e --- /dev/null +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul @@ -0,0 +1,22 @@ +{ + pop(f()) + pop(g()) + function f() -> b { let a := mload(0) b := a } + function g() -> a { let b := mload(0) a := b } +} +// ---- +// equivalentFunctionCombiner +// { +// pop(f()) +// pop(f()) +// function f() -> b +// { +// let a := mload(0) +// b := a +// } +// function g() -> a_1 +// { +// let b_2 := mload(0) +// a_1 := b_2 +// } +// } diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul new file mode 100644 index 00000000..4f3cad36 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul @@ -0,0 +1,32 @@ +{ + f(0) + g(1) + function f(x) { switch x case 0 { mstore(0, 42) } case 1 { mstore(1, 42) } } + function g(x) { switch x case 1 { mstore(1, 42) } case 0 { mstore(0, 42) } } +} +// ---- +// equivalentFunctionCombiner +// { +// f(0) +// f(1) +// function f(x) +// { +// switch x +// case 0 { +// mstore(0, 42) +// } +// case 1 { +// mstore(1, 42) +// } +// } +// function g(x_1) +// { +// switch x_1 +// case 1 { +// mstore(1, 42) +// } +// case 0 { +// mstore(0, 42) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index bb60b1fa..887399b6 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1073,12 +1073,12 @@ // fullSuite // { // let _2 := mload(1) -// let _1042 := mload(0) -// if slt(sub(_2, _1042), 64) +// let _172 := mload(0) +// if slt(sub(_2, _172), 64) // { // revert(0, 0) // } -// sstore(0, and(calldataload(_1042), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// sstore(0, and(calldataload(_172), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) // let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) // sstore(x1, x0) // sstore(x3, x2) @@ -1093,40 +1093,40 @@ // value0_57 := and(calldataload(add(headStart_55, value4)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) // value1_58 := calldataload(add(headStart_55, 32)) // let offset_62 := calldataload(add(headStart_55, 64)) -// let _1090 := 0xffffffffffffffff -// if gt(offset_62, _1090) +// let _220 := 0xffffffffffffffff +// if gt(offset_62, _220) // { // revert(value4, value4) // } -// let _1092 := add(headStart_55, offset_62) -// if iszero(slt(add(_1092, 0x1f), dataEnd_56)) +// let _222 := add(headStart_55, offset_62) +// if iszero(slt(add(_222, 0x1f), dataEnd_56)) // { // revert(value4, value4) // } -// let abi_decode_length_15_689 := calldataload(_1092) -// if gt(abi_decode_length_15_689, _1090) +// let abi_decode_length_15_116 := calldataload(_222) +// if gt(abi_decode_length_15_116, _220) // { // revert(value4, value4) // } -// if gt(add(add(_1092, abi_decode_length_15_689), 0x20), dataEnd_56) +// if gt(add(add(_222, abi_decode_length_15_116), 32), dataEnd_56) // { // revert(value4, value4) // } -// value2_59 := add(_1092, 0x20) -// value3 := abi_decode_length_15_689 -// let _1095 := calldataload(add(headStart_55, 96)) -// if iszero(lt(_1095, 3)) +// value2_59 := add(_222, 32) +// value3 := abi_decode_length_15_116 +// let _225 := calldataload(add(headStart_55, 96)) +// if iszero(lt(_225, 3)) // { // revert(value4, value4) // } -// value4 := _1095 +// value4 := _225 // } // function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart_252, value10_253, value9_254, value8_255, value7_256, value6_257, value5_258, value4_259, value3_260, value2_261, value1_262, value0_263) -> tail_264 // { // tail_264 := add(headStart_252, 352) // mstore(headStart_252, value0_263) -// let _1307 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// mstore(add(headStart_252, 32), and(value1_262, _1307)) +// let _439 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// mstore(add(headStart_252, 32), and(value1_262, _439)) // mstore(add(headStart_252, 64), value2_261) // mstore(add(headStart_252, 96), value3_260) // if iszero(lt(value4_259, 3)) @@ -1137,8 +1137,8 @@ // mstore(add(headStart_252, 160), value5_258) // mstore(add(headStart_252, 192), value6_257) // mstore(add(headStart_252, 224), value7_256) -// mstore(add(headStart_252, 256), and(value8_255, _1307)) -// mstore(add(headStart_252, 288), and(value9_254, _1307)) +// mstore(add(headStart_252, 256), and(value8_255, _439)) +// mstore(add(headStart_252, 288), and(value9_254, _439)) // mstore(add(headStart_252, 320), value10_253) // } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index c909d39e..a8cac6c6 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -458,13 +458,14 @@ // ---- // fullSuite // { +// let _1 := 0x20 // let _2 := 0 -// let _485 := mload(_2) -// let abi_encode_pos := 0x20 -// let abi_encode_length_68 := mload(_485) -// mstore(0x20, abi_encode_length_68) +// let _268 := mload(_2) +// let abi_encode_pos := _1 +// let abi_encode_length_68 := mload(_268) +// mstore(_1, abi_encode_length_68) // abi_encode_pos := 64 -// let abi_encode_srcPtr := add(_485, 0x20) +// let abi_encode_srcPtr := add(_268, _1) // let abi_encode_i_69 := _2 // for { // } @@ -473,31 +474,45 @@ // abi_encode_i_69 := add(abi_encode_i_69, 1) // } // { -// abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(abi_encode_srcPtr), abi_encode_pos) -// abi_encode_srcPtr := add(abi_encode_srcPtr, 0x20) +// let _668 := mload(abi_encode_srcPtr) +// let abi_encode_pos_71_760 := abi_encode_pos +// let abi_encode_srcPtr_73_762 := _668 +// let abi_encode_i_74_763 := _2 +// for { +// } +// lt(abi_encode_i_74_763, 0x3) +// { +// abi_encode_i_74_763 := add(abi_encode_i_74_763, 1) +// } +// { +// mstore(abi_encode_pos_71_760, and(mload(abi_encode_srcPtr_73_762), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// abi_encode_srcPtr_73_762 := add(abi_encode_srcPtr_73_762, _1) +// abi_encode_pos_71_760 := add(abi_encode_pos_71_760, _1) +// } +// abi_encode_srcPtr := add(abi_encode_srcPtr, _1) // abi_encode_pos := add(abi_encode_pos, 0x60) // } -// let _487 := mload(0x40) -// let _488 := mload(0x20) -// if slt(sub(_487, _488), 128) +// let _270 := mload(64) +// let _271 := mload(_1) +// if slt(sub(_270, _271), 128) // { // revert(_2, _2) // } -// let abi_decode_offset_64 := calldataload(add(_488, 64)) -// let abi_decode__165 := 0xffffffffffffffff -// if gt(abi_decode_offset_64, abi_decode__165) +// let abi_decode_offset_64 := calldataload(add(_271, 64)) +// let abi_decode__74 := 0xffffffffffffffff +// if gt(abi_decode_offset_64, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value2_587 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_488, abi_decode_offset_64), _487) -// let abi_decode_offset_65 := calldataload(add(_488, 96)) -// if gt(abi_decode_offset_65, abi_decode__165) +// let abi_decode_value2_367 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_271, abi_decode_offset_64), _270) +// let abi_decode_offset_65 := calldataload(add(_271, 96)) +// if gt(abi_decode_offset_65, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value3_588 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_488, abi_decode_offset_65), _487) -// sstore(calldataload(_488), calldataload(add(_488, 32))) -// sstore(abi_decode_value2_587, abi_decode_value3_588) +// let abi_decode_value3_368 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_271, abi_decode_offset_65), _270) +// sstore(calldataload(_271), calldataload(add(_271, _1))) +// sstore(abi_decode_value2_367, abi_decode_value3_368) // sstore(_2, abi_encode_pos) // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5 // { @@ -506,23 +521,19 @@ // revert(0, 0) // } // let length_6 := calldataload(offset_3) -// if gt(length_6, 0xffffffffffffffff) +// let array_5_115 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_6)) +// array_5 := array_5_115 +// let dst_7 := array_5_115 +// mstore(array_5_115, length_6) +// let _16 := 0x20 +// dst_7 := add(array_5_115, _16) +// let src_8 := add(offset_3, _16) +// if gt(add(add(offset_3, mul(length_6, 0x40)), _16), end_4) // { // revert(0, 0) // } -// let array_allo__217 := 0x20 -// let array_5_254 := allocateMemory(add(mul(length_6, array_allo__217), array_allo__217)) -// array_5 := array_5_254 -// let dst_7 := array_5_254 -// mstore(array_5_254, length_6) -// dst_7 := add(array_5_254, array_allo__217) -// let src_8 := add(offset_3, array_allo__217) -// if gt(add(add(offset_3, mul(length_6, 0x40)), array_allo__217), end_4) -// { -// revert(0, 0) -// } -// let i_9_566 := 0 -// let i_9 := i_9_566 +// let i_9_346 := 0 +// let i_9 := i_9_346 // for { // } // lt(i_9, length_6) @@ -532,16 +543,17 @@ // { // if iszero(slt(add(src_8, 0x1f), end_4)) // { -// revert(i_9_566, i_9_566) +// revert(i_9_346, i_9_346) // } -// let abi_decode_array_13_263 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) -// let abi_decode_dst_15 := abi_decode_array_13_263 +// let abi_decode_array_13_124 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) +// let abi_decode_dst_15 := abi_decode_array_13_124 // let abi_decode_src_16 := src_8 -// if gt(add(src_8, 64), end_4) +// let abi_decode__289 := add(src_8, 0x40) +// if gt(abi_decode__289, end_4) // { -// revert(i_9_566, i_9_566) +// revert(i_9_346, i_9_346) // } -// let abi_decode_i_17 := i_9_566 +// let abi_decode_i_17 := i_9_346 // for { // } // lt(abi_decode_i_17, 0x2) @@ -550,12 +562,12 @@ // } // { // mstore(abi_decode_dst_15, calldataload(abi_decode_src_16)) -// abi_decode_dst_15 := add(abi_decode_dst_15, array_allo__217) -// abi_decode_src_16 := add(abi_decode_src_16, array_allo__217) +// abi_decode_dst_15 := add(abi_decode_dst_15, _16) +// abi_decode_src_16 := add(abi_decode_src_16, _16) // } -// mstore(dst_7, abi_decode_array_13_263) -// dst_7 := add(dst_7, array_allo__217) -// src_8 := add(src_8, 0x40) +// mstore(dst_7, abi_decode_array_13_124) +// dst_7 := add(dst_7, _16) +// src_8 := abi_decode__289 // } // } // function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29 @@ -565,19 +577,14 @@ // revert(0, 0) // } // let length_30 := calldataload(offset_27) -// if gt(length_30, 0xffffffffffffffff) -// { -// revert(0, 0) -// } -// let array_allo__234 := 0x20 -// let array_allo__560 := mul(length_30, array_allo__234) -// let array_29_279 := allocateMemory(add(array_allo__560, array_allo__234)) -// array_29 := array_29_279 -// let dst_31 := array_29_279 -// mstore(array_29_279, length_30) -// dst_31 := add(array_29_279, array_allo__234) -// let src_32 := add(offset_27, array_allo__234) -// if gt(add(add(offset_27, array_allo__560), array_allo__234), end_28) +// let array_29_131 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_30)) +// array_29 := array_29_131 +// let dst_31 := array_29_131 +// mstore(array_29_131, length_30) +// let _52 := 0x20 +// dst_31 := add(array_29_131, _52) +// let src_32 := add(offset_27, _52) +// if gt(add(add(offset_27, mul(length_30, _52)), _52), end_28) // { // revert(0, 0) // } @@ -590,36 +597,28 @@ // } // { // mstore(dst_31, calldataload(src_32)) -// dst_31 := add(dst_31, array_allo__234) -// src_32 := add(src_32, array_allo__234) +// dst_31 := add(dst_31, _52) +// src_32 := add(src_32, _52) // } // } -// function abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(value_70, pos_71) +// function allocateMemory(size) -> memPtr // { -// let srcPtr_73 := value_70 -// let i_74 := 0 -// for { -// } -// lt(i_74, 0x3) -// { -// i_74 := add(i_74, 1) -// } +// let memPtr_157 := mload(64) +// memPtr := memPtr_157 +// let newFreePtr := add(memPtr_157, size) +// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_157)) // { -// mstore(pos_71, and(mload(srcPtr_73), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) -// srcPtr_73 := add(srcPtr_73, 0x20) -// pos_71 := add(pos_71, 0x20) +// revert(0, 0) // } +// mstore(64, newFreePtr) // } -// function allocateMemory(size) -> memPtr +// function array_allocation_size_t_array$_t_address_$dyn_memory(length_90) -> size_91 // { -// let memPtr_315 := mload(64) -// memPtr := memPtr_315 -// let newFreePtr := add(memPtr_315, size) -// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_315)) +// if gt(length_90, 0xffffffffffffffff) // { // revert(0, 0) // } -// mstore(64, newFreePtr) +// size_91 := add(mul(length_90, 0x20), 0x20) // } // function array_allocation_size_t_array$_t_uint256_$2_memory(length_94) -> size_95 // { diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 4fcdaaf1..fc09b5d9 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -264,10 +264,10 @@ // } // { // let validateJo__34 := 0x20 -// let validateJo__376 := add(validateJo__10, mul(validateJo_i, 0xc0)) -// let validateJo_noteIndex := add(validateJo__376, 36) +// let validateJo__373 := add(validateJo__10, mul(validateJo_i, 0xc0)) +// let validateJo_noteIndex := add(validateJo__373, 0x24) // let validateJo_k := validateJo_i_290 -// let validateJo_a_292 := calldataload(add(validateJo__376, 68)) +// let validateJo_a_292 := calldataload(add(validateJo__373, 0x44)) // let validateJo_a := validateJo_a_292 // let validateJo_c := validateJo_challenge // let validateJo__39 := add(validateJo_i, 0x01) @@ -286,18 +286,18 @@ // switch gt(validateJo__39, validateJo_m) // case 1 { // validateJo_kn := addmod(validateJo_kn, sub(validateJo_gen_order, validateJo_k), validateJo_gen_order) -// let validateJo_x := mod(mload(0x00), validateJo_gen_order) +// let validateJo_x := mod(mload(validateJo_i_290), validateJo_gen_order) // validateJo_k := mulmod(validateJo_k, validateJo_x, validateJo_gen_order) // validateJo_a := mulmod(validateJo_a_292, validateJo_x, validateJo_gen_order) // validateJo_c := mulmod(validateJo_challenge, validateJo_x, validateJo_gen_order) -// mstore(0x00, keccak256(0x00, validateJo__34)) +// mstore(validateJo_i_290, keccak256(validateJo_i_290, validateJo__34)) // } // case 0 { // validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) // } // let validateJo__52 := 0x40 -// calldatacopy(0xe0, add(validateJo__376, 164), validateJo__52) -// calldatacopy(validateJo__34, add(validateJo__376, 100), validateJo__52) +// calldatacopy(0xe0, add(validateJo__373, 164), validateJo__52) +// calldatacopy(validateJo__34, add(validateJo__373, 100), validateJo__52) // let validateJo__61 := 0x120 // mstore(validateJo__61, sub(validateJo_gen_order, validateJo_c)) // let validateJo__62 := 0x60 @@ -329,8 +329,8 @@ // } // if iszero(validateJo_result) // { -// mstore(0x00, 400) -// revert(0x00, validateJo__34) +// mstore(validateJo_i_290, 400) +// revert(validateJo_i_290, validateJo__34) // } // validateJo_b := add(validateJo_b, validateJo__52) // } @@ -340,13 +340,13 @@ // } // if iszero(eq(mod(keccak256(validateJo__24, add(validateJo_b, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60)), validateJo_gen_order), validateJo_challenge)) // { -// mstore(0x00, 404) -// revert(0x00, 0x20) +// mstore(validateJo_i_290, 404) +// revert(validateJo_i_290, 0x20) // } -// mstore(0x00, 0x01) -// return(0x00, 0x20) -// mstore(0x00, 404) -// revert(0x00, 0x20) +// mstore(validateJo_i_290, 0x01) +// return(validateJo_i_290, 0x20) +// mstore(validateJo_i_290, 404) +// revert(validateJo_i_290, 0x20) // function validatePairing(t2) // { // let t2_x_1 := calldataload(t2) @@ -379,8 +379,8 @@ // let success := call(gas(), 8, 0, _165, _216, _165, _165) // if or(iszero(success), iszero(mload(_165))) // { -// mstore(0x00, 400) -// revert(0x00, _165) +// mstore(0, 400) +// revert(0, _165) // } // } // function validateCommitment(note, k_1, a_2) @@ -409,6 +409,6 @@ // { // calldatacopy(add(0x300, mul(i_7, 0x80)), add(add(notes_5, mul(i_7, 0xc0)), 0x60), 0x80) // } -// mstore(0x00, keccak256(0x300, mul(n_6, 0x80))) +// mstore(0, keccak256(0x300, mul(n_6, 0x80))) // } // } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index ac21fd91..efd1ba05 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -128,7 +129,7 @@ public: cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter/" << endl; - cout << " s(t)ructural simplifier? " << endl; + cout << " s(t)ructural simplifier/equi(v)alent function combiner? " << endl; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -184,6 +185,9 @@ public: case 'm': Rematerialiser::run(*m_dialect, *m_ast); break; + case 'v': + EquivalentFunctionCombiner::run(*m_ast); + break; default: cout << "Unknown option." << endl; } -- cgit From a10db051de404f9f049ad3a951c3b5a9de571697 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 20 Dec 2018 13:20:07 +0100 Subject: [SMTChecker] Support basic typecast --- Changelog.md | 1 + libsolidity/formal/SMTChecker.cpp | 45 +++++++++++++++++++++- libsolidity/formal/SMTChecker.h | 1 + .../smtCheckerTests/complex/warn_on_typecast.sol | 2 +- .../smtCheckerTests/special/msg_sender_2.sol | 3 +- .../smtCheckerTests/typecast/cast_address_1.sol | 12 ++++++ .../typecast/cast_different_size_1.sol | 26 +++++++++++++ .../smtCheckerTests/typecast/cast_larger_1.sol | 12 ++++++ .../smtCheckerTests/typecast/cast_larger_2.sol | 13 +++++++ .../typecast/cast_larger_2_fail.sol | 13 +++++++ .../smtCheckerTests/typecast/cast_larger_3.sol | 17 ++++++++ .../smtCheckerTests/typecast/cast_smaller_1.sol | 12 ++++++ .../smtCheckerTests/typecast/cast_smaller_2.sol | 14 +++++++ .../smtCheckerTests/typecast/cast_smaller_3.sol | 14 +++++++ 14 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_address_1.sol create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_different_size_1.sol create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_larger_1.sol create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_larger_2.sol create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_larger_2_fail.sol create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_smaller_1.sol create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_smaller_2.sol create mode 100644 test/libsolidity/smtCheckerTests/typecast/cast_smaller_3.sol diff --git a/Changelog.md b/Changelog.md index bb7b191f..614acacc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: * Control Flow Graph: Warn about unreachable code. + * SMTChecker: Support basic typecasts without truncation. Bugfixes: diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 35c1e2f1..38715289 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -386,7 +386,7 @@ void SMTChecker::endVisit(BinaryOperation const& _op) void SMTChecker::endVisit(FunctionCall const& _funCall) { solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall) { m_errorReporter.warning( _funCall.location(), @@ -395,6 +395,12 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) return; } + if (_funCall.annotation().kind == FunctionCallKind::TypeConversion) + { + visitTypeConversion(_funCall); + return; + } + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); std::vector> const args = _funCall.arguments(); @@ -571,6 +577,43 @@ void SMTChecker::endVisit(Identifier const& _identifier) } } +void SMTChecker::visitTypeConversion(FunctionCall const& _funCall) +{ + solAssert(_funCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + solAssert(_funCall.arguments().size() == 1, ""); + auto argument = _funCall.arguments().at(0); + unsigned argSize = argument->annotation().type->storageBytes(); + unsigned castSize = _funCall.annotation().type->storageBytes(); + if (argSize == castSize) + defineExpr(_funCall, expr(*argument)); + else + { + createExpr(_funCall); + setUnknownValue(*m_expressions.at(&_funCall)); + auto const& funCallCategory = _funCall.annotation().type->category(); + // TODO: truncating and bytesX needs a different approach because of right padding. + if (funCallCategory == Type::Category::Integer || funCallCategory == Type::Category::Address) + { + if (argSize < castSize) + defineExpr(_funCall, expr(*argument)); + else + { + auto const& intType = dynamic_cast(*m_expressions.at(&_funCall)->type()); + defineExpr(_funCall, smt::Expression::ite( + expr(*argument) >= minValue(intType) && expr(*argument) <= maxValue(intType), + expr(*argument), + expr(_funCall) + )); + } + } + + m_errorReporter.warning( + _funCall.location(), + "Type conversion is not yet fully supported and might yield false positives." + ); + } +} + void SMTChecker::visitFunctionIdentifier(Identifier const& _identifier) { auto const& fType = dynamic_cast(*_identifier.annotation().type); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index f14d2ac0..caa83764 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -86,6 +86,7 @@ private: void visitAssert(FunctionCall const& _funCall); void visitRequire(FunctionCall const& _funCall); void visitGasLeft(FunctionCall const& _funCall); + void visitTypeConversion(FunctionCall const& _funCall); /// Visits the FunctionDefinition of the called function /// if available and inlines the return value. void inlineFunctionCall(FunctionCall const& _funCall); diff --git a/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol b/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol index be785414..8ecfe41e 100644 --- a/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol +++ b/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// Warning: (106-114): Assertion checker does not yet implement this expression. +// Warning: (106-114): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol index ad45d076..f122f4f2 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol @@ -10,5 +10,4 @@ contract C } } // ---- -// Warning: (98-108): Assertion checker does not yet implement this expression. -// Warning: (98-108): Internal error: Expression undefined for SMT solver. +// Warning: (98-108): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_address_1.sol b/test/libsolidity/smtCheckerTests/typecast/cast_address_1.sol new file mode 100644 index 00000000..885118a5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_address_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(address a) public pure { + require(a != address(0)); + assert(a != address(0)); + } +} +// ---- +// Warning: (98-108): Type conversion is not yet fully supported and might yield false positives. +// Warning: (125-135): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_different_size_1.sol b/test/libsolidity/smtCheckerTests/typecast/cast_different_size_1.sol new file mode 100644 index 00000000..baacaef1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_different_size_1.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + bytes2 a = 0x1234; + uint32 b = uint16(a); // b will be 0x00001234 + assert(b == 0x1234); + uint32 c = uint32(bytes4(a)); // c will be 0x12340000 + // This fails because right padding is not supported. + assert(c == 0x12340000); + uint8 d = uint8(uint16(a)); // d will be 0x34 + // False positive since truncating is not supported yet. + assert(d == 0x34); + uint8 e = uint8(bytes1(a)); // e will be 0x12 + // False positive since truncating is not supported yet. + assert(e == 0x12); + } +} +// ---- +// Warning: (186-195): Type conversion is not yet fully supported and might yield false positives. +// Warning: (280-303): Assertion violation happens here +// Warning: (317-333): Type conversion is not yet fully supported and might yield false positives. +// Warning: (414-431): Assertion violation happens here +// Warning: (451-460): Type conversion is not yet fully supported and might yield false positives. +// Warning: (542-559): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_1.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_1.sol new file mode 100644 index 00000000..4b0f42e7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint8 x) public pure { + uint16 y = uint16(x); + // True because of x's type + assert(y < 300); + } +} +// ---- +// Warning: (94-103): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_2.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_2.sol new file mode 100644 index 00000000..1f981c8c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_2.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + uint16 a = 0x1234; + uint32 b = uint32(a); // b will be 0x00001234 now + // This is correct (left padding). + assert(a == b); + } +} +// ---- +// Warning: (108-117): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_2_fail.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_2_fail.sol new file mode 100644 index 00000000..7f707381 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_2_fail.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + uint16 a = 0x1234; + uint32 b = uint32(a); // b will be 0x00001234 now + assert(a != b); + } +} +// ---- +// Warning: (108-117): Type conversion is not yet fully supported and might yield false positives. +// Warning: (149-163): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol new file mode 100644 index 00000000..cc51639e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + bytes2 a = 0x1234; + bytes4 b = bytes4(a); // b will be 0x12340000 + // False positive since right padding is not supported yet. + assert(b == 0x12340000); + // This should fail (right padding). + assert(a == b); + } +} +// ---- +// Warning: (108-117): Type conversion is not yet fully supported and might yield false positives. +// Warning: (207-230): Assertion violation happens here +// Warning: (273-287): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_1.sol b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_1.sol new file mode 100644 index 00000000..3e964dfd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint16 x) public pure { + uint8 y = uint8(x); + // True because of y's type + assert(y < 300); + } +} +// ---- +// Warning: (94-102): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_2.sol b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_2.sol new file mode 100644 index 00000000..25270108 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_2.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + uint32 a = 0x12345678; + uint16 b = uint16(a); // b will be 0x5678 now + // False positive since truncation is not supported yet. + assert(b == 0x5678); + } +} +// ---- +// Warning: (112-121): Type conversion is not yet fully supported and might yield false positives. +// Warning: (208-227): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_3.sol b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_3.sol new file mode 100644 index 00000000..1c9ea545 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_3.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + bytes2 a = 0x1234; + bytes1 b = bytes1(a); // b will be 0x12 + // False positive since truncation is not supported yet. + assert(b == 0x12); + } +} +// ---- +// Warning: (108-117): Type conversion is not yet fully supported and might yield false positives. +// Warning: (198-215): Assertion violation happens here -- cgit From b58a6a4a04ef10d19b24cf80866bfce33091136d Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 16 Jan 2019 14:50:50 +0200 Subject: Remove FAQ item --- docs/frequently-asked-questions.rst | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 4635d577..1c24eef1 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -108,36 +108,6 @@ In this example:: } } -Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) to another contract? -===================================================================================================== - -Sure. Take care that if you cross the memory / storage boundary, -independent copies will be created:: - - pragma solidity >=0.4.16 <0.6.0; - - contract C { - uint[20] x; - - function f() public { - g(x); - h(x); - } - - function g(uint[20] memory y) internal pure { - y[2] = 3; - } - - function h(uint[20] storage y) internal { - y[3] = 4; - } - } - -The call to ``g(x)`` will not have an effect on ``x`` because it needs -to create an independent copy of the storage value in memory. -On the other hand, ``h(x)`` successfully modifies ``x`` because only -a reference and not a copy is passed. - What does the following strange check do in the Custom Token contract? ====================================================================== -- cgit From 816e23c7f658f568a38d35a7b62d22d9001682ca Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 16 Jan 2019 14:57:31 +0200 Subject: Remove FAQ Item --- docs/frequently-asked-questions.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 4635d577..71636735 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -151,16 +151,6 @@ does not fit inside this range, it is truncated. These truncations can have `serious consequences `_, so code like the one above is necessary to avoid certain attacks. - -Why can number literals not be converted to fixed-size bytes types? -=================================================================== - -Since version 0.5.0 only hexadecimal number literals can be converted to fixed-size bytes -types and only if the number of hex digits matches the size of the type. See :ref:`types-conversion-literals` -for a full explanation and examples. - - - More Questions? =============== -- cgit From 065c3c87af0cd4f620d929a01b2902edf6c5892d Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 9 Jan 2019 14:05:03 +0100 Subject: libyul: changing some AST members from shared_ptr<> to unique_ptr<> * Some spaces look a little more verbose now, but that shouln't be a problem as it also should raise readability, too. * This makes some use of return-value-optimizations also. --- libyul/AsmData.h | 12 ++++++------ libyul/AsmParser.cpp | 20 ++++++++++---------- libyul/optimiser/ASTCopier.h | 5 +++-- libyul/optimiser/DataFlowAnalyzer.cpp | 2 ++ libyul/optimiser/ExpressionSplitter.cpp | 2 +- libyul/optimiser/FullInliner.cpp | 12 ++++++------ libyul/optimiser/SSATransform.cpp | 23 ++++++++++++++--------- libyul/optimiser/StructuralSimplifier.cpp | 26 ++++++++++++++++++-------- libyul/optimiser/SyntacticalEquality.cpp | 10 +++++----- libyul/optimiser/SyntacticalEquality.h | 2 +- libyul/optimiser/VarDeclInitializer.cpp | 4 ++-- 11 files changed, 68 insertions(+), 50 deletions(-) diff --git a/libyul/AsmData.h b/libyul/AsmData.h index 86c373a4..fd8eab38 100644 --- a/libyul/AsmData.h +++ b/libyul/AsmData.h @@ -59,25 +59,25 @@ struct StackAssignment { langutil::SourceLocation location; Identifier variableN /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// a single stack slot and expects a single expression on the right hand returning /// the same amount of items as the number of variables. -struct Assignment { langutil::SourceLocation location; std::vector variableNames; std::shared_ptr value; }; +struct Assignment { langutil::SourceLocation location; std::vector variableNames; std::unique_ptr value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" struct FunctionalInstruction { langutil::SourceLocation location; dev::solidity::Instruction instruction; std::vector arguments; }; struct FunctionCall { langutil::SourceLocation location; Identifier functionName; std::vector arguments; }; /// Statement that contains only a single expression struct ExpressionStatement { langutil::SourceLocation location; Expression expression; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::shared_ptr value; }; +struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::unique_ptr value; }; /// Block that creates a scope (frees declared stack variables) struct Block { langutil::SourceLocation location; std::vector statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") struct FunctionDefinition { langutil::SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. -struct If { langutil::SourceLocation location; std::shared_ptr condition; Block body; }; +struct If { langutil::SourceLocation location; std::unique_ptr condition; Block body; }; /// Switch case or default case -struct Case { langutil::SourceLocation location; std::shared_ptr value; Block body; }; +struct Case { langutil::SourceLocation location; std::unique_ptr value; Block body; }; /// Switch statement -struct Switch { langutil::SourceLocation location; std::shared_ptr expression; std::vector cases; }; -struct ForLoop { langutil::SourceLocation location; Block pre; std::shared_ptr condition; Block post; Block body; }; +struct Switch { langutil::SourceLocation location; std::unique_ptr expression; std::vector cases; }; +struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr condition; Block post; Block body; }; struct LocationExtractor: boost::static_visitor { diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index f3ca6cd0..217c838a 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -81,15 +81,15 @@ Statement Parser::parseStatement() { If _if = createWithLocation(); m_scanner->next(); - _if.condition = make_shared(parseExpression()); + _if.condition = make_unique(parseExpression()); _if.body = parseBlock(); - return _if; + return Statement{move(_if)}; } case Token::Switch: { Switch _switch = createWithLocation(); m_scanner->next(); - _switch.expression = make_shared(parseExpression()); + _switch.expression = make_unique(parseExpression()); while (m_scanner->currentToken() == Token::Case) _switch.cases.emplace_back(parseCase()); if (m_scanner->currentToken() == Token::Default) @@ -101,7 +101,7 @@ Statement Parser::parseStatement() if (_switch.cases.empty()) fatalParserError("Switch statement without any cases."); _switch.location.end = _switch.cases.back().body.location.end; - return _switch; + return Statement{move(_switch)}; } case Token::For: return parseForLoop(); @@ -120,7 +120,7 @@ Statement Parser::parseStatement() fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); - return assignment; + return Statement{move(assignment)}; } default: break; @@ -163,7 +163,7 @@ Statement Parser::parseStatement() assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; - return assignment; + return Statement{std::move(assignment)}; } case Token::Colon: { @@ -184,7 +184,7 @@ Statement Parser::parseStatement() assignment.variableNames.emplace_back(identifier); assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; - return assignment; + return Statement{std::move(assignment)}; } else { @@ -230,7 +230,7 @@ Case Parser::parseCase() ElementaryOperation literal = parseElementaryOperation(); if (literal.type() != typeid(Literal)) fatalParserError("Literal expected."); - _case.value = make_shared(boost::get(std::move(literal))); + _case.value = make_unique(boost::get(std::move(literal))); } else fatalParserError("Case or default case expected."); @@ -245,7 +245,7 @@ ForLoop Parser::parseForLoop() ForLoop forLoop = createWithLocation(); expectToken(Token::For); forLoop.pre = parseBlock(); - forLoop.condition = make_shared(parseExpression()); + forLoop.condition = make_unique(parseExpression()); forLoop.post = parseBlock(); forLoop.body = parseBlock(); forLoop.location.end = forLoop.body.location.end; @@ -443,7 +443,7 @@ VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::Colon); expectToken(Token::Assign); - varDecl.value.reset(new Expression(parseExpression())); + varDecl.value = make_unique(parseExpression()); varDecl.location.end = locationOf(*varDecl.value).end; } else diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index 4d2f18ae..7e5e9c86 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -93,10 +93,11 @@ protected: std::vector translateVector(std::vector const& _values); template - std::shared_ptr translate(std::shared_ptr const& _v) + std::unique_ptr translate(std::unique_ptr const& _v) { - return _v ? std::make_shared(translate(*_v)) : nullptr; + return _v ? std::make_unique(translate(*_v)) : nullptr; } + Block translate(Block const& _block); Case translate(Case const& _case); Identifier translate(Identifier const& _identifier); diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index e97bcdc4..b4f2d1f9 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -51,8 +51,10 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl) for (auto const& var: _varDecl.variables) names.emplace(var.name); m_variableScopes.back().variables += names; + if (_varDecl.value) visit(*_varDecl.value); + handleAssignment(names, _varDecl.value.get()); } diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 334e33f4..2f80fc32 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -106,7 +106,7 @@ void ExpressionSplitter::outlineExpression(Expression& _expr) m_statementsToPrefix.emplace_back(VariableDeclaration{ location, {{TypedName{location, var, {}}}}, - make_shared(std::move(_expr)) + make_unique(std::move(_expr)) }); _expr = Identifier{location, var}; } diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index dd969faf..ca6162ec 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -181,9 +181,9 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC variableReplacements[_existingVariable.name] = newName; VariableDeclaration varDecl{_funCall.location, {{_funCall.location, newName, _existingVariable.type}}, {}}; if (_value) - varDecl.value = make_shared(std::move(*_value)); + varDecl.value = make_unique(std::move(*_value)); else - varDecl.value = make_shared(zero); + varDecl.value = make_unique(zero); newStatements.emplace_back(std::move(varDecl)); }; @@ -202,7 +202,7 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC newStatements.emplace_back(Assignment{ _assignment.location, {_assignment.variableNames[i]}, - make_shared(Identifier{ + make_unique(Identifier{ _assignment.location, variableReplacements.at(function->returnVariables[i].name) }) @@ -214,7 +214,7 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC newStatements.emplace_back(VariableDeclaration{ _varDecl.location, {std::move(_varDecl.variables[i])}, - make_shared(Identifier{ + make_unique(Identifier{ _varDecl.location, variableReplacements.at(function->returnVariables[i].name) }) @@ -232,10 +232,10 @@ Statement BodyCopier::operator()(VariableDeclaration const& _varDecl) return ASTCopier::operator()(_varDecl); } -Statement BodyCopier::operator()(FunctionDefinition const& _funDef) +Statement BodyCopier::operator()(FunctionDefinition const&) { assertThrow(false, OptimizerException, "Function hoisting has to be done before function inlining."); - return _funDef; + return {}; } YulString BodyCopier::translateIdentifier(YulString _name) diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index 928c0859..33c875b5 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -66,12 +66,13 @@ void SSATransform::operator()(Block& _block) // Creates a new variable (and returns its declaration) with value _value // and replaces _value by a reference to that new variable. - auto replaceByNew = [&](SourceLocation _loc, YulString _varName, YulString _type, shared_ptr& _value) -> VariableDeclaration + + auto replaceByNew = [&](SourceLocation _loc, YulString _varName, YulString _type, unique_ptr& _value) -> VariableDeclaration { YulString newName = m_nameDispenser.newName(_varName); m_currentVariableValues[_varName] = newName; variablesToClearAtEnd.emplace(_varName); - shared_ptr v = make_shared(Identifier{_loc, newName}); + unique_ptr v = make_unique(Identifier{_loc, newName}); _value.swap(v); return VariableDeclaration{_loc, {TypedName{_loc, std::move(newName), std::move(_type)}}, std::move(v)}; }; @@ -87,14 +88,16 @@ void SSATransform::operator()(Block& _block) visit(*varDecl.value); if (varDecl.variables.size() != 1 || !m_variablesToReplace.count(varDecl.variables.front().name)) return {}; - // Replace "let a := v" by "let a_1 := v let a := v" - VariableDeclaration newVarDecl = replaceByNew( + vector v; + // Replace "let a := v" by "let a_1 := v let a := a_1" + v.emplace_back(replaceByNew( varDecl.location, varDecl.variables.front().name, varDecl.variables.front().type, varDecl.value - ); - return vector{std::move(newVarDecl), std::move(varDecl)}; + )); + v.emplace_back(move(varDecl)); + return v; } else if (_s.type() == typeid(Assignment)) { @@ -103,14 +106,16 @@ void SSATransform::operator()(Block& _block) if (assignment.variableNames.size() != 1) return {}; assertThrow(m_variablesToReplace.count(assignment.variableNames.front().name), OptimizerException, ""); + vector v; // Replace "a := v" by "let a_1 := v a := v" - VariableDeclaration newVarDecl = replaceByNew( + v.emplace_back(replaceByNew( assignment.location, assignment.variableNames.front().name, {}, // TODO determine type assignment.value - ); - return vector{std::move(newVarDecl), std::move(assignment)}; + )); + v.emplace_back(move(assignment)); + return v; } else visit(_s); diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 8d7dcd57..727775cb 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -51,7 +51,11 @@ void StructuralSimplifier::simplify(std::vector& _statements) GenericFallbackReturnsVisitor const visitor( [&](If& _ifStmt) -> OptionalStatements { if (_ifStmt.body.statements.empty()) - return {{makePopExpressionStatement(_ifStmt.location, std::move(*_ifStmt.condition))}}; + { + OptionalStatements s = vector{}; + s->emplace_back(makePopExpressionStatement(_ifStmt.location, std::move(*_ifStmt.condition))); + return s; + } if (expressionAlwaysTrue(*_ifStmt.condition)) return {std::move(_ifStmt.body.statements)}; else if (expressionAlwaysFalse(*_ifStmt.condition)) @@ -64,18 +68,24 @@ void StructuralSimplifier::simplify(std::vector& _statements) auto& switchCase = _switchStmt.cases.front(); auto loc = locationOf(*_switchStmt.expression); if (switchCase.value) - return {{If{ + { + OptionalStatements s = vector{}; + s->emplace_back(If{ std::move(_switchStmt.location), - make_shared(FunctionalInstruction{ + make_unique(FunctionalInstruction{ std::move(loc), solidity::Instruction::EQ, {std::move(*switchCase.value), std::move(*_switchStmt.expression)} - }), std::move(switchCase.body)}}}; + }), std::move(switchCase.body)}); + return s; + } else - return {{ - makePopExpressionStatement(loc, std::move(*_switchStmt.expression)), - std::move(switchCase.body) - }}; + { + OptionalStatements s = vector{}; + s->emplace_back(makePopExpressionStatement(loc, std::move(*_switchStmt.expression))); + s->emplace_back(std::move(switchCase.body)); + return s; + } } else return {}; diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index ba8cc793..53f0b029 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -100,7 +100,7 @@ bool SyntacticallyEqual::statementEqual(Assignment const& _lhs, Assignment const bool SyntacticallyEqual::statementEqual(VariableDeclaration const& _lhs, VariableDeclaration const& _rhs) { // first visit expression, then variable declarations - if (!compareSharedPtr(_lhs.value, _rhs.value)) + if (!compareUniquePtr(_lhs.value, _rhs.value)) return false; return containerEqual(_lhs.variables, _rhs.variables, [this](TypedName const& _lhsVarName, TypedName const& _rhsVarName) -> bool { return this->visitDeclaration(_lhsVarName, _rhsVarName); @@ -123,7 +123,7 @@ bool SyntacticallyEqual::statementEqual(FunctionDefinition const& _lhs, Function bool SyntacticallyEqual::statementEqual(If const& _lhs, If const& _rhs) { return - compareSharedPtr(_lhs.condition, _rhs.condition) && + compareUniquePtr(_lhs.condition, _rhs.condition) && statementEqual(_lhs.body, _rhs.body); } @@ -139,7 +139,7 @@ bool SyntacticallyEqual::statementEqual(Switch const& _lhs, Switch const& _rhs) for (auto const& rhsCase: _rhs.cases) rhsCases.insert(&rhsCase); return - compareSharedPtr(_lhs.expression, _rhs.expression) && + compareUniquePtr(_lhs.expression, _rhs.expression) && containerEqual(lhsCases, rhsCases, [this](Case const* _lhsCase, Case const* _rhsCase) -> bool { return this->switchCaseEqual(*_lhsCase, *_rhsCase); }); @@ -149,7 +149,7 @@ bool SyntacticallyEqual::statementEqual(Switch const& _lhs, Switch const& _rhs) bool SyntacticallyEqual::switchCaseEqual(Case const& _lhs, Case const& _rhs) { return - compareSharedPtr(_lhs.value, _rhs.value) && + compareUniquePtr(_lhs.value, _rhs.value) && statementEqual(_lhs.body, _rhs.body); } @@ -157,7 +157,7 @@ bool SyntacticallyEqual::statementEqual(ForLoop const& _lhs, ForLoop const& _rhs { return statementEqual(_lhs.pre, _rhs.pre) && - compareSharedPtr(_lhs.condition, _rhs.condition) && + compareUniquePtr(_lhs.condition, _rhs.condition) && statementEqual(_lhs.body, _rhs.body) && statementEqual(_lhs.post, _rhs.post); } diff --git a/libyul/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h index d4630d95..af240717 100644 --- a/libyul/optimiser/SyntacticalEquality.h +++ b/libyul/optimiser/SyntacticalEquality.h @@ -76,7 +76,7 @@ private: } template - bool compareSharedPtr(std::shared_ptr const& _lhs, std::shared_ptr const& _rhs) + bool compareUniquePtr(std::unique_ptr const& _lhs, std::unique_ptr const& _rhs) { return (_lhs == _rhs) || (_lhs && _rhs && (this->*CompareMember)(*_lhs, *_rhs)); } diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index 4a26757f..7009cc9b 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -39,7 +39,7 @@ void VarDeclInitializer::operator()(Block& _block) return {}; else if (_varDecl.variables.size() == 1) { - _varDecl.value = make_shared(zero); + _varDecl.value = make_unique(zero); return {}; } else @@ -47,7 +47,7 @@ void VarDeclInitializer::operator()(Block& _block) OptionalStatements ret{vector{}}; langutil::SourceLocation loc{std::move(_varDecl.location)}; for (auto& var: _varDecl.variables) - ret->push_back(VariableDeclaration{loc, {std::move(var)}, make_shared(zero)}); + ret->push_back(VariableDeclaration{loc, {std::move(var)}, make_unique(zero)}); return ret; } } -- cgit From eab9e58e7e6b815e7dfb288510b49d52fe579fe2 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 16 Jan 2019 17:11:23 +0200 Subject: Add call warning into main docs and remove FAQ item --- docs/frequently-asked-questions.rst | 12 ------------ docs/units-and-global-variables.rst | 4 ++++ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 4635d577..3b59af81 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -54,18 +54,6 @@ Yes, you can use ``abi.encodePacked``:: } } - -Why is the low-level function ``.call()`` less favorable than instantiating a contract with a variable (``ContractB b;``) and executing its functions (``b.doSomething();``)? -============================================================================================================================================================================= - -If you use actual functions, the compiler will tell you if the types -or your arguments do not match, if the function does not exist -or is not visible and it will do the packing of the -arguments for you. - -See `ping.sol `_ and -`pong.sol `_. - What happens if you send ether along with a function call to a contract? ======================================================================== diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 59acfcff..e44caa8f 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -199,6 +199,10 @@ Members of Address Types For more information, see the section on :ref:`address`. +.. warning:: + You should avoid using ``.call()`` when executing another contract function as it bypasses the compiler type checking, + function existence check, and argument packing. + .. warning:: There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order -- cgit From 131e23100cab1cb3d6a69ec09d8d25aab35861f9 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 16 Jan 2019 17:39:29 +0200 Subject: Mention that there is no Cron in Ethereum --- docs/contracts.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 646e4cdc..5bab6e78 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -10,7 +10,8 @@ Contracts in Solidity are similar to classes in object-oriented languages. They contain persistent data in state variables and functions that can modify these variables. Calling a function on a different contract (instance) will perform an EVM function call and thus switch the context such that state variables are -inaccessible. +inaccessible. A contract and its functions need to be called for anything to happen. +There is no "cron" concept in Ethereum to call a function at a particular event automatically. .. include:: contracts/creating-contracts.rst -- cgit From 5279f9f03bdcaafaeaadb20121f8cab550b2ed9e Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Thu, 17 Jan 2019 10:19:16 +0200 Subject: Update docs/units-and-global-variables.rst Co-Authored-By: ChrisChinchilla --- docs/units-and-global-variables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index e44caa8f..c47f257b 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -200,7 +200,7 @@ Members of Address Types For more information, see the section on :ref:`address`. .. warning:: - You should avoid using ``.call()`` when executing another contract function as it bypasses the compiler type checking, + You should avoid using ``.call()`` whenever possible when executing another contract function as it bypasses type checking, function existence check, and argument packing. .. warning:: -- cgit From 1f06a94b5d200ca4fcbb580043ffec0c4fc549a5 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 17 Jan 2019 12:46:48 +0100 Subject: Add missing Changelog entries for Yul switch changes. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index bb7b191f..7668daa6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,8 @@ Compiler Features: Bugfixes: * Yul: Check that arguments to ``dataoffset`` and ``datasize`` are literals at parse time and properly take this into account in the optimizer. + * Yul: Parse number literals for detecting duplicate switch cases. + * Yul: Require switch cases to have the same type. Build System: -- cgit From c96b760c472a134c59deb20c27e5252319c74e2b Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 17 Jan 2019 13:36:06 +0100 Subject: Return TypeError is fixed point encoding is attempted. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 7668daa6..a0438b53 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: Bugfixes: + * TypeChecker: Return type error if fixed point encoding is attempted instead of throwing ``UnimplementedFeatureError``. * Yul: Check that arguments to ``dataoffset`` and ``datasize`` are literals at parse time and properly take this into account in the optimizer. * Yul: Parse number literals for detecting duplicate switch cases. * Yul: Require switch cases to have the same type. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 507a2c94..3d671a71 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1482,7 +1482,16 @@ void TypeChecker::typeCheckABIEncodeFunctions( if (argType->category() == Type::Category::RationalNumber) { - if (!argType->mobileType()) + auto const& rationalType = dynamic_cast(*argType); + if (rationalType.isFractional()) + { + m_errorReporter.typeError( + arguments[i]->location(), + "Fixed point numbers cannot yet be encoded." + ); + continue; + } + else if (!argType->mobileType()) { m_errorReporter.typeError( arguments[i]->location(), -- cgit From 83e7233bb8923a83d7ecc093dae88b140336a137 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 17 Jan 2019 13:54:31 +0100 Subject: Change error message and add tests --- libsolidity/analysis/TypeChecker.cpp | 2 +- test/libsolidity/syntaxTests/types/encoding_fractional.sol | 7 +++++++ .../syntaxTests/types/encoding_fractional_abiencoderv2.sol | 9 +++++++++ .../syntaxTests/types/encoding_packed_fractional.sol | 8 ++++++++ .../types/encoding_packed_fractional_abiencoderv2.sol | 10 ++++++++++ 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/types/encoding_fractional.sol create mode 100644 test/libsolidity/syntaxTests/types/encoding_fractional_abiencoderv2.sol create mode 100644 test/libsolidity/syntaxTests/types/encoding_packed_fractional.sol create mode 100644 test/libsolidity/syntaxTests/types/encoding_packed_fractional_abiencoderv2.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 3d671a71..7fa9933c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1487,7 +1487,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( { m_errorReporter.typeError( arguments[i]->location(), - "Fixed point numbers cannot yet be encoded." + "Fractional numbers cannot yet be encoded." ); continue; } diff --git a/test/libsolidity/syntaxTests/types/encoding_fractional.sol b/test/libsolidity/syntaxTests/types/encoding_fractional.sol new file mode 100644 index 00000000..16c76d9a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/encoding_fractional.sol @@ -0,0 +1,7 @@ +contract C { + function f1() public pure returns (bytes memory) { + return abi.encode(0.1, 1); + } +} +// ---- +// TypeError: (92-95): Fractional numbers cannot yet be encoded. diff --git a/test/libsolidity/syntaxTests/types/encoding_fractional_abiencoderv2.sol b/test/libsolidity/syntaxTests/types/encoding_fractional_abiencoderv2.sol new file mode 100644 index 00000000..80efa9c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/encoding_fractional_abiencoderv2.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; +contract C { + function f1() public pure returns (bytes memory) { + return abi.encode(0.1, 1); + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (126-129): Fractional numbers cannot yet be encoded. diff --git a/test/libsolidity/syntaxTests/types/encoding_packed_fractional.sol b/test/libsolidity/syntaxTests/types/encoding_packed_fractional.sol new file mode 100644 index 00000000..71080cb0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/encoding_packed_fractional.sol @@ -0,0 +1,8 @@ +contract C { + function f1() public pure returns (bytes memory) { + return abi.encodePacked(0.1, 1); + } +} +// ---- +// TypeError: (98-101): Fractional numbers cannot yet be encoded. +// TypeError: (103-104): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. diff --git a/test/libsolidity/syntaxTests/types/encoding_packed_fractional_abiencoderv2.sol b/test/libsolidity/syntaxTests/types/encoding_packed_fractional_abiencoderv2.sol new file mode 100644 index 00000000..e82c3c9a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/encoding_packed_fractional_abiencoderv2.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; +contract C { + function f1() public pure returns (bytes memory) { + return abi.encodePacked(0.1, 1); + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (132-135): Fractional numbers cannot yet be encoded. +// TypeError: (137-138): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. -- cgit From ecdc00d569e5f53b2dac125ea04c85e68e0ae595 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 7 Dec 2018 11:36:54 +0100 Subject: Set emscripten to strict mode (wrt deprecated compiler options). --- cmake/EthCompilerSettings.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index c45ecba6..252e3ae1 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -98,6 +98,8 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # Abort if linking results in any undefined symbols # Note: this is on by default in the CMake Emscripten module which we aren't using set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") + # Disallow deprecated emscripten build options. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1") add_definitions(-DETH_EMSCRIPTEN=1) endif() endif() -- cgit From 3f58505c76acf3f2ffe2c171aa1332a3eea02990 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 17 Jan 2019 15:23:19 +0000 Subject: Remove obsolete ETH_EMSCRIPTEN option This was made obsolete by using toolchain files (#2836) --- cmake/EthCompilerSettings.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 252e3ae1..8ffdfeb0 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -100,7 +100,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") # Disallow deprecated emscripten build options. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1") - add_definitions(-DETH_EMSCRIPTEN=1) endif() endif() -- cgit From 5baac8470be4b82d53939e260d40f38d2fc15e6e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 11 Jul 2018 17:15:03 +0200 Subject: Use emscripten 1.38.8 on both travis and circle. --- .circleci/config.yml | 2 +- .travis.yml | 2 +- Changelog.md | 1 + cmake/EthCompilerSettings.cmake | 5 +++++ libsolc/CMakeLists.txt | 3 +++ scripts/build_emscripten.sh | 2 +- scripts/travis-emscripten/build_emscripten.sh | 8 ++++---- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 66fe6430..52bb36cf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,7 +34,7 @@ version: 2 jobs: build_emscripten: docker: - - image: trzeci/emscripten:sdk-tag-1.37.21-64bit + - image: trzeci/emscripten:sdk-tag-1.38.8-64bit environment: TERM: xterm steps: diff --git a/.travis.yml b/.travis.yml index 2036f03e..a8a68aff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -114,7 +114,7 @@ matrix: before_install: - nvm install 8 - nvm use 8 - - docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit + - docker pull trzeci/emscripten:sdk-tag-1.38.8-64bit env: - SOLC_EMSCRIPTEN=On - SOLC_INSTALL_DEPS_TRAVIS=Off diff --git a/Changelog.md b/Changelog.md index 7668daa6..79ec90a0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Bugfixes: Build System: + * Emscripten: Upgrade to emscripten 1.38.8 on travis and circleci. ### 0.5.2 (2018-12-19) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 8ffdfeb0..d05ccaff 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -100,6 +100,11 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") # Disallow deprecated emscripten build options. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1") + # Export the Emscripten-generated auxiliary methods which are needed by solc-js. + # Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','Pointer_stringify','lengthBytesUTF8','_malloc','stringToUTF8','setValue']") + # Do not build as a WebAssembly target - we need an asm.js output. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0") endif() endif() diff --git a/libsolc/CMakeLists.txt b/libsolc/CMakeLists.txt index dfdf8162..7fb4aa05 100644 --- a/libsolc/CMakeLists.txt +++ b/libsolc/CMakeLists.txt @@ -1,4 +1,7 @@ if (EMSCRIPTEN) + # Specify which functions to export in soljson.js. + # Note that additional Emscripten-generated methods needed by solc-js are + # defined to be exported in cmake/EthCompilerSettings.cmake. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\"]' -s RESERVED_FUNCTION_POINTERS=20") add_executable(soljson libsolc.cpp libsolc.h) target_link_libraries(soljson PRIVATE solidity) diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index 14c497ae..46521cc5 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -30,5 +30,5 @@ set -e if [[ "$OSTYPE" != "darwin"* ]]; then ./scripts/travis-emscripten/install_deps.sh - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.37.21-64bit ./scripts/travis-emscripten/build_emscripten.sh + docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.38.8-64bit ./scripts/travis-emscripten/build_emscripten.sh fi diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 32677460..373f32db 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -49,11 +49,11 @@ fi WORKSPACE=/root/project # Increase nodejs stack size -if [ -e ~/.emscripten ] +if ! [ -e /emsdk_portable/node/bin/node_orig ] then - sed -i -e 's/NODE_JS="nodejs"/NODE_JS=["nodejs", "--stack_size=8192"]/' ~/.emscripten -else - echo 'NODE_JS=["nodejs", "--stack_size=8192"]' > ~/.emscripten + mv /emsdk_portable/node/bin/node /emsdk_portable/node/bin/node_orig + echo -e '#!/bin/sh\nexec /emsdk_portable/node/bin/node_orig --stack-size=8192 $@' > /emsdk_portable/node/bin/node + chmod 755 /emsdk_portable/node/bin/node fi # Boost -- cgit From 44237211d1032c739636ab60a558521d60023b88 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Jan 2019 12:11:55 +0100 Subject: Tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 64 ++++++++++++++++++++++ .../syntaxTests/metaTypes/codeAccess.sol | 12 ++++ .../metaTypes/codeAccessAbstractCreation.sol | 10 ++++ .../metaTypes/codeAccessAbstractRuntime.sol | 10 ++++ .../syntaxTests/metaTypes/codeAccessBase.sol | 22 ++++++++ .../syntaxTests/metaTypes/codeAccessCyclic.sol | 12 ++++ .../syntaxTests/metaTypes/codeAccessIsConstant.sol | 7 +++ .../syntaxTests/metaTypes/codeAccessLibrary.sol | 12 ++++ .../syntaxTests/metaTypes/codeIsNoLValue.sol | 10 ++++ .../syntaxTests/metaTypes/noArgForType.sol | 7 +++ .../syntaxTests/metaTypes/tooManyArgsForType.sol | 7 +++ .../typeNotRegularIdentifierContractName.sol | 3 + .../metaTypes/typeNotRegularIdentifierFunction.sol | 6 ++ .../typeNotRegularIdentifierParameter.sol | 6 ++ .../typeNotRegularIdentifierStateVariable.sol | 5 ++ .../metaTypes/typeNotRegularIdentifierVariable.sol | 7 +++ .../syntaxTests/metaTypes/typeOfContract.sol | 6 ++ .../syntaxTests/metaTypes/typeRecursive.sol | 8 +++ .../metaTypes/unsupportedArgForType.sol | 9 +++ 19 files changed, 223 insertions(+) create mode 100644 test/libsolidity/syntaxTests/metaTypes/codeAccess.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/noArgForType.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8d219d16..6f420466 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -14234,6 +14234,70 @@ BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables) ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(code_access) +{ + char const* sourceCode = R"( + contract C { + function lengths() public pure returns (bool) { + uint crLen = type(D).creationCode.length; + uint runLen = type(D).runtimeCode.length; + require(runLen < crLen); + require(crLen >= 0x95 && crLen < 0xd0); + require(runLen >= 0x7a && runLen < 0xb0); + return true; + } + function creation() public pure returns (bytes memory) { + return type(D).creationCode; + } + function runtime() public pure returns (bytes memory) { + return type(D).runtimeCode; + } + function runtimeAllocCheck() public pure returns (bytes memory) { + uint[] memory a = new uint[](2); + bytes memory c = type(D).runtimeCode; + uint[] memory b = new uint[](2); + a[0] = 0x1111; + a[1] = 0x2222; + b[0] = 0x3333; + b[1] = 0x4444; + return c; + } + } + contract D { + function f() public pure returns (uint) { return 7; } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("lengths()"), encodeArgs(true)); + bytes codeCreation = callContractFunction("creation()"); + bytes codeRuntime1 = callContractFunction("runtime()"); + bytes codeRuntime2 = callContractFunction("runtimeAllocCheck()"); + ABI_CHECK(codeRuntime1, codeRuntime2); +} + +BOOST_AUTO_TEST_CASE(code_access_create) +{ + char const* sourceCode = R"( + contract C { + function test() public returns (uint) { + bytes memory c = type(D).creationCode; + D d; + assembly { + d := create(0, add(c, 0x20), mload(c)) + } + return d.f(); + } + } + contract D { + uint x; + constructor() public { x = 7; } + function f() public view returns (uint) { return x; } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("test()"), encodeArgs(7)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol new file mode 100644 index 00000000..e90443e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol @@ -0,0 +1,12 @@ +contract Test { + function creationOther() public pure returns (bytes memory) { + return type(Other).creationCode; + } + function runtimeOther() public pure returns (bytes memory) { + return type(Other).runtimeCode; + } +} +contract Other { + function f(uint) public pure returns (uint) {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol new file mode 100644 index 00000000..bae137c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol @@ -0,0 +1,10 @@ +contract Test { + function creationOther() public pure returns (bytes memory) { + return type(Other).creationCode; + } +} +contract Other { + function f(uint) public returns (uint); +} +// ---- +// TypeError: (97-121): Member "creationCode" not found or not visible after argument-dependent lookup in type(contract Other). diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol new file mode 100644 index 00000000..186d2714 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol @@ -0,0 +1,10 @@ +contract Test { + function runtime() public pure returns (bytes memory) { + return type(Other).runtimeCode; + } +} +contract Other { + function f(uint) public returns (uint); +} +// ---- +// TypeError: (91-114): Member "runtimeCode" not found or not visible after argument-dependent lookup in type(contract Other). diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol new file mode 100644 index 00000000..33dbfd7c --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol @@ -0,0 +1,22 @@ +contract Base { + function f() public pure returns (uint) {} +} +contract Test is Base { + function creation() public pure returns (bytes memory) { + return type(Test).creationCode; + } + function runtime() public pure returns (bytes memory) { + return type(Test).runtimeCode; + } + function creationBase() public pure returns (bytes memory) { + return type(Base).creationCode; + } + function runtimeBase() public pure returns (bytes memory) { + return type(Base).runtimeCode; + } +} +// ---- +// TypeError: (165-188): Circular reference for contract code access. +// TypeError: (271-293): Circular reference for contract code access. +// TypeError: (381-404): Circular reference for contract code access. +// TypeError: (491-513): Circular reference for contract code access. diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol new file mode 100644 index 00000000..d5723df6 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol @@ -0,0 +1,12 @@ +contract A { + function f() public pure { + type(B).runtimeCode; + } +} +contract B { + function f() public pure { + type(A).runtimeCode; + } +} +// ---- +// TypeError: (133-152): Circular reference for contract code access. diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol new file mode 100644 index 00000000..cda5d5c3 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol @@ -0,0 +1,7 @@ +contract Test { + bytes constant c = type(B).creationCode; + bytes constant r = type(B).runtimeCode; + +} +contract B { function f() public pure {} } +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol new file mode 100644 index 00000000..f746dc35 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol @@ -0,0 +1,12 @@ +contract Test { + function creationOther() public pure returns (bytes memory) { + return type(Library).creationCode; + } + function runtime() public pure returns (bytes memory) { + return type(Library).runtimeCode; + } +} +contract Library { + function f(uint) public pure returns (uint) {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol b/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol new file mode 100644 index 00000000..022e4d6f --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol @@ -0,0 +1,10 @@ +contract Test { + function f() public pure { + type(C).creationCode = new bytes(6); + type(C).runtimeCode = new bytes(6); + } +} +contract C {} +// ---- +// TypeError: (55-75): Expression has to be an lvalue. +// TypeError: (100-119): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol b/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol new file mode 100644 index 00000000..542aaf53 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol @@ -0,0 +1,7 @@ +contract Test { + function creation() public pure returns (bytes memory) { + type(); + } +} +// ---- +// TypeError: (85-91): This function takes one argument, but 0 were provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol b/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol new file mode 100644 index 00000000..61c2b779 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol @@ -0,0 +1,7 @@ +contract Test { + function creation() public pure returns (bytes memory) { + type(1, 2); + } +} +// ---- +// TypeError: (85-95): This function takes one argument, but 2 were provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol new file mode 100644 index 00000000..144ca1c3 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol @@ -0,0 +1,3 @@ +contract type { } +// ---- +// ParserError: (9-13): Expected identifier but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol new file mode 100644 index 00000000..b7881f15 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol @@ -0,0 +1,6 @@ +contract Test { + function type() public pure { + } +} +// ---- +// ParserError: (29-33): Expected identifier but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol new file mode 100644 index 00000000..001ba840 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol @@ -0,0 +1,6 @@ +contract Test { + function f(uint type) public pure { + } +} +// ---- +// ParserError: (36-40): Expected ',' but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol new file mode 100644 index 00000000..fa827a33 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol @@ -0,0 +1,5 @@ +contract Test { + uint type; +} +// ---- +// ParserError: (25-29): Expected identifier but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol new file mode 100644 index 00000000..fa57698d --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol @@ -0,0 +1,7 @@ +contract Test { + function f() public pure { + uint type; + } +} +// ---- +// ParserError: (60-64): Expected ';' but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol b/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol new file mode 100644 index 00000000..036c36e8 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol @@ -0,0 +1,6 @@ +contract Test { + function f() public pure returns (bytes memory) { + type(Test); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol new file mode 100644 index 00000000..0ce06786 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol @@ -0,0 +1,8 @@ +contract Test { + function f() public pure { + type(type(type(Test))); + } +} +// ---- +// TypeError: (65-75): Invalid type for argument in function call. Contract type required, but type(contract Test) provided. +// TypeError: (60-76): Invalid type for argument in function call. Contract type required, but tuple() provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol new file mode 100644 index 00000000..5c27d42f --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol @@ -0,0 +1,9 @@ +contract Test { + struct S { uint x; } + function f() public pure { + // Unsupported for now, but might be supported in the future + type(S); + } +} +// ---- +// TypeError: (154-155): Invalid type for argument in function call. Contract type required, but type(struct Test.S) provided. -- cgit From 2fcfb216b5dcb5cec2d70d2ee7647df47c8166ca Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Jan 2019 16:28:39 +0100 Subject: Syntax for meta type information. --- liblangutil/Token.h | 2 +- libsolidity/analysis/GlobalContext.cpp | 9 ++++- libsolidity/analysis/TypeChecker.cpp | 40 ++++++++++++++++++++ libsolidity/analysis/TypeChecker.h | 2 + libsolidity/analysis/ViewPureChecker.cpp | 4 +- libsolidity/ast/Types.cpp | 43 +++++++++++++++++++--- libsolidity/ast/Types.h | 17 +++++++-- libsolidity/codegen/ExpressionCompiler.cpp | 3 ++ libsolidity/parsing/Parser.cpp | 6 +++ test/libsolidity/ASTJSON/address_payable.json | 2 +- .../ASTJSON/address_payable_legacy.json | 2 +- 11 files changed, 116 insertions(+), 14 deletions(-) diff --git a/liblangutil/Token.h b/liblangutil/Token.h index f832fdf7..b3a1acb1 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -180,6 +180,7 @@ namespace langutil K(CallData, "calldata", 0) \ K(Struct, "struct", 0) \ K(Throw, "throw", 0) \ + K(Type, "type", 0) \ K(Using, "using", 0) \ K(Var, "var", 0) \ K(View, "view", 0) \ @@ -256,7 +257,6 @@ namespace langutil K(Supports, "supports", 0) \ K(Switch, "switch", 0) \ K(Try, "try", 0) \ - K(Type, "type", 0) \ K(Typedef, "typedef", 0) \ K(TypeOf, "typeof", 0) \ K(Unchecked, "unchecked", 0) \ diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index cd5fe07d..2276d783 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -61,7 +61,14 @@ m_magicVariables(vector>{ make_shared("sha256", make_shared(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)), make_shared("sha3", make_shared(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), make_shared("suicide", make_shared(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), - make_shared("tx", make_shared(MagicType::Kind::Transaction)) + make_shared("tx", make_shared(MagicType::Kind::Transaction)), + make_shared("type", make_shared( + strings{"address"} /* accepts any contract type, handled by the type checker */, + strings{} /* returns a MagicType, handled by the type checker */, + FunctionType::Kind::MetaType, + false, + StateMutability::Pure + )), }) { } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 507a2c94..4cdfcc0c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -199,6 +199,38 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c return components; } +TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall) +{ + vector> arguments = _functionCall.arguments(); + if (arguments.size() != 1) + { + m_errorReporter.typeError( + _functionCall.location(), + "This function takes one argument, but " + + toString(arguments.size()) + + " were provided." + ); + return {}; + } + TypePointer firstArgType = type(*arguments.front()); + if ( + firstArgType->category() != Type::Category::TypeType || + dynamic_cast(*firstArgType).actualType()->category() != TypeType::Category::Contract + ) + { + m_errorReporter.typeError( + arguments.front()->location(), + "Invalid type for argument in function call. " + "Contract type required, but " + + type(*arguments.front())->toString(true) + + " provided." + ); + return {}; + } + + return {MagicType::metaType(dynamic_cast(*firstArgType).actualType())}; +} + void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) { auto base = dynamic_cast(&dereference(_inheritance.name())); @@ -1822,6 +1854,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes = functionType->returnParameterTypes(); break; } + case FunctionType::Kind::MetaType: + returnTypes = typeCheckMetaTypeFunctionAndRetrieveReturnType(_functionCall); + break; default: { typeCheckFunctionCall(_functionCall, functionType); @@ -2062,8 +2097,13 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (tt->actualType()->category() == Type::Category::Enum) annotation.isPure = true; if (auto magicType = dynamic_cast(exprType.get())) + { if (magicType->kind() == MagicType::Kind::ABI) annotation.isPure = true; + else if (magicType->kind() == MagicType::Kind::MetaType) + if (memberName == "creationCode" || memberName == "runtimeCode") + annotation.isPure = true; + } return false; } diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index b60c571a..d5f3645c 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -81,6 +81,8 @@ private: bool _abiEncoderV2 ); + TypePointers typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall); + /// Performs type checks and determines result types for type conversion FunctionCall nodes. TypePointer typeCheckTypeConversionAndRetrieveReturnType( FunctionCall const& _functionCall diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index eb019481..7df7ac17 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -338,7 +338,9 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::ABI, "encodeWithSignature"}, {MagicType::Kind::Block, "blockhash"}, {MagicType::Kind::Message, "data"}, - {MagicType::Kind::Message, "sig"} + {MagicType::Kind::Message, "sig"}, + {MagicType::Kind::MetaType, "creationCode"}, + {MagicType::Kind::MetaType, "runtimeCode"} }; set static const payableMembers{ {MagicType::Kind::Message, "value"} diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index cc978b4a..20859d28 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2626,6 +2626,7 @@ string FunctionType::richIdentifier() const case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break; case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break; case Kind::ABIDecode: id += "abidecode"; break; + case Kind::MetaType: id += "metatype"; break; } id += "_" + stateMutabilityToString(m_stateMutability); id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); @@ -3037,7 +3038,8 @@ bool FunctionType::isPure() const m_kind == Kind::ABIEncodePacked || m_kind == Kind::ABIEncodeWithSelector || m_kind == Kind::ABIEncodeWithSignature || - m_kind == Kind::ABIDecode; + m_kind == Kind::ABIDecode || + m_kind == Kind::MetaType; } TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) @@ -3305,6 +3307,14 @@ string ModuleType::toString(bool) const return string("module \"") + m_sourceUnit.annotation().path + string("\""); } +shared_ptr MagicType::metaType(TypePointer _type) +{ + solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now."); + auto t = make_shared(Kind::MetaType); + t->m_typeArgument = std::move(_type); + return t; +} + string MagicType::richIdentifier() const { switch (m_kind) @@ -3317,6 +3327,9 @@ string MagicType::richIdentifier() const return "t_magic_transaction"; case Kind::ABI: return "t_magic_abi"; + case Kind::MetaType: + solAssert(m_typeArgument, ""); + return "t_magic_meta_type_" + m_typeArgument->richIdentifier(); } return ""; } @@ -3403,12 +3416,27 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const StateMutability::Pure )} }); - default: - solAssert(false, "Unknown kind of magic."); + case Kind::MetaType: + { + solAssert( + m_typeArgument && m_typeArgument->category() == Type::Category::Contract, + "Only contracts supported for now" + ); + ContractDefinition const& contract = dynamic_cast(*m_typeArgument).contractDefinition(); + if (contract.annotation().unimplementedFunctions.empty() && contract.constructorIsPublic()) + return MemberList::MemberMap({ + {"creationCode", std::make_shared(DataLocation::Memory)}, + {"runtimeCode", std::make_shared(DataLocation::Memory)} + }); + else + return {}; } + } + solAssert(false, "Unknown kind of magic."); + return {}; } -string MagicType::toString(bool) const +string MagicType::toString(bool _short) const { switch (m_kind) { @@ -3420,7 +3448,10 @@ string MagicType::toString(bool) const return "tx"; case Kind::ABI: return "abi"; - default: - solAssert(false, "Unknown kind of magic."); + case Kind::MetaType: + solAssert(m_typeArgument, ""); + return "type(" + m_typeArgument->toString(_short) + ")"; } + solAssert(false, "Unknown kind of magic."); + return {}; } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index bee00661..5427786a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -989,6 +989,7 @@ public: ABIEncodeWithSignature, ABIDecode, GasLeft, ///< gasleft() + MetaType ///< type(...) }; Category category() const override { return Category::Function; } @@ -1299,16 +1300,23 @@ private: }; /** - * Special type for magic variables (block, msg, tx), similar to a struct but without any reference - * (it always references a global singleton by name). + * Special type for magic variables (block, msg, tx, type(...)), similar to a struct but without any reference. */ class MagicType: public Type { public: - enum class Kind { Block, Message, Transaction, ABI }; + enum class Kind { + Block, ///< "block" + Message, ///< "msg" + Transaction, ///< "tx" + ABI, ///< "abi" + MetaType ///< "type(...)" + }; Category category() const override { return Category::Magic; } explicit MagicType(Kind _kind): m_kind(_kind) {} + /// Factory function for meta type + static std::shared_ptr metaType(TypePointer _type); TypeResult binaryOperatorResult(Token, TypePointer const&) const override { @@ -1329,6 +1337,9 @@ public: private: Kind m_kind; + /// Contract type used for contract metadata magic. + TypePointer m_typeArgument; + }; /** diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index be2709ae..5c2fa6d0 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1107,6 +1107,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case FunctionType::Kind::GasLeft: m_context << Instruction::GAS; break; + case FunctionType::Kind::MetaType: + // No code to generate. + break; } } return false; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8a6bc343..35476a76 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1551,6 +1551,12 @@ ASTPointer Parser::parsePrimaryExpression() nodeFactory.markEndPosition(); expression = nodeFactory.createNode(getLiteralAndAdvance()); break; + case Token::Type: + // Inside expressions "type" is the name of a special, globally-available function. + nodeFactory.markEndPosition(); + m_scanner->next(); + expression = nodeFactory.createNode(make_shared("type")); + break; case Token::LParen: case Token::LBrack: { diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json index 0f30e8e8..c8b71628 100644 --- a/test/libsolidity/ASTJSON/address_payable.json +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -277,7 +277,7 @@ "name" : "this", "nodeType" : "Identifier", "overloadedDeclarations" : [], - "referencedDeclaration" : 65, + "referencedDeclaration" : 66, "src" : "217:4:1", "typeDescriptions" : { diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json index dd8a5582..8abebce0 100644 --- a/test/libsolidity/ASTJSON/address_payable_legacy.json +++ b/test/libsolidity/ASTJSON/address_payable_legacy.json @@ -424,7 +424,7 @@ [ null ], - "referencedDeclaration" : 65, + "referencedDeclaration" : 66, "type" : "contract C", "value" : "this" }, -- cgit From e6fee257e68e7b145a47eee8c5937db7a7a99849 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Jan 2019 16:44:31 +0100 Subject: Code generation for access to contract code. --- libsolidity/ast/Types.cpp | 7 ++++++ libsolidity/ast/Types.h | 2 ++ libsolidity/codegen/Compiler.cpp | 6 ++--- libsolidity/codegen/Compiler.h | 4 +++- libsolidity/codegen/CompilerContext.cpp | 15 +++++++++---- libsolidity/codegen/CompilerContext.h | 6 +++-- libsolidity/codegen/CompilerUtils.cpp | 23 ++++++++++++++++++++ libsolidity/codegen/CompilerUtils.h | 7 ++++++ libsolidity/codegen/ContractCompiler.cpp | 12 +++++----- libsolidity/codegen/ContractCompiler.h | 6 ++--- libsolidity/codegen/ExpressionCompiler.cpp | 35 ++++++++++++++++-------------- libsolidity/interface/CompilerStack.cpp | 14 ++++++------ libsolidity/interface/CompilerStack.h | 6 +++-- test/libsolidity/Assembly.cpp | 3 ++- 14 files changed, 101 insertions(+), 45 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 20859d28..8f1fe491 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3455,3 +3455,10 @@ string MagicType::toString(bool _short) const solAssert(false, "Unknown kind of magic."); return {}; } + +TypePointer MagicType::typeArgument() const +{ + solAssert(m_kind == Kind::MetaType, ""); + solAssert(m_typeArgument, ""); + return m_typeArgument; +} diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 5427786a..53109de1 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1335,6 +1335,8 @@ public: Kind kind() const { return m_kind; } + TypePointer typeArgument() const; + private: Kind m_kind; /// Contract type used for contract metadata magic. diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index a22e6e9d..f0eaca4f 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -31,18 +31,18 @@ using namespace dev::solidity; void Compiler::compileContract( ContractDefinition const& _contract, - std::map const& _contracts, + std::map> const& _otherCompilers, bytes const& _metadata ) { ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns); - runtimeCompiler.compileContract(_contract, _contracts); + runtimeCompiler.compileContract(_contract, _otherCompilers); m_runtimeContext.appendAuxiliaryData(_metadata); // This might modify m_runtimeContext because it can access runtime functions at // creation time. ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1); - m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts); + m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers); m_context.optimise(m_optimize, m_optimizeRuns); } diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 784d7f8c..953267a4 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -45,11 +45,13 @@ public: /// @arg _metadata contains the to be injected metadata CBOR void compileContract( ContractDefinition const& _contract, - std::map const& _contracts, + std::map> const& _otherCompilers, bytes const& _metadata ); /// @returns Entire assembly. eth::Assembly const& assembly() const { return m_context.assembly(); } + /// @returns Runtime assembly. + eth::Assembly const& runtimeAssembly() const { return m_context.assembly().sub(m_runtimeSub); } /// @returns The entire assembled object (with constructor). eth::LinkerObject assembledObject() const { return m_context.assembledObject(); } /// @returns Only the runtime object (without constructor). diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index be681b2e..20e1af7c 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -167,11 +167,18 @@ unsigned CompilerContext::numberOfLocalVariables() const return m_localVariables.size(); } -eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const +eth::Assembly const& CompilerContext::compiledContract(ContractDefinition const& _contract) const { - auto ret = m_compiledContracts.find(&_contract); - solAssert(ret != m_compiledContracts.end(), "Compiled contract not found."); - return *ret->second; + auto ret = m_otherCompilers.find(&_contract); + solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); + return ret->second->assembly(); +} + +eth::Assembly const& CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const +{ + auto ret = m_otherCompilers.find(&_contract); + solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); + return ret->second->runtimeAssembly(); } bool CompilerContext::isLocalVariable(Declaration const* _declaration) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index dedcd95f..43e1ea77 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -41,6 +41,7 @@ namespace dev { namespace solidity { +class Compiler; /** * Context to be shared by all units that compile the same contract. @@ -74,8 +75,9 @@ public: /// Returns the number of currently allocated local variables. unsigned numberOfLocalVariables() const; - void setCompiledContracts(std::map const& _contracts) { m_compiledContracts = _contracts; } + void setOtherCompilers(std::map> const& _otherCompilers) { m_otherCompilers = _otherCompilers; } eth::Assembly const& compiledContract(ContractDefinition const& _contract) const; + eth::Assembly const& compiledContractRuntime(ContractDefinition const& _contract) const; void setStackOffset(int _offset) { m_asm->setDeposit(_offset); } void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); } @@ -307,7 +309,7 @@ private: /// Activated experimental features. std::set m_experimentalFeatures; /// Other already compiled contracts to be used in contract creation calls. - std::map m_compiledContracts; + std::map> m_otherCompilers; /// Storage offsets of state variables std::map> m_stateVariables; /// Offsets of local variables on the stack (relative to stack base). diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index bbc703c7..9b7244ba 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -1199,6 +1199,29 @@ void CompilerUtils::computeHashStatic() m_context << u256(32) << u256(0) << Instruction::KECCAK256; } +void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract, bool _creation) +{ + string which = _creation ? "Creation" : "Runtime"; + m_context.callLowLevelFunction( + "$copyContract" + which + "CodeToMemory_" + contract.type()->identifier(), + 1, + 1, + [&contract, _creation](CompilerContext& _context) + { + // copy the contract's code into memory + eth::Assembly const& assembly = + _creation ? + _context.compiledContract(contract) : + _context.compiledContractRuntime(contract); + // pushes size + auto subroutine = _context.addSubroutine(make_shared(assembly)); + _context << Instruction::DUP1 << subroutine; + _context << Instruction::DUP4 << Instruction::CODECOPY; + _context << Instruction::ADD; + } + ); +} + void CompilerUtils::storeStringData(bytesConstRef _data) { //@todo provide both alternatives to the optimiser diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 7e4f47ba..6bde2e8b 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -263,6 +263,13 @@ public: /// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type. void computeHashStatic(); + /// Apppends code that copies the code of the given contract to memory. + /// Stack pre: Memory position + /// Stack post: Updated memory position + /// @param creation if true, copies creation code, if false copies runtime code. + /// @note the contract has to be compiled already, so beware of cyclic dependencies! + void copyContractCodeToMemory(ContractDefinition const& contract, bool _creationCode); + /// Bytes we need to the start of call data. /// - The size in bytes of the function (hash) identifier. static const unsigned dataStartOffset; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index b051d260..a718603b 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -60,7 +60,7 @@ private: void ContractCompiler::compileContract( ContractDefinition const& _contract, - std::map const& _contracts + map> const& _otherCompilers ) { CompilerContext::LocationSetter locationSetter(m_context, _contract); @@ -70,7 +70,7 @@ void ContractCompiler::compileContract( // This has to be the first code in the contract. appendDelegatecallCheck(); - initializeContext(_contract, _contracts); + initializeContext(_contract, _otherCompilers); // This generates the dispatch function for externally visible functions // and adds the function to the compilation queue. Additionally internal functions, // which are referenced directly or indirectly will be added. @@ -81,7 +81,7 @@ void ContractCompiler::compileContract( size_t ContractCompiler::compileConstructor( ContractDefinition const& _contract, - std::map const& _contracts + std::map> const& _otherCompilers ) { CompilerContext::LocationSetter locationSetter(m_context, _contract); @@ -89,18 +89,18 @@ size_t ContractCompiler::compileConstructor( return deployLibrary(_contract); else { - initializeContext(_contract, _contracts); + initializeContext(_contract, _otherCompilers); return packIntoContractCreator(_contract); } } void ContractCompiler::initializeContext( ContractDefinition const& _contract, - map const& _compiledContracts + map> const& _otherCompilers ) { m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); - m_context.setCompiledContracts(_compiledContracts); + m_context.setOtherCompilers(_otherCompilers); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); CompilerUtils(m_context).initialiseFreeMemoryPointer(); registerStateVariables(_contract); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 40871f0d..9ab006f6 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -49,13 +49,13 @@ public: void compileContract( ContractDefinition const& _contract, - std::map const& _contracts + std::map> const& _otherCompilers ); /// Compiles the constructor part of the contract. /// @returns the identifier of the runtime sub-assembly. size_t compileConstructor( ContractDefinition const& _contract, - std::map const& _contracts + std::map> const& _otherCompilers ); private: @@ -63,7 +63,7 @@ private: /// information about the contract like the AST annotations. void initializeContext( ContractDefinition const& _contract, - std::map const& _compiledContracts + std::map> const& _otherCompilers ); /// Adds the code that is run at creation time. Should be run after exchanging the run-time context /// with a new and initialized context. Adds the constructor code. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 5c2fa6d0..e6bb163d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -594,22 +594,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } ContractDefinition const* contract = &dynamic_cast(*function.returnParameterTypes().front()).contractDefinition(); - m_context.callLowLevelFunction( - "$copyContractCreationCodeToMemory_" + contract->type()->identifier(), - 0, - 1, - [contract](CompilerContext& _context) - { - // copy the contract's code into memory - eth::Assembly const& assembly = _context.compiledContract(*contract); - CompilerUtils(_context).fetchFreeMemoryPointer(); - // pushes size - auto subroutine = _context.addSubroutine(make_shared(assembly)); - _context << Instruction::DUP1 << subroutine; - _context << Instruction::DUP4 << Instruction::CODECOPY; - _context << Instruction::ADD; - } - ); + utils().fetchFreeMemoryPointer(); + utils().copyContractCodeToMemory(*contract, true); utils().abiEncode(argumentTypes, function.parameterTypes()); // now on stack: memory_end_ptr // need: size, offset, endowment @@ -1351,6 +1337,23 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Gas has been removed."); else if (member == "blockhash") solAssert(false, "Blockhash has been removed."); + else if (member == "creationCode" || member == "runtimeCode") + { + TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); + ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + utils().fetchFreeMemoryPointer(); + m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; + utils().copyContractCodeToMemory(contract, member == "creationCode"); + // Stack: start end + m_context.appendInlineAssembly( + Whiskers(R"({ + mstore(start, sub(end, add(start, 0x20))) + mstore(, end) + })")("free", to_string(CompilerUtils::freeMemoryPointer)).render(), + {"start", "end"} + ); + m_context << Instruction::POP; + } else solAssert(false, "Unknown magic member."); break; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index f9d889e7..fc33f755 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -343,12 +343,12 @@ bool CompilerStack::compile() return false; // Only compile contracts individually which have been requested. - map compiledContracts; + map> otherCompilers; for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->nodes()) if (auto contract = dynamic_cast(node.get())) if (isRequestedContract(*contract)) - compileContract(*contract, compiledContracts); + compileContract(*contract, otherCompilers); m_stackState = CompilationSuccessful; this->link(); return true; @@ -795,19 +795,19 @@ bool onlySafeExperimentalFeaturesActivated(set const& featu void CompilerStack::compileContract( ContractDefinition const& _contract, - map& _compiledContracts + map>& _otherCompilers ) { solAssert(m_stackState >= AnalysisSuccessful, ""); if ( - _compiledContracts.count(&_contract) || + _otherCompilers.count(&_contract) || !_contract.annotation().unimplementedFunctions.empty() || !_contract.constructorIsPublic() ) return; for (auto const* dependency: _contract.annotation().contractDependencies) - compileContract(*dependency, _compiledContracts); + compileContract(*dependency, _otherCompilers); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); @@ -825,7 +825,7 @@ void CompilerStack::compileContract( try { // Run optimiser and compile the contract. - compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata); + compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata); } catch(eth::OptimizerException const&) { @@ -852,7 +852,7 @@ void CompilerStack::compileContract( solAssert(false, "Assembly exception for deployed bytecode"); } - _compiledContracts[compiledContract.contract] = &compiler->assembly(); + _otherCompilers[compiledContract.contract] = compiler; } CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 81d5009f..79927850 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -293,10 +293,12 @@ private: /// @returns true if the contract is requested to be compiled. bool isRequestedContract(ContractDefinition const& _contract) const; - /// Compile a single contract and put the result in @a _compiledContracts. + /// Compile a single contract. + /// @param _otherCompilers provides access to compilers of other contracts, to get + /// their bytecode if needed. Only filled after they have been compiled. void compileContract( ContractDefinition const& _contract, - std::map& _compiledContracts + std::map>& _otherCompilers ); /// Links all the known library addresses in the available objects. Any unknown diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index baa9bff1..b5a1797b 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -46,6 +46,7 @@ namespace dev { namespace solidity { +class Contract; namespace test { @@ -84,7 +85,7 @@ eth::AssemblyItems compileContract(std::shared_ptr _sourceCode) if (ContractDefinition* contract = dynamic_cast(node.get())) { Compiler compiler(dev::test::Options::get().evmVersion()); - compiler.compileContract(*contract, map{}, bytes()); + compiler.compileContract(*contract, map>{}, bytes()); return compiler.runtimeAssemblyItems(); } -- cgit From 0bfdaa500af6ae1a480f6f6f417a6a12ada0a313 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Jan 2019 00:14:10 +0100 Subject: Add code access dependency. --- libsolidity/analysis/TypeChecker.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 4cdfcc0c..ee5865f7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2100,9 +2100,20 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) { if (magicType->kind() == MagicType::Kind::ABI) annotation.isPure = true; - else if (magicType->kind() == MagicType::Kind::MetaType) - if (memberName == "creationCode" || memberName == "runtimeCode") - annotation.isPure = true; + else if (magicType->kind() == MagicType::Kind::MetaType && ( + memberName == "creationCode" || memberName == "runtimeCode" + )) + { + annotation.isPure = true; + m_scope->annotation().contractDependencies.insert( + &dynamic_cast(*magicType->typeArgument()).contractDefinition() + ); + if (contractDependenciesAreCyclic(*m_scope)) + m_errorReporter.typeError( + _memberAccess.location(), + "Circular reference for contract code access." + ); + } } return false; -- cgit From 4669b06ab448b61350bff23db12da1a8e9fff5fa Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Jan 2019 22:49:15 +0100 Subject: Warn if type(..).runtimeCode is used with assembly in the constructor. --- libsolidity/analysis/StaticAnalyzer.cpp | 62 ++++++++++++++++++++++ libsolidity/analysis/StaticAnalyzer.h | 9 +++- .../metaTypes/runtimeCodeWarningAssembly.sol | 17 ++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index aaaa4f9f..11ed6a4f 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -32,6 +32,56 @@ using namespace dev; using namespace langutil; using namespace dev::solidity; +/** + * Helper class that determines whether a contract's constructor uses inline assembly. + */ +class dev::solidity::ConstructorUsesAssembly +{ +public: + /// @returns true if and only if the contract's or any of its bases' constructors + /// use inline assembly. + bool check(ContractDefinition const& _contract) + { + for (auto const* base: _contract.annotation().linearizedBaseContracts) + if (checkInternal(*base)) + return true; + return false; + } + + +private: + class Checker: public ASTConstVisitor + { + public: + Checker(FunctionDefinition const& _f) { _f.accept(*this); } + bool visit(InlineAssembly const&) override { assemblySeen = true; return false; } + bool assemblySeen = false; + }; + + bool checkInternal(ContractDefinition const& _contract) + { + if (!m_usesAssembly.count(&_contract)) + { + bool usesAssembly = false; + if (_contract.constructor()) + usesAssembly = Checker{*_contract.constructor()}.assemblySeen; + m_usesAssembly[&_contract] = usesAssembly; + } + return m_usesAssembly[&_contract]; + } + + map m_usesAssembly; +}; + +StaticAnalyzer::StaticAnalyzer(ErrorReporter& _errorReporter): + m_errorReporter(_errorReporter) +{ +} + +StaticAnalyzer::~StaticAnalyzer() +{ +} + bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); @@ -152,6 +202,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) _memberAccess.location(), "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" ); + else if (type->kind() == MagicType::Kind::MetaType && _memberAccess.memberName() == "runtimeCode") + { + if (!m_constructorUsesAssembly) + m_constructorUsesAssembly = make_unique(); + ContractType const& contract = dynamic_cast(*type->typeArgument()); + if (m_constructorUsesAssembly->check(contract.contractDefinition())) + m_errorReporter.warning( + _memberAccess.location(), + "The constructor of the contract (or its base) uses inline assembly. " + "Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode." + ); + } } if (_memberAccess.memberName() == "callcode") diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index ff33fa3a..3daf83b3 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -38,6 +38,8 @@ namespace dev namespace solidity { +class ConstructorUsesAssembly; + /** * The module that performs static analysis on the AST. @@ -49,7 +51,8 @@ class StaticAnalyzer: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter); + ~StaticAnalyzer(); /// Performs static analysis on the given source unit and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings @@ -85,6 +88,10 @@ private: /// when traversing. std::map, int> m_localVarUseCount; + /// Cache that holds information about whether a contract's constructor + /// uses inline assembly. + std::unique_ptr m_constructorUsesAssembly; + FunctionDefinition const* m_currentFunction = nullptr; /// Flag that indicates a constructor. diff --git a/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol b/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol new file mode 100644 index 00000000..ec8d9784 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol @@ -0,0 +1,17 @@ +contract Test { + function f() public pure returns (uint) { + return type(C).runtimeCode.length + + type(D).runtimeCode.length + + type(C).creationCode.length + + type(D).creationCode.length; + } +} +contract C { + constructor() public { assembly {} } +} +contract D is C { + constructor() public {} +} +// ---- +// Warning: (77-96): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode. +// Warning: (118-137): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode. -- cgit From fed56f33d562266726fa73df0d613c541eeb652c Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Jan 2019 23:05:07 +0100 Subject: Type is not reserved anymore. --- test/libsolidity/SolidityParser.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index a33c6134..413a8c9c 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -539,7 +539,6 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved) "supports", "switch", "try", - "type", "typedef", "typeof", "unchecked" -- cgit From 01ad4bffe7e16251efdf17121479369cabe5f993 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Jan 2019 10:55:18 +0100 Subject: Documentation. --- docs/miscellaneous.rst | 4 +++- docs/types/value-types.rst | 3 +++ docs/units-and-global-variables.rst | 30 ++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 5a6f3875..69124c77 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -385,6 +385,8 @@ Global Variables - ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information`. +- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information`. .. note:: Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, @@ -445,7 +447,7 @@ These keywords are reserved in Solidity. They might become part of the syntax in ``abstract``, ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``catch``, ``copyof``, ``default``, ``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, ``mutable``, ``null``, ``of``, ``override``, ``partial``, ``promise``, ``reference``, ``relocatable``, -``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``type``, ``typedef``, ``typeof``, +``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``typedef``, ``typeof``, ``unchecked``. Language Grammar diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 077587dd..09db1423 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -329,6 +329,9 @@ Contracts do not support any operators. The members of contract types are the external functions of the contract including public state variables. +For a contract ``C`` you can use ``type(C)`` to access +:ref:`type information` about the contract. + .. index:: byte array, bytes32 Fixed-size byte arrays diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index c47f257b..ce7706c1 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -244,3 +244,33 @@ Furthermore, all functions of the current contract are callable directly includi .. note:: Prior to version 0.5.0, there was a function called ``suicide`` with the same semantics as ``selfdestruct``. + +.. index:: type, creationCode, runtimeCode + +.. _meta-type: + +Type Information +---------------- + +The expression ``type(X)`` can be used to retrieve information about the +type ``X``. Currently, there is limited support for this feature, but +it might be expanded in the future. The following properties are +available for a conract type ``C``: + +``type(C).creationCode``: + Memory byte array that contains the creation bytecode of the contract. + This can be used in inline assembly to build custom creation routines, + especially by using the ``create2`` opcode. + This property can **not** be accessed in the contract itself or any + derived contract. It causes the bytecode to be included in the bytecode + of the call site and thus circular references like that are not possible. + +``type(C).runtimeCode``: + Memory byte array that contains the runtime bytecode of the contract. + This is the code that is usually deployed by the constructor of ``C``. + If ``C`` has a constructor that uses inline assembly, this might be + different from the actually deployed bytecode. Also note that libraries + modify their runtime bytecode at time of deployment to guard against + regular calls. + The same restrictions as with ``.creationCode`` also apply for this + property. -- cgit From a9fa2658d8690f18aa14c599a305cf59a5cd4e3c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 17 Jan 2019 20:08:17 +0100 Subject: Add helper to show if contract can be deployed. --- libsolidity/ast/AST.cpp | 5 +++++ libsolidity/ast/AST.h | 4 ++++ libsolidity/ast/Types.cpp | 2 +- libsolidity/interface/CompilerStack.cpp | 6 +----- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index f379d758..4cf8b1e8 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -138,6 +138,11 @@ bool ContractDefinition::constructorIsPublic() const return !f || f->isPublic(); } +bool ContractDefinition::canBeDeployed() const +{ + return constructorIsPublic() && annotation().unimplementedFunctions.empty(); +} + FunctionDefinition const* ContractDefinition::fallbackFunction() const { for (ContractDefinition const* contract: annotation().linearizedBaseContracts) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 9ac065ea..cd986050 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -408,6 +408,10 @@ public: FunctionDefinition const* constructor() const; /// @returns true iff the constructor of this contract is public (or non-existing). bool constructorIsPublic() const; + /// @returns true iff the contract can be deployed, i.e. is not abstract and has a + /// public constructor. + /// Should only be called after the type checker has run. + bool canBeDeployed() const; /// Returns the fallback function or nullptr if no fallback function was specified. FunctionDefinition const* fallbackFunction() const; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 8f1fe491..081c7fb6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3423,7 +3423,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const "Only contracts supported for now" ); ContractDefinition const& contract = dynamic_cast(*m_typeArgument).contractDefinition(); - if (contract.annotation().unimplementedFunctions.empty() && contract.constructorIsPublic()) + if (contract.canBeDeployed()) return MemberList::MemberMap({ {"creationCode", std::make_shared(DataLocation::Memory)}, {"runtimeCode", std::make_shared(DataLocation::Memory)} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index fc33f755..9e4da62d 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -800,11 +800,7 @@ void CompilerStack::compileContract( { solAssert(m_stackState >= AnalysisSuccessful, ""); - if ( - _otherCompilers.count(&_contract) || - !_contract.annotation().unimplementedFunctions.empty() || - !_contract.constructorIsPublic() - ) + if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed()) return; for (auto const* dependency: _contract.annotation().contractDependencies) compileContract(*dependency, _otherCompilers); -- cgit From 29f6aa7d560a7d82a9088489e663a079a3b41f73 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Jan 2019 11:44:11 +0100 Subject: Do not create a copy of the assembly. --- libsolidity/codegen/Compiler.cpp | 6 ++++++ libsolidity/codegen/Compiler.h | 4 +++- libsolidity/codegen/CompilerContext.cpp | 8 ++++---- libsolidity/codegen/CompilerContext.h | 12 ++++++------ libsolidity/codegen/CompilerUtils.cpp | 4 ++-- libsolidity/codegen/ContractCompiler.cpp | 2 +- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index f0eaca4f..72efed33 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -47,6 +47,12 @@ void Compiler::compileContract( m_context.optimise(m_optimize, m_optimizeRuns); } +std::shared_ptr Compiler::runtimeAssemblyPtr() const +{ + solAssert(m_context.runtimeContext(), ""); + return m_context.runtimeContext()->assemblyPtr(); +} + eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const { return m_runtimeContext.functionEntryLabelIfExists(_function); diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 953267a4..c21de96d 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -50,8 +50,10 @@ public: ); /// @returns Entire assembly. eth::Assembly const& assembly() const { return m_context.assembly(); } + /// @returns Entire assembly as a shared pointer to non-const. + std::shared_ptr assemblyPtr() const { return m_context.assemblyPtr(); } /// @returns Runtime assembly. - eth::Assembly const& runtimeAssembly() const { return m_context.assembly().sub(m_runtimeSub); } + std::shared_ptr runtimeAssemblyPtr() const; /// @returns The entire assembled object (with constructor). eth::LinkerObject assembledObject() const { return m_context.assembledObject(); } /// @returns Only the runtime object (without constructor). diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 20e1af7c..861b1c98 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -167,18 +167,18 @@ unsigned CompilerContext::numberOfLocalVariables() const return m_localVariables.size(); } -eth::Assembly const& CompilerContext::compiledContract(ContractDefinition const& _contract) const +shared_ptr CompilerContext::compiledContract(ContractDefinition const& _contract) const { auto ret = m_otherCompilers.find(&_contract); solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); - return ret->second->assembly(); + return ret->second->assemblyPtr(); } -eth::Assembly const& CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const +shared_ptr CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const { auto ret = m_otherCompilers.find(&_contract); solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); - return ret->second->runtimeAssembly(); + return ret->second->runtimeAssemblyPtr(); } bool CompilerContext::isLocalVariable(Declaration const* _declaration) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 43e1ea77..e5ddfbc5 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -76,8 +76,8 @@ public: unsigned numberOfLocalVariables() const; void setOtherCompilers(std::map> const& _otherCompilers) { m_otherCompilers = _otherCompilers; } - eth::Assembly const& compiledContract(ContractDefinition const& _contract) const; - eth::Assembly const& compiledContractRuntime(ContractDefinition const& _contract) const; + std::shared_ptr compiledContract(ContractDefinition const& _contract) const; + std::shared_ptr compiledContractRuntime(ContractDefinition const& _contract) const; void setStackOffset(int _offset) { m_asm->setDeposit(_offset); } void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); } @@ -224,15 +224,15 @@ public: void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. - CompilerContext* runtimeContext() { return m_runtimeContext; } + CompilerContext* runtimeContext() const { return m_runtimeContext; } /// @returns the identifier of the runtime subroutine. size_t runtimeSub() const { return m_runtimeSub; } /// @returns a const reference to the underlying assembly. eth::Assembly const& assembly() const { return *m_asm; } - /// @returns non-const reference to the underlying assembly. Should be avoided in favour of - /// wrappers in this class. - eth::Assembly& nonConstAssembly() { return *m_asm; } + /// @returns a shared pointer to the assembly. + /// Should be avoided except when adding sub-assemblies. + std::shared_ptr assemblyPtr() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 9b7244ba..6cfb0777 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -1209,12 +1209,12 @@ void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract, [&contract, _creation](CompilerContext& _context) { // copy the contract's code into memory - eth::Assembly const& assembly = + shared_ptr assembly = _creation ? _context.compiledContract(contract) : _context.compiledContractRuntime(contract); // pushes size - auto subroutine = _context.addSubroutine(make_shared(assembly)); + auto subroutine = _context.addSubroutine(assembly); _context << Instruction::DUP1 << subroutine; _context << Instruction::DUP4 << Instruction::CODECOPY; _context << Instruction::ADD; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index a718603b..f843e07a 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -716,7 +716,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) CodeGenerator::assemble( _inlineAssembly.operations(), *_inlineAssembly.annotation().analysisInfo, - m_context.nonConstAssembly(), + *m_context.assemblyPtr(), identifierAccess ); m_context.setStackOffset(startStackHeight); -- cgit From 2a0d4f358c6114740bb9fe063bc2620bd2d8724a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 17 Jan 2019 14:58:31 +0100 Subject: Add test for content of creationCode and runtimeCode. --- test/libsolidity/SolidityEndToEndTest.cpp | 47 +++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 6f420466..38be5ae7 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -14242,8 +14242,8 @@ BOOST_AUTO_TEST_CASE(code_access) uint crLen = type(D).creationCode.length; uint runLen = type(D).runtimeCode.length; require(runLen < crLen); - require(crLen >= 0x95 && crLen < 0xd0); - require(runLen >= 0x7a && runLen < 0xb0); + require(crLen >= 0x20); + require(runLen >= 0x20); return true; } function creation() public pure returns (bytes memory) { @@ -14298,6 +14298,49 @@ BOOST_AUTO_TEST_CASE(code_access_create) ABI_CHECK(callContractFunction("test()"), encodeArgs(7)); } +BOOST_AUTO_TEST_CASE(code_access_content) +{ + char const* sourceCode = R"( + contract C { + function testRuntime() public returns (bool) { + D d = new D(); + bytes32 runtimeHash = keccak256(type(D).runtimeCode); + bytes32 otherHash; + uint size; + assembly { + size := extcodesize(d) + extcodecopy(d, mload(0x40), 0, size) + otherHash := keccak256(mload(0x40), size) + } + require(size == type(D).runtimeCode.length); + require(runtimeHash == otherHash); + return true; + } + function testCreation() public returns (bool) { + D d = new D(); + bytes32 creationHash = keccak256(type(D).creationCode); + require(creationHash == d.x()); + return true; + } + } + contract D { + bytes32 public x; + constructor() public { + bytes32 codeHash; + assembly { + let size := codesize() + codecopy(mload(0x40), 0, size) + codeHash := keccak256(mload(0x40), size) + } + x = codeHash; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("testRuntime()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("testCreation()"), encodeArgs(true)); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit From 2a92403690a4998ab097503231ac39f854b9c76c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 17 Jan 2019 20:37:34 +0100 Subject: Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 7668daa6..5652cdb4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.5.3 (unreleased) Language Features: + * Provide access to creation and runtime code of contracts via ``type(C).creationCode`` / ``type(C).runtimeCode``. Compiler Features: -- cgit From 6de2d92f20d48d38797a628ee35e7615170cd63f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 16 Jan 2019 11:44:45 +0100 Subject: Add SSAReverser to the yul optimiser. --- libdevcore/CommonData.h | 53 ++++++++++++ libyul/CMakeLists.txt | 2 + libyul/optimiser/SSAReverser.cpp | 71 +++++++++++++++++ libyul/optimiser/SSAReverser.h | 54 +++++++++++++ libyul/optimiser/Suite.cpp | 13 ++- test/libyul/YulOptimizerTest.cpp | 18 +++++ test/libyul/yulOptimizerTests/fullSuite/abi2.yul | 38 ++++----- .../yulOptimizerTests/fullSuite/abi_example1.yul | 93 +++++++++++----------- test/libyul/yulOptimizerTests/fullSuite/aztec.yul | 15 ++-- test/libyul/yulOptimizerTests/fullSuite/medium.yul | 6 +- .../yulOptimizerTests/fullSuite/ssaReverse.yul | 62 +++++++++++++++ .../libyul/yulOptimizerTests/ssaAndBack/simple.yul | 18 +++++ .../yulOptimizerTests/ssaAndBack/ssaReverse.yul | 45 +++++++++++ .../yulOptimizerTests/ssaReverser/abi_example.yul | 44 ++++++++++ .../yulOptimizerTests/ssaReverser/simple.yul | 14 ++++ test/tools/yulopti.cpp | 6 +- 16 files changed, 470 insertions(+), 82 deletions(-) create mode 100644 libyul/optimiser/SSAReverser.cpp create mode 100644 libyul/optimiser/SSAReverser.h create mode 100644 test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/simple.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul create mode 100644 test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul create mode 100644 test/libyul/yulOptimizerTests/ssaReverser/simple.yul diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 1d668f26..98331936 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -263,6 +263,59 @@ void iterateReplacing(std::vector& _vector, const F& _f) _vector = std::move(modifiedVector); } + +namespace detail +{ +template +void iterateReplacingWindow(std::vector& _vector, F const& _f, std::index_sequence) +{ + // Concept: _f must be Callable, must accept sizeof...(I) parameters of type T&, must return optional> + bool useModified = false; + std::vector modifiedVector; + size_t i = 0; + for (; i + sizeof...(I) <= _vector.size(); ++i) + { + if (boost::optional> r = _f(_vector[i + I]...)) + { + if (!useModified) + { + std::move(_vector.begin(), _vector.begin() + i, back_inserter(modifiedVector)); + useModified = true; + } + modifiedVector += std::move(*r); + i += sizeof...(I) - 1; + } + else if (useModified) + modifiedVector.emplace_back(std::move(_vector[i])); + } + if (useModified) + { + for (; i < _vector.size(); ++i) + modifiedVector.emplace_back(std::move(_vector[i])); + _vector = std::move(modifiedVector); + } +} + +} + +/// Function that iterates over the vector @param _vector, +/// calling the function @param _f on sequences of @tparam N of its +/// elements. If @param _f returns a vector, these elements are replaced by +/// the returned vector and the iteration continues with the next @tparam N elements. +/// If the function does not return a vector, the iteration continues with an overlapping +/// sequence of @tparam N elements that starts with the second element of the previous +/// iteration. +/// During the iteration, the original vector is only valid +/// on the current element and after that. The actual replacement takes +/// place at the end, but already visited elements might be invalidated. +/// If nothing is replaced, no copy is performed. +template +void iterateReplacingWindow(std::vector& _vector, F const& _f) +{ + // Concept: _f must be Callable, must accept N parameters of type T&, must return optional> + detail::iterateReplacingWindow(_vector, _f, std::make_index_sequence{}); +} + /// @returns true iff @a _str passess the hex address checksum test. /// @param _strict if false, hex strings with only uppercase or only lowercase letters /// are considered valid. diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 44af2fc3..259f43f8 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -80,6 +80,8 @@ add_library(yul optimiser/RedundantAssignEliminator.h optimiser/Rematerialiser.cpp optimiser/Rematerialiser.h + optimiser/SSAReverser.cpp + optimiser/SSAReverser.h optimiser/SSATransform.cpp optimiser/SSATransform.h optimiser/SSAValueTracker.cpp diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp new file mode 100644 index 00000000..313a677a --- /dev/null +++ b/libyul/optimiser/SSAReverser.cpp @@ -0,0 +1,71 @@ +/* + 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 dev; +using namespace yul; + +void SSAReverser::operator()(Block& _block) +{ + walkVector(_block.statements); + iterateReplacingWindow<2>( + _block.statements, + [&](Statement& _stmt1, Statement& _stmt2) -> boost::optional> + { + // Replaces + // let a_1 := E + // a := a_1 + // with + // a := E + // let a_1 := a + + auto* varDecl = boost::get(&_stmt1); + auto* assignment = boost::get(&_stmt2); + + if (!varDecl || !assignment) + return {}; + + auto* identifier = boost::get(assignment->value.get()); + + if ( + varDecl->variables.size() == 1 && + varDecl->value && + assignment->variableNames.size() == 1 && + identifier && + identifier->name == varDecl->variables.front().name + ) + { + vector result; + result.emplace_back(Assignment{ + std::move(assignment->location), + assignment->variableNames, + std::move(varDecl->value) + }); + result.emplace_back(VariableDeclaration{ + std::move(varDecl->location), + std::move(varDecl->variables), + std::make_unique(std::move(assignment->variableNames.front())) + }); + return { std::move(result) }; + } + return {}; + } + ); +} diff --git a/libyul/optimiser/SSAReverser.h b/libyul/optimiser/SSAReverser.h new file mode 100644 index 00000000..a4a11074 --- /dev/null +++ b/libyul/optimiser/SSAReverser.h @@ -0,0 +1,54 @@ +/* + 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 yul +{ + +/** + * Reverses the SSA transformation. + * + * In particular, the SSA transform will rewrite + * + * a := E + * + * to + * + * let a_1 := E + * a := a_1 + * + * To undo this transformation, the SSAReverser changes this back to + * + * a := E + * let a_1 := a + * + * After that the CSE can replace references of a_1 by references to a, + * after which the unused pruner can remove the declaration of a_1. + * + * Prerequisites: None + * + */ +class SSAReverser: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(Block& _block) override; +}; + +} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 38c0bf49..63931554 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -88,9 +89,10 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); CommonSubexpressionEliminator{_dialect}(ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); - SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(_dialect, ast); - RedundantAssignEliminator::run(_dialect, ast); + + SSAReverser{}(ast); + CommonSubexpressionEliminator{_dialect}(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); ExpressionJoiner::run(ast); ExpressionJoiner::run(ast); @@ -127,6 +129,11 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilised(_dialect, ast); ExpressionJoiner::run(ast); UnusedPruner::runUntilStabilised(_dialect, ast); + + SSAReverser{}(ast); + CommonSubexpressionEliminator{_dialect}(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); + ExpressionJoiner::run(ast); Rematerialiser::run(_dialect, ast); UnusedPruner::runUntilStabilised(_dialect, ast); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 59cde4fe..914e0d6a 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -223,6 +224,23 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con disambiguate(); EquivalentFunctionCombiner::run(*m_ast); } + else if (m_optimizerStep == "ssaReverser") + { + disambiguate(); + SSAReverser{}(*m_ast); + } + else if (m_optimizerStep == "ssaAndBack") + { + disambiguate(); + // apply SSA + NameDispenser nameDispenser{*m_dialect, *m_ast}; + SSATransform::run(*m_ast, nameDispenser); + RedundantAssignEliminator::run(*m_dialect, *m_ast); + // reverse SSA + SSAReverser{}(*m_ast); + CommonSubexpressionEliminator{*m_dialect}(*m_ast); + UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); + } else if (m_optimizerStep == "fullSuite") OptimiserSuite::run(*m_dialect, *m_ast, *m_analysisInfo); else diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index 887399b6..41a52c67 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1073,12 +1073,12 @@ // fullSuite // { // let _2 := mload(1) -// let _172 := mload(0) -// if slt(sub(_2, _172), 64) +// let _153 := mload(0) +// if slt(sub(_2, _153), 64) // { // revert(0, 0) // } -// sstore(0, and(calldataload(_172), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// sstore(0, and(calldataload(_153), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) // let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) // sstore(x1, x0) // sstore(x3, x2) @@ -1093,40 +1093,40 @@ // value0_57 := and(calldataload(add(headStart_55, value4)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) // value1_58 := calldataload(add(headStart_55, 32)) // let offset_62 := calldataload(add(headStart_55, 64)) -// let _220 := 0xffffffffffffffff -// if gt(offset_62, _220) +// let _201 := 0xffffffffffffffff +// if gt(offset_62, _201) // { // revert(value4, value4) // } -// let _222 := add(headStart_55, offset_62) -// if iszero(slt(add(_222, 0x1f), dataEnd_56)) +// let _203 := add(headStart_55, offset_62) +// if iszero(slt(add(_203, 0x1f), dataEnd_56)) // { // revert(value4, value4) // } -// let abi_decode_length_15_116 := calldataload(_222) -// if gt(abi_decode_length_15_116, _220) +// let abi_decode_length_15_246 := calldataload(_203) +// if gt(abi_decode_length_15_246, _201) // { // revert(value4, value4) // } -// if gt(add(add(_222, abi_decode_length_15_116), 32), dataEnd_56) +// if gt(add(add(_203, abi_decode_length_15_246), 32), dataEnd_56) // { // revert(value4, value4) // } -// value2_59 := add(_222, 32) -// value3 := abi_decode_length_15_116 -// let _225 := calldataload(add(headStart_55, 96)) -// if iszero(lt(_225, 3)) +// value2_59 := add(_203, 32) +// value3 := abi_decode_length_15_246 +// let _206 := calldataload(add(headStart_55, 96)) +// if iszero(lt(_206, 3)) // { // revert(value4, value4) // } -// value4 := _225 +// value4 := _206 // } // function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart_252, value10_253, value9_254, value8_255, value7_256, value6_257, value5_258, value4_259, value3_260, value2_261, value1_262, value0_263) -> tail_264 // { // tail_264 := add(headStart_252, 352) // mstore(headStart_252, value0_263) -// let _439 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// mstore(add(headStart_252, 32), and(value1_262, _439)) +// let _413 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// mstore(add(headStart_252, 32), and(value1_262, _413)) // mstore(add(headStart_252, 64), value2_261) // mstore(add(headStart_252, 96), value3_260) // if iszero(lt(value4_259, 3)) @@ -1137,8 +1137,8 @@ // mstore(add(headStart_252, 160), value5_258) // mstore(add(headStart_252, 192), value6_257) // mstore(add(headStart_252, 224), value7_256) -// mstore(add(headStart_252, 256), and(value8_255, _439)) -// mstore(add(headStart_252, 288), and(value9_254, _439)) +// mstore(add(headStart_252, 256), and(value8_255, _413)) +// mstore(add(headStart_252, 288), and(value9_254, _413)) // mstore(add(headStart_252, 320), value10_253) // } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index a8cac6c6..05a35673 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -460,12 +460,12 @@ // { // let _1 := 0x20 // let _2 := 0 -// let _268 := mload(_2) +// let _218 := mload(_2) // let abi_encode_pos := _1 -// let abi_encode_length_68 := mload(_268) +// let abi_encode_length_68 := mload(_218) // mstore(_1, abi_encode_length_68) // abi_encode_pos := 64 -// let abi_encode_srcPtr := add(_268, _1) +// let abi_encode_srcPtr := add(_218, _1) // let abi_encode_i_69 := _2 // for { // } @@ -474,66 +474,65 @@ // abi_encode_i_69 := add(abi_encode_i_69, 1) // } // { -// let _668 := mload(abi_encode_srcPtr) -// let abi_encode_pos_71_760 := abi_encode_pos -// let abi_encode_srcPtr_73_762 := _668 -// let abi_encode_i_74_763 := _2 +// let _580 := mload(abi_encode_srcPtr) +// let abi_encode_pos_71_672 := abi_encode_pos +// let abi_encode_srcPtr_73_674 := _580 +// let abi_encode_i_74_675 := _2 // for { // } -// lt(abi_encode_i_74_763, 0x3) +// lt(abi_encode_i_74_675, 0x3) // { -// abi_encode_i_74_763 := add(abi_encode_i_74_763, 1) +// abi_encode_i_74_675 := add(abi_encode_i_74_675, 1) // } // { -// mstore(abi_encode_pos_71_760, and(mload(abi_encode_srcPtr_73_762), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) -// abi_encode_srcPtr_73_762 := add(abi_encode_srcPtr_73_762, _1) -// abi_encode_pos_71_760 := add(abi_encode_pos_71_760, _1) +// mstore(abi_encode_pos_71_672, and(mload(abi_encode_srcPtr_73_674), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// abi_encode_srcPtr_73_674 := add(abi_encode_srcPtr_73_674, _1) +// abi_encode_pos_71_672 := add(abi_encode_pos_71_672, _1) // } // abi_encode_srcPtr := add(abi_encode_srcPtr, _1) // abi_encode_pos := add(abi_encode_pos, 0x60) // } -// let _270 := mload(64) -// let _271 := mload(_1) -// if slt(sub(_270, _271), 128) +// let _220 := mload(64) +// let _221 := mload(_1) +// if slt(sub(_220, _221), 128) // { // revert(_2, _2) // } -// let abi_decode_offset_64 := calldataload(add(_271, 64)) +// let abi_decode_offset_64 := calldataload(add(_221, 64)) // let abi_decode__74 := 0xffffffffffffffff // if gt(abi_decode_offset_64, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value2_367 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_271, abi_decode_offset_64), _270) -// let abi_decode_offset_65 := calldataload(add(_271, 96)) +// let abi_decode_value2_317 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_221, abi_decode_offset_64), _220) +// let abi_decode_offset_65 := calldataload(add(_221, 96)) // if gt(abi_decode_offset_65, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value3_368 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_271, abi_decode_offset_65), _270) -// sstore(calldataload(_271), calldataload(add(_271, _1))) -// sstore(abi_decode_value2_367, abi_decode_value3_368) +// let abi_decode_value3_318 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_221, abi_decode_offset_65), _220) +// sstore(calldataload(_221), calldataload(add(_221, _1))) +// sstore(abi_decode_value2_317, abi_decode_value3_318) // sstore(_2, abi_encode_pos) // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5 // { // if iszero(slt(add(offset_3, 0x1f), end_4)) // { -// revert(0, 0) +// revert(array_5, array_5) // } // let length_6 := calldataload(offset_3) -// let array_5_115 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_6)) -// array_5 := array_5_115 -// let dst_7 := array_5_115 -// mstore(array_5_115, length_6) +// array_5 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_6)) +// let dst_7 := array_5 +// mstore(array_5, length_6) // let _16 := 0x20 -// dst_7 := add(array_5_115, _16) +// dst_7 := add(array_5, _16) // let src_8 := add(offset_3, _16) // if gt(add(add(offset_3, mul(length_6, 0x40)), _16), end_4) // { // revert(0, 0) // } -// let i_9_346 := 0 -// let i_9 := i_9_346 +// let i_9_296 := 0 +// let i_9 := i_9_296 // for { // } // lt(i_9, length_6) @@ -543,17 +542,17 @@ // { // if iszero(slt(add(src_8, 0x1f), end_4)) // { -// revert(i_9_346, i_9_346) +// revert(i_9_296, i_9_296) // } -// let abi_decode_array_13_124 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) -// let abi_decode_dst_15 := abi_decode_array_13_124 +// let abi_decode_array_13_300 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) +// let abi_decode_dst_15 := abi_decode_array_13_300 // let abi_decode_src_16 := src_8 -// let abi_decode__289 := add(src_8, 0x40) -// if gt(abi_decode__289, end_4) +// let abi_decode__239 := add(src_8, 0x40) +// if gt(abi_decode__239, end_4) // { -// revert(i_9_346, i_9_346) +// revert(i_9_296, i_9_296) // } -// let abi_decode_i_17 := i_9_346 +// let abi_decode_i_17 := i_9_296 // for { // } // lt(abi_decode_i_17, 0x2) @@ -565,24 +564,23 @@ // abi_decode_dst_15 := add(abi_decode_dst_15, _16) // abi_decode_src_16 := add(abi_decode_src_16, _16) // } -// mstore(dst_7, abi_decode_array_13_124) +// mstore(dst_7, abi_decode_array_13_300) // dst_7 := add(dst_7, _16) -// src_8 := abi_decode__289 +// src_8 := abi_decode__239 // } // } // function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29 // { // if iszero(slt(add(offset_27, 0x1f), end_28)) // { -// revert(0, 0) +// revert(array_29, array_29) // } // let length_30 := calldataload(offset_27) -// let array_29_131 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_30)) -// array_29 := array_29_131 -// let dst_31 := array_29_131 -// mstore(array_29_131, length_30) +// array_29 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_30)) +// let dst_31 := array_29 +// mstore(array_29, length_30) // let _52 := 0x20 -// dst_31 := add(array_29_131, _52) +// dst_31 := add(array_29, _52) // let src_32 := add(offset_27, _52) // if gt(add(add(offset_27, mul(length_30, _52)), _52), end_28) // { @@ -603,10 +601,9 @@ // } // function allocateMemory(size) -> memPtr // { -// let memPtr_157 := mload(64) -// memPtr := memPtr_157 -// let newFreePtr := add(memPtr_157, size) -// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_157)) +// memPtr := mload(64) +// let newFreePtr := add(memPtr, size) +// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) // { // revert(0, 0) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index fc09b5d9..e05a3c92 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -264,10 +264,10 @@ // } // { // let validateJo__34 := 0x20 -// let validateJo__373 := add(validateJo__10, mul(validateJo_i, 0xc0)) -// let validateJo_noteIndex := add(validateJo__373, 0x24) +// let validateJo__351 := add(validateJo__10, mul(validateJo_i, 0xc0)) +// let validateJo_noteIndex := add(validateJo__351, 0x24) // let validateJo_k := validateJo_i_290 -// let validateJo_a_292 := calldataload(add(validateJo__373, 0x44)) +// let validateJo_a_292 := calldataload(add(validateJo__351, 0x44)) // let validateJo_a := validateJo_a_292 // let validateJo_c := validateJo_challenge // let validateJo__39 := add(validateJo_i, 0x01) @@ -296,8 +296,8 @@ // validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) // } // let validateJo__52 := 0x40 -// calldatacopy(0xe0, add(validateJo__373, 164), validateJo__52) -// calldatacopy(validateJo__34, add(validateJo__373, 100), validateJo__52) +// calldatacopy(0xe0, add(validateJo__351, 164), validateJo__52) +// calldatacopy(validateJo__34, add(validateJo__351, 100), validateJo__52) // let validateJo__61 := 0x120 // mstore(validateJo__61, sub(validateJo_gen_order, validateJo_c)) // let validateJo__62 := 0x60 @@ -310,8 +310,7 @@ // let validateJo__80 := 0x160 // let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_290, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) // let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_290, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) -// let validateJo_result_306 := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) -// validateJo_result := validateJo_result_306 +// validateJo_result := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) // if eq(validateJo_i, validateJo_m) // { // mstore(0x260, mload(validateJo__34)) @@ -323,7 +322,7 @@ // { // mstore(validateJo__62, validateJo_c) // let validateJo__120 := 0x220 -// let validateJo_result_307 := and(validateJo_result_306, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) +// let validateJo_result_307 := and(validateJo_result, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) // let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_290, validateJo__120, validateJo__6, 0x260, validateJo__52)) // validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_290, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul index b10c6c69..1d07cd03 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -21,8 +21,8 @@ // { // let allocate__19 := 0x40 // mstore(allocate__19, add(mload(allocate__19), 0x20)) -// let allocate_p_24_41 := mload(allocate__19) -// mstore(allocate__19, add(allocate_p_24_41, allocate__19)) -// mstore(add(allocate_p_24_41, 96), 2) +// let allocate_p_35_39 := mload(allocate__19) +// mstore(allocate__19, add(allocate_p_35_39, allocate__19)) +// mstore(add(allocate_p_35_39, 96), 2) // mstore(allocate__19, 0x20) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul new file mode 100644 index 00000000..204b444e --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul @@ -0,0 +1,62 @@ +{ + // This is an abi decode function with the SSA transform applied once. + // This test is supposed to verify that the SSA transform is correctly reversed by the full suite. + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + let length_15_1 := calldataload(offset_12) + length_15 := length_15_1 + if gt(length_15_1, 0xffffffffffffffff) + { + revert(0, 0) + } + let arrayPos_14_2 := add(offset_12, 0x20) + arrayPos_14 := arrayPos_14_2 + if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) + { + revert(0, 0) + } + } + + // prevent inlining + let a,b := abi_decode_t_bytes_calldata_ptr(mload(0),mload(1)) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + mstore(a,b) +} +// ---- +// fullSuite +// { +// let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// mstore(a, b) +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(0, 0) +// } +// length_15 := calldataload(offset_12) +// if gt(length_15, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// arrayPos_14 := add(offset_12, 0x20) +// if gt(add(add(offset_12, length_15), 0x20), end_13) +// { +// revert(0, 0) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul new file mode 100644 index 00000000..23c433d1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul @@ -0,0 +1,18 @@ +{ + let a := mload(0) + a := mload(1) + a := mload(2) + a := mload(3) + a := mload(4) + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// pop(mload(0)) +// pop(mload(1)) +// pop(mload(2)) +// pop(mload(3)) +// let a_5 := mload(4) +// mstore(a_5, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul new file mode 100644 index 00000000..d2ba6471 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul @@ -0,0 +1,45 @@ +{ + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + length_15 := calldataload(offset_12) + if gt(length_15, 0xffffffffffffffff) + { + revert(0, 0) + } + arrayPos_14 := add(offset_12, 0x20) + if gt(add(add(offset_12, length_15), 0x20), end_13) + { + revert(0, 0) + } + } + // prevent removal of the function + let a,b := abi_decode_t_bytes_calldata_ptr(mload(0),mload(1)) + mstore(a,b) +} +// ---- +// ssaAndBack +// { +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(arrayPos_14, arrayPos_14) +// } +// length_15 := calldataload(offset_12) +// if gt(length_15, 0xffffffffffffffff) +// { +// revert(arrayPos_14, arrayPos_14) +// } +// arrayPos_14 := add(offset_12, 0x20) +// if gt(add(add(offset_12, length_15), 0x20), end_13) +// { +// revert(0, 0) +// } +// } +// let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul new file mode 100644 index 00000000..923a42ba --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul @@ -0,0 +1,44 @@ +{ + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + let length_15_1 := calldataload(offset_12) + length_15 := length_15_1 + if gt(length_15_1, 0xffffffffffffffff) + { + revert(0, 0) + } + let arrayPos_14_2 := add(offset_12, 0x20) + arrayPos_14 := arrayPos_14_2 + if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) + { + revert(0, 0) + } + } +} +// ---- +// ssaReverser +// { +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(0, 0) +// } +// length_15 := calldataload(offset_12) +// let length_15_1 := length_15 +// if gt(length_15_1, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// arrayPos_14 := add(offset_12, 0x20) +// let arrayPos_14_2 := arrayPos_14 +// if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) +// { +// revert(0, 0) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaReverser/simple.yul b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul new file mode 100644 index 00000000..eba1f5f1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul @@ -0,0 +1,14 @@ +{ + let a := mload(1) + let a_1 := mload(0) + a := a_1 + mstore(a_1, 0) +} +// ---- +// ssaReverser +// { +// let a := mload(1) +// a := mload(0) +// let a_1 := a +// mstore(a_1, 0) +// } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index efd1ba05..07ebfc7d 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +130,7 @@ public: cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter/" << endl; - cout << " s(t)ructural simplifier/equi(v)alent function combiner? " << endl; + cout << " s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser? " << endl; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -188,6 +189,9 @@ public: case 'v': EquivalentFunctionCombiner::run(*m_ast); break; + case 'V': + SSAReverser{}(*m_ast); + break; default: cout << "Unknown option." << endl; } -- cgit From fd1658572463a246f602ae0fe161430429daa9e0 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 17 Jan 2019 14:57:55 +0100 Subject: Undo second SSA transformation and add more tests. --- libyul/optimiser/SSAReverser.cpp | 88 +++++++++++++++------- libyul/optimiser/SSAReverser.h | 15 +++- .../yulOptimizerTests/fullSuite/abi_example1.yul | 15 ++-- test/libyul/yulOptimizerTests/fullSuite/aztec.yul | 57 +++++++------- .../fullSuite/ssaReverseComplex.yul | 23 ++++++ .../yulOptimizerTests/ssaAndBack/for_loop.yul | 36 +++++++++ .../yulOptimizerTests/ssaAndBack/multi_assign.yul | 18 +++++ .../ssaAndBack/multi_assign_if.yul | 22 ++++++ .../ssaAndBack/multi_assign_multi_var_if.yul | 25 ++++++ .../ssaAndBack/multi_assign_multi_var_switch.yul | 38 ++++++++++ .../ssaAndBack/multi_assign_switch.yul | 32 ++++++++ .../libyul/yulOptimizerTests/ssaAndBack/simple.yul | 10 +-- .../ssaAndBack/single_assign_if.yul | 18 +++++ .../ssaAndBack/single_assign_switch.yul | 24 ++++++ .../yulOptimizerTests/ssaAndBack/two_vars.yul | 20 +++++ 15 files changed, 366 insertions(+), 75 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul create mode 100644 test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp index 313a677a..2cfa3d58 100644 --- a/libyul/optimiser/SSAReverser.cpp +++ b/libyul/optimiser/SSAReverser.cpp @@ -29,42 +29,74 @@ void SSAReverser::operator()(Block& _block) _block.statements, [&](Statement& _stmt1, Statement& _stmt2) -> boost::optional> { + auto* varDecl = boost::get(&_stmt1); + + if (!varDecl || varDecl->variables.size() != 1 || !varDecl->value) + return {}; + // Replaces // let a_1 := E // a := a_1 // with // a := E // let a_1 := a - - auto* varDecl = boost::get(&_stmt1); - auto* assignment = boost::get(&_stmt2); - - if (!varDecl || !assignment) - return {}; - - auto* identifier = boost::get(assignment->value.get()); - - if ( - varDecl->variables.size() == 1 && - varDecl->value && - assignment->variableNames.size() == 1 && - identifier && - identifier->name == varDecl->variables.front().name - ) + if (auto* assignment = boost::get(&_stmt2)) + { + auto* identifier = boost::get(assignment->value.get()); + if ( + assignment->variableNames.size() == 1 && + identifier && + identifier->name == varDecl->variables.front().name + ) + { + vector result; + result.emplace_back(Assignment{ + std::move(assignment->location), + assignment->variableNames, + std::move(varDecl->value) + }); + result.emplace_back(VariableDeclaration{ + std::move(varDecl->location), + std::move(varDecl->variables), + std::make_unique(std::move(assignment->variableNames.front())) + }); + return { std::move(result) }; + } + } + // Replaces + // let a_1 := E + // let a := a_1 + // with + // let a := E + // let a_1 := a + else if (auto* varDecl2 = boost::get(&_stmt2)) { - vector result; - result.emplace_back(Assignment{ - std::move(assignment->location), - assignment->variableNames, - std::move(varDecl->value) - }); - result.emplace_back(VariableDeclaration{ - std::move(varDecl->location), - std::move(varDecl->variables), - std::make_unique(std::move(assignment->variableNames.front())) - }); - return { std::move(result) }; + auto* identifier = boost::get(varDecl2->value.get()); + if ( + varDecl2->variables.size() == 1 && + identifier && + identifier->name == varDecl->variables.front().name + ) + { + vector result; + auto varIdentifier2 = std::make_unique(Identifier{ + varDecl2->variables.front().location, + varDecl2->variables.front().name + }); + result.emplace_back(VariableDeclaration{ + std::move(varDecl2->location), + std::move(varDecl2->variables), + std::move(varDecl->value) + }); + result.emplace_back(VariableDeclaration{ + std::move(varDecl->location), + std::move(varDecl->variables), + std::move(varIdentifier2) + }); + return { std::move(result) }; + } } + return {}; } ); diff --git a/libyul/optimiser/SSAReverser.h b/libyul/optimiser/SSAReverser.h index a4a11074..34b61647 100644 --- a/libyul/optimiser/SSAReverser.h +++ b/libyul/optimiser/SSAReverser.h @@ -33,11 +33,24 @@ namespace yul * let a_1 := E * a := a_1 * - * To undo this transformation, the SSAReverser changes this back to + * To undo this kind of transformation, the SSAReverser changes this back to * * a := E * let a_1 := a * + * Secondly, the SSA transform will rewrite + * + * let a := E + * to + * + * let a_1 := E + * let a := a_1 + * + * To undo this kind of transformation, the SSAReverser changes this back to + * + * let a := E + * let a_1 := a + * * After that the CSE can replace references of a_1 by references to a, * after which the unused pruner can remove the declaration of a_1. * diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index 05a35673..d38025ae 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -531,8 +531,7 @@ // { // revert(0, 0) // } -// let i_9_296 := 0 -// let i_9 := i_9_296 +// let i_9 := 0 // for { // } // lt(i_9, length_6) @@ -542,17 +541,17 @@ // { // if iszero(slt(add(src_8, 0x1f), end_4)) // { -// revert(i_9_296, i_9_296) +// revert(0, 0) // } -// let abi_decode_array_13_300 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) -// let abi_decode_dst_15 := abi_decode_array_13_300 +// let abi_decode_dst_15 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) +// let abi_decode_dst_15_1155 := abi_decode_dst_15 // let abi_decode_src_16 := src_8 // let abi_decode__239 := add(src_8, 0x40) // if gt(abi_decode__239, end_4) // { -// revert(i_9_296, i_9_296) +// revert(0, 0) // } -// let abi_decode_i_17 := i_9_296 +// let abi_decode_i_17 := 0 // for { // } // lt(abi_decode_i_17, 0x2) @@ -564,7 +563,7 @@ // abi_decode_dst_15 := add(abi_decode_dst_15, _16) // abi_decode_src_16 := add(abi_decode_src_16, _16) // } -// mstore(dst_7, abi_decode_array_13_300) +// mstore(dst_7, abi_decode_dst_15_1155) // dst_7 := add(dst_7, _16) // src_8 := abi_decode__239 // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index e05a3c92..868c6b01 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -245,17 +245,16 @@ // mstore(0x00, 404) // revert(0x00, 0x20) // } -// let validateJo_kn_287 := calldataload(add(calldatasize(), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40)) -// let validateJo_kn := validateJo_kn_287 +// let validateJo_kn := calldataload(add(calldatasize(), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40)) // let validateJo__24 := 0x2a0 // mstore(validateJo__24, caller()) -// mstore(0x2c0, validateJo_kn_287) +// mstore(0x2c0, validateJo_kn) // mstore(0x2e0, validateJo_m) -// validateJo_kn := mulmod(sub(validateJo_gen_order, validateJo_kn_287), validateJo_challenge, validateJo_gen_order) +// validateJo_kn := mulmod(sub(validateJo_gen_order, validateJo_kn), validateJo_challenge, validateJo_gen_order) // hashCommitments(validateJo_notes, validateJo_n) // let validateJo_b := add(0x300, mul(validateJo_n, validateJo__6)) -// let validateJo_i_290 := 0 -// let validateJo_i := validateJo_i_290 +// let validateJo_i := 0 +// let validateJo_i_1306 := validateJo_i // for { // } // lt(validateJo_i, validateJo_n) @@ -266,9 +265,8 @@ // let validateJo__34 := 0x20 // let validateJo__351 := add(validateJo__10, mul(validateJo_i, 0xc0)) // let validateJo_noteIndex := add(validateJo__351, 0x24) -// let validateJo_k := validateJo_i_290 -// let validateJo_a_292 := calldataload(add(validateJo__351, 0x44)) -// let validateJo_a := validateJo_a_292 +// let validateJo_k := validateJo_i_1306 +// let validateJo_a := calldataload(add(validateJo__351, 0x44)) // let validateJo_c := validateJo_challenge // let validateJo__39 := add(validateJo_i, 0x01) // switch eq(validateJo__39, validateJo_n) @@ -282,15 +280,15 @@ // case 0 { // validateJo_k := calldataload(validateJo_noteIndex) // } -// validateCommitment(validateJo_noteIndex, validateJo_k, validateJo_a_292) +// validateCommitment(validateJo_noteIndex, validateJo_k, validateJo_a) // switch gt(validateJo__39, validateJo_m) // case 1 { // validateJo_kn := addmod(validateJo_kn, sub(validateJo_gen_order, validateJo_k), validateJo_gen_order) -// let validateJo_x := mod(mload(validateJo_i_290), validateJo_gen_order) +// let validateJo_x := mod(mload(validateJo_i_1306), validateJo_gen_order) // validateJo_k := mulmod(validateJo_k, validateJo_x, validateJo_gen_order) -// validateJo_a := mulmod(validateJo_a_292, validateJo_x, validateJo_gen_order) +// validateJo_a := mulmod(validateJo_a, validateJo_x, validateJo_gen_order) // validateJo_c := mulmod(validateJo_challenge, validateJo_x, validateJo_gen_order) -// mstore(validateJo_i_290, keccak256(validateJo_i_290, validateJo__34)) +// mstore(validateJo_i_1306, keccak256(validateJo_i_1306, validateJo__34)) // } // case 0 { // validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) @@ -304,13 +302,12 @@ // mstore(validateJo__62, validateJo_k) // mstore(0xc0, validateJo_a) // let validateJo__65 := 0x1a0 -// let validateJo_result_302 := call(gas(), 7, validateJo_i_290, 0xe0, validateJo__62, validateJo__65, validateJo__52) -// let validateJo_result := validateJo_result_302 -// let validateJo_result_303 := and(validateJo_result_302, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__61, validateJo__52)) +// let validateJo_result := call(gas(), 7, validateJo_i_1306, 0xe0, validateJo__62, validateJo__65, validateJo__52) +// let validateJo_result_303 := and(validateJo_result, call(gas(), 7, validateJo_i_1306, validateJo__34, validateJo__62, validateJo__61, validateJo__52)) // let validateJo__80 := 0x160 -// let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_290, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) -// let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_290, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) -// validateJo_result := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) +// let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_1306, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) +// let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_1306, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) +// validateJo_result := and(validateJo_result_305, call(gas(), 6, validateJo_i_1306, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) // if eq(validateJo_i, validateJo_m) // { // mstore(0x260, mload(validateJo__34)) @@ -322,14 +319,14 @@ // { // mstore(validateJo__62, validateJo_c) // let validateJo__120 := 0x220 -// let validateJo_result_307 := and(validateJo_result, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) -// let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_290, validateJo__120, validateJo__6, 0x260, validateJo__52)) -// validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_290, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) +// let validateJo_result_307 := and(validateJo_result, call(gas(), 7, validateJo_i_1306, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) +// let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_1306, validateJo__120, validateJo__6, 0x260, validateJo__52)) +// validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_1306, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) // } // if iszero(validateJo_result) // { -// mstore(validateJo_i_290, 400) -// revert(validateJo_i_290, validateJo__34) +// mstore(validateJo_i_1306, 400) +// revert(validateJo_i_1306, validateJo__34) // } // validateJo_b := add(validateJo_b, validateJo__52) // } @@ -339,13 +336,13 @@ // } // if iszero(eq(mod(keccak256(validateJo__24, add(validateJo_b, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60)), validateJo_gen_order), validateJo_challenge)) // { -// mstore(validateJo_i_290, 404) -// revert(validateJo_i_290, 0x20) +// mstore(validateJo_i_1306, 404) +// revert(validateJo_i_1306, 0x20) // } -// mstore(validateJo_i_290, 0x01) -// return(validateJo_i_290, 0x20) -// mstore(validateJo_i_290, 404) -// revert(validateJo_i_290, 0x20) +// mstore(validateJo_i_1306, 0x01) +// return(validateJo_i_1306, 0x20) +// mstore(validateJo_i_1306, 404) +// revert(validateJo_i_1306, 0x20) // function validatePairing(t2) // { // let t2_x_1 := calldataload(t2) diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul new file mode 100644 index 00000000..2e178f31 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul @@ -0,0 +1,23 @@ +{ + let a := mload(0) + let b := mload(1) + if mload(2) { + a := mload(b) + b := mload(a) + a := mload(b) + b := mload(a) + } + mstore(a, b) +} +// ---- +// fullSuite +// { +// let a := mload(0) +// let b := mload(1) +// if mload(2) +// { +// a := mload(mload(mload(b))) +// b := mload(a) +// } +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul b/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul new file mode 100644 index 00000000..18498e61 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul @@ -0,0 +1,36 @@ +{ + for { + let a := mload(0) + let b := mload(1) + } + lt(mload(a),mload(b)) + { + a := mload(b) + } + { + b := mload(a) + a := mload(b) + a := mload(b) + a := mload(b) + b := mload(a) + } +} +// ---- +// ssaAndBack +// { +// for { +// let a := mload(0) +// let b := mload(1) +// } +// lt(mload(a), mload(b)) +// { +// a := mload(b) +// } +// { +// let b_3 := mload(a) +// pop(mload(b_3)) +// pop(mload(b_3)) +// let a_6 := mload(b_3) +// b := mload(a_6) +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul new file mode 100644 index 00000000..23c433d1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul @@ -0,0 +1,18 @@ +{ + let a := mload(0) + a := mload(1) + a := mload(2) + a := mload(3) + a := mload(4) + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// pop(mload(0)) +// pop(mload(1)) +// pop(mload(2)) +// pop(mload(3)) +// let a_5 := mload(4) +// mstore(a_5, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul new file mode 100644 index 00000000..fd5981ef --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul @@ -0,0 +1,22 @@ +{ + let a := mload(0) + if mload(1) + { + a := mload(1) + a := mload(2) + a := mload(3) + } + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// if mload(1) +// { +// pop(mload(1)) +// pop(mload(2)) +// a := mload(3) +// } +// mstore(a, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul new file mode 100644 index 00000000..b0b3efb5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul @@ -0,0 +1,25 @@ +{ + let a := mload(0) + let b := mload(1) + if mload(2) { + a := mload(b) + b := mload(a) + a := mload(b) + b := mload(a) + } + mstore(a, b) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// let b := mload(1) +// if mload(2) +// { +// let a_3 := mload(b) +// let b_4 := mload(a_3) +// a := mload(b_4) +// b := mload(a) +// } +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul new file mode 100644 index 00000000..50f56b87 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul @@ -0,0 +1,38 @@ +{ + let a := mload(0) + let b := mload(1) + switch mload(2) + case 0 { + a := mload(b) + b := mload(a) + a := mload(b) + b := mload(a) + } + default { + b := mload(a) + a := mload(b) + b := mload(a) + a := mload(b) + } + mstore(a, b) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// let b := mload(1) +// switch mload(2) +// case 0 { +// let a_3 := mload(b) +// let b_4 := mload(a_3) +// a := mload(b_4) +// b := mload(a) +// } +// default { +// let b_7 := mload(a) +// let a_8 := mload(b_7) +// b := mload(a_8) +// a := mload(b) +// } +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul new file mode 100644 index 00000000..1efbbde7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul @@ -0,0 +1,32 @@ +{ + let a := mload(0) + switch mload(1) + case 0 { + a := mload(1) + a := mload(2) + a := mload(3) + } + default { + a := mload(4) + a := mload(5) + a := mload(6) + } + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// switch mload(1) +// case 0 { +// pop(mload(1)) +// pop(mload(2)) +// a := mload(3) +// } +// default { +// pop(mload(4)) +// pop(mload(5)) +// a := mload(6) +// } +// mstore(a, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul index 23c433d1..912940c5 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul @@ -1,18 +1,12 @@ { let a := mload(0) a := mload(1) - a := mload(2) - a := mload(3) - a := mload(4) mstore(a, 0) } // ---- // ssaAndBack // { // pop(mload(0)) -// pop(mload(1)) -// pop(mload(2)) -// pop(mload(3)) -// let a_5 := mload(4) -// mstore(a_5, 0) +// let a_2 := mload(1) +// mstore(a_2, 0) // } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul new file mode 100644 index 00000000..5185245c --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul @@ -0,0 +1,18 @@ +{ + let a := mload(0) + if mload(1) + { + a := mload(1) + } + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// if mload(1) +// { +// a := mload(1) +// } +// mstore(a, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul new file mode 100644 index 00000000..e0e53b3f --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul @@ -0,0 +1,24 @@ +{ + let a := mload(0) + switch mload(1) + case 0 { + a := mload(1) + } + default { + a := mload(2) + } + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// switch mload(1) +// case 0 { +// a := mload(1) +// } +// default { +// a := mload(2) +// } +// mstore(a, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul new file mode 100644 index 00000000..85325fb9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul @@ -0,0 +1,20 @@ +{ + let a := mload(0) + let b := mload(a) + a := mload(b) + b := mload(a) + a := mload(b) + b := mload(a) + mstore(a, b) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// let b := mload(a) +// let a_3 := mload(b) +// let b_4 := mload(a_3) +// let a_5 := mload(b_4) +// let b_6 := mload(a_5) +// mstore(a_5, b_6) +// } -- cgit From 29f66b267426535c7ffe8eda09c3b50888dcdfc0 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 17 Jan 2019 16:11:55 +0100 Subject: Stabilize SSAReverser. --- libyul/optimiser/Metrics.cpp | 12 ++++++++++++ libyul/optimiser/Metrics.h | 14 ++++++++++++++ libyul/optimiser/SSAReverser.cpp | 15 +++++++++++++-- libyul/optimiser/SSAReverser.h | 9 ++++++++- libyul/optimiser/Suite.cpp | 4 ++-- test/libyul/YulOptimizerTest.cpp | 4 ++-- test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul | 6 +++--- test/tools/yulopti.cpp | 2 +- 8 files changed, 55 insertions(+), 11 deletions(-) diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index df919682..e13940aa 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -134,3 +134,15 @@ void CodeCost::visit(Expression const& _expression) ++m_cost; ASTWalker::visit(_expression); } + +void AssignmentCounter::operator()(Assignment const& _assignment) +{ + for (auto const& variable: _assignment.variableNames) + ++m_assignmentCounters[variable.name]; +} + +size_t AssignmentCounter::assignmentCount(YulString _name) const +{ + auto it = m_assignmentCounters.find(_name); + return (it == m_assignmentCounters.end()) ? 0 : it->second; +} diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h index 03e1b62a..5364646e 100644 --- a/libyul/optimiser/Metrics.h +++ b/libyul/optimiser/Metrics.h @@ -77,4 +77,18 @@ private: size_t m_cost = 0; }; +/** + * Counts the number of assignments to every variable. + * Only works after running the Disambiguator. + */ +class AssignmentCounter: public ASTWalker +{ +public: + using ASTWalker::operator(); + void operator()(Assignment const& _assignment) override; + std::size_t assignmentCount(YulString _name) const; +private: + std::map m_assignmentCounters; +}; + } diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp index 2cfa3d58..6548ebb5 100644 --- a/libyul/optimiser/SSAReverser.cpp +++ b/libyul/optimiser/SSAReverser.cpp @@ -15,6 +15,7 @@ along with solidity. If not, see . */ #include +#include #include #include @@ -22,6 +23,13 @@ using namespace std; using namespace dev; using namespace yul; +void SSAReverser::run(Block& _block) +{ + AssignmentCounter assignmentCounter; + assignmentCounter(_block); + SSAReverser{assignmentCounter}(_block); +} + void SSAReverser::operator()(Block& _block) { walkVector(_block.statements); @@ -47,7 +55,7 @@ void SSAReverser::operator()(Block& _block) assignment->variableNames.size() == 1 && identifier && identifier->name == varDecl->variables.front().name - ) + ) { vector result; result.emplace_back(Assignment{ @@ -75,7 +83,10 @@ void SSAReverser::operator()(Block& _block) if ( varDecl2->variables.size() == 1 && identifier && - identifier->name == varDecl->variables.front().name + identifier->name == varDecl->variables.front().name && ( + m_assignmentCounter.assignmentCount(varDecl2->variables.front().name) > + m_assignmentCounter.assignmentCount(varDecl->variables.front().name) + ) ) { vector result; diff --git a/libyul/optimiser/SSAReverser.h b/libyul/optimiser/SSAReverser.h index 34b61647..67abeb56 100644 --- a/libyul/optimiser/SSAReverser.h +++ b/libyul/optimiser/SSAReverser.h @@ -21,6 +21,8 @@ namespace yul { +class AssignmentCounter; + /** * Reverses the SSA transformation. * @@ -54,7 +56,7 @@ namespace yul * After that the CSE can replace references of a_1 by references to a, * after which the unused pruner can remove the declaration of a_1. * - * Prerequisites: None + * Prerequisites: Disambiguator * */ class SSAReverser: public ASTModifier @@ -62,6 +64,11 @@ class SSAReverser: public ASTModifier public: using ASTModifier::operator(); void operator()(Block& _block) override; + + static void run(Block& _block); +private: + SSAReverser(AssignmentCounter const& _assignmentCounter): m_assignmentCounter(_assignmentCounter) {} + AssignmentCounter const& m_assignmentCounter; }; } diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 63931554..8cf6e104 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -90,7 +90,7 @@ void OptimiserSuite::run( CommonSubexpressionEliminator{_dialect}(ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); - SSAReverser{}(ast); + SSAReverser::run(ast); CommonSubexpressionEliminator{_dialect}(ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); @@ -130,7 +130,7 @@ void OptimiserSuite::run( ExpressionJoiner::run(ast); UnusedPruner::runUntilStabilised(_dialect, ast); - SSAReverser{}(ast); + SSAReverser::run(ast); CommonSubexpressionEliminator{_dialect}(ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 914e0d6a..306721a0 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -227,7 +227,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con else if (m_optimizerStep == "ssaReverser") { disambiguate(); - SSAReverser{}(*m_ast); + SSAReverser::run(*m_ast); } else if (m_optimizerStep == "ssaAndBack") { @@ -237,7 +237,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con SSATransform::run(*m_ast, nameDispenser); RedundantAssignEliminator::run(*m_dialect, *m_ast); // reverse SSA - SSAReverser{}(*m_ast); + SSAReverser::run(*m_ast); CommonSubexpressionEliminator{*m_dialect}(*m_ast); UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul index 85325fb9..9f2a046e 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul @@ -10,9 +10,9 @@ // ---- // ssaAndBack // { -// let a := mload(0) -// let b := mload(a) -// let a_3 := mload(b) +// let a_1 := mload(0) +// let b_2 := mload(a_1) +// let a_3 := mload(b_2) // let b_4 := mload(a_3) // let a_5 := mload(b_4) // let b_6 := mload(a_5) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 07ebfc7d..7203ef54 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -190,7 +190,7 @@ public: EquivalentFunctionCombiner::run(*m_ast); break; case 'V': - SSAReverser{}(*m_ast); + SSAReverser::run(*m_ast); break; default: cout << "Unknown option." << endl; -- cgit From 369a368a3f06c9ef82e434f083f2b94cc2844e22 Mon Sep 17 00:00:00 2001 From: Tomek Kopczynski Date: Thu, 17 Jan 2019 21:59:31 +0100 Subject: Remove unnecessary word --- docs/common-patterns.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 3704b730..65bf7f44 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -117,7 +117,7 @@ to read the data, so will everyone else. You can restrict read access to your contract's state by **other contracts**. That is actually the default -unless you declare make your state variables ``public``. +unless you declare your state variables ``public``. Furthermore, you can restrict who can make modifications to your contract's state or call your contract's -- cgit From bda0bc8f08883711f48c87d933f2f7d898d3aff0 Mon Sep 17 00:00:00 2001 From: Evan Saulpaugh Date: Fri, 18 Jan 2019 02:30:15 -0600 Subject: improve packed encoding test vector packed byte length of ints now unambiguously shown to be type-dependent, not value dependent e.g. uint16(0x03) is 0x0003 not 0x03 --- docs/abi-spec.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 68ca8ec4..26293a1f 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -611,15 +611,15 @@ Through ``abi.encodePacked()``, Solidity supports a non-standard packed mode whe This packed mode is mainly used for indexed event parameters. -As an example, the encoding of ``int8(-1), bytes1(0x42), uint16(0x2424), string("Hello, world!")`` results in: +As an example, the encoding of ``int16(-1), bytes1(0x42), uint16(0x03), string("Hello, world!")`` results in: .. code-block:: none - 0xff42242448656c6c6f2c20776f726c6421 - ^^ int8(-1) - ^^ bytes1(0x42) - ^^^^ uint16(0x2424) - ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field + 0xffff42000348656c6c6f2c20776f726c6421 + ^^^^ int16(-1) + ^^ bytes1(0x42) + ^^^^ uint16(0x03) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field More specifically: - Each value type takes as many bytes as its range has. -- cgit From a766efc79fbcdf0aa2fc9210b20caf36f4258714 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 17 Jan 2019 12:59:11 +0100 Subject: Add ContractDefinition::isInterface. --- libsolidity/analysis/SyntaxChecker.cpp | 2 +- libsolidity/analysis/TypeChecker.cpp | 11 +++++------ libsolidity/ast/AST.h | 1 + libsolidity/ast/Types.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 066b5004..7b8aa0f2 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -262,7 +262,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) bool SyntaxChecker::visit(ContractDefinition const& _contract) { - m_isInterface = _contract.contractKind() == ContractDefinition::ContractKind::Interface; + m_isInterface = _contract.isInterface(); ASTString const& contractName = _contract.name(); for (FunctionDefinition const* function: _contract.definedFunctions()) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 507a2c94..32cafb55 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -204,7 +204,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) auto base = dynamic_cast(&dereference(_inheritance.name())); solAssert(base, "Base contract not available."); - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + if (m_scope->isInterface()) m_errorReporter.typeError(_inheritance.location(), "Interfaces cannot inherit."); if (base->isLibrary()) @@ -212,7 +212,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) auto const& arguments = _inheritance.arguments(); TypePointers parameterTypes; - if (base->contractKind() != ContractDefinition::ContractKind::Interface) + if (!base->isInterface()) // Interfaces do not have constructors, so there are zero parameters. parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); @@ -325,7 +325,6 @@ bool TypeChecker::visit(FunctionDefinition const& _function) "This type is only supported in the new experimental ABI encoder. " "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." ); - var->accept(*this); } set modifiers; @@ -346,7 +345,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else modifiers.insert(decl); } - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + if (m_scope->isInterface()) { if (_function.isImplemented()) m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation."); @@ -375,7 +374,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) // * a function's input/output parameters, // * or inside of a struct definition. if ( - m_scope->contractKind() == ContractDefinition::ContractKind::Interface + m_scope->isInterface() && !_variable.isCallableParameter() && !m_insideStruct ) @@ -1862,7 +1861,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); - if (contract->contractKind() == ContractDefinition::ContractKind::Interface) + if (contract->isInterface()) m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) { diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 9ac065ea..3a64a34a 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -394,6 +394,7 @@ public: std::vector definedFunctions() const { return filteredNodes(m_subNodes); } std::vector events() const { return filteredNodes(m_subNodes); } std::vector const& interfaceEvents() const; + bool isInterface() const { return m_contractKind == ContractKind::Interface; } bool isLibrary() const { return m_contractKind == ContractKind::Library; } /// @returns a map of canonical function signatures to FunctionDefinitions diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index cc978b4a..c8280d0f 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2525,7 +2525,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c strings parameterNames; StateMutability stateMutability = StateMutability::NonPayable; - solAssert(_contract.contractKind() != ContractDefinition::ContractKind::Interface, ""); + solAssert(!_contract.isInterface(), ""); if (constructor) { -- cgit From b7a86a124d658f0723f576e0f447773f3bec148b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 17 Jan 2019 16:37:36 +0100 Subject: Refactor TypeChecker::visit(FunctionDefinition const&). --- libsolidity/analysis/TypeChecker.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 32cafb55..5f2443a2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -299,32 +299,40 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface()) m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable."); } - for (ASTPointer const& var: _function.parameters() + _function.returnParameters()) - { - if (type(*var)->category() == Type::Category::Mapping) + auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) { + if (type(var)->category() == Type::Category::Mapping) { - if (!type(*var)->dataStoredIn(DataLocation::Storage)) - m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"." ); + if (!type(var)->dataStoredIn(DataLocation::Storage)) + m_errorReporter.typeError(var.location(), "Mapping types can only have a data location of \"storage\"." ); else if (!isLibraryFunction && _function.isPublic()) - m_errorReporter.typeError(var->location(), "Mapping types for parameters or return variables can only be used in internal or library functions."); + m_errorReporter.typeError(var.location(), "Mapping types for parameters or return variables can only be used in internal or library functions."); } else { - if (!type(*var)->canLiveOutsideStorage() && _function.isPublic()) - m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); - if (_function.isPublic() && !(type(*var)->interfaceType(isLibraryFunction))) - m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); + if (!type(var)->canLiveOutsideStorage() && _function.isPublic()) + m_errorReporter.typeError(var.location(), "Type is required to live outside storage."); + if (_function.isPublic() && !(type(var)->interfaceType(isLibraryFunction))) + m_errorReporter.fatalTypeError(var.location(), "Internal or recursive type is not allowed for public or external functions."); } if ( _function.isPublic() && !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && - !typeSupportedByOldABIEncoder(*type(*var)) + !typeSupportedByOldABIEncoder(*type(var)) ) m_errorReporter.typeError( - var->location(), + var.location(), "This type is only supported in the new experimental ABI encoder. " "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." ); + }; + for (ASTPointer const& var: _function.parameters()) + { + checkArgumentAndReturnParameter(*var); + var->accept(*this); + } + for (ASTPointer const& var: _function.returnParameters()) + { + checkArgumentAndReturnParameter(*var); var->accept(*this); } set modifiers; -- cgit From 870b656eda5f854ecece7c06d093a7292c72fd59 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 18 Jan 2019 12:34:36 +0100 Subject: Split up rule list generation further to prevent issues in browsers with the emscripten build. --- Changelog.md | 1 + libevmasm/RuleList.h | 102 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/Changelog.md b/Changelog.md index bdfd9746..0562426d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Compiler Features: Bugfixes: + * Emscripten: Split simplification rule initialization up further to work around issues with soljson.js in some browsers. * TypeChecker: Return type error if fixed point encoding is attempted instead of throwing ``UnimplementedFeatureError``. * Yul: Check that arguments to ``dataoffset`` and ``datasize`` are literals at parse time and properly take this into account in the optimizer. * Yul: Parse number literals for detecting duplicate switch cases. diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 874a8929..01e9b984 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -44,7 +44,7 @@ template S modWorkaround(S const& _a, S const& _b) return (S)(bigint(_a) % bigint(_b)); } -// This part of simplificationRuleList below was split out to prevent +// simplificationRuleList below was split up into parts to prevent // stack overflows in the JavaScript optimizer for emscripten builds // that affected certain browser versions. template @@ -52,8 +52,8 @@ std::vector> simplificationRuleListPart1( Pattern A, Pattern B, Pattern C, - Pattern X, - Pattern Y + Pattern, + Pattern ) { return std::vector> { @@ -96,8 +96,20 @@ std::vector> simplificationRuleListPart1( if (A.d() > 255) return u256(0); return B.d() >> unsigned(A.d()); - }, false}, + }, false} + }; +} +template +std::vector> simplificationRuleListPart2( + Pattern, + Pattern, + Pattern, + Pattern X, + Pattern +) +{ + return std::vector> { // invariants involving known constants {{Instruction::ADD, {X, 0}}, [=]{ return X; }, false}, {{Instruction::ADD, {0, X}}, [=]{ return X; }, false}, @@ -128,7 +140,19 @@ std::vector> simplificationRuleListPart1( {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true}, {{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, {{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, + }; +} +template +std::vector> simplificationRuleListPart3( + Pattern, + Pattern, + Pattern, + Pattern X, + Pattern +) +{ + return std::vector> { // operations involving an expression and itself {{Instruction::AND, {X, X}}, [=]{ return X; }, true}, {{Instruction::OR, {X, X}}, [=]{ return X; }, true}, @@ -139,8 +163,20 @@ std::vector> simplificationRuleListPart1( {{Instruction::SLT, {X, X}}, [=]{ return u256(0); }, true}, {{Instruction::GT, {X, X}}, [=]{ return u256(0); }, true}, {{Instruction::SGT, {X, X}}, [=]{ return u256(0); }, true}, - {{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true}, + {{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true} + }; +} +template +std::vector> simplificationRuleListPart4( + Pattern, + Pattern, + Pattern, + Pattern X, + Pattern Y +) +{ + return std::vector> { // logical instruction combinations {{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }, false}, {{Instruction::XOR, {X, {Instruction::XOR, {X, Y}}}}, [=]{ return Y; }, true}, @@ -163,16 +199,13 @@ std::vector> simplificationRuleListPart1( } -// This part of simplificationRuleList below was split out to prevent -// stack overflows in the JavaScript optimizer for emscripten builds -// that affected certain browser versions. template -std::vector> simplificationRuleListPart2( - Pattern A, - Pattern B, +std::vector> simplificationRuleListPart5( + Pattern, + Pattern, Pattern, Pattern X, - Pattern Y + Pattern ) { std::vector> rules; @@ -207,7 +240,19 @@ std::vector> simplificationRuleListPart2( false }); } + return rules; +} +template +std::vector> simplificationRuleListPart6( + Pattern, + Pattern, + Pattern, + Pattern X, + Pattern Y +) +{ + std::vector> rules; // Double negation of opcodes with boolean result for (auto const& op: std::vector{ Instruction::EQ, @@ -234,6 +279,19 @@ std::vector> simplificationRuleListPart2( false }); + return rules; +} + +template +std::vector> simplificationRuleListPart7( + Pattern A, + Pattern B, + Pattern, + Pattern X, + Pattern Y +) +{ + std::vector> rules; // Associative operations for (auto const& opFun: std::vector>>{ {Instruction::ADD, std::plus()}, @@ -274,6 +332,20 @@ std::vector> simplificationRuleListPart2( } } + return rules; +} + +template +std::vector> simplificationRuleListPart8( + Pattern A, + Pattern, + Pattern, + Pattern X, + Pattern Y +) +{ + std::vector> rules; + // move constants across subtractions rules += std::vector>{ { @@ -322,6 +394,12 @@ std::vector> simplificationRuleList( std::vector> rules; rules += simplificationRuleListPart1(A, B, C, X, Y); rules += simplificationRuleListPart2(A, B, C, X, Y); + rules += simplificationRuleListPart3(A, B, C, X, Y); + rules += simplificationRuleListPart4(A, B, C, X, Y); + rules += simplificationRuleListPart5(A, B, C, X, Y); + rules += simplificationRuleListPart6(A, B, C, X, Y); + rules += simplificationRuleListPart7(A, B, C, X, Y); + rules += simplificationRuleListPart8(A, B, C, X, Y); return rules; } -- cgit From a582f0bec10839ee999975405e59e5454c165b99 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 18 Jan 2019 16:49:21 +0000 Subject: Update to emscripten 1.38.22 --- .circleci/config.yml | 2 +- .travis.yml | 2 +- scripts/build_emscripten.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 52bb36cf..b3bbd264 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,7 +34,7 @@ version: 2 jobs: build_emscripten: docker: - - image: trzeci/emscripten:sdk-tag-1.38.8-64bit + - image: trzeci/emscripten:sdk-tag-1.38.22-64bit environment: TERM: xterm steps: diff --git a/.travis.yml b/.travis.yml index a8a68aff..076833e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -114,7 +114,7 @@ matrix: before_install: - nvm install 8 - nvm use 8 - - docker pull trzeci/emscripten:sdk-tag-1.38.8-64bit + - docker pull trzeci/emscripten:sdk-tag-1.38.22-64bit env: - SOLC_EMSCRIPTEN=On - SOLC_INSTALL_DEPS_TRAVIS=Off diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index 46521cc5..dbd41113 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -30,5 +30,5 @@ set -e if [[ "$OSTYPE" != "darwin"* ]]; then ./scripts/travis-emscripten/install_deps.sh - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.38.8-64bit ./scripts/travis-emscripten/build_emscripten.sh + docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.38.22-64bit ./scripts/travis-emscripten/build_emscripten.sh fi -- cgit From 272566ac93ee27a34f17afa416403082e6da9d73 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 18 Jan 2019 17:03:50 +0000 Subject: CircleCI: always rebuilt boost cache after emscripten udpate --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b3bbd264..40aa6268 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,7 +41,7 @@ jobs: - checkout - restore_cache: name: Restore Boost build - key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }} + key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/build_emscripten.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }} - run: name: Bootstrap Boost command: | -- cgit From 128d2811d5aa84b3f155de0ea22fd0a295dc7023 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 18 Jan 2019 17:55:03 +0000 Subject: Travis: make cache dependent on emscripten version --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index 076833e0..6d3d70e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -122,6 +122,16 @@ matrix: - SOLC_TESTS=Off - ZIP_SUFFIX=emscripten - SOLC_STOREBYTECODE=On + # Travis doesn't seem to support "dynamic" cache keys where we could include + # the hashes of certain files. Our CircleCI configuration contains the hash of + # relevant emscripten files. + # + # It is important to invalidate the cache with each emscripten update, because + # dependencies, such as boost, might be broken otherwise. + # + # This key here has no significant on anything, apart from caching. Please keep + # it in sync with the version above. + - EMSCRIPTEN_VERSION_KEY="1.38.22" # OS X Mavericks (10.9) # https://en.wikipedia.org/wiki/OS_X_Mavericks -- cgit From 8c97fb1688f7d756c783c65504f400c7f5f5f7e5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 18 Jan 2019 17:59:32 +0000 Subject: Remove boost::filesystem::weakly_canonical workaround --- libdevcore/CommonIO.cpp | 20 -------------------- libdevcore/CommonIO.h | 4 ---- solc/CommandLineInterface.cpp | 2 +- 3 files changed, 1 insertion(+), 25 deletions(-) diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index cc730575..29b31ebc 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -128,26 +128,6 @@ int dev::readStandardInputChar() return cin.get(); } -boost::filesystem::path dev::weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path) -{ - if (boost::filesystem::exists(_path)) - return boost::filesystem::canonical(_path); - else - { - boost::filesystem::path head(_path); - boost::filesystem::path tail; - for (auto it = --_path.end(); !head.empty(); --it) - { - if (boost::filesystem::exists(head)) - break; - tail = (*it) / tail; - head.remove_filename(); - } - head = boost::filesystem::canonical(head); - return head / tail; - } -} - string dev::absolutePath(string const& _path, string const& _reference) { boost::filesystem::path p(_path); diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index b9f941ea..0d8aca79 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -50,10 +50,6 @@ std::string toString(_T const& _t) return o.str(); } -/// Partial implementation of boost::filesystem::weakly_canonical (available in boost>=1.60). -/// Should be replaced by the boost implementation as soon as support for boost<1.60 can be dropped. -boost::filesystem::path weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path); - /// @returns the absolute path corresponding to @a _path relative to @a _reference. std::string absolutePath(std::string const& _path, std::string const& _reference); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index bda1b78a..4bf24901 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -731,7 +731,7 @@ bool CommandLineInterface::processInput() try { auto path = boost::filesystem::path(_path); - auto canonicalPath = weaklyCanonicalFilesystemPath(path); + auto canonicalPath = boost::filesystem::weakly_canonical(path); bool isAllowed = false; for (auto const& allowedDir: m_allowedDirectories) { -- cgit From 6a32f7e4445372fe97c4b6a2415b90dba2deb6db Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 18 Jan 2019 18:01:33 +0000 Subject: Remove boost_multiprecision_number_compare_bug_workaround for Boost <=1.58 --- libdevcore/CMakeLists.txt | 1 - libdevcore/Common.h | 18 - ...ultiprecision_number_compare_bug_workaround.hpp | 520 --------------------- 3 files changed, 539 deletions(-) delete mode 100644 libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index e68ac10a..193fa41d 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -1,7 +1,6 @@ set(sources Algorithms.h Assertions.h - boost_multiprecision_number_compare_bug_workaround.hpp Common.h CommonData.cpp CommonData.h diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 6208424e..08757daf 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -39,26 +39,8 @@ #include -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif // defined(__GNUC__) - -// See https://github.com/ethereum/libweb3core/commit/90680a8c25bfb48b24371b4abcacde56c181517c -// See https://svn.boost.org/trac/boost/ticket/11328 -// Bob comment - perhaps we should just HARD FAIL here with Boost-1.58.00? -// It is quite old now, and requiring end-users to use a newer Boost release is probably not unreasonable. -#include -#if (BOOST_VERSION == 105800) - #include "boost_multiprecision_number_compare_bug_workaround.hpp" -#endif // (BOOST_VERSION == 105800) - #include -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif // defined(__GNUC__) - #include #include #include diff --git a/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp b/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp deleted file mode 100644 index 2568e17d..00000000 --- a/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp +++ /dev/null @@ -1,520 +0,0 @@ - -// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58. - -#ifdef BOOST_MP_COMPARE_HPP -#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Copyright 2012 John Maddock. Distributed under the Boost -// Software License, Version 1.0. (See accompanying file -// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_MP_COMPARE_HPP -#define BOOST_MP_COMPARE_HPP - -// A copy of boost/multiprecision/traits/is_backend.hpp -#ifndef BOOST_MP_IS_BACKEND_HPP -#define BOOST_MP_IS_BACKEND_HPP - -#include -#include -#include -#include -#include - -namespace boost{ namespace multiprecision{ namespace detail{ - - BOOST_MPL_HAS_XXX_TRAIT_DEF(signed_types) - BOOST_MPL_HAS_XXX_TRAIT_DEF(unsigned_types) - BOOST_MPL_HAS_XXX_TRAIT_DEF(float_types) - - template - struct is_backend - { - static const bool value = has_signed_types::value && has_unsigned_types::value && has_float_types::value; - }; - - template - struct other_backend - { - typedef typename boost::conditional< - boost::is_same, number >::value, - number, number >::type type; - }; - - template - struct number_from_backend - { - typedef typename boost::conditional < - boost::is_convertible >::value, - number, - typename other_backend::type > ::type type; - }; - - template - struct is_first_backend_imp{ static const bool value = false; }; - template - struct is_first_backend_imp{ static const bool value = is_convertible >::value || is_convertible >::value; }; - - template - struct is_first_backend : is_first_backend_imp::value, T, U> {}; - - template - struct is_second_backend_imp{ static const bool value = false; }; - template - struct is_second_backend_imp{ static const bool value = is_convertible >::value || is_convertible >::value; }; - - template - struct is_second_backend : is_second_backend_imp::value, T, U> {}; - -} -} -} - -#endif // BOOST_MP_IS_BACKEND_HPP - -// -// Comparison operators for number. -// - -namespace boost{ namespace multiprecision{ - -namespace default_ops{ - -template -inline bool eval_eq(const B& a, const B& b) -{ - return a.compare(b) == 0; -} -template -inline typename enable_if_c::value, bool>::type eval_eq(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend::type t(b); - return eval_eq(a, t.backend()); -} -template -inline typename enable_if_c::value, bool>::type eval_eq(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend::type t(a); - return eval_eq(t.backend(), b); -} - -template -inline bool eval_lt(const B& a, const B& b) -{ - return a.compare(b) < 0; -} -template -inline typename enable_if_c::value, bool>::type eval_lt(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend::type t(b); - return eval_lt(a, t.backend()); -} -template -inline typename enable_if_c::value, bool>::type eval_lt(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend::type t(a); - return eval_lt(t.backend(), b); -} - -template -inline bool eval_gt(const B& a, const B& b) -{ - return a.compare(b) > 0; -} -template -inline typename enable_if_c::value, bool>::type eval_gt(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend::type t(b); - return eval_gt(a, t.backend()); -} -template -inline typename enable_if_c::value, bool>::type eval_gt(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend::type t(a); - return eval_gt(t.backend(), b); -} - -} // namespace default_ops - -namespace detail{ - -template -struct is_valid_mixed_compare : public mpl::false_ {}; - -template -struct is_valid_mixed_compare, Val> : public is_convertible > {}; - -template -struct is_valid_mixed_compare, number > : public mpl::false_ {}; - -template -struct is_valid_mixed_compare, expression > - : public mpl::bool_, number >::value> {}; - -template -struct is_valid_mixed_compare, number > - : public mpl::bool_, number >::value> {}; - -template -inline BOOST_CONSTEXPR typename boost::enable_if_c::value != number_kind_floating_point, bool>::type is_unordered_value(const number&) -{ - return false; -} -template -inline BOOST_CONSTEXPR typename boost::enable_if_c::value == number_kind_floating_point, bool>::type is_unordered_value(const number& a) -{ - using default_ops::eval_fpclassify; - return eval_fpclassify(a.backend()) == FP_NAN; -} - -template -inline BOOST_CONSTEXPR typename boost::enable_if_c::value != number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic&) -{ - return false; -} -template -inline BOOST_CONSTEXPR typename boost::enable_if_c::value == number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic& a) -{ - return (boost::math::isnan)(a); -} - -template -inline BOOST_CONSTEXPR bool is_unordered_comparison(const T& a, const U& b) -{ - return is_unordered_value(a) || is_unordered_value(b); -} - -} - -template -inline bool operator == (const number& a, const number& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_eq(a.backend(), b.backend()); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator == (const number& a, const Arithmetic& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_eq(a.backend(), number::canonical_value(b)); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator == (const Arithmetic& a, const number& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_eq(b.backend(), number::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator == (const Arithmetic& a, const detail::expression& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_eq; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return eval_eq(t.backend(), result_type::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator == (const detail::expression& a, const Arithmetic& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_eq; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return eval_eq(t.backend(), result_type::canonical_value(b)); -} -template -inline typename enable_if::result_type, typename detail::expression::result_type>, bool>::type - operator == (const detail::expression& a, const detail::expression& b) -{ - using default_ops::eval_eq; - typename detail::expression::result_type t(a); - typename detail::expression::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return eval_eq(t.backend(), t2.backend()); -} - -template -inline bool operator != (const number& a, const number& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return true; - return !eval_eq(a.backend(), b.backend()); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator != (const number& a, const Arithmetic& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return true; - return !eval_eq(a.backend(), number::canonical_value(b)); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator != (const Arithmetic& a, const number& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return true; - return !eval_eq(b.backend(), number::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator != (const Arithmetic& a, const detail::expression& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_eq; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return true; - return !eval_eq(t.backend(), result_type::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator != (const detail::expression& a, const Arithmetic& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_eq; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return true; - return !eval_eq(t.backend(), result_type::canonical_value(b)); -} -template -inline typename enable_if::result_type, typename detail::expression::result_type>, bool>::type - operator != (const detail::expression& a, const detail::expression& b) -{ - using default_ops::eval_eq; - typename detail::expression::result_type t(a); - typename detail::expression::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return true; - return !eval_eq(t.backend(), t2.backend()); -} - -template -inline bool operator < (const number& a, const number& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_lt(a.backend(), b.backend()); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator < (const number& a, const Arithmetic& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_lt(a.backend(), number::canonical_value(b)); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator < (const Arithmetic& a, const number& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_gt(b.backend(), number::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator < (const Arithmetic& a, const detail::expression& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_gt; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return eval_gt(t.backend(), result_type::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator < (const detail::expression& a, const Arithmetic& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_lt; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return eval_lt(t.backend(), result_type::canonical_value(b)); -} -template -inline typename enable_if::result_type, typename detail::expression::result_type>, bool>::type - operator < (const detail::expression& a, const detail::expression& b) -{ - using default_ops::eval_lt; - typename detail::expression::result_type t(a); - typename detail::expression::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return eval_lt(t.backend(), t2.backend()); -} - -template -inline bool operator > (const number& a, const number& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_gt(a.backend(), b.backend()); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator > (const number& a, const Arithmetic& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_gt(a.backend(), number::canonical_value(b)); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator > (const Arithmetic& a, const number& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_lt(b.backend(), number::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator > (const Arithmetic& a, const detail::expression& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_lt; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return a > t; -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator > (const detail::expression& a, const Arithmetic& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_gt; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return t > b; -} -template -inline typename enable_if::result_type, typename detail::expression::result_type>, bool>::type - operator > (const detail::expression& a, const detail::expression& b) -{ - using default_ops::eval_gt; - typename detail::expression::result_type t(a); - typename detail::expression::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return t > t2; -} - -template -inline bool operator <= (const number& a, const number& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_gt(a.backend(), b.backend()); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator <= (const number& a, const Arithmetic& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_gt(a.backend(), number::canonical_value(b)); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator <= (const Arithmetic& a, const number& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_lt(b.backend(), number::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator <= (const Arithmetic& a, const detail::expression& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_lt; - if(detail::is_unordered_value(a) || detail::is_unordered_value(b)) - return false; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return !eval_lt(t.backend(), result_type::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator <= (const detail::expression& a, const Arithmetic& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_gt; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return !eval_gt(t.backend(), result_type::canonical_value(b)); -} -template -inline typename enable_if::result_type, typename detail::expression::result_type>, bool>::type - operator <= (const detail::expression& a, const detail::expression& b) -{ - using default_ops::eval_gt; - typename detail::expression::result_type t(a); - typename detail::expression::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return !eval_gt(t.backend(), t2.backend()); -} - -template -inline bool operator >= (const number& a, const number& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_lt(a.backend(), b.backend()); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator >= (const number& a, const Arithmetic& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_lt(a.backend(), number::canonical_value(b)); -} -template -inline typename enable_if_c, Arithmetic>::value, bool>::type - operator >= (const Arithmetic& a, const number& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_gt(b.backend(), number::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator >= (const Arithmetic& a, const detail::expression& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_gt; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return !eval_gt(t.backend(), result_type::canonical_value(a)); -} -template -inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type - operator >= (const detail::expression& a, const Arithmetic& b) -{ - typedef typename detail::expression::result_type result_type; - using default_ops::eval_lt; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return !eval_lt(t.backend(), result_type::canonical_value(b)); -} -template -inline typename enable_if::result_type, typename detail::expression::result_type>, bool>::type - operator >= (const detail::expression& a, const detail::expression& b) -{ - using default_ops::eval_lt; - typename detail::expression::result_type t(a); - typename detail::expression::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return !eval_lt(t.backend(), t2.backend()); -} - - -}} // namespaces - -#endif // BOOST_MP_COMPARE_HPP -- cgit From b1d43a868c1d5dc63731ca1672fd827d4bd536cb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 18 Jan 2019 18:13:59 +0000 Subject: Add error condition if compiling with Boost <1.65 --- libdevcore/Common.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 08757daf..e02cf7fa 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -39,6 +39,11 @@ #include +#include +#if (BOOST_VERSION < 106500) +#error "Unsupported Boost version. At least 1.65 required." +#endif + #include #include -- cgit From f4130d7910d78f741663f0bbb9df8537b64986e1 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Sun, 20 Jan 2019 19:11:24 +0200 Subject: Further clarify in-memory arrays --- docs/types/reference-types.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index b133bdf1..b9fd9194 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -132,7 +132,7 @@ The numeric index becomes a required parameter for the getter. Allocating Memory Arrays ^^^^^^^^^^^^^^^^^^^^^^^^ -You can use the ``new`` keyword to create arrays with a runtime-dependent length in memory. +You must use the ``new`` keyword to create arrays with a runtime-dependent length in memory. As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to the ``.length`` member). You either have to calculate the required size in advance or create a new memory array and copy every element. -- cgit From 610ef9f1999c1dd201a121334394b10f6ac54bc7 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 17 Jan 2019 12:59:11 +0100 Subject: Disallow calldata structs. --- Changelog.md | 3 ++- libsolidity/analysis/TypeChecker.cpp | 10 ++++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 14 ++++++++++---- .../inheritance/override/calldata_memory_struct.sol | 4 ++++ test/libsolidity/syntaxTests/structs/array_calldata.sol | 10 ++++++++++ test/libsolidity/syntaxTests/structs/calldata.sol | 8 ++++++++ 6 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 test/libsolidity/syntaxTests/structs/array_calldata.sol create mode 100644 test/libsolidity/syntaxTests/structs/calldata.sol diff --git a/Changelog.md b/Changelog.md index 0562426d..56577f63 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,8 @@ Compiler Features: Bugfixes: * Emscripten: Split simplification rule initialization up further to work around issues with soljson.js in some browsers. - * TypeChecker: Return type error if fixed point encoding is attempted instead of throwing ``UnimplementedFeatureError``. + * Type Checker: Disallow calldata structs until implemented. + * Type Checker: Return type error if fixed point encoding is attempted instead of throwing ``UnimplementedFeatureError``. * Yul: Check that arguments to ``dataoffset`` and ``datasize`` are literals at parse time and properly take this into account in the optimizer. * Yul: Parse number literals for detecting duplicate switch cases. * Yul: Require switch cases to have the same type. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f1a5f7ce..6d887c45 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -359,6 +359,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function) }; for (ASTPointer const& var: _function.parameters()) { + TypePointer baseType = type(*var); + while (auto const* arrayType = dynamic_cast(baseType.get())) + baseType = arrayType->baseType(); + + if ( + !m_scope->isInterface() && + baseType->category() == Type::Category::Struct && + baseType->dataStoredIn(DataLocation::CallData) + ) + m_errorReporter.typeError(var->location(), "Calldata structs are not yet supported."); checkArgumentAndReturnParameter(*var); var->accept(*this); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 774f67fe..0470cf4c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(enum_external_type) } } -BOOST_AUTO_TEST_CASE(external_structs) +BOOST_AUTO_TEST_CASE(external_struct_signatures) { char const* text = R"( pragma experimental ABIEncoderV2; @@ -213,7 +213,10 @@ BOOST_AUTO_TEST_CASE(external_structs) function i(Nested[] calldata) external {} } )"; - SourceUnit const* sourceUnit = parseAndAnalyse(text); + // Ignore analysis errors. This test only checks that correct signatures + // are generated for external structs, but they are not yet supported + // in code generation and therefore cause an error in the TypeChecker. + SourceUnit const* sourceUnit = parseAnalyseAndReturnError(text, false, true, true).first; for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -226,7 +229,7 @@ BOOST_AUTO_TEST_CASE(external_structs) } } -BOOST_AUTO_TEST_CASE(external_structs_in_libraries) +BOOST_AUTO_TEST_CASE(external_struct_signatures_in_libraries) { char const* text = R"( pragma experimental ABIEncoderV2; @@ -241,7 +244,10 @@ BOOST_AUTO_TEST_CASE(external_structs_in_libraries) function i(Nested[] calldata) external {} } )"; - SourceUnit const* sourceUnit = parseAndAnalyse(text); + // Ignore analysis errors. This test only checks that correct signatures + // are generated for external structs, but calldata structs are not yet supported + // in code generation and therefore cause an error in the TypeChecker. + SourceUnit const* sourceUnit = parseAnalyseAndReturnError(text, false, true, true).first; for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol index 42aebf30..b81e3859 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol @@ -15,3 +15,7 @@ contract B is A { } // ---- // Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (102-112): Calldata structs are not yet supported. +// TypeError: (146-156): Calldata structs are not yet supported. +// TypeError: (198-208): Calldata structs are not yet supported. +// TypeError: (250-260): Calldata structs are not yet supported. diff --git a/test/libsolidity/syntaxTests/structs/array_calldata.sol b/test/libsolidity/syntaxTests/structs/array_calldata.sol new file mode 100644 index 00000000..3aac5606 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/array_calldata.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; +contract Test { + struct S { int a; } + function f(S[] calldata) external { } + function f(S[][] calldata) external { } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (89-101): Calldata structs are not yet supported. +// TypeError: (131-145): Calldata structs are not yet supported. diff --git a/test/libsolidity/syntaxTests/structs/calldata.sol b/test/libsolidity/syntaxTests/structs/calldata.sol new file mode 100644 index 00000000..dadf6e4f --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/calldata.sol @@ -0,0 +1,8 @@ +pragma experimental ABIEncoderV2; +contract Test { + struct S { int a; } + function f(S calldata) external { } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (89-99): Calldata structs are not yet supported. -- cgit From f39993ced653d6c34338e8aa85d7c664faa6af1d Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 21 Jan 2019 12:33:11 +0200 Subject: Readd example --- docs/control-structures.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index f32e7879..46b3a7f1 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -264,6 +264,31 @@ Complications for Arrays and Structs The semantics of assignments are a bit more complicated for non-value types like arrays and structs. Assigning *to* a state variable always creates an independent copy. On the other hand, assigning to a local variable creates an independent copy only for elementary types, i.e. static types that fit into 32 bytes. If structs or arrays (including ``bytes`` and ``string``) are assigned from a state variable to a local variable, the local variable holds a reference to the original state variable. A second assignment to the local variable does not modify the state but only changes the reference. Assignments to members (or elements) of the local variable *do* change the state. +In the example below the call to ``g(x)`` has no effect on ``x`` because it needs +to create an independent copy of the storage value in memory. However ``h(x)`` modifies ``x`` because a reference and +not a copy is passed. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract C { + uint[20] x; + + function f() public { + g(x); + h(x); + } + + function g(uint[20] memory y) internal pure { + y[2] = 3; + } + + function h(uint[20] storage y) internal { + y[3] = 4; + } + } + .. index:: ! scoping, declarations, default value .. _default-value: -- cgit From a1df27ae3d65d22e69c1bf6b812e69d3d92a28cc Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 16 Jan 2019 14:36:59 +0200 Subject: Merge payable FAQ item --- docs/contracts/functions.rst | 4 ++-- docs/frequently-asked-questions.rst | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 76245952..522ce5c4 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -236,9 +236,9 @@ functions match the given function identifier (or if no data was supplied at all). Furthermore, this function is executed whenever the contract receives plain -Ether (without data). Additionally, in order to receive Ether, the fallback function +Ether (without data). To receive Ether and add it to the total balance of the contract, the fallback function must be marked ``payable``. If no such function exists, the contract cannot receive -Ether through regular transactions. +Ether through regular transactions and throws an exception. In the worst case, the fallback function can only rely on 2300 gas being available (for example when `send` or `transfer` is used), leaving little diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index ff2f1fac..4cd9acb2 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -54,13 +54,6 @@ Yes, you can use ``abi.encodePacked``:: } } -What happens if you send ether along with a function call to a contract? -======================================================================== - -It gets added to the total balance of the contract, just like when you send ether when creating a contract. -You can only send ether along to a function that has the ``payable`` modifier, -otherwise an exception is thrown. - ****************** Advanced Questions ****************** -- cgit From 7f8ceaadab0c265674b591aa50cfeb8910628b9f Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 20 Dec 2018 18:11:20 +0100 Subject: [SMTChecker] Clear state knowledge after external function calls --- Changelog.md | 1 + libsolidity/formal/SMTChecker.cpp | 36 +++++++++++++++------- libsolidity/formal/SMTChecker.h | 3 ++ .../function_call_does_not_clear_local_vars.sol | 1 - .../functions/functions_external_1.sol | 21 +++++++++++++ .../functions/functions_external_2.sol | 21 +++++++++++++ .../functions/functions_external_3.sol | 22 +++++++++++++ 7 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/functions_external_1.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/functions_external_2.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/functions_external_3.sol diff --git a/Changelog.md b/Changelog.md index d3bb8e10..53097159 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Language Features: Compiler Features: * Control Flow Graph: Warn about unreachable code. * SMTChecker: Support basic typecasts without truncation. + * SMTChecker: Support external function calls and erase all knowledge regarding storage variables and references. Bugfixes: diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 38715289..500b610f 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -418,6 +418,10 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::Internal: inlineFunctionCall(_funCall); break; + case FunctionType::Kind::External: + resetStateVariables(); + resetStorageReferences(); + break; case FunctionType::Kind::KECCAK256: case FunctionType::Kind::ECRecover: case FunctionType::Kind::SHA256: @@ -1194,25 +1198,35 @@ void SMTChecker::removeLocalVariables() } } +void SMTChecker::resetVariable(VariableDeclaration const& _variable) +{ + newValue(_variable); + setUnknownValue(_variable); +} + void SMTChecker::resetStateVariables() { - for (auto const& variable: m_variables) - { - if (variable.first->isStateVariable()) - { - newValue(*variable.first); - setUnknownValue(*variable.first); - } - } + resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); }); +} + +void SMTChecker::resetStorageReferences() +{ + resetVariables([&](VariableDeclaration const& _variable) { return _variable.hasReferenceOrMappingType(); }); } void SMTChecker::resetVariables(vector _variables) { for (auto const* decl: _variables) + resetVariable(*decl); +} + +void SMTChecker::resetVariables(function const& _filter) +{ + for_each(begin(m_variables), end(m_variables), [&](auto _variable) { - newValue(*decl); - setUnknownValue(*decl); - } + if (_filter(*_variable.first)) + this->resetVariable(*_variable.first); + }); } void SMTChecker::mergeVariables(vector const& _variables, smt::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index caa83764..a85933c8 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -147,8 +147,11 @@ private: void initializeLocalVariables(FunctionDefinition const& _function); void initializeFunctionCallParameters(FunctionDefinition const& _function, std::vector const& _callArgs); + void resetVariable(VariableDeclaration const& _variable); void resetStateVariables(); + void resetStorageReferences(); void resetVariables(std::vector _variables); + void resetVariables(std::function const& _filter); /// Given two different branches and the touched variables, /// merge the touched variables into after-branch ite variables /// using the branch condition as guard. diff --git a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol index b4260224..0ceb3b46 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol @@ -9,5 +9,4 @@ contract C { } } // ---- -// Warning: (99-107): Assertion checker does not yet implement this type of function call. // Warning: (141-144): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_external_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_external_1.sol new file mode 100644 index 00000000..16482e7a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_external_1.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract D +{ + function g(uint x) public; +} + +contract C +{ + uint x; + function f(uint y, D d) public { + require(x == y); + assert(x == y); + d.g(y); + // Storage knowledge is cleared after an external call. + assert(x == y); + } +} +// ---- +// Warning: (119-122): Assertion checker does not yet support the type of this variable. +// Warning: (240-254): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol b/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol new file mode 100644 index 00000000..1e704c9d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract D +{ + function g(uint x) public; +} + +contract C +{ + mapping (uint => uint) map; + function f(uint y, D d) public { + require(map[0] == map[1]); + assert(map[0] == map[1]); + d.g(y); + // Storage knowledge is cleared after an external call. + assert(map[0] == map[1]); + } +} +// ---- +// Warning: (139-142): Assertion checker does not yet support the type of this variable. +// Warning: (280-304): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_external_3.sol b/test/libsolidity/smtCheckerTests/functions/functions_external_3.sol new file mode 100644 index 00000000..dd36ec73 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_external_3.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract D +{ + function g(uint x) public; +} + +contract C +{ + mapping (uint => uint) storageMap; + function f(uint y, D d) public { + mapping (uint => uint) storage map = storageMap; + require(map[0] == map[1]); + assert(map[0] == map[1]); + d.g(y); + // Storage knowledge is cleared after an external call. + assert(map[0] == map[1]); + } +} +// ---- +// Warning: (146-149): Assertion checker does not yet support the type of this variable. +// Warning: (338-362): Assertion violation happens here -- cgit From 4c9bbd85e6cf0f15906ba4aa84b907617e409f82 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Jan 2019 15:30:29 +0100 Subject: Patch soljson.js to provide backwards compatibility with older emscripten versions. --- scripts/travis-emscripten/build_emscripten.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 373f32db..0b6c5346 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -96,6 +96,8 @@ make -j 4 cd .. mkdir -p upload +# Patch soljson.js to provide backwards-compatibility with older emscripten versions +echo ";/* backwards compatibility */ Module['Runtime'] = Module;" >> build/libsolc/soljson.js cp build/libsolc/soljson.js upload/ cp build/libsolc/soljson.js ./ -- cgit From 7a69455c1351e1acd3f9a30bcc621f3f4719eedd Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Jan 2019 16:43:32 +0100 Subject: Provide ABI encoding options as single struct parameter. --- libsolidity/codegen/ABIFunctions.cpp | 101 +++++++++++++++++++++-------------- libsolidity/codegen/ABIFunctions.h | 35 ++++++++---- 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index c1ab03e3..c0fa81ce 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -39,14 +39,19 @@ string ABIFunctions::tupleEncoder( bool _encodeAsLibraryTypes ) { + EncodingOptions options; + options.encodeAsLibraryTypes = _encodeAsLibraryTypes; + options.encodeFunctionFromStack = true; + options.padded = true; + options.dynamicInplace = false; + string functionName = string("abi_encode_tuple_"); for (auto const& t: _givenTypes) functionName += t->identifier() + "_"; functionName += "_to_"; for (auto const& t: _targetTypes) functionName += t->identifier() + "_"; - if (_encodeAsLibraryTypes) - functionName += "_library"; + functionName += options.toFunctionNameSuffix(); return createExternallyUsedFunction(functionName, [&]() { solAssert(!_givenTypes.empty(), ""); @@ -90,7 +95,7 @@ string ABIFunctions::tupleEncoder( ); elementTempl("values", valueNames); elementTempl("pos", to_string(headPos)); - elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, true)); + elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options)); encodeElements += elementTempl.render(); headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); } @@ -184,6 +189,20 @@ pair> ABIFunctions::requestedFunctions() return make_pair(result, std::move(m_externallyUsedFunctions)); } +string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const +{ + string suffix; + if (!padded) + suffix += "_nonPadded"; + if (dynamicInplace) + suffix += "_inplace"; + if (encodeFunctionFromStack) + suffix += "_fromStack"; + if (encodeAsLibraryTypes) + suffix += "_library"; + return suffix; +} + string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) { string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier(); @@ -490,32 +509,31 @@ string ABIFunctions::splitExternalFunctionIdFunction() string ABIFunctions::abiEncodingFunction( Type const& _from, Type const& _to, - bool _encodeAsLibraryTypes, - bool _fromStack + EncodingOptions const& _options ) { - TypePointer toInterface = _to.fullEncodingType(_encodeAsLibraryTypes, true, false); + TypePointer toInterface = _to.fullEncodingType(_options.encodeAsLibraryTypes, true, false); solUnimplementedAssert(toInterface, "Encoding type \"" + _to.toString() + "\" not yet implemented."); Type const& to = *toInterface; if (_from.category() == Type::Category::StringLiteral) - return abiEncodingFunctionStringLiteral(_from, to, _encodeAsLibraryTypes); + return abiEncodingFunctionStringLiteral(_from, to, _options); else if (auto toArray = dynamic_cast(&to)) { solAssert(_from.category() == Type::Category::Array, ""); solAssert(to.dataStoredIn(DataLocation::Memory), ""); ArrayType const& fromArray = dynamic_cast(_from); if (fromArray.location() == DataLocation::CallData) - return abiEncodingFunctionCalldataArray(fromArray, *toArray, _encodeAsLibraryTypes); + return abiEncodingFunctionCalldataArray(fromArray, *toArray, _options); else if (!fromArray.isByteArray() && ( fromArray.location() == DataLocation::Memory || fromArray.baseType()->storageBytes() > 16 )) - return abiEncodingFunctionSimpleArray(fromArray, *toArray, _encodeAsLibraryTypes); + return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options); else if (fromArray.location() == DataLocation::Memory) - return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _encodeAsLibraryTypes); + return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _options); else if (fromArray.location() == DataLocation::Storage) - return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _encodeAsLibraryTypes); + return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _options); else solAssert(false, ""); } @@ -523,14 +541,13 @@ string ABIFunctions::abiEncodingFunction( { StructType const* fromStruct = dynamic_cast(&_from); solAssert(fromStruct, ""); - return abiEncodingFunctionStruct(*fromStruct, *toStruct, _encodeAsLibraryTypes); + return abiEncodingFunctionStruct(*fromStruct, *toStruct, _options); } else if (_from.category() == Type::Category::Function) return abiEncodingFunctionFunctionType( dynamic_cast(_from), to, - _encodeAsLibraryTypes, - _fromStack + _options ); solAssert(_from.sizeOnStack() == 1, ""); @@ -541,7 +558,7 @@ string ABIFunctions::abiEncodingFunction( _from.identifier() + "_to_" + to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); return createFunction(functionName, [&]() { solAssert(!to.isDynamicallyEncoded(), ""); @@ -556,7 +573,7 @@ string ABIFunctions::abiEncodingFunction( { // special case: convert storage reference type to value type - this is only // possible for library calls where we just forward the storage reference - solAssert(_encodeAsLibraryTypes, ""); + solAssert(_options.encodeAsLibraryTypes, ""); solAssert(to == IntegerType::uint256(), ""); templ("cleanupConvert", "value"); } @@ -574,7 +591,7 @@ string ABIFunctions::abiEncodingFunction( string ABIFunctions::abiEncodingFunctionCalldataArray( Type const& _from, Type const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { solAssert(_to.isDynamicallySized(), ""); @@ -596,7 +613,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); return createFunction(functionName, [&]() { solUnimplementedAssert(fromArrayType.isByteArray(), "Only byte arrays can be encoded from calldata currently."); // TODO if this is not a byte array, we might just copy byte-by-byte anyway, @@ -622,7 +639,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray( string ABIFunctions::abiEncodingFunctionSimpleArray( ArrayType const& _from, ArrayType const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { string functionName = @@ -630,7 +647,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); @@ -691,11 +708,13 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( templ("storeLength", ""); templ("dataAreaFun", arrayDataAreaFunction(_from)); templ("elementEncodedSize", toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize())); + + EncodingOptions subOptions(_options); + subOptions.encodeFunctionFromStack = false; templ("encodeToMemoryFun", abiEncodingFunction( *_from.baseType(), *_to.baseType(), - _encodeAsLibraryTypes, - false + subOptions )); templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" ); templ("nextArrayElement", nextArrayElementFunction(_from)); @@ -706,7 +725,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( string ABIFunctions::abiEncodingFunctionMemoryByteArray( ArrayType const& _from, ArrayType const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { string functionName = @@ -714,7 +733,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); @@ -742,7 +761,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray( string ABIFunctions::abiEncodingFunctionCompactStorageArray( ArrayType const& _from, ArrayType const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { string functionName = @@ -750,7 +769,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); @@ -840,11 +859,13 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( templ("itemsPerSlot", to_string(itemsPerSlot)); string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize()); templ("elementEncodedSize", elementEncodedSize); + + EncodingOptions subOptions(_options); + subOptions.encodeFunctionFromStack = false; string encodeToMemoryFun = abiEncodingFunction( *_from.baseType(), *_to.baseType(), - _encodeAsLibraryTypes, - false + subOptions ); templ("encodeToMemoryFun", encodeToMemoryFun); std::vector> items(itemsPerSlot); @@ -859,7 +880,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( string ABIFunctions::abiEncodingFunctionStruct( StructType const& _from, StructType const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { string functionName = @@ -867,7 +888,7 @@ string ABIFunctions::abiEncodingFunctionStruct( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "Encoding struct from calldata is not yet supported."); solAssert(&_from.structDefinition() == &_to.structDefinition(), ""); @@ -904,7 +925,7 @@ string ABIFunctions::abiEncodingFunctionStruct( solAssert(member.type, ""); if (!member.type->canLiveOutsideStorage()) continue; - TypePointer memberTypeTo = member.type->fullEncodingType(_encodeAsLibraryTypes, true, false); + TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false); solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented."); auto memberTypeFrom = _from.memberType(member.name); solAssert(memberTypeFrom, ""); @@ -958,7 +979,10 @@ string ABIFunctions::abiEncodingFunctionStruct( } memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset)); encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize(); - memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, _encodeAsLibraryTypes, false)); + + EncodingOptions subOptions(_options); + subOptions.encodeFunctionFromStack = false; + memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions)); members.push_back({}); members.back()["encode"] = memberTempl.render(); @@ -973,7 +997,7 @@ string ABIFunctions::abiEncodingFunctionStruct( string ABIFunctions::abiEncodingFunctionStringLiteral( Type const& _from, Type const& _to, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ) { solAssert(_from.category() == Type::Category::StringLiteral, ""); @@ -983,7 +1007,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( _from.identifier() + "_to_" + _to.identifier() + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); return createFunction(functionName, [&]() { auto const& strType = dynamic_cast(_from); string const& value = strType.value(); @@ -1034,8 +1058,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( string ABIFunctions::abiEncodingFunctionFunctionType( FunctionType const& _from, Type const& _to, - bool _encodeAsLibraryTypes, - bool _fromStack + EncodingOptions const& _options ) { solAssert(_from.kind() == FunctionType::Kind::External, ""); @@ -1046,10 +1069,9 @@ string ABIFunctions::abiEncodingFunctionFunctionType( _from.identifier() + "_to_" + _to.identifier() + - (_fromStack ? "_fromStack" : "") + - (_encodeAsLibraryTypes ? "_library" : ""); + _options.toFunctionNameSuffix(); - if (_fromStack) + if (_options.encodeFunctionFromStack) return createFunction(functionName, [&]() { return Whiskers(R"( function (addr, function_id, pos) { @@ -1716,3 +1738,4 @@ size_t ABIFunctions::headSize(TypePointers const& _targetTypes) return headSize; } + diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 1e0cf7fa..c5443236 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -87,6 +87,23 @@ public: std::pair> requestedFunctions(); private: + struct EncodingOptions + { + /// Pad/signextend value types and bytes/string to multiples of 32 bytes. + bool padded = true; + /// Store arrays and structs in place without "data pointer" and do not store the length. + bool dynamicInplace = false; + /// Only for external function types: The value is a pair of address / function id instead + /// of a memory pointer to the compression representation. + bool encodeFunctionFromStack = false; + /// Encode storage pointers as storage pointers (we are targeting a library call). + bool encodeAsLibraryTypes = false; + + /// @returns a string to uniquely identify the encoding options for the encoding + /// function name. Skips everything that has its default value. + std::string toFunctionNameSuffix() const; + }; + /// @returns the name of the cleanup function for the given type and /// adds its implementation to the requested functions. /// @param _revertOnFailure if true, causes revert on invalid data, @@ -115,40 +132,39 @@ private: std::string abiEncodingFunction( Type const& _givenType, Type const& _targetType, - bool _encodeAsLibraryTypes, - bool _fromStack + EncodingOptions const& _options ); /// Part of @a abiEncodingFunction for array target type and given calldata array. std::string abiEncodingFunctionCalldataArray( Type const& _givenType, Type const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); /// Part of @a abiEncodingFunction for array target type and given memory array or /// a given storage array with one item per slot. std::string abiEncodingFunctionSimpleArray( ArrayType const& _givenType, ArrayType const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); std::string abiEncodingFunctionMemoryByteArray( ArrayType const& _givenType, ArrayType const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); /// Part of @a abiEncodingFunction for array target type and given storage array /// where multiple items are packed into the same storage slot. std::string abiEncodingFunctionCompactStorageArray( ArrayType const& _givenType, ArrayType const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); /// Part of @a abiEncodingFunction for struct types. std::string abiEncodingFunctionStruct( StructType const& _givenType, StructType const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); // @returns the name of the ABI encoding function with the given type @@ -157,14 +173,13 @@ private: std::string abiEncodingFunctionStringLiteral( Type const& _givenType, Type const& _targetType, - bool _encodeAsLibraryTypes + EncodingOptions const& _options ); std::string abiEncodingFunctionFunctionType( FunctionType const& _from, Type const& _to, - bool _encodeAsLibraryTypes, - bool _fromStack + EncodingOptions const& _options ); /// @returns the name of the ABI decoding function for the given type -- cgit From 43fb38a84ecd0f4c1ce9aba4a208c23ad656689b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 22 Jan 2019 09:03:19 +0100 Subject: Update changelog for release. --- Changelog.md | 2 +- docs/bugs_by_version.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 53097159..1238d4b5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.5.3 (unreleased) +### 0.5.3 (2019-01-22) Language Features: * Provide access to creation and runtime code of contracts via ``type(C).creationCode`` / ``type(C).runtimeCode``. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 438abbdd..e1581293 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -620,5 +620,9 @@ "0.5.2": { "bugs": [], "released": "2018-12-19" + }, + "0.5.3": { + "bugs": [], + "released": "2019-01-22" } } \ No newline at end of file -- cgit From faa66983aeeac03c63cc1c0871d258370279dc29 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Jan 2019 23:33:41 +0100 Subject: Use v0.5.0 tag of solc-js repository for external tests. --- test/externalTests.sh | 4 ++-- test/solcjsTests.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/externalTests.sh b/test/externalTests.sh index 16f9e55a..126b13b5 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -55,13 +55,13 @@ function test_truffle cd "$DIR" echo "Current commit hash: `git rev-parse HEAD`" npm install - # Replace solc package by master + # Replace solc package by v0.5.0 for d in node_modules node_modules/truffle/node_modules do ( cd $d rm -rf solc - git clone --depth 1 https://github.com/ethereum/solc-js.git solc + git clone --depth 1 -b v0.5.0 https://github.com/ethereum/solc-js.git solc cp "$SOLJSON" solc/ ) done diff --git a/test/solcjsTests.sh b/test/solcjsTests.sh index b9224862..ab263178 100755 --- a/test/solcjsTests.sh +++ b/test/solcjsTests.sh @@ -39,8 +39,8 @@ VERSION="$2" DIR=$(mktemp -d) ( - echo "Preparing solc-js..." - git clone --depth 1 https://github.com/ethereum/solc-js "$DIR" + echo "Preparing solc-js (0.5.0)..." + git clone --depth 1 --branch v0.5.0 https://github.com/ethereum/solc-js "$DIR" cd "$DIR" # disable "prepublish" script which downloads the latest version # (we will replace it anyway and it is often incorrectly cached -- cgit From 96fae0c2201caf33b134e40d770927ee60074fe6 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Sun, 20 Jan 2019 19:32:59 +0200 Subject: Move FAQ item about truncation checks Move warning Updates from feedback Link to security note and fix link rendering Move solution to security docs and turn warning to a warning --- docs/frequently-asked-questions.rst | 13 ------------- docs/security-considerations.rst | 6 ++++-- docs/types/value-types.rst | 5 +++++ 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 645789ce..00d9e043 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -89,19 +89,6 @@ In this example:: } } -What does the following strange check do in the Custom Token contract? -====================================================================== - -:: - - require((balanceOf[_to] + _value) >= balanceOf[_to]); - -Integers in Solidity (and most other machine-related programming languages) are restricted to a certain range. -For ``uint256``, this is ``0`` up to ``2**256 - 1``. If the result of some operation on those numbers -does not fit inside this range, it is truncated. These truncations can have -`serious consequences `_, so code like the one -above is necessary to avoid certain attacks. - More Questions? =============== diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index d83302a0..ebc39ad0 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -223,7 +223,7 @@ Now someone tricks you into sending ether to the address of this attack wallet: If your wallet had checked ``msg.sender`` for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking ``tx.origin``, it gets the original address that kicked off the transaction, which is still the owner address. The attack wallet instantly drains all your funds. - +.. _underflow-overflow: Two's Complement / Underflows / Overflows ========================================= @@ -241,9 +241,11 @@ more special edge cases for signed numbers. Try to use ``require`` to limit the size of inputs to a reasonable range and use the :ref:`SMT checker` to find potential overflows, or use a library like -`SafeMath` +`SafeMath `_ if you want all overflows to cause a revert. +Code such as ``require((balanceOf[_to] + _value) >= balanceOf[_to])`` can also help you check if values are what you expect. + Minor Details ============= diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 09db1423..b85863dd 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -39,6 +39,11 @@ Operators: * Shift operators: ``<<`` (left shift), ``>>`` (right shift) * Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) +.. warning:: + + Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``. + If the result of some operation on those numbers does not fit inside this range, it is truncated. These truncations can have + serious consequences that you should :ref:`be aware of and mitigate against`. Comparisons ^^^^^^^^^^^ -- cgit