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

This commit is contained in:
chriseth 2022-03-07 16:34:55 +01:00
commit 6b88e470ff
259 changed files with 2810 additions and 1360 deletions

View File

@ -9,16 +9,16 @@ version: 2.1
parameters:
ubuntu-2004-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-10
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:e61939751ff9777307857361f712b581bfc8a8aaae75fab7b50febc764710587"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-11
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9928dc357829e475e8729c62a1c2d495dbb41cb9fe4c4b115a5568be8e1ed69e"
ubuntu-2004-clang-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-10
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:0de8c68f084120b2a165406e3a0c2aab58b32f5b7182c2322245245f1d243b8d"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-11
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:72fb9574c90e8ef908dce4c9dd9788ff4de708b504d970cd9146eed8911c313e"
ubuntu-1604-clang-ossfuzz-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-15
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:87f1a57586eec194a6217ab624efc69d3d9af2f7ac8ca36497ad57488c2f08ae"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-16
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:fe54d8e5409827d43edb0dc8ad0d9e4232a675050ceb271c873b73e5ee267938"
emscripten-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:emscripten-9

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
/docs/style-guide.rst @fulldecent

View File

@ -13,15 +13,15 @@ Breaking changes:
### 0.8.13 (unreleased)
Language Features:
* General: Allow annotating inline assembly as memory-safe to allow optimizations and stack limit evasion that rely on respecting Solidity's memory model.
Compiler Features:
* JSON-AST: Added selector field for errors and events.
Bugfixes:
### 0.8.12 (2022-02-16)
Language Features:

View File

