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

This commit is contained in:
chriseth 2022-02-17 09:42:15 +01:00
commit 0801c48e11
99 changed files with 2494 additions and 230 deletions

View File

@ -214,6 +214,16 @@ defaults:
command: ./test/lsp.py ./build/solc/solc
- gitter_notify_failure_unless_pr
- steps_build: &steps_build
steps:
- checkout
- run: *run_build
- store_artifacts: *artifacts_solc
- store_artifacts: *artifact_solidity_upgrade
- store_artifacts: *artifact_yul_phaser
- persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
- steps_soltest_all: &steps_soltest_all
steps:
- checkout
@ -234,6 +244,14 @@ defaults:
- store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
- steps_install_dependencies_osx: &steps_install_dependencies_osx
steps:
- restore_cache:
keys:
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- attach_workspace:
at: .
# --------------------------------------------------------------------------
# Base Image Templates
@ -565,7 +583,7 @@ defaults:
project: uniswap
binary_type: native
nodejs_version: '16'
- job_native_test_prb_math: &job_native_test_prb_math
- job_native_test_ext_prb_math: &job_native_test_ext_prb_math
<<: *workflow_ubuntu2004_static
name: t_native_test_ext_prb_math
project: prb-math
@ -586,6 +604,15 @@ defaults:
nodejs_version: '14'
resource_class: medium
- job_b_ubu_asan_clang: &job_b_ubu_asan_clang
<<: *workflow_trigger_on_tags
name: b_ubu_asan_clang
cmake_options: -DSANITIZE=address
- job_b_ubu_ubsan_clang: &job_b_ubu_ubsan_clang
<<: *workflow_trigger_on_tags
name: b_ubu_ubsan_clang
cmake_options: -DSANITIZE=address
# -----------------------------------------------------------------------------------------------
jobs:
@ -731,14 +758,7 @@ jobs:
# this runs 2x faster on xlarge but takes 4x more resources (compared to medium).
# Enough other jobs depend on it that it's worth it though.
<<: *base_ubuntu2004_xlarge
steps:
- checkout
- run: *run_build
- store_artifacts: *artifacts_solc
- store_artifacts: *artifact_solidity_upgrade
- store_artifacts: *artifact_yul_phaser
- persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
<<: *steps_build
# x64 ASAN build, for testing for memory related bugs
b_ubu_asan: &b_ubu_asan
@ -748,12 +768,7 @@ jobs:
CMAKE_OPTIONS: -DSANITIZE=address
MAKEFLAGS: -j 3
CMAKE_BUILD_TYPE: Release
steps:
- checkout
- run: *run_build
- store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
<<: *steps_build
b_ubu_clang: &b_ubu_clang
<<: *base_ubuntu2004_clang_large
@ -762,42 +777,21 @@ jobs:
CC: clang
CXX: clang++
MAKEFLAGS: -j 10
steps:
- checkout
- run: *run_build
- store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
<<: *steps_build
b_ubu_asan_clang: &b_ubu_asan_clang
b_ubu_san_clang:
# This runs a bit faster on large and xlarge but on nightly efficiency matters more.
parameters:
cmake_options:
type: string
<<: *base_ubuntu2004_clang
environment:
TERM: xterm
CC: clang
CXX: clang++
CMAKE_OPTIONS: -DSANITIZE=address
MAKEFLAGS: -j 3
steps:
- checkout
- run: *run_build
- store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
b_ubu_ubsan_clang: &b_ubu_ubsan_clang
# This runs a bit faster on large and xlarge but on nightly efficiency matters more.
<<: *base_ubuntu2004_clang
environment:
CC: clang
CXX: clang++
CMAKE_OPTIONS: -DSANITIZE=undefined
MAKEFLAGS: -j 3
steps:
- checkout
- run: *run_build
- store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
CMAKE_OPTIONS: << parameters.cmake_options >>
<<: *steps_build
b_ubu_release: &b_ubu_release
<<: *b_ubu
@ -949,7 +943,7 @@ jobs:
- build/test/tools/solfuzzer
- gitter_notify_failure_unless_pr
t_osx_soltest:
t_osx_soltest: &t_osx_soltest
<<: *base_osx
environment:
EVM: << pipeline.parameters.evm-version >>
@ -957,11 +951,9 @@ jobs:
TERM: xterm
steps:
- checkout
- restore_cache:
keys:
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- attach_workspace:
at: .
- when:
condition: true
<<: *steps_install_dependencies_osx
- run: *run_soltest
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
@ -971,11 +963,9 @@ jobs:
<<: *base_osx
steps:
- checkout
- restore_cache:
keys:
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- attach_workspace:
at: .
- when:
condition: true
<<: *steps_install_dependencies_osx
- run: *run_cmdline_tests
- store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
@ -992,10 +982,10 @@ jobs:
command: |
scripts/ci/build_emscripten.sh
- store_artifacts:
path: emscripten_build/libsolc/soljson.js
path: upload/soljson.js
destination: soljson.js
- run: mkdir -p workspace
- run: cp emscripten_build/libsolc/soljson.js workspace/soljson.js
- run: cp upload/soljson.js workspace/soljson.js
- run: scripts/get_version.sh > workspace/version.txt
- persist_to_workspace:
root: workspace
@ -1108,6 +1098,7 @@ jobs:
parallelism: 20
environment:
EVM: << pipeline.parameters.evm-version >>
SOLTEST_FLAGS: --no-smt
<<: *steps_soltest
t_ubu_ubsan_clang_cli:
@ -1215,8 +1206,34 @@ jobs:
name: External <<parameters.project>> tests (native)
command: |
test/externalTests/<<parameters.project>>.sh native /tmp/workspace/solc/solc
- store_artifacts:
path: reports/externalTests/
# persist_to_workspace fails if the directory does not exist and the test script will create
# it only if it actually has benchmark results.
- run: mkdir -p reports/externalTests/
- persist_to_workspace:
root: .
paths:
- reports/externalTests/
- gitter_notify_failure_unless_pr
c_ext_benchmarks:
<<: *base_node_small
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Combine benchmark reports
command: cat reports/externalTests/benchmark-*.json | scripts/externalTests/merge_benchmarks.sh > reports/externalTests/all-benchmarks.json
- run:
name: Summarize reports
command: cat reports/externalTests/all-benchmarks.json | scripts/externalTests/summarize_benchmarks.sh > reports/externalTests/summarized-benchmarks.json
- store_artifacts:
path: reports/externalTests/all-benchmarks.json
- store_artifacts:
path: reports/externalTests/summarized-benchmarks.json
b_win: &b_win
<<: *base_win_powershell_large
steps:
@ -1464,9 +1481,27 @@ workflows:
- t_ems_ext: *job_native_test_ext_pool_together
- t_ems_ext: *job_native_test_ext_perpetual_pools
- t_ems_ext: *job_native_test_ext_uniswap
- t_ems_ext: *job_native_test_prb_math
- t_ems_ext: *job_native_test_ext_prb_math
- t_ems_ext: *job_native_test_ext_elementfi
- c_ext_benchmarks:
<<: *workflow_trigger_on_tags
requires:
- t_ems_compile_ext_colony
- t_native_compile_ext_gnosis
- t_native_test_ext_gnosis_v2
- t_native_test_ext_zeppelin
- t_native_test_ext_ens
- t_native_test_ext_trident
- t_native_test_ext_euler
- t_native_test_ext_yield_liquidator
- t_native_test_ext_bleeps
- t_native_test_ext_pool_together
- t_native_test_ext_perpetual_pools
- t_native_test_ext_uniswap
- t_native_test_ext_prb_math
- t_native_test_ext_elementfi
# Windows build and tests
- b_win: *workflow_trigger_on_tags
- b_win_release: *workflow_trigger_on_tags
@ -1519,13 +1554,13 @@ workflows:
# ASan build and tests
- b_ubu_asan: *workflow_trigger_on_tags
- b_ubu_asan_clang: *workflow_trigger_on_tags
- b_ubu_san_clang: *job_b_ubu_asan_clang
- t_ubu_asan_soltest: *workflow_ubuntu2004_asan
- t_ubu_asan_clang_soltest: *workflow_ubuntu2004_asan_clang
- t_ubu_asan_cli: *workflow_ubuntu2004_asan
# UBSan build and tests
- b_ubu_ubsan_clang: *workflow_trigger_on_tags
- b_ubu_san_clang: *job_b_ubu_ubsan_clang
- t_ubu_ubsan_clang_soltest: *workflow_ubuntu2004_ubsan_clang
- t_ubu_ubsan_clang_cli: *workflow_ubuntu2004_ubsan_clang

View File

@ -21,7 +21,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.8.12")
set(PROJECT_VERSION "0.8.13")
# OSX target needed in order to support std::visit
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)

View File

@ -9,30 +9,46 @@ Breaking changes:
* Commandline Interface: Assembler mode no longer enables all outputs by default.
### 0.8.12 (unreleased)
### 0.8.13 (unreleased)
Language Features:
* General: Support ``ContractName.functionName`` for ``abi.encodeCall``, in addition to external function pointers.
* General: Add equality-comparison operators for external function types.
Compiler Features:
Bugfixes:
### 0.8.12 (2022-02-16)
Language Features:
* General: Add equality-comparison operators for external function types.
* General: Support ``ContractName.functionName`` for ``abi.encodeCall``, in addition to external function pointers.
Compiler Features:
* Commandline Interface: Event and error signatures are also returned when using ``--hashes``.
* Yul Optimizer: Remove ``mstore`` and ``sstore`` operations if the slot already contains the same value.
* Yul: Emit immutable references for pure yul code when requested.
Bugfixes:
* Antlr Grammar: Allow builtin names in ``yulPath`` to support ``.address`` in function pointers.
* Code Generator: Fix ICE when accessing the members of external functions occupying more than two stack slots.
* Code Generator: Fix ICE when doing an explicit conversion from ``string calldata`` to ``bytes``.
* Code Generator: Fix internal error when accessing the members of external functions occupying more than two stack slots.
* Code Generator: Fix internal error when doing an explicit conversion from ``string calldata`` to ``bytes``.
* Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis.
* General: ``string.concat`` now properly takes strings as arguments and returns ``string memory``. It was accidentally introduced as a copy of ``bytes.concat`` before.
* Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the derived contract contains immutable variables.
* Inheritance: Consider functions in all ancestors during override analysis.
* IR Generator: Add missing cleanup during the conversion of fixed bytes types to smaller fixed bytes types.
* IR Generator: Add missing cleanup for indexed event arguments of value type.
* IR Generator: Fix internal error when copying reference types in calldata and storage to struct or array members in memory.
* IR Generator: Fix IR syntax error when copying storage arrays of structs containing functions.
* Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different.
* TypeChecker: Fix ICE when a constant variable declaration forward references a struct.
* Natspec: Fix internal error when overriding a struct getter with a Natspec-documented return value and the name in the struct is different.
* Type Checker: Fix internal error when a constant variable declaration forward references a struct.
* Yul EVM Code Transform: Improved stack shuffling in corner cases.
Solc-Js:
@ -40,6 +56,10 @@ Solc-Js:
* The code has been ported to TypeScript.
Build System:
* Emscripten builds store the embedded WebAssembly binary in LZ4 compressed format and transparently decompress on loading.
### 0.8.11 (2021-12-20)
Language Features:

View File

@ -142,8 +142,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM=1")
# Set webassembly build to synchronous loading.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM_ASYNC_COMPILATION=0")
# Output a single js file with the wasm binary embedded as base64 string.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s SINGLE_FILE=1")
# Allow new functions to be added to the wasm module via addFunction.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_TABLE_GROWTH=1")
# Disable warnings about not being pure asm.js due to memory growth.
@ -153,6 +151,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
# The major alternative compiler to GCC/Clang is Microsoft's Visual C++ compiler, only available on Windows.
elseif (DEFINED MSVC)
# Remove NDEBUG from RELWITHDEBINFO (to enable asserts)
# CMAKE_CXX_FLAGS_RELWITHDEBINFO for GCC/Clang does not include NDEBUG
string(REPLACE "/DNDEBUG" " " CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
add_compile_options(/MP) # enable parallel compilation
add_compile_options(/EHsc) # specify Exception Handling Model in msvc

View File

@ -137,6 +137,36 @@ evmc:
See the License for the specific language governing permissions and
limitations under the License.
mini-lz4:
The file scripts/ci/mini-lz4.js is derived from the emscripten adaptation of
node-lz4 and licensed under the following terms:
Copyright (c) 2012 Pierre Curto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
base64:
The file scripts/ci/base64DecToArr.js is derived from a code example
in the MDN Web Docs, which permits use under CC0 terms:
Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/
All other code is licensed under GPL version 3:

View File

@ -1552,6 +1552,10 @@
"bugs": [],
"released": "2021-12-20"
},
"0.8.12": {
"bugs": [],
"released": "2022-02-16"
},
"0.8.2": {
"bugs": [
"SignedImmutables",

View File

@ -86,6 +86,8 @@ Global Variables
to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)``
- ``bytes.concat(...) returns (bytes memory)``: :ref:`Concatenates variable number of
arguments to one byte array<bytes-concat>`
- ``string.concat(...) returns (string memory)``: :ref:`Concatenates variable number of
arguments to one string array<string-concat>`
- ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 <https://eips.ethereum.org/EIPS/eip-3198>`_ and `EIP-1559 <https://eips.ethereum.org/EIPS/eip-1559>`_)
- ``block.chainid`` (``uint``): current chain id
- ``block.coinbase`` (``address payable``): current block miner's address

View File

@ -94,6 +94,13 @@ dependencies (`evmone <https://github.com/ethereum/evmone/releases>`_,
On macOS some of the testing scripts expect GNU coreutils to be installed.
This can be easiest accomplished using Homebrew: ``brew install coreutils``.
On Windows systems make sure that you have a privilege to create symlinks,
otherwise several tests may fail.
Administrators should have that privilege, but you may also
`grant it to other users <https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links#policy-management>`_
or
`enable Developer Mode <https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development>`_.
Running the Tests
-----------------

View File

@ -130,9 +130,12 @@ of votes.
// Since `sender` is a reference, this
// modifies `voters[msg.sender].voted`
Voter storage delegate_ = voters[to];
// Voters cannot delegate to wallets that cannot vote.
require(delegate_.weight >= 1);
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

View File

@ -24,7 +24,7 @@ actual release. They are not meant for production use.
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).
We currently use a 0.x version number `to indicate this fast pace of change <https://semver.org/#spec-item-4>`_.
Remix
=====

View File

@ -56,7 +56,8 @@ as individual values.
of Solidity due to the fact that storage pointers can be passed to libraries. This means that
any change to the rules outlined in this section is considered a breaking change
of the language and due to its critical nature should be considered very carefully before
being executed.
being executed. In the event of such a breaking change, we would want to release a
compatibility mode in which the compiler would generate bytecode supporting the old layout.
Mappings and Dynamic Arrays

View File

@ -222,7 +222,7 @@ than the maximum value of ``uint`` (``2**256 - 1``). This is also true for the s
:ref:`Errors <errors>` allow you to provide more information to the caller about
why a condition or operation failed. Errors are used together with the
:ref:`revert statement <revert-statement>`. The revert statement unconditionally
:ref:`revert statement <revert-statement>`. The ``revert`` statement unconditionally
aborts and reverts all changes similar to the ``require`` function, but it also
allows you to provide the name of an error and additional data which will be supplied to the caller
(and eventually to the front-end application or block explorer) so that

View File

@ -27,6 +27,9 @@ it does include the supplied string in the :ref:`bytecode metadata <metadata>`.
If you do not want to specify a license or if the source code is
not open-source, please use the special value ``UNLICENSED``.
Note that ``UNLICENSED`` (no usage allowed, not present in SPDX license list)
is different from ``UNLICENSE`` (grants all rights to everyone).
Solidity follows `the npm recommendation <https://docs.npmjs.com/cli/v7/configuring-npm/package-json#license>`_.
Supplying this comment of course does not free you from other
obligations related to licensing like having to mention

View File

@ -82,6 +82,9 @@ Editor Integrations
* `Visual Studio Code extension <https://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_
Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler.
* `Solidity Visual Auditor extension <https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-visual-auditor>`_
Adds security centric syntax and semantic highlighting to Visual Studio Code.
Solidity Tools
==============

View File

@ -8,7 +8,7 @@ Style Guide
Introduction
************
This guide is intended to provide coding conventions for writing solidity code.
This guide is intended to provide coding conventions for writing Solidity code.
This guide should be thought of as an evolving document that will change over
time as useful conventions are found and old conventions are rendered obsolete.
@ -20,7 +20,7 @@ taken from python's
`pep8 style guide <https://www.python.org/dev/peps/pep-0008/>`_.
The goal of this guide is *not* to be the right way or the best way to write
solidity code. The goal of this guide is *consistency*. A quote from python's
Solidity code. The goal of this guide is *consistency*. A quote from python's
`pep8 <https://www.python.org/dev/peps/pep-0008/#a-foolish-consistency-is-the-hobgoblin-of-little-minds>`_
captures this concept well.
@ -28,7 +28,7 @@ captures this concept well.
A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important.
But most importantly: **know when to be inconsistent** -- sometimes the style guide just doesn't apply. When in doubt, use your best judgement. Look at other examples and decide what looks best. And don't hesitate to ask!
But most importantly: **know when to be inconsistent** -- sometimes the style guide just doesn't apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don't hesitate to ask!
***********
@ -51,7 +51,7 @@ Mixing tabs and spaces should be avoided.
Blank Lines
===========
Surround top level declarations in solidity source with two blank lines.
Surround top level declarations in Solidity source with two blank lines.
Yes:
@ -680,7 +680,7 @@ No:
}
For long function declarations, it is recommended to drop each argument onto
it's own line at the same indentation level as the function body. The closing
its own line at the same indentation level as the function body. The closing
parenthesis and opening bracket should be placed on their own line as well at
the same indentation level as the function declaration.
@ -933,7 +933,7 @@ Permissible:
function shortFunction() public { doSomething(); }
These guidelines for function declarations are intended to improve readability.
Authors should use their best judgement as this guide does not try to cover all
Authors should use their best judgment as this guide does not try to cover all
possible permutations for function declarations.
Mappings
@ -1023,7 +1023,7 @@ No:
* Operators with a higher priority than others can exclude surrounding
whitespace in order to denote precedence. This is meant to allow for
improved readability for complex statement. You should always use the same
improved readability for complex statements. You should always use the same
amount of whitespace on either side of an operator:
Yes:

