diff --git a/.circleci/config.yml b/.circleci/config.yml index bcfc99cd7..d42021373 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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 diff --git a/.gitignore b/.gitignore index 9322b0c81..0ec3f7ace 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,7 @@ CMakeLists.txt.user # place to put local temporary files tmp + +# OS specific local files +.DS_Store +Thumbs.db diff --git a/Changelog.md b/Changelog.md index 6badda517..6c7f30d21 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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``. diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index ffa825446..0ddeb452a 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -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( - $<$:-Wextra-semi> - ) + check_cxx_compiler_flag(-Wextra-semi WEXTRA_SEMI) + if(WEXTRA_SEMI) + add_compile_options($<$:-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) diff --git a/docs/assembly.rst b/docs/assembly.rst index 0876229b1..70c4a458d 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -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 diff --git a/docs/contracts/abstract-contracts.rst b/docs/contracts/abstract-contracts.rst index 34792f58c..fb1beb2da 100644 --- a/docs/contracts/abstract-contracts.rst +++ b/docs/contracts/abstract-contracts.rst @@ -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 diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 54817837c..a33a36d27 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -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 diff --git a/docs/contributing.rst b/docs/contributing.rst index ba5a0a0f8..0c2ee9124 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -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 diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 0ce7d21e3..de3b58485 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -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) )) ))))); diff --git a/scripts/docs.sh b/docs/docs.sh similarity index 94% rename from scripts/docs.sh rename to docs/docs.sh index 30bb96f61..f2c566761 100755 --- a/scripts/docs.sh +++ b/docs/docs.sh @@ -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 .. diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 7adbfaedb..9b2e5fceb 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -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 diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index 1c94b20ab..c59d6e845 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -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 diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 8f1009407..cc3f17dd3 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -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. diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 6d7b97cc4..43e06ef9b 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -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 const& _sourceIndices) const +Json::Value Assembly::assemblyJSON(map 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(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(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(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 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; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 11bc16662..592119f17 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -39,6 +39,7 @@ #include #include #include +#include namespace solidity::evmasm { @@ -147,7 +148,8 @@ public: /// Create a JSON representation of the assembly. Json::Value assemblyJSON( - std::map const& _sourceIndices = std::map() + std::map const& _sourceIndices = std::map(), + 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; }; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 984058765..a36e0ddb1 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -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 AssemblyItem::splitForeignPushTag() const return make_pair(subId, tag); } +pair 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, ""); diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 796a792e8..4aef82d8b 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -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(_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 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) diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 4e4a2c9d7..334903c92 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -146,6 +146,35 @@ struct OpStop: SimplePeepholeOptimizerMethod } }; +struct OpReturnRevert: SimplePeepholeOptimizerMethod +{ + static bool applySimple( + AssemblyItem const& _op, + AssemblyItem const& _push, + AssemblyItem const& _pushOrDup, + AssemblyItem const& _returnRevert, + std::back_insert_iterator _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 { static size_t applySimple(AssemblyItem const& _s1, AssemblyItem const& _s2, std::back_insert_iterator) @@ -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() ); diff --git a/liblangutil/Exceptions.h b/liblangutil/Exceptions.h index 10fca8029..fb6f23cf1 100644 --- a/liblangutil/Exceptions.h +++ b/liblangutil/Exceptions.h @@ -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" \ ) diff --git a/libsmtutil/Exceptions.h b/libsmtutil/Exceptions.h index fd144ca72..2a32c4fcd 100644 --- a/libsmtutil/Exceptions.h +++ b/libsmtutil/Exceptions.h @@ -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" \ ) diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index 4fb210435..54a7354e2 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -297,7 +297,7 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) else if (optional 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, diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c519f7bfe..1c749b103 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -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(type(*arguments.front())); - - if (!functionPointerType) + FunctionType const* externalFunctionType = nullptr; + if (auto const functionPointerType = dynamic_cast(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> 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()) ); diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 4338cc64f..f75f5bc15 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -190,9 +190,9 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair #include +#include #include #include @@ -58,7 +59,7 @@ public: std::map _sourceIndices = std::map() ); /// 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 Json::Value toJson(std::vector> const& _nodes) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index cb62cc292..7c76f8344 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include @@ -95,9 +95,9 @@ pair 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() ); diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 7cc978d28..8c5dd647e 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -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(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(memberExpr)) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index de5bea137..6560b0be5 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -62,7 +62,7 @@ #include #include #include -#include +#include #include #include @@ -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); } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 31400ccad..9ca8de3ea 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -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() : diff --git a/libsolidity/lsp/LanguageServer.cpp b/libsolidity/lsp/LanguageServer.cpp index 671476ab0..8a42ca104 100644 --- a/libsolidity/lsp/LanguageServer.cpp +++ b/libsolidity/lsp/LanguageServer.cpp @@ -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(); diff --git a/libsolidity/lsp/LanguageServer.h b/libsolidity/lsp/LanguageServer.h index d2ad09367..e827f6259 100644 --- a/libsolidity/lsp/LanguageServer.h +++ b/libsolidity/lsp/LanguageServer.h @@ -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); diff --git a/libsolidity/lsp/Transport.cpp b/libsolidity/lsp/Transport.cpp index fcd3c8249..b82b34ec4 100644 --- a/libsolidity/lsp/Transport.cpp +++ b/libsolidity/lsp/Transport.cpp @@ -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()); diff --git a/libsolidity/lsp/Transport.h b/libsolidity/lsp/Transport.h index 82fe43909..d84edf49d 100644 --- a/libsolidity/lsp/Transport.h +++ b/libsolidity/lsp/Transport.h @@ -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; }; /** diff --git a/libsolutil/Assertions.h b/libsolutil/Assertions.h index 81823e040..4924522d6 100644 --- a/libsolutil/Assertions.h +++ b/libsolutil/Assertions.h @@ -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") } diff --git a/libsolutil/Exceptions.h b/libsolutil/Exceptions.h index 66b2442ef..8ac7a5fae 100644 --- a/libsolutil/Exceptions.h +++ b/libsolutil/Exceptions.h @@ -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__) \ diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 806f094fe..cc7033a0b 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -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 diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h index 1ad7785e9..45c681a5d 100644 --- a/libyul/Exceptions.h +++ b/libyul/Exceptions.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" \ ) diff --git a/libyul/AssemblyStack.cpp b/libyul/YulStack.cpp similarity index 89% rename from libyul/AssemblyStack.cpp rename to libyul/YulStack.cpp index 3fefb9caf..3bca7288c 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/YulStack.cpp @@ -21,7 +21,7 @@ */ -#include +#include #include #include @@ -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(); @@ -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 -AssemblyStack::assembleWithDeployed(optional _deployName) const +YulStack::assembleWithDeployed(optional _deployName) const { auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName); yulAssert(creationAssembly, ""); @@ -277,7 +277,7 @@ AssemblyStack::assembleWithDeployed(optional _deployName) const } std::pair, std::shared_ptr> -AssemblyStack::assembleEVMWithDeployed(optional _deployName) const +YulStack::assembleEVMWithDeployed(optional _deployName) const { yulAssert(m_analysisSuccessful, ""); yulAssert(m_parserResult, ""); @@ -317,7 +317,7 @@ AssemblyStack::assembleEVMWithDeployed(optional _deployName) const return {make_shared(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 AssemblyStack::parserResult() const +shared_ptr YulStack::parserResult() const { yulAssert(m_analysisSuccessful, "Analysis was not successful."); yulAssert(m_parserResult, ""); diff --git a/libyul/AssemblyStack.h b/libyul/YulStack.h similarity index 97% rename from libyul/AssemblyStack.h rename to libyul/YulStack.h index 950265ca4..77d0025f1 100644 --- a/libyul/AssemblyStack.h +++ b/libyul/YulStack.h @@ -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, diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index eca509f43..c2a82d494 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -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); } diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index 8c9dc14ff..34dd5f9b3 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -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, diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index 6e00fcd8a..7dff1df80 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -37,16 +37,6 @@ void Rematerialiser::run(Dialect const& _dialect, Block& _ast, set _v Rematerialiser{_dialect, _ast, std::move(_varsToAlwaysRematerialize), _onlySelectedVariables}(_ast); } -void Rematerialiser::run( - Dialect const& _dialect, - FunctionDefinition& _function, - set _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 _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(_e)) diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index d1ebb1595..592f79d1b 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -53,12 +53,6 @@ public: std::set _varsToAlwaysRematerialize = {}, bool _onlySelectedVariables = false ); - static void run( - Dialect const& _dialect, - FunctionDefinition& _function, - std::set _varsToAlwaysRematerialize = {}, - bool _onlySelectedVariables = false - ); protected: Rematerialiser( @@ -67,12 +61,6 @@ protected: std::set _varsToAlwaysRematerialize = {}, bool _onlySelectedVariables = false ); - Rematerialiser( - Dialect const& _dialect, - FunctionDefinition& _function, - std::set _varsToAlwaysRematerialize = {}, - bool _onlySelectedVariables = false - ); using DataFlowAnalyzer::operator(); diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index 8b06d6625..b3402e4d7 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -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>>> candidates() + map>> candidates() { - map>>> cand; - for (auto const& candidate: m_candidates) + map>> 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 const* ref = references(candidate); - cand[*cost * numRef].emplace_back(candidate, ref ? move(*ref) : set{}); + 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 m_candidates; + YulString m_currentFunctionName = {}; + + /// All candidate variables by function name, in order of occurrence. + vector> m_candidates; /// Candidate variables and the code cost of their value. map m_expressionCodeCost; /// Number of references to each candidate variable. @@ -132,86 +143,95 @@ public: /// Selects at most @a _numVariables among @a _candidates. set chooseVarsToEliminate( - map>>> const& _candidates, + map> const& _candidates, size_t _numVariables ) { set 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 void eliminateVariables( Dialect const& _dialect, - ASTNode& _node, - size_t _numVariables, + Block& _ast, + map 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>> candidates = selector.candidates(); + + set varsToEliminate; + for (auto const& [functionName, numVariables]: _numVariables) + { + yulAssert(numVariables > 0); + varsToEliminate += chooseVarsToEliminate(candidates[functionName], static_cast(numVariables)); + } + + Rematerialiser::run(_dialect, _ast, move(varsToEliminate)); + // Do not remove functions. + set allFunctions = NameCollector{_ast, NameCollector::OnlyFunctions}.names(); + UnusedPruner::runUntilStabilised(_dialect, _ast, _allowMSizeOptimization, nullptr, allFunctions); } -void eliminateVariables( +void eliminateVariablesOptimizedCodegen( Dialect const& _dialect, - Block& _block, - vector const& _unreachables, + Block& _ast, + map> 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 candidates; - for (auto [cost, candidatesWithCost]: selector.candidates()) - for (auto candidate: candidatesWithCost) - candidates[get<0>(candidate)] = cost; + selector(_ast); + + map candidates; + for (auto const& [functionName, candidatesInFunction]: selector.candidates()) + for (auto [cost, candidatesWithCost]: candidatesInFunction) + for (auto candidate: candidatesWithCost) + candidates[candidate] = cost; set varsToEliminate; // TODO: this currently ignores the fact that variables may reference other variables we want to eliminate. - for (auto const& unreachable: _unreachables) - { - map> 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> 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 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 = ControlFlowGraphBuilder::build(analysisInfo, _dialect, *_object.code); - Block& mainBlock = std::get(_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(_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 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(_object.code->statements.at(0)), - static_cast(stackSurplus.at({})), - allowMSizeOptimzation - ); - } - - for (size_t i = 1; i < _object.code->statements.size(); ++i) - { - auto& fun = std::get(_object.code->statements[i]); - if (!stackSurplus.count(fun.name)) - continue; - - yulAssert(stackSurplus.at(fun.name) > 0, "Invalid surplus value."); - eliminateVariables( - _dialect, - fun, - static_cast(stackSurplus.at(fun.name)), - allowMSizeOptimzation - ); - } + eliminateVariables( + _dialect, + *_object.code, + stackSurplus, + allowMSizeOptimzation + ); } return false; } diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index ca8f94239..0b80c42cd 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -93,26 +93,31 @@ void StructuralSimplifier::operator()(Block& _block) void StructuralSimplifier::simplify(std::vector& _statements) { - util::GenericVisitor visitor{ - util::VisitorFallback{}, - [&](If& _ifStmt) -> OptionalStatements { - if (expressionAlwaysTrue(*_ifStmt.condition)) - return {std::move(_ifStmt.body.statements)}; - else if (expressionAlwaysFalse(*_ifStmt.condition)) - return {vector{}}; - return {}; - }, - [&](Switch& _switchStmt) -> OptionalStatements { - if (std::optional 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{}}; + return {}; }; + auto switchLambda = [&](Switch& _switchStmt) -> OptionalStatements + { + if (std::optional 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{}, ifLambda, switchLambda, forLoopLambda}; util::iterateReplacing( _statements, diff --git a/scripts/common.sh b/scripts/common.sh index 7cb96ed02..1ea0e1fc1 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -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}\")}" +} diff --git a/scripts/externalTests/benchmark_diff.py b/scripts/externalTests/benchmark_diff.py new file mode 100755 index 000000000..234f85ac0 --- /dev/null +++ b/scripts/externalTests/benchmark_diff.py @@ -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()) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 2495bf2f4..40de51b52 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -41,7 +41,7 @@ #include #include -#include +#include #include #include @@ -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 assemblyStacks; + map 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()); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 951731825..b7ab158f9 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -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(); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 8ae64032c..1cab3358e 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -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)) diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 9723d5e06..108a16cd2 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -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 diff --git a/test/Common.cpp b/test/Common.cpp index a702cddd6..8cdf0fed1 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -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()) { diff --git a/test/Common.h b/test/Common.h index e7e211d9e..28862348f 100644 --- a/test/Common.h +++ b/test/Common.h @@ -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; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 1cc512c32..b7a4c80f0 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -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(); - 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; diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index c8fef6a31..86900b56e 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -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 '/^======= /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 '/^======= /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 '/^======= /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 '/^======= /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 ) diff --git a/test/cmdlineTests/asm_json/args b/test/cmdlineTests/asm_json/args index 8f4713692..dd9e77b16 100644 --- a/test/cmdlineTests/asm_json/args +++ b/test/cmdlineTests/asm_json/args @@ -1 +1 @@ ---asm-json +--asm-json --pretty-json diff --git a/test/cmdlineTests/asm_json/output b/test/cmdlineTests/asm_json/output index 26ab87147..000ad1b75 100644 --- a/test/cmdlineTests/asm_json/output +++ b/test/cmdlineTests/asm_json/output @@ -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" + ] } diff --git a/test/cmdlineTests/asm_json_no_pretty_print/args b/test/cmdlineTests/asm_json_no_pretty_print/args new file mode 100644 index 000000000..8f4713692 --- /dev/null +++ b/test/cmdlineTests/asm_json_no_pretty_print/args @@ -0,0 +1 @@ +--asm-json diff --git a/test/cmdlineTests/asm_json_no_pretty_print/input.sol b/test/cmdlineTests/asm_json_no_pretty_print/input.sol new file mode 100644 index 000000000..da3a08d7f --- /dev/null +++ b/test/cmdlineTests/asm_json_no_pretty_print/input.sol @@ -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); + } +} diff --git a/test/cmdlineTests/asm_json_no_pretty_print/output b/test/cmdlineTests/asm_json_no_pretty_print/output new file mode 100644 index 000000000..232d0bdea --- /dev/null +++ b/test/cmdlineTests/asm_json_no_pretty_print/output @@ -0,0 +1,4 @@ + +======= asm_json_no_pretty_print/input.sol:C ======= +EVM assembly: +{".code":[{"begin":60,"end":160,"name":"PUSH","source":0,"value":"80"},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"40"},{"begin":60,"end":160,"name":"MSTORE","source":0},{"begin":60,"end":160,"name":"CALLVALUE","source":0},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"ISZERO","source":0},{"begin":60,"end":160,"name":"PUSH [tag]","source":0,"value":"1"},{"begin":60,"end":160,"name":"JUMPI","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"REVERT","source":0},{"begin":60,"end":160,"name":"tag","source":0,"value":"1"},{"begin":60,"end":160,"name":"JUMPDEST","source":0},{"begin":60,"end":160,"name":"POP","source":0},{"begin":60,"end":160,"name":"PUSH #[$]","source":0,"value":"0000000000000000000000000000000000000000000000000000000000000000"},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"PUSH [$]","source":0,"value":"0000000000000000000000000000000000000000000000000000000000000000"},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"CODECOPY","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"RETURN","source":0}],".data":{"0":{".auxdata":"",".code":[{"begin":60,"end":160,"name":"PUSH","source":0,"value":"80"},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"40"},{"begin":60,"end":160,"name":"MSTORE","source":0},{"begin":60,"end":160,"name":"CALLVALUE","source":0},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"ISZERO","source":0},{"begin":60,"end":160,"name":"PUSH [tag]","source":0,"value":"1"},{"begin":60,"end":160,"name":"JUMPI","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"REVERT","source":0},{"begin":60,"end":160,"name":"tag","source":0,"value":"1"},{"begin":60,"end":160,"name":"JUMPDEST","source":0},{"begin":60,"end":160,"name":"POP","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"4"},{"begin":60,"end":160,"name":"CALLDATASIZE","source":0},{"begin":60,"end":160,"name":"LT","source":0},{"begin":60,"end":160,"name":"PUSH [tag]","source":0,"value":"2"},{"begin":60,"end":160,"name":"JUMPI","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"CALLDATALOAD","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"E0"},{"begin":60,"end":160,"name":"SHR","source":0},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"B3DE648B"},{"begin":60,"end":160,"name":"EQ","source":0},{"begin":60,"end":160,"name":"PUSH [tag]","source":0,"value":"3"},{"begin":60,"end":160,"name":"JUMPI","source":0},{"begin":60,"end":160,"name":"tag","source":0,"value":"2"},{"begin":60,"end":160,"name":"JUMPDEST","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"REVERT","source":0},{"begin":77,"end":158,"name":"tag","source":0,"value":"3"},{"begin":77,"end":158,"name":"JUMPDEST","source":0},{"begin":77,"end":158,"name":"PUSH [tag]","source":0,"value":"4"},{"begin":77,"end":158,"name":"PUSH","source":0,"value":"4"},{"begin":77,"end":158,"name":"DUP1","source":0},{"begin":77,"end":158,"name":"CALLDATASIZE","source":0},{"begin":77,"end":158,"name":"SUB","source":0},{"begin":77,"end":158,"name":"DUP2","source":0},{"begin":77,"end":158,"name":"ADD","source":0},{"begin":77,"end":158,"name":"SWAP1","source":0},{"begin":77,"end":158,"name":"PUSH [tag]","source":0,"value":"5"},{"begin":77,"end":158,"name":"SWAP2","source":0},{"begin":77,"end":158,"name":"SWAP1","source":0},{"begin":77,"end":158,"name":"PUSH [tag]","source":0,"value":"6"},{"begin":77,"end":158,"jumpType":"[in]","name":"JUMP","source":0},{"begin":77,"end":158,"name":"tag","source":0,"value":"5"},{"begin":77,"end":158,"name":"JUMPDEST","source":0},{"begin":77,"end":158,"name":"PUSH [tag]","source":0,"value":"7"},{"begin":77,"end":158,"jumpType":"[in]","name":"JUMP","source":0},{"begin":77,"end":158,"name":"tag","source":0,"value":"4"},{"begin":77,"end":158,"name":"JUMPDEST","source":0},{"begin":77,"end":158,"name":"STOP","source":0},{"begin":77,"end":158,"name":"tag","source":0,"value":"7"},{"begin":77,"end":158,"name":"JUMPDEST","source":0},{"begin":123,"end":125,"name":"PUSH","source":0,"value":"2A"},{"begin":118,"end":125,"name":"DUP2","source":0},{"begin":118,"end":125,"name":"PUSH [tag]","source":0,"value":"9"},{"begin":118,"end":125,"name":"SWAP2","source":0},{"begin":118,"end":125,"name":"SWAP1","source":0},{"begin":118,"end":125,"name":"PUSH [tag]","source":0,"value":"10"},{"begin":118,"end":125,"jumpType":"[in]","name":"JUMP","source":0},{"begin":118,"end":125,"name":"tag","source":0,"value":"9"},{"begin":118,"end":125,"name":"JUMPDEST","source":0},{"begin":118,"end":125,"name":"SWAP1","source":0},{"begin":118,"end":125,"name":"POP","source":0},{"begin":147,"end":150,"name":"PUSH","source":0,"value":"64"},{"begin":143,"end":144,"name":"DUP2","source":0},{"begin":143,"end":150,"name":"GT","source":0},{"begin":135,"end":151,"name":"PUSH [tag]","source":0,"value":"11"},{"begin":135,"end":151,"name":"JUMPI","source":0},{"begin":135,"end":151,"name":"PUSH","source":0,"value":"0"},{"begin":135,"end":151,"name":"DUP1","source":0},{"begin":135,"end":151,"name":"REVERT","source":0},{"begin":135,"end":151,"name":"tag","source":0,"value":"11"},{"begin":135,"end":151,"name":"JUMPDEST","source":0},{"begin":77,"end":158,"name":"POP","source":0},{"begin":77,"end":158,"jumpType":"[out]","name":"JUMP","source":0},{"begin":88,"end":205,"name":"tag","source":1,"value":"13"},{"begin":88,"end":205,"name":"JUMPDEST","source":1},{"begin":197,"end":198,"name":"PUSH","source":1,"value":"0"},{"begin":194,"end":195,"name":"DUP1","source":1},{"begin":187,"end":199,"name":"REVERT","source":1},{"begin":334,"end":411,"name":"tag","source":1,"value":"15"},{"begin":334,"end":411,"name":"JUMPDEST","source":1},{"begin":371,"end":378,"name":"PUSH","source":1,"value":"0"},{"begin":400,"end":405,"name":"DUP2","source":1},{"begin":389,"end":405,"name":"SWAP1","source":1},{"begin":389,"end":405,"name":"POP","source":1},{"begin":334,"end":411,"name":"SWAP2","source":1},{"begin":334,"end":411,"name":"SWAP1","source":1},{"begin":334,"end":411,"name":"POP","source":1},{"begin":334,"end":411,"jumpType":"[out]","name":"JUMP","source":1},{"begin":417,"end":539,"name":"tag","source":1,"value":"16"},{"begin":417,"end":539,"name":"JUMPDEST","source":1},{"begin":490,"end":514,"name":"PUSH [tag]","source":1,"value":"25"},{"begin":508,"end":513,"name":"DUP2","source":1},{"begin":490,"end":514,"name":"PUSH [tag]","source":1,"value":"15"},{"begin":490,"end":514,"jumpType":"[in]","name":"JUMP","source":1},{"begin":490,"end":514,"name":"tag","source":1,"value":"25"},{"begin":490,"end":514,"name":"JUMPDEST","source":1},{"begin":483,"end":488,"name":"DUP2","source":1},{"begin":480,"end":515,"name":"EQ","source":1},{"begin":470,"end":533,"name":"PUSH [tag]","source":1,"value":"26"},{"begin":470,"end":533,"name":"JUMPI","source":1},{"begin":529,"end":530,"name":"PUSH","source":1,"value":"0"},{"begin":526,"end":527,"name":"DUP1","source":1},{"begin":519,"end":531,"name":"REVERT","source":1},{"begin":470,"end":533,"name":"tag","source":1,"value":"26"},{"begin":470,"end":533,"name":"JUMPDEST","source":1},{"begin":417,"end":539,"name":"POP","source":1},{"begin":417,"end":539,"jumpType":"[out]","name":"JUMP","source":1},{"begin":545,"end":684,"name":"tag","source":1,"value":"17"},{"begin":545,"end":684,"name":"JUMPDEST","source":1},{"begin":591,"end":596,"name":"PUSH","source":1,"value":"0"},{"begin":629,"end":635,"name":"DUP2","source":1},{"begin":616,"end":636,"name":"CALLDATALOAD","source":1},{"begin":607,"end":636,"name":"SWAP1","source":1},{"begin":607,"end":636,"name":"POP","source":1},{"begin":645,"end":678,"name":"PUSH [tag]","source":1,"value":"28"},{"begin":672,"end":677,"name":"DUP2","source":1},{"begin":645,"end":678,"name":"PUSH [tag]","source":1,"value":"16"},{"begin":645,"end":678,"jumpType":"[in]","name":"JUMP","source":1},{"begin":645,"end":678,"name":"tag","source":1,"value":"28"},{"begin":645,"end":678,"name":"JUMPDEST","source":1},{"begin":545,"end":684,"name":"SWAP3","source":1},{"begin":545,"end":684,"name":"SWAP2","source":1},{"begin":545,"end":684,"name":"POP","source":1},{"begin":545,"end":684,"name":"POP","source":1},{"begin":545,"end":684,"jumpType":"[out]","name":"JUMP","source":1},{"begin":690,"end":1019,"name":"tag","source":1,"value":"6"},{"begin":690,"end":1019,"name":"JUMPDEST","source":1},{"begin":749,"end":755,"name":"PUSH","source":1,"value":"0"},{"begin":798,"end":800,"name":"PUSH","source":1,"value":"20"},{"begin":786,"end":795,"name":"DUP3","source":1},{"begin":777,"end":784,"name":"DUP5","source":1},{"begin":773,"end":796,"name":"SUB","source":1},{"begin":769,"end":801,"name":"SLT","source":1},{"begin":766,"end":885,"name":"ISZERO","source":1},{"begin":766,"end":885,"name":"PUSH [tag]","source":1,"value":"30"},{"begin":766,"end":885,"name":"JUMPI","source":1},{"begin":804,"end":883,"name":"PUSH [tag]","source":1,"value":"31"},{"begin":804,"end":883,"name":"PUSH [tag]","source":1,"value":"13"},{"begin":804,"end":883,"jumpType":"[in]","name":"JUMP","source":1},{"begin":804,"end":883,"name":"tag","source":1,"value":"31"},{"begin":804,"end":883,"name":"JUMPDEST","source":1},{"begin":766,"end":885,"name":"tag","source":1,"value":"30"},{"begin":766,"end":885,"name":"JUMPDEST","source":1},{"begin":924,"end":925,"name":"PUSH","source":1,"value":"0"},{"begin":949,"end":1002,"name":"PUSH [tag]","source":1,"value":"32"},{"begin":994,"end":1001,"name":"DUP5","source":1},{"begin":985,"end":991,"name":"DUP3","source":1},{"begin":974,"end":983,"name":"DUP6","source":1},{"begin":970,"end":992,"name":"ADD","source":1},{"begin":949,"end":1002,"name":"PUSH [tag]","source":1,"value":"17"},{"begin":949,"end":1002,"jumpType":"[in]","name":"JUMP","source":1},{"begin":949,"end":1002,"name":"tag","source":1,"value":"32"},{"begin":949,"end":1002,"name":"JUMPDEST","source":1},{"begin":939,"end":1002,"name":"SWAP2","source":1},{"begin":939,"end":1002,"name":"POP","source":1},{"begin":895,"end":1012,"name":"POP","source":1},{"begin":690,"end":1019,"name":"SWAP3","source":1},{"begin":690,"end":1019,"name":"SWAP2","source":1},{"begin":690,"end":1019,"name":"POP","source":1},{"begin":690,"end":1019,"name":"POP","source":1},{"begin":690,"end":1019,"jumpType":"[out]","name":"JUMP","source":1},{"begin":1025,"end":1205,"name":"tag","source":1,"value":"18"},{"begin":1025,"end":1205,"name":"JUMPDEST","source":1},{"begin":1073,"end":1150,"name":"PUSH","source":1,"value":"4E487B7100000000000000000000000000000000000000000000000000000000"},{"begin":1070,"end":1071,"name":"PUSH","source":1,"value":"0"},{"begin":1063,"end":1151,"name":"MSTORE","source":1},{"begin":1170,"end":1174,"name":"PUSH","source":1,"value":"11"},{"begin":1167,"end":1168,"name":"PUSH","source":1,"value":"4"},{"begin":1160,"end":1175,"name":"MSTORE","source":1},{"begin":1194,"end":1198,"name":"PUSH","source":1,"value":"24"},{"begin":1191,"end":1192,"name":"PUSH","source":1,"value":"0"},{"begin":1184,"end":1199,"name":"REVERT","source":1},{"begin":1211,"end":1516,"name":"tag","source":1,"value":"10"},{"begin":1211,"end":1516,"name":"JUMPDEST","source":1},{"begin":1251,"end":1254,"name":"PUSH","source":1,"value":"0"},{"begin":1270,"end":1290,"name":"PUSH [tag]","source":1,"value":"35"},{"begin":1288,"end":1289,"name":"DUP3","source":1},{"begin":1270,"end":1290,"name":"PUSH [tag]","source":1,"value":"15"},{"begin":1270,"end":1290,"jumpType":"[in]","name":"JUMP","source":1},{"begin":1270,"end":1290,"name":"tag","source":1,"value":"35"},{"begin":1270,"end":1290,"name":"JUMPDEST","source":1},{"begin":1265,"end":1290,"name":"SWAP2","source":1},{"begin":1265,"end":1290,"name":"POP","source":1},{"begin":1304,"end":1324,"name":"PUSH [tag]","source":1,"value":"36"},{"begin":1322,"end":1323,"name":"DUP4","source":1},{"begin":1304,"end":1324,"name":"PUSH [tag]","source":1,"value":"15"},{"begin":1304,"end":1324,"jumpType":"[in]","name":"JUMP","source":1},{"begin":1304,"end":1324,"name":"tag","source":1,"value":"36"},{"begin":1304,"end":1324,"name":"JUMPDEST","source":1},{"begin":1299,"end":1324,"name":"SWAP3","source":1},{"begin":1299,"end":1324,"name":"POP","source":1},{"begin":1458,"end":1459,"name":"DUP3","source":1},{"begin":1390,"end":1456,"name":"PUSH","source":1,"value":"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"},{"begin":1386,"end":1460,"name":"SUB","source":1},{"begin":1383,"end":1384,"name":"DUP3","source":1},{"begin":1380,"end":1461,"name":"GT","source":1},{"begin":1377,"end":1484,"name":"ISZERO","source":1},{"begin":1377,"end":1484,"name":"PUSH [tag]","source":1,"value":"37"},{"begin":1377,"end":1484,"name":"JUMPI","source":1},{"begin":1464,"end":1482,"name":"PUSH [tag]","source":1,"value":"38"},{"begin":1464,"end":1482,"name":"PUSH [tag]","source":1,"value":"18"},{"begin":1464,"end":1482,"jumpType":"[in]","name":"JUMP","source":1},{"begin":1464,"end":1482,"name":"tag","source":1,"value":"38"},{"begin":1464,"end":1482,"name":"JUMPDEST","source":1},{"begin":1377,"end":1484,"name":"tag","source":1,"value":"37"},{"begin":1377,"end":1484,"name":"JUMPDEST","source":1},{"begin":1508,"end":1509,"name":"DUP3","source":1},{"begin":1505,"end":1506,"name":"DUP3","source":1},{"begin":1501,"end":1510,"name":"ADD","source":1},{"begin":1494,"end":1510,"name":"SWAP1","source":1},{"begin":1494,"end":1510,"name":"POP","source":1},{"begin":1211,"end":1516,"name":"SWAP3","source":1},{"begin":1211,"end":1516,"name":"SWAP2","source":1},{"begin":1211,"end":1516,"name":"POP","source":1},{"begin":1211,"end":1516,"name":"POP","source":1},{"begin":1211,"end":1516,"jumpType":"[out]","name":"JUMP","source":1}]}},"sourceList":["asm_json_no_pretty_print/input.sol","#utility.yul"]} diff --git a/test/cmdlineTests/ast_compact_json_no_pretty_json/args b/test/cmdlineTests/ast_compact_json_no_pretty_json/args new file mode 100644 index 000000000..f60d99b44 --- /dev/null +++ b/test/cmdlineTests/ast_compact_json_no_pretty_json/args @@ -0,0 +1 @@ +--ast-compact-json --allow-paths . diff --git a/test/cmdlineTests/ast_compact_json_no_pretty_json/input.sol b/test/cmdlineTests/ast_compact_json_no_pretty_json/input.sol new file mode 100644 index 000000000..f123f6c8b --- /dev/null +++ b/test/cmdlineTests/ast_compact_json_no_pretty_json/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C {} diff --git a/test/cmdlineTests/ast_compact_json_no_pretty_json/output b/test/cmdlineTests/ast_compact_json_no_pretty_json/output new file mode 100644 index 000000000..abdc17125 --- /dev/null +++ b/test/cmdlineTests/ast_compact_json_no_pretty_json/output @@ -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"} diff --git a/test/cmdlineTests/ast_compact_json_with_base_path/args b/test/cmdlineTests/ast_compact_json_with_base_path/args index 103ae311a..a30ef24c0 100644 --- a/test/cmdlineTests/ast_compact_json_with_base_path/args +++ b/test/cmdlineTests/ast_compact_json_with_base_path/args @@ -1 +1 @@ ---ast-compact-json --base-path . --allow-paths . +--ast-compact-json --pretty-json --base-path . --allow-paths . diff --git a/test/cmdlineTests/pretty_json_combined/args b/test/cmdlineTests/combined_json_abi/args similarity index 100% rename from test/cmdlineTests/pretty_json_combined/args rename to test/cmdlineTests/combined_json_abi/args diff --git a/test/cmdlineTests/pretty_json_combined/input.sol b/test/cmdlineTests/combined_json_abi/input.sol similarity index 100% rename from test/cmdlineTests/pretty_json_combined/input.sol rename to test/cmdlineTests/combined_json_abi/input.sol diff --git a/test/cmdlineTests/pretty_json_combined/output b/test/cmdlineTests/combined_json_abi/output similarity index 70% rename from test/cmdlineTests/pretty_json_combined/output rename to test/cmdlineTests/combined_json_abi/output index 6bed3b94d..c775c6d72 100644 --- a/test/cmdlineTests/pretty_json_combined/output +++ b/test/cmdlineTests/combined_json_abi/output @@ -1,7 +1,7 @@ { "contracts": { - "pretty_json_combined/input.sol:C": + "combined_json_abi/input.sol:C": { "abi": [] } diff --git a/test/cmdlineTests/combined_json_no_pretty_print/args b/test/cmdlineTests/combined_json_no_pretty_print/args new file mode 100644 index 000000000..92b156465 --- /dev/null +++ b/test/cmdlineTests/combined_json_no_pretty_print/args @@ -0,0 +1 @@ +--combined-json abi diff --git a/test/cmdlineTests/combined_json_no_pretty_print/input.sol b/test/cmdlineTests/combined_json_no_pretty_print/input.sol new file mode 100644 index 000000000..625af5682 --- /dev/null +++ b/test/cmdlineTests/combined_json_no_pretty_print/input.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C {} diff --git a/test/cmdlineTests/combined_json_no_pretty_print/output b/test/cmdlineTests/combined_json_no_pretty_print/output new file mode 100644 index 000000000..081dc4fa4 --- /dev/null +++ b/test/cmdlineTests/combined_json_no_pretty_print/output @@ -0,0 +1 @@ +{"contracts":{"combined_json_no_pretty_print/input.sol:C":{"abi":[]}},"version": ""} diff --git a/test/cmdlineTests/combined_json_with_base_path/args b/test/cmdlineTests/combined_json_with_base_path/args index 0dec582ba..4e1f48462 100644 --- a/test/cmdlineTests/combined_json_with_base_path/args +++ b/test/cmdlineTests/combined_json_with_base_path/args @@ -1 +1 @@ ---combined-json ast --base-path . --allow-paths . +--combined-json ast --pretty-json --base-path . --allow-paths . diff --git a/test/cmdlineTests/combined_json_with_base_path/output b/test/cmdlineTests/combined_json_with_base_path/output index fe00b71a0..9acdea553 100644 --- a/test/cmdlineTests/combined_json_with_base_path/output +++ b/test/cmdlineTests/combined_json_with_base_path/output @@ -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": ""} +{ + "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": "" +} diff --git a/test/cmdlineTests/combined_json_with_devdoc/args b/test/cmdlineTests/combined_json_with_devdoc/args new file mode 100644 index 000000000..dc55cdec7 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_devdoc/args @@ -0,0 +1 @@ +--combined-json devdoc --pretty-json --allow-paths . diff --git a/test/cmdlineTests/combined_json_with_devdoc/input.sol b/test/cmdlineTests/combined_json_with_devdoc/input.sol new file mode 100644 index 000000000..6787b59b1 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_devdoc/input.sol @@ -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 {} diff --git a/test/cmdlineTests/combined_json_with_devdoc/output b/test/cmdlineTests/combined_json_with_devdoc/output new file mode 100644 index 000000000..d3df44a90 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_devdoc/output @@ -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": "" +} diff --git a/test/cmdlineTests/combined_json_with_userdoc/args b/test/cmdlineTests/combined_json_with_userdoc/args new file mode 100644 index 000000000..b7c2697aa --- /dev/null +++ b/test/cmdlineTests/combined_json_with_userdoc/args @@ -0,0 +1 @@ +--combined-json userdoc --pretty-json --allow-paths . diff --git a/test/cmdlineTests/combined_json_with_userdoc/input.sol b/test/cmdlineTests/combined_json_with_userdoc/input.sol new file mode 100644 index 000000000..c6d37c8d4 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_userdoc/input.sol @@ -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 {} diff --git a/test/cmdlineTests/combined_json_with_userdoc/output b/test/cmdlineTests/combined_json_with_userdoc/output new file mode 100644 index 000000000..650981d67 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_userdoc/output @@ -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": "" +} diff --git a/test/cmdlineTests/function_debug_info_via_yul/output b/test/cmdlineTests/function_debug_info_via_yul/output index 4e9c732b4..97125e4b9 100644 --- a/test/cmdlineTests/function_debug_info_via_yul/output +++ b/test/cmdlineTests/function_debug_info_via_yul/output @@ -13,7 +13,7 @@ }, "calldata_array_index_access_uint256_dyn_calldata": { - "entryPoint": 145, + "entryPoint": 144, "parameterSlots": 2, "returnSlots": 1 } diff --git a/test/cmdlineTests/recovery_ast_constructor/args b/test/cmdlineTests/recovery_ast_constructor/args index c7bde9942..8b7c575db 100644 --- a/test/cmdlineTests/recovery_ast_constructor/args +++ b/test/cmdlineTests/recovery_ast_constructor/args @@ -1 +1 @@ ---error-recovery --ast-compact-json --hashes +--error-recovery --ast-compact-json --pretty-json --hashes diff --git a/test/cmdlineTests/standard_json_no_pretty_print/args b/test/cmdlineTests/standard_json_no_pretty_print/args new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/standard_json_no_pretty_print/input.json b/test/cmdlineTests/standard_json_no_pretty_print/input.json new file mode 100644 index 000000000..e9dc4cf38 --- /dev/null +++ b/test/cmdlineTests/standard_json_no_pretty_print/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {}" + } + } +} diff --git a/test/cmdlineTests/standard_json_no_pretty_print/output.json b/test/cmdlineTests/standard_json_no_pretty_print/output.json new file mode 100644 index 000000000..59b90c8cc --- /dev/null +++ b/test/cmdlineTests/standard_json_no_pretty_print/output.json @@ -0,0 +1 @@ +{"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_user_defined/args b/test/cmdlineTests/storage_layout_user_defined/args index f69ac4e39..0538465fe 100644 --- a/test/cmdlineTests/storage_layout_user_defined/args +++ b/test/cmdlineTests/storage_layout_user_defined/args @@ -1 +1 @@ ---storage-layout +--storage-layout --pretty-json diff --git a/test/cmdlineTests/storage_layout_user_defined/output b/test/cmdlineTests/storage_layout_user_defined/output index 0f6890dae..f79b8413e 100644 --- a/test/cmdlineTests/storage_layout_user_defined/output +++ b/test/cmdlineTests/storage_layout_user_defined/output @@ -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" + } + } +} diff --git a/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/args b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/args new file mode 100644 index 000000000..f69ac4e39 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/args @@ -0,0 +1 @@ +--storage-layout diff --git a/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/input.sol b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/input.sol new file mode 100644 index 000000000..c80f7c736 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL v3 +pragma solidity >=0.0; + +contract C {} diff --git a/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/output b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/output new file mode 100644 index 000000000..1be4da0b6 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/output @@ -0,0 +1,4 @@ + +======= storage_layout_user_defined_no_pretty_print/input.sol:C ======= +Contract Storage Layout: +{"storage":[]} diff --git a/test/cmdlineTests/viair_subobject_optimization/output b/test/cmdlineTests/viair_subobject_optimization/output index 1686e3ef7..5d4e89480 100644 --- a/test/cmdlineTests/viair_subobject_optimization/output +++ b/test/cmdlineTests/viair_subobject_optimization/output @@ -125,7 +125,6 @@ sub_0: assembly { eq tag_4 jumpi - pop 0x00 dup1 revert diff --git a/test/compilationTests/MultiSigWallet/MultiSigWallet.sol b/test/compilationTests/MultiSigWallet/MultiSigWallet.sol index b4594344c..6f1b8a975 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWallet.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWallet.sol @@ -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 +# +# (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 diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index 9c68c8828..c510ebc92 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -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" } diff --git a/test/externalTests/elementfi.sh b/test/externalTests/elementfi.sh index b12c7dd0b..5cb1e8088 100755 --- a/test/externalTests/elementfi.sh +++ b/test/externalTests/elementfi.sh @@ -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" diff --git a/test/externalTests/ens.sh b/test/externalTests/ens.sh index e4edbf46c..bf018d0ce 100755 --- a/test/externalTests/ens.sh +++ b/test/externalTests/ens.sh @@ -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 diff --git a/test/externalTests/euler.sh b/test/externalTests/euler.sh index 39283bcd5..0d63ecd43 100755 --- a/test/externalTests/euler.sh +++ b/test/externalTests/euler.sh @@ -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 diff --git a/test/externalTests/gnosis.sh b/test/externalTests/gnosis.sh index ccfc6cf5c..b8553e4eb 100755 --- a/test/externalTests/gnosis.sh +++ b/test/externalTests/gnosis.sh @@ -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" diff --git a/test/externalTests/perpetual-pools.sh b/test/externalTests/perpetual-pools.sh index 7e4289698..1b54fc577 100755 --- a/test/externalTests/perpetual-pools.sh +++ b/test/externalTests/perpetual-pools.sh @@ -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 diff --git a/test/externalTests/uniswap.sh b/test/externalTests/uniswap.sh index 2f94c6abd..efea480b6 100755 --- a/test/externalTests/uniswap.sh +++ b/test/externalTests/uniswap.sh @@ -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 diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index ab79a8775..ccbf7595a 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -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" diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 3a3167816..680531799 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -56,7 +56,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) { map indices = { { "root.asm", 0 }, - { "sub.asm", 1 } + { "sub.asm", 1 }, + { "verbatim.asm", 2 } }; Assembly _assembly{false, {}}; auto root_asm = make_shared("root.asm"); @@ -65,11 +66,22 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) Assembly _subAsm{false, {}}; auto sub_asm = make_shared("sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); + + Assembly _verbatimAsm(true, ""); + auto verbatim_asm = make_shared("verbatim.asm"); + _verbatimAsm.setSourceLocation({8, 18, verbatim_asm}); + // PushImmutable _subAsm.appendImmutable("someImmutable"); + _subAsm.append(AssemblyItem(PushTag, 0)); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); + _verbatimAsm.appendVerbatim({0xff,0xff}, 0, 0); + _verbatimAsm.appendVerbatim({0x74, 0x65, 0x73, 0x74}, 0, 1); + _verbatimAsm.append(Instruction::MSTORE); + shared_ptr _verbatimAsmPtr = make_shared(_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(sub.data())); + // PushSubSize + auto verbatim_sub = _assembly.appendSubroutine(_verbatimAsmPtr); + // PushSub + _assembly.pushSubroutineOffset(static_cast(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\"]}" ); } diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index 14e2c2a8a..a509adde6 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -248,7 +249,7 @@ bool ASTJSONTest::runTest( for (size_t i = 0; i < m_sources.size(); i++) { ostringstream result; - ASTJsonConverter(_compiler.state(), _sourceIndices).print(result, _compiler.ast(m_sources[i].first)); + ASTJsonConverter(_compiler.state(), _sourceIndices).print(result, _compiler.ast(m_sources[i].first), JsonFormat{ JsonFormat::Pretty }); _variant.result += result.str(); if (i != m_sources.size() - 1) _variant.result += ","; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index f0e5e455f..314a0d760 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -26,7 +26,7 @@ #include -#include +#include #include #include @@ -56,11 +56,11 @@ std::optional parseAndReturnFirstError( string const& _source, bool _assemble = false, bool _allowWarnings = true, - AssemblyStack::Language _language = AssemblyStack::Language::Assembly, - AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM + YulStack::Language _language = YulStack::Language::Assembly, + YulStack::Machine _machine = YulStack::Machine::EVM ) { - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), _language, solidity::frontend::OptimiserSettings::none(), @@ -103,24 +103,24 @@ bool successParse( string const& _source, bool _assemble = false, bool _allowWarnings = true, - AssemblyStack::Language _language = AssemblyStack::Language::Assembly, - AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM + YulStack::Language _language = YulStack::Language::Assembly, + YulStack::Machine _machine = YulStack::Machine::EVM ) { return !parseAndReturnFirstError(_source, _assemble, _allowWarnings, _language, _machine); } -bool successAssemble(string const& _source, bool _allowWarnings = true, AssemblyStack::Language _language = AssemblyStack::Language::Assembly) +bool successAssemble(string const& _source, bool _allowWarnings = true, YulStack::Language _language = YulStack::Language::Assembly) { return - successParse(_source, true, _allowWarnings, _language, AssemblyStack::Machine::EVM); + successParse(_source, true, _allowWarnings, _language, YulStack::Machine::EVM); } Error expectError( std::string const& _source, bool _assemble, bool _allowWarnings = false, - AssemblyStack::Language _language = AssemblyStack::Language::Assembly + YulStack::Language _language = YulStack::Language::Assembly ) { @@ -131,9 +131,9 @@ Error expectError( void parsePrintCompare(string const& _source, bool _canWarn = false) { - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::Assembly, + YulStack::Language::Assembly, OptimiserSettings::none(), DebugInfoSelection::None() ); @@ -157,7 +157,7 @@ do \ } while(0) #define CHECK_ERROR(text, assemble, typ, substring, warnings) \ -CHECK_ERROR_LANG(text, assemble, typ, substring, warnings, AssemblyStack::Language::Assembly) +CHECK_ERROR_LANG(text, assemble, typ, substring, warnings, YulStack::Language::Assembly) #define CHECK_PARSE_ERROR(text, type, substring) \ CHECK_ERROR(text, false, type, substring, false) @@ -169,13 +169,13 @@ CHECK_ERROR(text, false, type, substring, false) CHECK_ERROR(text, true, type, substring, false) #define CHECK_STRICT_ERROR(text, type, substring) \ -CHECK_ERROR_LANG(text, false, type, substring, false, AssemblyStack::Language::StrictAssembly) +CHECK_ERROR_LANG(text, false, type, substring, false, YulStack::Language::StrictAssembly) #define CHECK_STRICT_WARNING(text, type, substring) \ -CHECK_ERROR(text, false, type, substring, false, AssemblyStack::Language::StrictAssembly) +CHECK_ERROR(text, false, type, substring, false, YulStack::Language::StrictAssembly) #define SUCCESS_STRICT(text) \ -do { successParse((text), false, false, AssemblyStack::Language::StrictAssembly); } while (false) +do { successParse((text), false, false, YulStack::Language::StrictAssembly); } while (false) BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly) @@ -221,9 +221,9 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; string parsed = "object \"object\" {\n code { let x := \"\\xe1\\xae\\xac\" }\n}\n"; - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::Assembly, + YulStack::Language::Assembly, OptimiserSettings::none(), DebugInfoSelection::None() ); diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 7ff226a69..70d0b2a6e 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -97,9 +97,9 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( else if (forceEnableOptimizer) optimiserSettings = OptimiserSettings::full(); - yul::AssemblyStack asmStack( + yul::YulStack asmStack( m_evmVersion, - yul::AssemblyStack::Language::StrictAssembly, + yul::YulStack::Language::StrictAssembly, optimiserSettings, DebugInfoSelection::All() ); @@ -109,7 +109,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( try { asmStack.optimize(); - obj = move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); + obj = move(*asmStack.assemble(yul::YulStack::Machine::EVM).bytecode); obj.link(_libraryAddresses); break; } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 202addffa..dfa495152 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -30,7 +30,7 @@ #include #include -#include +#include namespace solidity::frontend::test { diff --git a/test/libsolidity/lsp/didOpen_with_import.sol b/test/libsolidity/lsp/didOpen_with_import.sol index f505ca6e5..a335df759 100644 --- a/test/libsolidity/lsp/didOpen_with_import.sol +++ b/test/libsolidity/lsp/didOpen_with_import.sol @@ -8,5 +8,6 @@ contract C function f(uint a, uint b) public pure returns (uint) { return Lib.add(2 * a, b); + // ^^^^^^^ @diagnostics } } diff --git a/test/libsolidity/lsp/lib.sol b/test/libsolidity/lsp/lib.sol index 22efe6ca2..031cf19ad 100644 --- a/test/libsolidity/lsp/lib.sol +++ b/test/libsolidity/lsp/lib.sol @@ -23,13 +23,16 @@ enum Color { library Lib { function add(uint a, uint b) public pure returns (uint result) +// ^( @addFunction { result = a + b; } +// ^) @addFunction function warningWithUnused() public pure { uint unused; + // ^^^^^^^^^^^ @diagnostics } } diff --git a/test/libsolidity/lsp/publish_diagnostics_1.sol b/test/libsolidity/lsp/publish_diagnostics_1.sol index e66718512..be15a4090 100644 --- a/test/libsolidity/lsp/publish_diagnostics_1.sol +++ b/test/libsolidity/lsp/publish_diagnostics_1.sol @@ -6,13 +6,16 @@ contract MyContract constructor() { uint unused; // [Warning 2072] Unused local variable. + // ^^^^^^^^^^^ @unusedVariable } } contract D { function main() public payable returns (uint) + // ^^^^ @unusedReturnVariable { MyContract c = new MyContract(); + // ^^^^^^^^^^^^ @unusedContractVariable } } diff --git a/test/libsolidity/lsp/publish_diagnostics_2.sol b/test/libsolidity/lsp/publish_diagnostics_2.sol index 968618955..65b4df585 100644 --- a/test/libsolidity/lsp/publish_diagnostics_2.sol +++ b/test/libsolidity/lsp/publish_diagnostics_2.sol @@ -6,7 +6,9 @@ contract C function makeSomeError() public pure returns (uint res) { uint x = "hi"; + // ^^^^^^^^^^^^^ @conversionError return; + // ^^^^^^^ @argumentsRequired res = 2; } } @@ -17,5 +19,6 @@ contract D { C c = new C(); return c.makeSomeError(2, 3); + // ^^^^^^^^^^^^^^^^^^^^^ @wrongArgumentsCount } } diff --git a/test/libsolidity/lsp/publish_diagnostics_3.sol b/test/libsolidity/lsp/publish_diagnostics_3.sol index bb8998a6a..45b418a90 100644 --- a/test/libsolidity/lsp/publish_diagnostics_3.sol +++ b/test/libsolidity/lsp/publish_diagnostics_3.sol @@ -6,5 +6,7 @@ abstract contract A { } contract B is A +// ^( @notAbstract { } +// ^) @notAbstract diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol index 1a72e0888..b7d917dbf 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol @@ -32,6 +32,6 @@ contract C is B { // compileViaYul: also // ---- // test() -> 77 -// gas irOptimized: 119911 +// gas irOptimized: 119711 // gas legacy: 155093 // gas legacyOptimized: 111550 diff --git a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol index 2aa3d43f8..42d14ba24 100644 --- a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -11,7 +11,7 @@ contract C { // compileViaYul: also // ---- // constructor(): 1, 2, 3 -> -// gas irOptimized: 142856 +// gas irOptimized: 142640 // gas legacy: 183490 // gas legacyOptimized: 151938 // a(uint256): 0 -> 1 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol index a19475f7b..3ce5f1a8d 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol @@ -21,6 +21,6 @@ contract B { // compileViaYul: also // ---- // f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 -// gas irOptimized: 128110 +// gas irOptimized: 127910 // gas legacy: 234719 // gas legacyOptimized: 132639 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol index 66e385812..087ecf0a8 100644 --- a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -45,6 +45,6 @@ contract C { // compileViaYul: also // ---- // test() -> 5, 6, 7 -// gas irOptimized: 292702 +// gas irOptimized: 292502 // gas legacy: 452136 // gas legacyOptimized: 284945 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol index d2d09645d..455d6f948 100644 --- a/test/libsolidity/semanticTests/array/reusing_memory.sol +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -26,6 +26,6 @@ contract Main { // compileViaYul: also // ---- // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 -// gas irOptimized: 113398 +// gas irOptimized: 113198 // gas legacy: 126596 // gas legacyOptimized: 113823 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index 2e0835c25..bdd5c6eb5 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -26,6 +26,6 @@ contract Creator { // compileViaYul: also // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 295403 +// gas irOptimized: 293203 // gas legacy: 428711 // gas legacyOptimized: 297922 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol index 31212c5e8..d0b3f64cb 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -10,7 +10,7 @@ contract Test { // compileViaYul: also // ---- // constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> -// gas irOptimized: 286205 +// gas irOptimized: 283829 // gas legacy: 309607 // gas legacyOptimized: 260566 // m_x() -> 7 diff --git a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol index b8f4d42bb..3e9ae1ab2 100644 --- a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol +++ b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol @@ -23,7 +23,7 @@ contract D is B, C { // compileViaYul: also // ---- // constructor(): 2, 0 -> -// gas irOptimized: 158225 +// gas irOptimized: 156071 // gas legacy: 170665 // gas legacyOptimized: 145396 // i() -> 2 diff --git a/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol b/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol index 36e5ee73c..0f685b783 100644 --- a/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol +++ b/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol @@ -17,7 +17,7 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 173316 +// gas irOptimized: 173094 // gas legacy: 250376 // gas legacyOptimized: 174522 // deposit(bytes32), 18 wei: 0x1234 -> diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol index bcbf816ae..9e3211ecb 100644 --- a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -178,7 +178,7 @@ contract DepositContract is IDepositContract, ERC165 { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1532125 +// gas irOptimized: 1529797 // gas legacy: 2435803 // gas legacyOptimized: 1775425 // supportsInterface(bytes4): 0x0 -> 0 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index c3fb5183b..c37fe52b5 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -50,7 +50,7 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1783505 +// gas irOptimized: 1780841 // gas legacy: 2248594 // gas legacyOptimized: 1749096 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 diff --git a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol index 2152a38a0..b1c4c0169 100644 --- a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol +++ b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol @@ -35,7 +35,7 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 456094 +// gas irOptimized: 455866 // gas legacy: 671453 // gas legacyOptimized: 480242 // prb_pi() -> 3141592656369545286 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol index 99167ee8e..0f1b63523 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol @@ -17,7 +17,7 @@ contract D { // compileViaYul: also // ---- // constructor(): 2 -> -// gas irOptimized: 200649 +// gas irOptimized: 200217 // gas legacy: 245842 // gas legacyOptimized: 195676 // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol index 7a44bf1b9..462e9ad76 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol @@ -18,7 +18,7 @@ contract D { // compileViaYul: also // ---- // constructor(): 2 -> -// gas irOptimized: 200812 +// gas irOptimized: 200380 // gas legacy: 246202 // gas legacyOptimized: 195914 // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol index 7a29cc254..79c3adccb 100644 --- a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol +++ b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol @@ -25,7 +25,7 @@ contract C { // compileViaYul: also // ---- // constructor(), 1 ether -> -// gas irOptimized: 304151 +// gas irOptimized: 303935 // gas legacy: 464030 // gas legacyOptimized: 304049 // f(uint256): 0 -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/failed_create.sol b/test/libsolidity/semanticTests/functionCall/failed_create.sol index 0a8b5a922..19fd10bf6 100644 --- a/test/libsolidity/semanticTests/functionCall/failed_create.sol +++ b/test/libsolidity/semanticTests/functionCall/failed_create.sol @@ -18,7 +18,7 @@ contract C { // compileViaYul: also // ---- // constructor(), 20 wei -// gas irOptimized: 214971 +// gas irOptimized: 212583 // gas legacy: 294335 // gas legacyOptimized: 174279 // f(uint256): 20 -> 1370859564726510389319704988634906228201275401179 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol index ca2cf152e..40de1b509 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol @@ -41,7 +41,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 275142 +// gas irOptimized: 270609 // gas legacy: 402654 // gas legacyOptimized: 274470 // sendAmount(uint256): 5 -> 5 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol index a08d75557..6b3f99dee 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol @@ -40,7 +40,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 275142 +// gas irOptimized: 270609 // gas legacy: 402654 // gas legacyOptimized: 274470 // sendAmount(uint256): 5 -> 5 diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol index 188052e75..a3d93043d 100644 --- a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -19,7 +19,7 @@ contract Main { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 100480 +// gas irOptimized: 100264 // gas legacy: 116691 // gas legacyOptimized: 100361 // s() -> true diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol index c50acf2be..a66c86027 100644 --- a/test/libsolidity/semanticTests/immutable/multi_creation.sol +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -29,7 +29,7 @@ contract C { // compileViaYul: also // ---- // f() -> 3, 7, 5 -// gas irOptimized: 126536 +// gas irOptimized: 126136 // gas legacy: 151334 // gas legacyOptimized: 125166 // x() -> 7 diff --git a/test/libsolidity/semanticTests/immutable/use_scratch.sol b/test/libsolidity/semanticTests/immutable/use_scratch.sol index 4f3e8a3bd..9dec40935 100644 --- a/test/libsolidity/semanticTests/immutable/use_scratch.sol +++ b/test/libsolidity/semanticTests/immutable/use_scratch.sol @@ -17,7 +17,7 @@ contract C { // compileViaYul: also // ---- // constructor(): 3 -> -// gas irOptimized: 129602 +// gas irOptimized: 127454 // gas legacy: 209361 // gas legacyOptimized: 139324 // f() -> 84, 23 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol index 165ae909d..2ce71c31f 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol @@ -25,6 +25,6 @@ contract B { // compileViaYul: also // ---- // g() -> 42 -// gas irOptimized: 102144 +// gas irOptimized: 101944 // gas legacy: 185053 // gas legacyOptimized: 114598 diff --git a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol index 95dd7c3d9..b38e9a6ad 100644 --- a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol +++ b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol @@ -42,7 +42,7 @@ contract Main { // compileViaYul: also // ---- // constructor(), 22 wei -> -// gas irOptimized: 280056 +// gas irOptimized: 277680 // gas legacy: 402045 // gas legacyOptimized: 266772 // getFlag() -> true diff --git a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol index 3b1a485c1..b02e48804 100644 --- a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol +++ b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol @@ -18,7 +18,7 @@ contract ClientReceipt { // compileViaYul: also // ---- // constructor(), 2000 wei -> -// gas irOptimized: 183976 +// gas irOptimized: 183544 // gas legacy: 235195 // gas legacyOptimized: 176766 // balance -> 1500 diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index aab46a9a4..6613e471b 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -22,6 +22,6 @@ contract A { // compileViaYul: also // ---- // f(), 10 ether -> 3007, 3008, 3009 -// gas irOptimized: 272431 +// gas irOptimized: 271831 // gas legacy: 422501 // gas legacyOptimized: 287472 diff --git a/test/libsolidity/semanticTests/smoke/constructor.sol b/test/libsolidity/semanticTests/smoke/constructor.sol index 4e5a07e95..3003c5d8d 100644 --- a/test/libsolidity/semanticTests/smoke/constructor.sol +++ b/test/libsolidity/semanticTests/smoke/constructor.sol @@ -14,7 +14,7 @@ contract C { // compileViaYul: also // ---- // constructor(), 2 wei: 3 -> -// gas irOptimized: 109775 +// gas irOptimized: 107627 // gas legacy: 151416 // gas legacyOptimized: 108388 // state() -> 3 diff --git a/test/libsolidity/semanticTests/state/blockhash_basic.sol b/test/libsolidity/semanticTests/state/blockhash_basic.sol index 558e1ca90..829a8695d 100644 --- a/test/libsolidity/semanticTests/state/blockhash_basic.sol +++ b/test/libsolidity/semanticTests/state/blockhash_basic.sol @@ -14,7 +14,7 @@ contract C { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 113738 +// gas irOptimized: 111584 // gas legacy: 155081 // gas legacyOptimized: 107997 // genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737 diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol index 6f1fbe7a9..2adf31716 100644 --- a/test/libsolidity/semanticTests/various/address_code.sol +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -17,7 +17,7 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 194717 +// gas irOptimized: 192317 // gas legacy: 240889 // gas legacyOptimized: 155314 // initCode() -> 0x20, 0 diff --git a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol index 3d5a480e4..9ff4fe5e2 100644 --- a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol +++ b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol @@ -21,4 +21,4 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 101495 +// gas irOptimized: 101063 diff --git a/test/libsolidity/semanticTests/various/senders_balance.sol b/test/libsolidity/semanticTests/various/senders_balance.sol index 344daf8d8..0e0e59996 100644 --- a/test/libsolidity/semanticTests/various/senders_balance.sol +++ b/test/libsolidity/semanticTests/various/senders_balance.sol @@ -19,7 +19,7 @@ contract D { // compileViaYul: also // ---- // constructor(), 27 wei -> -// gas irOptimized: 175589 +// gas irOptimized: 175157 // gas legacy: 222977 // gas legacyOptimized: 169779 // f() -> 27 diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol index d2c520545..90dd3a6f7 100644 --- a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol @@ -38,10 +38,10 @@ contract D { // f() -> 0x1 # This should work, next should throw # // gas legacy: 103716 // fview() -> FAILURE -// gas irOptimized: 98438622 +// gas irOptimized: 98438619 // gas legacy: 98438801 // gas legacyOptimized: 98438594 // fpure() -> FAILURE -// gas irOptimized: 98438622 +// gas irOptimized: 98438619 // gas legacy: 98438801 // gas legacyOptimized: 98438595 diff --git a/test/libsolidity/semanticTests/various/value_complex.sol b/test/libsolidity/semanticTests/various/value_complex.sol index 879e9d23c..c868652a0 100644 --- a/test/libsolidity/semanticTests/various/value_complex.sol +++ b/test/libsolidity/semanticTests/various/value_complex.sol @@ -22,7 +22,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 190491 +// gas irOptimized: 190275 // gas legacy: 265006 // gas legacyOptimized: 182842 // sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/semanticTests/various/value_insane.sol b/test/libsolidity/semanticTests/various/value_insane.sol index 0499c1b06..c304732bb 100644 --- a/test/libsolidity/semanticTests/various/value_insane.sol +++ b/test/libsolidity/semanticTests/various/value_insane.sol @@ -21,7 +21,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 192213 +// gas irOptimized: 191991 // gas legacy: 266728 // gas legacyOptimized: 184762 // sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple.sol deleted file mode 100644 index 72fd4290c..000000000 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple.sol +++ /dev/null @@ -1,26 +0,0 @@ -contract C { - function callMeMaybe(uint a, uint b, uint[] memory c) external {} - - function abiEncodeSimple(uint x, uint y, uint z, uint[] memory a, uint[] memory b) public view { - require(x == y); - bytes memory b1 = abi.encodeCall(this.callMeMaybe, (x, z, a)); - bytes memory b2 = abi.encodeCall(this.callMeMaybe, (y, z, a)); - assert(b1.length == b2.length); - - bytes memory b3 = abi.encodeCall(this.callMeMaybe, (y, z, b)); - assert(b1.length == b3.length); // should fail - } -} -// ==== -// SMTEngine: all -// SMTIgnoreCex: yes -// ---- -// Warning 6031: (233-249): Internal error: Expression undefined for SMT solver. -// Warning 6031: (298-314): Internal error: Expression undefined for SMT solver. -// Warning 6031: (398-414): Internal error: Expression undefined for SMT solver. -// Warning 1218: (330-360): CHC: Error trying to invoke SMT solver. -// Warning 1218: (430-460): CHC: Error trying to invoke SMT solver. -// Warning 6328: (330-360): CHC: Assertion violation might happen here. -// Warning 6328: (430-460): CHC: Assertion violation might happen here. -// Warning 4661: (330-360): BMC: Assertion violation happens here. -// Warning 4661: (430-460): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_1.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_1.sol new file mode 100644 index 000000000..be0bc4769 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_1.sol @@ -0,0 +1,22 @@ +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(uint x, uint y, uint z) public view { + require(x == y); + function (uint, uint) external f = this.callMeMaybe; + bytes memory b1 = abi.encodeCall(f, (x, z)); + bytes memory b2 = abi.encodeCall(f, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(this.callMeMaybe, (3, z)); + assert(b1.length == b3.length); // should hold, but we don't encode the length computation precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (354-359): CHC: Out of bounds access happens here. +// Warning 6368: (363-368): CHC: Out of bounds access happens here. +// Warning 6328: (451-481): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_2.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_2.sol new file mode 100644 index 000000000..c6044a8cd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_2.sol @@ -0,0 +1,21 @@ +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(uint x, uint y, uint z) public view { + require(x == y); + bytes memory b1 = abi.encodeCall(this.callMeMaybe, (x, z)); + bytes memory b2 = abi.encodeCall(this.callMeMaybe, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(this.callMeMaybe, (3, z)); + assert(b1.length == b3.length); // should hold but we don't compute the length precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (329-334): CHC: Out of bounds access happens here. +// Warning 6368: (338-343): CHC: Out of bounds access happens here. +// Warning 6328: (426-456): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_3.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_3.sol new file mode 100644 index 000000000..a3b20f7f7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_3.sol @@ -0,0 +1,21 @@ +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(uint x, uint y, uint z) public pure { + require(x == y); + bytes memory b1 = abi.encodeCall(C.callMeMaybe, (x, z)); + bytes memory b2 = abi.encodeCall(C.callMeMaybe, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(C.callMeMaybe, (3, z)); + assert(b1.length == b3.length); // should hold but we don't compute the length precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (323-328): CHC: Out of bounds access happens here. +// Warning 6368: (332-337): CHC: Out of bounds access happens here. +// Warning 6328: (417-447): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_4.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_4.sol new file mode 100644 index 000000000..6f0128f8a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_4.sol @@ -0,0 +1,25 @@ +abstract contract D { + function no(uint a, uint b) external virtual; +} + +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(uint x, uint y, uint z) public pure { + require(x == y); + bytes memory b1 = abi.encodeCall(D.no, (x, z)); + bytes memory b2 = abi.encodeCall(D.no, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(D.no, (3, z)); + assert(b1.length == b3.length); // should hold but we don't compute the length precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (377-382): CHC: Out of bounds access happens here. +// Warning 6368: (386-391): CHC: Out of bounds access happens here. +// Warning 6328: (462-492): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_5.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_5.sol new file mode 100644 index 000000000..98e42ad9f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_5.sol @@ -0,0 +1,25 @@ +abstract contract D { + function no(uint a, uint b) external virtual; +} + +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(D d, uint x, uint y, uint z) public pure { + require(x == y); + bytes memory b1 = abi.encodeCall(d.no, (x, z)); + bytes memory b2 = abi.encodeCall(d.no, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(d.no, (3, z)); + assert(b1.length == b3.length); // should hold but we don't compute the length precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (382-387): CHC: Out of bounds access happens here. +// Warning 6368: (391-396): CHC: Out of bounds access happens here. +// Warning 6328: (467-497): CHC: Assertion violation happens here. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions.sol new file mode 100644 index 000000000..710478580 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions.sol @@ -0,0 +1,35 @@ +interface testInterface { + function C(function (string memory) external) external; + function D(string calldata) external; + function E(string memory) external; + function F(address) external; +} + +contract testContract { + function g(string calldata) external {} + function h(string memory) external {} + function i(string calldata str) external { + this.h(str); + this.g(str); + } + function j(string memory str) external { + this.h(str); + this.g(str); + } + function k(string memory str) external pure { + abi.encodeCall(testInterface.D, (str)); + } + string s; + + function main() external view { + abi.encodeCall(testInterface.C, (this.g)); + abi.encodeCall(testInterface.C, (this.h)); + abi.encodeCall(testInterface.D, (s)); + abi.encodeCall(testInterface.E, (s)); + abi.encodeCall(testInterface.F, (payable(address(0)))); + abi.encodeCall(this.i, (s)); + abi.encodeCall(this.j, (s)); + } +} +// ---- +// Warning 6133: (860-914): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_address.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_address.sol new file mode 100644 index 000000000..b5d53142c --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_address.sol @@ -0,0 +1,11 @@ +interface testInterface { + function A(address payable) external; +} + +contract testContract { + function main() external view { + abi.encodeCall(testInterface.A, (address(0))); + } +} +// ---- +// TypeError 5407: (171-183): Cannot implicitly convert component at position 0 from "address" to "address payable". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_function_pointers.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_function_pointers.sol new file mode 100644 index 000000000..242eb61c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_function_pointers.sol @@ -0,0 +1,16 @@ +interface testInterface { + function B(function (string calldata) external) external; +} + +contract testContract { + function g(string calldata) external {} + function h(string memory) external {} + + function main() external view { + abi.encodeCall(testInterface.B, (this.g)); + abi.encodeCall(testInterface.B, (this.h)); + } +} +// ---- +// TypeError 5407: (278-286): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external". +// TypeError 5407: (329-337): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_address_to_address_payable.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_address_to_address_payable.sol deleted file mode 100644 index a4c823a85..000000000 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_address_to_address_payable.sol +++ /dev/null @@ -1,11 +0,0 @@ -interface I { - function f(address payable) external; -} - -contract C { - function main() external view { - abi.encodeCall(I.f, (address(0))); - } -} -// ---- -// TypeError 5407: (136-148): Cannot implicitly convert component at position 0 from "address" to "address payable". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_pointer_to_different_function_pointer.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_pointer_to_different_function_pointer.sol deleted file mode 100644 index 7c570e29e..000000000 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_pointer_to_different_function_pointer.sol +++ /dev/null @@ -1,13 +0,0 @@ -interface I { - function f(function (string calldata) external) external; -} - -contract C { - function g(string calldata) external {} - - function main() external view { - abi.encodeCall(I.f, (this.g)); - } -} -// ---- -// TypeError 5407: (201-209): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_to_different_function.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_to_different_function.sol deleted file mode 100644 index 7a35bfa55..000000000 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_to_different_function.sol +++ /dev/null @@ -1,13 +0,0 @@ -interface I { - function f(function (string calldata) external view returns (uint)) external; -} - -contract C { - function g(string memory) external {} - - function main() external view { - abi.encodeCall(I.f, (this.g)); - } -} -// ---- -// TypeError 5407: (219-227): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) view external returns (uint256)". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_string_to_different_string.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_string_to_different_string.sol deleted file mode 100644 index 72efe4a07..000000000 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_string_to_different_string.sol +++ /dev/null @@ -1,12 +0,0 @@ -interface I { - function f(string calldata) external; -} - -contract C { - string s; - function main() external view { - abi.encodeCall(I.f, (s)); - } -} -// ---- -// TypeError 5407: (150-153): Cannot implicitly convert component at position 0 from "string storage ref" to "string calldata". diff --git a/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol b/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol index 37f194899..8ab641984 100644 --- a/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol +++ b/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol @@ -2,4 +2,4 @@ contract C { bytes32[8**90] ids; } // ---- -// TypeError 5462: (25-30): Invalid array length, expected integer literal or constant expression. +// TypeError 1847: (25-30): Array length too large, maximum is 2**256 - 1. diff --git a/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol index 1a4e7f64c..8a7eeb4d5 100644 --- a/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol +++ b/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol @@ -2,4 +2,4 @@ contract C { bytes32[8**90][500] ids; } // ---- -// TypeError 5462: (25-30): Invalid array length, expected integer literal or constant expression. +// TypeError 1847: (25-30): Array length too large, maximum is 2**256 - 1. diff --git a/test/libsolidity/syntaxTests/array/length/literal_conversion.sol b/test/libsolidity/syntaxTests/array/length/literal_conversion.sol new file mode 100644 index 000000000..cf769f337 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/literal_conversion.sol @@ -0,0 +1,16 @@ +contract C { + uint[uint(1)] valid_size_invalid_expr1; + uint[uint(2**256-1)] valid_size_invalid_expr2; + uint[uint(2**256)] invalid_size_invalid_expr3; + + uint[int(1)] valid_size_invalid_expr4; + uint[int(2**256-1)] valid_size_invalid_expr5; + uint[int(2**256)] invalid_size_invalid_expr6; +} +// ---- +// TypeError 5462: (22-29): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (66-80): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (117-129): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (169-175): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (212-225): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (262-273): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/too_large.sol b/test/libsolidity/syntaxTests/array/length/too_large.sol index 687e80303..7d59fd86c 100644 --- a/test/libsolidity/syntaxTests/array/length/too_large.sol +++ b/test/libsolidity/syntaxTests/array/length/too_large.sol @@ -1,5 +1,8 @@ contract C { uint[8**90] ids; + uint[2**256-1] okay; + uint[2**256] tooLarge; } // ---- -// TypeError 5462: (22-27): Invalid array length, expected integer literal or constant expression. +// TypeError 1847: (22-27): Array length too large, maximum is 2**256 - 1. +// TypeError 1847: (68-74): Array length too large, maximum is 2**256 - 1. diff --git a/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol index 6ea3eda50..f3e249f83 100644 --- a/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol +++ b/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol @@ -2,4 +2,4 @@ contract C { uint[8**90][500] ids; } // ---- -// TypeError 5462: (22-27): Invalid array length, expected integer literal or constant expression. +// TypeError 1847: (22-27): Array length too large, maximum is 2**256 - 1. diff --git a/test/libsolidity/util/SoltestErrors.h b/test/libsolidity/util/SoltestErrors.h index e223f714b..849a8b34e 100644 --- a/test/libsolidity/util/SoltestErrors.h +++ b/test/libsolidity/util/SoltestErrors.h @@ -15,20 +15,35 @@ #pragma once #include +#include #include #include +#include +#include +#include + namespace solidity::frontend::test { -#define soltestAssert(CONDITION, DESCRIPTION) \ - do \ - { \ - if (!(CONDITION)) \ - BOOST_THROW_EXCEPTION(std::runtime_error(DESCRIPTION)); \ - } \ - while (false) +struct InternalSoltestError: virtual util::Exception {}; +#if !BOOST_PP_VARIADICS_MSVC +#define soltestAssert(...) BOOST_PP_OVERLOAD(soltestAssert_,__VA_ARGS__)(__VA_ARGS__) +#else +#define soltestAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(soltestAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) +#endif + +#define soltestAssert_1(CONDITION) \ + soltestAssert_2((CONDITION), "") + +#define soltestAssert_2(CONDITION, DESCRIPTION) \ + assertThrowWithDefaultDescription( \ + (CONDITION), \ + ::solidity::frontend::test::InternalSoltestError, \ + (DESCRIPTION), \ + "Soltest assertion failed" \ + ) class TestParserError: virtual public util::Exception { diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index fc9be2001..f40f422a8 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -55,9 +55,9 @@ Dialect const& defaultDialect(bool _yul) pair, shared_ptr> yul::test::parse(string const& _source, bool _yul) { - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), - _yul ? AssemblyStack::Language::Yul : AssemblyStack::Language::StrictAssembly, + _yul ? YulStack::Language::Yul : YulStack::Language::StrictAssembly, solidity::test::CommonOptions::get().optimize ? solidity::frontend::OptimiserSettings::standard() : solidity::frontend::OptimiserSettings::minimal(), diff --git a/test/libyul/EVMCodeTransformTest.cpp b/test/libyul/EVMCodeTransformTest.cpp index 679493f35..c1f6ae5df 100644 --- a/test/libyul/EVMCodeTransformTest.cpp +++ b/test/libyul/EVMCodeTransformTest.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include @@ -51,9 +51,9 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _ solidity::frontend::OptimiserSettings settings = solidity::frontend::OptimiserSettings::none(); settings.runYulOptimiser = false; settings.optimizeStackAllocation = m_stackOpt; - AssemblyStack stack( + YulStack stack( EVMVersion{}, - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, settings, DebugInfoSelection::All() ); diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index 17b7f1868..34a714404 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -80,9 +80,9 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _ bool EwasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - m_stack = AssemblyStack( + m_stack = YulStack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::All() ); diff --git a/test/libyul/EwasmTranslationTest.h b/test/libyul/EwasmTranslationTest.h index eba82beee..d54b8732a 100644 --- a/test/libyul/EwasmTranslationTest.h +++ b/test/libyul/EwasmTranslationTest.h @@ -20,12 +20,12 @@ #include -#include +#include namespace solidity::yul { struct Object; -class AssemblyStack; +class YulStack; } namespace solidity::yul::test @@ -48,7 +48,7 @@ private: std::string interpret(); std::shared_ptr m_object; - AssemblyStack m_stack; + YulStack m_stack; }; } diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index f22870952..dcb7158c2 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -22,7 +22,7 @@ #include -#include +#include #include #include @@ -63,9 +63,9 @@ ObjectCompilerTest::ObjectCompilerTest(string const& _filename): TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { - AssemblyStack stack( + YulStack stack( EVMVersion(), - m_wasm ? AssemblyStack::Language::Ewasm : AssemblyStack::Language::StrictAssembly, + m_wasm ? YulStack::Language::Ewasm : YulStack::Language::StrictAssembly, OptimiserSettings::preset(m_optimisationPreset), DebugInfoSelection::All() ); @@ -80,7 +80,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li if (m_wasm) { - MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::Ewasm); + MachineAssemblyObject obj = stack.assemble(YulStack::Machine::Ewasm); solAssert(obj.bytecode, ""); m_obtainedResult = "Text:\n" + obj.assembly + "\n"; @@ -88,7 +88,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li } else { - MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM); + MachineAssemblyObject obj = stack.assemble(YulStack::Machine::EVM); solAssert(obj.bytecode, ""); solAssert(obj.sourceMappings, ""); diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 1d6f0bed3..bee0025af 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -58,9 +58,9 @@ pair parse(string const& _source) { try { - AssemblyStack asmStack( + YulStack asmStack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::All() ); @@ -180,9 +180,9 @@ BOOST_AUTO_TEST_CASE(to_string) } )"; expectation = boost::replace_all_copy(expectation, "\t", " "); - AssemblyStack asmStack( + YulStack asmStack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::All() ); diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index 3e7de4d17..3c890972e 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -65,9 +65,9 @@ TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _li bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::All() ); diff --git a/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul b/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul index 35dfa5343..a51c39f4f 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul @@ -11,7 +11,6 @@ // step: stackCompressor // // { -// { let x := 8 } // function f() // { // mstore(calldataload(calldataload(9)), add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(calldataload(calldataload(9)), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)) diff --git a/test/lsp.py b/test/lsp.py index a20c28c3c..8b7398cf2 100755 --- a/test/lsp.py +++ b/test/lsp.py @@ -7,8 +7,10 @@ import os import subprocess import sys import traceback +import re from typing import Any, List, Optional, Tuple, Union +from enum import Enum, auto import colorama # Enables the use of SGR & CUP terminal VT sequences on Windows. from deepdiff import DeepDiff @@ -18,6 +20,7 @@ class BadHeader(Exception): def __init__(self, msg: str): super().__init__("Bad header: " + msg) + class JsonRpcProcess: exe_path: str exe_args: List[str] @@ -116,15 +119,23 @@ SGR_STATUS_FAIL = '\033[1;31m' class ExpectationFailed(Exception): def __init__(self, actual, expected): - self.actual = actual - self.expected = expected - diff = DeepDiff(actual, expected) + self.actual = json.dumps(actual, sort_keys=True) + self.expected = json.dumps(expected, sort_keys=True) + diff = json.dumps(DeepDiff(actual, expected), indent=4) super().__init__( - f"Expectation failed.\n\tExpected {expected}\n\tbut got {actual}.\n\t{diff}" + f"\n\tExpected {self.expected}\n\tbut got {self.actual}.\n\t{diff}" ) + def create_cli_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Solidity LSP Test suite") + parser.set_defaults(fail_fast=False) + parser.add_argument( + "-f, --fail-fast", + dest="fail_fast", + action="store_true", + help="Terminates the running tests on first failure." + ) parser.set_defaults(trace_io=False) parser.add_argument( "-T, --trace-io", @@ -168,12 +179,26 @@ class Counter: passed: int = 0 failed: int = 0 + +class Marker(Enum): + SimpleRange = auto() + MultilineRange = auto() + + +# Returns the given marker with the end extended by 'amount' +def extendEnd(marker, amount=1): + marker["end"]["character"] += amount + return marker + + class SolidityLSPTestSuite: # {{{ test_counter = Counter() assertion_counter = Counter() print_assertions: bool = False trace_io: bool = False + fail_fast: bool = False test_pattern: str + marker_regexes: {} def __init__(self): colorama.init() @@ -184,6 +209,11 @@ class SolidityLSPTestSuite: # {{{ self.print_assertions = args.print_assertions self.trace_io = args.trace_io self.test_pattern = args.test_pattern + self.fail_fast = args.fail_fast + self.marker_regexes = { + Marker.SimpleRange: re.compile(R"(?P[\^]+) (?P@\w+)"), + Marker.MultilineRange: re.compile(R"\^(?P[()]) (?P@\w+)$") + } print(f"{SGR_NOTICE}test pattern: {self.test_pattern}{SGR_RESET}") @@ -206,14 +236,17 @@ class SolidityLSPTestSuite: # {{{ with JsonRpcProcess(self.solc_path, ["--lsp"], trace_io=self.trace_io) as solc: test_fn(solc) self.test_counter.passed += 1 - except ExpectationFailed as e: + except ExpectationFailed: self.test_counter.failed += 1 - print(e) print(traceback.format_exc()) + if self.fail_fast: + break except Exception as e: # pragma pylint: disable=broad-except self.test_counter.failed += 1 print(f"Unhandled exception {e.__class__.__name__} caught: {e}") print(traceback.format_exc()) + if self.fail_fast: + break print( f"\n{SGR_NOTICE}Summary:{SGR_RESET}\n\n" @@ -347,20 +380,26 @@ class SolidityLSPTestSuite: # {{{ self, diagnostic, code: int, - lineNo: int, - startEndColumns: Tuple[int, int] + lineNo: int = None, + startEndColumns: Tuple[int, int] = None, + marker: {} = None ): - assert len(startEndColumns) == 2 - [startColumn, endColumn] = startEndColumns self.expect_equal(diagnostic['code'], code, f'diagnostic: {code}') - self.expect_equal( - diagnostic['range'], - { - 'start': {'character': startColumn, 'line': lineNo}, - 'end': {'character': endColumn, 'line': lineNo} - }, - "diagnostic: check range" - ) + + if marker: + self.expect_equal(diagnostic['range'], marker, "diagnostic: check range") + else: + assert len(startEndColumns) == 2 + [startColumn, endColumn] = startEndColumns + self.expect_equal( + diagnostic['range'], + { + 'start': {'character': startColumn, 'line': lineNo}, + 'end': {'character': endColumn, 'line': lineNo} + }, + "diagnostic: check range" + ) + def expect_location( self, @@ -421,10 +460,12 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] + markers = self.get_file_tags(TEST_NAME) + self.expect_equal(len(diagnostics), 3, "3 diagnostic messages") - self.expect_diagnostic(diagnostics[0], code=6321, lineNo=13, startEndColumns=(44, 48)) - self.expect_diagnostic(diagnostics[1], code=2072, lineNo= 7, startEndColumns=( 8, 19)) - self.expect_diagnostic(diagnostics[2], code=2072, lineNo=15, startEndColumns=( 8, 20)) + self.expect_diagnostic(diagnostics[0], code=6321, marker=markers["@unusedReturnVariable"]) + self.expect_diagnostic(diagnostics[1], code=2072, marker=markers["@unusedVariable"]) + self.expect_diagnostic(diagnostics[2], code=2072, marker=markers["@unusedContractVariable"]) def test_publish_diagnostics_errors(self, solc: JsonRpcProcess) -> None: self.setup_lsp(solc) @@ -437,10 +478,12 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] + markers = self.get_file_tags(TEST_NAME) + self.expect_equal(len(diagnostics), 3, "3 diagnostic messages") - self.expect_diagnostic(diagnostics[0], code=9574, lineNo= 7, startEndColumns=( 8, 21)) - self.expect_diagnostic(diagnostics[1], code=6777, lineNo= 8, startEndColumns=( 8, 15)) - self.expect_diagnostic(diagnostics[2], code=6160, lineNo=18, startEndColumns=(15, 36)) + self.expect_diagnostic(diagnostics[0], code=9574, marker=markers["@conversionError"]) + self.expect_diagnostic(diagnostics[1], code=6777, marker=markers["@argumentsRequired"]) + self.expect_diagnostic(diagnostics[2], code=6160, marker=markers["@wrongArgumentsCount"]) def test_publish_diagnostics_errors_multiline(self, solc: JsonRpcProcess) -> None: self.setup_lsp(solc) @@ -453,13 +496,13 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] - self.expect_equal(len(diagnostics), 1, "3 diagnostic messages") + self.expect_equal(len(diagnostics), 1, "1 diagnostic messages") self.expect_equal(diagnostics[0]['code'], 3656, "diagnostic: check code") self.expect_equal( diagnostics[0]['range'], { - 'end': {'character': 1, 'line': 9}, - 'start': {'character': 0, 'line': 7} + 'start': {'character': 0, 'line': 7}, + 'end': {'character': 1, 'line': 10} }, "diagnostic: check range" ) @@ -480,11 +523,58 @@ class SolidityLSPTestSuite: # {{{ report = published_diagnostics[1] self.expect_equal(report['uri'], self.get_test_file_uri('lib'), "Correct file URI") self.expect_equal(len(report['diagnostics']), 1, "one diagnostic") - self.expect_diagnostic(report['diagnostics'][0], code=2072, lineNo=31, startEndColumns=(8, 19)) + marker = self.get_file_tags("lib")["@diagnostics"] + self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker) + + + def get_file_tags(self, test_name: str, verbose=False): + """ + Finds all tags (e.g. @tagname) in the given test and returns them as a + dictionary having the following structure: { + "@tagname": { + "start": { "character": 3, "line": 2 }, + "end": { "character": 30, "line": 2 } + } + } + """ + content = self.get_test_file_contents(test_name) + + markers = {} + + for lineNum, line in enumerate(content.splitlines(), start=-1): + commentStart = line.find("//") + if commentStart == -1: + continue + + for kind, regex in self.marker_regexes.items(): + for match in regex.finditer(line[commentStart:]): + if kind == Marker.SimpleRange: + markers[match.group("tag")] = { + "start": { + "line": lineNum, + "character": match.start("range") + commentStart + }, + "end": { + "line": lineNum, + "character": match.end("range") + commentStart + }} + elif kind == Marker.MultilineRange: + if match.group("delimiter") == "(": + markers[match.group("tag")] = \ + { "start": { "line": lineNum, "character": 0 } } + elif match.group("delimiter") == ")": + markers[match.group("tag")]["end"] = \ + { "line": lineNum, "character": 0 } + + if verbose: + print(markers) + return markers + def test_didChange_in_A_causing_error_in_B(self, solc: JsonRpcProcess) -> None: # Reusing another test but now change some file that generates an error in the other. self.test_textDocument_didOpen_with_relative_import(solc) + marker = self.get_file_tags("lib")["@addFunction"] self.open_file_and_wait_for_diagnostics(solc, 'lib', 2) solc.send_message( 'textDocument/didChange', @@ -496,10 +586,7 @@ class SolidityLSPTestSuite: # {{{ 'contentChanges': [ { - 'range': { - 'start': { 'line': 24, 'character': 0 }, - 'end': { 'line': 29, 'character': 0 } - }, + 'range': marker, 'text': "" # deleting function `add` } ] @@ -512,8 +599,9 @@ class SolidityLSPTestSuite: # {{{ report = published_diagnostics[0] self.expect_equal(report['uri'], self.get_test_file_uri('didOpen_with_import')) diagnostics = report['diagnostics'] + marker = self.get_file_tags("didOpen_with_import")["@diagnostics"] self.expect_equal(len(diagnostics), 1, "now, no diagnostics") - self.expect_diagnostic(diagnostics[0], code=9582, lineNo=9, startEndColumns=(15, 22)) + self.expect_diagnostic(diagnostics[0], code=9582, marker=marker) # The modified file retains the same diagnostics. report = published_diagnostics[1] @@ -543,7 +631,10 @@ class SolidityLSPTestSuite: # {{{ report = published_diagnostics[1] self.expect_equal(report['uri'], self.get_test_file_uri('lib'), "Correct file URI") self.expect_equal(len(report['diagnostics']), 1, "one diagnostic") - self.expect_diagnostic(report['diagnostics'][0], code=2072, lineNo=31, startEndColumns=(8, 19)) + + marker = self.get_file_tags('lib')["@diagnostics"] + self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker) + def test_textDocument_didChange_updates_diagnostics(self, solc: JsonRpcProcess) -> None: self.setup_lsp(solc) @@ -554,9 +645,10 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] self.expect_equal(len(diagnostics), 3, "3 diagnostic messages") - self.expect_diagnostic(diagnostics[0], code=6321, lineNo=13, startEndColumns=(44, 48)) - self.expect_diagnostic(diagnostics[1], code=2072, lineNo= 7, startEndColumns=( 8, 19)) - self.expect_diagnostic(diagnostics[2], code=2072, lineNo=15, startEndColumns=( 8, 20)) + markers = self.get_file_tags(TEST_NAME) + self.expect_diagnostic(diagnostics[0], code=6321, marker=markers["@unusedReturnVariable"]) + self.expect_diagnostic(diagnostics[1], code=2072, marker=markers["@unusedVariable"]) + self.expect_diagnostic(diagnostics[2], code=2072, marker=markers["@unusedContractVariable"]) solc.send_message( 'textDocument/didChange', @@ -566,10 +658,7 @@ class SolidityLSPTestSuite: # {{{ }, 'contentChanges': [ { - 'range': { - 'start': { 'line': 7, 'character': 1 }, - 'end': { 'line': 8, 'character': 1 } - }, + 'range': extendEnd(markers["@unusedVariable"]), 'text': "" } ] @@ -581,13 +670,16 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] self.expect_equal(len(diagnostics), 2) - self.expect_diagnostic(diagnostics[0], code=6321, lineNo=12, startEndColumns=(44, 48)) - self.expect_diagnostic(diagnostics[1], code=2072, lineNo=14, startEndColumns=( 8, 20)) + self.expect_diagnostic(diagnostics[0], code=6321, marker=markers["@unusedReturnVariable"]) + self.expect_diagnostic(diagnostics[1], code=2072, marker=markers["@unusedContractVariable"]) def test_textDocument_didChange_delete_line_and_close(self, solc: JsonRpcProcess) -> None: # Reuse this test to prepare and ensure it is as expected self.test_textDocument_didOpen_with_relative_import(solc) self.open_file_and_wait_for_diagnostics(solc, 'lib', 2) + + marker = self.get_file_tags('lib')["@diagnostics"] + # lib.sol: Fix the unused variable message by removing it. solc.send_message( 'textDocument/didChange', @@ -599,11 +691,7 @@ class SolidityLSPTestSuite: # {{{ 'contentChanges': # delete the in-body statement: `uint unused;` [ { - 'range': - { - 'start': { 'line': 31, 'character': 1 }, - 'end': { 'line': 32, 'character': 1 } - }, + 'range': extendEnd(marker), 'text': "" } ] @@ -719,7 +807,11 @@ class SolidityLSPTestSuite: # {{{ reports = self.wait_for_diagnostics(solc, 2) self.expect_equal(len(reports), 2, '') self.expect_equal(len(reports[0]['diagnostics']), 0, "should not contain diagnostics") - self.expect_diagnostic(reports[1]['diagnostics'][0], 2072, 31, (8, 19)) # unused variable in lib.sol + + marker = self.get_file_tags("lib")["@diagnostics"] + + # unused variable in lib.sol + self.expect_diagnostic(reports[1]['diagnostics'][0], code=2072, marker=marker) # Now close the file and expect the warning for lib.sol to be removed solc.send_message( @@ -807,7 +899,7 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(len(published_diagnostics), 2, "publish diagnostics for 2 files") self.expect_equal(len(published_diagnostics[0]['diagnostics']), 0) self.expect_equal(len(published_diagnostics[1]['diagnostics']), 1) - self.expect_diagnostic(published_diagnostics[1]['diagnostics'][0], 2072, 31, (8, 19)) # unused variable in lib.sol + self.expect_diagnostic(published_diagnostics[1]['diagnostics'][0], 2072, 33, (8, 19)) # unused variable in lib.sol # import directive self.expect_goto_definition_location( @@ -940,7 +1032,7 @@ class SolidityLSPTestSuite: # {{{ document_uri=FILE_URI, document_position=(64, 33), # symbol `RGBColor` right hand side expression. expected_uri=LIB_URI, - expected_lineNo=35, + expected_lineNo=38, expected_startEndColumns=(7, 15), description="Struct constructor." ) @@ -962,7 +1054,7 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(len(published_diagnostics), 2, "publish diagnostics for 2 files") self.expect_equal(len(published_diagnostics[0]['diagnostics']), 0) self.expect_equal(len(published_diagnostics[1]['diagnostics']), 1) - self.expect_diagnostic(published_diagnostics[1]['diagnostics'][0], 2072, 31, (8, 19)) # unused variable in lib.sol + self.expect_diagnostic(published_diagnostics[1]['diagnostics'][0], 2072, 33, (8, 19)) # unused variable in lib.sol # import directive: test symbol alias self.expect_goto_definition_location( diff --git a/test/scripts/fixtures/summarized-benchmark-diff-develop-branch-humanized.md b/test/scripts/fixtures/summarized-benchmark-diff-develop-branch-humanized.md new file mode 100644 index 000000000..ce351a5b7 --- /dev/null +++ b/test/scripts/fixtures/summarized-benchmark-diff-develop-branch-humanized.md @@ -0,0 +1,75 @@ + +### `ir-no-optimize` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|---------------:|---------------:|---------------:| +| bleeps | | | | +| colony | | | | +| elementfi | | | `0%` | +| ens | `!A` | `!A` | `!A` | +| euler | **`+1.43% ❌`** | `0%` | **`+2.47% ❌`** | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + +### `ir-optimize-evm+yul` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|----------------:|----------------:|-----------:| +| bleeps | **`+0.53% ❌`** | `0%` | `-0%` | +| colony | `!A` | `!A` | `!A` | +| elementfi | | | | +| ens | `!A` | `!A` | `!A` | +| euler | **`+12.64% ❌`** | **`+11.98% ❌`** | `0%` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + +### `ir-optimize-evm-only` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|--------------:|---------------:|-----------:| +| bleeps | | | | +| colony | | | | +| elementfi | `!B` | `!B` | `!B` | +| ens | `!A` | `!A` | `!A` | +| euler | `!V` | `!V` | `!V` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + +### `legacy-no-optimize` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|--------------:|---------------:|-----------:| +| bleeps | | | | +| colony | `!B` | `!B` | `!B` | +| elementfi | `!A` | `!B` | | +| ens | `!A` | `!A` | `!A` | +| euler | `!V` | `!V` | `!V` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + +### `legacy-optimize-evm+yul` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|--------------:|---------------:|-----------:| +| bleeps | `0%` | `0%` | `0%` | +| colony | `0%` | | | +| elementfi | `!A` | `!B` | | +| ens | `!A` | `!A` | `!A` | +| euler | `!V` | `!V` | `!V` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | `0%` | `0%` | | + +### `legacy-optimize-evm-only` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|--------------:|---------------:|-----------:| +| bleeps | | | | +| colony | | | | +| elementfi | `!A` | `!A` | `!A` | +| ens | `!A` | `!A` | `!A` | +| euler | `!V` | `!V` | `!V` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + + +`!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 + diff --git a/test/scripts/fixtures/summarized-benchmarks-branch.json b/test/scripts/fixtures/summarized-benchmarks-branch.json new file mode 100644 index 000000000..0ba46b87d --- /dev/null +++ b/test/scripts/fixtures/summarized-benchmarks-branch.json @@ -0,0 +1,100 @@ +{ + "bleeps": { + "ir-optimize-evm+yul": { + "bytecode_size": 132868, + "deployment_gas": 0, + "method_gas": 39289198, + "version": "bb90cd0" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 137869, + "deployment_gas": 0, + "method_gas": 38863224, + "version": "bb90cd0" + } + }, + "colony": { + "legacy-no-optimize": { + "bytecode_size": 664190, + "deployment_gas": null, + "method_gas": null, + "version": "573399b" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 363606, + "deployment_gas": null, + "method_gas": null, + "version": "573399b" + } + }, + "elementfi": { + "legacy-no-optimize": { + "bytecode_size": null, + "deployment_gas": 69200158, + "method_gas": null, + "version": "87f8b5e" + }, + "legacy-optimize-evm+yul": { + "deployment_gas": 40951128, + "version": "87f8b5e" + }, + "ir-optimize-evm-only": {}, + "ir-no-optimize": { + "deployment_gas": null, + "method_gas": 2777867251, + "version": "87f8b5e" + } + }, + "euler": { + "ir-no-optimize": { + "bytecode_size": 328540, + "deployment_gas": 61591870, + "method_gas": 3537419168, + "version": "2ef99fc" + }, + "legacy-no-optimize": { + "bytecode_size": 328540, + "deployment_gas": 62590688, + "method_gas": 3537419168, + "version": "2ef99fc" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 182190, + "deployment_gas": 35236828, + "method_gas": 2777867251, + "version": "2ef99fc" + }, + "legacy-optimize-evm-only": { + "bytecode_size": 205211, + "deployment_gas": 39459629, + "method_gas": 2978467272, + "version": "2ef99fc" + }, + "ir-optimize-evm-only": { + "bytecode_size": 205211, + "deployment_gas": 39459629, + "method_gas": 2978467272, + "version": "2ef99fc" + }, + "ir-optimize-evm+yul": { + "bytecode_size": 205211, + "deployment_gas": 39459629, + "method_gas": 2777867251 + } + }, + "gnosis": { + "ir-optimize-evm+yul": { + "bytecode_size": 56069, + "deployment_gas": null, + "method_gas": null, + "version": "ea09294" + } + }, + "zeppelin": { + "legacy-optimize-evm+yul": { + "bytecode_size": 510428, + "deployment_gas": 94501114, + "version": "af7ec04" + } + } +} diff --git a/test/scripts/fixtures/summarized-benchmarks-develop.json b/test/scripts/fixtures/summarized-benchmarks-develop.json new file mode 100644 index 000000000..1961870f3 --- /dev/null +++ b/test/scripts/fixtures/summarized-benchmarks-develop.json @@ -0,0 +1,99 @@ +{ + "bleeps": { + "ir-optimize-evm+yul": { + "bytecode_size": 132165, + "deployment_gas": 0, + "method_gas": 39289935, + "version": "bb90cd0" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 137869, + "deployment_gas": 0, + "method_gas": 38863224, + "version": "bb90cd0" + } + }, + "colony": { + "ir-optimize-evm+yul": { + "bytecode_size": 363606, + "deployment_gas": null, + "method_gas": null, + "version": "573399b" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 363606, + "deployment_gas": null, + "method_gas": null, + "version": "573399b" + } + }, + "elementfi": { + "legacy-no-optimize": { + "bytecode_size": 890560, + "deployment_gas": null, + "method_gas": null, + "version": "87f8b5e" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 536668, + "version": "87f8b5e" + }, + "legacy-optimize-evm-only": {}, + "ir-no-optimize": { + "bytecode_size": null, + "method_gas": 2777867251, + "version": "87f8b5e" + } + }, + "euler": { + "ir-no-optimize": { + "bytecode_size": 323909, + "deployment_gas": 61591870, + "method_gas": 3452105184, + "version": "2ef99fc" + }, + "legacy-no-optimize": { + "bytecode_size": 323909, + "deployment_gas": 61591870, + "method_gas": 3452105184, + "version": "c23e8bd" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 182190, + "deployment_gas": 35236828, + "method_gas": 2777867251, + "version": "c23e8bd" + }, + "legacy-optimize-evm-only": { + "bytecode_size": 202106, + "deployment_gas": 38790600, + "method_gas": 2907368790, + "version": "v1.2.3" + }, + "ir-optimize-evm-only": { + "bytecode_size": 182190, + "deployment_gas": 35236828, + "method_gas": 2777867251 + }, + "ir-optimize-evm+yul": { + "bytecode_size": 182190, + "deployment_gas": 35236828, + "method_gas": 2777867251 + } + }, + "ens": { + "legacy-optimize-evm+yul": { + "bytecode_size": 156937, + "deployment_gas": 30073789, + "method_gas": 105365362, + "version": "v0.0.8" + } + }, + "zeppelin": { + "legacy-optimize-evm+yul": { + "bytecode_size": 510428, + "deployment_gas": 94501114, + "version": "af7ec04" + } + } +} diff --git a/test/scripts/test_externalTests_benchmark_diff.py b/test/scripts/test_externalTests_benchmark_diff.py new file mode 100644 index 000000000..b40db8e63 --- /dev/null +++ b/test/scripts/test_externalTests_benchmark_diff.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 + +from textwrap import dedent +import json +import unittest + +from unittest_helpers import FIXTURE_DIR, load_fixture + +# NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports +# pragma pylint: disable=import-error +from externalTests.benchmark_diff import BenchmarkDiffer, DifferenceStyle, DiffTableSet, DiffTableFormatter, OutputFormat +# pragma pylint: enable=import-error + +SUMMARIZED_BENCHMARKS_DEVELOP_JSON_PATH = FIXTURE_DIR / 'summarized-benchmarks-develop.json' +SUMMARIZED_BENCHMARKS_BRANCH_JSON_PATH = FIXTURE_DIR / 'summarized-benchmarks-branch.json' + +SUMMARIZED_DIFF_HUMANIZED_MD_PATH = FIXTURE_DIR / 'summarized-benchmark-diff-develop-branch-humanized.md' +SUMMARIZED_DIFF_HUMANIZED_MD = load_fixture(SUMMARIZED_DIFF_HUMANIZED_MD_PATH) + + +class TestBenchmarkDiff(unittest.TestCase): + def setUp(self): + self.maxDiff = 10000 + + def test_benchmark_diff(self): + report_before = json.loads(load_fixture(SUMMARIZED_BENCHMARKS_DEVELOP_JSON_PATH)) + report_after = json.loads(load_fixture(SUMMARIZED_BENCHMARKS_BRANCH_JSON_PATH)) + expected_diff = { + "bleeps": { + "ir-optimize-evm+yul": { + # Numerical difference -> negative/positive/zero. + # Zeros are not skipped to differentiate them from missing values. + "bytecode_size": 132868 - 132165, + "deployment_gas": 0, + "method_gas": 39289198 - 39289935, + }, + "legacy-optimize-evm+yul": { + # No differences within preset -> zeros still present. + "bytecode_size": 0, + "deployment_gas": 0, + "method_gas": 0, + }, + }, + "colony": { + # Preset missing on one side -> replace dict with string + "ir-optimize-evm+yul": "!A", + "legacy-no-optimize": "!B", + "legacy-optimize-evm+yul": { + "bytecode_size": 0, + # Attribute missing on both sides -> skip + #"deployment_gas": + #"method_gas": + }, + }, + "elementfi": { + "legacy-no-optimize": { + # Attributes null on one side -> replace value with string + "bytecode_size": "!A", + "deployment_gas": "!B", + # Attribute null on both sides -> skip + #"method_gas": + }, + "legacy-optimize-evm+yul": { + # Attributes missing on one side -> replace value with string + "bytecode_size": "!A", + "deployment_gas": "!B", + # Attribute missing on both sides -> skip + #"method_gas": + }, + "ir-no-optimize": { + # Attributes missing on one side, null on the other -> skip + #"bytecode_size": + #"deployment_gas": + "method_gas": 0, + }, + # Empty preset missing on one side -> replace dict with string + "legacy-optimize-evm-only": "!A", + "ir-optimize-evm-only": "!B", + }, + "euler": { + # Matching versions -> show attributes, skip version + "ir-no-optimize": { + "bytecode_size": 328540 - 323909, + "deployment_gas": 0, + "method_gas": 3537419168 - 3452105184, + }, + # Different versions, different values -> replace whole preset with string + "legacy-no-optimize": "!V", + # Different versions, same values -> replace whole preset with string + "legacy-optimize-evm+yul": "!V", + # Different versions (not a commit hash), different values -> replace whole preset with string + "legacy-optimize-evm-only": "!V", + # Version missing on one side -> replace whole preset with string + "ir-optimize-evm-only": "!V", + # Version missing on both sides -> assume same version + "ir-optimize-evm+yul": { + "bytecode_size": 205211 - 182190, + "deployment_gas": 39459629 - 35236828, + "method_gas": 0, + }, + }, + "zeppelin": { + "legacy-optimize-evm+yul": { + # Whole project identical -> attributes still present, with zeros + "bytecode_size": 0, + "deployment_gas": 0, + # Field missing on both sides -> skip + #"method_gas": + } + }, + # Empty project missing on one side -> replace its dict with a string + "gnosis": "!B", + "ens": "!A", + } + differ = BenchmarkDiffer(DifferenceStyle.ABSOLUTE, None, OutputFormat.JSON) + self.assertEqual(differ.run(report_before, report_after), expected_diff) + + +class TestBenchmarkDiffer(unittest.TestCase): + def setUp(self): + self.maxDiff = 10000 + + @staticmethod + def _nest(value, levels): + nested_value = value + for level in levels: + nested_value = {level: nested_value} + + return nested_value + + def _assert_single_value_diff_matches(self, differ, cases, nest_result=True, nestings=None): + if nestings is None: + nestings = [[], ['p'], ['p', 's'], ['p', 's', 'a']] + + for levels in nestings: + for (before, after, expected_diff) in cases: + self.assertEqual( + differ.run(self._nest(before, levels), self._nest(after, levels)), + self._nest(expected_diff, levels) if nest_result else expected_diff, + f'Wrong diff for {self._nest(before, levels)} vs {self._nest(after, levels)}' + ) + + def test_empty(self): + for style in DifferenceStyle: + differ = BenchmarkDiffer(style, None, OutputFormat.JSON) + self._assert_single_value_diff_matches(differ, [({}, {}, {})], nest_result=False) + + def test_null(self): + for style in DifferenceStyle: + differ = BenchmarkDiffer(style, None, OutputFormat.JSON) + self._assert_single_value_diff_matches(differ, [(None, None, {})], nest_result=False) + + def test_number_diff_absolute_json(self): + for output_format in OutputFormat: + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.ABSOLUTE, 4, output_format), + [ + (2, 2, 0), + (2, 5, 3), + (5, 2, -3), + (2.0, 2.0, 0), + (2, 2.0, 0), + (2.0, 2, 0), + (2, 2.5, 2.5 - 2), + (2.5, 2, 2 - 2.5), + + (0, 0, 0), + (0, 2, 2), + (0, -2, -2), + + (-3, -1, 2), + (-1, -3, -2), + (2, 0, -2), + (-2, 0, 2), + + (1.00006, 1, 1 - 1.00006), + (1, 1.00006, 1.00006 - 1), + (1.00004, 1, 1 - 1.00004), + (1, 1.00004, 1.00004 - 1), + ], + ) + + def test_number_diff_json(self): + for output_format in OutputFormat: + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.RELATIVE, 4, output_format), + [ + (2, 2, 0), + (2, 5, (5 - 2) / 2), + (5, 2, (2 - 5) / 5), + (2.0, 2.0, 0), + (2, 2.0, 0), + (2.0, 2, 0), + (2, 2.5, (2.5 - 2) / 2), + (2.5, 2, (2 - 2.5) / 2.5), + + (0, 0, 0), + (0, 2, '+INF'), + (0, -2, '-INF'), + + (-3, -1, 0.6667), + (-1, -3, -2), + (2, 0, -1), + (-2, 0, 1), + + (1.00006, 1, -0.0001), + (1, 1.00006, 0.0001), + (1.000004, 1, '-0'), + (1, 1.000004, '+0'), + ], + ) + + def test_number_diff_humanized_json_and_console(self): + for output_format in [OutputFormat.JSON, OutputFormat.CONSOLE]: + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4, output_format), + [ + (2, 2, '0%'), + (2, 5, '+150%'), + (5, 2, '-60%'), + (2.0, 2.0, '0%'), + (2, 2.0, '0%'), + (2.0, 2, '0%'), + (2, 2.5, '+25%'), + (2.5, 2, '-20%'), + + (0, 0, '0%'), + (0, 2, '+INF%'), + (0, -2, '-INF%'), + + (-3, -1, '+66.67%'), + (-1, -3, '-200%'), + (2, 0, '-100%'), + (-2, 0, '+100%'), + + (1.00006, 1, '-0.01%'), + (1, 1.00006, '+0.01%'), + (1.000004, 1, '-0%'), + (1, 1.000004, '+0%'), + ], + ) + + def test_number_diff_humanized_markdown(self): + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4, OutputFormat.MARKDOWN), + [ + (2, 2, '`0%`'), + (2, 5, '**`+150% ❌`**'), + (5, 2, '**`-60% ✅`**'), + (2.0, 2.0, '`0%`'), + (2, 2.0, '`0%`'), + (2.0, 2, '`0%`'), + (2, 2.5, '**`+25% ❌`**'), + (2.5, 2, '**`-20% ✅`**'), + + (0, 0, '`0%`'), + (0, 2, '`+INF%`'), + (0, -2, '`-INF%`'), + + (-3, -1, '**`+66.67% ❌`**'), + (-1, -3, '**`-200% ✅`**'), + (2, 0, '**`-100% ✅`**'), + (-2, 0, '**`+100% ❌`**'), + + (1.00006, 1, '**`-0.01% ✅`**'), + (1, 1.00006, '**`+0.01% ❌`**'), + (1.000004, 1, '`-0%`'), + (1, 1.000004, '`+0%`'), + ], + ) + + def test_type_mismatch(self): + for style in DifferenceStyle: + self._assert_single_value_diff_matches( + BenchmarkDiffer(style, 4, OutputFormat.JSON), + [ + (1, {}, '!T'), + ({}, 1, '!T'), + (1.5, {}, '!T'), + ({}, 1.5, '!T'), + ('1', {}, '!T'), + ({}, '1', '!T'), + (1, '1', '!T'), + ('1', 1, '!T'), + (1.5, '1', '!T'), + ('1', 1.5, '!T'), + ('1', '1', '!T'), + ], + ) + + def test_version_mismatch(self): + for style in DifferenceStyle: + self._assert_single_value_diff_matches( + BenchmarkDiffer(style, 4, OutputFormat.JSON), + [ + ({'a': 123, 'version': 1}, {'a': 123, 'version': 2}, '!V'), + ({'a': 123, 'version': 2}, {'a': 123, 'version': 1}, '!V'), + ({'a': 123, 'version': 'a'}, {'a': 123, 'version': 'b'}, '!V'), + ({'a': 123, 'version': 'a'}, {'a': 123, 'version': 1}, '!V'), + + ({'a': 'a', 'version': 1}, {'a': 'a', 'version': 2}, '!V'), + ({'a': {}, 'version': 1}, {'a': {}, 'version': 2}, '!V'), + ({'s': {'a': 1}, 'version': 1}, {'s': {'a': 1}, 'version': 2}, '!V'), + + ({'a': 123, 'version': 1}, {'a': 456, 'version': 2}, '!V'), + ({'a': 'a', 'version': 1}, {'a': 'b', 'version': 2}, '!V'), + ({'s': {'a': 1}, 'version': 1}, {'s': {'a': 2}, 'version': 2}, '!V'), + ], + ) + + def test_missing(self): + for style in DifferenceStyle: + self._assert_single_value_diff_matches( + BenchmarkDiffer(style, None, OutputFormat.JSON), + [ + (1, None, '!A'), + (None, 1, '!B'), + ('1', None, '!A'), + (None, '1', '!B'), + ({}, None, '!A'), + (None, {}, '!B'), + + ({'x': 1}, {}, {'x': '!A'}), + ({}, {'x': 1}, {'x': '!B'}), + ({'x': 1}, {'x': None}, {'x': '!A'}), + ({'x': None}, {'x': 1}, {'x': '!B'}), + ({'x': 1}, {'y': 1}, {'x': '!A', 'y': '!B'}), + + ({'x': {}}, {}, {'x': '!A'}), + ({}, {'x': {}}, {'x': '!B'}), + ({'p': {'x': {}}}, {}, {'p': '!A'}), + ({}, {'p': {'x': {}}}, {'p': '!B'}), + ], + ) + + def test_missing_vs_null(self): + for style in DifferenceStyle: + self._assert_single_value_diff_matches( + BenchmarkDiffer(style, None, OutputFormat.JSON), + [ + ({'a': None}, {}, {}), + ({}, {'a': None}, {}), + ], + nest_result=False, + ) + + +class TestDiffTableFormatter(unittest.TestCase): + def setUp(self): + self.maxDiff = 10000 + + self.report_before = { + 'project A': { + 'preset X': {'A1': 99, 'A2': 50, 'version': 1}, + 'preset Y': {'A1': 0, 'A2': 50, 'version': 1}, + }, + 'project B': { + 'preset X': { 'A2': 50}, + 'preset Y': {'A1': 0}, + }, + 'project C': { + 'preset X': {'A1': 0, 'A2': 50, 'version': 1}, + }, + 'project D': { + 'preset X': {'A1': 999}, + }, + } + self.report_after = { + 'project A': { + 'preset X': {'A1': 100, 'A2': 50, 'version': 1}, + 'preset Y': {'A1': 500, 'A2': 500, 'version': 2}, + }, + 'project B': { + 'preset X': {'A1': 0}, + 'preset Y': { 'A2': 50}, + }, + 'project C': { + 'preset Y': {'A1': 0, 'A2': 50, 'version': 1}, + }, + 'project E': { + 'preset Y': { 'A2': 999}, + }, + } + + def test_diff_table_formatter(self): + report_before = json.loads(load_fixture(SUMMARIZED_BENCHMARKS_DEVELOP_JSON_PATH)) + report_after = json.loads(load_fixture(SUMMARIZED_BENCHMARKS_BRANCH_JSON_PATH)) + differ = BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4, OutputFormat.MARKDOWN) + diff = differ.run(report_before, report_after) + + self.assertEqual(DiffTableFormatter.run(DiffTableSet(diff), OutputFormat.MARKDOWN), SUMMARIZED_DIFF_HUMANIZED_MD) + + def test_diff_table_formatter_json_absolute(self): + differ = BenchmarkDiffer(DifferenceStyle.ABSOLUTE, 4, OutputFormat.JSON) + diff = differ.run(self.report_before, self.report_after) + + expected_formatted_table = dedent("""\ + { + "preset X": { + "project A": { + "A1": 1, + "A2": 0 + }, + "project B": { + "A1": "!B", + "A2": "!A" + }, + "project C": { + "A1": "!A", + "A2": "!A" + }, + "project D": { + "A1": "!A", + "A2": "!A" + }, + "project E": { + "A1": "!B", + "A2": "!B" + } + }, + "preset Y": { + "project A": { + "A1": "!V", + "A2": "!V" + }, + "project B": { + "A1": "!A", + "A2": "!B" + }, + "project C": { + "A1": "!B", + "A2": "!B" + }, + "project D": { + "A1": "!A", + "A2": "!A" + }, + "project E": { + "A1": "!B", + "A2": "!B" + } + } + }""" + ) + self.assertEqual(DiffTableFormatter.run(DiffTableSet(diff), OutputFormat.JSON), expected_formatted_table) + + def test_diff_table_formatter_console_relative(self): + differ = BenchmarkDiffer(DifferenceStyle.RELATIVE, 4, OutputFormat.CONSOLE) + diff = differ.run(self.report_before, self.report_after) + + expected_formatted_table = dedent(""" + PRESET X + |-----------|--------|----| + | project | A1 | A2 | + |-----------|--------|----| + | project A | 0.0101 | 0 | + | project B | !B | !A | + | project C | !A | !A | + | project D | !A | !A | + | project E | !B | !B | + |-----------|--------|----| + + PRESET Y + |-----------|----|----| + | project | A1 | A2 | + |-----------|----|----| + | project A | !V | !V | + | project B | !A | !B | + | project C | !B | !B | + | project D | !A | !A | + | project E | !B | !B | + |-----------|----|----| + """) + self.assertEqual(DiffTableFormatter.run(DiffTableSet(diff), OutputFormat.CONSOLE), expected_formatted_table) + + def test_diff_table_formatter_markdown_humanized(self): + differ = BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4, OutputFormat.MARKDOWN) + diff = differ.run(self.report_before, self.report_after) + + expected_formatted_table = dedent(""" + ### `preset X` + | project | A1 | A2 | + |:---------:|---------------:|-----:| + | project A | **`+1.01% ❌`** | `0%` | + | project B | `!B` | `!A` | + | project C | `!A` | `!A` | + | project D | `!A` | `!A` | + | project E | `!B` | `!B` | + + ### `preset Y` + | project | A1 | A2 | + |:---------:|-----:|-----:| + | project A | `!V` | `!V` | + | project B | `!A` | `!B` | + | project C | `!B` | `!B` | + | project D | `!A` | `!A` | + | project E | `!B` | `!B` | + + + `!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 + + """) + self.assertEqual(DiffTableFormatter.run(DiffTableSet(diff), OutputFormat.MARKDOWN), expected_formatted_table) diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 2fc095f88..9e900cdc7 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -233,23 +233,23 @@ BOOST_AUTO_TEST_CASE(via_ir_options) BOOST_AUTO_TEST_CASE(assembly_mode_options) { - static vector, AssemblyStack::Machine, AssemblyStack::Language>> const allowedCombinations = { - {{"--machine=ewasm", "--yul-dialect=ewasm", "--assemble"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, - {{"--machine=ewasm", "--yul-dialect=ewasm", "--yul"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, - {{"--machine=ewasm", "--yul-dialect=ewasm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, - {{"--machine=ewasm", "--yul-dialect=evm", "--assemble"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, - {{"--machine=ewasm", "--yul-dialect=evm", "--yul"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, - {{"--machine=ewasm", "--yul-dialect=evm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, - {{"--machine=ewasm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, - {{"--machine=evm", "--yul-dialect=evm", "--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, - {{"--machine=evm", "--yul-dialect=evm", "--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, - {{"--machine=evm", "--yul-dialect=evm", "--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, - {{"--machine=evm", "--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Assembly}, - {{"--machine=evm", "--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Yul}, - {{"--machine=evm", "--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, - {{"--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Assembly}, - {{"--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Yul}, - {{"--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + static vector, YulStack::Machine, YulStack::Language>> const allowedCombinations = { + {{"--machine=ewasm", "--yul-dialect=ewasm", "--assemble"}, YulStack::Machine::Ewasm, YulStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=ewasm", "--yul"}, YulStack::Machine::Ewasm, YulStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=ewasm", "--strict-assembly"}, YulStack::Machine::Ewasm, YulStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=evm", "--assemble"}, YulStack::Machine::Ewasm, YulStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--yul-dialect=evm", "--yul"}, YulStack::Machine::Ewasm, YulStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--yul-dialect=evm", "--strict-assembly"}, YulStack::Machine::Ewasm, YulStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--strict-assembly"}, YulStack::Machine::Ewasm, YulStack::Language::Ewasm}, + {{"--machine=evm", "--yul-dialect=evm", "--assemble"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, + {{"--machine=evm", "--yul-dialect=evm", "--yul"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, + {{"--machine=evm", "--yul-dialect=evm", "--strict-assembly"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, + {{"--machine=evm", "--assemble"}, YulStack::Machine::EVM, YulStack::Language::Assembly}, + {{"--machine=evm", "--yul"}, YulStack::Machine::EVM, YulStack::Language::Yul}, + {{"--machine=evm", "--strict-assembly"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, + {{"--assemble"}, YulStack::Machine::EVM, YulStack::Language::Assembly}, + {{"--yul"}, YulStack::Machine::EVM, YulStack::Language::Yul}, + {{"--strict-assembly"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, }; for (auto const& [assemblyOptions, expectedMachine, expectedLanguage]: allowedCombinations) @@ -302,7 +302,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) "--ewasm-ir", }; commandLine += assemblyOptions; - if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) + if (expectedLanguage == YulStack::Language::StrictAssembly || expectedLanguage == YulStack::Language::Ewasm) commandLine += vector{ "--optimize", "--optimize-runs=1000", @@ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.compiler.outputs.irOptimized = true; expectedOptions.compiler.outputs.ewasm = true; expectedOptions.compiler.outputs.ewasmIR = true; - if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) + if (expectedLanguage == YulStack::Language::StrictAssembly || expectedLanguage == YulStack::Language::Ewasm) { expectedOptions.optimizer.enabled = true; expectedOptions.optimizer.yulSteps = "agf"; diff --git a/test/tools/IsolTestOptions.cpp b/test/tools/IsolTestOptions.cpp index cdd1b85ab..ca7761d74 100644 --- a/test/tools/IsolTestOptions.cpp +++ b/test/tools/IsolTestOptions.cpp @@ -75,9 +75,9 @@ void IsolTestOptions::addOptions() bool IsolTestOptions::parse(int _argc, char const* const* _argv) { - bool const res = CommonOptions::parse(_argc, _argv); + bool const shouldContinue = CommonOptions::parse(_argc, _argv); - if (showHelp || !res) + if (showHelp || !shouldContinue) { std::cout << options << std::endl; return false; @@ -85,7 +85,7 @@ bool IsolTestOptions::parse(int _argc, char const* const* _argv) enforceGasTest = enforceGasTest || (evmVersion() == langutil::EVMVersion{} && !useABIEncoderV1); - return res; + return shouldContinue; } void IsolTestOptions::validate() const diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index ae2ce6e24..ce45819b8 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -433,8 +433,9 @@ int main(int argc, char const *argv[]) { auto options = std::make_unique(); - if (!options->parse(argc, argv)) - return -1; + bool shouldContinue = options->parse(argc, argv); + if (!shouldContinue) + return EXIT_SUCCESS; options->validate(); CommonOptions::setSingleton(std::move(options)); @@ -443,7 +444,7 @@ int main(int argc, char const *argv[]) auto& options = dynamic_cast(CommonOptions::get()); if (!solidity::test::loadVMs(options)) - return 1; + return EXIT_FAILURE; if (options.disableSemanticTests) cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; @@ -479,7 +480,7 @@ int main(int argc, char const *argv[]) if (stats) global_stats += *stats; else - return 1; + return EXIT_FAILURE; } cout << endl << "Summary: "; @@ -497,11 +498,22 @@ int main(int argc, char const *argv[]) if (options.disableSemanticTests) cout << "\nNOTE: Skipped semantics tests.\n" << endl; - return global_stats ? 0 : 1; + return global_stats ? EXIT_SUCCESS : EXIT_FAILURE; } - catch (std::exception const& _exception) + catch (boost::program_options::error const& exception) { - cerr << _exception.what() << endl; - return 1; + cerr << exception.what() << endl; + return EXIT_FAILURE; + } + catch (std::runtime_error const& exception) + { + cerr << exception.what() << endl; + return EXIT_FAILURE; + } + catch (...) + { + cerr << "Unhandled exception caught." << endl; + cerr << boost::current_exception_diagnostic_information() << endl; + return EXIT_FAILURE; } } diff --git a/test/tools/ossfuzz/SolidityEvmoneInterface.h b/test/tools/ossfuzz/SolidityEvmoneInterface.h index da0fed58f..734caf560 100644 --- a/test/tools/ossfuzz/SolidityEvmoneInterface.h +++ b/test/tools/ossfuzz/SolidityEvmoneInterface.h @@ -22,7 +22,7 @@ #include -#include +#include #include diff --git a/test/tools/ossfuzz/YulEvmoneInterface.cpp b/test/tools/ossfuzz/YulEvmoneInterface.cpp index e21a881c6..2d7ddccc1 100644 --- a/test/tools/ossfuzz/YulEvmoneInterface.cpp +++ b/test/tools/ossfuzz/YulEvmoneInterface.cpp @@ -35,7 +35,7 @@ bytes YulAssembler::assemble() if (m_optimiseYul) m_stack.optimize(); - return m_stack.assemble(AssemblyStack::Machine::EVM).bytecode->bytecode; + return m_stack.assemble(YulStack::Machine::EVM).bytecode->bytecode; } evmc::result YulEvmoneUtility::deployCode(bytes const& _input, EVMHost& _host) diff --git a/test/tools/ossfuzz/YulEvmoneInterface.h b/test/tools/ossfuzz/YulEvmoneInterface.h index 0b3384940..709e5475f 100644 --- a/test/tools/ossfuzz/YulEvmoneInterface.h +++ b/test/tools/ossfuzz/YulEvmoneInterface.h @@ -19,7 +19,7 @@ #include -#include +#include #include @@ -37,7 +37,7 @@ public: ): m_stack( _version, - solidity::yul::AssemblyStack::Language::StrictAssembly, + solidity::yul::YulStack::Language::StrictAssembly, _optSettings, langutil::DebugInfoSelection::All() ), @@ -46,7 +46,7 @@ public: {} solidity::bytes assemble(); private: - solidity::yul::AssemblyStack m_stack; + solidity::yul::YulStack m_stack; std::string m_yulProgram; bool m_optimiseYul; }; diff --git a/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp index 7142ec23c..a353ca10b 100644 --- a/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include #include #include @@ -37,9 +37,9 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) YulStringRepository::reset(); string input(reinterpret_cast(_data), _size); - AssemblyStack stack( + YulStack stack( langutil::EVMVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), langutil::DebugInfoSelection::All() ); @@ -49,7 +49,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) try { - MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM); + MachineAssemblyObject obj = stack.assemble(YulStack::Machine::EVM); solAssert(obj.bytecode, ""); } catch (StackTooDeepError const&) diff --git a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp index dce6c6162..dd66102c1 100644 --- a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -60,9 +60,9 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) YulStringRepository::reset(); - AssemblyStack stack( + YulStack stack( langutil::EVMVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), DebugInfoSelection::All() ); diff --git a/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp index 05a0179bf..94a837d5a 100644 --- a/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include #include #include @@ -38,9 +38,9 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) YulStringRepository::reset(); string input(reinterpret_cast(_data), _size); - AssemblyStack stack( + YulStack stack( langutil::EVMVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), DebugInfoSelection::All() ); diff --git a/test/tools/ossfuzz/yulProtoFuzzer.cpp b/test/tools/ossfuzz/yulProtoFuzzer.cpp index ca15b3769..74183aa3f 100644 --- a/test/tools/ossfuzz/yulProtoFuzzer.cpp +++ b/test/tools/ossfuzz/yulProtoFuzzer.cpp @@ -25,7 +25,7 @@ #include -#include +#include #include #include @@ -61,10 +61,10 @@ DEFINE_PROTO_FUZZER(Program const& _input) YulStringRepository::reset(); - // AssemblyStack entry point - AssemblyStack stack( + // YulStack entry point + YulStack stack( version, - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), DebugInfoSelection::All() ); diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 4cafccb28..c3ad9c9f3 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -26,7 +26,7 @@ #include -#include +#include #include #include @@ -60,10 +60,10 @@ DEFINE_PROTO_FUZZER(Program const& _input) YulStringRepository::reset(); - // AssemblyStack entry point - AssemblyStack stack( + // YulStack entry point + YulStack stack( version, - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), DebugInfoSelection::All() ); diff --git a/test/tools/yulrun.cpp b/test/tools/yulrun.cpp index 570299768..fd43d222d 100644 --- a/test/tools/yulrun.cpp +++ b/test/tools/yulrun.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -56,9 +56,9 @@ namespace pair, shared_ptr> parse(string const& _source) { - AssemblyStack stack( + YulStack stack( langutil::EVMVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::Default() );