@ -26,7 +26,11 @@
- [ ] Click the `PUBLISH RELEASE` button on the release page, creating the tag.
- [ ] Wait for the CI runs on the tag itself.
### Download Binaries
### Upload Release Artifacts
- [ ] Switch to the tag that archives have to be created for.
- [ ] Create the ``prerelease.txt`` file: (``echo -n > prerelease.txt``).
- [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. This will create the tarball in a directory called ``upload``.
- [ ] Take the tarball from the upload directory (its name should be ``solidity_x.x.x.tar.gz``, otherwise ``prerelease.txt`` was missing in the step before) and upload the source tarball to the release page.
- [ ] Take the ``solc.exe`` binary from the ``b_win_release`` run of the released commit in circle-ci and add it to the release page as ``solc-windows.exe``.
- [ ] Take the ``solc`` binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``.
- [ ] Take the ``solc`` binary from the ``b_ubu_static`` run of the released commit in circle-ci and add it to the release page as ``solc-static-linux``.

View File

@ -89,8 +89,8 @@ For most of the topics the compiler will provide suggestions.
* Explicit data location for all variables of struct, array or mapping types is
now mandatory. This is also applied to function parameters and return
variables. For example, change ``uint[] x = m_x`` to ``uint[] storage x =
m_x``, and ``function f(uint[][] x)`` to ``function f(uint[][] memory x)``
variables. For example, change ``uint[] x = z`` to ``uint[] storage x =
z``, and ``function f(uint[][] x)`` to ``function f(uint[][] memory x)``
where ``memory`` is the data location and might be replaced by ``storage`` or
``calldata`` accordingly. Note that ``external`` functions require
parameters with a data location of ``calldata``.
@ -483,7 +483,7 @@ New version:
return data;
}
using address_make_payable for address;
using AddressMakePayable for address;
// Data location for 'arr' must be specified
function g(uint[] memory /* arr */, bytes8 x, OtherContract otherContract, address unknownContract) public payable {
// 'otherContract.transfer' is not provided.
@ -500,7 +500,7 @@ New version:
// 'address payable' should be used whenever possible.
// To increase clarity, we suggest the use of a library for
// the conversion (provided after the contract in this example).
address payable addr = unknownContract.make_payable();
address payable addr = unknownContract.makePayable();
require(addr.send(1 ether));
// Since uint32 (4 bytes) is smaller than bytes8 (8 bytes),
@ -516,8 +516,8 @@ New version:
// We can define a library for explicitly converting ``address``
// to ``address payable`` as a workaround.
library address_make_payable {
function make_payable(address x) internal pure returns (address payable) {
library AddressMakePayable {
function makePayable(address x) internal pure returns (address payable) {
return address(uint160(x));
}
}

View File

@ -45,19 +45,19 @@ Solidity language without a compiler change.
pragma solidity >=0.4.16 <0.9.0;
library GetCode {
function at(address _addr) public view returns (bytes memory o_code) {
function at(address _addr) public view returns (bytes memory code) {
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(_addr)
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
o_code := mload(0x40)
// by using code = new bytes(size)
code := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(o_code, size)
mstore(code, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(o_code, 0x20), 0, size)
extcodecopy(_addr, add(code, 0x20), 0, size)
}
}
}
@ -139,7 +139,7 @@ the variable will not point beyond ``calldatasize()`` is performed.
For external function pointers the address and the function selector can be
accessed using ``x.address`` and ``x.selector``.
The selector consists of four right-aligned bytes.
Both values are can be assigned to. For example:
Both values can be assigned to. For example:
.. code-block:: solidity
:force:
@ -228,6 +228,11 @@ of their block is reached.
Conventions in Solidity
-----------------------
.. _assembly-typed-variables:
Values of Typed Variables
=========================
In contrast to EVM assembly, Solidity has types which are narrower than 256 bits,
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that
types can be shorter than 256
@ -237,6 +242,11 @@ This means that if you access such a variable
from within inline assembly, you might have to manually clean the higher-order bits
first.
.. _assembly-memory-management:
Memory Management
=================
Solidity manages memory in the following way. There is a "free memory pointer"
at position ``0x40`` in memory. If you want to allocate memory, use the memory
starting from where this pointer points at and update it.
@ -268,3 +278,99 @@ first slot of the array and followed by the array elements.
Statically-sized memory arrays do not have a length field, but it might be added later
to allow better convertibility between statically- and dynamically-sized arrays, so
do not rely on this.
Memory Safety
=============
Without the use of inline assembly, the compiler can rely on memory to remain in a well-defined
state at all times. This is especially relevant for :ref:`the new code generation pipeline via Yul IR <ir-breaking-changes>`:
this code generation path can move local variables from stack to memory to avoid stack-too-deep errors and
perform additional memory optimizations, if it can rely on certain assumptions about memory use.
While we recommend to always respect Solidity's memory model, inline assembly allows you to use memory
in an incompatible way. Therefore, moving stack variables to memory and additional memory optimizations are,
by default, disabled in the presence of any inline assembly block that contains a memory operation or assigns
to solidity variables in memory.
However, you can specifically annotate an assembly block to indicate that it in fact respects Solidity's memory
model as follows:
.. code-block:: solidity
assembly ("memory-safe") {
...
}
In particular, a memory-safe assembly block may only access the following memory ranges:
- Memory allocated by yourself using a mechanism like the ``allocate`` function described above.
- Memory allocated by Solidity, e.g. memory within the bounds of a memory array you reference.
- The scratch space between memory offset 0 and 64 mentioned above.
- Temporary memory that is located *after* the value of the free memory pointer at the beginning of the assembly block,
i.e. memory that is "allocated" at the free memory pointer without updating the free memory pointer.
Furthermore, if the assembly block assigns to Solidity variables in memory, you need to assure that accesses to
the Solidity variables only access these memory ranges.
Since this is mainly about the optimizer, these restrictions still need to be followed, even if the assembly block
reverts or terminates. As an example, the following assembly snippet is not memory safe:
.. code-block:: solidity
assembly {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
But the following is:
.. code-block:: solidity
assembly ("memory-safe") {
let p := mload(0x40)
returndatacopy(p, 0, returndatasize())
revert(p, returndatasize())
}
Note that you do not need to update the free memory pointer if there is no following allocation,
but you can only use memory starting from the current offset given by the free memory pointer.
If the memory operations use a length of zero, it is also fine to just use any offset (not only if it falls into the scratch space):
.. code-block:: solidity
assembly ("memory-safe") {
revert(0, 0)
}
Note that not only memory operations in inline assembly itself can be memory-unsafe, but also assignments to
solidity variables of reference type in memory. For example the following is not memory-safe:
.. code-block:: solidity
bytes memory x;
assembly {
x := 0x40
}
x[0x20] = 0x42;
Inline assembly that neither involves any operations that access memory nor assigns to any solidity variables
in memory is automatically considered memory-safe and does not need to be annotated.
.. warning::
It is your responsibility to make sure that the assembly actually satisfies the memory model. If you annotate
an assembly block as memory-safe, but violate one of the memory assumptions, this **will** lead to incorrect and
undefined behaviour that cannot easily be discovered by testing.
In case you are developing a library that is meant to be compatible across multiple versions
of solidity, you can use a special comment to annotate an assembly block as memory-safe:
.. code-block:: solidity
/// @solidity memory-safe-assembly
assembly {
...
}
Note that we will disallow the annotation via comment in a future breaking release, so if you are not concerned with
backwards-compatibility with older compiler versions, prefer using the dialect string.

View File

@ -102,10 +102,10 @@ two integers passed as function parameters, then you use something like:
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
returns (uint sum, uint product)
{
o_sum = _a + _b;
o_product = _a * _b;
sum = _a + _b;
product = _a * _b;
}
}
@ -129,7 +129,7 @@ statement:
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
returns (uint sum, uint product)
{
return (_a + _b, _a * _b);
}

View File

@ -120,7 +120,7 @@ It is also possible to extend elementary types in that way:
}
Note that all external library calls are actual EVM function calls. This means that
if you pass memory or value types, a copy will be performed, even of the
if you pass memory or value types, a copy will be performed, even in case of the
``self`` variable. The only situation where no copy will be performed
is when storage reference variables are used or when internal library
functions are called.

View File

@ -251,6 +251,12 @@ mode AssemblyBlockMode;
AssemblyDialect: '"evmasm"';
AssemblyLBrace: '{' -> popMode, pushMode(YulMode);
AssemblyFlagString: '"' DoubleQuotedStringCharacter+ '"';
AssemblyBlockLParen: '(';
AssemblyBlockRParen: ')';
AssemblyBlockComma: ',';
AssemblyBlockWS: [ \t\r\n\u000C]+ -> skip ;
AssemblyBlockCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ;
AssemblyBlockLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ;

View File

@ -476,7 +476,13 @@ revertStatement: Revert expression callArgumentList Semicolon;
* The contents of an inline assembly block use a separate scanner/lexer, i.e. the set of keywords and
* allowed identifiers is different inside an inline assembly block.
*/
assemblyStatement: Assembly AssemblyDialect? AssemblyLBrace yulStatement* YulRBrace;
assemblyStatement: Assembly AssemblyDialect? assemblyFlags? AssemblyLBrace yulStatement* YulRBrace;
/**
* Assembly flags.
* Comma-separated list of double-quoted strings as flags.
*/
assemblyFlags: AssemblyBlockLParen AssemblyFlagString (AssemblyBlockComma AssemblyFlagString)* AssemblyBlockRParen;
//@doc:inline
variableDeclarationList: variableDeclarations+=variableDeclaration (Comma variableDeclarations+=variableDeclaration)*;

View File

@ -10,7 +10,7 @@ A Simple Smart Contract
Let us begin with a basic example that sets the value of a variable and exposes
it for other contracts to access. It is fine if you do not understand
everything right now, we will go into more detail later.
everything right now, we will go into more details later.
Storage Example
===============

View File

@ -1,6 +1,8 @@
.. index: ir breaking changes
.. _ir-breaking-changes:
*********************************
Solidity IR-based Codegen Changes
*********************************

View File

@ -204,7 +204,7 @@ Yes:
.. code-block:: solidity
thisIsALongNestedMapping[being][set][to_some_value] = someFunction(
thisIsALongNestedMapping[being][set][toSomeValue] = someFunction(
argument1,
argument2,
argument3,
@ -215,7 +215,7 @@ No:
.. code-block:: solidity
thisIsALongNestedMapping[being][set][to_some_value] = someFunction(argument1,
thisIsALongNestedMapping[being][set][toSomeValue] = someFunction(argument1,
argument2,
argument3,
argument4);
@ -439,15 +439,15 @@ Yes:
x = 1;
y = 2;
long_variable = 3;
longVariable = 3;
No:
.. code-block:: solidity
x = 1;
y = 2;
long_variable = 3;
x = 1;
y = 2;
longVariable = 3;
Don't include a whitespace in the receive and fallback functions:
@ -1092,12 +1092,10 @@ naming styles.
* ``b`` (single lowercase letter)
* ``B`` (single uppercase letter)
* ``lowercase``
* ``lower_case_with_underscores``
* ``UPPERCASE``
* ``UPPER_CASE_WITH_UNDERSCORES``
* ``CapitalizedWords`` (or CapWords)
* ``mixedCase`` (differs from CapitalizedWords by initial lowercase character!)
* ``Capitalized_Words_With_Underscores``
.. note:: When using initialisms in CapWords, capitalize all the letters of the initialisms. Thus HTTPServerError is better than HttpServerError. When using initialisms in mixedCase, capitalize all the letters of the initialisms, except keep the first one lower case if it is the beginning of the name. Thus xmlHTTPRequest is better than XMLHTTPRequest.
@ -1256,7 +1254,7 @@ Enums, in the style of simple type declarations, should be named using the CapWo
Avoiding Naming Collisions
==========================
* ``single_trailing_underscore_``
* ``singleTrailingUnderscore_``
This convention is suggested when the desired name collides with that of a
built-in or otherwise reserved name.

View File

@ -126,7 +126,7 @@ the ``sum`` function iterates over to sum all the values.
:force:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.8 <0.9.0;
pragma solidity ^0.8.8;
struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }
@ -137,6 +137,8 @@ the ``sum`` function iterates over to sum all the values.
uint size;
}
type Iterator is uint;
library IterableMapping {
function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
uint keyIndex = self.data[key].keyIndex;
@ -166,25 +168,29 @@ the ``sum`` function iterates over to sum all the values.
return self.data[key].keyIndex > 0;
}
function iterate_start(itmap storage self) internal view returns (uint keyIndex) {
return iterate_next(self, type(uint).max);
function iterateStart(itmap storage self) internal view returns (Iterator) {
return iteratorSkipDeleted(self, 0);
}
function iterate_valid(itmap storage self, uint keyIndex) internal view returns (bool) {
return keyIndex < self.keys.length;
function iterateValid(itmap storage self, Iterator iterator) internal view returns (bool) {
return Iterator.unwrap(iterator) < self.keys.length;
}
function iterate_next(itmap storage self, uint keyIndex) internal view returns (uint r_keyIndex) {
keyIndex++;
while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
keyIndex++;
return keyIndex;
function iterateNext(itmap storage self, Iterator iterator) internal view returns (Iterator) {
return iteratorSkipDeleted(self, Iterator.unwrap(iterator) + 1);
}
function iterate_get(itmap storage self, uint keyIndex) internal view returns (uint key, uint value) {
function iterateGet(itmap storage self, Iterator iterator) internal view returns (uint key, uint value) {
uint keyIndex = Iterator.unwrap(iterator);
key = self.keys[keyIndex].key;
value = self.data[key].value;
}
function iteratorSkipDeleted(itmap storage self, uint keyIndex) private view returns (Iterator) {
while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
keyIndex++;
return Iterator.wrap(keyIndex);
}
}
// How to use it
@ -206,11 +212,11 @@ the ``sum`` function iterates over to sum all the values.
// Computes the sum of all stored data.
function sum() public view returns (uint s) {
for (
uint i = data.iterate_start();
data.iterate_valid(i);
i = data.iterate_next(i)
Iterator i = data.iterateStart();
data.iterateValid(i);
i = data.iterateNext(i)
) {
(, uint value) = data.iterate_get(i);
(, uint value) = data.iterateGet(i);
s += value;
}
}

View File

@ -190,11 +190,11 @@ If you want to use string parameters or other types that are not implicitly conv
contract C {
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);
string memory concatString = string.concat(s, string(bc), "Literal", sm);
assert((bytes(s).length + bc.length + 7 + bytes(sm).length) == bytes(concatString).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);
bytes memory concatBytes = bytes.concat(bytes(s), bc, bc[:2], "Literal", bytes(sm), b);
assert((bytes(s).length + bc.length + 2 + 7 + bytes(sm).length + b.length) == concatBytes.length);
}
}
@ -376,20 +376,20 @@ Array Members
pragma solidity >=0.6.0 <0.9.0;
contract ArrayContract {
uint[2**20] m_aLotOfIntegers;
uint[2**20] aLotOfIntegers;
// Note that the following is not a pair of dynamic arrays but a
// dynamic array of pairs (i.e. of fixed size arrays of length two).
// Because of that, T[] is always a dynamic array of T, even if T
// itself is an array.
// Data location for all state variables is storage.
bool[2][] m_pairsOfFlags;
bool[2][] pairsOfFlags;
// newPairs is stored in memory - the only possibility
// for public contract function arguments
function setAllFlagPairs(bool[2][] memory newPairs) public {
// assignment to a storage array performs a copy of ``newPairs`` and
// replaces the complete array ``m_pairsOfFlags``.
m_pairsOfFlags = newPairs;
// replaces the complete array ``pairsOfFlags``.
pairsOfFlags = newPairs;
}
struct StructType {
@ -411,45 +411,45 @@ Array Members
function setFlagPair(uint index, bool flagA, bool flagB) public {
// access to a non-existing index will throw an exception
m_pairsOfFlags[index][0] = flagA;
m_pairsOfFlags[index][1] = flagB;
pairsOfFlags[index][0] = flagA;
pairsOfFlags[index][1] = flagB;
}
function changeFlagArraySize(uint newSize) public {
// using push and pop is the only way to change the
// length of an array
if (newSize < m_pairsOfFlags.length) {
while (m_pairsOfFlags.length > newSize)
m_pairsOfFlags.pop();
} else if (newSize > m_pairsOfFlags.length) {
while (m_pairsOfFlags.length < newSize)
m_pairsOfFlags.push();
if (newSize < pairsOfFlags.length) {
while (pairsOfFlags.length > newSize)
pairsOfFlags.pop();
} else if (newSize > pairsOfFlags.length) {
while (pairsOfFlags.length < newSize)
pairsOfFlags.push();
}
}
function clear() public {
// these clear the arrays completely
delete m_pairsOfFlags;
delete m_aLotOfIntegers;
delete pairsOfFlags;
delete aLotOfIntegers;
// identical effect here
m_pairsOfFlags = new bool[2][](0);
pairsOfFlags = new bool[2][](0);
}
bytes m_byteData;
bytes byteData;
function byteArrays(bytes memory data) public {
// byte arrays ("bytes") are different as they are stored without padding,
// but can be treated identical to "uint8[]"
m_byteData = data;
byteData = data;
for (uint i = 0; i < 7; i++)
m_byteData.push();
m_byteData[3] = 0x08;
delete m_byteData[2];
byteData.push();
byteData[3] = 0x08;
delete byteData[2];
}
function addFlag(bool[2] memory flag) public returns (uint) {
m_pairsOfFlags.push(flag);
return m_pairsOfFlags.length;
pairsOfFlags.push(flag);
return pairsOfFlags.length;
}
function createMemoryArray(uint size) public pure returns (bytes memory) {

View File

@ -409,12 +409,13 @@ Input Description
"source1.sol": ["contract1"],
"source2.sol": ["contract2", "contract3"]
},
// Choose whether division and modulo operations should be replaced by
// multiplication with slack variables. Default is `true`.
// Using `false` here is recommended if you are using the CHC engine
// Choose how division and modulo operations should be encoded.
// When using `false` they are replaced by multiplication with slack
// variables. This is the default.
// Using `true` here is recommended if you are using the CHC engine
// and not using Spacer as the Horn solver (using Eldarica, for example).
// See the Formal Verification section for a more detailed explanation of this option.
"divModWithSlacks": true,
"divModNoSlacks": false,
// Choose which model checker engine to use: all (default), bmc, chc, none.
"engine": "chc",
// Choose which types of invariants should be reported to the user: contract, reentrancy.

View File

@ -768,7 +768,7 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a
+-------------------------+-----+---+-----------------------------------------------------------------+
| Instruction | | | Explanation |
+=========================+=====+===+=================================================================+
| stop() + `-` | F | stop execution, identical to return(0, 0) |
| stop() | `-` | F | stop execution, identical to return(0, 0) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| add(x, y) | | F | x + y |
+-------------------------+-----+---+-----------------------------------------------------------------+

View File

@ -397,26 +397,6 @@ AssemblyItem Assembly::newImmutableAssignment(string const& _identifier)
return AssemblyItem{AssignImmutable, h};
}
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
{
OptimiserSettings settings;
settings.isCreation = _isCreation;
settings.runInliner = true;
settings.runJumpdestRemover = true;
settings.runPeephole = true;
if (_enable)
{
settings.runDeduplicate = true;
settings.runCSE = true;
settings.runConstantOptimiser = true;
}
settings.evmVersion = _evmVersion;
settings.expectedExecutionsPerDeployment = _runs;
optimise(settings);
return *this;
}
Assembly& Assembly::optimise(OptimiserSettings const& _settings)
{
optimiseInternal(_settings, {});

View File

@ -134,13 +134,6 @@ public:
/// is optimised according to the settings in @a _settings.
Assembly& optimise(OptimiserSettings const& _settings);
/// Modify (if @a _enable is set) and return the current assembly such that creation and
/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
/// If @a _enable is not set, will perform some simple peephole optimizations.
Assembly& optimise(bool _enable, langutil::EVMVersion _evmVersion, bool _isCreation, size_t _runs);
/// Create a text representation of the assembly.
std::string assemblyString(
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),

View File

@ -26,6 +26,7 @@
#include <libevmasm/Exceptions.h>
#include <liblangutil/SourceLocation.h>
#include <libsolutil/Common.h>
#include <libsolutil/Numeric.h>
#include <libsolutil/Assertions.h>
#include <optional>
#include <iostream>

View File

@ -11,6 +11,8 @@ set(sources
ConstantOptimiser.h
ControlFlowGraph.cpp
ControlFlowGraph.h
Disassemble.cpp
Disassemble.h
Exceptions.h
ExpressionClasses.cpp
ExpressionClasses.h

View File

@ -26,6 +26,7 @@
#include <liblangutil/EVMVersion.h>
#include <libsolutil/Numeric.h>
#include <libsolutil/Assertions.h>
#include <vector>

76
libevmasm/Disassemble.cpp Normal file
View File

@ -0,0 +1,76 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libevmasm/Disassemble.h>
#include <libsolutil/Common.h>
#include <libsolutil/CommonIO.h>
#include <functional>
using namespace std;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::evmasm;
void solidity::evmasm::eachInstruction(
bytes const& _mem,
function<void(Instruction,u256 const&)> const& _onInstruction
)
{
for (auto it = _mem.begin(); it < _mem.end(); ++it)
{
Instruction const instr{*it};
int additional = 0;
if (isValidInstruction(instr))
additional = instructionInfo(instr).additional;
u256 data{};
// fill the data with the additional data bytes from the instruction stream
while (additional > 0 && std::next(it) < _mem.end())
{
data <<= 8;
data |= *++it;
--additional;
}
// pad the remaining number of additional octets with zeros
data <<= 8 * additional;
_onInstruction(instr, data);
}
}
string solidity::evmasm::disassemble(bytes const& _mem, string const& _delimiter)
{
stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
if (!isValidInstruction(_instr))
ret << "0x" << std::uppercase << std::hex << static_cast<int>(_instr) << _delimiter;
else
{
InstructionInfo info = instructionInfo(_instr);
ret << info.name;
if (info.additional)
ret << " 0x" << std::uppercase << std::hex << _data;
ret << _delimiter;
}
});
return ret.str();
}

38
libevmasm/Disassemble.h Normal file
View File

@ -0,0 +1,38 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libsolutil/Common.h>
#include <libsolutil/Numeric.h>
#include <libevmasm/Instruction.h>
#include <functional>
#include <string>
namespace solidity::evmasm
{
/// Iterate through EVM code and call a function on each instruction.
void eachInstruction(bytes const& _mem, std::function<void(Instruction, u256 const&)> const& _onInstruction);
/// Convert from EVM code to simple EVM assembly language.
std::string disassemble(bytes const& _mem, std::string const& _delimiter = " ");
}

View File

@ -22,10 +22,6 @@
#include <libevmasm/Instruction.h>
#include <libsolutil/Common.h>
#include <libsolutil/CommonIO.h>
#include <functional>
using namespace std;
using namespace solidity;
using namespace solidity::util;
@ -325,53 +321,6 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Special } }
};
void solidity::evmasm::eachInstruction(
bytes const& _mem,
function<void(Instruction,u256 const&)> const& _onInstruction
)
{
for (auto it = _mem.begin(); it < _mem.end(); ++it)
{
auto instr = Instruction(*it);
int additional = 0;
if (isValidInstruction(instr))
additional = instructionInfo(instr).additional;
u256 data;
// fill the data with the additional data bytes from the instruction stream
while (additional > 0 && std::next(it) < _mem.end())
{
data <<= 8;
data |= *++it;
--additional;
}
// pad the remaining number of additional octets with zeros
data <<= 8 * additional;
_onInstruction(instr, data);
}
}
string solidity::evmasm::disassemble(bytes const& _mem, string const& _delimiter)
{
stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
if (!isValidInstruction(_instr))
ret << "0x" << std::uppercase << std::hex << static_cast<int>(_instr) << _delimiter;
else
{
InstructionInfo info = instructionInfo(_instr);
ret << info.name;
if (info.additional)
ret << " 0x" << std::uppercase << std::hex << _data;
ret << _delimiter;
}
});
return ret.str();
}
InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst)
{
try
@ -380,7 +329,7 @@ InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst)
}
catch (...)
{
return InstructionInfo({"<INVALID_INSTRUCTION: " + toString((unsigned)_inst) + ">", 0, 0, 0, false, Tier::Invalid});
return InstructionInfo({"<INVALID_INSTRUCTION: " + to_string(static_cast<unsigned>(_inst)) + ">", 0, 0, 0, false, Tier::Invalid});
}
}

View File