View File

@ -150,7 +150,7 @@ length or index access.
Solidity does not have string manipulation functions, but there are
third-party string libraries. You can also compare two strings by their keccak256-hash using
``keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))`` and
concatenate two strings using ``bytes.concat(bytes(s1), bytes(s2))``.
concatenate two strings using ``string.concat(s1, s2)``.
You should use ``bytes`` over ``bytes1[]`` because it is cheaper,
since using ``bytes1[]`` in ``memory`` adds 31 padding bytes between the elements. Note that in ``storage``, the
@ -165,31 +165,40 @@ always use one of the value types ``bytes1`` to ``bytes32`` because they are muc
that you are accessing the low-level bytes of the UTF-8 representation,
and not the individual characters.
.. index:: ! bytes-concat
.. index:: ! bytes-concat, ! string-concat
.. _bytes-concat:
.. _string-concat:
``bytes.concat`` function
^^^^^^^^^^^^^^^^^^^^^^^^^
The functions ``bytes.concat`` and ``string.concat``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can concatenate a variable number of ``bytes`` or ``bytes1 ... bytes32`` using ``bytes.concat``.
You can concatenate an arbitrary number of ``string`` values using ``string.concat``.
The function returns a single ``string memory`` array that contains the contents of the arguments without padding.
If you want to use parameters of other types that are not implicitly convertible to ``string``, you need to convert them to ``string`` first.
Analogously, the ``bytes.concat`` function can concatenate an arbitrary number of ``bytes`` or ``bytes1 ... bytes32`` values.
The function returns a single ``bytes memory`` array that contains the contents of the arguments without padding.
If you want to use string parameters or other types, you need to convert them to ``bytes`` or ``bytes1``/.../``bytes32`` first.
If you want to use string parameters or other types that are not implicitly convertible to ``bytes``, you need to convert them to ``bytes`` or ``bytes1``/.../``bytes32`` first.
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
pragma solidity ^0.8.12;
contract C {
bytes s = "Storage";
function f(bytes calldata c, string memory m, bytes16 b) public view {
bytes memory a = bytes.concat(s, c, c[:2], "Literal", bytes(m), b);
assert((s.length + c.length + 2 + 7 + bytes(m).length + 16) == a.length);
string s = "Storage";
function f(bytes calldata bc, string memory sm, bytes16 b) public view {
string memory concat_string = string.concat(s, string(bc), "Literal", sm);
assert((bytes(s).length + bc.length + 7 + bytes(sm).length) == bytes(concat_string).length);
bytes memory concat_bytes = bytes.concat(bytes(s), bc, bc[:2], "Literal", bytes(sm), b);
assert((bytes(s).length + bc.length + 2 + 7 + bytes(sm).length + b.length) == concat_bytes.length);
}
}
If you call ``bytes.concat`` without arguments it will return an empty ``bytes`` array.
If you call ``string.concat`` or ``bytes.concat`` without arguments they return an empty array.
.. index:: ! array;allocating, new

View File

@ -154,6 +154,14 @@ Members of bytes
- ``bytes.concat(...) returns (bytes memory)``: :ref:`Concatenates variable number of bytes and bytes1, ..., bytes32 arguments to one byte array<bytes-concat>`
.. index:: string members
Members of string
-----------------
- ``string.concat(...) returns (string memory)``: :ref:`Concatenates variable number of string arguments to one string array<string-concat>`
.. index:: assert, revert, require
Error Handling

View File

@ -897,10 +897,11 @@ OverrideChecker::OverrideProxyBySignatureMultiSet const& OverrideChecker::inheri
if (var->isPublic())
functionsInBase.emplace(OverrideProxy{var});
for (OverrideProxy const& func: inheritedFunctions(*base))
functionsInBase.insert(func);
result += functionsInBase;
for (OverrideProxy const& func: inheritedFunctions(*base))
if (!functionsInBase.count(func))
result.insert(func);
}
m_inheritedFunctions[&_contract] = result;

View File

@ -2207,14 +2207,42 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
}
}
void TypeChecker::typeCheckStringConcatFunction(
FunctionCall const& _functionCall,
FunctionType const* _functionType
)
{
solAssert(_functionType);
solAssert(_functionType->kind() == FunctionType::Kind::StringConcat);
solAssert(_functionCall.names().empty());
typeCheckFunctionGeneralChecks(_functionCall, _functionType);
for (shared_ptr<Expression const> const& argument: _functionCall.arguments())
{
Type const* argumentType = type(*argument);
bool notConvertibleToString = !argumentType->isImplicitlyConvertibleTo(*TypeProvider::stringMemory());
if (notConvertibleToString)
m_errorReporter.typeError(
9977_error,
argument->location(),
"Invalid type for argument in the string.concat function call. "
"string type is required, but " +
argumentType->identifier() + " provided."
);
}
}
void TypeChecker::typeCheckBytesConcatFunction(
FunctionCall const& _functionCall,
FunctionType const* _functionType
)
{
solAssert(_functionType, "");
solAssert(_functionType->kind() == FunctionType::Kind::BytesConcat, "");
solAssert(_functionCall.names().empty(), "");
solAssert(_functionType);
solAssert(_functionType->kind() == FunctionType::Kind::BytesConcat);
solAssert(_functionCall.names().empty());
typeCheckFunctionGeneralChecks(_functionCall, _functionType);
@ -2651,6 +2679,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
returnTypes = functionType->returnParameterTypes();
break;
}
case FunctionType::Kind::StringConcat:
{
typeCheckStringConcatFunction(_functionCall, functionType);
returnTypes = functionType->returnParameterTypes();
break;
}
case FunctionType::Kind::Wrap:
case FunctionType::Kind::Unwrap:
{

View File

@ -113,6 +113,12 @@ private:
/// Performs checks specific to the ABI encode functions of type ABIEncodeCall
void typeCheckABIEncodeCallFunction(FunctionCall const& _functionCall);
/// Performs general checks and checks specific to string concat function call
void typeCheckStringConcatFunction(
FunctionCall const& _functionCall,
FunctionType const* _functionType
);
/// Performs general checks and checks specific to bytes concat function call
void typeCheckBytesConcatFunction(
FunctionCall const& _functionCall,

View File

@ -192,7 +192,7 @@ FunctionDefinition const* ContractDefinition::receiveFunction() const
return nullptr;
}
vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() const
vector<EventDefinition const*> const& ContractDefinition::definedInterfaceEvents() const
{
return m_interfaceEvents.init([&]{
set<string> eventsSeen;
@ -213,11 +213,20 @@ vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() cons
interfaceEvents.push_back(e);
}
}
return interfaceEvents;
});
}
vector<EventDefinition const*> const ContractDefinition::usedInterfaceEvents() const
{
solAssert(annotation().creationCallGraph.set(), "");
return convertContainer<std::vector<EventDefinition const*>>(
(*annotation().creationCallGraph)->emittedEvents +
(*annotation().deployedCallGraph)->emittedEvents
);
}
vector<ErrorDefinition const*> ContractDefinition::interfaceErrors(bool _requireCallGraph) const
{
set<ErrorDefinition const*, CompareByID> result;
@ -227,10 +236,9 @@ vector<ErrorDefinition const*> ContractDefinition::interfaceErrors(bool _require
if (_requireCallGraph)
solAssert(annotation().creationCallGraph.set(), "");
if (annotation().creationCallGraph.set())
{
result += (*annotation().creationCallGraph)->usedErrors;
result += (*annotation().deployedCallGraph)->usedErrors;
}
result +=
(*annotation().creationCallGraph)->usedErrors +
(*annotation().deployedCallGraph)->usedErrors;
return convertContainer<vector<ErrorDefinition const*>>(move(result));
}

View File

@ -519,7 +519,8 @@ public:
return ranges::subrange<decltype(b)>(b, e) | ranges::views::values;
}
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
std::vector<EventDefinition const*> const& interfaceEvents() const;
std::vector<EventDefinition const*> const& definedInterfaceEvents() const;
std::vector<EventDefinition const*> const usedInterfaceEvents() const;
/// @returns all errors defined in this contract or any base contract
/// and all errors referenced during execution.
/// @param _requireCallGraph if false, do not fail if the call graph has not been computed yet.

View File

@ -2932,6 +2932,7 @@ string FunctionType::richIdentifier() const
case Kind::ArrayPush: id += "arraypush"; break;
case Kind::ArrayPop: id += "arraypop"; break;
case Kind::BytesConcat: id += "bytesconcat"; break;
case Kind::StringConcat: id += "stringconcat"; break;
case Kind::ObjectCreation: id += "objectcreation"; break;
case Kind::Assert: id += "assert"; break;
case Kind::Require: id += "require"; break;
@ -3821,15 +3822,14 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
)
members.emplace_back("concat", TypeProvider::function(
TypePointers{},
TypePointers{TypeProvider::bytesMemory()},
TypePointers{arrayType->isString() ? TypeProvider::stringMemory() : TypeProvider::bytesMemory()},
strings{},
strings{string()},
FunctionType::Kind::BytesConcat,
strings{string{}},
arrayType->isString() ? FunctionType::Kind::StringConcat : FunctionType::Kind::BytesConcat,
StateMutability::Pure,
nullptr,
FunctionType::Options::withArbitraryParameters()
));
return members;
}

View File

@ -1228,6 +1228,7 @@ public:
ArrayPush, ///< .push() to a dynamically sized array in storage
ArrayPop, ///< .pop() from a dynamically sized array in storage
BytesConcat, ///< .concat() on bytes (type type)
StringConcat, ///< .concat() on string (type type)
ObjectCreation, ///< array creation using new
Assert, ///< assert()
Require, ///< require()

View File

@ -1101,6 +1101,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
ArrayUtils(m_context).popStorageArrayElement(*arrayType);
break;
}
case FunctionType::Kind::StringConcat:
case FunctionType::Kind::BytesConcat:
{
_functionCall.expression().accept(*this);
@ -1121,10 +1122,18 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
else
{
solAssert(!dynamic_cast<RationalNumberType const*>(argument->annotation().type), "");
if (function.kind() == FunctionType::Kind::StringConcat)
{
solAssert(argument->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()), "");
targetTypes.emplace_back(TypeProvider::stringMemory());
}
else if (function.kind() == FunctionType::Kind::BytesConcat)
{
solAssert(argument->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
targetTypes.emplace_back(TypeProvider::bytesMemory());
}
}
}
utils().fetchFreeMemoryPointer();
// stack: <arg1> <arg2> ... <argn> <free mem>
m_context << u256(32) << Instruction::ADD;

View File

