mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
commit
a433511128
@ -246,11 +246,11 @@ defaults:
|
||||
|
||||
- steps_install_dependencies_osx: &steps_install_dependencies_osx
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||
- attach_workspace:
|
||||
at: .
|
||||
# FIXME: We used to cache dependencies on macOS but now it takes longer than just installing
|
||||
# them each time. See https://github.com/ethereum/solidity/issues/12925.
|
||||
- run:
|
||||
name: Install build dependencies
|
||||
command: ./.circleci/osx_install_dependencies.sh
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Base Image Templates
|
||||
@ -582,6 +582,12 @@ defaults:
|
||||
binary_type: native
|
||||
nodejs_version: '16'
|
||||
resource_class: medium
|
||||
- job_native_test_ext_brink: &job_native_test_ext_brink
|
||||
<<: *workflow_ubuntu2004_static
|
||||
name: t_native_test_ext_brink
|
||||
project: brink
|
||||
binary_type: native
|
||||
nodejs_version: '16'
|
||||
- job_ems_test_ext_colony: &job_ems_test_ext_colony
|
||||
<<: *workflow_emscripten
|
||||
name: t_ems_test_ext_colony
|
||||
@ -900,23 +906,9 @@ jobs:
|
||||
MAKEFLAGS: -j10
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||
# DO NOT EDIT between here and save_cache, but rather edit ./circleci/osx_install_dependencies.sh
|
||||
# WARNING! If you do edit anything here instead, remember to invalidate the cache manually.
|
||||
- run:
|
||||
name: Install build dependencies
|
||||
command: ./.circleci/osx_install_dependencies.sh
|
||||
- save_cache:
|
||||
key: dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||
paths:
|
||||
- /usr/local/bin
|
||||
- /usr/local/sbin
|
||||
- /usr/local/lib
|
||||
- /usr/local/include
|
||||
- /usr/local/Cellar
|
||||
- /usr/local/Homebrew
|
||||
- when:
|
||||
condition: true
|
||||
<<: *steps_install_dependencies_osx
|
||||
- run: *run_build
|
||||
- store_artifacts: *artifacts_solc
|
||||
- store_artifacts: *artifact_solidity_upgrade
|
||||
@ -940,6 +932,8 @@ jobs:
|
||||
- when:
|
||||
condition: true
|
||||
<<: *steps_install_dependencies_osx
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run: *run_soltest
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
@ -952,6 +946,8 @@ jobs:
|
||||
- when:
|
||||
condition: true
|
||||
<<: *steps_install_dependencies_osx
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run: *run_cmdline_tests
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
@ -987,7 +983,7 @@ jobs:
|
||||
- run: *setup_prerelease_commit_hash
|
||||
- run:
|
||||
name: Build documentation
|
||||
command: ./scripts/docs.sh
|
||||
command: ./docs/docs.sh
|
||||
- store_artifacts:
|
||||
path: docs/_build/html/
|
||||
destination: docs-html
|
||||
@ -1480,6 +1476,7 @@ workflows:
|
||||
- t_ems_ext: *job_native_test_ext_uniswap
|
||||
- t_ems_ext: *job_native_test_ext_prb_math
|
||||
- t_ems_ext: *job_native_test_ext_elementfi
|
||||
- t_ems_ext: *job_native_test_ext_brink
|
||||
|
||||
- c_ext_benchmarks:
|
||||
<<: *workflow_trigger_on_tags
|
||||
@ -1497,6 +1494,7 @@ workflows:
|
||||
- t_native_test_ext_uniswap
|
||||
- t_native_test_ext_prb_math
|
||||
- t_native_test_ext_elementfi
|
||||
- t_native_test_ext_brink
|
||||
|
||||
# Windows build and tests
|
||||
- b_win: *workflow_trigger_on_tags
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -61,3 +61,7 @@ CMakeLists.txt.user
|
||||
|
||||
# place to put local temporary files
|
||||
tmp
|
||||
|
||||
# OS specific local files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
@ -16,9 +16,15 @@ Language Features:
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* Peephole Optimizer: Remove operations without side effects before simple terminations.
|
||||
* Assembly-Json: Export: Include source list in `sourceList` field.
|
||||
* Commandline Interface: option ``--pretty-json`` works also with the following options: ``--abi``, ``--asm-json``, ``--ast-compact-json``, ``--devdoc``, ``--storage-layout``, ``--userdoc``.
|
||||
* SMTChecker: Support ``abi.encodeCall`` taking into account the called selector.
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* Assembly-Json: Fix assembly json export to store jump types of operations in `jumpType` field instead of `value`.
|
||||
* TypeChecker: Convert parameters of function type to how they would be called for ``abi.encodeCall``.
|
||||
|
||||
|
||||
|
||||
|
@ -54,9 +54,10 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
||||
add_compile_options(-Wsign-conversion)
|
||||
add_compile_options(-Wconversion)
|
||||
|
||||
eth_add_cxx_compiler_flag_if_supported(
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wextra-semi>
|
||||
)
|
||||
check_cxx_compiler_flag(-Wextra-semi WEXTRA_SEMI)
|
||||
if(WEXTRA_SEMI)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wextra-semi>)
|
||||
endif()
|
||||
eth_add_cxx_compiler_flag_if_supported(-Wfinal-dtor-non-final-class)
|
||||
eth_add_cxx_compiler_flag_if_supported(-Wnewline-eof)
|
||||
eth_add_cxx_compiler_flag_if_supported(-Wsuggest-destructor-override)
|
||||
|
@ -313,7 +313,8 @@ Furthermore, if the assembly block assigns to Solidity variables in memory, you
|
||||
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:
|
||||
reverts or terminates. As an example, the following assembly snippet is not memory safe, because the value of
|
||||
``returndatasize()`` may exceed the 64 byte scratch space:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
@ -322,7 +323,8 @@ reverts or terminates. As an example, the following assembly snippet is not memo
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
|
||||
But the following is:
|
||||
On the other hand, the following code *is* memory safe, because memory beyond the location pointed to by the
|
||||
free memory pointer can safely be used as temporary scratch space:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
|
@ -6,12 +6,15 @@
|
||||
Abstract Contracts
|
||||
******************
|
||||
|
||||
Contracts need to be marked as abstract when at least one of their functions is not implemented.
|
||||
Contracts may be marked as abstract even though all functions are implemented.
|
||||
Contracts must be marked as abstract when at least one of their functions is not implemented or when
|
||||
they do not provide arguments for all of their base contract constructors.
|
||||
Even if this is not the case, a contract may still be marked abstract, such as when you do not intend
|
||||
for the contract to be created directly. Abstract contracts are similar to :ref:`interfaces` but an
|
||||
interface is more limited in what it can declare.
|
||||
|
||||
This can be done by using the ``abstract`` keyword as shown in the following example. Note that this contract needs to be
|
||||
defined as abstract, because the function ``utterance()`` was defined, but no implementation was
|
||||
provided (no implementation body ``{ }`` was given).
|
||||
An abstract contract is declared using the ``abstract`` keyword as shown in the following example.
|
||||
Note that this contract needs to be defined as abstract, because the function ``utterance()`` is declared,
|
||||
but no implementation was provided (no implementation body ``{ }`` was given).
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
|
@ -76,9 +76,9 @@ Details are given in the following example.
|
||||
}
|
||||
|
||||
|
||||
// Multiple inheritance is possible. Note that `owned` is
|
||||
// Multiple inheritance is possible. Note that `Owned` is
|
||||
// also a base class of `Destructible`, yet there is only a single
|
||||
// instance of `owned` (as for virtual inheritance in C++).
|
||||
// instance of `Owned` (as for virtual inheritance in C++).
|
||||
contract Named is Owned, Destructible {
|
||||
constructor(bytes32 name) {
|
||||
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
|
||||
@ -443,7 +443,7 @@ cannot be assigned valid values from outside but only through the constructors o
|
||||
``internal`` or ``public``.
|
||||
|
||||
|
||||
.. index:: ! base;constructor
|
||||
.. index:: ! base;constructor, inheritance list, contract;abstract, abstract contract
|
||||
|
||||
Arguments for Base Constructors
|
||||
===============================
|
||||
@ -467,11 +467,20 @@ derived contracts need to specify all of them. This can be done in two ways:
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
// or through a "modifier" of the derived constructor.
|
||||
// or through a "modifier" of the derived constructor...
|
||||
contract Derived2 is Base {
|
||||
constructor(uint y) Base(y * y) {}
|
||||
}
|
||||
|
||||
// or declare abstract...
|
||||
abstract contract Derived3 is Base {
|
||||
}
|
||||
|
||||
// and have the next concrete derived contract initialize it.
|
||||
contract DerivedFromDerived is Derived3 {
|
||||
constructor() Base(10 + 10) {}
|
||||
}
|
||||
|
||||
One way is directly in the inheritance list (``is Base(7)``). The other is in
|
||||
the way a modifier is invoked as part of
|
||||
the derived constructor (``Base(y * y)``). The first way to
|
||||
@ -484,7 +493,12 @@ inheritance list or in modifier-style in the derived constructor.
|
||||
Specifying arguments in both places is an error.
|
||||
|
||||
If a derived contract does not specify the arguments to all of its base
|
||||
contracts' constructors, it will be abstract.
|
||||
contracts' constructors, it must be declared abstract. In that case, when
|
||||
another contract derives from it, that other contract's inheritance list
|
||||
or constructor must provide the necessary parameters
|
||||
for all base classes that haven't had their parameters specified (otherwise,
|
||||
that other contract must be declared abstract as well). For example, in the above
|
||||
code snippet, see ``Derived3`` and ``DerivedFromDerived``.
|
||||
|
||||
.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization
|
||||
|
||||
|
@ -475,7 +475,7 @@ For example ``pragma solidity >=0.4.0 <0.9.0;``.
|
||||
Running Documentation Tests
|
||||
---------------------------
|
||||
|
||||
Make sure your contributions pass our documentation tests by running ``./scripts/docs.sh`` that installs dependencies
|
||||
Make sure your contributions pass our documentation tests by running ``./docs/docs.sh`` that installs dependencies
|
||||
needed for documentation and checks for any problems such as broken links or syntax issues.
|
||||
|
||||
Solidity Language Design
|
||||
|
@ -283,7 +283,7 @@ which only need to be created if there is a dispute.
|
||||
salt,
|
||||
keccak256(abi.encodePacked(
|
||||
type(D).creationCode,
|
||||
arg
|
||||
abi.encode(arg)
|
||||
))
|
||||
)))));
|
||||
|
||||
|
@ -26,8 +26,10 @@
|
||||
# (c) 2016 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
set -e
|
||||
cd docs
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(dirname "$0")"
|
||||
|
||||
cd "${script_dir}"
|
||||
pip3 install -r requirements.txt --upgrade --upgrade-strategy eager
|
||||
sphinx-build -nW -b html -d _build/doctrees . _build/html
|
||||
cd ..
|
@ -396,27 +396,34 @@ returns that code when executed.
|
||||
Gas
|
||||
===
|
||||
|
||||
Upon creation, each transaction is charged with a certain amount of **gas**,
|
||||
whose purpose is to limit the amount of work that is needed to execute
|
||||
the transaction and to pay for this execution at the same time. While the EVM executes the
|
||||
Upon creation, each transaction is charged with a certain amount of **gas**
|
||||
that has to be paid for by the originator of the transaction (``tx.origin``).
|
||||
While the EVM executes the
|
||||
transaction, the gas is gradually depleted according to specific rules.
|
||||
|
||||
The **gas price** is a value set by the creator of the transaction, who
|
||||
has to pay ``gas_price * gas`` up front from the sending account.
|
||||
If some gas is left after the execution, it is refunded to the creator in the same way.
|
||||
|
||||
If the gas is used up at any point (i.e. it would be negative),
|
||||
an out-of-gas exception is triggered, which reverts all modifications
|
||||
an out-of-gas exception is triggered, which ends execution and reverts all modifications
|
||||
made to the state in the current call frame.
|
||||
|
||||
This mechanism incentivizes economical use of EVM execution time
|
||||
and also compensates EVM executors (i.e. miners / stakers) for their work.
|
||||
Since each block has a maximum amount of gas, it also limits the amount
|
||||
of work needed to validate a block.
|
||||
|
||||
The **gas price** is a value set by the originator of the transaction, who
|
||||
has to pay ``gas_price * gas`` up front to the EVM executor.
|
||||
If some gas is left after execution, it is refunded to the transaction originator.
|
||||
In case of an exception that reverts changes, already used up gas is not refunded.
|
||||
|
||||
Since EVM executors can choose to include a transaction or not,
|
||||
transaction senders cannot abuse the system by setting a low gas price.
|
||||
|
||||
.. index:: ! storage, ! memory, ! stack
|
||||
|
||||
Storage, Memory and the Stack
|
||||
=============================
|
||||
|
||||
The Ethereum Virtual Machine has three areas where it can store data-
|
||||
storage, memory and the stack, which are explained in the following
|
||||
paragraphs.
|
||||
The Ethereum Virtual Machine has three areas where it can store data:
|
||||
storage, memory and the stack.
|
||||
|
||||
Each account has a data area called **storage**, which is persistent between function calls
|
||||
and transactions.
|
||||
@ -504,7 +511,7 @@ Delegatecall / Callcode and Libraries
|
||||
|
||||
There exists a special variant of a message call, named **delegatecall**
|
||||
which is identical to a message call apart from the fact that
|
||||
the code at the target address is executed in the context of the calling
|
||||
the code at the target address is executed in the context (i.e. at the address) of the calling
|
||||
contract and ``msg.sender`` and ``msg.value`` do not change their values.
|
||||
|
||||
This means that a contract can dynamically load code from a different
|
||||
|
@ -119,7 +119,7 @@ Iterable Mappings
|
||||
You cannot iterate over mappings, i.e. you cannot enumerate their keys.
|
||||
It is possible, though, to implement a data structure on
|
||||
top of them and iterate over that. For example, the code below implements an
|
||||
``IterableMapping`` library that the ``User`` contract then adds data too, and
|
||||
``IterableMapping`` library that the ``User`` contract then adds data to, and
|
||||
the ``sum`` function iterates over to sum all the values.
|
||||
|
||||
.. code-block:: solidity
|
||||
|
@ -463,7 +463,7 @@ There is no additional semantic meaning added to a number literal containing und
|
||||
the underscores are ignored.
|
||||
|
||||
Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by
|
||||
using them together with anything else than a number literal expression (like boolean literals) or by explicit conversion).
|
||||
using them together with anything other than a number literal expression (like boolean literals) or by explicit conversion).
|
||||
This means that computations do not overflow and divisions do not truncate
|
||||
in number literal expressions.
|
||||
|
||||
|
@ -222,123 +222,59 @@ string Assembly::assemblyString(
|
||||
return tmp.str();
|
||||
}
|
||||
|
||||
Json::Value Assembly::createJsonValue(string _name, int _source, int _begin, int _end, string _value, string _jumpType)
|
||||
{
|
||||
Json::Value value{Json::objectValue};
|
||||
value["name"] = _name;
|
||||
value["source"] = _source;
|
||||
value["begin"] = _begin;
|
||||
value["end"] = _end;
|
||||
if (!_value.empty())
|
||||
value["value"] = _value;
|
||||
if (!_jumpType.empty())
|
||||
value["jumpType"] = _jumpType;
|
||||
return value;
|
||||
}
|
||||
|
||||
string Assembly::toStringInHex(u256 _value)
|
||||
{
|
||||
std::stringstream hexStr;
|
||||
hexStr << std::uppercase << hex << _value;
|
||||
return hexStr.str();
|
||||
}
|
||||
|
||||
Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices) const
|
||||
Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices, bool _includeSourceList) const
|
||||
{
|
||||
Json::Value root;
|
||||
root[".code"] = Json::arrayValue;
|
||||
|
||||
Json::Value& collection = root[".code"];
|
||||
for (AssemblyItem const& i: m_items)
|
||||
Json::Value& code = root[".code"];
|
||||
for (AssemblyItem const& item: m_items)
|
||||
{
|
||||
int sourceIndex = -1;
|
||||
if (i.location().sourceName)
|
||||
if (item.location().sourceName)
|
||||
{
|
||||
auto iter = _sourceIndices.find(*i.location().sourceName);
|
||||
auto iter = _sourceIndices.find(*item.location().sourceName);
|
||||
if (iter != _sourceIndices.end())
|
||||
sourceIndex = static_cast<int>(iter->second);
|
||||
}
|
||||
|
||||
switch (i.type())
|
||||
auto [name, data] = item.nameAndData();
|
||||
Json::Value jsonItem;
|
||||
jsonItem["name"] = name;
|
||||
jsonItem["begin"] = item.location().start;
|
||||
jsonItem["end"] = item.location().end;
|
||||
if (item.m_modifierDepth != 0)
|
||||
jsonItem["modifierDepth"] = static_cast<int>(item.m_modifierDepth);
|
||||
std::string jumpType = item.getJumpTypeAsString();
|
||||
if (!jumpType.empty())
|
||||
jsonItem["jumpType"] = jumpType;
|
||||
if (name == "PUSHLIB")
|
||||
data = m_libraries.at(h256(data));
|
||||
else if (name == "PUSHIMMUTABLE" || name == "ASSIGNIMMUTABLE")
|
||||
data = m_immutables.at(h256(data));
|
||||
if (!data.empty())
|
||||
jsonItem["value"] = data;
|
||||
jsonItem["source"] = sourceIndex;
|
||||
code.append(move(jsonItem));
|
||||
|
||||
if (item.type() == AssemblyItemType::Tag)
|
||||
{
|
||||
case Operation:
|
||||
collection.append(
|
||||
createJsonValue(
|
||||
instructionInfo(i.instruction()).name,
|
||||
sourceIndex,
|
||||
i.location().start,
|
||||
i.location().end,
|
||||
i.getJumpTypeAsString())
|
||||
);
|
||||
break;
|
||||
case Push:
|
||||
collection.append(
|
||||
createJsonValue("PUSH", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString()));
|
||||
break;
|
||||
case PushTag:
|
||||
if (i.data() == 0)
|
||||
collection.append(
|
||||
createJsonValue("PUSH [ErrorTag]", sourceIndex, i.location().start, i.location().end, ""));
|
||||
else
|
||||
collection.append(
|
||||
createJsonValue("PUSH [tag]", sourceIndex, i.location().start, i.location().end, toString(i.data())));
|
||||
break;
|
||||
case PushSub:
|
||||
collection.append(
|
||||
createJsonValue("PUSH [$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data()))));
|
||||
break;
|
||||
case PushSubSize:
|
||||
collection.append(
|
||||
createJsonValue("PUSH #[$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data()))));
|
||||
break;
|
||||
case PushProgramSize:
|
||||
collection.append(
|
||||
createJsonValue("PUSHSIZE", sourceIndex, i.location().start, i.location().end));
|
||||
break;
|
||||
case PushLibraryAddress:
|
||||
collection.append(
|
||||
createJsonValue("PUSHLIB", sourceIndex, i.location().start, i.location().end, m_libraries.at(h256(i.data())))
|
||||
);
|
||||
break;
|
||||
case PushDeployTimeAddress:
|
||||
collection.append(
|
||||
createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end)
|
||||
);
|
||||
break;
|
||||
case PushImmutable:
|
||||
collection.append(createJsonValue(
|
||||
"PUSHIMMUTABLE",
|
||||
sourceIndex,
|
||||
i.location().start,
|
||||
i.location().end,
|
||||
m_immutables.at(h256(i.data()))
|
||||
));
|
||||
break;
|
||||
case AssignImmutable:
|
||||
collection.append(createJsonValue(
|
||||
"ASSIGNIMMUTABLE",
|
||||
sourceIndex,
|
||||
i.location().start,
|
||||
i.location().end,
|
||||
m_immutables.at(h256(i.data()))
|
||||
));
|
||||
break;
|
||||
case Tag:
|
||||
collection.append(
|
||||
createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data())));
|
||||
collection.append(
|
||||
createJsonValue("JUMPDEST", sourceIndex, i.location().start, i.location().end));
|
||||
break;
|
||||
case PushData:
|
||||
collection.append(createJsonValue("PUSH data", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data())));
|
||||
break;
|
||||
case VerbatimBytecode:
|
||||
collection.append(createJsonValue("VERBATIM", sourceIndex, i.location().start, i.location().end, util::toHex(i.verbatimData())));
|
||||
break;
|
||||
default:
|
||||
assertThrow(false, InvalidOpcode, "");
|
||||
Json::Value jumpdest;
|
||||
jumpdest["name"] = "JUMPDEST";
|
||||
jumpdest["begin"] = item.location().start;
|
||||
jumpdest["end"] = item.location().end;
|
||||
jumpdest["source"] = sourceIndex;
|
||||
if (item.m_modifierDepth != 0)
|
||||
jumpdest["modifierDepth"] = static_cast<int>(item.m_modifierDepth);
|
||||
code.append(move(jumpdest));
|
||||
}
|
||||
}
|
||||
if (_includeSourceList)
|
||||
{
|
||||
root["sourceList"] = Json::arrayValue;
|
||||
Json::Value& jsonSourceList = root["sourceList"];
|
||||
for (auto const& [name, index]: _sourceIndices)
|
||||
jsonSourceList[index] = name;
|
||||
}
|
||||
|
||||
if (!m_data.empty() || !m_subs.empty())
|
||||
{
|
||||
@ -346,17 +282,17 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices)
|
||||
Json::Value& data = root[".data"];
|
||||
for (auto const& i: m_data)
|
||||
if (u256(i.first) >= m_subs.size())
|
||||
data[toStringInHex((u256)i.first)] = util::toHex(i.second);
|
||||
data[util::toHex(toBigEndian((u256)i.first), util::HexPrefix::DontAdd, util::HexCase::Upper)] = util::toHex(i.second);
|
||||
|
||||
for (size_t i = 0; i < m_subs.size(); ++i)
|
||||
{
|
||||
std::stringstream hexStr;
|
||||
hexStr << hex << i;
|
||||
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices);
|
||||
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices, /*_includeSourceList = */false);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_auxiliaryData.size() > 0)
|
||||
if (!m_auxiliaryData.empty())
|
||||
root[".auxdata"] = util::toHex(m_auxiliaryData);
|
||||
|
||||
return root;
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
namespace solidity::evmasm
|
||||
{
|
||||
@ -147,7 +148,8 @@ public:
|
||||
|
||||
/// Create a JSON representation of the assembly.
|
||||
Json::Value assemblyJSON(
|
||||
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>()
|
||||
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>(),
|
||||
bool _includeSourceList = true
|
||||
) const;
|
||||
|
||||
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
|
||||
@ -167,16 +169,6 @@ protected:
|
||||
unsigned codeSize(unsigned subTagSize) const;
|
||||
|
||||
private:
|
||||
static Json::Value createJsonValue(
|
||||
std::string _name,
|
||||
int _source,
|
||||
int _begin,
|
||||
int _end,
|
||||
std::string _value = std::string(),
|
||||
std::string _jumpType = std::string()
|
||||
);
|
||||
static std::string toStringInHex(u256 _value);
|
||||
|
||||
bool m_invalid = false;
|
||||
|
||||
Assembly const* subAssemblyById(size_t _subId) const;
|
||||
@ -222,6 +214,7 @@ protected:
|
||||
std::string m_name;
|
||||
|
||||
langutil::SourceLocation m_currentSourceLocation;
|
||||
|
||||
public:
|
||||
size_t m_currentModifierDepth = 0;
|
||||
};
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
#include <libsolutil/Numeric.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolutil/FixedHash.h>
|
||||
@ -36,6 +37,18 @@ using namespace solidity::langutil;
|
||||
|
||||
static_assert(sizeof(size_t) <= 8, "size_t must be at most 64-bits wide");
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
string toStringInHex(u256 _value)
|
||||
{
|
||||
std::stringstream hexStr;
|
||||
hexStr << std::uppercase << hex << _value;
|
||||
return hexStr.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
|
||||
{
|
||||
assertThrow(data() < (u256(1) << 64), util::Exception, "Tag already has subassembly set.");
|
||||
@ -56,6 +69,44 @@ pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
|
||||
return make_pair(subId, tag);
|
||||
}
|
||||
|
||||
pair<string, string> AssemblyItem::nameAndData() const
|
||||
{
|
||||
switch (type())
|
||||
{
|
||||
case Operation:
|
||||
return {instructionInfo(instruction()).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
|
||||
case Push:
|
||||
return {"PUSH", toStringInHex(data())};
|
||||
case PushTag:
|
||||
if (data() == 0)
|
||||
return {"PUSH [ErrorTag]", ""};
|
||||
else
|
||||
return {"PUSH [tag]", util::toString(data())};
|
||||
case PushSub:
|
||||
return {"PUSH [$]", toString(util::h256(data()))};
|
||||
case PushSubSize:
|
||||
return {"PUSH #[$]", toString(util::h256(data()))};
|
||||
case PushProgramSize:
|
||||
return {"PUSHSIZE", ""};
|
||||
case PushLibraryAddress:
|
||||
return {"PUSHLIB", toString(util::h256(data()))};
|
||||
case PushDeployTimeAddress:
|
||||
return {"PUSHDEPLOYADDRESS", ""};
|
||||
case PushImmutable:
|
||||
return {"PUSHIMMUTABLE", toString(util::h256(data()))};
|
||||
case AssignImmutable:
|
||||
return {"ASSIGNIMMUTABLE", toString(util::h256(data()))};
|
||||
case Tag:
|
||||
return {"tag", util::toString(data())};
|
||||
case PushData:
|
||||
return {"PUSH data", toStringInHex(data())};
|
||||
case VerbatimBytecode:
|
||||
return {"VERBATIM", util::toHex(verbatimData())};
|
||||
default:
|
||||
assertThrow(false, InvalidOpcode, "");
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
|
||||
{
|
||||
assertThrow(m_type == PushTag || m_type == Tag, util::Exception, "");
|
||||
|
@ -106,6 +106,13 @@ public:
|
||||
u256 const& data() const { assertThrow(m_type != Operation, util::Exception, ""); return *m_data; }
|
||||
void setData(u256 const& _data) { assertThrow(m_type != Operation, util::Exception, ""); m_data = std::make_shared<u256>(_data); }
|
||||
|
||||
/// This function is used in `Assembly::assemblyJSON`.
|
||||
/// It returns the name & data of the current assembly item.
|
||||
/// @returns a pair, where the first element is the json-assembly
|
||||
/// item name, where second element is the string representation
|
||||
/// of it's data.
|
||||
std::pair<std::string, std::string> nameAndData() const;
|
||||
|
||||
bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return std::get<2>(*m_verbatimBytecode); }
|
||||
|
||||
/// @returns the instruction of this item (only valid if type() == Operation)
|
||||
|
@ -146,6 +146,35 @@ struct OpStop: SimplePeepholeOptimizerMethod<OpStop>
|
||||
}
|
||||
};
|
||||
|
||||
struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
|
||||
{
|
||||
static bool applySimple(
|
||||
AssemblyItem const& _op,
|
||||
AssemblyItem const& _push,
|
||||
AssemblyItem const& _pushOrDup,
|
||||
AssemblyItem const& _returnRevert,
|
||||
std::back_insert_iterator<AssemblyItems> _out
|
||||
)
|
||||
{
|
||||
if (
|
||||
(_returnRevert == Instruction::RETURN || _returnRevert == Instruction::REVERT) &&
|
||||
_push.type() == Push &&
|
||||
(_pushOrDup.type() == Push || _pushOrDup == dupInstruction(1))
|
||||
)
|
||||
if (
|
||||
(_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) ||
|
||||
_op.type() == Push
|
||||
)
|
||||
{
|
||||
*_out = _push;
|
||||
*_out = _pushOrDup;
|
||||
*_out = _returnRevert;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct DoubleSwap: SimplePeepholeOptimizerMethod<DoubleSwap>
|
||||
{
|
||||
static size_t applySimple(AssemblyItem const& _s1, AssemblyItem const& _s2, std::back_insert_iterator<AssemblyItems>)
|
||||
@ -459,7 +488,7 @@ bool PeepholeOptimiser::optimise()
|
||||
while (state.i < m_items.size())
|
||||
applyMethods(
|
||||
state,
|
||||
PushPop(), OpPop(), OpStop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(),
|
||||
PushPop(), OpPop(), OpStop(), OpReturnRevert(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(),
|
||||
DupSwap(), IsZeroIsZeroJumpI(), EqIsZeroJumpI(), DoubleJump(), JumpToNext(), UnreachableCode(),
|
||||
TagConjunctions(), TruthyAnd(), Identity()
|
||||
);
|
||||
|
@ -58,13 +58,13 @@ struct InvalidAstError: virtual util::Exception {};
|
||||
#endif
|
||||
|
||||
#define solAssert_1(CONDITION) \
|
||||
solAssert_2(CONDITION, "")
|
||||
solAssert_2((CONDITION), "")
|
||||
|
||||
#define solAssert_2(CONDITION, DESCRIPTION) \
|
||||
assertThrowWithDefaultDescription( \
|
||||
CONDITION, \
|
||||
(CONDITION), \
|
||||
::solidity::langutil::InternalCompilerError, \
|
||||
DESCRIPTION, \
|
||||
(DESCRIPTION), \
|
||||
"Solidity assertion failed" \
|
||||
)
|
||||
|
||||
@ -77,13 +77,13 @@ struct InvalidAstError: virtual util::Exception {};
|
||||
#endif
|
||||
|
||||
#define solUnimplementedAssert_1(CONDITION) \
|
||||
solUnimplementedAssert_2(CONDITION, "")
|
||||
solUnimplementedAssert_2((CONDITION), "")
|
||||
|
||||
#define solUnimplementedAssert_2(CONDITION, DESCRIPTION) \
|
||||
assertThrowWithDefaultDescription( \
|
||||
CONDITION, \
|
||||
(CONDITION), \
|
||||
::solidity::langutil::UnimplementedFeatureError, \
|
||||
DESCRIPTION, \
|
||||
(DESCRIPTION), \
|
||||
"Unimplemented feature" \
|
||||
)
|
||||
|
||||
@ -105,9 +105,9 @@ struct InvalidAstError: virtual util::Exception {};
|
||||
|
||||
#define astAssert_2(CONDITION, DESCRIPTION) \
|
||||
assertThrowWithDefaultDescription( \
|
||||
CONDITION, \
|
||||
(CONDITION), \
|
||||
::solidity::langutil::InvalidAstError, \
|
||||
DESCRIPTION, \
|
||||
(DESCRIPTION), \
|
||||
"AST assertion failed" \
|
||||
)
|
||||
|
||||
|
@ -38,13 +38,13 @@ struct SMTLogicError: virtual util::Exception {};
|
||||
#endif
|
||||
|
||||
#define smtAssert_1(CONDITION) \
|
||||
smtAssert_2(CONDITION, "")
|
||||
smtAssert_2((CONDITION), "")
|
||||
|
||||
#define smtAssert_2(CONDITION, DESCRIPTION) \
|
||||
assertThrowWithDefaultDescription( \
|
||||
CONDITION, \
|
||||
(CONDITION), \
|
||||
::solidity::smtutil::SMTLogicError, \
|
||||
DESCRIPTION, \
|
||||
(DESCRIPTION), \
|
||||
"SMT assertion failed" \
|
||||
)
|
||||
|
||||
|
@ -297,7 +297,7 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
else if (optional<ConstantEvaluator::TypedRational> value = ConstantEvaluator::evaluate(m_errorReporter, *length))
|
||||
lengthValue = value->value;
|
||||
|
||||
if (!lengthValue || lengthValue > TypeProvider::uint256()->max())
|
||||
if (!lengthValue)
|
||||
m_errorReporter.typeError(
|
||||
5462_error,
|
||||
length->location(),
|
||||
@ -309,6 +309,12 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
m_errorReporter.typeError(3208_error, length->location(), "Array with fractional length specified.");
|
||||
else if (*lengthValue < 0)
|
||||
m_errorReporter.typeError(3658_error, length->location(), "Array with negative length specified.");
|
||||
else if (lengthValue > TypeProvider::uint256()->max())
|
||||
m_errorReporter.typeError(
|
||||
1847_error,
|
||||
length->location(),
|
||||
"Array length too large, maximum is 2**256 - 1."
|
||||
);
|
||||
|
||||
_typeName.annotation().type = TypeProvider::array(
|
||||
DataLocation::Storage,
|
||||
|
@ -1658,24 +1658,21 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
|
||||
|
||||
Type const* subExprType = type(_operation.subExpression());
|
||||
TypeResult result = subExprType->unaryOperatorResult(op);
|
||||
Type const* t = result;
|
||||
if (!result)
|
||||
{
|
||||
string description = "Unary operator " +
|
||||
string(TokenTraits::toString(op)) +
|
||||
" cannot be applied to type " +
|
||||
subExprType->toString() +
|
||||
(result.message().empty() ? "" : (": " + result.message()));
|
||||
|
||||
string description = "Unary operator " + string(TokenTraits::toString(op)) + " cannot be applied to type " + subExprType->toString();
|
||||
if (!result.message().empty())
|
||||
description += ". " + result.message();
|
||||
if (modifying)
|
||||
// Cannot just report the error, ignore the unary operator, and continue,
|
||||
// because the sub-expression was already processed with requireLValue()
|
||||
m_errorReporter.fatalTypeError(9767_error, _operation.location(), description);
|
||||
else
|
||||
m_errorReporter.typeError(4907_error, _operation.location(), description);
|
||||
t = subExprType;
|
||||
_operation.annotation().type = subExprType;
|
||||
}
|
||||
_operation.annotation().type = t;
|
||||
else
|
||||
_operation.annotation().type = result.get();
|
||||
_operation.annotation().isConstant = false;
|
||||
_operation.annotation().isPure = !modifying && *_operation.subExpression().annotation().isPure;
|
||||
_operation.annotation().isLValue = false;
|
||||
@ -2111,57 +2108,60 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
||||
return;
|
||||
}
|
||||
|
||||
auto const functionPointerType = dynamic_cast<FunctionTypePointer>(type(*arguments.front()));
|
||||
|
||||
if (!functionPointerType)
|
||||
FunctionType const* externalFunctionType = nullptr;
|
||||
if (auto const functionPointerType = dynamic_cast<FunctionTypePointer>(type(*arguments.front())))
|
||||
{
|
||||
// this cannot be a library function, that is checked below
|
||||
externalFunctionType = functionPointerType->asExternallyCallableFunction(false);
|
||||
solAssert(externalFunctionType->kind() == functionPointerType->kind());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
5511_error,
|
||||
arguments.front()->location(),
|
||||
"Expected first argument to be a function pointer, not \"" +
|
||||
type(*arguments.front())->canonicalName() +
|
||||
type(*arguments.front())->toString() +
|
||||
"\"."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
functionPointerType->kind() != FunctionType::Kind::External &&
|
||||
functionPointerType->kind() != FunctionType::Kind::Declaration
|
||||
externalFunctionType->kind() != FunctionType::Kind::External &&
|
||||
externalFunctionType->kind() != FunctionType::Kind::Declaration
|
||||
)
|
||||
{
|
||||
string msg = "Expected regular external function type, or external view on public function.";
|
||||
if (functionPointerType->kind() == FunctionType::Kind::Internal)
|
||||
if (externalFunctionType->kind() == FunctionType::Kind::Internal)
|
||||
msg += " Provided internal function.";
|
||||
else if (functionPointerType->kind() == FunctionType::Kind::DelegateCall)
|
||||
else if (externalFunctionType->kind() == FunctionType::Kind::DelegateCall)
|
||||
msg += " Cannot use library functions for abi.encodeCall.";
|
||||
else if (functionPointerType->kind() == FunctionType::Kind::Creation)
|
||||
else if (externalFunctionType->kind() == FunctionType::Kind::Creation)
|
||||
msg += " Provided creation function.";
|
||||
else
|
||||
msg += " Cannot use special function.";
|
||||
SecondarySourceLocation ssl{};
|
||||
|
||||
if (functionPointerType->hasDeclaration())
|
||||
if (externalFunctionType->hasDeclaration())
|
||||
{
|
||||
ssl.append("Function is declared here:", functionPointerType->declaration().location());
|
||||
ssl.append("Function is declared here:", externalFunctionType->declaration().location());
|
||||
if (
|
||||
functionPointerType->declaration().visibility() == Visibility::Public &&
|
||||
functionPointerType->declaration().scope() == m_currentContract
|
||||
externalFunctionType->declaration().visibility() == Visibility::Public &&
|
||||
externalFunctionType->declaration().scope() == m_currentContract
|
||||
)
|
||||
msg += " Did you forget to prefix \"this.\"?";
|
||||
else if (util::contains(
|
||||
m_currentContract->annotation().linearizedBaseContracts,
|
||||
functionPointerType->declaration().scope()
|
||||
) && functionPointerType->declaration().scope() != m_currentContract)
|
||||
externalFunctionType->declaration().scope()
|
||||
) && externalFunctionType->declaration().scope() != m_currentContract)
|
||||
msg += " Functions from base contracts have to be external.";
|
||||
}
|
||||
|
||||
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
solAssert(!functionPointerType->takesArbitraryParameters(), "Function must have fixed parameters.");
|
||||
|
||||
solAssert(!externalFunctionType->takesArbitraryParameters(), "Function must have fixed parameters.");
|
||||
// Tuples with only one component become that component
|
||||
vector<ASTPointer<Expression const>> callArguments;
|
||||
|
||||
@ -2174,14 +2174,14 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
||||
else
|
||||
callArguments.push_back(arguments[1]);
|
||||
|
||||
if (functionPointerType->parameterTypes().size() != callArguments.size())
|
||||
if (externalFunctionType->parameterTypes().size() != callArguments.size())
|
||||
{
|
||||
if (tupleType)
|
||||
m_errorReporter.typeError(
|
||||
7788_error,
|
||||
_functionCall.location(),
|
||||
"Expected " +
|
||||
to_string(functionPointerType->parameterTypes().size()) +
|
||||
to_string(externalFunctionType->parameterTypes().size()) +
|
||||
" instead of " +
|
||||
to_string(callArguments.size()) +
|
||||
" components for the tuple parameter."
|
||||
@ -2191,18 +2191,18 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
||||
7515_error,
|
||||
_functionCall.location(),
|
||||
"Expected a tuple with " +
|
||||
to_string(functionPointerType->parameterTypes().size()) +
|
||||
to_string(externalFunctionType->parameterTypes().size()) +
|
||||
" components instead of a single non-tuple parameter."
|
||||
);
|
||||
}
|
||||
|
||||
// Use min() to check as much as we can before failing fatally
|
||||
size_t const numParameters = min(callArguments.size(), functionPointerType->parameterTypes().size());
|
||||
size_t const numParameters = min(callArguments.size(), externalFunctionType->parameterTypes().size());
|
||||
|
||||
for (size_t i = 0; i < numParameters; i++)
|
||||
{
|
||||
Type const& argType = *type(*callArguments[i]);
|
||||
BoolResult result = argType.isImplicitlyConvertibleTo(*functionPointerType->parameterTypes()[i]);
|
||||
BoolResult result = argType.isImplicitlyConvertibleTo(*externalFunctionType->parameterTypes()[i]);
|
||||
if (!result)
|
||||
m_errorReporter.typeError(
|
||||
5407_error,
|
||||
@ -2212,7 +2212,7 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
||||
" from \"" +
|
||||
argType.toString() +
|
||||
"\" to \"" +
|
||||
functionPointerType->parameterTypes()[i]->toString() +
|
||||
externalFunctionType->parameterTypes()[i]->toString() +
|
||||
"\"" +
|
||||
(result.message().empty() ? "." : ": " + result.message())
|
||||
);
|
||||
|
@ -190,9 +190,9 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifie
|
||||
return tuple;
|
||||
}
|
||||
|
||||
void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node)
|
||||
void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node, util::JsonFormat const& _format)
|
||||
{
|
||||
_stream << util::jsonPrettyPrint(toJson(_node));
|
||||
_stream << util::jsonPrint(toJson(_node), _format);
|
||||
}
|
||||
|
||||
Json::Value ASTJsonConverter::toJson(ASTNode const& _node)
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <json/json.h>
|
||||
#include <libsolutil/JSON.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
@ -58,7 +59,7 @@ public:
|
||||
std::map<std::string, unsigned> _sourceIndices = std::map<std::string, unsigned>()
|
||||
);
|
||||
/// Output the json representation of the AST to _stream.
|
||||
void print(std::ostream& _stream, ASTNode const& _node);
|
||||
void print(std::ostream& _stream, ASTNode const& _node, util::JsonFormat const& _format);
|
||||
Json::Value toJson(ASTNode const& _node);
|
||||
template <class T>
|
||||
Json::Value toJson(std::vector<ASTPointer<T>> const& _nodes)
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <libsolidity/codegen/ABIFunctions.h>
|
||||
#include <libsolidity/codegen/CompilerUtils.h>
|
||||
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <libyul/YulStack.h>
|
||||
#include <libyul/Utilities.h>
|
||||
|
||||
#include <libsolutil/Algorithms.h>
|
||||
@ -95,9 +95,9 @@ pair<string, string> IRGenerator::run(
|
||||
{
|
||||
string ir = yul::reindent(generate(_contract, _cborMetadata, _otherYulSources));
|
||||
|
||||
yul::AssemblyStack asmStack(
|
||||
yul::YulStack asmStack(
|
||||
m_evmVersion,
|
||||
yul::AssemblyStack::Language::StrictAssembly,
|
||||
yul::YulStack::Language::StrictAssembly,
|
||||
m_optimiserSettings,
|
||||
m_context.debugInfoSelection()
|
||||
);
|
||||
|
@ -586,6 +586,16 @@ bool SMTEncoder::visit(FunctionCall const& _funCall)
|
||||
arg->accept(*this);
|
||||
return false;
|
||||
}
|
||||
else if (funType.kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
auto fun = _funCall.arguments().front();
|
||||
createExpr(*fun);
|
||||
auto const* functionType = dynamic_cast<FunctionType const*>(fun->annotation().type);
|
||||
if (functionType->hasDeclaration())
|
||||
defineExpr(*fun, functionType->externalIdentifier());
|
||||
return true;
|
||||
}
|
||||
|
||||
// We do not really need to visit the expression in a wrap/unwrap no-op call,
|
||||
// so we just ignore the function call expression to avoid "unsupported" warnings.
|
||||
else if (
|
||||
@ -1323,6 +1333,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
||||
|
||||
auto const& exprType = memberExpr->annotation().type;
|
||||
solAssert(exprType, "");
|
||||
|
||||
if (exprType->category() == Type::Category::Magic)
|
||||
{
|
||||
if (auto const* identifier = dynamic_cast<Identifier const*>(memberExpr))
|
||||
|
@ -62,7 +62,7 @@
|
||||
#include <libyul/YulString.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AsmJsonConverter.h>
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <libyul/YulStack.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
|
||||
@ -1382,9 +1382,9 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
|
||||
return;
|
||||
|
||||
// Re-parse the Yul IR in EVM dialect
|
||||
yul::AssemblyStack stack(
|
||||
yul::YulStack stack(
|
||||
m_evmVersion,
|
||||
yul::AssemblyStack::Language::StrictAssembly,
|
||||
yul::YulStack::Language::StrictAssembly,
|
||||
m_optimiserSettings,
|
||||
m_debugInfoSelection
|
||||
);
|
||||
@ -1414,22 +1414,22 @@ void CompilerStack::generateEwasm(ContractDefinition const& _contract)
|
||||
return;
|
||||
|
||||
// Re-parse the Yul IR in EVM dialect
|
||||
yul::AssemblyStack stack(
|
||||
yul::YulStack stack(
|
||||
m_evmVersion,
|
||||
yul::AssemblyStack::Language::StrictAssembly,
|
||||
yul::YulStack::Language::StrictAssembly,
|
||||
m_optimiserSettings,
|
||||
m_debugInfoSelection
|
||||
);
|
||||
stack.parseAndAnalyze("", compiledContract.yulIROptimized);
|
||||
|
||||
stack.optimize();
|
||||
stack.translate(yul::AssemblyStack::Language::Ewasm);
|
||||
stack.translate(yul::YulStack::Language::Ewasm);
|
||||
stack.optimize();
|
||||
|
||||
//cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl;
|
||||
|
||||
// Turn into Ewasm text representation.
|
||||
auto result = stack.assemble(yul::AssemblyStack::Machine::Ewasm);
|
||||
auto result = stack.assemble(yul::YulStack::Machine::Ewasm);
|
||||
compiledContract.ewasm = std::move(result.assembly);
|
||||
compiledContract.ewasmObject = std::move(*result.bytecode);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <libsolidity/interface/ImportRemapper.h>
|
||||
|
||||
#include <libsolidity/ast/ASTJsonConverter.h>
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <libyul/YulStack.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libyul/optimiser/Suite.h>
|
||||
|
||||
@ -1407,9 +1407,9 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
||||
return output;
|
||||
}
|
||||
|
||||
AssemblyStack stack(
|
||||
YulStack stack(
|
||||
_inputsAndSettings.evmVersion,
|
||||
AssemblyStack::Language::StrictAssembly,
|
||||
YulStack::Language::StrictAssembly,
|
||||
_inputsAndSettings.optimiserSettings,
|
||||
_inputsAndSettings.debugInfoSelection.has_value() ?
|
||||
_inputsAndSettings.debugInfoSelection.value() :
|
||||
|
@ -76,6 +76,7 @@ LanguageServer::LanguageServer(Transport& _transport):
|
||||
{"exit", [this](auto, auto) { m_state = (m_state == State::ShutdownRequested ? State::ExitRequested : State::ExitWithoutShutdown); }},
|
||||
{"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)},
|
||||
{"initialized", [](auto, auto) {}},
|
||||
{"$/setTrace", bind(&LanguageServer::setTrace, this, _2)},
|
||||
{"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }},
|
||||
{"textDocument/definition", GotoDefinition(*this) },
|
||||
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)},
|
||||
@ -166,6 +167,13 @@ void LanguageServer::compileAndUpdateDiagnostics()
|
||||
diagnosticsBySourceUnit[*location->sourceName].append(jsonDiag);
|
||||
}
|
||||
|
||||
if (m_client.traceValue() != TraceValue::Off)
|
||||
{
|
||||
Json::Value extra;
|
||||
extra["openFileCount"] = Json::UInt64(diagnosticsBySourceUnit.size());
|
||||
m_client.trace("Number of currently open files: " + to_string(diagnosticsBySourceUnit.size()), extra);
|
||||
}
|
||||
|
||||
m_nonemptyDiagnostics.clear();
|
||||
for (auto&& [sourceUnitName, diagnostics]: diagnosticsBySourceUnit)
|
||||
{
|
||||
@ -273,6 +281,21 @@ void LanguageServer::handleWorkspaceDidChangeConfiguration(Json::Value const& _a
|
||||
changeConfiguration(_args["settings"]);
|
||||
}
|
||||
|
||||
void LanguageServer::setTrace(Json::Value const& _args)
|
||||
{
|
||||
if (!_args["value"].isString())
|
||||
// Simply ignore invalid parameter.
|
||||
return;
|
||||
|
||||
string const stringValue = _args["value"].asString();
|
||||
if (stringValue == "off")
|
||||
m_client.setTrace(TraceValue::Off);
|
||||
else if (stringValue == "messages")
|
||||
m_client.setTrace(TraceValue::Messages);
|
||||
else if (stringValue == "verbose")
|
||||
m_client.setTrace(TraceValue::Verbose);
|
||||
}
|
||||
|
||||
void LanguageServer::handleTextDocumentDidOpen(Json::Value const& _args)
|
||||
{
|
||||
requireServerInitialized();
|
||||
|
@ -68,6 +68,7 @@ private:
|
||||
void requireServerInitialized();
|
||||
void handleInitialize(MessageID _id, Json::Value const& _args);
|
||||
void handleWorkspaceDidChangeConfiguration(Json::Value const& _args);
|
||||
void setTrace(Json::Value const& _args);
|
||||
void handleTextDocumentDidOpen(Json::Value const& _args);
|
||||
void handleTextDocumentDidChange(Json::Value const& _args);
|
||||
void handleTextDocumentDidClose(Json::Value const& _args);
|
||||
|
@ -98,6 +98,18 @@ void IOStreamTransport::error(MessageID _id, ErrorCode _code, string _message)
|
||||
send(move(json), _id);
|
||||
}
|
||||
|
||||
void Transport::trace(std::string _message, Json::Value _extra)
|
||||
{
|
||||
if (m_logTrace != TraceValue::Off)
|
||||
{
|
||||
Json::Value params;
|
||||
if (_extra.isObject())
|
||||
params = move(_extra);
|
||||
params["message"] = move(_message);
|
||||
notify("$/logTrace", move(params));
|
||||
}
|
||||
}
|
||||
|
||||
void IOStreamTransport::send(Json::Value _json, MessageID _id)
|
||||
{
|
||||
solAssert(_json.isObject());
|
||||
|
@ -34,6 +34,13 @@ namespace solidity::lsp
|
||||
|
||||
using MessageID = Json::Value;
|
||||
|
||||
enum class TraceValue
|
||||
{
|
||||
Off,
|
||||
Messages,
|
||||
Verbose
|
||||
};
|
||||
|
||||
enum class ErrorCode
|
||||
{
|
||||
// Defined by JSON RPC
|
||||
@ -89,6 +96,15 @@ public:
|
||||
virtual void notify(std::string _method, Json::Value _params) = 0;
|
||||
virtual void reply(MessageID _id, Json::Value _result) = 0;
|
||||
virtual void error(MessageID _id, ErrorCode _code, std::string _message) = 0;
|
||||
|
||||
void trace(std::string _message, Json::Value _extra = Json::nullValue);
|
||||
|
||||
TraceValue traceValue() const noexcept { return m_logTrace; }
|
||||
void setTrace(TraceValue _value) noexcept { m_logTrace = _value; }
|
||||
|
||||
|
||||
private:
|
||||
TraceValue m_logTrace = TraceValue::Off;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -63,7 +63,7 @@ inline std::string stringOrDefault(std::string _string, std::string _defaultStri
|
||||
if (!(_condition)) \
|
||||
solThrow( \
|
||||
_exceptionType, \
|
||||
::solidity::util::assertions::stringOrDefault(_description, _defaultDescription) \
|
||||
::solidity::util::assertions::stringOrDefault((_description), (_defaultDescription)) \
|
||||
); \
|
||||
} \
|
||||
while (false)
|
||||
@ -72,6 +72,6 @@ inline std::string stringOrDefault(std::string _string, std::string _defaultStri
|
||||
/// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong.");
|
||||
/// The second parameter must be an exception class (rather than an instance).
|
||||
#define assertThrow(_condition, _exceptionType, _description) \
|
||||
assertThrowWithDefaultDescription(_condition, _exceptionType, _description, "Assertion failed")
|
||||
assertThrowWithDefaultDescription((_condition), _exceptionType, (_description), "Assertion failed")
|
||||
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ struct Exception: virtual std::exception, virtual boost::exception
|
||||
#define solThrow(_exceptionType, _description) \
|
||||
::boost::throw_exception( \
|
||||
_exceptionType() << \
|
||||
::solidity::util::errinfo_comment(_description) << \
|
||||
::solidity::util::errinfo_comment((_description)) << \
|
||||
::boost::throw_function(ETH_FUNC) << \
|
||||
::boost::throw_file(__FILE__) << \
|
||||
::boost::throw_line(__LINE__) \
|
||||
|
@ -30,8 +30,8 @@ add_library(yul
|
||||
AsmParser.h
|
||||
AsmPrinter.cpp
|
||||
AsmPrinter.h
|
||||
AssemblyStack.h
|
||||
AssemblyStack.cpp
|
||||
YulStack.h
|
||||
YulStack.cpp
|
||||
CompilabilityChecker.cpp
|
||||
CompilabilityChecker.h
|
||||
ControlFlowSideEffects.h
|
||||
|
@ -63,13 +63,13 @@ struct StackTooDeepError: virtual YulException
|
||||
#endif
|
||||
|
||||
#define yulAssert_1(CONDITION) \
|
||||
yulAssert_2(CONDITION, "")
|
||||
yulAssert_2((CONDITION), "")
|
||||
|
||||
#define yulAssert_2(CONDITION, DESCRIPTION) \
|
||||
assertThrowWithDefaultDescription( \
|
||||
CONDITION, \
|
||||
(CONDITION), \
|
||||
::solidity::yul::YulAssertion, \
|
||||
DESCRIPTION, \
|
||||
(DESCRIPTION), \
|
||||
"Yul assertion failed" \
|
||||
)
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <libyul/YulStack.h>
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
@ -48,16 +48,16 @@ using namespace solidity::langutil;
|
||||
|
||||
namespace
|
||||
{
|
||||
Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _version)
|
||||
Dialect const& languageToDialect(YulStack::Language _language, EVMVersion _version)
|
||||
{
|
||||
switch (_language)
|
||||
{
|
||||
case AssemblyStack::Language::Assembly:
|
||||
case AssemblyStack::Language::StrictAssembly:
|
||||
case YulStack::Language::Assembly:
|
||||
case YulStack::Language::StrictAssembly:
|
||||
return EVMDialect::strictAssemblyForEVMObjects(_version);
|
||||
case AssemblyStack::Language::Yul:
|
||||
case YulStack::Language::Yul:
|
||||
return EVMDialectTyped::instance(_version);
|
||||
case AssemblyStack::Language::Ewasm:
|
||||
case YulStack::Language::Ewasm:
|
||||
return WasmDialect::instance();
|
||||
}
|
||||
yulAssert(false, "");
|
||||
@ -88,14 +88,14 @@ evmasm::Assembly::OptimiserSettings translateOptimiserSettings(
|
||||
}
|
||||
|
||||
|
||||
CharStream const& AssemblyStack::charStream(string const& _sourceName) const
|
||||
CharStream const& YulStack::charStream(string const& _sourceName) const
|
||||
{
|
||||
yulAssert(m_charStream, "");
|
||||
yulAssert(m_charStream->name() == _sourceName, "");
|
||||
return *m_charStream;
|
||||
}
|
||||
|
||||
bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
|
||||
bool YulStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
|
||||
{
|
||||
m_errors.clear();
|
||||
m_analysisSuccessful = false;
|
||||
@ -110,7 +110,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
|
||||
return analyzeParsed();
|
||||
}
|
||||
|
||||
void AssemblyStack::optimize()
|
||||
void YulStack::optimize()
|
||||
{
|
||||
if (!m_optimiserSettings.runYulOptimiser)
|
||||
return;
|
||||
@ -123,7 +123,7 @@ void AssemblyStack::optimize()
|
||||
yulAssert(analyzeParsed(), "Invalid source code after optimization.");
|
||||
}
|
||||
|
||||
void AssemblyStack::translate(AssemblyStack::Language _targetLanguage)
|
||||
void YulStack::translate(YulStack::Language _targetLanguage)
|
||||
{
|
||||
if (m_language == _targetLanguage)
|
||||
return;
|
||||
@ -141,14 +141,14 @@ void AssemblyStack::translate(AssemblyStack::Language _targetLanguage)
|
||||
m_language = _targetLanguage;
|
||||
}
|
||||
|
||||
bool AssemblyStack::analyzeParsed()
|
||||
bool YulStack::analyzeParsed()
|
||||
{
|
||||
yulAssert(m_parserResult, "");
|
||||
m_analysisSuccessful = analyzeParsed(*m_parserResult);
|
||||
return m_analysisSuccessful;
|
||||
}
|
||||
|
||||
bool AssemblyStack::analyzeParsed(Object& _object)
|
||||
bool YulStack::analyzeParsed(Object& _object)
|
||||
{
|
||||
yulAssert(_object.code, "");
|
||||
_object.analysisInfo = make_shared<AsmAnalysisInfo>();
|
||||
@ -168,7 +168,7 @@ bool AssemblyStack::analyzeParsed(Object& _object)
|
||||
return success;
|
||||
}
|
||||
|
||||
void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) const
|
||||
void YulStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) const
|
||||
{
|
||||
EVMDialect const* dialect = nullptr;
|
||||
switch (m_language)
|
||||
@ -188,7 +188,7 @@ void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) cons
|
||||
EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _optimize);
|
||||
}
|
||||
|
||||
void AssemblyStack::optimize(Object& _object, bool _isCreation)
|
||||
void YulStack::optimize(Object& _object, bool _isCreation)
|
||||
{
|
||||
yulAssert(_object.code, "");
|
||||
yulAssert(_object.analysisInfo, "");
|
||||
@ -214,7 +214,7 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation)
|
||||
);
|
||||
}
|
||||
|
||||
MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
||||
MachineAssemblyObject YulStack::assemble(Machine _machine) const
|
||||
{
|
||||
yulAssert(m_analysisSuccessful, "");
|
||||
yulAssert(m_parserResult, "");
|
||||
@ -243,7 +243,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
||||
}
|
||||
|
||||
std::pair<MachineAssemblyObject, MachineAssemblyObject>
|
||||
AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
|
||||
YulStack::assembleWithDeployed(optional<string_view> _deployName) const
|
||||
{
|
||||
auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName);
|
||||
yulAssert(creationAssembly, "");
|
||||
@ -277,7 +277,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<evmasm::Assembly>, std::shared_ptr<evmasm::Assembly>>
|
||||
AssemblyStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
|
||||
YulStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
|
||||
{
|
||||
yulAssert(m_analysisSuccessful, "");
|
||||
yulAssert(m_parserResult, "");
|
||||
@ -317,7 +317,7 @@ AssemblyStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
|
||||
return {make_shared<evmasm::Assembly>(assembly), {}};
|
||||
}
|
||||
|
||||
string AssemblyStack::print(
|
||||
string YulStack::print(
|
||||
CharStreamProvider const* _soliditySourceProvider
|
||||
) const
|
||||
{
|
||||
@ -326,7 +326,7 @@ string AssemblyStack::print(
|
||||
return m_parserResult->toString(&languageToDialect(m_language, m_evmVersion), m_debugInfoSelection, _soliditySourceProvider) + "\n";
|
||||
}
|
||||
|
||||
shared_ptr<Object> AssemblyStack::parserResult() const
|
||||
shared_ptr<Object> YulStack::parserResult() const
|
||||
{
|
||||
yulAssert(m_analysisSuccessful, "Analysis was not successful.");
|
||||
yulAssert(m_parserResult, "");
|
@ -63,14 +63,14 @@ struct MachineAssemblyObject
|
||||
* Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and
|
||||
* Ewasm as output.
|
||||
*/
|
||||
class AssemblyStack: public langutil::CharStreamProvider
|
||||
class YulStack: public langutil::CharStreamProvider
|
||||
{
|
||||
public:
|
||||
enum class Language { Yul, Assembly, StrictAssembly, Ewasm };
|
||||
enum class Machine { EVM, Ewasm };
|
||||
|
||||
AssemblyStack():
|
||||
AssemblyStack(
|
||||
YulStack():
|
||||
YulStack(
|
||||
langutil::EVMVersion{},
|
||||
Language::Assembly,
|
||||
solidity::frontend::OptimiserSettings::none(),
|
||||
@ -78,7 +78,7 @@ public:
|
||||
)
|
||||
{}
|
||||
|
||||
AssemblyStack(
|
||||
YulStack(
|
||||
langutil::EVMVersion _evmVersion,
|
||||
Language _language,
|
||||
solidity::frontend::OptimiserSettings _optimiserSettings,
|
@ -30,18 +30,22 @@ using namespace solidity::util;
|
||||
|
||||
void NameCollector::operator()(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
for (auto const& var: _varDecl.variables)
|
||||
m_names.emplace(var.name);
|
||||
if (m_collectWhat != OnlyFunctions)
|
||||
for (auto const& var: _varDecl.variables)
|
||||
m_names.emplace(var.name);
|
||||
}
|
||||
|
||||
void NameCollector::operator ()(FunctionDefinition const& _funDef)
|
||||
void NameCollector::operator()(FunctionDefinition const& _funDef)
|
||||
{
|
||||
if (m_collectWhat == VariablesAndFunctions)
|
||||
if (m_collectWhat != OnlyVariables)
|
||||
m_names.emplace(_funDef.name);
|
||||
for (auto const& arg: _funDef.parameters)
|
||||
m_names.emplace(arg.name);
|
||||
for (auto const& ret: _funDef.returnVariables)
|
||||
m_names.emplace(ret.name);
|
||||
if (m_collectWhat != OnlyFunctions)
|
||||
{
|
||||
for (auto const& arg: _funDef.parameters)
|
||||
m_names.emplace(arg.name);
|
||||
for (auto const& ret: _funDef.returnVariables)
|
||||
m_names.emplace(ret.name);
|
||||
}
|
||||
ASTWalker::operator ()(_funDef);
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace solidity::yul
|
||||
class NameCollector: public ASTWalker
|
||||
{
|
||||
public:
|
||||
enum CollectWhat { VariablesAndFunctions, OnlyVariables };
|
||||
enum CollectWhat { VariablesAndFunctions, OnlyVariables, OnlyFunctions };
|
||||
|
||||
explicit NameCollector(
|
||||
Block const& _block,
|
||||
|
@ -37,16 +37,6 @@ void Rematerialiser::run(Dialect const& _dialect, Block& _ast, set<YulString> _v
|
||||
Rematerialiser{_dialect, _ast, std::move(_varsToAlwaysRematerialize), _onlySelectedVariables}(_ast);
|
||||
}
|
||||
|
||||
void Rematerialiser::run(
|
||||
Dialect const& _dialect,
|
||||
FunctionDefinition& _function,
|
||||
set<YulString> _varsToAlwaysRematerialize,
|
||||
bool _onlySelectedVariables
|
||||
)
|
||||
{
|
||||
Rematerialiser{_dialect, _function, std::move(_varsToAlwaysRematerialize), _onlySelectedVariables}(_function);
|
||||
}
|
||||
|
||||
Rematerialiser::Rematerialiser(
|
||||
Dialect const& _dialect,
|
||||
Block& _ast,
|
||||
@ -60,19 +50,6 @@ Rematerialiser::Rematerialiser(
|
||||
{
|
||||
}
|
||||
|
||||
Rematerialiser::Rematerialiser(
|
||||
Dialect const& _dialect,
|
||||
FunctionDefinition& _function,
|
||||
set<YulString> _varsToAlwaysRematerialize,
|
||||
bool _onlySelectedVariables
|
||||
):
|
||||
DataFlowAnalyzer(_dialect),
|
||||
m_referenceCounts(ReferencesCounter::countReferences(_function)),
|
||||
m_varsToAlwaysRematerialize(std::move(_varsToAlwaysRematerialize)),
|
||||
m_onlySelectedVariables(_onlySelectedVariables)
|
||||
{
|
||||
}
|
||||
|
||||
void Rematerialiser::visit(Expression& _e)
|
||||
{
|
||||
if (holds_alternative<Identifier>(_e))
|
||||
|
@ -53,12 +53,6 @@ public:
|
||||
std::set<YulString> _varsToAlwaysRematerialize = {},
|
||||
bool _onlySelectedVariables = false
|
||||
);
|
||||
static void run(
|
||||
Dialect const& _dialect,
|
||||
FunctionDefinition& _function,
|
||||
std::set<YulString> _varsToAlwaysRematerialize = {},
|
||||
bool _onlySelectedVariables = false
|
||||
);
|
||||
|
||||
protected:
|
||||
Rematerialiser(
|
||||
@ -67,12 +61,6 @@ protected:
|
||||
std::set<YulString> _varsToAlwaysRematerialize = {},
|
||||
bool _onlySelectedVariables = false
|
||||
);
|
||||
Rematerialiser(
|
||||
Dialect const& _dialect,
|
||||
FunctionDefinition& _function,
|
||||
std::set<YulString> _varsToAlwaysRematerialize = {},
|
||||
bool _onlySelectedVariables = false
|
||||
);
|
||||
|
||||
using DataFlowAnalyzer::operator();
|
||||
|
||||
|
@ -50,31 +50,40 @@ namespace
|
||||
/**
|
||||
* Class that discovers all variables that can be fully eliminated by rematerialization,
|
||||
* and the corresponding approximate costs.
|
||||
*
|
||||
* Prerequisite: Disambiguator, Function Grouper
|
||||
*/
|
||||
class RematCandidateSelector: public DataFlowAnalyzer
|
||||
{
|
||||
public:
|
||||
explicit RematCandidateSelector(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {}
|
||||
|
||||
/// @returns a map from rematerialisation costs to a vector of variables to rematerialise
|
||||
/// @returns a map from function name to rematerialisation costs to a vector of variables to rematerialise
|
||||
/// and variables that occur in their expression.
|
||||
/// While the map is sorted by cost, the contained vectors are sorted by the order of occurrence.
|
||||
map<size_t, vector<tuple<YulString, set<YulString>>>> candidates()
|
||||
map<YulString, map<size_t, vector<YulString>>> candidates()
|
||||
{
|
||||
map<size_t, vector<tuple<YulString, set<YulString>>>> cand;
|
||||
for (auto const& candidate: m_candidates)
|
||||
map<YulString, map<size_t, vector<YulString>>> cand;
|
||||
for (auto const& [functionName, candidate]: m_candidates)
|
||||
{
|
||||
if (size_t const* cost = util::valueOrNullptr(m_expressionCodeCost, candidate))
|
||||
{
|
||||
size_t numRef = m_numReferences[candidate];
|
||||
set<YulString> const* ref = references(candidate);
|
||||
cand[*cost * numRef].emplace_back(candidate, ref ? move(*ref) : set<YulString>{});
|
||||
cand[functionName][*cost * numRef].emplace_back(candidate);
|
||||
}
|
||||
}
|
||||
return cand;
|
||||
}
|
||||
|
||||
using DataFlowAnalyzer::operator();
|
||||
void operator()(FunctionDefinition& _function) override
|
||||
{
|
||||
yulAssert(m_currentFunctionName.empty());
|
||||
m_currentFunctionName = _function.name;
|
||||
DataFlowAnalyzer::operator()(_function);
|
||||
m_currentFunctionName = {};
|
||||
}
|
||||
|
||||
void operator()(VariableDeclaration& _varDecl) override
|
||||
{
|
||||
DataFlowAnalyzer::operator()(_varDecl);
|
||||
@ -84,7 +93,7 @@ public:
|
||||
if (AssignedValue const* value = variableValue(varName))
|
||||
{
|
||||
yulAssert(!m_expressionCodeCost.count(varName), "");
|
||||
m_candidates.emplace_back(varName);
|
||||
m_candidates.emplace_back(m_currentFunctionName, varName);
|
||||
m_expressionCodeCost[varName] = CodeCost::codeCost(m_dialect, *value->value);
|
||||
}
|
||||
}
|
||||
@ -122,8 +131,10 @@ public:
|
||||
m_expressionCodeCost.erase(_variable);
|
||||
}
|
||||
|
||||
/// All candidate variables in order of occurrence.
|
||||
vector<YulString> m_candidates;
|
||||
YulString m_currentFunctionName = {};
|
||||
|
||||
/// All candidate variables by function name, in order of occurrence.
|
||||
vector<pair<YulString, YulString>> m_candidates;
|
||||
/// Candidate variables and the code cost of their value.
|
||||
map<YulString, size_t> m_expressionCodeCost;
|
||||
/// Number of references to each candidate variable.
|
||||
@ -132,86 +143,95 @@ public:
|
||||
|
||||
/// Selects at most @a _numVariables among @a _candidates.
|
||||
set<YulString> chooseVarsToEliminate(
|
||||
map<size_t, vector<tuple<YulString, set<YulString>>>> const& _candidates,
|
||||
map<size_t, vector<YulString>> const& _candidates,
|
||||
size_t _numVariables
|
||||
)
|
||||
{
|
||||
set<YulString> varsToEliminate;
|
||||
for (auto&& [cost, candidates]: _candidates)
|
||||
for (auto&& [candidate, references]: candidates)
|
||||
for (auto&& candidate: candidates)
|
||||
{
|
||||
if (varsToEliminate.size() >= _numVariables)
|
||||
return varsToEliminate;
|
||||
// If a variable we would like to eliminate references another one
|
||||
// we already selected for elimination, then stop selecting
|
||||
// candidates. If we would add that variable, then the cost calculation
|
||||
// for the previous variable would be off. Furthermore, we
|
||||
// do not skip the variable because it would be better to properly re-compute
|
||||
// the costs of all other variables instead.
|
||||
for (YulString const& referencedVar: references)
|
||||
if (varsToEliminate.count(referencedVar))
|
||||
return varsToEliminate;
|
||||
varsToEliminate.insert(candidate);
|
||||
}
|
||||
return varsToEliminate;
|
||||
}
|
||||
|
||||
template <typename ASTNode>
|
||||
void eliminateVariables(
|
||||
Dialect const& _dialect,
|
||||
ASTNode& _node,
|
||||
size_t _numVariables,
|
||||
Block& _ast,
|
||||
map<YulString, int> const& _numVariables,
|
||||
bool _allowMSizeOptimization
|
||||
)
|
||||
{
|
||||
RematCandidateSelector selector{_dialect};
|
||||
selector(_node);
|
||||
Rematerialiser::run(_dialect, _node, chooseVarsToEliminate(selector.candidates(), _numVariables));
|
||||
UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization);
|
||||
selector(_ast);
|
||||
map<YulString, map<size_t, vector<YulString>>> candidates = selector.candidates();
|
||||
|
||||
set<YulString> varsToEliminate;
|
||||
for (auto const& [functionName, numVariables]: _numVariables)
|
||||
{
|
||||
yulAssert(numVariables > 0);
|
||||
varsToEliminate += chooseVarsToEliminate(candidates[functionName], static_cast<size_t>(numVariables));
|
||||
}
|
||||
|
||||
Rematerialiser::run(_dialect, _ast, move(varsToEliminate));
|
||||
// Do not remove functions.
|
||||
set<YulString> allFunctions = NameCollector{_ast, NameCollector::OnlyFunctions}.names();
|
||||
UnusedPruner::runUntilStabilised(_dialect, _ast, _allowMSizeOptimization, nullptr, allFunctions);
|
||||
}
|
||||
|
||||
void eliminateVariables(
|
||||
void eliminateVariablesOptimizedCodegen(
|
||||
Dialect const& _dialect,
|
||||
Block& _block,
|
||||
vector<StackLayoutGenerator::StackTooDeep> const& _unreachables,
|
||||
Block& _ast,
|
||||
map<YulString, vector<StackLayoutGenerator::StackTooDeep>> const& _unreachables,
|
||||
bool _allowMSizeOptimization
|
||||
)
|
||||
{
|
||||
if (std::all_of(_unreachables.begin(), _unreachables.end(), [](auto const& _item) { return _item.second.empty(); }))
|
||||
return;
|
||||
|
||||
RematCandidateSelector selector{_dialect};
|
||||
selector(_block);
|
||||
std::map<YulString, size_t> candidates;
|
||||
for (auto [cost, candidatesWithCost]: selector.candidates())
|
||||
for (auto candidate: candidatesWithCost)
|
||||
candidates[get<0>(candidate)] = cost;
|
||||
selector(_ast);
|
||||
|
||||
map<YulString, size_t> candidates;
|
||||
for (auto const& [functionName, candidatesInFunction]: selector.candidates())
|
||||
for (auto [cost, candidatesWithCost]: candidatesInFunction)
|
||||
for (auto candidate: candidatesWithCost)
|
||||
candidates[candidate] = cost;
|
||||
|
||||
set<YulString> varsToEliminate;
|
||||
|
||||
// TODO: this currently ignores the fact that variables may reference other variables we want to eliminate.
|
||||
for (auto const& unreachable: _unreachables)
|
||||
{
|
||||
map<size_t, vector<YulString>> suitableCandidates;
|
||||
size_t neededSlots = unreachable.deficit;
|
||||
for (auto varName: unreachable.variableChoices)
|
||||
for (auto const& [functionName, unreachables]: _unreachables)
|
||||
for (auto const& unreachable: unreachables)
|
||||
{
|
||||
if (varsToEliminate.count(varName))
|
||||
--neededSlots;
|
||||
else if (size_t* cost = util::valueOrNullptr(candidates, varName))
|
||||
if (!util::contains(suitableCandidates[*cost], varName))
|
||||
suitableCandidates[*cost].emplace_back(varName);
|
||||
}
|
||||
for (auto candidatesByCost: suitableCandidates)
|
||||
{
|
||||
for (auto candidate: candidatesByCost.second)
|
||||
if (neededSlots--)
|
||||
varsToEliminate.emplace(candidate);
|
||||
else
|
||||
map<size_t, vector<YulString>> suitableCandidates;
|
||||
size_t neededSlots = unreachable.deficit;
|
||||
for (auto varName: unreachable.variableChoices)
|
||||
{
|
||||
if (varsToEliminate.count(varName))
|
||||
--neededSlots;
|
||||
else if (size_t* cost = util::valueOrNullptr(candidates, varName))
|
||||
if (!util::contains(suitableCandidates[*cost], varName))
|
||||
suitableCandidates[*cost].emplace_back(varName);
|
||||
}
|
||||
for (auto candidatesByCost: suitableCandidates)
|
||||
{
|
||||
for (auto candidate: candidatesByCost.second)
|
||||
if (neededSlots--)
|
||||
varsToEliminate.emplace(candidate);
|
||||
else
|
||||
break;
|
||||
if (!neededSlots)
|
||||
break;
|
||||
if (!neededSlots)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Rematerialiser::run(_dialect, _block, std::move(varsToEliminate), true);
|
||||
UnusedPruner::runUntilStabilised(_dialect, _block, _allowMSizeOptimization);
|
||||
Rematerialiser::run(_dialect, _ast, std::move(varsToEliminate), true);
|
||||
// Do not remove functions.
|
||||
set<YulString> allFunctions = NameCollector{_ast, NameCollector::OnlyFunctions}.names();
|
||||
UnusedPruner::runUntilStabilised(_dialect, _ast, _allowMSizeOptimization, nullptr, allFunctions);
|
||||
}
|
||||
|
||||
}
|
||||
@ -239,21 +259,12 @@ bool StackCompressor::run(
|
||||
{
|
||||
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object);
|
||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, _dialect, *_object.code);
|
||||
Block& mainBlock = std::get<Block>(_object.code->statements.at(0));
|
||||
if (
|
||||
auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, YulString{});
|
||||
!stackTooDeepErrors.empty()
|
||||
)
|
||||
eliminateVariables(_dialect, mainBlock, stackTooDeepErrors, allowMSizeOptimzation);
|
||||
for (size_t i = 1; i < _object.code->statements.size(); ++i)
|
||||
{
|
||||
auto& fun = std::get<FunctionDefinition>(_object.code->statements[i]);
|
||||
if (
|
||||
auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, fun.name);
|
||||
!stackTooDeepErrors.empty()
|
||||
)
|
||||
eliminateVariables(_dialect, fun.body, stackTooDeepErrors, allowMSizeOptimzation);
|
||||
}
|
||||
eliminateVariablesOptimizedCodegen(
|
||||
_dialect,
|
||||
*_object.code,
|
||||
StackLayoutGenerator::reportStackTooDeep(*cfg),
|
||||
allowMSizeOptimzation
|
||||
);
|
||||
}
|
||||
else
|
||||
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
||||
@ -261,32 +272,12 @@ bool StackCompressor::run(
|
||||
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;
|
||||
if (stackSurplus.empty())
|
||||
return true;
|
||||
|
||||
if (stackSurplus.count(YulString{}))
|
||||
{
|
||||
yulAssert(stackSurplus.at({}) > 0, "Invalid surplus value.");
|
||||
eliminateVariables(
|
||||
_dialect,
|
||||
std::get<Block>(_object.code->statements.at(0)),
|
||||
static_cast<size_t>(stackSurplus.at({})),
|
||||
allowMSizeOptimzation
|
||||
);
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < _object.code->statements.size(); ++i)
|
||||
{
|
||||
auto& fun = std::get<FunctionDefinition>(_object.code->statements[i]);
|
||||
if (!stackSurplus.count(fun.name))
|
||||
continue;
|
||||
|
||||
yulAssert(stackSurplus.at(fun.name) > 0, "Invalid surplus value.");
|
||||
eliminateVariables(
|
||||
_dialect,
|
||||
fun,
|
||||
static_cast<size_t>(stackSurplus.at(fun.name)),
|
||||
allowMSizeOptimzation
|
||||
);
|
||||
}
|
||||
eliminateVariables(
|
||||
_dialect,
|
||||
*_object.code,
|
||||
stackSurplus,
|
||||
allowMSizeOptimzation
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -93,26 +93,31 @@ void StructuralSimplifier::operator()(Block& _block)
|
||||
|
||||
void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements)
|
||||
{
|
||||
util::GenericVisitor visitor{
|
||||
util::VisitorFallback<OptionalStatements>{},
|
||||
[&](If& _ifStmt) -> OptionalStatements {
|
||||
if (expressionAlwaysTrue(*_ifStmt.condition))
|
||||
return {std::move(_ifStmt.body.statements)};
|
||||
else if (expressionAlwaysFalse(*_ifStmt.condition))
|
||||
return {vector<Statement>{}};
|
||||
return {};
|
||||
},
|
||||
[&](Switch& _switchStmt) -> OptionalStatements {
|
||||
if (std::optional<u256> const constExprVal = hasLiteralValue(*_switchStmt.expression))
|
||||
return replaceConstArgSwitch(_switchStmt, constExprVal.value());
|
||||
return {};
|
||||
},
|
||||
[&](ForLoop& _forLoop) -> OptionalStatements {
|
||||
if (expressionAlwaysFalse(*_forLoop.condition))
|
||||
return {std::move(_forLoop.pre.statements)};
|
||||
return {};
|
||||
}
|
||||
// Explicit local variables ifLambda, switchLambda, forLoopLambda are created to avoid MSVC C++17 Debug test crash
|
||||
// (Run-Time Check Failure #2 - Stack around the variable '....' was corrupted).
|
||||
// As soon as the issue is fixed, this workaround can be removed.
|
||||
auto ifLambda = [&](If& _ifStmt) -> OptionalStatements
|
||||
{
|
||||
if (expressionAlwaysTrue(*_ifStmt.condition))
|
||||
return {std::move(_ifStmt.body.statements)};
|
||||
else if (expressionAlwaysFalse(*_ifStmt.condition))
|
||||
return {vector<Statement>{}};
|
||||
return {};
|
||||
};
|
||||
auto switchLambda = [&](Switch& _switchStmt) -> OptionalStatements
|
||||
{
|
||||
if (std::optional<u256> const constExprVal = hasLiteralValue(*_switchStmt.expression))
|
||||
return replaceConstArgSwitch(_switchStmt, constExprVal.value());
|
||||
return {};
|
||||
};
|
||||
auto forLoopLambda = [&](ForLoop& _forLoop) -> OptionalStatements
|
||||
{
|
||||
if (expressionAlwaysFalse(*_forLoop.condition))
|
||||
return {std::move(_forLoop.pre.statements)};
|
||||
return {};
|
||||
};
|
||||
|
||||
util::GenericVisitor visitor{util::VisitorFallback<OptionalStatements>{}, ifLambda, switchLambda, forLoopLambda};
|
||||
|
||||
util::iterateReplacing(
|
||||
_statements,
|
||||
|
@ -246,3 +246,12 @@ function first_word
|
||||
|
||||
echo "$words" | cut -d " " -f 1
|
||||
}
|
||||
|
||||
# Function reads from stdin. Therefore it has to be used with pipes.
|
||||
function split_on_empty_lines_into_numbered_files
|
||||
{
|
||||
path_prefix="${1}"
|
||||
path_suffix="${2}"
|
||||
|
||||
awk -v RS= "{print > (\"${path_prefix}_\"NR \"${path_suffix}\")}"
|
||||
}
|
||||
|
453
scripts/externalTests/benchmark_diff.py
Executable file
453
scripts/externalTests/benchmark_diff.py
Executable file
@ -0,0 +1,453 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
from typing import Any, Mapping, Optional, Set, Sequence, Union
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
class DiffMode(Enum):
|
||||
IN_PLACE = 'inplace'
|
||||
TABLE = 'table'
|
||||
|
||||
|
||||
class DifferenceStyle(Enum):
|
||||
ABSOLUTE = 'absolute'
|
||||
RELATIVE = 'relative'
|
||||
HUMANIZED = 'humanized'
|
||||
|
||||
|
||||
class OutputFormat(Enum):
|
||||
JSON = 'json'
|
||||
CONSOLE = 'console'
|
||||
MARKDOWN = 'markdown'
|
||||
|
||||
|
||||
DEFAULT_RELATIVE_PRECISION = 4
|
||||
|
||||
DEFAULT_DIFFERENCE_STYLE = {
|
||||
DiffMode.IN_PLACE: DifferenceStyle.ABSOLUTE,
|
||||
DiffMode.TABLE: DifferenceStyle.HUMANIZED,
|
||||
}
|
||||
assert all(t in DiffMode for t in DEFAULT_DIFFERENCE_STYLE)
|
||||
assert all(d in DifferenceStyle for d in DEFAULT_DIFFERENCE_STYLE.values())
|
||||
|
||||
DEFAULT_OUTPUT_FORMAT = {
|
||||
DiffMode.IN_PLACE: OutputFormat.JSON,
|
||||
DiffMode.TABLE: OutputFormat.CONSOLE,
|
||||
}
|
||||
assert all(m in DiffMode for m in DEFAULT_OUTPUT_FORMAT)
|
||||
assert all(o in OutputFormat for o in DEFAULT_OUTPUT_FORMAT.values())
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CommandLineError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class BenchmarkDiffer:
|
||||
difference_style: DifferenceStyle
|
||||
relative_precision: Optional[int]
|
||||
output_format: OutputFormat
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
difference_style: DifferenceStyle,
|
||||
relative_precision: Optional[int],
|
||||
output_format: OutputFormat,
|
||||
):
|
||||
self.difference_style = difference_style
|
||||
self.relative_precision = relative_precision
|
||||
self.output_format = output_format
|
||||
|
||||
def run(self, before: Any, after: Any) -> Optional[Union[dict, str, int, float]]:
|
||||
if not isinstance(before, dict) or not isinstance(after, dict):
|
||||
return self._diff_scalars(before, after)
|
||||
|
||||
if before.get('version') != after.get('version'):
|
||||
return self._humanize_diff('!V')
|
||||
|
||||
diff = {}
|
||||
for key in (set(before) | set(after)) - {'version'}:
|
||||
value_diff = self.run(before.get(key), after.get(key))
|
||||
if value_diff not in [None, {}]:
|
||||
diff[key] = value_diff
|
||||
|
||||
return diff
|
||||
|
||||
def _diff_scalars(self, before: Any, after: Any) -> Optional[Union[str, int, float, dict]]:
|
||||
assert not isinstance(before, dict) or not isinstance(after, dict)
|
||||
|
||||
if before is None and after is None:
|
||||
return {}
|
||||
if before is None:
|
||||
return self._humanize_diff('!B')
|
||||
if after is None:
|
||||
return self._humanize_diff('!A')
|
||||
if not isinstance(before, (int, float)) or not isinstance(after, (int, float)):
|
||||
return self._humanize_diff('!T')
|
||||
|
||||
number_diff = self._diff_numbers(before, after)
|
||||
if self.difference_style != DifferenceStyle.HUMANIZED:
|
||||
return number_diff
|
||||
|
||||
return self._humanize_diff(number_diff)
|
||||
|
||||
def _diff_numbers(self, value_before: Union[int, float], value_after: Union[int, float]) -> Union[str, int, float]:
|
||||
diff: Union[str, int, float]
|
||||
|
||||
if self.difference_style == DifferenceStyle.ABSOLUTE:
|
||||
diff = value_after - value_before
|
||||
if isinstance(diff, float) and diff.is_integer():
|
||||
diff = int(diff)
|
||||
|
||||
return diff
|
||||
|
||||
if value_before == 0:
|
||||
if value_after > 0:
|
||||
return '+INF'
|
||||
elif value_after < 0:
|
||||
return '-INF'
|
||||
else:
|
||||
return 0
|
||||
|
||||
diff = (value_after - value_before) / abs(value_before)
|
||||
if self.relative_precision is not None:
|
||||
rounded_diff = round(diff, self.relative_precision)
|
||||
if rounded_diff == 0 and diff < 0:
|
||||
diff = '-0'
|
||||
elif rounded_diff == 0 and diff > 0:
|
||||
diff = '+0'
|
||||
else:
|
||||
diff = rounded_diff
|
||||
|
||||
if isinstance(diff, float) and diff.is_integer():
|
||||
diff = int(diff)
|
||||
|
||||
return diff
|
||||
|
||||
def _humanize_diff(self, diff: Union[str, int, float]) -> str:
|
||||
def wrap(value: str, symbol: str):
|
||||
return f"{symbol}{value}{symbol}"
|
||||
|
||||
markdown = (self.output_format == OutputFormat.MARKDOWN)
|
||||
|
||||
if isinstance(diff, str) and diff.startswith('!'):
|
||||
return wrap(diff, '`' if markdown else '')
|
||||
|
||||
value: Union[str, int, float]
|
||||
if isinstance(diff, (int, float)):
|
||||
value = diff * 100
|
||||
if isinstance(value, float) and self.relative_precision is not None:
|
||||
# The multiplication can result in new significant digits appearing. We need to reround.
|
||||
# NOTE: round() works fine with negative precision.
|
||||
value = round(value, self.relative_precision - 2)
|
||||
if isinstance(value, float) and value.is_integer():
|
||||
value = int(value)
|
||||
suffix = ''
|
||||
prefix = ''
|
||||
if diff < 0:
|
||||
prefix = ''
|
||||
if markdown:
|
||||
suffix += ' ✅'
|
||||
elif diff > 0:
|
||||
prefix = '+'
|
||||
if markdown:
|
||||
suffix += ' ❌'
|
||||
important = (diff != 0)
|
||||
else:
|
||||
value = diff
|
||||
important = False
|
||||
prefix = ''
|
||||
suffix = ''
|
||||
|
||||
return wrap(
|
||||
wrap(
|
||||
f"{prefix}{value}%{suffix}",
|
||||
'`' if markdown else ''
|
||||
),
|
||||
'**' if important and markdown else ''
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DiffTable:
|
||||
columns: Mapping[str, Sequence[Union[int, float, str]]]
|
||||
|
||||
|
||||
class DiffTableSet:
|
||||
table_headers: Sequence[str]
|
||||
row_headers: Sequence[str]
|
||||
column_headers: Sequence[str]
|
||||
|
||||
# Cells is a nested dict rather than a 3D array so that conversion to JSON is straightforward
|
||||
cells: Mapping[str, Mapping[str, Mapping[str, Union[int, float, str]]]] # preset -> project -> attribute
|
||||
|
||||
def __init__(self, diff: dict):
|
||||
self.table_headers = sorted(self._find_all_preset_names(diff))
|
||||
self.column_headers = sorted(self._find_all_attribute_names(diff))
|
||||
self.row_headers = sorted(project for project in diff)
|
||||
|
||||
# All dimensions must have unique values
|
||||
assert len(self.table_headers) == len(set(self.table_headers))
|
||||
assert len(self.column_headers) == len(set(self.column_headers))
|
||||
assert len(self.row_headers) == len(set(self.row_headers))
|
||||
|
||||
self.cells = {
|
||||
preset: {
|
||||
project: {
|
||||
attribute: self._cell_content(diff, project, preset, attribute)
|
||||
for attribute in self.column_headers
|
||||
}
|
||||
for project in self.row_headers
|
||||
}
|
||||
for preset in self.table_headers
|
||||
}
|
||||
|
||||
def calculate_row_column_width(self) -> int:
|
||||
return max(len(h) for h in self.row_headers)
|
||||
|
||||
def calculate_column_widths(self, table_header: str) -> Sequence[int]:
|
||||
assert table_header in self.table_headers
|
||||
|
||||
return [
|
||||
max(
|
||||
len(column_header),
|
||||
max(
|
||||
len(str(self.cells[table_header][row_header][column_header]))
|
||||
for row_header in self.row_headers
|
||||
)
|
||||
)
|
||||
for column_header in self.column_headers
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _find_all_preset_names(cls, diff: dict) -> Set[str]:
|
||||
return {
|
||||
preset
|
||||
for project, project_diff in diff.items()
|
||||
if isinstance(project_diff, dict)
|
||||
for preset in project_diff
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _find_all_attribute_names(cls, diff: dict) -> Set[str]:
|
||||
return {
|
||||
attribute
|
||||
for project, project_diff in diff.items()
|
||||
if isinstance(project_diff, dict)
|
||||
for preset, preset_diff in project_diff.items()
|
||||
if isinstance(preset_diff, dict)
|
||||
for attribute in preset_diff
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _cell_content(cls, diff: dict, project: str, preset: str, attribute: str) -> str:
|
||||
assert project in diff
|
||||
|
||||
if isinstance(diff[project], str):
|
||||
return diff[project]
|
||||
if preset not in diff[project]:
|
||||
return ''
|
||||
if isinstance(diff[project][preset], str):
|
||||
return diff[project][preset]
|
||||
if attribute not in diff[project][preset]:
|
||||
return ''
|
||||
|
||||
return diff[project][preset][attribute]
|
||||
|
||||
|
||||
class DiffTableFormatter:
|
||||
LEGEND = dedent("""
|
||||
`!V` = version mismatch
|
||||
`!B` = no value in the "before" version
|
||||
`!A` = no value in the "after" version
|
||||
`!T` = one or both values were not numeric and could not be compared
|
||||
`-0` = very small negative value rounded to zero
|
||||
`+0` = very small positive value rounded to zero
|
||||
""")
|
||||
|
||||
@classmethod
|
||||
def run(cls, diff_table_set: DiffTableSet, output_format: OutputFormat):
|
||||
if output_format == OutputFormat.JSON:
|
||||
return json.dumps(diff_table_set.cells, indent=4, sort_keys=True)
|
||||
else:
|
||||
assert output_format in {OutputFormat.CONSOLE, OutputFormat.MARKDOWN}
|
||||
|
||||
output = ''
|
||||
for table_header in diff_table_set.table_headers:
|
||||
column_widths = ([
|
||||
diff_table_set.calculate_row_column_width(),
|
||||
*diff_table_set.calculate_column_widths(table_header)
|
||||
])
|
||||
|
||||
if output_format == OutputFormat.MARKDOWN:
|
||||
output += f'\n### `{table_header}`\n'
|
||||
else:
|
||||
output += f'\n{table_header.upper()}\n'
|
||||
|
||||
if output_format == OutputFormat.CONSOLE:
|
||||
output += cls._format_separator_row(column_widths, output_format) + '\n'
|
||||
output += cls._format_data_row(['project', *diff_table_set.column_headers], column_widths) + '\n'
|
||||
output += cls._format_separator_row(column_widths, output_format) + '\n'
|
||||
|
||||
for row_header in diff_table_set.row_headers:
|
||||
row = [
|
||||
diff_table_set.cells[table_header][row_header][column_header]
|
||||
for column_header in diff_table_set.column_headers
|
||||
]
|
||||
output += cls._format_data_row([row_header, *row], column_widths) + '\n'
|
||||
|
||||
if output_format == OutputFormat.CONSOLE:
|
||||
output += cls._format_separator_row(column_widths, output_format) + '\n'
|
||||
|
||||
if output_format == OutputFormat.MARKDOWN:
|
||||
output += f'\n{cls.LEGEND}\n'
|
||||
return output
|
||||
|
||||
@classmethod
|
||||
def _format_separator_row(cls, widths: Sequence[int], output_format: OutputFormat):
|
||||
assert output_format in {OutputFormat.CONSOLE, OutputFormat.MARKDOWN}
|
||||
|
||||
if output_format == OutputFormat.MARKDOWN:
|
||||
return '|:' + ':|-'.join('-' * width for width in widths) + ':|'
|
||||
else:
|
||||
return '|-' + '-|-'.join('-' * width for width in widths) + '-|'
|
||||
|
||||
@classmethod
|
||||
def _format_data_row(cls, cells: Sequence[Union[int, float, str]], widths: Sequence[int]):
|
||||
assert len(cells) == len(widths)
|
||||
|
||||
return '| ' + ' | '.join(str(cell).rjust(width) for cell, width in zip(cells, widths)) + ' |'
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CommandLineOptions:
|
||||
diff_mode: DiffMode
|
||||
report_before: Path
|
||||
report_after: Path
|
||||
difference_style: DifferenceStyle
|
||||
relative_precision: int
|
||||
output_format: OutputFormat
|
||||
|
||||
|
||||
def process_commandline() -> CommandLineOptions:
|
||||
script_description = (
|
||||
"Compares summarized benchmark reports and outputs JSON with the same structure but listing only differences. "
|
||||
"Can also print the output as markdown table and format the values to make differences stand out more."
|
||||
)
|
||||
|
||||
parser = ArgumentParser(description=script_description)
|
||||
parser.add_argument(
|
||||
dest='diff_mode',
|
||||
choices=[m.value for m in DiffMode],
|
||||
help=(
|
||||
"Diff mode: "
|
||||
f"'{DiffMode.IN_PLACE.value}' preserves input JSON structure and replace values with differences; "
|
||||
f"'{DiffMode.TABLE.value}' creates a table assuming 3-level project/preset/attribute structure."
|
||||
)
|
||||
)
|
||||
parser.add_argument(dest='report_before', help="Path to a JSON file containing original benchmark results.")
|
||||
parser.add_argument(dest='report_after', help="Path to a JSON file containing new benchmark results.")
|
||||
parser.add_argument(
|
||||
'--style',
|
||||
dest='difference_style',
|
||||
choices=[s.value for s in DifferenceStyle],
|
||||
help=(
|
||||
"How to present numeric differences: "
|
||||
f"'{DifferenceStyle.ABSOLUTE.value}' subtracts new from original; "
|
||||
f"'{DifferenceStyle.RELATIVE.value}' also divides by the original; "
|
||||
f"'{DifferenceStyle.HUMANIZED.value}' is like relative but value is a percentage and "
|
||||
"positive/negative changes are emphasized. "
|
||||
f"(default: '{DEFAULT_DIFFERENCE_STYLE[DiffMode.IN_PLACE]}' in '{DiffMode.IN_PLACE.value}' mode, "
|
||||
f"'{DEFAULT_DIFFERENCE_STYLE[DiffMode.TABLE]}' in '{DiffMode.TABLE.value}' mode)"
|
||||
)
|
||||
)
|
||||
# NOTE: Negative values are valid for precision. round() handles them in a sensible way.
|
||||
parser.add_argument(
|
||||
'--precision',
|
||||
dest='relative_precision',
|
||||
type=int,
|
||||
default=DEFAULT_RELATIVE_PRECISION,
|
||||
help=(
|
||||
"Number of significant digits for relative differences. "
|
||||
f"Note that with --style={DifferenceStyle.HUMANIZED.value} the rounding is applied "
|
||||
"**before** converting the value to a percentage so you need to add 2. "
|
||||
f"Has no effect when used together with --style={DifferenceStyle.ABSOLUTE.value}. "
|
||||
f"(default: {DEFAULT_RELATIVE_PRECISION})"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output-format',
|
||||
dest='output_format',
|
||||
choices=[o.value for o in OutputFormat],
|
||||
help=(
|
||||
"The format to use for the diff: "
|
||||
f"'{OutputFormat.JSON.value}' is raw JSON; "
|
||||
f"'{OutputFormat.CONSOLE.value}' is a table with human-readable values that will look good in the console output. "
|
||||
f"'{OutputFormat.MARKDOWN.value}' is similar '{OutputFormat.CONSOLE.value}' but adjusted to "
|
||||
"render as proper markdown and with extra elements (legend, emoji to make non-zero values stand out more, etc)."
|
||||
f"(default: '{DEFAULT_OUTPUT_FORMAT[DiffMode.IN_PLACE]}' in '{DiffMode.IN_PLACE.value}' mode, "
|
||||
f"'{DEFAULT_OUTPUT_FORMAT[DiffMode.TABLE]}' in '{DiffMode.TABLE.value}' mode)"
|
||||
)
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
if options.difference_style is not None:
|
||||
difference_style = DifferenceStyle(options.difference_style)
|
||||
else:
|
||||
difference_style = DEFAULT_DIFFERENCE_STYLE[DiffMode(options.diff_mode)]
|
||||
|
||||
if options.output_format is not None:
|
||||
output_format = OutputFormat(options.output_format)
|
||||
else:
|
||||
output_format = DEFAULT_OUTPUT_FORMAT[DiffMode(options.diff_mode)]
|
||||
|
||||
processed_options = CommandLineOptions(
|
||||
diff_mode=DiffMode(options.diff_mode),
|
||||
report_before=Path(options.report_before),
|
||||
report_after=Path(options.report_after),
|
||||
difference_style=difference_style,
|
||||
relative_precision=options.relative_precision,
|
||||
output_format=output_format,
|
||||
)
|
||||
|
||||
if processed_options.diff_mode == DiffMode.IN_PLACE and processed_options.output_format != OutputFormat.JSON:
|
||||
raise CommandLineError(
|
||||
f"Only the '{OutputFormat.JSON.value}' output format is supported in the '{DiffMode.IN_PLACE.value}' mode."
|
||||
)
|
||||
|
||||
return processed_options
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
options = process_commandline()
|
||||
|
||||
differ = BenchmarkDiffer(options.difference_style, options.relative_precision, options.output_format)
|
||||
diff = differ.run(
|
||||
json.loads(options.report_before.read_text('utf-8')),
|
||||
json.loads(options.report_after.read_text('utf-8')),
|
||||
)
|
||||
|
||||
if options.diff_mode == DiffMode.IN_PLACE:
|
||||
print(json.dumps(diff, indent=4, sort_keys=True))
|
||||
else:
|
||||
assert options.diff_mode == DiffMode.TABLE
|
||||
print(DiffTableFormatter.run(DiffTableSet(diff), options.output_format))
|
||||
|
||||
return 0
|
||||
except CommandLineError as exception:
|
||||
print(f"ERROR: {exception}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -41,7 +41,7 @@
|
||||
#include <libsolidity/lsp/LanguageServer.h>
|
||||
#include <libsolidity/lsp/Transport.h>
|
||||
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <libyul/YulStack.h>
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <libevmasm/Disassemble.h>
|
||||
@ -317,7 +317,7 @@ void CommandLineInterface::handleABI(string const& _contract)
|
||||
if (!m_options.compiler.outputs.abi)
|
||||
return;
|
||||
|
||||
string data = jsonCompactPrint(removeNullMembers(m_compiler->contractABI(_contract)));
|
||||
string data = jsonPrint(removeNullMembers(m_compiler->contractABI(_contract)), m_options.formatting.json);
|
||||
if (!m_options.output.dir.empty())
|
||||
createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data);
|
||||
else
|
||||
@ -331,7 +331,7 @@ void CommandLineInterface::handleStorageLayout(string const& _contract)
|
||||
if (!m_options.compiler.outputs.storageLayout)
|
||||
return;
|
||||
|
||||
string data = jsonCompactPrint(removeNullMembers(m_compiler->storageLayout(_contract)));
|
||||
string data = jsonPrint(removeNullMembers(m_compiler->storageLayout(_contract)), m_options.formatting.json);
|
||||
if (!m_options.output.dir.empty())
|
||||
createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data);
|
||||
else
|
||||
@ -361,12 +361,13 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
std::string output = jsonPrettyPrint(
|
||||
std::string output = jsonPrint(
|
||||
removeNullMembers(
|
||||
_natspecDev ?
|
||||
m_compiler->natspecDev(_contract) :
|
||||
m_compiler->natspecUser(_contract)
|
||||
)
|
||||
),
|
||||
m_options.formatting.json
|
||||
);
|
||||
|
||||
if (!m_options.output.dir.empty())
|
||||
@ -892,7 +893,7 @@ void CommandLineInterface::handleAst()
|
||||
{
|
||||
stringstream data;
|
||||
string postfix = "";
|
||||
ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first));
|
||||
ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first), m_options.formatting.json);
|
||||
postfix += "_json";
|
||||
boost::filesystem::path path(sourceCode.first);
|
||||
createFile(path.filename().string() + postfix + ".ast", data.str());
|
||||
@ -904,7 +905,7 @@ void CommandLineInterface::handleAst()
|
||||
for (auto const& sourceCode: m_fileReader.sourceUnits())
|
||||
{
|
||||
sout() << endl << "======= " << sourceCode.first << " =======" << endl;
|
||||
ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first));
|
||||
ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first), m_options.formatting.json);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1013,18 +1014,18 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _
|
||||
return out;
|
||||
}
|
||||
|
||||
void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine)
|
||||
void CommandLineInterface::assemble(yul::YulStack::Language _language, yul::YulStack::Machine _targetMachine)
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Assembler, "");
|
||||
|
||||
bool successful = true;
|
||||
map<string, yul::AssemblyStack> assemblyStacks;
|
||||
map<string, yul::YulStack> yulStacks;
|
||||
for (auto const& src: m_fileReader.sourceUnits())
|
||||
{
|
||||
// --no-optimize-yul option is not accepted in assembly mode.
|
||||
solAssert(!m_options.optimizer.noOptimizeYul, "");
|
||||
|
||||
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(
|
||||
auto& stack = yulStacks[src.first] = yul::YulStack(
|
||||
m_options.output.evmVersion,
|
||||
_language,
|
||||
m_options.optimiserSettings(),
|
||||
@ -1039,7 +1040,7 @@ void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
|
||||
stack.optimize();
|
||||
}
|
||||
|
||||
for (auto const& sourceAndStack: assemblyStacks)
|
||||
for (auto const& sourceAndStack: yulStacks)
|
||||
{
|
||||
auto const& stack = sourceAndStack.second;
|
||||
SourceReferenceFormatter formatter(serr(false), stack, coloredOutput(m_options), m_options.formatting.withErrorIds);
|
||||
@ -1062,11 +1063,11 @@ void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
|
||||
for (auto const& src: m_fileReader.sourceUnits())
|
||||
{
|
||||
string machine =
|
||||
_targetMachine == yul::AssemblyStack::Machine::EVM ? "EVM" :
|
||||
_targetMachine == yul::YulStack::Machine::EVM ? "EVM" :
|
||||
"Ewasm";
|
||||
sout() << endl << "======= " << src.first << " (" << machine << ") =======" << endl;
|
||||
|
||||
yul::AssemblyStack& stack = assemblyStacks[src.first];
|
||||
yul::YulStack& stack = yulStacks[src.first];
|
||||
|
||||
if (m_options.compiler.outputs.irOptimized)
|
||||
{
|
||||
@ -1076,9 +1077,9 @@ void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
|
||||
sout() << stack.print() << endl;
|
||||
}
|
||||
|
||||
if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm)
|
||||
if (_language != yul::YulStack::Language::Ewasm && _targetMachine == yul::YulStack::Machine::Ewasm)
|
||||
{
|
||||
stack.translate(yul::AssemblyStack::Language::Ewasm);
|
||||
stack.translate(yul::YulStack::Language::Ewasm);
|
||||
stack.optimize();
|
||||
|
||||
if (m_options.compiler.outputs.ewasmIR)
|
||||
@ -1102,10 +1103,10 @@ void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
|
||||
serr() << "No binary representation found." << endl;
|
||||
}
|
||||
|
||||
solAssert(_targetMachine == yul::AssemblyStack::Machine::Ewasm || _targetMachine == yul::AssemblyStack::Machine::EVM, "");
|
||||
solAssert(_targetMachine == yul::YulStack::Machine::Ewasm || _targetMachine == yul::YulStack::Machine::EVM, "");
|
||||
if (
|
||||
(_targetMachine == yul::AssemblyStack::Machine::EVM && m_options.compiler.outputs.asm_) ||
|
||||
(_targetMachine == yul::AssemblyStack::Machine::Ewasm && m_options.compiler.outputs.ewasm)
|
||||
(_targetMachine == yul::YulStack::Machine::EVM && m_options.compiler.outputs.asm_) ||
|
||||
(_targetMachine == yul::YulStack::Machine::Ewasm && m_options.compiler.outputs.ewasm)
|
||||
)
|
||||
{
|
||||
sout() << endl << "Text representation:" << endl;
|
||||
@ -1149,7 +1150,7 @@ void CommandLineInterface::outputCompilationResults()
|
||||
{
|
||||
string ret;
|
||||
if (m_options.compiler.outputs.asmJson)
|
||||
ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract)));
|
||||
ret = util::jsonPrint(removeNullMembers(m_compiler->assemblyJSON(contract)), m_options.formatting.json);
|
||||
else
|
||||
ret = m_compiler->assemblyString(contract, m_fileReader.sourceUnits());
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
#include <libsolidity/interface/DebugSettings.h>
|
||||
#include <libsolidity/interface/FileReader.h>
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <libyul/YulStack.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@ -90,7 +90,7 @@ private:
|
||||
/// @returns the full object with library placeholder hints in hex.
|
||||
static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj);
|
||||
|
||||
void assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine);
|
||||
void assemble(yul::YulStack::Language _language, yul::YulStack::Machine _targetMachine);
|
||||
|
||||
void outputCompilationResults();
|
||||
|
||||
|
@ -1109,8 +1109,8 @@ void CommandLineParser::processArgs()
|
||||
}
|
||||
|
||||
// switch to assembly mode
|
||||
using Input = yul::AssemblyStack::Language;
|
||||
using Machine = yul::AssemblyStack::Machine;
|
||||
using Input = yul::YulStack::Language;
|
||||
using Machine = yul::YulStack::Machine;
|
||||
m_options.assembly.inputLanguage = m_args.count(g_strYul) ? Input::Yul : (m_args.count(g_strStrictAssembly) ? Input::StrictAssembly : Input::Assembly);
|
||||
|
||||
if (m_args.count(g_strMachine))
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <libsolidity/interface/FileReader.h>
|
||||
#include <libsolidity/interface/ImportRemapper.h>
|
||||
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <libyul/YulStack.h>
|
||||
|
||||
#include <liblangutil/DebugInfoSelection.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
@ -190,8 +190,8 @@ struct CommandLineOptions
|
||||
|
||||
struct
|
||||
{
|
||||
yul::AssemblyStack::Machine targetMachine = yul::AssemblyStack::Machine::EVM;
|
||||
yul::AssemblyStack::Language inputLanguage = yul::AssemblyStack::Language::StrictAssembly;
|
||||
yul::YulStack::Machine targetMachine = yul::YulStack::Machine::EVM;
|
||||
yul::YulStack::Language inputLanguage = yul::YulStack::Language::StrictAssembly;
|
||||
} assembly;
|
||||
|
||||
struct
|
||||
|
@ -159,26 +159,33 @@ bool CommonOptions::parse(int argc, char const* const* argv)
|
||||
po::variables_map arguments;
|
||||
addOptions();
|
||||
|
||||
po::command_line_parser cmdLineParser(argc, argv);
|
||||
cmdLineParser.options(options);
|
||||
auto parsedOptions = cmdLineParser.run();
|
||||
po::store(parsedOptions, arguments);
|
||||
po::notify(arguments);
|
||||
try
|
||||
{
|
||||
po::command_line_parser cmdLineParser(argc, argv);
|
||||
cmdLineParser.options(options);
|
||||
auto parsedOptions = cmdLineParser.run();
|
||||
po::store(parsedOptions, arguments);
|
||||
po::notify(arguments);
|
||||
|
||||
for (auto const& parsedOption: parsedOptions.options)
|
||||
if (parsedOption.position_key >= 0)
|
||||
{
|
||||
if (
|
||||
parsedOption.original_tokens.empty() ||
|
||||
(parsedOption.original_tokens.size() == 1 && parsedOption.original_tokens.front().empty())
|
||||
)
|
||||
continue; // ignore empty options
|
||||
std::stringstream errorMessage;
|
||||
errorMessage << "Unrecognized option: ";
|
||||
for (auto const& token: parsedOption.original_tokens)
|
||||
errorMessage << token;
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(errorMessage.str()));
|
||||
}
|
||||
for (auto const& parsedOption: parsedOptions.options)
|
||||
if (parsedOption.position_key >= 0)
|
||||
{
|
||||
if (
|
||||
parsedOption.original_tokens.empty() ||
|
||||
(parsedOption.original_tokens.size() == 1 && parsedOption.original_tokens.front().empty())
|
||||
)
|
||||
continue; // ignore empty options
|
||||
std::stringstream errorMessage;
|
||||
errorMessage << "Unrecognized option: ";
|
||||
for (auto const& token: parsedOption.original_tokens)
|
||||
errorMessage << token;
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(errorMessage.str()));
|
||||
}
|
||||
}
|
||||
catch (po::error const& exception)
|
||||
{
|
||||
solThrow(ConfigException, exception.what());
|
||||
}
|
||||
|
||||
if (vmPaths.empty())
|
||||
{
|
||||
|
@ -75,6 +75,9 @@ struct CommonOptions
|
||||
langutil::EVMVersion evmVersion() const;
|
||||
|
||||
virtual void addOptions();
|
||||
// @returns true if the program should continue, false if it should exit immediately without
|
||||
// reporting an error.
|
||||
// Throws ConfigException or std::runtime_error if parsing fails.
|
||||
virtual bool parse(int argc, char const* const* argv);
|
||||
// Throws a ConfigException on error
|
||||
virtual void validate() const;
|
||||
|
@ -204,88 +204,106 @@ int registerTests(
|
||||
return numTestsAdded;
|
||||
}
|
||||
|
||||
void initializeOptions()
|
||||
bool initializeOptions()
|
||||
{
|
||||
auto const& suite = boost::unit_test::framework::master_test_suite();
|
||||
|
||||
auto options = std::make_unique<solidity::test::CommonOptions>();
|
||||
solAssert(options->parse(suite.argc, suite.argv), "Failed to parse options!");
|
||||
bool shouldContinue = options->parse(suite.argc, suite.argv);
|
||||
if (!shouldContinue)
|
||||
return false;
|
||||
options->validate();
|
||||
|
||||
solidity::test::CommonOptions::setSingleton(std::move(options));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Prototype -- why isn't this declared in the boost headers?
|
||||
// TODO: replace this with a (global) fixture.
|
||||
test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] );
|
||||
test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]);
|
||||
|
||||
test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
||||
test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[])
|
||||
{
|
||||
using namespace solidity::test;
|
||||
|
||||
master_test_suite_t& master = framework::master_test_suite();
|
||||
master.p_name.value = "SolidityTests";
|
||||
|
||||
initializeOptions();
|
||||
|
||||
if (!solidity::test::loadVMs(solidity::test::CommonOptions::get()))
|
||||
exit(1);
|
||||
|
||||
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
||||
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
|
||||
|
||||
if (!solidity::test::CommonOptions::get().enforceGasTest)
|
||||
cout << endl << "WARNING :: Gas Cost Expectations are not being enforced" << endl << endl;
|
||||
|
||||
Batcher batcher(CommonOptions::get().selectedBatch, CommonOptions::get().batches);
|
||||
if (CommonOptions::get().batches > 1)
|
||||
cout << "Batch " << CommonOptions::get().selectedBatch << " out of " << CommonOptions::get().batches << endl;
|
||||
|
||||
// Batch the boost tests
|
||||
BoostBatcher boostBatcher(batcher);
|
||||
traverse_test_tree(master, boostBatcher, true);
|
||||
|
||||
// Include the interactive tests in the automatic tests as well
|
||||
for (auto const& ts: g_interactiveTestsuites)
|
||||
try
|
||||
{
|
||||
auto const& options = solidity::test::CommonOptions::get();
|
||||
bool shouldContinue = initializeOptions();
|
||||
if (!shouldContinue)
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
if (ts.smt && options.disableSMT)
|
||||
continue;
|
||||
if (!solidity::test::loadVMs(solidity::test::CommonOptions::get()))
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (ts.needsVM && solidity::test::CommonOptions::get().disableSemanticTests)
|
||||
continue;
|
||||
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
||||
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
|
||||
|
||||
//TODO
|
||||
//solAssert(
|
||||
registerTests(
|
||||
master,
|
||||
options.testPath / ts.path,
|
||||
ts.subpath,
|
||||
options.enforceViaYul,
|
||||
options.enforceCompileToEwasm,
|
||||
ts.labels,
|
||||
ts.testCaseCreator,
|
||||
batcher
|
||||
);
|
||||
// > 0, std::string("no ") + ts.title + " tests found");
|
||||
if (!solidity::test::CommonOptions::get().enforceGasTest)
|
||||
cout << endl << "WARNING :: Gas Cost Expectations are not being enforced" << endl << endl;
|
||||
|
||||
Batcher batcher(CommonOptions::get().selectedBatch, CommonOptions::get().batches);
|
||||
if (CommonOptions::get().batches > 1)
|
||||
cout << "Batch " << CommonOptions::get().selectedBatch << " out of " << CommonOptions::get().batches << endl;
|
||||
|
||||
// Batch the boost tests
|
||||
BoostBatcher boostBatcher(batcher);
|
||||
traverse_test_tree(master, boostBatcher, true);
|
||||
|
||||
// Include the interactive tests in the automatic tests as well
|
||||
for (auto const& ts: g_interactiveTestsuites)
|
||||
{
|
||||
auto const& options = solidity::test::CommonOptions::get();
|
||||
|
||||
if (ts.smt && options.disableSMT)
|
||||
continue;
|
||||
|
||||
if (ts.needsVM && solidity::test::CommonOptions::get().disableSemanticTests)
|
||||
continue;
|
||||
|
||||
//TODO
|
||||
//solAssert(
|
||||
registerTests(
|
||||
master,
|
||||
options.testPath / ts.path,
|
||||
ts.subpath,
|
||||
options.enforceViaYul,
|
||||
options.enforceCompileToEwasm,
|
||||
ts.labels,
|
||||
ts.testCaseCreator,
|
||||
batcher
|
||||
);
|
||||
// > 0, std::string("no ") + ts.title + " tests found");
|
||||
}
|
||||
|
||||
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
||||
{
|
||||
for (auto suite: {
|
||||
"ABIDecoderTest",
|
||||
"ABIEncoderTest",
|
||||
"SolidityAuctionRegistrar",
|
||||
"SolidityWallet",
|
||||
"GasMeterTests",
|
||||
"GasCostTests",
|
||||
"SolidityEndToEndTest",
|
||||
"SolidityOptimizer"
|
||||
})
|
||||
removeTestSuite(suite);
|
||||
}
|
||||
}
|
||||
|
||||
if (solidity::test::CommonOptions::get().disableSemanticTests)
|
||||
catch (solidity::test::ConfigException const& exception)
|
||||
{
|
||||
for (auto suite: {
|
||||
"ABIDecoderTest",
|
||||
"ABIEncoderTest",
|
||||
"SolidityAuctionRegistrar",
|
||||
"SolidityWallet",
|
||||
"GasMeterTests",
|
||||
"GasCostTests",
|
||||
"SolidityEndToEndTest",
|
||||
"SolidityOptimizer"
|
||||
})
|
||||
removeTestSuite(suite);
|
||||
cerr << exception.what() << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
catch (std::runtime_error const& exception)
|
||||
{
|
||||
cerr << exception.what() << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -310,6 +310,9 @@ function test_solc_assembly_output
|
||||
|
||||
function test_via_ir_equivalence()
|
||||
{
|
||||
SOLTMPDIR=$(mktemp -d)
|
||||
pushd "$SOLTMPDIR"
|
||||
|
||||
(( $# <= 2 )) || fail "This function accepts at most two arguments."
|
||||
|
||||
if [[ $2 != --optimize ]] && [[ $2 != "" ]]
|
||||
@ -317,50 +320,57 @@ function test_via_ir_equivalence()
|
||||
fail "The second argument must be --optimize if present."
|
||||
fi
|
||||
|
||||
local solidity_code="$1"
|
||||
local solidity_file="$1"
|
||||
local optimize_flag="$2"
|
||||
|
||||
output_file_prefix=$(basename "$1" .sol)
|
||||
|
||||
local optimizer_flags=()
|
||||
[[ $optimize_flag == "" ]] || optimizer_flags+=("$optimize_flag")
|
||||
[[ $optimize_flag == "" ]] || output_file_prefix+="_optimize"
|
||||
|
||||
local ir_output
|
||||
ir_output=$(
|
||||
echo "$solidity_code" |
|
||||
msg_on_error --no-stderr "$SOLC" - --ir-optimized --debug-info location "${optimizer_flags[@]}" |
|
||||
sed '/^Optimized IR:$/d'
|
||||
)
|
||||
msg_on_error --no-stderr "$SOLC" --ir-optimized --debug-info location "${optimizer_flags[@]}" "$solidity_file" |
|
||||
sed '/^Optimized IR:$/d' |
|
||||
split_on_empty_lines_into_numbered_files $output_file_prefix ".yul"
|
||||
|
||||
for yul_file in $(find . -name "${output_file_prefix}*.yul" | sort -V); do
|
||||
msg_on_error --no-stderr "$SOLC" --strict-assembly --asm "${optimizer_flags[@]}" "$yul_file" |
|
||||
sed '/^Text representation:$/d' > "${yul_file/.yul/.asm}"
|
||||
done
|
||||
|
||||
local asm_output_two_stage asm_output_via_ir
|
||||
asm_output_two_stage=$(
|
||||
echo "$ir_output" |
|
||||
msg_on_error --no-stderr "$SOLC" - --strict-assembly --asm "${optimizer_flags[@]}" |
|
||||
sed '/^======= <stdin>/d' |
|
||||
sed '/^Text representation:$/d'
|
||||
)
|
||||
for asm_file in $(find . -name "${output_file_prefix}*.asm" | sort -V); do
|
||||
asm_output_two_stage+=$(sed '/^asm_output_two_stage:$/d' "$asm_file" | sed '/^=======/d')
|
||||
done
|
||||
|
||||
asm_output_via_ir=$(
|
||||
echo "$solidity_code" |
|
||||
msg_on_error --no-stderr "$SOLC" - --via-ir --asm --debug-info location "${optimizer_flags[@]}" |
|
||||
sed '/^======= <stdin>/d' |
|
||||
sed '/^EVM assembly:$/d'
|
||||
msg_on_error --no-stderr "$SOLC" --via-ir --asm --debug-info location "${optimizer_flags[@]}" "$solidity_file" |
|
||||
sed '/^EVM assembly:$/d' |
|
||||
sed '/^=======/d'
|
||||
)
|
||||
|
||||
diff_values "$asm_output_two_stage" "$asm_output_via_ir" --ignore-space-change --ignore-blank-lines
|
||||
|
||||
local bin_output_two_stage bin_output_via_ir
|
||||
bin_output_two_stage=$(
|
||||
echo "$ir_output" |
|
||||
msg_on_error --no-stderr "$SOLC" - --strict-assembly --bin "${optimizer_flags[@]}" |
|
||||
sed '/^======= <stdin>/d' |
|
||||
sed '/^Binary representation:$/d'
|
||||
)
|
||||
|
||||
for yul_file in $(find . -name "${output_file_prefix}*.yul" | sort -V); do
|
||||
bin_output_two_stage+=$(
|
||||
msg_on_error --no-stderr "$SOLC" --strict-assembly --bin "${optimizer_flags[@]}" "$yul_file" |
|
||||
sed '/^Binary representation:$/d' |
|
||||
sed '/^=======/d'
|
||||
)
|
||||
done
|
||||
|
||||
bin_output_via_ir=$(
|
||||
echo "$solidity_code" |
|
||||
msg_on_error --no-stderr "$SOLC" - --via-ir --bin "${optimizer_flags[@]}" |
|
||||
sed '/^======= <stdin>/d' |
|
||||
sed '/^Binary:$/d'
|
||||
msg_on_error --no-stderr "$SOLC" --via-ir --bin "${optimizer_flags[@]}" "$solidity_file" |
|
||||
sed '/^Binary:$/d' |
|
||||
sed '/^=======/d'
|
||||
)
|
||||
|
||||
diff_values "$bin_output_two_stage" "$bin_output_via_ir" --ignore-space-change --ignore-blank-lines
|
||||
|
||||
popd
|
||||
rm -r "$SOLTMPDIR"
|
||||
}
|
||||
|
||||
## RUN
|
||||
@ -590,20 +600,21 @@ printTask "Testing assemble, yul, strict-assembly and optimize..."
|
||||
|
||||
printTask "Testing the eqivalence of --via-ir and a two-stage compilation..."
|
||||
(
|
||||
printTask " - Smoke test"
|
||||
test_via_ir_equivalence "contract C {}"
|
||||
|
||||
printTask " - Smoke test (optimized)"
|
||||
test_via_ir_equivalence "contract C {}" --optimize
|
||||
|
||||
externalContracts=(
|
||||
deposit_contract.sol
|
||||
FixedFeeRegistrar.sol
|
||||
_stringutils/stringutils.sol
|
||||
externalTests/solc-js/DAO/TokenCreation.sol
|
||||
libsolidity/semanticTests/externalContracts/_prbmath/PRBMathSD59x18.sol
|
||||
libsolidity/semanticTests/externalContracts/_prbmath/PRBMathUD60x18.sol
|
||||
libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol
|
||||
libsolidity/semanticTests/externalContracts/deposit_contract.sol
|
||||
libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol
|
||||
libsolidity/semanticTests/externalContracts/snark.sol
|
||||
)
|
||||
|
||||
requiresOptimizer=(
|
||||
deposit_contract.sol
|
||||
FixedFeeRegistrar.sol
|
||||
externalTests/solc-js/DAO/TokenCreation.sol
|
||||
libsolidity/semanticTests/externalContracts/deposit_contract.sol
|
||||
libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol
|
||||
libsolidity/semanticTests/externalContracts/snark.sol
|
||||
)
|
||||
|
||||
for contractFile in "${externalContracts[@]}"
|
||||
@ -611,11 +622,11 @@ printTask "Testing the eqivalence of --via-ir and a two-stage compilation..."
|
||||
if ! [[ "${requiresOptimizer[*]}" =~ $contractFile ]]
|
||||
then
|
||||
printTask " - ${contractFile}"
|
||||
test_via_ir_equivalence "$(cat "${REPO_ROOT}/test/libsolidity/semanticTests/externalContracts/${contractFile}")"
|
||||
test_via_ir_equivalence "${REPO_ROOT}/test/${contractFile}"
|
||||
fi
|
||||
|
||||
printTask " - ${contractFile} (optimized)"
|
||||
test_via_ir_equivalence "$(cat "${REPO_ROOT}/test/libsolidity/semanticTests/externalContracts/${contractFile}")" --optimize
|
||||
test_via_ir_equivalence "${REPO_ROOT}/test/${contractFile}" --optimize
|
||||
done
|
||||
)
|
||||
|
||||
|
@ -1 +1 @@
|
||||
--asm-json
|
||||
--asm-json --pretty-json
|
||||
|
@ -450,9 +450,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 77,
|
||||
"end": 158,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 0,
|
||||
"value": "[in]"
|
||||
"source": 0
|
||||
},
|
||||
{
|
||||
"begin": 77,
|
||||
@ -477,9 +477,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 77,
|
||||
"end": 158,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 0,
|
||||
"value": "[in]"
|
||||
"source": 0
|
||||
},
|
||||
{
|
||||
"begin": 77,
|
||||
@ -555,9 +555,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 118,
|
||||
"end": 125,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 0,
|
||||
"value": "[in]"
|
||||
"source": 0
|
||||
},
|
||||
{
|
||||
"begin": 118,
|
||||
@ -657,9 +657,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 77,
|
||||
"end": 158,
|
||||
"jumpType": "[out]",
|
||||
"name": "JUMP",
|
||||
"source": 0,
|
||||
"value": "[out]"
|
||||
"source": 0
|
||||
},
|
||||
{
|
||||
"begin": 88,
|
||||
@ -752,9 +752,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 334,
|
||||
"end": 411,
|
||||
"jumpType": "[out]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[out]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 417,
|
||||
@ -792,9 +792,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 490,
|
||||
"end": 514,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[in]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 490,
|
||||
@ -875,9 +875,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 417,
|
||||
"end": 539,
|
||||
"jumpType": "[out]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[out]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 545,
|
||||
@ -946,9 +946,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 645,
|
||||
"end": 678,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[in]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 645,
|
||||
@ -990,9 +990,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 545,
|
||||
"end": 684,
|
||||
"jumpType": "[out]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[out]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 690,
|
||||
@ -1081,9 +1081,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 804,
|
||||
"end": 883,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[in]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 804,
|
||||
@ -1159,9 +1159,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 949,
|
||||
"end": 1002,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[in]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 949,
|
||||
@ -1221,9 +1221,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 690,
|
||||
"end": 1019,
|
||||
"jumpType": "[out]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[out]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 1025,
|
||||
@ -1341,9 +1341,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 1270,
|
||||
"end": 1290,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[in]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 1270,
|
||||
@ -1393,9 +1393,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 1304,
|
||||
"end": 1324,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[in]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 1304,
|
||||
@ -1489,9 +1489,9 @@ EVM assembly:
|
||||
{
|
||||
"begin": 1464,
|
||||
"end": 1482,
|
||||
"jumpType": "[in]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[in]"
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"begin": 1464,
|
||||
@ -1576,11 +1576,16 @@ EVM assembly:
|
||||
{
|
||||
"begin": 1211,
|
||||
"end": 1516,
|
||||
"jumpType": "[out]",
|
||||
"name": "JUMP",
|
||||
"source": 1,
|
||||
"value": "[out]"
|
||||
"source": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceList":
|
||||
[
|
||||
"asm_json/input.sol",
|
||||
"#utility.yul"
|
||||
]
|
||||
}
|
||||
|
1
test/cmdlineTests/asm_json_no_pretty_print/args
Normal file
1
test/cmdlineTests/asm_json_no_pretty_print/args
Normal file
@ -0,0 +1 @@
|
||||
--asm-json
|
9
test/cmdlineTests/asm_json_no_pretty_print/input.sol
Normal file
9
test/cmdlineTests/asm_json_no_pretty_print/input.sol
Normal file
@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.0;
|
||||
|
||||
contract C {
|
||||
function f(uint x) public pure {
|
||||
x += 42;
|
||||
require(x > 100);
|
||||
}
|
||||
}
|
4
test/cmdlineTests/asm_json_no_pretty_print/output
Normal file
4
test/cmdlineTests/asm_json_no_pretty_print/output
Normal file
File diff suppressed because one or more lines are too long
1
test/cmdlineTests/ast_compact_json_no_pretty_json/args
Normal file
1
test/cmdlineTests/ast_compact_json_no_pretty_json/args
Normal file
@ -0,0 +1 @@
|
||||
--ast-compact-json --allow-paths .
|
@ -0,0 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.0;
|
||||
|
||||
contract C {}
|
5
test/cmdlineTests/ast_compact_json_no_pretty_json/output
Normal file
5
test/cmdlineTests/ast_compact_json_no_pretty_json/output
Normal file
@ -0,0 +1,5 @@
|
||||
JSON AST (compact format):
|
||||
|
||||
|
||||
======= ast_compact_json_no_pretty_json/input.sol =======
|
||||
{"absolutePath":"ast_compact_json_no_pretty_json/input.sol","exportedSymbols":{"C":[2]},"id":3,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":2,"linearizedBaseContracts":[2],"name":"C","nameLocation":"69:1:0","nodeType":"ContractDefinition","nodes":[],"scope":3,"src":"60:13:0","usedErrors":[]}],"src":"36:38:0"}
|
@ -1 +1 @@
|
||||
--ast-compact-json --base-path . --allow-paths .
|
||||
--ast-compact-json --pretty-json --base-path . --allow-paths .
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"contracts":
|
||||
{
|
||||
"pretty_json_combined/input.sol:C":
|
||||
"combined_json_abi/input.sol:C":
|
||||
{
|
||||
"abi": []
|
||||
}
|
1
test/cmdlineTests/combined_json_no_pretty_print/args
Normal file
1
test/cmdlineTests/combined_json_no_pretty_print/args
Normal file
@ -0,0 +1 @@
|
||||
--combined-json abi
|
@ -0,0 +1,3 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.0;
|
||||
contract C {}
|
1
test/cmdlineTests/combined_json_no_pretty_print/output
Normal file
1
test/cmdlineTests/combined_json_no_pretty_print/output
Normal file
@ -0,0 +1 @@
|
||||
{"contracts":{"combined_json_no_pretty_print/input.sol:C":{"abi":[]}},"version": "<VERSION REMOVED>"}
|
@ -1 +1 @@
|
||||
--combined-json ast --base-path . --allow-paths .
|
||||
--combined-json ast --pretty-json --base-path . --allow-paths .
|
||||
|
@ -1 +1,111 @@
|
||||
{"contracts":{"combined_json_with_base_path/c.sol:C":{}},"sourceList":["combined_json_with_base_path/c.sol","combined_json_with_base_path/input.sol"],"sources":{"combined_json_with_base_path/c.sol":{"AST":{"absolutePath":"combined_json_with_base_path/c.sol","exportedSymbols":{"C":[5]},"id":6,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":4,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":5,"linearizedBaseContracts":[5],"name":"C","nameLocation":"69:1:0","nodeType":"ContractDefinition","nodes":[],"scope":6,"src":"60:13:0","usedErrors":[]}],"src":"36:38:0"}},"combined_json_with_base_path/input.sol":{"AST":{"absolutePath":"combined_json_with_base_path/input.sol","exportedSymbols":{"C":[5]},"id":3,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:1"},{"absolutePath":"combined_json_with_base_path/c.sol","file":"./c.sol","id":2,"nameLocation":"-1:-1:-1","nodeType":"ImportDirective","scope":3,"sourceUnit":6,"src":"60:17:1","symbolAliases":[],"unitAlias":""}],"src":"36:42:1"}}},"version": "<VERSION REMOVED>"}
|
||||
{
|
||||
"contracts":
|
||||
{
|
||||
"combined_json_with_base_path/c.sol:C": {}
|
||||
},
|
||||
"sourceList":
|
||||
[
|
||||
"combined_json_with_base_path/c.sol",
|
||||
"combined_json_with_base_path/input.sol"
|
||||
],
|
||||
"sources":
|
||||
{
|
||||
"combined_json_with_base_path/c.sol":
|
||||
{
|
||||
"AST":
|
||||
{
|
||||
"absolutePath": "combined_json_with_base_path/c.sol",
|
||||
"exportedSymbols":
|
||||
{
|
||||
"C":
|
||||
[
|
||||
5
|
||||
]
|
||||
},
|
||||
"id": 6,
|
||||
"license": "GPL-3.0",
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"id": 4,
|
||||
"literals":
|
||||
[
|
||||
"solidity",
|
||||
">=",
|
||||
"0.0"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "36:22:0"
|
||||
},
|
||||
{
|
||||
"abstract": false,
|
||||
"baseContracts": [],
|
||||
"canonicalName": "C",
|
||||
"contractDependencies": [],
|
||||
"contractKind": "contract",
|
||||
"fullyImplemented": true,
|
||||
"id": 5,
|
||||
"linearizedBaseContracts":
|
||||
[
|
||||
5
|
||||
],
|
||||
"name": "C",
|
||||
"nameLocation": "69:1:0",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [],
|
||||
"scope": 6,
|
||||
"src": "60:13:0",
|
||||
"usedErrors": []
|
||||
}
|
||||
],
|
||||
"src": "36:38:0"
|
||||
}
|
||||
},
|
||||
"combined_json_with_base_path/input.sol":
|
||||
{
|
||||
"AST":
|
||||
{
|
||||
"absolutePath": "combined_json_with_base_path/input.sol",
|
||||
"exportedSymbols":
|
||||
{
|
||||
"C":
|
||||
[
|
||||
5
|
||||
]
|
||||
},
|
||||
"id": 3,
|
||||
"license": "GPL-3.0",
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"literals":
|
||||
[
|
||||
"solidity",
|
||||
">=",
|
||||
"0.0"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "36:22:1"
|
||||
},
|
||||
{
|
||||
"absolutePath": "combined_json_with_base_path/c.sol",
|
||||
"file": "./c.sol",
|
||||
"id": 2,
|
||||
"nameLocation": "-1:-1:-1",
|
||||
"nodeType": "ImportDirective",
|
||||
"scope": 3,
|
||||
"sourceUnit": 6,
|
||||
"src": "60:17:1",
|
||||
"symbolAliases": [],
|
||||
"unitAlias": ""
|
||||
}
|
||||
],
|
||||
"src": "36:42:1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": "<VERSION REMOVED>"
|
||||
}
|
||||
|
1
test/cmdlineTests/combined_json_with_devdoc/args
Normal file
1
test/cmdlineTests/combined_json_with_devdoc/args
Normal file
@ -0,0 +1 @@
|
||||
--combined-json devdoc --pretty-json --allow-paths .
|
8
test/cmdlineTests/combined_json_with_devdoc/input.sol
Normal file
8
test/cmdlineTests/combined_json_with_devdoc/input.sol
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.0;
|
||||
|
||||
/// @dev This is devdoc.
|
||||
contract C {}
|
||||
|
||||
/// And this is a notice.
|
||||
contract D {}
|
25
test/cmdlineTests/combined_json_with_devdoc/output
Normal file
25
test/cmdlineTests/combined_json_with_devdoc/output
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"contracts":
|
||||
{
|
||||
"combined_json_with_devdoc/input.sol:C":
|
||||
{
|
||||
"devdoc":
|
||||
{
|
||||
"details": "This is devdoc.",
|
||||
"kind": "dev",
|
||||
"methods": {},
|
||||
"version": 1
|
||||
}
|
||||
},
|
||||
"combined_json_with_devdoc/input.sol:D":
|
||||
{
|
||||
"devdoc":
|
||||
{
|
||||
"kind": "dev",
|
||||
"methods": {},
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": "<VERSION REMOVED>"
|
||||
}
|
1
test/cmdlineTests/combined_json_with_userdoc/args
Normal file
1
test/cmdlineTests/combined_json_with_userdoc/args
Normal file
@ -0,0 +1 @@
|
||||
--combined-json userdoc --pretty-json --allow-paths .
|
8
test/cmdlineTests/combined_json_with_userdoc/input.sol
Normal file
8
test/cmdlineTests/combined_json_with_userdoc/input.sol
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.0;
|
||||
|
||||
/// @notice Description for users.
|
||||
contract C {}
|
||||
|
||||
/// @dev Description for developers.
|
||||
contract D {}
|
25
test/cmdlineTests/combined_json_with_userdoc/output
Normal file
25
test/cmdlineTests/combined_json_with_userdoc/output
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"contracts":
|
||||
{
|
||||
"combined_json_with_userdoc/input.sol:C":
|
||||
{
|
||||
"userdoc":
|
||||
{
|
||||
"kind": "user",
|
||||
"methods": {},
|
||||
"notice": "Description for users.",
|
||||
"version": 1
|
||||
}
|
||||
},
|
||||
"combined_json_with_userdoc/input.sol:D":
|
||||
{
|
||||
"userdoc":
|
||||
{
|
||||
"kind": "user",
|
||||
"methods": {},
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": "<VERSION REMOVED>"
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
},
|
||||
"calldata_array_index_access_uint256_dyn_calldata":
|
||||
{
|
||||
"entryPoint": 145,
|
||||
"entryPoint": 144,
|
||||
"parameterSlots": 2,
|
||||
"returnSlots": 1
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
--error-recovery --ast-compact-json --hashes
|
||||
--error-recovery --ast-compact-json --pretty-json --hashes
|
||||
|
10
test/cmdlineTests/standard_json_no_pretty_print/input.json
Normal file
10
test/cmdlineTests/standard_json_no_pretty_print/input.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources":
|
||||
{
|
||||
"A":
|
||||
{
|
||||
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {}"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{"sources":{"A":{"id":0}}}
|
@ -1 +1 @@
|
||||
--storage-layout
|
||||
--storage-layout --pretty-json
|
||||
|
@ -1,4 +1,87 @@
|
||||
|
||||
======= storage_layout_user_defined/input.sol:C =======
|
||||
Contract Storage Layout:
|
||||
{"storage":[{"astId":7,"contract":"storage_layout_user_defined/input.sol:C","label":"a","offset":0,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":10,"contract":"storage_layout_user_defined/input.sol:C","label":"b","offset":16,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":13,"contract":"storage_layout_user_defined/input.sol:C","label":"c","offset":0,"slot":"1","type":"t_userDefinedValueType(MyInt128)2"},{"astId":16,"contract":"storage_layout_user_defined/input.sol:C","label":"d","offset":16,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":19,"contract":"storage_layout_user_defined/input.sol:C","label":"e","offset":17,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":22,"contract":"storage_layout_user_defined/input.sol:C","label":"f","offset":18,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":25,"contract":"storage_layout_user_defined/input.sol:C","label":"g","offset":19,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":28,"contract":"storage_layout_user_defined/input.sol:C","label":"h","offset":20,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"}],"types":{"t_userDefinedValueType(MyInt128)2":{"encoding":"inplace","label":"MyInt128","numberOfBytes":"16"},"t_userDefinedValueType(MyInt8)4":{"encoding":"inplace","label":"MyInt8","numberOfBytes":"1"}}}
|
||||
{
|
||||
"storage":
|
||||
[
|
||||
{
|
||||
"astId": 7,
|
||||
"contract": "storage_layout_user_defined/input.sol:C",
|
||||
"label": "a",
|
||||
"offset": 0,
|
||||
"slot": "0",
|
||||
"type": "t_userDefinedValueType(MyInt128)2"
|
||||
},
|
||||
{
|
||||
"astId": 10,
|
||||
"contract": "storage_layout_user_defined/input.sol:C",
|
||||
"label": "b",
|
||||
"offset": 16,
|
||||
"slot": "0",
|
||||
"type": "t_userDefinedValueType(MyInt128)2"
|
||||
},
|
||||
{
|
||||
"astId": 13,
|
||||
"contract": "storage_layout_user_defined/input.sol:C",
|
||||
"label": "c",
|
||||
"offset": 0,
|
||||
"slot": "1",
|
||||
"type": "t_userDefinedValueType(MyInt128)2"
|
||||
},
|
||||
{
|
||||
"astId": 16,
|
||||
"contract": "storage_layout_user_defined/input.sol:C",
|
||||
"label": "d",
|
||||
"offset": 16,
|
||||
"slot": "1",
|
||||
"type": "t_userDefinedValueType(MyInt8)4"
|
||||
},
|
||||
{
|
||||
"astId": 19,
|
||||
"contract": "storage_layout_user_defined/input.sol:C",
|
||||
"label": "e",
|
||||
"offset": 17,
|
||||
"slot": "1",
|
||||
"type": "t_userDefinedValueType(MyInt8)4"
|
||||
},
|
||||
{
|
||||
"astId": 22,
|
||||
"contract": "storage_layout_user_defined/input.sol:C",
|
||||
"label": "f",
|
||||
"offset": 18,
|
||||
"slot": "1",
|
||||
"type": "t_userDefinedValueType(MyInt8)4"
|
||||
},
|
||||
{
|
||||
"astId": 25,
|
||||
"contract": "storage_layout_user_defined/input.sol:C",
|
||||
"label": "g",
|
||||
"offset": 19,
|
||||
"slot": "1",
|
||||
"type": "t_userDefinedValueType(MyInt8)4"
|
||||
},
|
||||
{
|
||||
"astId": 28,
|
||||
"contract": "storage_layout_user_defined/input.sol:C",
|
||||
"label": "h",
|
||||
"offset": 20,
|
||||
"slot": "1",
|
||||
"type": "t_userDefinedValueType(MyInt8)4"
|
||||
}
|
||||
],
|
||||
"types":
|
||||
{
|
||||
"t_userDefinedValueType(MyInt128)2":
|
||||
{
|
||||
"encoding": "inplace",
|
||||
"label": "MyInt128",
|
||||
"numberOfBytes": "16"
|
||||
},
|
||||
"t_userDefinedValueType(MyInt8)4":
|
||||
{
|
||||
"encoding": "inplace",
|
||||
"label": "MyInt8",
|
||||
"numberOfBytes": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
--storage-layout
|
@ -0,0 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL v3
|
||||
pragma solidity >=0.0;
|
||||
|
||||
contract C {}
|
@ -0,0 +1,4 @@
|
||||
|
||||
======= storage_layout_user_defined_no_pretty_print/input.sol:C =======
|
||||
Contract Storage Layout:
|
||||
{"storage":[]}
|
@ -125,7 +125,6 @@ sub_0: assembly {
|
||||
eq
|
||||
tag_4
|
||||
jumpi
|
||||
pop
|
||||
0x00
|
||||
dup1
|
||||
revert
|
||||
|
@ -88,18 +88,6 @@ contract MultiSigWallet {
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Receive function allows to deposit ether.
|
||||
receive()
|
||||
external
|
||||
payable
|
||||
{
|
||||
if (msg.value > 0)
|
||||
emit Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
*/
|
||||
/// @dev Contract constructor sets initial owners and required number of confirmations.
|
||||
/// @param _owners List of initial owners.
|
||||
/// @param _required Number of required confirmations.
|
||||
@ -115,6 +103,19 @@ contract MultiSigWallet {
|
||||
required = _required;
|
||||
}
|
||||
|
||||
/// @dev Receive function allows to deposit ether.
|
||||
receive()
|
||||
external
|
||||
payable
|
||||
{
|
||||
if (msg.value > 0)
|
||||
emit Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
*/
|
||||
|
||||
/// @dev Allows to add a new owner. Transaction has to be sent by wallet.
|
||||
/// @param owner Address of new owner.
|
||||
function addOwner(address owner)
|
||||
@ -225,9 +226,9 @@ contract MultiSigWallet {
|
||||
notExecuted(transactionId)
|
||||
{
|
||||
if (isConfirmed(transactionId)) {
|
||||
Transaction storage tx = transactions[transactionId];
|
||||
(tx.executed,) = tx.destination.call{value: tx.value}(tx.data);
|
||||
if (tx.executed)
|
||||
Transaction storage transaction = transactions[transactionId];
|
||||
(transaction.executed,) = transaction.destination.call{value: transaction.value}(transaction.data);
|
||||
if (transaction.executed)
|
||||
emit Execution(transactionId);
|
||||
else
|
||||
emit ExecutionFailure(transactionId);
|
||||
@ -249,30 +250,7 @@ contract MultiSigWallet {
|
||||
if (count == required)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal functions
|
||||
*/
|
||||
/// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
|
||||
/// @param destination Transaction target address.
|
||||
/// @param value Transaction ether value.
|
||||
/// @param data Transaction data payload.
|
||||
/// @return transactionId Returns transaction ID.
|
||||
function addTransaction(address destination, uint value, bytes memory data)
|
||||
internal
|
||||
notNull(destination)
|
||||
returns (uint transactionId)
|
||||
{
|
||||
transactionId = transactionCount;
|
||||
transactions[transactionId] = Transaction({
|
||||
destination: destination,
|
||||
value: value,
|
||||
data: data,
|
||||
executed: false
|
||||
});
|
||||
transactionCount += 1;
|
||||
emit Submission(transactionId);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -362,4 +340,27 @@ contract MultiSigWallet {
|
||||
for (i=from; i<to; i++)
|
||||
_transactionIds[i - from] = transactionIdsTemp[i];
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Internal functions
|
||||
*/
|
||||
/// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
|
||||
/// @param destination Transaction target address.
|
||||
/// @param value Transaction ether value.
|
||||
/// @param data Transaction data payload.
|
||||
/// @return transactionId Returns transaction ID.
|
||||
function addTransaction(address destination, uint value, bytes memory data)
|
||||
internal
|
||||
notNull(destination)
|
||||
returns (uint transactionId)
|
||||
{
|
||||
transactionId = transactionCount;
|
||||
transactions[transactionId] = Transaction({
|
||||
destination: destination,
|
||||
value: value,
|
||||
data: data,
|
||||
executed: false
|
||||
});
|
||||
transactionCount += 1;
|
||||
emit Submission(transactionId);
|
||||
}
|
||||
}
|
@ -37,16 +37,17 @@ verify_input "$@"
|
||||
|
||||
printTask "Running external tests..."
|
||||
|
||||
"{$REPO_ROOT}/test/externalTests/zeppelin.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/gnosis.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/colony.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/ens.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/trident.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/euler.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/yield-liquidator.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/bleeps.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/pool-together.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/perpetual-pools.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/uniswap.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/prb-math.sh" "$@"
|
||||
"{$REPO_ROOT}/test/externalTests/elementfi.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/zeppelin.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/gnosis.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/colony.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/ens.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/trident.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/euler.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/yield-liquidator.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/bleeps.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/pool-together.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/perpetual-pools.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/uniswap.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/prb-math.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/elementfi.sh" "$@"
|
||||
"${REPO_ROOT}/test/externalTests/brink.sh" "$@"
|
||||
|
83
test/externalTests/brink.sh
Executable file
83
test/externalTests/brink.sh
Executable file
@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# This file is part of solidity.
|
||||
#
|
||||
# solidity is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# solidity is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
# (c) 2022 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
set -e
|
||||
|
||||
source scripts/common.sh
|
||||
source test/externalTests/common.sh
|
||||
|
||||
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
|
||||
|
||||
verify_input "$@"
|
||||
BINARY_TYPE="$1"
|
||||
BINARY_PATH="$2"
|
||||
SELECTED_PRESETS="$3"
|
||||
|
||||
function compile_fn { yarn compile; }
|
||||
function test_fn { SNAPSHOT_UPDATE=1 npx --no hardhat test; }
|
||||
|
||||
function brink_test
|
||||
{
|
||||
local repo="https://github.com/brinktrade/brink-core"
|
||||
local ref_type=branch
|
||||
local ref=master
|
||||
local config_file="hardhat.config.js"
|
||||
local config_var=""
|
||||
local extra_settings="metadata: {bytecodeHash: 'none'}"
|
||||
local extra_optimizer_settings="runs: 800"
|
||||
|
||||
local compile_only_presets=(
|
||||
#ir-no-optimize # Compilation fails with "YulException: Variable var_signature_127_offset is 2 slot(s) too deep inside the stack."
|
||||
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_signature_127_offset is 2 slot(s) too deep inside the stack."
|
||||
ir-optimize-evm+yul # Lots of test failures. Tests depend on constants.js, which seems to be calculated specifically for 0.8.10.
|
||||
legacy-optimize-evm+yul # Lots of test failures. Tests depend on constants.js, which seems to be calculated specifically for 0.8.10.
|
||||
legacy-no-optimize # Lots of test failures. Tests depend on constants.js, which seems to be calculated specifically for 0.8.10.
|
||||
legacy-optimize-evm-only # Lots of test failures. Tests depend on constants.js, which seems to be calculated specifically for 0.8.10.
|
||||
)
|
||||
local settings_presets=(
|
||||
"${compile_only_presets[@]}"
|
||||
)
|
||||
|
||||
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||
print_presets_or_exit "$SELECTED_PRESETS"
|
||||
|
||||
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
|
||||
download_project "$repo" "$ref_type" "$ref" "$DIR"
|
||||
|
||||
neutralize_package_lock
|
||||
neutralize_package_json_hooks
|
||||
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" "$CURRENT_EVM_VERSION" "$extra_settings" "$extra_optimizer_settings"
|
||||
yarn install
|
||||
yarn add hardhat-gas-reporter
|
||||
|
||||
# TODO: Remove when https://github.com/brinktrade/brink-core/issues/48 is fixed.
|
||||
yarn add chai
|
||||
|
||||
replace_version_pragmas
|
||||
|
||||
for preset in $SELECTED_PRESETS; do
|
||||
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var" "$extra_settings" "$extra_optimizer_settings"
|
||||
store_benchmark_report hardhat brink "$repo" "$preset"
|
||||
done
|
||||
}
|
||||
|
||||
external_test Brink brink_test
|
@ -198,6 +198,8 @@ function force_truffle_compiler_settings
|
||||
local solc_path="$3"
|
||||
local preset="$4"
|
||||
local evm_version="${5:-"$CURRENT_EVM_VERSION"}"
|
||||
local extra_settings="$6"
|
||||
local extra_optimizer_settings="$7"
|
||||
|
||||
[[ $binary_type == native || $binary_type == solcjs ]] || assertFail
|
||||
|
||||
@ -209,14 +211,14 @@ function force_truffle_compiler_settings
|
||||
echo "Binary type: $binary_type"
|
||||
echo "Compiler path: $solc_path"
|
||||
echo "Settings preset: ${preset}"
|
||||
echo "Settings: $(settings_from_preset "$preset" "$evm_version")"
|
||||
echo "Settings: $(settings_from_preset "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")"
|
||||
echo "EVM version: $evm_version"
|
||||
echo "Compiler version: ${SOLCVERSION_SHORT}"
|
||||
echo "Compiler version (full): ${SOLCVERSION}"
|
||||
echo "-------------------------------------"
|
||||
|
||||
local compiler_settings gas_reporter_settings
|
||||
compiler_settings=$(truffle_compiler_settings "$solc_path" "$preset" "$evm_version")
|
||||
compiler_settings=$(truffle_compiler_settings "$solc_path" "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")
|
||||
gas_reporter_settings=$(eth_gas_reporter_settings "$preset")
|
||||
|
||||
{
|
||||
@ -307,19 +309,21 @@ function force_hardhat_compiler_settings
|
||||
local preset="$2"
|
||||
local config_var_name="$3"
|
||||
local evm_version="${4:-"$CURRENT_EVM_VERSION"}"
|
||||
local extra_settings="$5"
|
||||
local extra_optimizer_settings="$6"
|
||||
|
||||
printLog "Configuring Hardhat..."
|
||||
echo "-------------------------------------"
|
||||
echo "Config file: ${config_file}"
|
||||
echo "Settings preset: ${preset}"
|
||||
echo "Settings: $(settings_from_preset "$preset" "$evm_version")"
|
||||
echo "Settings: $(settings_from_preset "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")"
|
||||
echo "EVM version: ${evm_version}"
|
||||
echo "Compiler version: ${SOLCVERSION_SHORT}"
|
||||
echo "Compiler version (full): ${SOLCVERSION}"
|
||||
echo "-------------------------------------"
|
||||
|
||||
local compiler_settings gas_reporter_settings
|
||||
compiler_settings=$(hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version")
|
||||
compiler_settings=$(hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")
|
||||
gas_reporter_settings=$(eth_gas_reporter_settings "$preset")
|
||||
if [[ $config_file == *\.js ]]; then
|
||||
[[ $config_var_name == "" ]] || assertFail
|
||||
@ -372,17 +376,22 @@ function settings_from_preset
|
||||
{
|
||||
local preset="$1"
|
||||
local evm_version="$2"
|
||||
local extra_settings="$3"
|
||||
local extra_optimizer_settings="$4"
|
||||
|
||||
[[ " ${AVAILABLE_PRESETS[*]} " == *" $preset "* ]] || assertFail
|
||||
|
||||
[[ $extra_settings == "" ]] || extra_settings="${extra_settings}, "
|
||||
[[ $extra_optimizer_settings == "" ]] || extra_optimizer_settings="${extra_optimizer_settings}, "
|
||||
|
||||
case "$preset" in
|
||||
# NOTE: Remember to update `parallelism` of `t_ems_ext` job in CI config if you add/remove presets
|
||||
legacy-no-optimize) echo "{evmVersion: '${evm_version}', viaIR: false, optimizer: {enabled: false}}" ;;
|
||||
ir-no-optimize) echo "{evmVersion: '${evm_version}', viaIR: true, optimizer: {enabled: false}}" ;;
|
||||
legacy-optimize-evm-only) echo "{evmVersion: '${evm_version}', viaIR: false, optimizer: {enabled: true, details: {yul: false}}}" ;;
|
||||
ir-optimize-evm-only) echo "{evmVersion: '${evm_version}', viaIR: true, optimizer: {enabled: true, details: {yul: false}}}" ;;
|
||||
legacy-optimize-evm+yul) echo "{evmVersion: '${evm_version}', viaIR: false, optimizer: {enabled: true, details: {yul: true}}}" ;;
|
||||
ir-optimize-evm+yul) echo "{evmVersion: '${evm_version}', viaIR: true, optimizer: {enabled: true, details: {yul: true}}}" ;;
|
||||
legacy-no-optimize) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: false, optimizer: {${extra_optimizer_settings}enabled: false}}" ;;
|
||||
ir-no-optimize) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: true, optimizer: {${extra_optimizer_settings}enabled: false}}" ;;
|
||||
legacy-optimize-evm-only) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: false, optimizer: {${extra_optimizer_settings}enabled: true, details: {yul: false}}}" ;;
|
||||
ir-optimize-evm-only) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: true, optimizer: {${extra_optimizer_settings}enabled: true, details: {yul: false}}}" ;;
|
||||
legacy-optimize-evm+yul) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: false, optimizer: {${extra_optimizer_settings}enabled: true, details: {yul: true}}}" ;;
|
||||
ir-optimize-evm+yul) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: true, optimizer: {${extra_optimizer_settings}enabled: true, details: {yul: true}}}" ;;
|
||||
*)
|
||||
fail "Unknown settings preset: '${preset}'."
|
||||
;;
|
||||
@ -419,11 +428,13 @@ function truffle_compiler_settings
|
||||
local solc_path="$1"
|
||||
local preset="$2"
|
||||
local evm_version="$3"
|
||||
local extra_settings="$4"
|
||||
local extra_optimizer_settings="$5"
|
||||
|
||||
echo "{"
|
||||
echo " solc: {"
|
||||
echo " version: \"${solc_path}\","
|
||||
echo " settings: $(settings_from_preset "$preset" "$evm_version")"
|
||||
echo " settings: $(settings_from_preset "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")"
|
||||
echo " }"
|
||||
echo "}"
|
||||
}
|
||||
@ -468,10 +479,12 @@ function hardhat_compiler_settings {
|
||||
local solc_version="$1"
|
||||
local preset="$2"
|
||||
local evm_version="$3"
|
||||
local extra_settings="$4"
|
||||
local extra_optimizer_settings="$5"
|
||||
|
||||
echo "{"
|
||||
echo " version: '${solc_version}',"
|
||||
echo " settings: $(settings_from_preset "$preset" "$evm_version")"
|
||||
echo " settings: $(settings_from_preset "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")"
|
||||
echo "}"
|
||||
}
|
||||
|
||||
@ -506,9 +519,11 @@ function truffle_run_test
|
||||
local compile_only_presets="$5"
|
||||
local compile_fn="$6"
|
||||
local test_fn="$7"
|
||||
local extra_settings="$8"
|
||||
local extra_optimizer_settings="$9"
|
||||
|
||||
truffle_clean
|
||||
force_truffle_compiler_settings "$config_file" "$binary_type" "$solc_path" "$preset"
|
||||
force_truffle_compiler_settings "$config_file" "$binary_type" "$solc_path" "$preset" "$CURRENT_EVM_VERSION" "$extra_settings" "$extra_optimizer_settings"
|
||||
compile_and_run_test compile_fn test_fn truffle_verify_compiler_version "$preset" "$compile_only_presets"
|
||||
}
|
||||
|
||||
@ -520,9 +535,11 @@ function hardhat_run_test
|
||||
local compile_fn="$4"
|
||||
local test_fn="$5"
|
||||
local config_var_name="$6"
|
||||
local extra_settings="$7"
|
||||
local extra_optimizer_settings="$8"
|
||||
|
||||
hardhat_clean
|
||||
force_hardhat_compiler_settings "$config_file" "$preset" "$config_var_name"
|
||||
force_hardhat_compiler_settings "$config_file" "$preset" "$config_var_name" "$CURRENT_EVM_VERSION" "$extra_settings" "$extra_optimizer_settings"
|
||||
compile_and_run_test compile_fn test_fn hardhat_verify_compiler_version "$preset" "$compile_only_presets"
|
||||
}
|
||||
|
||||
|
@ -42,9 +42,9 @@ function elementfi_test
|
||||
local config_file="hardhat.config.ts"
|
||||
local config_var=config
|
||||
|
||||
local compile_only_presets=()
|
||||
local settings_presets=(
|
||||
"${compile_only_presets[@]}"
|
||||
local compile_only_presets=(
|
||||
# ElementFi's test suite is hard-coded for mainnet forked via alchemy.io.
|
||||
# Locally we can only compile.
|
||||
#ir-no-optimize # Compilation fails with "YulException: Variable var_amount_9311 is 10 slot(s) too deep inside the stack."
|
||||
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_amount_9311 is 10 slot(s) too deep inside the stack."
|
||||
ir-optimize-evm+yul
|
||||
@ -52,6 +52,9 @@ function elementfi_test
|
||||
legacy-optimize-evm-only
|
||||
legacy-optimize-evm+yul
|
||||
)
|
||||
local settings_presets=(
|
||||
"${compile_only_presets[@]}"
|
||||
)
|
||||
|
||||
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
|
||||
print_presets_or_exit "$SELECTED_PRESETS"
|
||||
|
@ -65,6 +65,10 @@ function ens_test
|
||||
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")"
|
||||
yarn install
|
||||
|
||||
# With ethers.js 5.6.2 many tests for revert messages fail.
|
||||
# TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved.
|
||||
yarn add ethers@5.6.1
|
||||
|
||||
replace_version_pragmas
|
||||
neutralize_packaged_contracts
|
||||
|
||||
|
@ -68,6 +68,10 @@ function euler_test
|
||||
force_hardhat_unlimited_contract_size "$config_file"
|
||||
npm install
|
||||
|
||||
# With ethers.js 5.6.2 many tests for revert messages fail.
|
||||
# TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved.
|
||||
npm install ethers@5.6.1
|
||||
|
||||
replace_version_pragmas
|
||||
neutralize_packaged_contracts
|
||||
|
||||
|
@ -81,6 +81,10 @@ function gnosis_safe_test
|
||||
npm install
|
||||
npm install hardhat-gas-reporter
|
||||
|
||||
# With ethers.js 5.6.2 many tests for revert messages fail.
|
||||
# TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved.
|
||||
npm install ethers@5.6.1
|
||||
|
||||
replace_version_pragmas
|
||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc/dist"
|
||||
|
||||
|
@ -66,10 +66,9 @@ 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
|
||||
# With ethers.js 5.6.2 many tests for revert messages fail.
|
||||
# TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved.
|
||||
yarn add ethers@5.6.1
|
||||
|
||||
replace_version_pragmas
|
||||
|
||||
|
@ -77,6 +77,10 @@ function uniswap_test
|
||||
yarn install
|
||||
yarn add hardhat-gas-reporter
|
||||
|
||||
# With ethers.js 5.6.2 many tests for revert messages fail.
|
||||
# TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved.
|
||||
yarn add ethers@5.6.1
|
||||
|
||||
replace_version_pragmas
|
||||
|
||||
for preset in $SELECTED_PRESETS; do
|
||||
|
@ -70,6 +70,8 @@ function zeppelin_test
|
||||
# In some cases Hardhat does not detect revert reasons properly via IR.
|
||||
# TODO: Remove this when https://github.com/NomicFoundation/hardhat/issues/2453 gets fixed.
|
||||
sed -i "s|it(\('reverts if the current value is 0'\)|it.skip(\1|g" test/utils/Counters.test.js
|
||||
# TODO: Remove this when https://github.com/NomicFoundation/hardhat/issues/2115 gets fixed.
|
||||
sed -i "s|describe\(('Polygon-Child'\)|describe.skip\1|g" test/crosschain/CrossChainEnabled.test.js
|
||||
|
||||
neutralize_package_json_hooks
|
||||
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||
|
@ -56,7 +56,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
{
|
||||
map<string, unsigned> indices = {
|
||||
{ "root.asm", 0 },
|
||||
{ "sub.asm", 1 }
|
||||
{ "sub.asm", 1 },
|
||||
{ "verbatim.asm", 2 }
|
||||
};
|
||||
Assembly _assembly{false, {}};
|
||||
auto root_asm = make_shared<string>("root.asm");
|
||||
@ -65,11 +66,22 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
Assembly _subAsm{false, {}};
|
||||
auto sub_asm = make_shared<string>("sub.asm");
|
||||
_subAsm.setSourceLocation({6, 8, sub_asm});
|
||||
|
||||
Assembly _verbatimAsm(true, "");
|
||||
auto verbatim_asm = make_shared<string>("verbatim.asm");
|
||||
_verbatimAsm.setSourceLocation({8, 18, verbatim_asm});
|
||||
|
||||
// PushImmutable
|
||||
_subAsm.appendImmutable("someImmutable");
|
||||
_subAsm.append(AssemblyItem(PushTag, 0));
|
||||
_subAsm.append(Instruction::INVALID);
|
||||
shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm);
|
||||
|
||||
_verbatimAsm.appendVerbatim({0xff,0xff}, 0, 0);
|
||||
_verbatimAsm.appendVerbatim({0x74, 0x65, 0x73, 0x74}, 0, 1);
|
||||
_verbatimAsm.append(Instruction::MSTORE);
|
||||
shared_ptr<Assembly> _verbatimAsmPtr = make_shared<Assembly>(_verbatimAsm);
|
||||
|
||||
// Tag
|
||||
auto tag = _assembly.newTag();
|
||||
_assembly.append(tag);
|
||||
@ -77,7 +89,10 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
_assembly.append(u256(1));
|
||||
_assembly.append(u256(2));
|
||||
// Push
|
||||
_assembly.append(Instruction::KECCAK256);
|
||||
auto keccak256 = AssemblyItem(Instruction::KECCAK256);
|
||||
_assembly.m_currentModifierDepth = 1;
|
||||
_assembly.append(keccak256);
|
||||
_assembly.m_currentModifierDepth = 0;
|
||||
// PushProgramSize
|
||||
_assembly.appendProgramSize();
|
||||
// PushLibraryAddress
|
||||
@ -90,6 +105,10 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
auto sub = _assembly.appendSubroutine(_subAsmPtr);
|
||||
// PushSub
|
||||
_assembly.pushSubroutineOffset(static_cast<size_t>(sub.data()));
|
||||
// PushSubSize
|
||||
auto verbatim_sub = _assembly.appendSubroutine(_verbatimAsmPtr);
|
||||
// PushSub
|
||||
_assembly.pushSubroutineOffset(static_cast<size_t>(verbatim_sub.data()));
|
||||
// PushDeployTimeAddress
|
||||
_assembly.append(PushDeployTimeAddress);
|
||||
// AssignImmutable.
|
||||
@ -102,16 +121,21 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
_assembly.appendToAuxiliaryData(bytes{0x42, 0x66});
|
||||
_assembly.appendToAuxiliaryData(bytes{0xee, 0xaa});
|
||||
|
||||
_assembly.m_currentModifierDepth = 2;
|
||||
_assembly.appendJump(tag);
|
||||
_assembly.m_currentModifierDepth = 0;
|
||||
|
||||
checkCompilation(_assembly);
|
||||
|
||||
BOOST_CHECK_EQUAL(
|
||||
_assembly.assemble().toHex(),
|
||||
"5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__"
|
||||
"6000566067602260457300000000000000000000000000000000000000005050"
|
||||
"5b6001600220607f73__$bf005014d9d0f534b8fcb268bd84c491a2$__"
|
||||
"60005660776024604c600760707300000000000000000000000000000000000000005050"
|
||||
"600260010152"
|
||||
"00fe"
|
||||
"006000"
|
||||
"56fe"
|
||||
"7f0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"fe010203044266eeaa"
|
||||
"6000feffff7465737452010203044266eeaa"
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
_assembly.assemblyString(),
|
||||
@ -124,30 +148,40 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
" data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b\n"
|
||||
" dataSize(sub_0)\n"
|
||||
" dataOffset(sub_0)\n"
|
||||
" dataSize(sub_1)\n"
|
||||
" dataOffset(sub_1)\n"
|
||||
" deployTimeAddress()\n"
|
||||
" assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n"
|
||||
" 0x02\n"
|
||||
" assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
|
||||
" stop\n"
|
||||
" jump(tag_1)\n"
|
||||
"stop\n"
|
||||
"data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b 01020304\n"
|
||||
"\n"
|
||||
"sub_0: assembly {\n"
|
||||
" /* \"sub.asm\":6:8 */\n"
|
||||
" immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
|
||||
" tag_0\n"
|
||||
" invalid\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"sub_1: assembly {\n"
|
||||
" /* \"verbatim.asm\":8:18 */\n"
|
||||
" verbatimbytecode_ffff\n"
|
||||
" verbatimbytecode_74657374\n"
|
||||
" mstore\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"auxdata: 0x4266eeaa\n"
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
|
||||
string json{
|
||||
"{\".auxdata\":\"4266eeaa\",\".code\":["
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"tag\",\"source\":0,\"value\":\"1\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\",\"source\":0},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"1\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\",\"source\":0},"
|
||||
"{\"begin\":1,\"end\":3,\"modifierDepth\":1,\"name\":\"KECCAK256\",\"source\":0},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\",\"source\":0},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"source\":0,\"value\":\"someLibrary\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"},"
|
||||
@ -155,16 +189,28 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"source\":0,\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000001\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000001\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"},"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}"
|
||||
"{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0},"
|
||||
"{\"begin\":1,\"end\":3,\"modifierDepth\":2,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"},{\"begin\":1,\"end\":3,\"modifierDepth\":2,\"name\":\"JUMP\",\"source\":0}"
|
||||
"],\".data\":{\"0\":{\".code\":["
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"},"
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSH [ErrorTag]\",\"source\":1},"
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}"
|
||||
"]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}"
|
||||
);
|
||||
"]},"
|
||||
"\"1\":{\".code\":["
|
||||
"{\"begin\":8,\"end\":18,\"name\":\"VERBATIM\",\"source\":2,\"value\":\"ffff\"},"
|
||||
"{\"begin\":8,\"end\":18,\"name\":\"VERBATIM\",\"source\":2,\"value\":\"74657374\"},"
|
||||
"{\"begin\":8,\"end\":18,\"name\":\"MSTORE\",\"source\":2}"
|
||||
"]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"},\"sourceList\":[\"root.asm\",\"sub.asm\",\"verbatim.asm\"]}"
|
||||
};
|
||||
Json::Value jsonValue;
|
||||
BOOST_CHECK(util::jsonParseStrict(json, jsonValue));
|
||||
BOOST_CHECK_EQUAL(util::jsonCompactPrint(_assembly.assemblyJSON(indices)), util::jsonCompactPrint(jsonValue));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
|
||||
@ -343,7 +389,7 @@ BOOST_AUTO_TEST_CASE(immutable)
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"},"
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someOtherImmutable\"},"
|
||||
"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}"
|
||||
"]}}}"
|
||||
"]}},\"sourceList\":[\"root.asm\",\"sub.asm\"]}"
|
||||
);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user