@ -25,8 +25,6 @@
#include <libevmasm/Exceptions.h>
#include <libsolutil/Common.h>
#include <libsolutil/Assertions.h>
#include <libsolutil/Numeric.h>
#include <functional>
namespace solidity::evmasm
{
@ -217,25 +215,25 @@ inline bool isLogInstruction(Instruction _inst)
/// @returns the number of PUSH Instruction _inst
inline unsigned getPushNumber(Instruction _inst)
{
return (uint8_t)_inst - unsigned(Instruction::PUSH1) + 1;
return static_cast<uint8_t>(_inst) - unsigned(Instruction::PUSH1) + 1;
}
/// @returns the number of DUP Instruction _inst
inline unsigned getDupNumber(Instruction _inst)
{
return (uint8_t)_inst - unsigned(Instruction::DUP1) + 1;
return static_cast<uint8_t>(_inst) - unsigned(Instruction::DUP1) + 1;
}
/// @returns the number of SWAP Instruction _inst
inline unsigned getSwapNumber(Instruction _inst)
{
return (uint8_t)_inst - unsigned(Instruction::SWAP1) + 1;
return static_cast<uint8_t>(_inst) - unsigned(Instruction::SWAP1) + 1;
}
/// @returns the number of LOG Instruction _inst
inline unsigned getLogNumber(Instruction _inst)
{
return (uint8_t)_inst - unsigned(Instruction::LOG0);
return static_cast<uint8_t>(_inst) - unsigned(Instruction::LOG0);
}
/// @returns the PUSH<_number> instruction
@ -266,7 +264,7 @@ inline Instruction logInstruction(unsigned _number)
return Instruction(unsigned(Instruction::LOG0) + _number);
}
enum class Tier : unsigned
enum class Tier
{
Zero = 0, // 0, Zero
Base, // 2, Quick
@ -301,10 +299,4 @@ bool isValidInstruction(Instruction _inst);
/// Convert from string mnemonic to Instruction type.
extern const std::map<std::string, Instruction> c_instructions;
/// Iterate through EVM code and call a function on each instruction.
void eachInstruction(bytes const& _mem, std::function<void(Instruction, u256 const&)> const& _onInstruction);
/// Convert from EVM code to simple EVM assembly language.
std::string disassemble(bytes const& _mem, std::string const& _delimiter = " ");
}

View File

@ -29,6 +29,125 @@ using namespace std;
using namespace solidity;
using namespace solidity::evmasm;
vector<SemanticInformation::Operation> SemanticInformation::readWriteOperations(Instruction _instruction)
{
switch (_instruction)
{
case Instruction::SSTORE:
case Instruction::SLOAD:
{
assertThrow(memory(_instruction) == Effect::None, OptimizerException, "");
assertThrow(storage(_instruction) != Effect::None, OptimizerException, "");
Operation op;
op.effect = storage(_instruction);
op.location = Location::Storage;
op.startParameter = 0;
// We know that exactly one slot is affected.
op.lengthConstant = 1;
return {op};
}
case Instruction::MSTORE:
case Instruction::MSTORE8:
case Instruction::MLOAD:
{
assertThrow(memory(_instruction) != Effect::None, OptimizerException, "");
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
Operation op;
op.effect = memory(_instruction);
op.location = Location::Memory;
op.startParameter = 0;
if (_instruction == Instruction::MSTORE || _instruction == Instruction::MLOAD)
op.lengthConstant = 32;
else if (_instruction == Instruction::MSTORE8)
op.lengthConstant = 1;
return {op};
}
case Instruction::REVERT:
case Instruction::RETURN:
case Instruction::KECCAK256:
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
{
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
assertThrow(memory(_instruction) == Effect::Read, OptimizerException, "");
Operation op;
op.effect = memory(_instruction);
op.location = Location::Memory;
op.startParameter = 0;
op.lengthParameter = 1;
return {op};
}
case Instruction::EXTCODECOPY:
{
assertThrow(memory(_instruction) == Effect::Write, OptimizerException, "");
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
Operation op;
op.effect = memory(_instruction);
op.location = Location::Memory;
op.startParameter = 1;
op.lengthParameter = 3;
return {op};
}
case Instruction::CODECOPY:
case Instruction::CALLDATACOPY:
case Instruction::RETURNDATACOPY:
{
assertThrow(memory(_instruction) == Effect::Write, OptimizerException, "");
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
Operation op;
op.effect = memory(_instruction);
op.location = Location::Memory;
op.startParameter = 0;
op.lengthParameter = 2;
return {op};
}
case Instruction::STATICCALL:
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
{
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction).args);
vector<Operation> operations{
Operation{Location::Memory, Effect::Read, paramCount - 4, paramCount - 3, {}},
Operation{Location::Storage, Effect::Read, {}, {}, {}}
};
if (_instruction != Instruction::STATICCALL)
operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}});
operations.emplace_back(Operation{
Location::Memory,
Effect::Write,
paramCount - 2,
paramCount - 1,
{}
});
return operations;
}
case Instruction::CREATE:
case Instruction::CREATE2:
return vector<Operation>{
Operation{
Location::Memory,
Effect::Read,
1,
2,
{}
},
Operation{Location::Storage, Effect::Read, {}, {}, {}},
Operation{Location::Storage, Effect::Write, {}, {}, {}}
};
case Instruction::MSIZE:
// This is just to satisfy the assert below.
return vector<Operation>{};
default:
assertThrow(storage(_instruction) == None && memory(_instruction) == None, AssemblyException, "");
}
return {};
}
bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant)
{
switch (_item.type())
@ -49,7 +168,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
case PushLibraryAddress:
case PushImmutable:
return false;
case Operation:
case evmasm::Operation:
{
if (isSwapInstruction(_item) || isDupInstruction(_item))
return false;
@ -79,7 +198,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
{
if (_item.type() != Operation)
if (_item.type() != evmasm::Operation)
return false;
switch (_item.instruction())
{
@ -97,14 +216,14 @@ bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
{
if (_item.type() != Operation)
if (_item.type() != evmasm::Operation)
return false;
return evmasm::isDupInstruction(_item.instruction());
}
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
{
if (_item.type() != Operation)
if (_item.type() != evmasm::Operation)
return false;
return evmasm::isSwapInstruction(_item.instruction());
}
@ -116,7 +235,7 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
{
if (_item.type() != Operation)
if (_item.type() != evmasm::Operation)
return false;
switch (_item.instruction())
{
@ -166,7 +285,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
{
assertThrow(_item.type() != VerbatimBytecode, AssemblyException, "");
if (_item.type() != Operation)
if (_item.type() != evmasm::Operation)
return true;
switch (_item.instruction())

View File

@ -26,6 +26,9 @@
#include <libevmasm/Instruction.h>
#include <optional>
#include <vector>
namespace solidity::evmasm
{
@ -45,6 +48,32 @@ struct SemanticInformation
Write
};
enum class Location { Storage, Memory };
/**
* Represents a read or write operation from or to one of the data locations.
*/
struct Operation
{
Location location;
Effect effect;
/// Start of affected area as an index into the parameters.
/// Unknown if not provided.
std::optional<size_t> startParameter;
/// Length of the affected area as an index into the parameters (if this is an opcode).
/// Unknown if neither this nor lengthConstant is provided.
std::optional<size_t> lengthParameter;
/// Length as a constant.
/// Unknown if neither this nor lengthArgument is provided.
std::optional<size_t> lengthConstant;
};
/// @returns the sequence of read write operations performed by the instruction.
/// Order matters.
/// For external calls, there is just one unknown read and one unknown write operation,
/// event though there might be multiple.
static std::vector<Operation> readWriteOperations(Instruction _instruction);
/// @returns true if the given items starts a new block for common subexpression analysis.
/// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize.
static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant);

View File

@ -30,6 +30,7 @@
#include <liblangutil/Common.h>
#include <range/v3/algorithm/any_of.hpp>
#include <range/v3/view/filter.hpp>
#include <boost/algorithm/string.hpp>
@ -162,6 +163,81 @@ bool DocStringTagParser::visit(ErrorDefinition const& _error)
return true;
}
bool DocStringTagParser::visit(InlineAssembly const& _assembly)
{
if (!_assembly.documentation())
return true;
StructuredDocumentation documentation{-1, _assembly.location(), _assembly.documentation()};
ErrorList errors;
ErrorReporter errorReporter{errors};
auto docTags = DocStringParser{documentation, errorReporter}.parse();
if (!errors.empty())
{
SecondarySourceLocation ssl;
for (auto const& error: errors)
if (error->comment())
ssl.append(
*error->comment(),
_assembly.location()
);
m_errorReporter.warning(
7828_error,
_assembly.location(),
"Inline assembly has invalid NatSpec documentation.",
ssl
);
}
for (auto const& [tagName, tagValue]: docTags)
{
if (tagName == "solidity")
{
vector<string> values;
boost::split(values, tagValue.content, isWhiteSpace);
set<string> valuesSeen;
set<string> duplicates;
for (auto const& value: values | ranges::views::filter(not_fn(&string::empty)))
if (valuesSeen.insert(value).second)
{
if (value == "memory-safe-assembly")
{
if (_assembly.annotation().markedMemorySafe)
m_errorReporter.warning(
8544_error,
_assembly.location(),
"Inline assembly marked as memory safe using both a NatSpec tag and an assembly flag. "
"If you are not concerned with backwards compatibility, only use the assembly flag, "
"otherwise only use the NatSpec tag."
);
_assembly.annotation().markedMemorySafe = true;
}
else
m_errorReporter.warning(
8787_error,
_assembly.location(),
"Unexpected value for @solidity tag in inline assembly: " + value
);
}
else if (duplicates.insert(value).second)
m_errorReporter.warning(
4377_error,
_assembly.location(),
"Value for @solidity tag in inline assembly specified multiple times: " + value
);
}
else
m_errorReporter.warning(
6269_error,
_assembly.location(),
"Unexpected NatSpec tag \"" + tagName + "\" with value \"" + tagValue.content + "\" in inline assembly."
);
}
return true;
}
void DocStringTagParser::checkParameters(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,

View File

@ -48,6 +48,7 @@ private:
bool visit(ModifierDefinition const& _modifier) override;
bool visit(EventDefinition const& _event) override;
bool visit(ErrorDefinition const& _error) override;
bool visit(InlineAssembly const& _assembly) override;
void checkParameters(
CallableDeclaration const& _callable,

View File

@ -334,6 +334,27 @@ bool SyntaxChecker::visit(UnaryOperation const& _operation)
bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly)
{
if (_inlineAssembly.flags())
for (auto flag: *_inlineAssembly.flags())
{
if (*flag == "memory-safe")
{
if (_inlineAssembly.annotation().markedMemorySafe)
m_errorReporter.syntaxError(
7026_error,
_inlineAssembly.location(),
"Inline assembly marked memory-safe multiple times."
);
_inlineAssembly.annotation().markedMemorySafe = true;
}
else
m_errorReporter.warning(
4430_error,
_inlineAssembly.location(),
"Unknown inline assembly flag: \"" + *flag + "\""
);
}
if (!m_useYulOptimizer)
return false;

View File

@ -763,6 +763,7 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType)
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{
bool lvalueAccessToMemoryVariable = false;
// External references have already been resolved in a prior stage and stored in the annotation.
// We run the resolve step again regardless.
yul::ExternalIdentifierAccess::Resolver identifierAccess = [&](
@ -780,6 +781,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{
solAssert(var->type(), "Expected variable type!");
if (_context == yul::IdentifierContext::LValue && var->type()->dataStoredIn(DataLocation::Memory))
lvalueAccessToMemoryVariable = true;
if (var->immutable())
{
m_errorReporter.typeError(3773_error, nativeLocationOf(_identifier), "Assembly access to immutable variables is not supported.");
@ -967,8 +970,11 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations()))
return false;
return true;
solAssert(m_errorReporter.hasErrors());
_inlineAssembly.annotation().hasMemoryEffects =
lvalueAccessToMemoryVariable ||
(analyzer.sideEffects().memory != yul::SideEffects::None);
return false;
}
bool TypeChecker::visit(IfStatement const& _ifStatement)

View File

@ -256,10 +256,9 @@ void ViewPureChecker::reportMutability(
m_errorReporter.typeError(
8961_error,
_location,
"Function declared as " +
"Function cannot be declared as " +
stateMutabilityToString(m_currentFunction->stateMutability()) +
", but this expression (potentially) modifies the state and thus "
"requires non-payable (the default) or payable."
" because this expression (potentially) modifies the state."
);
m_errors = true;
}

View File

@ -1463,19 +1463,26 @@ public:
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
yul::Dialect const& _dialect,
ASTPointer<std::vector<ASTPointer<ASTString>>> _flags,
std::shared_ptr<yul::Block> _operations
):
Statement(_id, _location, _docString), m_dialect(_dialect), m_operations(std::move(_operations)) {}
Statement(_id, _location, _docString),
m_dialect(_dialect),
m_flags(move(_flags)),
m_operations(std::move(_operations))
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
yul::Dialect const& dialect() const { return m_dialect; }
yul::Block const& operations() const { return *m_operations; }
ASTPointer<std::vector<ASTPointer<ASTString>>> const& flags() const { return m_flags; }
InlineAssemblyAnnotation& annotation() const override;
private:
yul::Dialect const& m_dialect;
ASTPointer<std::vector<ASTPointer<ASTString>>> m_flags;
std::shared_ptr<yul::Block> m_operations;
};

View File

@ -220,6 +220,10 @@ struct InlineAssemblyAnnotation: StatementAnnotation
std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences;
/// Information generated during analysis phase.
std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;
/// True, if the assembly block was annotated to be memory-safe.
bool markedMemorySafe = false;
/// True, if the assembly block involves any memory opcode or assigns to variables in memory.
SetOnce<bool> hasMemoryEffects;
};
struct BlockAnnotation: StatementAnnotation, ScopableAnnotation

View File

@ -32,6 +32,7 @@
#include <libsolutil/JSON.h>
#include <libsolutil/UTF8.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Keccak256.h>
#include <boost/algorithm/string/join.hpp>
@ -493,24 +494,36 @@ bool ASTJsonConverter::visit(ModifierInvocation const& _node)
bool ASTJsonConverter::visit(EventDefinition const& _node)
{
m_inEvent = true;
setJsonNode(_node, "EventDefinition", {
std::vector<pair<string, Json::Value>> _attributes = {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("parameters", toJson(_node.parameterList())),
make_pair("anonymous", _node.isAnonymous())
});
};
if (m_stackState >= CompilerStack::State::AnalysisPerformed)
_attributes.emplace_back(
make_pair(
"eventSelector",
toHex(u256(h256::Arith(util::keccak256(_node.functionType(true)->externalSignature()))))
));
setJsonNode(_node, "EventDefinition", std::move(_attributes));
return false;
}
bool ASTJsonConverter::visit(ErrorDefinition const& _node)
{
setJsonNode(_node, "ErrorDefinition", {
std::vector<pair<string, Json::Value>> _attributes = {
make_pair("name", _node.name()),
make_pair("nameLocation", sourceLocationToString(_node.nameLocation())),
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("parameters", toJson(_node.parameterList()))
});
};
if (m_stackState >= CompilerStack::State::AnalysisPerformed)
_attributes.emplace_back(make_pair("errorSelector", _node.functionType(true)->externalIdentifierHex()));
setJsonNode(_node, "ErrorDefinition", std::move(_attributes));
return false;
}
@ -587,11 +600,23 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
for (Json::Value& it: externalReferences | ranges::views::values)
externalReferencesJson.append(std::move(it));
setJsonNode(_node, "InlineAssembly", {
std::vector<pair<string, Json::Value>> attributes = {
make_pair("AST", Json::Value(yul::AsmJsonConverter(sourceIndexFromLocation(_node.location()))(_node.operations()))),
make_pair("externalReferences", std::move(externalReferencesJson)),
make_pair("evmVersion", dynamic_cast<solidity::yul::EVMDialect const&>(_node.dialect()).evmVersion().name())
});
};
if (_node.flags())
{
Json::Value flags(Json::arrayValue);
for (auto const& flag: *_node.flags())
if (flag)
flags.append(*flag);
else
flags.append(Json::nullValue);
attributes.emplace_back(make_pair("flags", move(flags)));
}
setJsonNode(_node, "InlineAssembly", move(attributes));
return false;
}

View File

@ -626,11 +626,24 @@ ASTPointer<InlineAssembly> ASTJsonImporter::createInlineAssembly(Json::Value con
astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!");
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value());
ASTPointer<vector<ASTPointer<ASTString>>> flags;
if (_node.isMember("flags"))
{
flags = make_shared<vector<ASTPointer<ASTString>>>();
Json::Value const& flagsNode = _node["flags"];
astAssert(flagsNode.isArray(), "Assembly flags must be an array.");
for (Json::ArrayIndex i = 0; i < flagsNode.size(); ++i)
{
astAssert(flagsNode[i].isString(), "Assembly flag must be a string.");
flags->emplace_back(make_shared<ASTString>(flagsNode[i].asString()));
}
}
shared_ptr<yul::Block> operations = make_shared<yul::Block>(yul::AsmJsonImporter(m_sourceNames).createBlock(member(_node, "AST")));
return createASTNode<InlineAssembly>(
_node,
nullOrASTString(_node, "documentation"),
dialect,
move(flags),
operations
);
}

View File

@ -54,4 +54,6 @@ ReturnInfo::ReturnInfo(EVMVersion const& _evmVersion, FunctionType const& _funct
estimatedReturnSize += retType->decodingType()->calldataEncodedSize();
}
}
if (dynamicReturnSize)
solAssert(estimatedReturnSize == 0);
}