@ -2475,18 +2475,26 @@ string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _
});
}
string YulUtilFunctions::bytesConcatFunction(vector<Type const*> const& _argumentTypes)
string YulUtilFunctions::bytesOrStringConcatFunction(
vector<Type const*> const& _argumentTypes,
FunctionType::Kind _functionTypeKind
)
{
string functionName = "bytes_concat";
solAssert(_functionTypeKind == FunctionType::Kind::BytesConcat || _functionTypeKind == FunctionType::Kind::StringConcat);
std::string functionName = (_functionTypeKind == FunctionType::Kind::StringConcat) ? "string_concat" : "bytes_concat";
size_t totalParams = 0;
vector<Type const*> targetTypes;
for (Type const* argumentType: _argumentTypes)
{
if (_functionTypeKind == FunctionType::Kind::StringConcat)
solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()));
else if (_functionTypeKind == FunctionType::Kind::BytesConcat)
solAssert(
argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) ||
argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32)),
""
argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32))
);
if (argumentType->category() == Type::Category::FixedBytes)
targetTypes.emplace_back(argumentType);
else if (
@ -2496,15 +2504,16 @@ string YulUtilFunctions::bytesConcatFunction(vector<Type const*> const& _argumen
targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast<unsigned>(literalType->value().size())));
else
{
solAssert(!dynamic_cast<RationalNumberType const*>(argumentType), "");
solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
targetTypes.emplace_back(TypeProvider::bytesMemory());
solAssert(!dynamic_cast<RationalNumberType const*>(argumentType));
targetTypes.emplace_back(
_functionTypeKind == FunctionType::Kind::StringConcat ?
TypeProvider::stringMemory() :
TypeProvider::bytesMemory()
);
}
totalParams += argumentType->sizeOnStack();
functionName += "_" + argumentType->identifier();
}
return m_functionCollector.createFunction(functionName, [&]() {
Whiskers templ(R"(
function <functionName>(<parameters>) -> outPtr {

View File

@ -312,9 +312,13 @@ public:
/// of the storage array into it.
std::string copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to);
/// @returns the name of a function that does concatenation of variadic number of bytes
/// or fixed bytes
std::string bytesConcatFunction(std::vector<Type const*> const& _argumentTypes);
/// @returns the name of a function that does concatenation of variadic number of
/// bytes if @a functionTypeKind is FunctionType::Kind::BytesConcat,
/// or of strings, if @a functionTypeKind is FunctionType::Kind::StringConcat.
std::string bytesOrStringConcatFunction(
std::vector<Type const*> const& _argumentTypes,
FunctionType::Kind _functionTypeKind
);
/// @returns the name of a function that performs index access for mappings.
/// @param _mappingType the type of the mapping

View File

@ -1389,6 +1389,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
}
break;
}
case FunctionType::Kind::StringConcat:
case FunctionType::Kind::BytesConcat:
{
TypePointers argumentTypes;
@ -1399,11 +1400,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
argumentVars += IRVariable(*argument).stackSlots();
}
define(IRVariable(_functionCall)) <<
m_utils.bytesConcatFunction(argumentTypes) <<
m_utils.bytesOrStringConcatFunction(argumentTypes, functionType->kind()) <<
"(" <<
joinHumanReadable(argumentVars) <<
")\n";
break;
}
case FunctionType::Kind::MetaType:

View File

@ -101,7 +101,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability());
abi.emplace(std::move(method));
}
for (auto const& it: _contractDef.interfaceEvents())
for (auto const& it: _contractDef.definedInterfaceEvents())
{
Json::Value event{Json::objectValue};
event["type"] = "event";

View File

@ -75,11 +75,14 @@
#include <libsolutil/IpfsHash.h>
#include <libsolutil/JSON.h>
#include <libsolutil/Algorithms.h>
#include <libsolutil/FunctionSelector.h>
#include <json/json.h>
#include <boost/algorithm/string/replace.hpp>
#include <range/v3/view/concat.hpp>
#include <utility>
#include <map>
#include <limits>
@ -1013,15 +1016,34 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
return _contract.devDocumentation.init([&]{ return Natspec::devDocumentation(*_contract.contract); });
}
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
Json::Value CompilerStack::interfaceSymbols(string const& _contractName) const
{
if (m_stackState < AnalysisPerformed)
solThrow(CompilerError, "Analysis was not successful.");
Json::Value methodIdentifiers(Json::objectValue);
Json::Value interfaceSymbols(Json::objectValue);
// Always have a methods object
interfaceSymbols["methods"] = Json::objectValue;
for (auto const& it: contractDefinition(_contractName).interfaceFunctions())
methodIdentifiers[it.second->externalSignature()] = it.first.hex();
return methodIdentifiers;
interfaceSymbols["methods"][it.second->externalSignature()] = it.first.hex();
for (ErrorDefinition const* error: contractDefinition(_contractName).interfaceErrors())
{
string signature = error->functionType(true)->externalSignature();
interfaceSymbols["errors"][signature] = toHex(toCompactBigEndian(selectorFromSignature32(signature), 4));
}
for (EventDefinition const* event: ranges::concat_view(
contractDefinition(_contractName).definedInterfaceEvents(),
contractDefinition(_contractName).usedInterfaceEvents()
))
if (!event->isAnonymous())
{
string signature = event->functionType(true)->externalSignature();
interfaceSymbols["events"][signature] = toHex(u256(h256::Arith(keccak256(signature))));
}
return interfaceSymbols;
}
bytes CompilerStack::cborMetadata(string const& _contractName, bool _forIR) const

View File

@ -327,8 +327,8 @@ public:
/// Prerequisite: Successful call to parse or compile.
Json::Value const& natspecDev(std::string const& _contractName) const;
/// @returns a JSON representing a map of method identifiers (hashes) to function names.
Json::Value methodIdentifiers(std::string const& _contractName) const;
/// @returns a JSON object with the three members ``methods``, ``events``, ``errors``. Each is a map, mapping identifiers (hashes) to function names.
Json::Value interfaceSymbols(std::string const& _contractName) const;
/// @returns the Contract Metadata matching the pipeline selected using the viaIR setting.
std::string const& metadata(std::string const& _contractName) const { return metadata(contract(_contractName)); }

View File

@ -116,7 +116,6 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so
for (auto const& prefix: prefixes)
{
boost::filesystem::path canonicalPath = normalizeCLIPathForVFS(prefix / strippedSourceUnitName, SymlinkResolution::Enabled);
if (boost::filesystem::exists(canonicalPath))
candidates.push_back(std::move(canonicalPath));
}
@ -124,7 +123,12 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so
auto pathToQuotedString = [](boost::filesystem::path const& _path){ return "\"" + _path.string() + "\""; };
if (candidates.empty())
return ReadCallback::Result{false, "File not found."};
return ReadCallback::Result{
false,
"File not found. Searched the following locations: " +
joinHumanReadable(prefixes | ranges::views::transform(pathToQuotedString), ", ") +
"."
};
if (candidates.size() >= 2)
return ReadCallback::Result{
@ -135,11 +139,13 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so
"."
};
FileSystemPathSet extraAllowedPaths = {m_basePath.empty() ? "." : m_basePath};
extraAllowedPaths += m_includePaths;
FileSystemPathSet allowedPaths =
m_allowedDirectories +
decltype(allowedPaths){m_basePath.empty() ? "." : m_basePath} +
m_includePaths;
bool isAllowed = false;
for (boost::filesystem::path const& allowedDir: m_allowedDirectories + extraAllowedPaths)
for (boost::filesystem::path const& allowedDir: allowedPaths)
if (isPathPrefix(normalizeCLIPathForVFS(allowedDir, SymlinkResolution::Enabled), candidates[0]))
{
isAllowed = true;
@ -147,7 +153,12 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so
}
if (!isAllowed)
return ReadCallback::Result{false, "File outside of allowed directories."};
return ReadCallback::Result{
false,
"File outside of allowed directories. The following are allowed: " +
joinHumanReadable(allowedPaths | ranges::views::transform(pathToQuotedString), ", ") +
"."
};
if (!boost::filesystem::is_regular_file(candidates[0]))
return ReadCallback::Result{false, "Not a valid file."};
@ -269,7 +280,8 @@ boost::filesystem::path FileReader::normalizeCLIPathForVFS(
if (!isUNCPath(normalizedPath))
{
boost::filesystem::path workingDirRootPath = canonicalWorkDir.root_path();
if (normalizedRootPath == workingDirRootPath)
// Ignore drive letter case on Windows (C:\ <=> c:\).
if (boost::filesystem::equivalent(normalizedRootPath, workingDirRootPath))
normalizedRootPath = "/";
}

View File

@ -78,7 +78,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
doc["methods"][it.second->externalSignature()]["notice"] = value;
}
for (auto const& event: _contractDef.interfaceEvents())
for (auto const& event: _contractDef.definedInterfaceEvents())
{
string value = extractDoc(event->annotation().docTags, "notice");
if (!value.empty())

View File

@ -1298,7 +1298,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesExperimental))
evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesExperimental))
evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName);
evmData["methodIdentifiers"] = compilerStack.interfaceSymbols(contractName)["methods"];
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesExperimental))
evmData["gasEstimates"] = compilerStack.gasEstimates(contractName);

View File

@ -262,6 +262,11 @@ private:
if (ops.sourceMultiplicity(ops.sourceSize() - 1 - swapDepth) < 0)
{
ops.swap(swapDepth);
if (ops.targetIsArbitrary(sourceTop))
// Usually we keep a slot that is to-be-removed, if the current top is arbitrary.
// However, since we are in a stack-too-deep situation, pop it immediately
// to compress the stack (we can always push back junk in the end).
ops.pop();
return true;
}
// Otherwise we rely on stack compression or stack-to-memory.
@ -321,14 +326,44 @@ private:
yulAssert(ops.sourceMultiplicity(i) == 0 && (ops.targetIsArbitrary(i) || ops.targetMultiplicity(i) == 0), "");
yulAssert(ops.isCompatible(sourceTop, sourceTop), "");
auto swappableOffsets = ranges::views::iota(size > 17 ? size - 17 : 0u, size);
// If we find a lower slot that is out of position, but also compatible with the top, swap that up.
for (size_t offset: swappableOffsets)
if (!ops.isCompatible(offset, offset) && ops.isCompatible(sourceTop, offset))
{
ops.swap(size - offset - 1);
return true;
}
// Swap up any reachable slot that is still out of position.
for (size_t offset: swappableOffsets)
if (!ops.isCompatible(offset, offset) && !ops.sourceIsSame(offset, sourceTop))
{
ops.swap(size - offset - 1);
return true;
}
// We are in a stack-too-deep situation and try to reduce the stack size.
// If the current top is merely kept since the target slot is arbitrary, pop it.
if (ops.targetIsArbitrary(sourceTop) && ops.sourceMultiplicity(sourceTop) <= 0)
{
ops.pop();
return true;
}
// If any reachable slot is merely kept, since the target slot is arbitrary, swap it up and pop it.
for (size_t offset: swappableOffsets)
if (ops.targetIsArbitrary(offset) && ops.sourceMultiplicity(offset) <= 0)
{
ops.swap(size - offset - 1);
ops.pop();
return true;
}
// We cannot avoid a stack-too-deep error. Repeat the above without restricting to reachable slots.
for (size_t offset: ranges::views::iota(0u, size))
if (!ops.isCompatible(offset, offset) && ops.isCompatible(sourceTop, offset))
{
ops.swap(size - offset - 1);
return true;
}
// Swap up any slot that is still out of position.
for (size_t offset: ranges::views::iota(0u, size))
if (!ops.isCompatible(offset, offset) && !ops.sourceIsSame(offset, sourceTop))
{

View File

@ -0,0 +1,46 @@
function base64DecToArr (sBase64) {
/*\
|*|
|*| Base64 / binary data / UTF-8 strings utilities
|*|
|*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
|*|
\*/
/* Array of bytes to Base64 string decoding */
function b64ToUint6 (nChr) {
return nChr > 64 && nChr < 91 ?
nChr - 65
: nChr > 96 && nChr < 123 ?
nChr - 71
: nChr > 47 && nChr < 58 ?
nChr + 4
: nChr === 43 ?
62
: nChr === 47 ?
63
:
0;
}
var
nInLen = sBase64.length,
nOutLen = nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64ToUint6(sBase64.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
if (nMod4 === 3 || nInLen - nInIdx === 1) {
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
}
nUint24 = 0;
}
}
return taBytes;
}

View File

@ -40,6 +40,8 @@ else
BUILD_DIR="$1"
fi
apt-get update && apt-get install lz4
WORKSPACE=/root/project
cd $WORKSPACE
@ -71,8 +73,8 @@ make soljson
cd ..
mkdir -p upload
cp "$BUILD_DIR/libsolc/soljson.js" upload/
cp "$BUILD_DIR/libsolc/soljson.js" ./
scripts/ci/pack_soljson.sh "$BUILD_DIR/libsolc/soljson.js" "$BUILD_DIR/libsolc/soljson.wasm" upload/soljson.js
cp upload/soljson.js ./
OUTPUT_SIZE=$(ls -la soljson.js)

116
scripts/ci/mini-lz4.js Normal file
View File

@ -0,0 +1,116 @@
function uncompress(source, uncompressedSize) {
/*
based off https://github.com/emscripten-core/emscripten/blob/main/third_party/mini-lz4.js
The license only applies to the body of this function (``uncompress``).
====
MiniLZ4: Minimal LZ4 block decoding and encoding.
based off of node-lz4, https://github.com/pierrec/node-lz4
====
Copyright (c) 2012 Pierre Curto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
====
changes have the same license
*/
/**
* Decode a block. Assumptions: input contains all sequences of a
* chunk, output is large enough to receive the decoded data.
* If the output buffer is too small, an error will be thrown.
* If the returned value is negative, an error occurred at the returned offset.
*
* @param {ArrayBufferView} input input data
* @param {ArrayBufferView} output output data
* @param {number=} sIdx
* @param {number=} eIdx
* @return {number} number of decoded bytes
* @private
*/
function uncompressBlock (input, output, sIdx, eIdx) {
sIdx = sIdx || 0
eIdx = eIdx || (input.length - sIdx)
// Process each sequence in the incoming data
for (var i = sIdx, n = eIdx, j = 0; i < n;) {
var token = input[i++]
// Literals
var literals_length = (token >> 4)
if (literals_length > 0) {
// length of literals
var l = literals_length + 240
while (l === 255) {
l = input[i++]
literals_length += l
}
// Copy the literals
var end = i + literals_length
while (i < end) output[j++] = input[i++]
// End of buffer?
if (i === n) return j
}
// Match copy
// 2 bytes offset (little endian)
var offset = input[i++] | (input[i++] << 8)
// XXX 0 is an invalid offset value
if (offset === 0) return j
if (offset > j) return -(i-2)
// length of match copy
var match_length = (token & 0xf)
var l = match_length + 240
while (l === 255) {
l = input[i++]
match_length += l
}
// Copy the match
var pos = j - offset // position of the match copy in the current output
var end = j + match_length + 4 // minmatch = 4
while (j < end) output[j++] = output[pos++]
}
return j
}
var result = new ArrayBuffer(uncompressedSize);
var sourceIndex = 0;
var destIndex = 0;
var blockSize;
while((blockSize = (source[sourceIndex] | (source[sourceIndex + 1] << 8) | (source[sourceIndex + 2] << 16) | (source[sourceIndex + 3] << 24))) > 0)
{
sourceIndex += 4;
if (blockSize & 0x80000000)
{
blockSize &= 0x7FFFFFFFF;
for (var i = 0; i < blockSize; i++) {
result[destIndex++] = source[sourceIndex++];
}
}
else
{
destIndex += uncompressBlock(source, new Uint8Array(result, destIndex, uncompressedSize - destIndex), sourceIndex, sourceIndex + blockSize);
sourceIndex += blockSize;
}
}
return new Uint8Array(result, 0, uncompressedSize);
}