View File

@ -41,6 +41,14 @@ YulArity YulArity::fromType(FunctionType const& _functionType)
};
}
string IRNames::externalFunctionABIWrapper(Declaration const& _functionOrVarDecl)
{
if (auto const* function = dynamic_cast<FunctionDefinition const*>(&_functionOrVarDecl))
solAssert(!function->isConstructor());
return "external_fun_" + _functionOrVarDecl.name() + "_" + to_string(_functionOrVarDecl.id());
}
string IRNames::function(FunctionDefinition const& _function)
{
if (_function.isConstructor())

View File

@ -49,6 +49,7 @@ struct YulArity
struct IRNames
{
static std::string externalFunctionABIWrapper(Declaration const& _functionOrVardecl);
static std::string function(FunctionDefinition const& _function);
static std::string function(VariableDeclaration const& _varDecl);
static std::string modifierInvocation(ModifierInvocation const& _modifierInvocation);

View File

@ -160,8 +160,8 @@ public:
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
bool inlineAssemblySeen() const { return m_inlineAssemblySeen; }
void setInlineAssemblySeen() { m_inlineAssemblySeen = true; }
bool memoryUnsafeInlineAssemblySeen() const { return m_memoryUnsafeInlineAssemblySeen; }
void setMemoryUnsafeInlineAssemblySeen() { m_memoryUnsafeInlineAssemblySeen = true; }
/// @returns the runtime ID to be used for the function in the dispatch routine
/// and for internal function pointers.
@ -202,8 +202,8 @@ private:
/// Whether to use checked or wrapping arithmetic.
Arithmetic m_arithmetic = Arithmetic::Checked;
/// Flag indicating whether any inline assembly block was seen.
bool m_inlineAssemblySeen = false;
/// Flag indicating whether any memory-unsafe inline assembly block was seen.
bool m_memoryUnsafeInlineAssemblySeen = false;
/// Function definitions queued for code generation. They're the Solidity functions whose calls
/// were discovered by the IR generator during AST traversal.

View File

@ -213,8 +213,8 @@ string IRGenerator::generate(
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
// This has to be called only after all other code generation for the creation object is complete.
bool creationInvolvesAssembly = m_context.inlineAssemblySeen();
t("memoryInitCreation", memoryInit(!creationInvolvesAssembly));
bool creationInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
t("memoryInitCreation", memoryInit(!creationInvolvesMemoryUnsafeAssembly));
t("useSrcMapCreation", formatUseSrcMap(m_context));
resetContext(_contract, ExecutionContext::Deployed);
@ -239,8 +239,8 @@ string IRGenerator::generate(
t("useSrcMapDeployed", formatUseSrcMap(m_context));
// This has to be called only after all other code generation for the deployed object is complete.
bool deployedInvolvesAssembly = m_context.inlineAssemblySeen();
t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly));
bool deployedInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
t("memoryInitDeployed", memoryInit(!deployedInvolvesMemoryUnsafeAssembly));
solAssert(_contract.annotation().creationCallGraph->get() != nullptr, "");
solAssert(_contract.annotation().deployedCallGraph->get() != nullptr, "");
@ -735,6 +735,44 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
});
}
string IRGenerator::generateExternalFunction(ContractDefinition const& _contract, FunctionType const& _functionType)
{
string functionName = IRNames::externalFunctionABIWrapper(_functionType.declaration());
return m_context.functionCollector().createFunction(functionName, [&](vector<string>&, vector<string>&) -> string {
Whiskers t(R"X(
<callValueCheck>
<?+params>let <params> := </+params> <abiDecode>(4, calldatasize())
<?+retParams>let <retParams> := </+retParams> <function>(<params>)
let memPos := <allocateUnbounded>()
let memEnd := <abiEncode>(memPos <?+retParams>,</+retParams> <retParams>)
return(memPos, sub(memEnd, memPos))
)X");
t("callValueCheck", (_functionType.isPayable() || _contract.isLibrary()) ? "" : callValueCheck());
unsigned paramVars = make_shared<TupleType>(_functionType.parameterTypes())->sizeOnStack();
unsigned retVars = make_shared<TupleType>(_functionType.returnParameterTypes())->sizeOnStack();
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
t("abiDecode", abiFunctions.tupleDecoder(_functionType.parameterTypes()));
t("params", suffixedVariableNameList("param_", 0, paramVars));
t("retParams", suffixedVariableNameList("ret_", 0, retVars));
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&_functionType.declaration()))
{
solAssert(!funDef->isConstructor());
t("function", m_context.enqueueFunctionForCodeGeneration(*funDef));
}
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(&_functionType.declaration()))
t("function", generateGetter(*varDecl));
else
solAssert(false, "Unexpected declaration for function!");
t("allocateUnbounded", m_utils.allocateUnboundedFunction());
t("abiEncode", abiFunctions.tupleEncoder(_functionType.returnParameterTypes(), _functionType.returnParameterTypes(), _contract.isLibrary()));
return t.render();
});
}
string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDecl)
{
IRGeneratorForStatements generator(m_context, m_utils);
@ -976,12 +1014,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
{
// <functionName>
<delegatecallCheck>
<callValueCheck>
<?+params>let <params> := </+params> <abiDecode>(4, calldatasize())
<?+retParams>let <retParams> := </+retParams> <function>(<params>)
let memPos := <allocateUnbounded>()
let memEnd := <abiEncode>(memPos <?+retParams>,</+retParams> <retParams>)
return(memPos, sub(memEnd, memPos))
<externalFunction>()
}
</cases>
default {}
@ -1011,25 +1044,8 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
"() }";
}
templ["delegatecallCheck"] = delegatecallCheck;
templ["callValueCheck"] = (type->isPayable() || _contract.isLibrary()) ? "" : callValueCheck();
unsigned paramVars = make_shared<TupleType>(type->parameterTypes())->sizeOnStack();
unsigned retVars = make_shared<TupleType>(type->returnParameterTypes())->sizeOnStack();
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
templ["params"] = suffixedVariableNameList("param_", 0, paramVars);
templ["retParams"] = suffixedVariableNameList("ret_", 0, retVars);
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration()))
templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef);
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(&type->declaration()))
templ["function"] = generateGetter(*varDecl);
else
solAssert(false, "Unexpected declaration for function!");
templ["allocateUnbounded"] = m_utils.allocateUnboundedFunction();
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), _contract.isLibrary());
templ["externalFunction"] = generateExternalFunction(_contract, *type);
}
t("cases", functions);
FunctionDefinition const* etherReceiver = _contract.receiveFunction();

View File

@ -102,6 +102,9 @@ private:
/// Generates a getter for the given declaration and returns its name
std::string generateGetter(VariableDeclaration const& _varDecl);
/// Generates the external part (ABI decoding and encoding) of a function or getter.
std::string generateExternalFunction(ContractDefinition const& _contract, FunctionType const& _functionType);
/// Generates code that assigns the initial value of the respective type.
std::string generateInitialAssignment(VariableDeclaration const& _varDecl);

View File

@ -2138,7 +2138,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
{
setLocation(_inlineAsm);
m_context.setInlineAssemblySeen();
if (*_inlineAsm.annotation().hasMemoryEffects && !_inlineAsm.annotation().markedMemorySafe)
m_context.setMemoryUnsafeInlineAssemblySeen();
CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences};
yul::Statement modified = bodyCopier(_inlineAsm.operations());
@ -2505,6 +2506,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
}
// NOTE: When the expected size of returndata is static, we pass that in to the call opcode and it gets copied automatically.
// When it's dynamic, we get zero from estimatedReturnSize() instead and then we need an explicit returndatacopy().
Whiskers templ(R"(
<?checkExtcodesize>
if iszero(extcodesize(<address>)) { <revertNoCode>() }
@ -2514,22 +2517,29 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
mstore(<pos>, <shl28>(<funSel>))
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <staticReturndataSize>)
<?noTryCall>
if iszero(<success>) { <forwardingRevert>() }
</noTryCall>
<?+retVars> let <retVars> </+retVars>
if <success> {
<?dynamicReturnSize>
// copy dynamic return data out
returndatacopy(<pos>, 0, returndatasize())
</dynamicReturnSize>
<?isReturndataSizeDynamic>
let <returnDataSizeVar> := returndatasize()
returndatacopy(<pos>, 0, <returnDataSizeVar>)
<!isReturndataSizeDynamic>
let <returnDataSizeVar> := <staticReturndataSize>
<?supportsReturnData>
if gt(<returnDataSizeVar>, returndatasize()) {
<returnDataSizeVar> := returndatasize()
}
</supportsReturnData>
</isReturndataSizeDynamic>
// update freeMemoryPointer according to dynamic return size
<finalizeAllocation>(<pos>, <returnSize>)
<finalizeAllocation>(<pos>, <returnDataSizeVar>)
// decode return parameters from external try-call into retVars
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnDataSizeVar>))
}
)");
templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code"));
@ -2558,21 +2568,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("funSel", IRVariable(_functionCall.expression()).part("functionSelector").name());
templ("address", IRVariable(_functionCall.expression()).part("address").name());
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
// This ensures it can catch badly formatted input from external calls.
if (m_context.evmVersion().supportsReturndata())
templ("returnSize", "returndatasize()");
else
templ("returnSize", to_string(returnInfo.estimatedReturnSize));
templ("reservedReturnSize", returnInfo.dynamicReturnSize ? "0" : to_string(returnInfo.estimatedReturnSize));
if (returnInfo.dynamicReturnSize)
solAssert(m_context.evmVersion().supportsReturndata());
templ("returnDataSizeVar", m_context.newYulVariable());
templ("staticReturndataSize", to_string(returnInfo.estimatedReturnSize));
templ("supportsReturnData", m_context.evmVersion().supportsReturndata());
string const retVars = IRVariable(_functionCall).commaSeparatedList();
templ("retVars", retVars);
solAssert(retVars.empty() == returnInfo.returnTypes.empty());
templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true));
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
templ("isReturndataSizeDynamic", returnInfo.dynamicReturnSize);
templ("noTryCall", !_functionCall.annotation().tryCall);

View File

@ -276,14 +276,8 @@ boost::filesystem::path FileReader::normalizeCLIPathForVFS(
// If the path is on the same drive as the working dir, for portability we prefer not to
// include the root name. Do this only for non-UNC paths - my experiments show that on Windows
// when the working dir is an UNC path, / does not not actually refer to the root of the UNC path.
boost::filesystem::path normalizedRootPath = normalizedPath.root_path();
if (!isUNCPath(normalizedPath))
{
boost::filesystem::path workingDirRootPath = canonicalWorkDir.root_path();
// Ignore drive letter case on Windows (C:\ <=> c:\).
if (boost::filesystem::equivalent(normalizedRootPath, workingDirRootPath))
normalizedRootPath = "/";
}
boost::filesystem::path normalizedRootPath = normalizeCLIRootPathForVFS(normalizedPath, canonicalWorkDir);
// lexically_normal() will not squash paths like "/../../" into "/". We have to do it manually.
boost::filesystem::path dotDotPrefix = absoluteDotDotPrefix(normalizedPath);
@ -308,6 +302,27 @@ boost::filesystem::path FileReader::normalizeCLIPathForVFS(
return normalizedPathNoDotDot;
}
boost::filesystem::path FileReader::normalizeCLIRootPathForVFS(
boost::filesystem::path const& _path,
boost::filesystem::path const& _workDir
)
{
solAssert(_workDir.is_absolute(), "");
boost::filesystem::path absolutePath = boost::filesystem::absolute(_path, _workDir);
boost::filesystem::path rootPath = absolutePath.root_path();
boost::filesystem::path baseRootPath = _workDir.root_path();
if (isUNCPath(absolutePath))
return rootPath;
// Ignore drive letter case on Windows (C:\ <=> c:\).
if (boost::filesystem::equivalent(rootPath, baseRootPath))
return "/";
return rootPath;
}
bool FileReader::isPathPrefix(boost::filesystem::path const& _prefix, boost::filesystem::path const& _path)
{
solAssert(!_prefix.empty() && !_path.empty(), "");

View File

@ -115,6 +115,17 @@ public:
SymlinkResolution _symlinkResolution = SymlinkResolution::Disabled
);
/// Normalizes a root path by excluding, in some cases, its root name.
/// The function is used for better portability, and intended to omit root name
/// if the path can be used without it.
/// @param _path Path to normalize the root path.
/// @param _workDir Current working directory path, must be absolute.
/// @returns a normalized root path.
static boost::filesystem::path normalizeCLIRootPathForVFS(
boost::filesystem::path const& _path,
boost::filesystem::path const& _workDir = boost::filesystem::current_path()
);
/// @returns true if all the path components of @a _prefix are present at the beginning of @a _path.
/// Both paths must be absolute (or have slash as root) and normalized (no . or .. segments, no
/// multiple consecutive slashes).

View File

@ -29,7 +29,7 @@
#include <libyul/Exceptions.h>
#include <libyul/optimiser/Suite.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/Disassemble.h>
#include <libsmtutil/Exceptions.h>

View File

@ -1321,13 +1321,28 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
advance();
}
ASTPointer<vector<ASTPointer<ASTString>>> flags;
if (m_scanner->currentToken() == Token::LParen)
{
flags = make_shared<vector<ASTPointer<ASTString>>>();
do
{
advance();
expectToken(Token::StringLiteral, false);
flags->emplace_back(make_shared<ASTString>(m_scanner->currentLiteral()));
advance();
}
while (m_scanner->currentToken() == Token::Comma);
expectToken(Token::RParen);
}
yul::Parser asmParser(m_errorReporter, dialect);
shared_ptr<yul::Block> block = asmParser.parseInline(m_scanner);
if (block == nullptr)
BOOST_THROW_EXCEPTION(FatalError());
location.end = nativeLocationOf(*block).end;
return make_shared<InlineAssembly>(nextID(), location, _docString, dialect, block);
return make_shared<InlineAssembly>(nextID(), location, _docString, dialect, move(flags), block);
}
ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)

View File

@ -309,6 +309,7 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
literalArguments = &f->literalArguments;
validateInstructions(_funCall);
m_sideEffects += f->sideEffects;
}
else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{
[&](Scope::Variable const&)

View File

@ -94,6 +94,8 @@ public:
void operator()(Leave const&) { }
void operator()(Block const& _block);
/// @returns the worst side effects encountered during analysis (including within defined functions).
SideEffects const& sideEffects() const { return m_sideEffects; }
private:
/// Visits the expression, expects that it evaluates to exactly one value and
/// returns the type. Reports errors on errors and returns the default type.
@ -128,6 +130,8 @@ private:
/// Names of data objects to be referenced by builtin functions with literal arguments.
std::set<YulString> m_dataNames;
ForLoop const* m_currentForLoop = nullptr;
/// Worst side effects encountered during analysis (including within defined functions).
SideEffects m_sideEffects;
};
}

View File

@ -23,6 +23,7 @@
#include <libyul/optimiser/ASTWalker.h>
#include <liblangutil/EVMVersion.h>
#include <libsolutil/Numeric.h>
#include <libevmasm/Instruction.h>
namespace solidity::yul

View File