37
scripts/ci/pack_soljson.sh Executable file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(realpath "$(dirname "$0")")"
soljson_js="$1"
soljson_wasm="$2"
soljson_wasm_size=$(wc -c "${soljson_wasm}" | cut -d ' ' -f 1)
output="$3"
(( $# == 3 )) || { >&2 echo "Usage: $0 soljson.js soljson.wasm packed_soljson.js"; exit 1; }
# If this changes in an emscripten update, it's probably nothing to worry about,
# but we should double-check when it happens and adjust the tail command below.
[[ $(head -c 5 "${soljson_js}") == "null;" ]] || { >&2 echo 'Expected soljson.js to start with "null;"'; exit 1; }
echo "Packing $soljson_js and $soljson_wasm to $output."
(
echo -n 'var Module = Module || {}; Module["wasmBinary"] = '
echo -n '(function(source, uncompressedSize) {'
# Note that base64DecToArr assumes no trailing equals signs.
cpp "${script_dir}/base64DecToArr.js" | grep -v "^#.*"
# Note that mini-lz4.js assumes no file header and no frame crc checksums.
cpp "${script_dir}/mini-lz4.js" | grep -v "^#.*"
echo 'return uncompress(base64DecToArr(source), uncompressedSize);})('
echo -n '"'
# We fix lz4 format settings, remove the 8 bytes file header and remove the trailing equals signs of the base64 encoding.
lz4c --no-frame-crc --best --favor-decSpeed "${soljson_wasm}" - | tail -c +8 | base64 -w 0 | sed 's/[^A-Za-z0-9\+\/]//g'
echo '",'
echo -n "${soljson_wasm_size});"
# Remove "null;" from the js wrapper.
tail -c +6 "${soljson_js}"
) > "$output"
echo "Testing $output."
echo "process.stdout.write(require('$(realpath "${output}")').wasmBinary)" | node | cmp "${soljson_wasm}" && echo "Binaries match."
# Allow the wasm binary to be garbage collected after compilation.
echo 'Module["wasmBinary"] = undefined;' >> "${output}"

View File

@ -0,0 +1,60 @@
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
# Reads multiple individual benchmark reports produced by scripts from
# test/externalTests/ from standard input and creates a combined report.
#
# Usage:
# <script name>.sh < <CONCATENATED_REPORTS>
#
# CONCATENATED_REPORTS: JSON report files concatenated into a single stream (e.g. using cat).
#
# Example:
# cat reports/externalTests/benchmark-*.json | <script name>.sh
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2021 solidity contributors.
#------------------------------------------------------------------------------
set -euo pipefail
# We expect a series of dicts of the form {"<project>": {"<preset>": {...}}}.
# Unfortunately jq's built-in `add` filter can't handle nested dicts and
# would just overwrite values sharing a project name instead of merging them.
# This is done by first grouping the dicts into an array of the form
# [
# [{"key": "<project1>", "value": {"<preset1>": {...}}}, {"key": "<project1>", "value": {"<preset2>": {...}}, ...],
# [{"key": "<project2>", "value": {"<preset1>": {...}}}, {"key": "<project2>", "value": {"<preset2>": {...}}, ...],
# ...
# ]
# and then using reduce() on each group sharing the same project name to convert it into a
# dict having preset names as keys.
jq --slurp --indent 4 --sort-keys '
map(to_entries[]) |
group_by(.key) |
map({
(.[0].key): (
reduce (.[].value | to_entries[]) as {$key, $value} (
{}; . + {
($key): $value
}
)
)
}) |
add
'

View File

@ -0,0 +1,269 @@
#!/usr/bin/env python3
# coding=utf-8
from dataclasses import asdict, dataclass, field
from typing import Dict, Optional, Tuple
import json
import re
import sys
REPORT_HEADER_REGEX = re.compile(r'''
^[|\s]+ Solc[ ]version:\s*(?P<solc_version>[\w\d.]+)
[|\s]+ Optimizer[ ]enabled:\s*(?P<optimize>[\w]+)
[|\s]+ Runs:\s*(?P<runs>[\d]+)
[|\s]+ Block[ ]limit:\s*(?P<block_limit>[\d]+)\s*gas
[|\s]+$
''', re.VERBOSE)
METHOD_HEADER_REGEX = re.compile(r'^[|\s]+Methods[|\s]+$')
METHOD_COLUMN_HEADERS_REGEX = re.compile(r'''
^[|\s]+ Contract
[|\s]+ Method
[|\s]+ Min
[|\s]+ Max
[|\s]+ Avg
[|\s]+ \#[ ]calls
[|\s]+ \w+[ ]\(avg\)
[|\s]+$
''', re.VERBOSE)
METHOD_ROW_REGEX = re.compile(r'''
^[|\s]+ (?P<contract>[^|]+)
[|\s]+ (?P<method>[^|]+)
[|\s]+ (?P<min>[^|]+)
[|\s]+ (?P<max>[^|]+)
[|\s]+ (?P<avg>[^|]+)
[|\s]+ (?P<call_count>[^|]+)
[|\s]+ (?P<eur_avg>[^|]+)
[|\s]+$
''', re.VERBOSE)
FRAME_REGEX = re.compile(r'^[-|\s]+$')
DEPLOYMENT_HEADER_REGEX = re.compile(r'^[|\s]+Deployments[|\s]+% of limit[|\s]+$')
DEPLOYMENT_ROW_REGEX = re.compile(r'''
^[|\s]+ (?P<contract>[^|]+)
[|\s]+ (?P<min>[^|]+)
[|\s]+ (?P<max>[^|]+)
[|\s]+ (?P<avg>[^|]+)
[|\s]+ (?P<percent_of_limit>[^|]+)\s*%
[|\s]+ (?P<eur_avg>[^|]+)
[|\s]+$
''', re.VERBOSE)
class ReportError(Exception):
pass
class ReportValidationError(ReportError):
pass
class ReportParsingError(Exception):
def __init__(self, message: str, line: str, line_number: int):
# pylint: disable=useless-super-delegation # It's not useless, it adds type annotations.
super().__init__(message, line, line_number)
def __str__(self):
return f"Parsing error on line {self.args[2] + 1}: {self.args[0]}\n{self.args[1]}"
@dataclass(frozen=True)
class MethodGasReport:
min_gas: int
max_gas: int
avg_gas: int
call_count: int
total_gas: int = field(init=False)
def __post_init__(self):
object.__setattr__(self, 'total_gas', self.avg_gas * self.call_count)
@dataclass(frozen=True)
class ContractGasReport:
min_deployment_gas: Optional[int]
max_deployment_gas: Optional[int]
avg_deployment_gas: Optional[int]
methods: Optional[Dict[str, MethodGasReport]]
total_method_gas: int = field(init=False, default=0)
def __post_init__(self):
if self.methods is not None:
object.__setattr__(self, 'total_method_gas', sum(method.total_gas for method in self.methods.values()))
@dataclass(frozen=True)
class GasReport:
solc_version: str
optimize: bool
runs: int
block_limit: int
contracts: Dict[str, ContractGasReport]
total_method_gas: int = field(init=False)
total_deployment_gas: int = field(init=False)
def __post_init__(self):
object.__setattr__(self, 'total_method_gas', sum(
total_method_gas
for total_method_gas in (contract.total_method_gas for contract in self.contracts.values())
if total_method_gas is not None
))
object.__setattr__(self, 'total_deployment_gas', sum(
contract.avg_deployment_gas
for contract in self.contracts.values()
if contract.avg_deployment_gas is not None
))
def to_json(self):
return json.dumps(asdict(self), indent=4, sort_keys=True)
def parse_bool(input_string: str) -> bool:
if input_string == 'true':
return True
elif input_string == 'false':
return True
else:
raise ValueError(f"Invalid boolean value: '{input_string}'")
def parse_optional_int(input_string: str, default: Optional[int] = None) -> Optional[int]:
if input_string.strip() == '-':
return default
return int(input_string)
def parse_report_header(line: str) -> Optional[dict]:
match = REPORT_HEADER_REGEX.match(line)
if match is None:
return None
return {
'solc_version': match.group('solc_version'),
'optimize': parse_bool(match.group('optimize')),
'runs': int(match.group('runs')),
'block_limit': int(match.group('block_limit')),
}
def parse_method_row(line: str, line_number: int) -> Optional[Tuple[str, str, MethodGasReport]]:
match = METHOD_ROW_REGEX.match(line)
if match is None:
raise ReportParsingError("Expected a table row with method details.", line, line_number)
avg_gas = parse_optional_int(match['avg'])
call_count = int(match['call_count'])
if avg_gas is None and call_count == 0:
# No calls, no gas values. Uninteresting. Skip the row.
return None
return (
match['contract'].strip(),
match['method'].strip(),
MethodGasReport(
min_gas=parse_optional_int(match['min'], avg_gas),
max_gas=parse_optional_int(match['max'], avg_gas),
avg_gas=avg_gas,
call_count=call_count,
)
)
def parse_deployment_row(line: str, line_number: int) -> Tuple[str, int, int, int]:
match = DEPLOYMENT_ROW_REGEX.match(line)
if match is None:
raise ReportParsingError("Expected a table row with deployment details.", line, line_number)
return (
match['contract'].strip(),
parse_optional_int(match['min'].strip()),
parse_optional_int(match['max'].strip()),
int(match['avg'].strip()),
)
def preprocess_unicode_frames(input_string: str) -> str:
# The report has a mix of normal pipe chars and its unicode variant.
# Let's just replace all frame chars with normal pipes for easier parsing.
return input_string.replace('\u2502', '|').replace('·', '|')
def parse_report(rst_report: str) -> GasReport:
report_params = None
methods_by_contract = {}
deployment_costs = {}
expected_row_type = None
for line_number, line in enumerate(preprocess_unicode_frames(rst_report).splitlines()):
try:
if (
line.strip() == "" or
FRAME_REGEX.match(line) is not None or
METHOD_COLUMN_HEADERS_REGEX.match(line) is not None
):
continue
if METHOD_HEADER_REGEX.match(line) is not None:
expected_row_type = 'method'
continue
if DEPLOYMENT_HEADER_REGEX.match(line) is not None:
expected_row_type = 'deployment'
continue
new_report_params = parse_report_header(line)
if new_report_params is not None:
if report_params is not None:
raise ReportParsingError("Duplicate report header.", line, line_number)
report_params = new_report_params
continue
if expected_row_type == 'method':
parsed_row = parse_method_row(line, line_number)
if parsed_row is None:
continue
(contract, method, method_report) = parsed_row
if contract not in methods_by_contract:
methods_by_contract[contract] = {}
if method in methods_by_contract[contract]:
# Report must be generated with full signatures for method names to be unambiguous.
raise ReportParsingError(f"Duplicate method row for '{contract}.{method}'.", line, line_number)
methods_by_contract[contract][method] = method_report
elif expected_row_type == 'deployment':
(contract, min_gas, max_gas, avg_gas) = parse_deployment_row(line, line_number)
if contract in deployment_costs:
raise ReportParsingError(f"Duplicate contract deployment row for '{contract}'.", line, line_number)
deployment_costs[contract] = (min_gas, max_gas, avg_gas)
else:
assert expected_row_type is None
raise ReportParsingError("Found data row without a section header.", line, line_number)
except ValueError as error:
raise ReportParsingError(error.args[0], line, line_number) from error
if report_params is None:
raise ReportValidationError("Report header not found.")
report_params['contracts'] = {
contract: ContractGasReport(
min_deployment_gas=deployment_costs.get(contract, (None, None, None))[0],
max_deployment_gas=deployment_costs.get(contract, (None, None, None))[1],
avg_deployment_gas=deployment_costs.get(contract, (None, None, None))[2],
methods=methods_by_contract.get(contract),
)
for contract in methods_by_contract.keys() | deployment_costs.keys()
}
return GasReport(**report_params)
if __name__ == "__main__":
try:
report = parse_report(sys.stdin.read())
print(report.to_json())
except ReportError as exception:
print(f"{exception}", file=sys.stderr)
sys.exit(1)

View File

@ -0,0 +1,53 @@
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
# Reads a combined benchmark report from standard input and outputs an abbreviated
# report containing only totals. Can handle individual reports coming directly
# from scripts in test/externalTests/ as well as combined report from merge_benchmarks.sh.
#
# Usage:
# <script name>.sh < <CONCATENATED_REPORTS>
#
# CONCATENATED_REPORTS: JSON report files concatenated into a single stream (e.g. using cat).
#
# Example:
# cat reports/externalTests/benchmark-*.json | <script name>.sh
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2021 solidity contributors.
#------------------------------------------------------------------------------
set -euo pipefail
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
# Iterates over presets in a dict of the form {"<project>": {"<preset>": {...}}} and for each
# one preserves only the few keys with totals that we want to see in the summary.
exec "${REPO_ROOT}/scripts/externalTests/merge_benchmarks.sh" | jq --indent 4 --sort-keys '
with_entries({
key: .key,
value: .value | with_entries({
key: .key,
value: {
bytecode_size: .value.total_bytecode_size,
method_gas: .value.gas.total_method_gas,
deployment_gas: .value.gas.total_deployment_gas,
version: .value.project.version
}
})
})
'

View File

@ -270,15 +270,29 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract)
if (!m_options.compiler.outputs.signatureHashes)
return;
Json::Value methodIdentifiers = m_compiler->methodIdentifiers(_contract);
string out;
for (auto const& name: methodIdentifiers.getMemberNames())
out += methodIdentifiers[name].asString() + ": " + name + "\n";
Json::Value interfaceSymbols = m_compiler->interfaceSymbols(_contract);
string out = "Function signatures:\n";
for (auto const& name: interfaceSymbols["methods"].getMemberNames())
out += interfaceSymbols["methods"][name].asString() + ": " + name + "\n";
if (interfaceSymbols.isMember("errors"))
{
out += "\nError signatures:\n";
for (auto const& name: interfaceSymbols["errors"].getMemberNames())
out += interfaceSymbols["errors"][name].asString() + ": " + name + "\n";
}
if (interfaceSymbols.isMember("events"))
{
out += "\nEvent signatures:\n";
for (auto const& name: interfaceSymbols["events"].getMemberNames())
out += interfaceSymbols["events"][name].asString() + ": " + name + "\n";
}
if (!m_options.output.dir.empty())
createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out);
else
sout() << "Function signatures:" << endl << out;
sout() << out;
}
void CommandLineInterface::handleMetadata(string const& _contract)
@ -822,7 +836,7 @@ void CommandLineInterface::handleCombinedJSON()
m_compiler->runtimeObject(contractName).functionDebugData
);
if (m_options.compiler.combinedJsonRequests->signatureHashes)
contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName);
contractData[g_strSignatureHashes] = m_compiler->interfaceSymbols(contractName)["methods"];
if (m_options.compiler.combinedJsonRequests->natspecDev)
contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName);
if (m_options.compiler.combinedJsonRequests->natspecUser)

View File