@ -25,6 +25,8 @@
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/backends/evm/OptimizedEVMCodeTransform.h>
#include <libyul/optimiser/FunctionCallFinder.h>
#include <libyul/Object.h>
#include <libyul/Exceptions.h>
@ -74,7 +76,22 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
);
if (!stackErrors.empty())
BOOST_THROW_EXCEPTION(stackErrors.front());
{
vector<FunctionCall*> memoryGuardCalls = FunctionCallFinder::run(
*_object.code,
"memoryguard"_yulstring
);
auto stackError = stackErrors.front();
string msg = stackError.comment() ? *stackError.comment() : "";
if (memoryGuardCalls.empty())
msg += "\nNo memoryguard was present. "
"Consider using memory-safe assembly only and annotating it via "
"\"/// @solidity memory-safe-assembly\".";
else
msg += "\nmemoryguard was present.";
stackError << util::errinfo_comment(msg);
BOOST_THROW_EXCEPTION(stackError);
}
}
else
{

View File

@ -27,6 +27,7 @@
#include <libyul/YulString.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Numeric.h>
#include <liblangutil/EVMVersion.h>
#include <liblangutil/SourceLocation.h>

View File

@ -73,6 +73,7 @@ void UnusedStoreBase::operator()(FunctionDefinition const& _functionDefinition)
{
ScopedSaveAndRestore outerAssignments(m_stores, {});
ScopedSaveAndRestore forLoopInfo(m_forLoopInfo, {});
ScopedSaveAndRestore forLoopNestingDepth(m_forLoopNestingDepth, 0);
(*this)(_functionDefinition.body);

View File

@ -1,10 +1,12 @@
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# Script used for cross-platform comparison as part of the travis automation.
# Splits all test source code into multiple files, generates bytecode and
# uploads the bytecode into github.com/ethereum/solidity-test-bytecode where
# another travis job is triggered to do the actual comparison.
# Script used for cross-platform comparison of the bytecode generated by the compiler.
# Splits all test source code into multiple files, generates bytecode and metadata
# for each file and combines it into a single report.txt file.
#
# The script is meant to be executed in CI on all supported platforms. All generated
# reports must be identical for a given compiler version.
#
# ------------------------------------------------------------------------------
# This file is part of solidity.

View File

@ -180,7 +180,7 @@ function diff_values
shift
shift
diff --color=auto --unified=0 <(echo "$value1") <(echo "$value2") "$@"
diff --unified=0 <(echo "$value1") <(echo "$value2") "$@"
}
function safe_kill

View File

@ -22,7 +22,7 @@
# (c) 2016-2021 solidity contributors.
#------------------------------------------------------------------------------
FROM gcr.io/oss-fuzz-base/base-clang:latest as base
LABEL version="15"
LABEL version="16"
ARG DEBIAN_FRONTEND=noninteractive
@ -32,7 +32,7 @@ RUN apt-get update; \
software-properties-common \
ninja-build git wget \
libbz2-dev zlib1g-dev git curl uuid-dev \
pkg-config openjdk-8-jdk liblzma-dev unzip mlton m4; \
pkg-config openjdk-8-jdk liblzma-dev unzip mlton m4 jq; \
apt-get install -qy python3-pip;
# Install cmake 3.21.2 (minimum requirement is cmake 3.10)

View File

@ -22,7 +22,7 @@
# (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------
FROM buildpack-deps:focal AS base
LABEL version="10"
LABEL version="11"
ARG DEBIAN_FRONTEND=noninteractive
@ -37,7 +37,7 @@ RUN set -ex; \
cmake ninja-build \
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
libboost-program-options-dev \
libcvc4-dev libz3-static-dev z3-static \
libcvc4-dev libz3-static-dev z3-static jq \
; \
apt-get install -qy python3-pip python3-sphinx; \
pip3 install codecov; \

View File

@ -22,7 +22,7 @@
# (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------
FROM buildpack-deps:focal AS base
LABEL version="10"
LABEL version="11"
ARG DEBIAN_FRONTEND=noninteractive
@ -38,7 +38,7 @@ RUN set -ex; \
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
libboost-program-options-dev \
clang \
libz3-static-dev \
libz3-static-dev jq \
; \
rm -rf /var/lib/apt/lists/*

View File

@ -1,66 +0,0 @@
# ------------------------------------------------------------------------------
# This Python script is used within the OS X release process, to ensure
# that the standalone OS X ZIP files which we make are actually
# standalone, and not implicitly dependent on Homebrew installs for
# external libraries which we use.
#
# This implicit dependencies seem to show up only where we have
# external dependencies which are dependent on each other, and the
# path from one to another is an absolute path to "/usr/local/opt",
# the Homebrew install location. External dependencies which only
# depend on system libraries are fine. Our main applications seem
# to be fine.
#
# An example of a dependency which requires this fix-up at the time
# of writing is the following dependency edge:
#
# libjsonrpccpp-client.0.dylib
# -> /usr/local/opt/jsoncpp/lib/libjsoncpp.0.dylib
#
# See https://blogs.oracle.com/dipol/entry/dynamic_libraries_rpath_and_mac
# for a little overview of "install_name_tool" and "otool".
#
# ------------------------------------------------------------------------------
# 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) 2016 solidity contributors.
# -----------------------------------------------------------------------------
import os
import subprocess
import sys
def readDependencies(fname):
with subprocess.Popen(['otool', '-L', fname], stdout=subprocess.PIPE) as o:
for line in o.stdout:
if line[0] == '\t':
library = line.split(' ', 1)[0][1:]
if (library.startswith("/usr/local/lib") or
library.startswith("/usr/local/opt") or
library.startswith("/Users/")
):
if os.path.basename(library) != os.path.basename(fname):
command = "install_name_tool -change " + \
library + " @executable_path/./" + \
os.path.basename(library) + " " + fname
print(command)
os.system("chmod +w " + fname)
os.system(command)
root = sys.argv[1]
for (dirpath, dirnames, filenames) in os.walk(root):
for filename in filenames:
readDependencies(os.path.join(root, filename))

View File

@ -1,38 +0,0 @@
@ECHO OFF
REM ---------------------------------------------------------------------------
REM Batch file for implementing release flow for solidity for Windows.
REM
REM The documentation for solidity is hosted at:
REM
REM https://docs.soliditylang.org
REM
REM ---------------------------------------------------------------------------
REM This file is part of solidity.
REM
REM solidity is free software: you can redistribute it and/or modify
REM it under the terms of the GNU General Public License as published by
REM the Free Software Foundation, either version 3 of the License, or
REM (at your option) any later version.
REM
REM solidity is distributed in the hope that it will be useful,
REM but WITHOUT ANY WARRANTY; without even the implied warranty of
REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
REM GNU General Public License for more details.
REM
REM You should have received a copy of the GNU General Public License
REM along with solidity. If not, see <http://www.gnu.org/licenses/>
REM
REM Copyright (c) 2016 solidity contributors.
REM ---------------------------------------------------------------------------
set CONFIGURATION=%1
set VERSION=%2
set "DLLS=MSVC_DLLS_NOT_FOUND"
FOR /d %%d IN ("C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Redist\MSVC\*"
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\*") DO set "DLLS=%%d\x86\Microsoft.VC141.CRT\msvc*.dll"
7z a solidity-windows.zip ^
.\build\solc\%CONFIGURATION%\solc.exe .\build\test\%CONFIGURATION%\soltest.exe ^
"%DLLS%"

View File

@ -1,56 +0,0 @@
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# Bash script implementing release flow for solidity for Linux and macOS.
#
# TODO - At the time of writing, we only have ZIPs working. Need to hook up
# support for Homebrew and PPAs.
#
# The documentation for solidity is hosted at:
#
# https://docs.soliditylang.org
#
# ------------------------------------------------------------------------------
# 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) 2016 solidity contributors.
#------------------------------------------------------------------------------
ZIP_SUFFIX="$1"
ZIP_TEMP_DIR="$(pwd)/build/zip/"
# There is an implicit assumption here that we HAVE to run from root directory.
REPO_ROOT=$(pwd)
mkdir -p "$ZIP_TEMP_DIR"
# Copy all the solidity executables into a temporary directory prior to ZIP creation
cp "$REPO_ROOT/build/solc/solc" "$ZIP_TEMP_DIR"
# For macOS, we run a fix-up script which alters all of the symbolic links within
# the executables and dynamic libraries such that the ZIP becomes self-contained, by
# revectoring all the dylib references to be relative to the directory containing the
# application, so that the ZIPs are self-contained, with the only external references
# being for kernel-level dylibs.
if [[ "$OSTYPE" == "darwin"* ]]; then
python3 "$REPO_ROOT/scripts/fix_homebrew_paths_in_standalone_zip.py" "$ZIP_TEMP_DIR"
fi
# And ZIP it all up, with a filename suffix passed in on the command-line.
mkdir -p "$REPO_ROOT/upload"
zip -j "$REPO_ROOT/upload/solidity-$ZIP_SUFFIX.zip" "$ZIP_TEMP_DIR/"*

View File

@ -55,9 +55,9 @@ keyid=379F4801D622CDCF
email=builds@ethereum.org
packagename=solc
static_build_distribution=hirsute
static_build_distribution=impish
DISTRIBUTIONS="focal hirsute impish jammy"
DISTRIBUTIONS="focal impish jammy"
if is_release
then

View File

@ -45,7 +45,7 @@ set +e
if [ -e scripts/ci/build_emscripten.sh ]; then
scripts/ci/build_emscripten.sh
else
# The script used to be in scripts/ci/ in earlier versions.
# The script used to be in scripts/travis-emscripten/ in earlier versions.
scripts/travis-emscripten/build_emscripten.sh
fi

View File

@ -27,7 +27,7 @@ done
SOLIDITY_REPO_URL="https://github.com/ethereum/solidity"
SOLC_JS_REPO_URL="https://github.com/ethereum/solc-js"
SOLC_JS_BRANCH=wasmRebuildTests
RELEASE_URL="https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin"
RELEASE_URL="https://binaries.soliditylang.org/bin"
RELEASE_COMMIT_LIST_URL="$RELEASE_URL/list.txt"
SCRIPTDIR=$(dirname "$0")
@ -196,6 +196,7 @@ cp scripts/bytecodecompare/storebytecode.sh /tmp
# shellcheck disable=SC2016
sed -i -e 's/rm -rf "\$TMPDIR"/cp "\$TMPDIR"\/report.txt \/tmp\/report.txt ; rm -rf "\$TMPDIR"/' /tmp/storebytecode.sh
sed -i -e 's/REPO_ROOT=.*/REPO_ROOT=\/src/' /tmp/storebytecode.sh
sed -i -e 's/git clone/git clone --branch '"${SOLC_JS_BRANCH}"'/' /tmp/storebytecode.sh
export SOLC_EMSCRIPTEN="On"
echo "Check out solc-js repository..."
@ -213,7 +214,9 @@ ln -sf /emsdk_portable/emscripten/bin/* /usr/local/bin
rm -rf /src
ln -sf /root/project /src
echo "Install dependencies and upgrade system packages."
apt-get -qq update >/dev/null 2>&1
apt-get -qq upgrade >/dev/null 2>&1
apt-get -qq install cmake >/dev/null 2>&1
echo "Create output directories."
@ -228,7 +231,6 @@ mkdir -p "${OUTPUTDIR}"/bin
echo "Prepare solc-js."
cd /root/solc-js
npm install >/dev/null 2>&1
npm run build >/dev/null 2>&1
echo "Install semver helper."
npm install -g semver >/dev/null 2>&1
@ -238,6 +240,7 @@ wget -q "${RELEASE_COMMIT_LIST_URL}" -O /tmp/release_commit_list.txt
cd /src
TAGS=$(git tag --list "${TAG_FILTER}" | tac)
echo "Matching tags: ${TAGS}"
for TAG in ${TAGS}; do
process_tag "${TAG}"
done

View File

@ -44,6 +44,7 @@
#include <libyul/AssemblyStack.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/Disassemble.h>
#include <libevmasm/GasMeter.h>
#include <liblangutil/Exceptions.h>

View File

@ -83,6 +83,8 @@ set(libsolidity_sources
libsolidity/InlineAssembly.cpp
libsolidity/LibSolc.cpp
libsolidity/Metadata.cpp
libsolidity/MemoryGuardTest.cpp
libsolidity/MemoryGuardTest.h
libsolidity/SemanticTest.cpp
libsolidity/SemanticTest.h
libsolidity/SemVerMatcher.cpp

View File

@ -21,6 +21,7 @@
#include <libsolutil/Exceptions.h>
#include <liblangutil/EVMVersion.h>
#include <liblangutil/Exceptions.h>
#include <libsolutil/Numeric.h>
#include <test/evmc/evmc.h>
@ -47,7 +48,7 @@ static constexpr auto heraFilename = "libhera.so";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-linux-x86_64.tar.gz";
#endif
struct ConfigException : public util::Exception {};
struct ConfigException: public util::Exception {};
struct CommonOptions
{

View File

@ -266,7 +266,7 @@ h160 ExecutionFramework::logAddress(size_t _logIdx) const
bytes ExecutionFramework::logData(size_t _logIdx) const
{
const auto& data = m_evmcHost->recorded_logs.at(_logIdx).data;
auto const& data = m_evmcHost->recorded_logs.at(_logIdx).data;
// TODO: Return a copy of log data, because this is expected from REQUIRE_LOG_DATA(),
// but reference type like string_view would be preferable.
return {data.begin(), data.end()};

View File

@ -22,6 +22,7 @@
#include <test/libsolidity/ABIJsonTest.h>
#include <test/libsolidity/ASTJSONTest.h>
#include <test/libsolidity/GasTest.h>
#include <test/libsolidity/MemoryGuardTest.h>
#include <test/libsolidity/SyntaxTest.h>
#include <test/libsolidity/SemanticTest.h>
#include <test/libsolidity/SMTCheckerTest.h>
@ -74,6 +75,7 @@ Testsuite const g_interactiveTestsuites[] = {
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create},
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create},
{"Memory Guard Tests", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create},
{"Ewasm Translation", "libyul", "ewasmTranslationTests", false, false, &yul::test::EwasmTranslationTest::create}
};

View File

@ -11,14 +11,15 @@ object "C_12" {
code {
{
/// @src 0:61:418 "contract C {..."
mstore(64, 128)
let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) }
/// @src 0:103:238 "assembly {..."
sstore(0, shl(180, 1))
/// @src 0:61:418 "contract C {..."
let _1 := datasize("C_12_deployed")
codecopy(128, dataoffset("C_12_deployed"), _1)
return(128, _1)
let _2 := datasize("C_12_deployed")
codecopy(_1, dataoffset("C_12_deployed"), _2)
return(_1, _2)
}
}
/// @use-src 0:"constant_optimizer_yul/input.sol"
@ -26,7 +27,7 @@ object "C_12" {
code {
{
/// @src 0:61:418 "contract C {..."
mstore(64, 128)
mstore(64, memoryguard(0x80))
if callvalue() { revert(0, 0) }
/// @src 0:279:410 "assembly {..."
sstore(0, 0x1000000000000000000000000000000000000000000000)

View File

@ -108,12 +108,7 @@ object "C_6" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_5()
}
default {}
@ -150,6 +145,17 @@ object "C_6" {
}
function external_fun_f_5() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -108,12 +108,7 @@ object "C_6" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_5()
}
default {}
@ -150,6 +145,17 @@ object "C_6" {
}
function external_fun_f_5() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -101,12 +101,7 @@ object "C_6" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_5()
}
default {}
@ -143,6 +138,17 @@ object "C_6" {
}
function external_fun_f_5() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -158,12 +158,7 @@ object "D_27" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_26()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_26()
}
default {}
@ -238,6 +233,17 @@ object "D_27" {
}
function external_fun_f_26() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_26()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}
@ -484,7 +490,7 @@ object "D_27" {
revert(pos, returndatasize())
}
mstore(add(allocate_memory_array_string(), 32), "/*")
let memPtr := allocate_memory_array_string_480()
let memPtr := allocate_memory_array_string_546()
mstore(add(memPtr, 32), 0x2f2a2a204073726320303a39363a313635202022636f6e74726163742044207b)
mstore(add(memPtr, 64), shl(200, 0x2e2e2e22202a2f))
let memPos := mload(64)
@ -525,7 +531,7 @@ object "D_27" {
memPtr := memPtr_1
mstore(memPtr_1, 2)
}
function allocate_memory_array_string_480() -> memPtr
function allocate_memory_array_string_546() -> memPtr
{
let memPtr_1 := mload(64)
let newFreePtr := add(memPtr_1, 96)

View File

@ -53,12 +53,7 @@ object "C_81" {
{
// f(uint256,uint256,uint256,uint256)
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
let param_0, param_1, param_2, param_3 := abi_decode_tuple_t_uint256t_uint256t_uint256t_uint256(4, calldatasize())
let ret_0, ret_1, ret_2, ret_3 := fun_f_80(param_0, param_1, param_2, param_3)
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_uint256_t_int256_t_uint256_t_uint256__to_t_uint256_t_int256_t_uint256_t_uint256__fromStack(memPos , ret_0, ret_1, ret_2, ret_3)
return(memPos, sub(memEnd, memPos))
external_fun_f_80()
}
default {}
@ -160,6 +155,17 @@ object "C_81" {
}
function external_fun_f_80() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
let param_0, param_1, param_2, param_3 := abi_decode_tuple_t_uint256t_uint256t_uint256t_uint256(4, calldatasize())
let ret_0, ret_1, ret_2, ret_3 := fun_f_80(param_0, param_1, param_2, param_3)
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_uint256_t_int256_t_uint256_t_uint256__to_t_uint256_t_int256_t_uint256_t_uint256__fromStack(memPos , ret_0, ret_1, ret_2, ret_3)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -3,6 +3,6 @@ pragma solidity >=0.0.0;
pragma abicoder v2;
contract D {
constructor() { assembly {}}
constructor() { assembly { mstore(0,0) } }
function f() public pure {}
}

View File

@ -10,9 +10,12 @@ Optimized IR:
object "D_12" {
code {
{
/// @src 0:82:161 "contract D {..."
/// @src 0:82:175 "contract D {..."
mstore(64, 128)
if callvalue() { revert(0, 0) }
/// @src 0:115:139 "assembly { mstore(0,0) }"
mstore(0, 0)
/// @src 0:82:175 "contract D {..."
let _1 := datasize("D_12_deployed")
codecopy(128, dataoffset("D_12_deployed"), _1)
return(128, _1)
@ -22,7 +25,7 @@ object "D_12" {
object "D_12_deployed" {
code {
{
/// @src 0:82:161 "contract D {..."
/// @src 0:82:175 "contract D {..."
let _1 := memoryguard(0x80)
mstore(64, _1)
if iszero(lt(calldatasize(), 4))

View File

@ -4,6 +4,6 @@ pragma abicoder v2;
contract D {
function f() public pure {
assembly {}
assembly { mstore(0,0) }
}
}

View File

@ -10,7 +10,7 @@ Optimized IR:
object "D_8" {
code {
{
/// @src 0:82:153 "contract D {..."
/// @src 0:82:166 "contract D {..."
let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) }
@ -23,7 +23,7 @@ object "D_8" {
object "D_8_deployed" {
code {
{
/// @src 0:82:153 "contract D {..."
/// @src 0:82:166 "contract D {..."
mstore(64, 128)
if iszero(lt(calldatasize(), 4))
{
@ -32,6 +32,8 @@ object "D_8" {
{
if callvalue() { revert(_1, _1) }
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
/// @src 0:134:158 "assembly { mstore(0,0) }"
mstore(/** @src 0:82:166 "contract D {..." */ _1, _1)
return(128, _1)
}
}

View File