@ -147,6 +147,7 @@ set(libyul_sources
libyul/Parser.cpp
libyul/StackLayoutGeneratorTest.cpp
libyul/StackLayoutGeneratorTest.h
libyul/StackShufflingTest.cpp
libyul/SyntaxTest.h
libyul/SyntaxTest.cpp
libyul/YulInterpreterTest.cpp

View File

@ -81,5 +81,6 @@ bool solidity::test::createSymlinkIfSupportedByFilesystem(
BOOST_THROW_EXCEPTION(runtime_error(
"Failed to create a symbolic link: \"" + _linkName.string() + "\""
" -> " + _targetPath.string() + "\"."
" " + symlinkCreationError.message() + "."
));
}

View File

@ -57,7 +57,7 @@ void removeTestSuite(std::string const& _name)
{
master_test_suite_t& master = framework::master_test_suite();
auto id = master.get(_name);
assert(id != INV_TEST_UNIT_ID);
soltestAssert(id != INV_TEST_UNIT_ID, "Removing non-existent test suite!");
master.remove(id);
}
@ -279,7 +279,6 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
"ABIDecoderTest",
"ABIEncoderTest",
"SolidityAuctionRegistrar",
"SolidityFixedFeeRegistrar",
"SolidityWallet",
"GasMeterTests",
"GasCostTests",

View File

@ -0,0 +1 @@
--hashes

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
error fileLevelError(uint z);
library L {
event libraryEvent(uint r);
error libraryError(uint r);
error libraryErrorUnused(uint u);
event libraryEventUnused(uint u);
}
contract C {
struct S { uint x; }
event ev(uint y);
event anon_ev(uint y) anonymous;
error err(uint z, uint w);
function f(S memory s) public {
emit L.libraryEvent(3);
if (s.x > 1)
revert fileLevelError(3);
else
revert L.libraryError(4);
}
}

View File

@ -0,0 +1,24 @@
======= hashes/input.sol:C =======
Function signatures:
3fc03eeb: f((uint256))
Error signatures:
619a0bb7: err(uint256,uint256)
82b5f64f: fileLevelError(uint256)
8c41f45c: libraryError(uint256)
Event signatures:
2d4dd5fe18ada5a020a9f5591539a8dc3010a5c074ba6a70e1c956659f02786a: ev(uint256)
81f3fb02f88d32d3bb08c80c9a622ca3b3223292f131c6ad049811f9a8a606dc: libraryEvent(uint256)
======= hashes/input.sol:L =======
Function signatures:
Error signatures:
8c41f45c: libraryError(uint256)
c61c03f5: libraryErrorUnused(uint256)
Event signatures:
81f3fb02f88d32d3bb08c80c9a622ca3b3223292f131c6ad049811f9a8a606dc: libraryEvent(uint256)
0a994ad3600197f16ffe1ea1101caea3174efe5ebd9ba9a75d6d5524c5de28cd: libraryEventUnused(uint256)

View File

@ -0,0 +1,17 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { }"
}
},
"settings":
{
"outputSelection":
{
"*": { "*": ["evm.methodIdentifiers"] }
}
}
}

View File

@ -0,0 +1 @@
{"contracts":{"A":{"C":{"evm":{"methodIdentifiers":{}}}}},"sources":{"A":{"id":0}}}

View File