@ -50,7 +50,7 @@ object "C_59" {
for { } lt(src, srcEnd) { src := add(src, _2) }
{
if slt(sub(calldatasize(), src), _2) { revert(_1, _1) }
let value := allocate_memory_1172()
let value := allocate_memory_1307()
mstore(value, calldataload(src))
mstore(dst, value)
dst := add(dst, _2)
@ -68,7 +68,7 @@ object "C_59" {
mstore(4, 0x41)
revert(0, 0x24)
}
function allocate_memory_1172() -> memPtr
function allocate_memory_1307() -> memPtr
{
memPtr := mload(64)
let newFreePtr := add(memPtr, 32)

View File

@ -68,12 +68,7 @@ object "C_15" {
{
// f(uint256[][],uint8)
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
let param_0, param_1 := abi_decode_tuple_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptrt_enum$_E_$3(4, calldatasize())
fun_f_14(param_0, param_1)
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_14()
}
default {}
@ -338,6 +333,17 @@ object "C_15" {
}
function external_fun_f_14() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
let param_0, param_1 := abi_decode_tuple_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptrt_enum$_E_$3(4, calldatasize())
fun_f_14(param_0, param_1)
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
let start := allocate_unbounded()

View File

@ -114,12 +114,7 @@ object \"C_6\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_5()
}
default {}
@ -156,6 +151,17 @@ object \"C_6\" {
}
function external_fun_f_5() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -114,12 +114,7 @@ object \"C_6\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_5()
}
default {}
@ -156,6 +151,17 @@ object \"C_6\" {
}
function external_fun_f_5() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -107,12 +107,7 @@ object \"C_6\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_5()
}
default {}
@ -149,6 +144,17 @@ object \"C_6\" {
}
function external_fun_f_5() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -172,48 +172,28 @@ object \"C_54\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_30()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_30()
}
case 0x793816ec
{
// stateVar()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := getter_fun_stateVar_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_stateVar_10()
}
case 0x9942ec6f
{
// f2()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f2_53()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f2_53()
}
case 0xa00b982b
{
// constVar()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := getter_fun_constVar_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_constVar_5()
}
default {}
@ -260,6 +240,17 @@ object \"C_54\" {
}
function external_fun_f_30() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_30()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function shift_right_unsigned_dynamic(bits, value) -> newValue {
newValue :=
@ -292,6 +283,28 @@ object \"C_54\" {
}
/// @src 0:79:435 \"contract C...\"
function external_fun_stateVar_10() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := getter_fun_stateVar_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function external_fun_f2_53() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f2_53()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function cleanup_t_rational_41_by_1(value) -> cleaned {
cleaned := value
}
@ -320,6 +333,17 @@ object \"C_54\" {
}
/// @src 0:79:435 \"contract C...\"
function external_fun_constVar_5() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := getter_fun_constVar_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}
@ -539,18 +563,24 @@ object \"C_54\" {
let expr_47
if _12 {
let _13 := 32
if gt(_13, returndatasize()) {
_13 := returndatasize()
}
// update freeMemoryPointer according to dynamic return size
finalize_allocation(_10, returndatasize())
finalize_allocation(_10, _13)
// decode return parameters from external try-call into retVars
expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, returndatasize()))
expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, _13))
}
/// @src 0:399:418 \"stateVar + this.f()\"
let expr_48 := checked_add_t_int256(expr_44, expr_47)
/// @src 0:421:429 \"immutVar\"
let _13 := loadimmutable(\"8\")
let expr_49 := _13
let _14 := loadimmutable(\"8\")
let expr_49 := _14
/// @src 0:399:429 \"stateVar + this.f() + immutVar\"
let expr_50 := checked_add_t_int256(expr_48, expr_49)
@ -638,62 +668,99 @@ object \"C_54\" {
switch shr(224, calldataload(_1))
case 0x26121ff0 {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
let ret := /** @src 0:286:305 \"constVar + immutVar\" */ checked_add_int256_556(/** @src 0:297:305 \"immutVar\" */ loadimmutable(\"8\"))
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
/// @src 0:297:305 \"immutVar\"
let _2 := loadimmutable(\"8\")
/// @src 0:79:435 \"contract C...\"
if and(1, sgt(_2, sub(shl(255, 1), 42))) { panic_error_0x11() }
let memPos := mload(64)
return(memPos, sub(abi_encode_int256(memPos, ret), memPos))
}
case 0x793816ec {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
let ret_1 := sload(_1)
let memPos_1 := mload(64)
return(memPos_1, sub(abi_encode_int256(memPos_1, ret_1), memPos_1))
}
case 0x9942ec6f {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
let ret_2 := /** @src 0:382:385 \"int\" */ modifier_m()
/// @src 0:79:435 \"contract C...\"
let memPos_2 := mload(64)
return(memPos_2, sub(abi_encode_int256(memPos_2, ret_2), memPos_2))
}
case 0xa00b982b {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
let memPos_3 := mload(64)
return(memPos_3, sub(abi_encode_int256_555(memPos_3), memPos_3))
mstore(memPos, add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 0:79:435 \"contract C...\" */ _2))
return(memPos, 32)
}
case 0x793816ec { external_fun_stateVar() }
case 0x9942ec6f { external_fun_f2() }
case 0xa00b982b { external_fun_constVar() }
}
revert(0, 0)
}
function abi_decode(dataEnd)
{
if slt(add(dataEnd, not(3)), 0) { revert(0, 0) }
}
function abi_encode_int256_555(headStart) -> tail
{
tail := add(headStart, 32)
mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29)
}
/// @src 0:79:435 \"contract C...\"
function abi_encode_int256(headStart, value0) -> tail
{
tail := add(headStart, 32)
mstore(headStart, value0)
}
function external_fun_stateVar()
{
if callvalue() { revert(0, 0) }
if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) }
let _1 := sload(0)
let memPos := mload(64)
mstore(memPos, _1)
return(memPos, 32)
}
function external_fun_f2()
{
if callvalue() { revert(0, 0) }
let _1 := 0
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
let _2 := sload(_1)
if eq(_2, sub(shl(255, 1), 1)) { panic_error_0x11() }
let ret := add(_2, 1)
sstore(_1, ret)
/// @src 0:410:418 \"this.f()\"
let _3 := /** @src 0:79:435 \"contract C...\" */ mload(64)
/// @src 0:410:418 \"this.f()\"
mstore(_3, /** @src 0:79:435 \"contract C...\" */ shl(228, 0x026121ff))
/// @src 0:410:418 \"this.f()\"
let _4 := staticcall(gas(), /** @src 0:410:414 \"this\" */ address(), /** @src 0:410:418 \"this.f()\" */ _3, /** @src 0:79:435 \"contract C...\" */ 4, /** @src 0:410:418 \"this.f()\" */ _3, 32)
if iszero(_4)
{
/// @src 0:79:435 \"contract C...\"
let pos := mload(64)
returndatacopy(pos, _1, returndatasize())
revert(pos, returndatasize())
}
/// @src 0:410:418 \"this.f()\"
let expr := /** @src 0:79:435 \"contract C...\" */ _1
/// @src 0:410:418 \"this.f()\"
if _4
{
let _5 := 32
if gt(_5, returndatasize()) { _5 := returndatasize() }
/// @src 0:79:435 \"contract C...\"
let newFreePtr := add(_3, and(add(_5, 31), not(31)))
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _3))
{
mstore(_1, shl(224, 0x4e487b71))
mstore(4, 0x41)
revert(_1, 0x24)
}
mstore(64, newFreePtr)
/// @src 0:410:418 \"this.f()\"
expr := abi_decode_int256_fromMemory(_3, add(_3, _5))
}
/// @src 0:399:418 \"stateVar + this.f()\"
let expr_1 := checked_add_int256(ret, expr)
/// @src 0:392:429 \"return stateVar + this.f() + immutVar\"
let var := /** @src 0:399:429 \"stateVar + this.f() + immutVar\" */ checked_add_int256(expr_1, /** @src 0:421:429 \"immutVar\" */ loadimmutable(\"8\"))
/// @src 0:79:435 \"contract C...\"
let memPos := mload(64)
return(memPos, sub(abi_encode_int256(memPos, var), memPos))
}
function external_fun_constVar()
{
if callvalue() { revert(0, 0) }
if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) }
let memPos := mload(64)
mstore(memPos, /** @src 0:124:126 \"41\" */ 0x29)
/// @src 0:79:435 \"contract C...\"
return(memPos, 32)
}
function panic_error_0x11()
{
mstore(0, shl(224, 0x4e487b71))
mstore(4, 0x11)
revert(0, 0x24)
}
function checked_add_int256_556(y) -> sum
{
if and(1, sgt(y, sub(shl(255, 1), 42))) { panic_error_0x11() }
sum := add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 0:79:435 \"contract C...\" */ y)
}
function checked_add_int256(x, y) -> sum
{
let _1 := slt(x, 0)
@ -701,51 +768,6 @@ object \"C_54\" {
if and(_1, slt(y, sub(shl(255, 1), x))) { panic_error_0x11() }
sum := add(x, y)
}
/// @ast-id 37 @src 0:311:348 \"modifier m()...\"
function modifier_m() -> _1
{
/// @src 0:79:435 \"contract C...\"
let _2 := 0
let _3 := sload(_2)
if eq(_3, sub(shl(255, 1), 1)) { panic_error_0x11() }
let ret := add(_3, 1)
sstore(_2, ret)
/// @src 0:410:418 \"this.f()\"
let _4 := /** @src 0:79:435 \"contract C...\" */ mload(64)
/// @src 0:410:418 \"this.f()\"
mstore(_4, /** @src 0:79:435 \"contract C...\" */ shl(228, 0x026121ff))
/// @src 0:410:418 \"this.f()\"
let _5 := staticcall(gas(), /** @src 0:410:414 \"this\" */ address(), /** @src 0:410:418 \"this.f()\" */ _4, 4, _4, 32)
if iszero(_5)
{
/// @src 0:79:435 \"contract C...\"
let pos := mload(64)
returndatacopy(pos, _2, returndatasize())
revert(pos, returndatasize())
}
/// @src 0:410:418 \"this.f()\"
let expr := /** @src 0:79:435 \"contract C...\" */ _2
/// @src 0:410:418 \"this.f()\"
if _5
{
/// @src 0:79:435 \"contract C...\"
let newFreePtr := add(_4, and(add(/** @src 0:410:418 \"this.f()\" */ returndatasize(), /** @src 0:79:435 \"contract C...\" */ 31), not(31)))
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _4))
{
mstore(_2, shl(224, 0x4e487b71))
mstore(/** @src 0:410:418 \"this.f()\" */ 4, /** @src 0:79:435 \"contract C...\" */ 0x41)
revert(_2, 0x24)
}
mstore(64, newFreePtr)
/// @src 0:410:418 \"this.f()\"
expr := abi_decode_int256_fromMemory(_4, add(_4, returndatasize()))
}
/// @src 0:399:418 \"stateVar + this.f()\"
let expr_1 := checked_add_int256(ret, expr)
/// @src 0:343:344 \"_\"
_1 := /** @src 0:399:429 \"stateVar + this.f() + immutVar\" */ checked_add_int256(expr_1, /** @src 0:421:429 \"immutVar\" */ loadimmutable(\"8\"))
}
/// @src 0:79:435 \"contract C...\"
function abi_decode_int256_fromMemory(headStart, dataEnd) -> value0
{
if slt(sub(dataEnd, headStart), 32) { revert(0, 0) }
@ -997,48 +1019,28 @@ object \"D_72\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_30()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_30()
}
case 0x793816ec
{
// stateVar()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := getter_fun_stateVar_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_stateVar_10()
}
case 0x9942ec6f
{
// f2()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f2_53()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f2_53()
}
case 0xa00b982b
{
// constVar()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := getter_fun_constVar_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_constVar_5()
}
default {}
@ -1085,6 +1087,17 @@ object \"D_72\" {
}
function external_fun_f_30() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_30()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function shift_right_unsigned_dynamic(bits, value) -> newValue {
newValue :=
@ -1117,6 +1130,28 @@ object \"D_72\" {
}
/// @src 1:91:166 \"contract D is C(3)...\"
function external_fun_stateVar_10() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := getter_fun_stateVar_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function external_fun_f2_53() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f2_53()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function cleanup_t_rational_41_by_1(value) -> cleaned {
cleaned := value
}
@ -1145,6 +1180,17 @@ object \"D_72\" {
}
/// @src 1:91:166 \"contract D is C(3)...\"
function external_fun_constVar_5() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := getter_fun_constVar_5()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}
@ -1364,18 +1410,24 @@ object \"D_72\" {
let expr_47
if _12 {
let _13 := 32
if gt(_13, returndatasize()) {
_13 := returndatasize()
}
// update freeMemoryPointer according to dynamic return size
finalize_allocation(_10, returndatasize())
finalize_allocation(_10, _13)
// decode return parameters from external try-call into retVars
expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, returndatasize()))
expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, _13))
}
/// @src 0:399:418 \"stateVar + this.f()\"
let expr_48 := checked_add_t_int256(expr_44, expr_47)
/// @src 0:421:429 \"immutVar\"
let _13 := loadimmutable(\"8\")
let expr_49 := _13
let _14 := loadimmutable(\"8\")
let expr_49 := _14
/// @src 0:399:429 \"stateVar + this.f() + immutVar\"
let expr_50 := checked_add_t_int256(expr_48, expr_49)
@ -1471,62 +1523,99 @@ object \"D_72\" {
switch shr(224, calldataload(_1))
case 0x26121ff0 {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
let ret := /** @src 0:286:305 \"constVar + immutVar\" */ checked_add_int256_556(/** @src 0:297:305 \"immutVar\" */ loadimmutable(\"8\"))
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
/// @src 0:297:305 \"immutVar\"
let _2 := loadimmutable(\"8\")
/// @src 1:91:166 \"contract D is C(3)...\"
if and(1, sgt(_2, sub(shl(255, 1), 42))) { panic_error_0x11() }
let memPos := mload(64)
return(memPos, sub(abi_encode_int256(memPos, ret), memPos))
}
case 0x793816ec {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
let ret_1 := sload(_1)
let memPos_1 := mload(64)
return(memPos_1, sub(abi_encode_int256(memPos_1, ret_1), memPos_1))
}
case 0x9942ec6f {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
let ret_2 := /** @src 0:382:385 \"int\" */ modifier_m()
/// @src 1:91:166 \"contract D is C(3)...\"
let memPos_2 := mload(64)
return(memPos_2, sub(abi_encode_int256(memPos_2, ret_2), memPos_2))
}
case 0xa00b982b {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
let memPos_3 := mload(64)
return(memPos_3, sub(abi_encode_int256_555(memPos_3), memPos_3))
mstore(memPos, add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 1:91:166 \"contract D is C(3)...\" */ _2))
return(memPos, 32)
}
case 0x793816ec { external_fun_stateVar() }
case 0x9942ec6f { external_fun_f2() }
case 0xa00b982b { external_fun_constVar() }
}
revert(0, 0)
}
function abi_decode(dataEnd)
{
if slt(add(dataEnd, not(3)), 0) { revert(0, 0) }
}
function abi_encode_int256_555(headStart) -> tail
{
tail := add(headStart, 32)
mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29)
}
/// @src 1:91:166 \"contract D is C(3)...\"
function abi_encode_int256(headStart, value0) -> tail
{
tail := add(headStart, 32)
mstore(headStart, value0)
}
function external_fun_stateVar()
{
if callvalue() { revert(0, 0) }
if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) }
let _1 := sload(0)
let memPos := mload(64)
mstore(memPos, _1)
return(memPos, 32)
}
function external_fun_f2()
{
if callvalue() { revert(0, 0) }
let _1 := 0
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
let _2 := sload(_1)
if eq(_2, sub(shl(255, 1), 1)) { panic_error_0x11() }
let ret := add(_2, 1)
sstore(_1, ret)
/// @src 0:410:418 \"this.f()\"
let _3 := /** @src 1:91:166 \"contract D is C(3)...\" */ mload(64)
/// @src 0:410:418 \"this.f()\"
mstore(_3, /** @src 1:91:166 \"contract D is C(3)...\" */ shl(228, 0x026121ff))
/// @src 0:410:418 \"this.f()\"
let _4 := staticcall(gas(), /** @src 0:410:414 \"this\" */ address(), /** @src 0:410:418 \"this.f()\" */ _3, /** @src 1:91:166 \"contract D is C(3)...\" */ 4, /** @src 0:410:418 \"this.f()\" */ _3, 32)
if iszero(_4)
{
/// @src 1:91:166 \"contract D is C(3)...\"
let pos := mload(64)
returndatacopy(pos, _1, returndatasize())
revert(pos, returndatasize())
}
/// @src 0:410:418 \"this.f()\"
let expr := /** @src 1:91:166 \"contract D is C(3)...\" */ _1
/// @src 0:410:418 \"this.f()\"
if _4
{
let _5 := 32
if gt(_5, returndatasize()) { _5 := returndatasize() }
/// @src 1:91:166 \"contract D is C(3)...\"
let newFreePtr := add(_3, and(add(_5, 31), not(31)))
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _3))
{
mstore(_1, shl(224, 0x4e487b71))
mstore(4, 0x41)
revert(_1, 0x24)
}
mstore(64, newFreePtr)
/// @src 0:410:418 \"this.f()\"
expr := abi_decode_int256_fromMemory(_3, add(_3, _5))
}
/// @src 0:399:418 \"stateVar + this.f()\"
let expr_1 := checked_add_int256(ret, expr)
/// @src 0:392:429 \"return stateVar + this.f() + immutVar\"
let var := /** @src 0:399:429 \"stateVar + this.f() + immutVar\" */ checked_add_int256(expr_1, /** @src 0:421:429 \"immutVar\" */ loadimmutable(\"8\"))
/// @src 1:91:166 \"contract D is C(3)...\"
let memPos := mload(64)
return(memPos, sub(abi_encode_int256(memPos, var), memPos))
}
function external_fun_constVar()
{
if callvalue() { revert(0, 0) }
if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) }
let memPos := mload(64)
mstore(memPos, /** @src 0:124:126 \"41\" */ 0x29)
/// @src 1:91:166 \"contract D is C(3)...\"
return(memPos, 32)
}
function panic_error_0x11()
{
mstore(0, shl(224, 0x4e487b71))
mstore(4, 0x11)
revert(0, 0x24)
}
function checked_add_int256_556(y) -> sum
{
if and(1, sgt(y, sub(shl(255, 1), 42))) { panic_error_0x11() }
sum := add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 1:91:166 \"contract D is C(3)...\" */ y)
}
function checked_add_int256(x, y) -> sum
{
let _1 := slt(x, 0)
@ -1534,51 +1623,6 @@ object \"D_72\" {
if and(_1, slt(y, sub(shl(255, 1), x))) { panic_error_0x11() }
sum := add(x, y)
}
/// @ast-id 37 @src 0:311:348 \"modifier m()...\"
function modifier_m() -> _1
{
/// @src 1:91:166 \"contract D is C(3)...\"
let _2 := 0
let _3 := sload(_2)
if eq(_3, sub(shl(255, 1), 1)) { panic_error_0x11() }
let ret := add(_3, 1)
sstore(_2, ret)
/// @src 0:410:418 \"this.f()\"
let _4 := /** @src 1:91:166 \"contract D is C(3)...\" */ mload(64)
/// @src 0:410:418 \"this.f()\"
mstore(_4, /** @src 1:91:166 \"contract D is C(3)...\" */ shl(228, 0x026121ff))
/// @src 0:410:418 \"this.f()\"
let _5 := staticcall(gas(), /** @src 0:410:414 \"this\" */ address(), /** @src 0:410:418 \"this.f()\" */ _4, 4, _4, 32)
if iszero(_5)
{
/// @src 1:91:166 \"contract D is C(3)...\"
let pos := mload(64)
returndatacopy(pos, _2, returndatasize())
revert(pos, returndatasize())
}
/// @src 0:410:418 \"this.f()\"
let expr := /** @src 1:91:166 \"contract D is C(3)...\" */ _2
/// @src 0:410:418 \"this.f()\"
if _5
{
/// @src 1:91:166 \"contract D is C(3)...\"
let newFreePtr := add(_4, and(add(/** @src 0:410:418 \"this.f()\" */ returndatasize(), /** @src 1:91:166 \"contract D is C(3)...\" */ 31), not(31)))
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _4))
{
mstore(_2, shl(224, 0x4e487b71))
mstore(/** @src 0:410:418 \"this.f()\" */ 4, /** @src 1:91:166 \"contract D is C(3)...\" */ 0x41)
revert(_2, 0x24)
}
mstore(64, newFreePtr)
/// @src 0:410:418 \"this.f()\"
expr := abi_decode_int256_fromMemory(_4, add(_4, returndatasize()))
}
/// @src 0:399:418 \"stateVar + this.f()\"
let expr_1 := checked_add_int256(ret, expr)
/// @src 0:343:344 \"_\"
_1 := /** @src 0:399:429 \"stateVar + this.f() + immutVar\" */ checked_add_int256(expr_1, /** @src 0:421:429 \"immutVar\" */ loadimmutable(\"8\"))
}
/// @src 1:91:166 \"contract D is C(3)...\"
function abi_decode_int256_fromMemory(headStart, dataEnd) -> value0
{
if slt(sub(dataEnd, headStart), 32) { revert(0, 0) }

View File

@ -34,17 +34,7 @@ object \"C_7\" {
{
let selector := shift_right_224_unsigned(calldataload(0))
switch selector
case 0x26121ff0 {
if callvalue()
{
revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb()
}
abi_decode_tuple_(4, calldatasize())
fun_f_6()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos)
return(memPos, sub(memEnd, memPos))
}
case 0x26121ff0 { external_fun_f_6() }
default { }
}
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
@ -65,6 +55,18 @@ object \"C_7\" {
}
function abi_encode_tuple__to__fromStack(headStart) -> tail
{ tail := add(headStart, 0) }
function external_fun_f_6()
{
if callvalue()
{
revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb()
}
abi_decode_tuple_(4, calldatasize())
fun_f_6()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
{ revert(0, 0) }
/// @ast-id 6 @src 0:92:119 \"function f() public pure {}\"

View File

@ -52,12 +52,7 @@ object \"C_7\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_6()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_6()
}
default {}
@ -94,6 +89,17 @@ object \"C_7\" {
}
function external_fun_f_6() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_6()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -121,12 +121,7 @@ object \"D_16\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_15()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
external_fun_f_15()
}
default {}
@ -163,6 +158,17 @@ object \"D_16\" {
}
function external_fun_f_15() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
fun_f_15()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -53,12 +53,7 @@ object "test_11" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_bool__to_t_bool__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_10()
}
default {}
@ -105,6 +100,17 @@ object "test_11" {
}
function external_fun_f_10() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_bool__to_t_bool__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -125,18 +125,7 @@ object "C_6" {
if iszero(lt(calldatasize(), 4))
{
let selector := shift_right_unsigned(calldataload(0))
if eq(0x26121ff0, selector)
{
if callvalue()
{
revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb()
}
abi_decode(4, calldatasize())
fun_f()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple(memPos)
return(memPos, sub(memEnd, memPos))
}
if eq(0x26121ff0, selector) { external_fun_f() }
}
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
}
@ -421,6 +410,18 @@ object "C_6" {
let tail_86 := tail_1
tail := tail_1
}
function external_fun_f()
{
if callvalue()
{
revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb()
}
abi_decode(4, calldatasize())
fun_f()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple(memPos)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
{ revert(0, 0) }
/// @ast-id 5 @src 0:74:101 "function f() public pure {}"

View File

@ -52,12 +52,7 @@ object \"C_11\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_10()
}
default {}
@ -132,6 +127,17 @@ object \"C_11\" {
}
function external_fun_f_10() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -52,12 +52,7 @@ object \"C_11\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_10()
}
default {}
@ -104,6 +99,17 @@ object \"C_11\" {
}
function external_fun_f_10() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -52,12 +52,7 @@ object \"C_11\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_10()
}
default {}
@ -104,6 +99,17 @@ object \"C_11\" {
}
function external_fun_f_10() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -52,12 +52,7 @@ object \"C_11\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_10()
}
default {}
@ -132,6 +127,17 @@ object \"C_11\" {
}
function external_fun_f_10() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -52,12 +52,7 @@ object \"C_11\" {
{
// f()
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
external_fun_f_10()
}
default {}
@ -104,6 +99,17 @@ object \"C_11\" {
}
function external_fun_f_10() {
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_10()
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos))
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