@ -3,8 +3,8 @@
[
{
"component": "general",
"formattedMessage": "Cannot import url (\"in.yul\"): File not found.",
"message": "Cannot import url (\"in.yul\"): File not found.",
"formattedMessage": "Cannot import url (\"in.yul\"): File not found. Searched the following locations: \"\".",
"message": "Cannot import url (\"in.yul\"): File not found. Searched the following locations: \"\".",
"severity": "error",
"type": "IOError"
},

View File

@ -28,26 +28,26 @@
set -e
REPO_ROOT="$(dirname "$0")"
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/..")
verify_input "$@"
printTask "Running external tests..."
"$REPO_ROOT/externalTests/zeppelin.sh" "$@"
"$REPO_ROOT/externalTests/gnosis.sh" "$@"
"$REPO_ROOT/externalTests/gnosis-v2.sh" "$@"
"$REPO_ROOT/externalTests/colony.sh" "$@"
"$REPO_ROOT/externalTests/ens.sh" "$@"
"$REPO_ROOT/externalTests/trident.sh" "$@"
"$REPO_ROOT/externalTests/euler.sh" "$@"
"$REPO_ROOT/externalTests/yield-liquidator.sh" "$@"
"$REPO_ROOT/externalTests/bleeps.sh" "$@"
"$REPO_ROOT/externalTests/pool-together.sh" "$@"
"$REPO_ROOT/externalTests/perpetual-pools.sh" "$@"
"$REPO_ROOT/externalTests/uniswap.sh" "$@"
"$REPO_ROOT/externalTests/prb-math.sh" "$@"
"$REPO_ROOT/externalTests/elementfi.sh" "$@"
"{$REPO_ROOT}/test/externalTests/zeppelin.sh" "$@"
"{$REPO_ROOT}/test/externalTests/gnosis.sh" "$@"
"{$REPO_ROOT}/test/externalTests/gnosis-v2.sh" "$@"
"{$REPO_ROOT}/test/externalTests/colony.sh" "$@"
"{$REPO_ROOT}/test/externalTests/ens.sh" "$@"
"{$REPO_ROOT}/test/externalTests/trident.sh" "$@"
"{$REPO_ROOT}/test/externalTests/euler.sh" "$@"
"{$REPO_ROOT}/test/externalTests/yield-liquidator.sh" "$@"
"{$REPO_ROOT}/test/externalTests/bleeps.sh" "$@"
"{$REPO_ROOT}/test/externalTests/pool-together.sh" "$@"
"{$REPO_ROOT}/test/externalTests/perpetual-pools.sh" "$@"
"{$REPO_ROOT}/test/externalTests/uniswap.sh" "$@"
"{$REPO_ROOT}/test/externalTests/prb-math.sh" "$@"
"{$REPO_ROOT}/test/externalTests/elementfi.sh" "$@"

View File

@ -24,13 +24,16 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
SELECTED_PRESETS="$3"
function compile_fn { npm run compile; }
function test_fn { npm run test; }
# NOTE: `npm run test` runs `mocha` which seems to disable the gas reporter.
function test_fn { HARDHAT_DEPLOY_FIXTURE=true npx --no hardhat --no-compile test; }
function bleeps_test
{
@ -87,6 +90,7 @@ function bleeps_test
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat bleeps "$repo" "$preset"
done
popd

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -73,6 +75,7 @@ function colony_test
for preset in $SELECTED_PRESETS; do
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
store_benchmark_report truffle colony "$repo" "$preset"
done
}

View File

@ -20,7 +20,7 @@
#------------------------------------------------------------------------------
set -e
# Requires "${REPO_ROOT}/scripts/common.sh" to be included before.
# Requires $REPO_ROOT to be defined and "${REPO_ROOT}/scripts/common.sh" to be included before.
CURRENT_EVM_VERSION=london
@ -71,15 +71,23 @@ function setup_solc
local binary_path="$3"
local solcjs_branch="${4:-master}"
local install_dir="${5:-solc/}"
local solcjs_dir="$6"
[[ $binary_type == native || $binary_type == solcjs ]] || assertFail
[[ $binary_type == solcjs || $solcjs_dir == "" ]] || assertFail
cd "$test_dir"
if [[ $binary_type == solcjs ]]
then
printLog "Setting up solc-js..."
if [[ $solcjs_dir == "" ]]; then
printLog "Cloning branch ${solcjs_branch}..."
git clone --depth 1 -b "$solcjs_branch" https://github.com/ethereum/solc-js.git "$install_dir"
else
printLog "Using local solc-js from ${solcjs_dir}..."
cp -ra "$solcjs_dir" solc
fi
pushd "$install_dir"
npm install
@ -207,9 +215,19 @@ function force_truffle_compiler_settings
echo "Compiler version (full): ${SOLCVERSION}"
echo "-------------------------------------"
# Forcing the settings should always work by just overwriting the solc object. Forcing them by using a
# dedicated settings objects should only be the fallback.
echo "module.exports['compilers'] = $(truffle_compiler_settings "$solc_path" "$preset" "$evm_version");" >> "$config_file"
local compiler_settings gas_reporter_settings
compiler_settings=$(truffle_compiler_settings "$solc_path" "$preset" "$evm_version")
gas_reporter_settings=$(eth_gas_reporter_settings "$preset")
{
echo "require('eth-gas-reporter');"
echo "module.exports['mocha'] = {"
echo " reporter: 'eth-gas-reporter',"
echo " reporterOptions: ${gas_reporter_settings}"
echo "};"
echo "module.exports['compilers'] = ${compiler_settings};"
} >> "$config_file"
}
function name_hardhat_default_export
@ -278,16 +296,21 @@ function force_hardhat_compiler_settings
echo "Compiler version (full): ${SOLCVERSION}"
echo "-------------------------------------"
local settings
settings=$(hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version")
local compiler_settings gas_reporter_settings
compiler_settings=$(hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version")
gas_reporter_settings=$(eth_gas_reporter_settings "$preset")
if [[ $config_file == *\.js ]]; then
[[ $config_var_name == "" ]] || assertFail
echo "module.exports['solidity'] = ${settings}" >> "$config_file"
echo "require('hardhat-gas-reporter');"
echo "module.exports.gasReporter = ${gas_reporter_settings};"
echo "module.exports.solidity = ${compiler_settings};"
else
[[ $config_file == *\.ts ]] || assertFail
[[ $config_var_name != "" ]] || assertFail
echo "${config_var_name}.solidity = {compilers: [${settings}]}" >> "$config_file"
fi
echo 'import "hardhat-gas-reporter";'
echo "${config_var_name}.gasReporter = ${gas_reporter_settings};"
echo "${config_var_name}.solidity = {compilers: [${compiler_settings}]};"
fi >> "$config_file"
}
function truffle_verify_compiler_version
@ -320,21 +343,7 @@ function truffle_clean
function hardhat_clean
{
rm -rf artifacts/ cache/
}
function run_test
{
local compile_fn="$1"
local test_fn="$2"
replace_version_pragmas
printLog "Running compile function..."
time $compile_fn
printLog "Running test function..."
$test_fn
rm -rf build/ artifacts/ cache/
}
function settings_from_preset
@ -368,6 +377,21 @@ function replace_global_solc
export PATH="$PWD:$PATH"
}
function eth_gas_reporter_settings
{
local preset="$1"
echo "{"
echo " enabled: true,"
echo " gasPrice: 1," # Gas price does not matter to us at all. Set to whatever to avoid API call.
echo " noColors: true,"
echo " showTimeSpent: false," # We're not interested in test timing
echo " onlyCalledMethods: true," # Exclude entries with no gas for shorter report
echo " showMethodSig: true," # Should make diffs more stable if there are overloaded functions
echo " outputFile: \"$(gas_report_path "$preset")\""
echo "}"
}
function truffle_compiler_settings
{
local solc_path="$1"
@ -495,3 +519,121 @@ function external_test
rm -rf "$DIR"
echo "Done."
}
function gas_report_path
{
local preset="$1"
echo "${DIR}/gas-report-${preset}.rst"
}
function gas_report_to_json
{
cat - | "${REPO_ROOT}/scripts/externalTests/parse_eth_gas_report.py" | jq '{gas: .}'
}
function detect_hardhat_artifact_dir
{
if [[ -e build/ && -e artifacts/ ]]; then
fail "Cannot determine Hardhat artifact location. Both build/ and artifacts/ exist"
elif [[ -e build/ ]]; then
echo -n build/artifacts
elif [[ -e artifacts/ ]]; then
echo -n artifacts
else
fail "Hardhat build artifacts not found."
fi
}
function bytecode_size_json_from_truffle_artifacts
{
# NOTE: The output of this function is a series of concatenated JSON dicts rather than a list.
for artifact in build/contracts/*.json; do
if [[ $(jq '. | has("unlinked_binary")' "$artifact") == false ]]; then
# Each artifact represents compilation output for a single contract. Some top-level keys contain
# bits of Standard JSON output while others are generated by Truffle. Process it into a dict
# of the form `{"<file>": {"<contract>": <size>}}`.
# NOTE: The `bytecode` field starts with 0x, which is why we subtract 1 from size.
jq '{
(.ast.absolutePath): {
(.contractName): (.bytecode | length / 2 - 1)
}
}' "$artifact"
fi
done
}
function bytecode_size_json_from_hardhat_artifacts
{
# NOTE: The output of this function is a series of concatenated JSON dicts rather than a list.
for artifact in "$(detect_hardhat_artifact_dir)"/build-info/*.json; do
# Each artifact contains Standard JSON output under the `output` key.
# Process it into a dict of the form `{"<file>": {"<contract>": <size>}}`,
# Note that one Hardhat artifact often represents multiple input files.
jq '.output.contracts | to_entries[] | {
"\(.key)": .value | to_entries[] | {
"\(.key)": (.value.evm.bytecode.object | length / 2)
}
}' "$artifact"
done
}
function combine_artifact_json
{
# Combine all dicts into a list with `jq --slurp` and then use `reduce` to merge them into one
# big dict with keys of the form `"<file>:<contract>"`. Then run jq again to filter out items
# with zero size and put the rest under under a top-level `bytecode_size` key. Also add another
# key with total bytecode size.
# NOTE: The extra inner `bytecode_size` key is there only to make diffs more readable.
cat - |
jq --slurp 'reduce (.[] | to_entries[]) as {$key, $value} ({}; . + {
($key + ":" + ($value | to_entries[].key)): {
bytecode_size: $value | to_entries[].value
}
})' |
jq --indent 4 --sort-keys '{
bytecode_size: [. | to_entries[] | select(.value.bytecode_size > 0)] | from_entries,
total_bytecode_size: (reduce (. | to_entries[]) as {$key, $value} (0; . + $value.bytecode_size))
}'
}
function project_info_json
{
local project_url="$1"
echo "{"
echo " \"project\": {"
# NOTE: Given that we clone with `--depth 1`, we'll only get useful output out of `git describe`
# if we directly check out a tag. Still better than nothing.
echo " \"version\": \"$(git describe --always)\","
echo " \"commit\": \"$(git rev-parse HEAD)\","
echo " \"url\": \"${project_url}\""
echo " }"
echo "}"
}
function store_benchmark_report
{
local framework="$1"
local project_name="$2"
local project_url="$3"
local preset="$4"
[[ $framework == truffle || $framework == hardhat ]] || assertFail
[[ " ${AVAILABLE_PRESETS[*]} " == *" $preset "* ]] || assertFail
local report_dir="${REPO_ROOT}/reports/externalTests"
local output_file="${report_dir}/benchmark-${project_name}-${preset}.json"
mkdir -p "$report_dir"
{
if [[ -e $(gas_report_path "$preset") ]]; then
gas_report_to_json < "$(gas_report_path "$preset")"
fi
"bytecode_size_json_from_${framework}_artifacts" | combine_artifact_json
project_info_json "$project_url"
} | jq --slurp "{\"${project_name}\": {\"${preset}\": add}}" --indent 4 --sort-keys > "$output_file"
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -84,6 +86,10 @@ function elementfi_test
sed -i 's|delete _twoTokenPoolTokens\[poolId\];|delete _twoTokenPoolTokens[poolId].tokenA;delete _twoTokenPoolTokens[poolId].tokenB;|g' vault/balances/TwoTokenPoolsBalance.sol
popd
# The test suite uses forked mainnet and an expiration period that's too short.
# TODO: Remove when https://github.com/element-fi/elf-contracts/issues/243 is fixed.
sed -i 's|^\s*require(_expiration - block\.timestamp < _unitSeconds);\s*$||g' contracts/ConvergentCurvePool.sol
# Several tests fail unless we use the exact versions hard-coded in package-lock.json
#neutralize_package_lock
@ -97,6 +103,7 @@ function elementfi_test
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat elementfi "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -68,6 +70,7 @@ function ens_test
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
store_benchmark_report hardhat ens "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -68,6 +70,7 @@ function euler_test
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
store_benchmark_report hardhat euler "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -40,12 +42,12 @@ function gnosis_safe_test
local config_file="truffle-config.js"
local compile_only_presets=(
legacy-no-optimize # "Error: while migrating GnosisSafe: Returned error: base fee exceeds gas limit"
legacy-no-optimize # Compiles but migrations run out of gas: "Error: while migrating GnosisSafe: Returned error: base fee exceeds gas limit"
)
local settings_presets=(
"${compile_only_presets[@]}"
#ir-no-optimize # "YulException: Variable var_call_430_mpos is 1 slot(s) too deep inside the stack."
#ir-optimize-evm-only # "YulException: Variable var_call_430_mpos is 1 slot(s) too deep inside the stack."
#ir-no-optimize # Compilation fails with "YulException: Variable var_call_430_mpos is 1 slot(s) too deep inside the stack."
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_call_430_mpos is 1 slot(s) too deep inside the stack."
ir-optimize-evm+yul
legacy-optimize-evm-only
legacy-optimize-evm+yul
@ -65,12 +67,14 @@ function gnosis_safe_test
neutralize_package_json_hooks
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$(first_word "$SELECTED_PRESETS")"
npm install --package-lock
npm install eth-gas-reporter
replace_version_pragmas
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc/dist"
for preset in $SELECTED_PRESETS; do
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
store_benchmark_report truffle gnosis2 "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -42,11 +44,11 @@ function gnosis_safe_test
local compile_only_presets=()
local settings_presets=(
"${compile_only_presets[@]}"
#ir-no-optimize # "YulException: Variable var_call_430_mpos is 1 slot(s) too deep inside the stack."
#ir-optimize-evm-only # "YulException: Variable var_call_430_mpos is 1 slot(s) too deep inside the stack."
#ir-no-optimize # Compilation fails with "YulException: Variable var_call_430_mpos is 1 slot(s) too deep inside the stack."
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_call_430_mpos is 1 slot(s) too deep inside the stack."
ir-optimize-evm+yul
#legacy-no-optimize # "Stack too deep" error
#legacy-optimize-evm-only # "Stack too deep" error
#legacy-no-optimize # Compilation fails with "Stack too deep" error
#legacy-optimize-evm-only # Compilation fails with "Stack too deep" error
legacy-optimize-evm+yul
)
@ -63,12 +65,14 @@ function gnosis_safe_test
neutralize_package_json_hooks
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$(first_word "$SELECTED_PRESETS")"
npm install --package-lock
npm install eth-gas-reporter
replace_version_pragmas
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc/dist"
for preset in $SELECTED_PRESETS; do
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
store_benchmark_report truffle gnosis "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -68,6 +70,7 @@ function perpetual_pools_test
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat perpetual-pools "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -72,6 +74,7 @@ function pool_together_test
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat pool-together "$repo" "$preset"
done
}

View File

@ -24,13 +24,16 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
SELECTED_PRESETS="$3"
function compile_fn { yarn compile; }
function test_fn { yarn test; }
# NOTE: `yarn test` runs `mocha` which seems to disable the gas reporter.
function test_fn { npx --no hardhat --no-compile test; }
function prb_math_test
{
@ -70,11 +73,13 @@ function prb_math_test
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var"
yarn install --no-lock-file
yarn add hardhat-gas-reporter
replace_version_pragmas
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat prb-math "$repo" "$preset"
done
}

View File

@ -26,11 +26,9 @@ source test/externalTests/common.sh
SOLJSON="$1"
VERSION="$2"
SOLCJS_CHECKOUT="$3" # optional
[[ $SOLJSON != "" && -f "$SOLJSON" && $VERSION != "" ]] || fail "Usage: $0 <path to soljson.js> <version>"
function compile_fn { echo "Nothing to compile."; }
function test_fn { npm test; }
[[ $SOLJSON != "" && -f "$SOLJSON" && $VERSION != "" ]] || fail "Usage: $0 <path to soljson.js> <version> [<path to solc-js>]"
function solcjs_test
{
@ -38,7 +36,7 @@ function solcjs_test
SOLCJS_INPUT_DIR="$TEST_DIR"/test/externalTests/solc-js
# set up solc-js on the branch specified
setup_solc "$DIR" solcjs "$SOLJSON" master solc/
setup_solc "$DIR" solcjs "$SOLJSON" master solc/ "$SOLCJS_CHECKOUT"
cd solc/
printLog "Updating index.js file..."
@ -60,7 +58,10 @@ function solcjs_test
echo "Updating package.json to version $VERSION"
npm version --allow-same-version --no-git-tag-version "$VERSION"
run_test compile_fn test_fn
replace_version_pragmas
printLog "Running test function..."
npm test
}
external_test solc-js solcjs_test

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -92,6 +94,7 @@ function trident_test
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat trident "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -73,11 +75,13 @@ function uniswap_test
yarn add @ethereumjs/tx@3.1.3
yarn install
yarn add hardhat-gas-reporter
replace_version_pragmas
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat uniswap "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -65,9 +67,11 @@ function yield_liquidator_test
npm install
replace_version_pragmas
neutralize_packaged_contracts
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat yield_liquidator "$repo" "$preset"
done
}

View File

@ -24,6 +24,8 @@ set -e
source scripts/common.sh
source test/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$2"
@ -44,8 +46,8 @@ function zeppelin_test
)
local settings_presets=(
"${compile_only_presets[@]}"
#ir-no-optimize # "YulException: Variable var_account_852 is 4 slot(s) too deep inside the stack."
#ir-optimize-evm-only # "YulException: Variable var_account_852 is 4 slot(s) too deep inside the stack."
#ir-no-optimize # Compilation fails with "YulException: Variable var_account_852 is 4 slot(s) too deep inside the stack."
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_account_852 is 4 slot(s) too deep inside the stack."
legacy-no-optimize
legacy-optimize-evm-only
legacy-optimize-evm+yul
@ -66,6 +68,7 @@ function zeppelin_test
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
store_benchmark_report hardhat zeppelin "$repo" "$preset"
done
}

View File

@ -435,7 +435,7 @@ TestCase::TestResult SemanticTest::runTest(
{
soltestAssert(
m_allowNonExistingFunctions ||
m_compiler.methodIdentifiers(m_compiler.lastContractName(m_sources.mainSourceFile)).isMember(test.call().signature),
m_compiler.interfaceSymbols(m_compiler.lastContractName(m_sources.mainSourceFile))["methods"].isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler"
);

View File

@ -2582,6 +2582,55 @@ BOOST_AUTO_TEST_CASE(dev_struct_getter_override)
checkNatspec(sourceCode, "Thing", natspec2, false);
}
BOOST_AUTO_TEST_CASE(dev_struct_getter_override_no_return_name)
{
char const *sourceCode = R"(
interface IThing {
///@return
function value(uint) external returns (uint128,uint128);
}
contract Thing is IThing {
struct Value {
uint128 x;
uint128 A;
}
mapping(uint=>Value) public override value;
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"value(uint256)":
{
"returns":
{
"_0": ""
}
}
}
})ABCDEF";
char const *natspec2 = R"ABCDEF({
"methods": {},
"stateVariables":
{
"value":
{
"return": "x ",
"returns":
{
"x": ""
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "IThing", natspec, false);
checkNatspec(sourceCode, "Thing", natspec2, false);
}
BOOST_AUTO_TEST_CASE(dev_struct_getter_override_different_return_parameter_names)
{
char const *sourceCode = R"(

View File

@ -25,6 +25,7 @@
#include <test/TemporaryDirectory.h>
#include <test/libsolidity/util/SoltestErrors.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/test/unit_test.hpp>
@ -192,8 +193,8 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_root_name_only)
#if defined(_WIN32)
boost::filesystem::path driveLetter = boost::filesystem::current_path().root_name();
solAssert(!driveLetter.empty(), "");
solAssert(driveLetter.is_relative(), "");
soltestAssert(!driveLetter.empty(), "");
soltestAssert(driveLetter.is_relative(), "");
BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(driveLetter, resolveSymlinks), expectedWorkDir);
#endif
@ -212,13 +213,32 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_stripping_root_name)
for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled})
{
boost::filesystem::path workDir = boost::filesystem::current_path();
boost::filesystem::path normalizedPath = FileReader::normalizeCLIPathForVFS(
boost::filesystem::current_path(),
workDir,
resolveSymlinks
);
BOOST_CHECK_EQUAL(normalizedPath, "/" / boost::filesystem::current_path().relative_path());
BOOST_CHECK_EQUAL(normalizedPath, "/" / workDir.relative_path());
BOOST_TEST(normalizedPath.root_name().empty());
BOOST_CHECK_EQUAL(normalizedPath.root_directory(), "/");
#if defined(_WIN32)
string root = workDir.root_path().string();
soltestAssert(root.length() == 3 && root[1] == ':' && root[2] == '\\', "");
for (auto convert: {boost::to_lower_copy<string>, boost::to_upper_copy<string>})
{
boost::filesystem::path workDirWin = convert(root, locale()) / workDir.relative_path();
normalizedPath = FileReader::normalizeCLIPathForVFS(
workDirWin,
resolveSymlinks
);
BOOST_CHECK_EQUAL(normalizedPath, "/" / workDir.relative_path());
BOOST_TEST(normalizedPath.root_name().empty());
BOOST_CHECK_EQUAL(normalizedPath.root_directory(), "/");
}
#endif
}
}

View File

@ -1,12 +1,12 @@
contract A {
uint public x;
uint public x = 2;
constructor(uint) {}
function f() public { x = 4; }
function f() public returns(uint) { x = 4; }
}
contract B is A {
constructor() A(f()) {}
}
// ====
// compileViaYul: also
// compileViaYul: false
// ----
// x() -> 4

View File

@ -0,0 +1,12 @@
contract A {
uint public x = 2;
constructor(uint) {}
function f() public returns(uint) { x = 4; }
}
contract B is A {
constructor() A(f()) {}
}
// ====
// compileViaYul: true
// ----
// x() -> 2

View File

@ -0,0 +1,14 @@
contract C {
function f(string memory a, string memory b) public returns (string memory) {
return string.concat(a, b);
}
}
// ====
// compileViaYul: also
// ----
// f(string,string): 0x40, 0x80, 32, "abcdabcdabcdabcdabcdabcdabcdabcd", 5, "bcdef" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
// f(string,string): 0x40, 0xa0, 64, "abcdabcdabcdabcdabcdabcdabcdabcd", "abcdabcdabcdabcdabcdabcdabcdabcd", 5, "bcdef" -> 0x20, 0x45, 0x6162636461626364616263646162636461626364616263646162636461626364, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
// f(string,string): 0x40, 0x80, 3, "abc", 3, "def" -> 0x20, 6, "abcdef"
// f(string,string): 0x40, 0xa0, 34, "abcdabcdabcdabcdabcdabcdabcdabcd", "ab", 30, "cdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x40, 0x6162636461626364616263646162636461626364616263646162636461626364, 0x6162636461626364616263646162636461626364616263646162636461626364
// f(string,string): 0x40, 0xa0, 34, "abcdabcdabcdabcdabcdabcdabcdabcd", "ab", 34, "cdabcdabcdabcdabcdabcdabcdabcdab", "cd" -> 0x20, 0x44, 0x6162636461626364616263646162636461626364616263646162636461626364, 0x6162636461626364616263646162636461626364616263646162636461626364, 44048183293808120317390542201052832727062033572611867748297851798484192067584
// f(string,string): 0x40, 0x80, 3, "abc", 30, "dabcdabcdabcdabcdabcdabcdabcda" -> 0x20, 0x21, 0x6162636461626364616263646162636461626364616263646162636461626364, 43874346312576839672212443538448152585028080127215369968075725190498334277632

View File

@ -0,0 +1,37 @@
contract C{
string s = "bcdef";
function f(string memory a) public returns (string memory) {
return string.concat(a, "bcdef");
}
function g(string calldata a) public returns (string memory) {
return string.concat(a, "abcdefghabcdefghabcdefghabcdefghab");
}
function h(string calldata a) public returns (string memory) {
return string.concat(a, s);
}
function j(string calldata a) public returns (string memory) {
string storage ref = s;
return string.concat(a, ref, s);
}
function k(string calldata a, bytes memory b) public returns (string memory) {
return string.concat(a, string(b));
}
function slice(string calldata a) public returns (string memory) {
require(bytes(a).length > 2, "");
return string.concat(a[:2], a[2:]);
}
function strParam(bytes calldata a) public returns (string memory) {
return string.concat(string(a), "bcdef");
}
}
// ====
// compileViaYul: also
// ----
// f(string): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
// g(string): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x42, 0x6162636461626364616263646162636461626364616263646162636461626364, 0x6162636465666768616263646566676861626364656667686162636465666768, 44047497324925121336511606693520958599579173549109180625971642598225011015680
// h(string): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
// j(string): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x2a, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928944786876717917111204727192787026596791669343131645116682757734400
// k(string,bytes): 0x40, 0x80, 32, "abcdabcdabcdabcdabcdabcdabcdabcd", 5, "bcdef" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
// slice(string): 0x20, 4, "abcd" -> 0x20, 4, "abcd"
// strParam(bytes): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200

View File

@ -0,0 +1,10 @@
contract C {
function f() public returns (string memory) {
return string.concat();
}
}
// ====
// compileToEwasm: also
// compileViaYul: also
// ----
// f() -> 0x20, 0

View File

@ -0,0 +1,27 @@
contract C {
function f() public returns (string memory) {
string memory b = "";
return string.concat(
string.concat(b),
string.concat(b, b),
string.concat("", b),
string.concat(b, "")
);
}
function g() public returns (string memory) {
return string.concat("", "abc", hex"", "abc", unicode"");
}
function h() public returns (string memory) {
string memory b = "";
return string.concat(b, "abc", b, "abc", b);
}
}
// ====
// compileToEwasm: also
// compileViaYul: also
// ----
// f() -> 0x20, 0
// g() -> 0x20, 6, "abcabc"
// h() -> 0x20, 6, "abcabc"

View File

@ -0,0 +1,9 @@
contract C {
function f(string memory a, string memory b, string memory c) public returns (string memory) {
return string.concat(string.concat(a, b), c);
}
}
// ====
// compileViaYul: also
// ----
// f(string,string,string): 0x60, 0x60, 0x60, 2, "ab" -> 0x20, 6, "ababab"

View File

@ -0,0 +1,17 @@
interface IBase {
function foo() external view;
}
contract Base is IBase {
function foo() public virtual view {}
}
interface IExt is IBase {}
contract Ext is IExt, Base {}
contract T { function foo() public virtual view {} }
contract Impl is Ext, T {
function foo() public view override(IBase, Base, T) {}
}

View File

@ -0,0 +1,17 @@
abstract contract IBase {
function foo() external view virtual;
}
contract Base is IBase {
function foo() public virtual override view {}
}
abstract contract IExt is IBase {}
contract Ext is IExt, Base {}
contract T { function foo() public virtual view {} }
contract Impl is Ext, T {
function foo() public view override(IBase, Base, T) {}
}

View File

@ -0,0 +1,18 @@
interface IBase {
function foo() external view;
}
contract Base is IBase {
function foo() public virtual view {}
}
interface IExt is IBase {}
contract Ext is IExt, Base {}
contract Impl is Ext {
function foo() public view {}
}
// ----
// TypeError 9456: (211-240): Overriding function is missing "override" specifier.
// TypeError 4327: (211-240): Function needs to specify overridden contracts "Base" and "IBase".

View File

@ -0,0 +1,16 @@
interface IBase {
function foo() external view;
}
contract Base is IBase {
function foo() public virtual view {}
}
interface IExt is IBase {}
contract Ext is IExt, Base {}
contract Impl is Ext {
function foo() public view override (IBase, Base) {}
}
// ----

View File

@ -0,0 +1,22 @@
interface IBase {
function foo() external view;
}
contract Base1 is IBase { function foo() public virtual view {} }
contract Base2 is IBase { function foo() public virtual view {} }
interface IExt1a is IBase {}
interface IExt1b is IBase {}
interface IExt2a is IBase {}
interface IExt2b is IBase {}
contract Ext1 is IExt1a, IExt1b, Base1 {}
contract Ext2 is IExt2a, IExt2b, Base2 {}
contract Impl is Ext1, Ext2 {
function foo() public view {}
}
// ----
// TypeError 9456: (424-453): Overriding function is missing "override" specifier.
// TypeError 9456: (424-453): Overriding function is missing "override" specifier.
// TypeError 4327: (424-453): Function needs to specify overridden contracts "Base1", "Base2" and "IBase".

View File

@ -0,0 +1,18 @@
interface IBase {
function foo() external view;
}
contract Base1 is IBase { function foo() public virtual view {} }
contract Base2 is IBase { function foo() public virtual view {} }
interface IExt1a is IBase {}
interface IExt1b is IBase {}
interface IExt2a is IBase {}
interface IExt2b is IBase {}
contract Ext1 is IExt1a, IExt1b, Base1 {}
contract Ext2 is IExt2a, IExt2b, Base2 {}
contract Impl is Ext1, Ext2 {
function foo() public view override (IBase, Base1, Base2) {}
}

View File

@ -0,0 +1,18 @@
interface IBase {
function foo() external view;
}
contract Base1 is IBase { function foo() public virtual view {} }
contract Base2 is IBase { function foo() public virtual view {} }
interface IExt1a is IBase {}
abstract contract IExt1b is IBase {}
abstract contract IExt2a is IBase {}
interface IExt2b is IBase {}
contract Ext1 is IExt1a, IExt1b, Base1 {}
contract Ext2 is IExt2a, IExt2b, Base2 {}
contract Impl is Ext1, Ext2 {
function foo() public view override (IBase, Base1, Base2) {}
}

View File

@ -0,0 +1,7 @@
contract C {
function g() public pure returns (string memory) {
return string.concat;
}
}
// ----
// TypeError 6359: (83-96): Return argument type function () pure returns (string memory) is not implicitly convertible to expected type (type of first return variable) string memory.

View File

@ -0,0 +1,13 @@
contract C {
function j() external {
string memory a = "hello";
string memory b = " world";
string memory d = string.concat(bytes(a), bytes(b));
string memory e = string.concat(a, 0);
}
}
// ----
// TypeError 9977: (153-161): Invalid type for argument in the string.concat function call. string type is required, but t_bytes_memory_ptr provided.
// TypeError 9977: (163-171): Invalid type for argument in the string.concat function call. string type is required, but t_bytes_memory_ptr provided.
// TypeError 9977: (217-218): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.

View File

@ -0,0 +1,15 @@
contract C {
string s;
function f(string calldata c, string calldata c1) public {
string memory a;
bytes16 b;
uint8[] memory num;
bytes1[] memory m;
string memory d = string.concat(a, b, c, num, s, "abc", m, c1, bytes(c1));
}
}
// ----
// TypeError 9977: (232-233): Invalid type for argument in the string.concat function call. string type is required, but t_bytes16 provided.
// TypeError 9977: (238-241): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint8_$dyn_memory_ptr provided.
// TypeError 9977: (253-254): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_bytes1_$dyn_memory_ptr provided.
// TypeError 9977: (260-269): Invalid type for argument in the string.concat function call. string type is required, but t_bytes_calldata_ptr provided.

View File

@ -0,0 +1,7 @@
contract C {
function f() public {
string.concat([], [], []);
}
}
// ----
// TypeError 6378: (61-63): Unable to deduce common type for array elements.

View File

@ -0,0 +1,40 @@
contract C {
struct S {
uint x;
}
enum E {A, B, C}
mapping(uint => E) m;
function f() public {
bool b;
uint u;
uint8 u8;
address a;
address payable ap;
function () external fext;
function () internal fint;
uint[] memory uDynamic;
uint[2] memory uStatic;
C c;
S memory s;
E e;
string.concat(b, u, u8, a, ap, fext, fint, uDynamic, uStatic, c, s, e, m);
}
}
// ----
// TypeError 9977: (426-427): Invalid type for argument in the string.concat function call. string type is required, but t_bool provided.
// TypeError 9977: (429-430): Invalid type for argument in the string.concat function call. string type is required, but t_uint256 provided.
// TypeError 9977: (432-434): Invalid type for argument in the string.concat function call. string type is required, but t_uint8 provided.
// TypeError 9977: (436-437): Invalid type for argument in the string.concat function call. string type is required, but t_address provided.
// TypeError 9977: (439-441): Invalid type for argument in the string.concat function call. string type is required, but t_address_payable provided.
// TypeError 9977: (443-447): Invalid type for argument in the string.concat function call. string type is required, but t_function_external_nonpayable$__$returns$__$ provided.
// TypeError 9977: (449-453): Invalid type for argument in the string.concat function call. string type is required, but t_function_internal_nonpayable$__$returns$__$ provided.
// TypeError 9977: (455-463): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint256_$dyn_memory_ptr provided.
// TypeError 9977: (465-472): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint256_$2_memory_ptr provided.
// TypeError 9977: (474-475): Invalid type for argument in the string.concat function call. string type is required, but t_contract$_C_$86 provided.
// TypeError 9977: (477-478): Invalid type for argument in the string.concat function call. string type is required, but t_struct$_S_$4_memory_ptr provided.
// TypeError 9977: (480-481): Invalid type for argument in the string.concat function call. string type is required, but t_enum$_E_$8 provided.
// TypeError 9977: (483-484): Invalid type for argument in the string.concat function call. string type is required, but t_mapping$_t_uint256_$_t_enum$_E_$8_$ provided.

View File

@ -0,0 +1,58 @@
contract C {
struct S {
uint x;
}
enum E {A, B, C}
function f() public {
string.concat(
false,
1,
1e10,
1e-10,
0.1,
0x1234567,
0x11112222333344445555666677778888999900, // One byte less than an address
0x1111222233334444555566667777888899990000, // Address
0x111122223333444455556666777788889999000011, // One byte more than an address
0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff, // exactly 32 bytes
-0x0000000000000000000000000000000000000000000000000000000000000001, // exactly 32 bytes
bytes(bytes32(0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff))[:],
f,
(),
(0, 0),
[0],
[0][:],
[0][0],
new C(),
S(0),
E.A
);
}
}
// ----
// TypeError 9640: (698-780): Explicit type conversion not allowed from "bytes32" to "bytes memory".
// TypeError 1227: (698-783): Index range access is only supported for dynamic calldata arrays.
// TypeError 1227: (865-871): Index range access is only supported for dynamic calldata arrays.
// TypeError 9977: (134-139): Invalid type for argument in the string.concat function call. string type is required, but t_bool provided.
// TypeError 9977: (153-154): Invalid type for argument in the string.concat function call. string type is required, but t_rational_1_by_1 provided.
// TypeError 9977: (168-172): Invalid type for argument in the string.concat function call. string type is required, but t_rational_10000000000_by_1 provided.
// TypeError 9977: (186-191): Invalid type for argument in the string.concat function call. string type is required, but t_rational_1_by_10000000000 provided.
// TypeError 9977: (205-208): Invalid type for argument in the string.concat function call. string type is required, but t_rational_1_by_10 provided.
// TypeError 9977: (222-231): Invalid type for argument in the string.concat function call. string type is required, but t_rational_19088743_by_1 provided.
// TypeError 9977: (245-285): Invalid type for argument in the string.concat function call. string type is required, but t_rational_380605192295934637532253317235440047844071680_by_1 provided.
// TypeError 9977: (336-378): Invalid type for argument in the string.concat function call. string type is required, but t_address provided.
// TypeError 9977: (405-449): Invalid type for argument in the string.concat function call. string type is required, but t_rational_24943341882306372405313753398341798975509081620497_by_1 provided.
// TypeError 9977: (496-562): Invalid type for argument in the string.concat function call. string type is required, but t_rational_30272441630670900764332283662402067049651745785153368133042924362431065855_by_1 provided.
// TypeError 9977: (597-664): Invalid type for argument in the string.concat function call. string type is required, but t_rational_minus_1_by_1 provided.
// TypeError 9977: (698-783): Invalid type for argument in the string.concat function call. string type is required, but t_bytes_memory_ptr_slice provided.
// TypeError 9977: (797-798): Invalid type for argument in the string.concat function call. string type is required, but t_function_internal_nonpayable$__$returns$__$ provided.
// TypeError 9977: (812-814): Invalid type for argument in the string.concat function call. string type is required, but t_tuple$__$ provided.
// TypeError 9977: (828-834): Invalid type for argument in the string.concat function call. string type is required, but t_tuple$_t_rational_0_by_1_$_t_rational_0_by_1_$ provided.
// TypeError 9977: (848-851): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint8_$1_memory_ptr provided.
// TypeError 9977: (865-871): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint8_$1_memory_ptr_slice provided.
// TypeError 9977: (885-891): Invalid type for argument in the string.concat function call. string type is required, but t_uint8 provided.
// TypeError 9977: (905-912): Invalid type for argument in the string.concat function call. string type is required, but t_contract$_C_$61 provided.
// TypeError 9977: (926-930): Invalid type for argument in the string.concat function call. string type is required, but t_struct$_S_$4_memory_ptr provided.
// TypeError 9977: (944-947): Invalid type for argument in the string.concat function call. string type is required, but t_enum$_E_$8 provided.

View File

@ -0,0 +1,33 @@
contract C {
function f() public pure {
string.concat(
0,
-0,
0.0,
-0.0,
0e10,
-0e10,
0e-10,
-0e-10,
(0),
0x00,
-0x00,
0x0000000000000000000000000000000000000000000000000000000000000000, // exactly 32 bytes
-0x0000000000000000000000000000000000000000000000000000000000000000 // exactly 32 bytes
);
}
}
// ----
// TypeError 9977: (79-80): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (94-96): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (110-113): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (127-131): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (145-149): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (163-168): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (182-187): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (201-207): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (221-224): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (238-242): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (256-261): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (275-341): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
// TypeError 9977: (375-442): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/**
* Unit tests for stack shuffling.
*/
#include <libyul/backends/evm/StackHelpers.h>
#include <boost/test/unit_test.hpp>
using namespace std;
using namespace solidity::langutil;
namespace solidity::yul::test
{
BOOST_AUTO_TEST_SUITE(YulStackShuffling)
BOOST_AUTO_TEST_CASE(swap_cycle)
{
std::vector<Scope::Variable> scopeVariables;
Scope::Function function;
std::vector<VariableSlot> v;
for (size_t i = 0; i < 17; ++i)
scopeVariables.emplace_back(Scope::Variable{""_yulstring, YulString{"v" + to_string(i)}});
for (size_t i = 0; i < 17; ++i)
v.emplace_back(VariableSlot{scopeVariables[i]});
Stack sourceStack{
v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[9], v[10], v[11], v[12], v[13], v[14], v[15], v[16],
FunctionReturnLabelSlot{function}, FunctionReturnLabelSlot{function}, v[5]};
Stack targetStack{
v[1], v[0], v[2], v[3], v[4], v[5], v[6], v[7], v[9], v[10], v[11], v[12], v[13], v[14], v[15], v[16],
FunctionReturnLabelSlot{function}, JunkSlot{}, JunkSlot{}
};
// Used to hit a swapping cycle.
createStackLayout(sourceStack, targetStack, [](auto){}, [](auto){}, [](){});
}
BOOST_AUTO_TEST_SUITE_END()
}

View File

@ -0,0 +1,381 @@
·----------------------------------------------------------------------------------------------------------------------------------------|---------------------------|-------------|------------------------------·
| Solc version: 0.8.10 · Optimizer enabled: true · Runs: 200 · Block limit: 100000000 gas │
·········································································································································|···························|·············|·······························
| Methods │
·································|·······································································································|·············|·············|·············|···············|···············
| Contract · Method · Min · Max · Avg · # calls · eur (avg) │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · getMessageHash(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · getMessageHashForSafe(address,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · getModules() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · isValidSignature(bytes,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · isValidSignature(bytes32,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · NAME() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · onERC1155BatchReceived(address,address,uint256[],uint256[],bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · onERC1155Received(address,address,uint256,uint256,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · onERC721Received(address,address,uint256,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · simulate(address,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · supportsInterface(bytes4) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · tokensReceived(address,address,address,uint256,bytes,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CompatibilityFallbackHandler · VERSION() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CreateCall · performCreate(uint256,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| CreateCall · performCreate2(uint256,bytes,bytes32) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DebugTransactionGuard · checkAfterExecution(bytes32,bool) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DebugTransactionGuard · checkTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DebugTransactionGuard · supportsInterface(bytes4) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DebugTransactionGuard · txNonces(bytes32) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DefaultCallbackHandler · NAME() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DefaultCallbackHandler · onERC1155BatchReceived(address,address,uint256[],uint256[],bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DefaultCallbackHandler · onERC1155Received(address,address,uint256,uint256,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DefaultCallbackHandler · onERC721Received(address,address,uint256,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DefaultCallbackHandler · supportsInterface(bytes4) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DefaultCallbackHandler · tokensReceived(address,address,address,uint256,bytes,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DefaultCallbackHandler · VERSION() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DelegateCallTransactionGuard · allowedTarget() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DelegateCallTransactionGuard · checkAfterExecution(bytes32,bool) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DelegateCallTransactionGuard · checkTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| DelegateCallTransactionGuard · supportsInterface(bytes4) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC1155Token · balanceOf(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC1155Token · mint(address,uint256,uint256,bytes) · 47934 · 59804 · 57826 · 6 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC1155Token · safeTransferFrom(address,address,uint256,uint256,bytes) · - · - · 53900 · 2 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · allowance(address,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · approve(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · balanceOf(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · decimals() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · decreaseAllowance(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · increaseAllowance(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · name() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · symbol() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · totalSupply() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · transfer(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20 · transferFrom(address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · allowance(address,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · approve(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · balanceOf(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · decimals() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · decreaseAllowance(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · increaseAllowance(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · name() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · symbol() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · totalSupply() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · transfer(address,uint256) · - · - · 51567 · 8 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ERC20Token · transferFrom(address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| FallbackManager · setFallbackHandler(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · addOwnerWithThreshold(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · approvedHashes(address,bytes32) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · approveHash(bytes32) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · changeThreshold(uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · checkNSignatures(bytes32,bytes,bytes,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · checkSignatures(bytes32,bytes,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · disableModule(address,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · domainSeparator() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · enableModule(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · encodeTransactionData(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes) · 59563 · 151736 · 94816 · 85 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · execTransactionFromModule(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · execTransactionFromModuleReturnData(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · getChainId() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · getModulesPaginated(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · getOwners() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · getStorageAt(uint256,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · getThreshold() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · getTransactionHash(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · isModuleEnabled(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · isOwner(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · nonce() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · removeOwner(address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · requiredTxGas(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · setFallbackHandler(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · setGuard(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · setup(address[],uint256,address,bytes,address,address,uint256,address) · 167642 · 263690 · 201944 · 49 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · signedMessages(bytes32) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · simulateAndRevert(address,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · swapOwner(address,address,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafe · VERSION() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · addOwnerWithThreshold(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · approvedHashes(address,bytes32) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · approveHash(bytes32) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · changeThreshold(uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · checkNSignatures(bytes32,bytes,bytes,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · checkSignatures(bytes32,bytes,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · disableModule(address,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · domainSeparator() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · enableModule(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · encodeTransactionData(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · execTransactionFromModule(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · execTransactionFromModuleReturnData(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · getChainId() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · getModulesPaginated(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · getOwners() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · getStorageAt(uint256,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · getThreshold() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · getTransactionHash(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · isModuleEnabled(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · isOwner(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · nonce() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · removeOwner(address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · requiredTxGas(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · setFallbackHandler(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · setGuard(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · setup(address[],uint256,address,bytes,address,address,uint256,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · signedMessages(bytes32) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · simulateAndRevert(address,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · swapOwner(address,address,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeL2 · VERSION() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeProxyFactory · calculateCreateProxyWithNonceAddress(address,bytes,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeProxyFactory · createProxy(address,bytes) · 105568 · 105580 · 105568 · 52 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeProxyFactory · createProxyWithCallback(address,bytes,uint256,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeProxyFactory · createProxyWithNonce(address,bytes,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeProxyFactory · proxyCreationCode() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GnosisSafeProxyFactory · proxyRuntimeCode() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| GuardManager · setGuard(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| Migration · migrate() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| Migration · migrationSingleton() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| Migration · safe120Singleton() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · DEFAULT_FALLBACK_VALUE() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenAnyReturn(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenAnyReturnAddress(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenAnyReturnBool(bool) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenAnyReturnUint(uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenAnyRevert() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenAnyRevertWithMessage(string) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenAnyRunOutOfGas() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenCalldataReturn(bytes,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenCalldataReturnAddress(bytes,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenCalldataReturnBool(bytes,bool) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenCalldataReturnUint(bytes,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenCalldataRevert(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenCalldataRevertWithMessage(bytes,string) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenCalldataRunOutOfGas(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenMethodReturn(bytes,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenMethodReturnAddress(bytes,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenMethodReturnBool(bytes,bool) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenMethodReturnUint(bytes,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenMethodRevert(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenMethodRevertWithMessage(bytes,string) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · givenMethodRunOutOfGas(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · invocationCount() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · invocationCountForCalldata(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · invocationCountForMethod(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · MOCKS_LIST_END_HASH() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · MOCKS_LIST_END() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · MOCKS_LIST_START() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · reset() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · SENTINEL_ANY_MOCKS() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MockContract · updateInvocationCount(bytes4,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ModuleManager · disableModule(address,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ModuleManager · enableModule(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ModuleManager · execTransactionFromModule(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ModuleManager · execTransactionFromModuleReturnData(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ModuleManager · getModulesPaginated(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ModuleManager · isModuleEnabled(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MultiSend · multiSend(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| MultiSendCallOnly · multiSend(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| OwnerManager · addOwnerWithThreshold(address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| OwnerManager · changeThreshold(uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| OwnerManager · getOwners() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| OwnerManager · getThreshold() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| OwnerManager · isOwner(address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| OwnerManager · removeOwner(address,address,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| OwnerManager · swapOwner(address,address,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ReentrancyTransactionGuard · checkAfterExecution(bytes32,bool) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ReentrancyTransactionGuard · checkTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes,address) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| ReentrancyTransactionGuard · supportsInterface(bytes4) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| SignMessageLib · getMessageHash(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| SignMessageLib · signMessage(bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| SimulateTxAccessor · simulate(address,uint256,bytes,uint8) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| StorageAccessible · getStorageAt(uint256,uint256) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| StorageAccessible · simulateAndRevert(address,bytes) · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| TestHandler · dudududu() · - · - · - · 0 · - │
·································|·······································································································|·············|·············|·············|···············|···············
| Deployments · · % of limit · │
·········································································································································|·············|·············|·············|···············|···············
| DelegateCallTransactionGuard · 283510 · 283522 · 283516 · 0.3 % · - │
·········································································································································|·············|·············|·············|···············|···············
| ERC1155Token · - · - · 525869 · 0.5 % · - │
·········································································································································|·············|·············|·············|···············|···············
| ERC20Token · - · - · 733462 · 0.7 % · - │
·----------------------------------------------------------------------------------------------------------------------------------------|-------------|-------------|-------------|---------------|--------------·

View File

@ -0,0 +1,219 @@
#!/usr/bin/env python3
from dataclasses import asdict
import unittest
from textwrap import dedent
from unittest_helpers import FIXTURE_DIR, load_fixture
# NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports
# pragma pylint: disable=import-error
from externalTests.parse_eth_gas_report import parse_report, ReportParsingError, ReportValidationError
# pragma pylint: enable=import-error
ETH_GAS_REPORT_GNOSIS_RST_PATH = FIXTURE_DIR / 'eth_gas_report_gnosis.rst'
ETH_GAS_REPORT_GNOSIS_RST_CONTENT = load_fixture(ETH_GAS_REPORT_GNOSIS_RST_PATH)
class TestEthGasReport(unittest.TestCase):
def setUp(self):
self.maxDiff = 10000
def test_parse_report(self):
parsed_report = parse_report(ETH_GAS_REPORT_GNOSIS_RST_CONTENT)
expected_report = {
'solc_version': '0.8.10',
'optimize': True,
'runs': 200,
'block_limit': 100000000,
'total_method_gas': 57826 * 6 + 53900 * 2 + 51567 * 8 + 94816 * 85 + 201944 * 49 + 105568 * 52,
'total_deployment_gas': 283516 + 525869 + 733462,
'contracts': {
'DelegateCallTransactionGuard': {
'total_method_gas': 0,
'min_deployment_gas': 283510,
'max_deployment_gas': 283522,
'avg_deployment_gas': 283516,
'methods': None,
},
'ERC1155Token': {
'total_method_gas': 57826 * 6 + 53900 * 2,
'min_deployment_gas': None,
'max_deployment_gas': None,
'avg_deployment_gas': 525869,
'methods': {
'mint(address,uint256,uint256,bytes)': {
'total_gas': 57826 * 6,
'min_gas': 47934,
'max_gas': 59804,
'avg_gas': 57826,
'call_count': 6
},
'safeTransferFrom(address,address,uint256,uint256,bytes)': {
'total_gas': 53900 * 2,
'min_gas': 53900,
'max_gas': 53900,
'avg_gas': 53900,
'call_count': 2,
},
},
},
'ERC20Token': {
'total_method_gas': 51567 * 8,
'min_deployment_gas': None,
'max_deployment_gas': None,
'avg_deployment_gas': 733462,
'methods': {
'transfer(address,uint256)': {
'total_gas': 51567 * 8,
'min_gas': 51567,
'max_gas': 51567,
'avg_gas': 51567,
'call_count': 8,
},
},
},
'GnosisSafe': {
'total_method_gas': 94816 * 85 + 201944 * 49,
'min_deployment_gas': None,
'max_deployment_gas': None,
'avg_deployment_gas': None,
'methods': {
'execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)': {
'total_gas': 94816 * 85,
'min_gas': 59563,
'max_gas': 151736,
'avg_gas': 94816,
'call_count': 85,
},
'setup(address[],uint256,address,bytes,address,address,uint256,address)': {
'total_gas': 201944 * 49,
'min_gas': 167642,
'max_gas': 263690,
'avg_gas': 201944,
'call_count': 49,
},
},
},
'GnosisSafeProxyFactory': {
'total_method_gas': 105568 * 52,
'min_deployment_gas': None,
'max_deployment_gas': None,
'avg_deployment_gas': None,
'methods': {
'createProxy(address,bytes)': {
'total_gas': 105568 * 52,
'min_gas': 105568,
'max_gas': 105580,
'avg_gas': 105568,
'call_count': 52,
},
},
},
}
}
self.assertEqual(asdict(parsed_report), expected_report)
def test_parse_report_should_fail_if_report_is_empty(self):
text_report = ""
with self.assertRaises(ReportValidationError) as manager:
parse_report(text_report)
self.assertEqual(str(manager.exception), "Report header not found.")
def test_parse_report_should_fail_if_report_has_no_header(self):
text_report = dedent("""
| Methods |
| ERC1155Token · mint() · 1 · 3 · 2 · 6 · - |
| Deployments · · % of limit ·
| ERC1155Token · - · - · 5 · 1 % · - |
""").strip('\n')
with self.assertRaises(ReportValidationError) as manager:
parse_report(text_report)
self.assertEqual(str(manager.exception), "Report header not found.")
def test_parse_report_should_fail_if_data_rows_have_no_headers(self):
text_report = dedent("""
| ERC1155Token · mint() · 1 · 3 · 2 · 6 · - |
""").strip('\n')
expected_message = dedent("""
Parsing error on line 1: Found data row without a section header.
| ERC1155Token | mint() | 1 | 3 | 2 | 6 | - |
""").strip('\n')
with self.assertRaises(ReportParsingError) as manager:
parse_report(text_report)
self.assertEqual(str(manager.exception), expected_message)
def test_parse_report_should_fail_if_report_has_more_than_one_header(self):
text_report = dedent("""
| Solc version: 0.8.10 · Optimizer enabled: true · Runs: 200 · Block limit: 100000000 gas |
| Solc version: 0.8.9 · Optimizer enabled: false · Runs: 111 · Block limit: 999999999 gas |
""").strip('\n')
expected_message = dedent("""
Parsing error on line 2: Duplicate report header.
| Solc version: 0.8.9 | Optimizer enabled: false | Runs: 111 | Block limit: 999999999 gas |
""").strip('\n')
with self.assertRaises(ReportParsingError) as manager:
parse_report(text_report)
self.assertEqual(str(manager.exception), expected_message)
def test_parse_report_should_fail_if_row_matching_same_method_call_appears_twice(self):
text_report = dedent("""
| Methods |
| ERC1155Token · mint() · 47934 · 59804 · 57826 · 6 · - |
| ERC1155Token · mint() · 11111 · 22222 · 33333 · 4 · - |
""").strip('\n')
expected_message = dedent("""
Parsing error on line 3: Duplicate method row for 'ERC1155Token.mint()'.
| ERC1155Token | mint() | 11111 | 22222 | 33333 | 4 | - |
""").strip('\n')
with self.assertRaises(ReportParsingError) as manager:
parse_report(text_report)
self.assertEqual(str(manager.exception), expected_message)
def test_parse_report_should_fail_if_row_matching_same_contract_deployment_appears_twice(self):
text_report = dedent("""
| Deployments · · % of limit ·
| ERC1155Token · - · - · 525869 · 0.5 % · - |
| ERC1155Token · - · - · 111111 · 0.6 % · - |
""").strip('\n')
expected_message = dedent("""
Parsing error on line 3: Duplicate contract deployment row for 'ERC1155Token'.
| ERC1155Token | - | - | 111111 | 0.6 % | - |
""").strip('\n')
with self.assertRaises(ReportParsingError) as manager:
parse_report(text_report)
self.assertEqual(str(manager.exception), expected_message)
def test_parse_report_should_fail_if_method_row_appears_under_deployments_header(self):
text_report = dedent("""
| Deployments · · % of limit ·
| ERC1155Token · mint() · 47934 · 59804 · 57826 · 6 · - |
""").strip('\n')
expected_message = dedent("""
Parsing error on line 2: Expected a table row with deployment details.
| ERC1155Token | mint() | 47934 | 59804 | 57826 | 6 | - |
""").strip('\n')
with self.assertRaises(ReportParsingError) as manager:
parse_report(text_report)
self.assertEqual(str(manager.exception), expected_message)
def test_parse_report_should_fail_if_deployment_row_appears_under_methods_header(self):
text_report = dedent("""
| Methods |
| ERC1155Token · - · - · 525869 · 5 · - |
""").strip('\n')
expected_message = dedent("""
Parsing error on line 2: Expected a table row with method details.
| ERC1155Token | - | - | 525869 | 5 | - |
""").strip('\n')
with self.assertRaises(ReportParsingError) as manager:
parse_report(text_report)
self.assertEqual(str(manager.exception), expected_message)

View File

@ -100,7 +100,7 @@ ImportCheck checkImport(
return ImportCheck::OK();
static regex const sourceNotFoundErrorRegex{
R"(^Error \(6275\): Source ".+" not found: (.*)\.\n)"
R"(^Error \(6275\): Source "[^"]+" not found: (.*)\.\n)"
R"(\s*--> .*<stdin>:\d+:\d+:\n)"
R"(\s*\|\n)"
R"(\d+\s*\| import '.+';\n)"
@ -110,12 +110,12 @@ ImportCheck checkImport(
smatch submatches;
if (!regex_match(cliResult.stderrContent, submatches, sourceNotFoundErrorRegex))
return ImportCheck::Unknown("Unexpected stderr content: '" + cliResult.stderrContent + "'");
if (submatches[1] != "File not found" && submatches[1] != "File outside of allowed directories")
if (submatches[1] != "File not found" && !boost::starts_with(string(submatches[1]), "File outside of allowed directories"))
return ImportCheck::Unknown("Unexpected error message: '" + cliResult.stderrContent + "'");
if (submatches[1] == "File not found")
return ImportCheck::FileNotFound();
else if (submatches[1] == "File outside of allowed directories")
else if (boost::starts_with(string(submatches[1]), "File outside of allowed directories"))
return ImportCheck::PathDisallowed();
else
return ImportCheck::Unknown("Unexpected error message '" + submatches[1].str() + "'");

View File

@ -58,7 +58,7 @@ optional<CompilerOutput> SolidityCompilationFramework::compileContract()
else
contractName = m_compilerInput.contractName;
evmasm::LinkerObject obj = m_compiler.object(contractName);
Json::Value methodIdentifiers = m_compiler.methodIdentifiers(contractName);
Json::Value methodIdentifiers = m_compiler.interfaceSymbols(contractName)["methods"];
return CompilerOutput{obj.bytecode, methodIdentifiers};
}
}

View File

@ -91,7 +91,7 @@ public:
/// @returns method identifiers in contract called @param _contractName.
Json::Value methodIdentifiers(std::string const& _contractName)
{
return m_compiler.methodIdentifiers(_contractName);
return m_compiler.interfaceSymbols(_contractName)["methods"];
}
/// @returns Compilation output comprising EVM bytecode and list of
/// method identifiers in contract if compilation is successful,