View File

@ -48,7 +48,7 @@ function ens_test
"${compile_only_presets[@]}"
#ir-no-optimize # Compilation fails with "YulException: Variable var__945 is 1 slot(s) too deep inside the stack."
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var__945 is 1 slot(s) too deep inside the stack."
#ir-optimize-evm+yul # Compilation fails with "YulException: Variable _5 is 1 too deep in the stack [ _5 usr$i usr$h _7 usr$scratch usr$k usr$f _4 usr$len usr$j_2 RET _2 _1 var_data_mpos usr$totallen usr$x _12 ]"
ir-optimize-evm+yul # Needs memory-safe inline assembly patch
legacy-optimize-evm-only
legacy-optimize-evm+yul
)
@ -68,6 +68,8 @@ function ens_test
replace_version_pragmas
neutralize_packaged_contracts
find . -name "*.sol" -exec sed -i -e 's/^\(\s*\)\(assembly\)/\1\/\/\/ @solidity memory-safe-assembly\n\1\2/' '{}' \;
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"

View File

@ -66,6 +66,11 @@ function perpetual_pools_test
force_hardhat_unlimited_contract_size "$config_file" "$config_var"
yarn install
# The project depends on @openzeppelin/hardhat-upgrades, which is currently not prepared
# for the parallel compilation introduced in Hardhat 2.9.0.
# TODO: Remove when https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/528 is fixed.
yarn add hardhat@2.8.4
replace_version_pragmas
for preset in $SELECTED_PRESETS; do

View File

@ -56,7 +56,7 @@ function trident_test
"${compile_only_presets[@]}"
#ir-no-optimize # Compilation fails with: "YulException: Variable var_amount_165 is 9 slot(s) too deep inside the stack."
#ir-optimize-evm-only # Compilation fails with: "YulException: Variable var_amount_165 is 9 slot(s) too deep inside the stack."
#ir-optimize-evm+yul # Compilation fails with: "YulException: Cannot swap Variable var_nearestTick with Variable _4: too deep in the stack by 4 slots"
ir-optimize-evm+yul # Needs memory-safe inline assembly patch
legacy-no-optimize
legacy-optimize-evm-only
legacy-optimize-evm+yul
@ -87,6 +87,7 @@ function trident_test
sed -i 's|uint32(-1)|type(uint32).max|g' contracts/flat/BentoBoxV1Flat.sol
sed -i 's|IERC20(0)|IERC20(address(0))|g' contracts/flat/BentoBoxV1Flat.sol
sed -i 's|IStrategy(0)|IStrategy(address(0))|g' contracts/flat/BentoBoxV1Flat.sol
find contracts -name "*.sol" -exec sed -i -e 's/^\(\s*\)\(assembly\)/\1\/\/\/ @solidity memory-safe-assembly\n\1\2/' '{}' \;
# @sushiswap/core package contains contracts that get built with 0.6.12 and fail our compiler
# version check. It's not used by tests so we can remove it.

Some files were not shown because too many files have changed in this diff Show More