diff options
305 files changed, 4550 insertions, 1776 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index f8e380d9..c975740d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -233,6 +233,9 @@ jobs: command: codecov --flags all --gcov-root build - store_test_results: path: test_results/ + - store_artifacts: + path: test_results/ + destination: test_results/ test_x86_mac: macos: @@ -254,6 +257,9 @@ jobs: - run: *run_tests - store_test_results: path: test_results/ + - store_artifacts: + path: test_results/ + destination: test_results/ docs: docker: @@ -45,6 +45,7 @@ deps/cache # IDE files .idea +.vscode browse.VC.db CMakeLists.txt.user /CMakeSettings.json diff --git a/Changelog.md b/Changelog.md index fbb41ece..05636a30 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,7 @@ Breaking Changes: * Commandline interface: Remove obsolete ``--formal`` option. * Commandline interface: Rename the ``--julia`` option to ``--yul``. * Commandline interface: Require ``-`` if standard input is used as source. + * Commandline interface: Use hash of library name for link placeholder instead of name itself. * Compiler interface: Disallow remappings with empty prefix. * Control Flow Analyzer: Consider mappings as well when checking for uninitialized return values. * Control Flow Analyzer: Turn warning about returning uninitialized storage pointers into an error. @@ -36,6 +37,7 @@ Breaking Changes: * General: C99-style scoping rules are enforced now. This was already the case in the experimental 0.5.0 mode. * General: Disallow combining hex numbers with unit denominations (e.g. ``0x1e wei``). This was already the case in the experimental 0.5.0 mode. * JSON AST: Remove ``constant`` and ``payable`` fields (the information is encoded in the ``stateMutability`` field). + * JSON AST: Replace the ``isConstructor`` field by a new ``kind`` field, which can be ``constructor``, ``fallback`` or ``function``. * Interface: Remove "clone contract" feature. The ``--clone-bin`` and ``--combined-json clone-bin`` commandline options are not available anymore. * Name Resolver: Do not exclude public state variables when looking for conflicting declarations. * Optimizer: Remove the no-op ``PUSH1 0 NOT AND`` sequence. @@ -98,6 +100,8 @@ Compiler Features: * Tests: Determine transaction status during IPC calls. * Code Generator: Allocate and free local variables according to their scope. * Removed ``pragma experimental "v0.5.0";``. + * Syntax Checker: Improved error message for lookup in function types. + * Name Resolver: Updated name suggestion look up function to take into account length of the identifier: 1: no search, 2-3: at most one change, 4-: at most two changes Bugfixes: * Build System: Support versions of CVC4 linked against CLN instead of GMP. In case of compilation issues due to the experimental SMT solver support, the solvers can be disabled when configuring the project with CMake using ``-DUSE_CVC4=OFF`` or ``-DUSE_Z3=OFF``. @@ -124,6 +128,7 @@ Bugfixes: * Type Checker: Dynamic types as key for public mappings return error instead of assertion fail. * Type Checker: Fix internal error when array index value is too large. * Type Checker: Fix internal error for array type conversions. + * Type Checker: Fix internal error when array index is not an unsigned. * Type System: Allow arbitrary exponents for literals with a mantissa of zero. * Parser: Fix incorrect source location for nameless parameters. diff --git a/appveyor.yml b/appveyor.yml index 81ec711d..91ea8af7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -55,7 +55,6 @@ install: $fileContent += "`n-----END RSA PRIVATE KEY-----`n"; Set-Content c:\users\appveyor\.ssh\id_rsa $fileContent } - - git submodule update --init --recursive - ps: $prerelease = "nightly." - ps: $prerelease += Get-Date -format "yyyy.M.d" - ps: if($env:appveyor_repo_branch -eq 'release') { Set-Content prerelease.txt $null } else { Set-Content prerelease.txt $prerelease } diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 9094000e..7b227297 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -154,6 +154,18 @@ Command Line and JSON Interfaces * The JSON AST fields ``constant`` and ``payable`` were removed. The information is now present in the ``stateMutability`` field. +* The JSON AST field ``isConstructor`` of the ``FunctionDefinition`` + node was replaced by a field called ``kind`` which can have the + value ``"constructor"``, ``"fallback"`` or ``"function"``. + +* In unlinked binary hex files, library address placeholders are now + the first 36 hex characters of the keccak256 hash of the fully qualified + library name, surrounded by ``$...$``. Previously, + just the fully qualified library name was used. + This recudes the chances of collisions, especially when long paths are used. + Binary files now also contain a list of mappings from these placeholders + to the fully qualified names. + Constructors ------------ diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index f31d9d45..2c206dad 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -405,6 +405,8 @@ Offset ``f`` points to the start of the content of the array ``[[1, 2], [3]]`` w Offset ``g`` points to the start of the content of the array ``["one", "two", "three"]`` which is line 10 (320 bytes); thus ``g = 0x0000000000000000000000000000000000000000000000000000000000000140``. +.. _abi_events: + Events ====== diff --git a/docs/assembly.rst b/docs/assembly.rst index 20fb0cd5..5bb9825a 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -282,14 +282,14 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | extcodehash(a) | | C | code hash of address a | +-------------------------+-----+---+-----------------------------------------------------------------+ -| create(v, p, s) | | F | create new contract with code mem[p...(p+s)) and send v wei | +| create(v, p, n) | | F | create new contract with code mem[p...(p+n)) and send v wei | | | | | and return the new address | +-------------------------+-----+---+-----------------------------------------------------------------+ -| create2(v, n, p, s) | | C | create new contract with code mem[p...(p+s)) at address | -| | | | keccak256(0xff . self . n . keccak256(mem[p...(p+s))) | +| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address | +| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) | | | | | and send v wei and return the new address, where ``0xff`` is a | -| | | | 8 byte value, ``self`` is the current contract's address | -| | | | as a 20 byte value and ``n`` is a big-endian 256-bit value | +| | | | 8 byte value, ``this`` is the current contract's address | +| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value | +-------------------------+-----+---+-----------------------------------------------------------------+ | call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) | | insize, out, outsize) | | | providing g gas and v wei and output area | diff --git a/docs/contracts.rst b/docs/contracts.rst index be89727d..d4160eac 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -31,6 +31,11 @@ When a contract is created, its constructor_ (a function declared with the ``co A constructor is optional. Only one constructor is allowed, which means overloading is not supported. +After the constructor has executed, the final code of the contract is deployed to the +blockchain. This code includes all public and external functions and all functions +that are reachable from there through function calls. The deployed code does not +include the constructor code or internal functions only called from the constructor. + .. index:: constructor;arguments Internally, constructor arguments are passed :ref:`ABI encoded <ABI>` after the code of @@ -485,7 +490,11 @@ Functions can be declared ``view`` in which case they promise not to modify the .. note:: If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used for ``view`` functions which enforces the state - to stay unmodified as part of the EVM execution. + to stay unmodified as part of the EVM execution. For library ``view`` functions + ``DELEGATECALL`` is used, because there is no combined ``DELEGATECALL`` and ``STATICCALL``. + This means library ``view`` functions do not have run-time checks that prevent state + modifications. This should not impact security negatively because library code is + usually known at compile-time and the static checker performs compile-time checks. The following statements are considered modifying the state: @@ -756,39 +765,60 @@ converted to ``uint8``. Events ****** -Events allow the convenient usage of the EVM logging facilities, -which in turn can be used to "call" JavaScript callbacks in the user interface -of a dapp, which listen for these events. +Solidity events give an abstraction on top of the EVM's logging functionality. +Applications can subscribe and listen to these events through the RPC interface of an Ethereum client. -Events are -inheritable members of contracts. When they are called, they cause the +Events are inheritable members of contracts. When you call them, they cause the arguments to be stored in the transaction's log - a special data structure -in the blockchain. These logs are associated with the address of -the contract and will be incorporated into the blockchain -and stay there as long as a block is accessible (forever as of -Frontier and Homestead, but this might change with Serenity). Log and -event data is not accessible from within contracts (not even from -the contract that created them). - -"Simple payment verification" (SPV) proofs for logs are possible, so if an external entity supplies -a contract with such a proof, it can check that the log actually -exists inside the blockchain. Be aware that block headers have to be supplied because -the contract can only see the last 256 block hashes. - -Up to three parameters can -receive the attribute ``indexed`` which will cause the respective arguments -to be stored in a special data structure as so-called "topics", which allows them to be searched for, -for example when filtering a sequence of blocks for certain events. Events can always -be filtered by the address of the contract that emitted the event. Also, -the hash of the signature of the event is one of the topics except if you -declared the event with ``anonymous`` specifier. This means that it is -not possible to filter for specific anonymous events by name. +in the blockchain. These logs are associated with the address of the contract, +are incorporated into the blockchain, and stay there as long as a block is +accessible (forever as of the Frontier and Homestead releases, but this might +change with Serenity). The Log and its event data is not accessible from within +contracts (not even from the contract that created them). + +It is possible to request a simple payment verification (SPV) for logs, so if +an external entity supplies a contract with such a verification, it can check +that the log actually exists inside the blockchain. You have to supply block headers +because the contract can only see the last 256 block hashes. + +You can add the attribute ``indexed`` to up to three parameters which adds them +to a special data structure known as :ref:`"topics" <abi_events>` instead of +the data part of the log. If you use arrays (including ``string`` and ``bytes``) +as indexed arguments, its Keccak-256 hash is stored as a topic instead, this is +because a topic can only hold a single word (32 bytes). + +All parameters without the ``indexed`` attribute are :ref:`ABI-encoded <ABI>` +into the data part of the log. + +Topics allow you to search for events, for example when filtering a sequence of +blocks for certain events. You can also filter events by the address of the +contract that emitted the event. + +For example, the code below uses the web3.js ``subscribe("logs")`` +`method <https://web3js.readthedocs.io/en/1.0/web3-eth-subscribe.html#subscribe-logs>`_ to filter +logs that match a topic with a certain address value: + +.. code-block:: javascript + + var options = { + fromBlock: 0, + address: web3.eth.defaultAccount, + topics: ["0x0000000000000000000000000000000000000000000000000000000000000000", null, null] + }; + web3.eth.subscribe('logs', options, function (error, result) { + if (!error) + console.log(result); + }) + .on("data", function (log) { + console.log(log); + }) + .on("changed", function (log) { + }); -If arrays (including ``string`` and ``bytes``) are used as indexed arguments, the -Keccak-256 hash of it is stored as topic instead. This is because a topic -can only hold a single word (32 bytes). -All non-indexed arguments will be :ref:`ABI-encoded <ABI>` into the data part of the log. +The hash of the signature of the event is one of the topics, except if you +declared the event with the ``anonymous`` specifier. This means that it is +not possible to filter for specific anonymous events by name. :: @@ -811,7 +841,7 @@ All non-indexed arguments will be :ref:`ABI-encoded <ABI>` into the data part of } } -The use in the JavaScript API would be as follows: +The use in the JavaScript API is as follows: :: @@ -823,19 +853,35 @@ The use in the JavaScript API would be as follows: // watch for changes event.watch(function(error, result){ - // result will contain various information - // including the arguments given to the `Deposit` - // call. + // result contains non-indexed arguments and topics + // given to the `Deposit` call. if (!error) console.log(result); }); + // Or pass a callback to start watching immediately var event = clientReceipt.Deposit(function(error, result) { if (!error) console.log(result); }); +The output of the above looks like the following (trimmed): + +.. code-block:: json + + { + "returnValues": { + "_from": "0x1111…FFFFCCCC", + "_id": "0x50…sd5adb20", + "_value": "0x420042" + }, + "raw": { + "data": "0x7f…91385", + "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] + } + } + .. index:: ! log Low-Level Interface to Logs @@ -1048,8 +1094,13 @@ initialisation code. Before the constructor code is executed, state variables are initialised to their specified value if you initialise them inline, or zero if you do not. -After the constructor has run, the final code of the contract is returned. The deployment of +After the constructor has run, the final code of the contract is deployed +to the blockchain. The deployment of the code costs additional gas linear to the length of the code. +This code includes all functions that are part of the public interface +and all functions that are reachable from there through function calls. +It does not include the constructor code or internal functions that are +only called from the constructor. Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the default constructor, which is diff --git a/docs/contributing.rst b/docs/contributing.rst index 361570a0..32696520 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -82,6 +82,11 @@ To run a basic set of tests that neither require ``aleth`` nor ``libz3``, run ``./scripts/soltest.sh --no-ipc --no-smt``. This script will run ``build/test/soltest`` internally. +.. note :: + + Those working in a Windows environment wanting to run the above basic sets without aleth or libz3 in Git Bash, you would have to do: ``./build/test/RelWithDebInfo/soltest.exe -- --no-ipc --no-smt``. + If you're running this in plain Command Prompt, use ``.\build\test\RelWithDebInfo\soltest.exe -- --no-ipc --no-smt``. + The option ``--no-smt`` disables the tests that require ``libz3`` and ``--no-ipc`` disables those that require ``aleth``. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 80311a63..353bb61d 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -279,7 +279,7 @@ groupings of expressions. :: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract C { uint[] data; diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index a474f905..0f8b34f8 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -173,7 +173,7 @@ situation. If you do not want to throw, you can return a pair:: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract C { uint[] counters; diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 9d4be70d..f8de0e8d 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -245,12 +245,6 @@ you should fork Solidity and add your personal fork as a second remote: git remote add personal git@github.com:[username]/solidity.git -Solidity has git submodules. Ensure they are properly loaded: - -.. code-block:: bash - - git submodule update --init --recursive - External Dependencies --------------------- diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index f5d5f89e..c8a45d30 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -332,14 +332,15 @@ Transactions ============ A transaction is a message that is sent from one account to another -account (which might be the same or the special zero-account, see below). +account (which might be the same or empty, see below). It can include binary data (which is called "payload") and Ether. If the target account contains code, that code is executed and the payload is provided as input data. -If the target account is the zero-account (the account with the -address ``0``), the transaction creates a **new contract**. +If the target account is not set (the transaction does not have +a recipient or the recipient is set to ``null``), the transaction +creates a **new contract**. As already mentioned, the address of that contract is not the zero address but an address derived from the sender and its number of transactions sent (the "nonce"). The payload diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 12603f2e..8cc52c8f 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -34,18 +34,20 @@ Statically-sized variables (everything except mapping and dynamically-sized arra The elements of structs and arrays are stored after each other, just as if they were given explicitly. +Mappings and Dynamic Arrays +=========================== + Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash computation to find the starting position of the value or the array data. These starting positions are always full stack slots. -The mapping or the dynamic array itself -occupies an (unfilled) slot in storage at some position ``p`` according to the above rule (or by -recursively applying this rule for mappings to mappings or arrays of arrays). For a dynamic array, this slot stores the number of elements in the array (byte arrays and strings are an exception here, see below). For a mapping, the slot is unused (but it is needed so that two equal mappings after each other will use a different hash distribution). -Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key +The mapping or the dynamic array itself occupies a slot in storage at some position ``p`` +according to the above rule (or by recursively applying this rule for mappings of mappings or arrays of arrays). For dynamic arrays, +this slot stores the number of elements in the array (byte arrays and strings are an exception, see :ref:`below <bytes-and-string>`). +For mappings, the slot is unused (but it is needed so that two equal mappings after each other will use a different +hash distribution). Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key ``k`` is located at ``keccak256(k . p)`` where ``.`` is concatenation. If the value is again a non-elementary type, the positions are found by adding an offset of ``keccak256(k . p)``. -``bytes`` and ``string`` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most ``31`` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. If it is longer, the main slot stores ``length * 2 + 1`` and the data is stored as usual in ``keccak256(slot)``. - So for the following contract snippet:: pragma solidity >=0.4.0 <0.6.0; @@ -58,6 +60,21 @@ So for the following contract snippet:: The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``. +.. _bytes-and-string: + +``bytes`` and ``string`` +------------------------ + +``bytes`` and ``string`` are encoded identically. For short byte arrays, they store their data in the same +slot where the length is also stored. In particular: if the data is at most ``31`` bytes long, it is stored +in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. +For byte arrays that store data which is ``32`` or more bytes long, the main slot stores ``length * 2 + 1`` and the data is +stored as usual in ``keccak256(slot)``. This means that you can distinguish a short array from a long array +by checking if the lowest bit is set: short (not set) and long (set). + +.. note:: + Handling invalidly encoded slots is currently not supported but may be added in the future. + .. index: memory layout **************** diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 1bbd42f8..8f58f339 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -389,7 +389,7 @@ high or low invalid bids. :: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract BlindAuction { struct Bid { diff --git a/docs/types.rst b/docs/types.rst index 50a2dc60..bd5d1734 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -458,6 +458,7 @@ a non-rational number). uint128 b = 2.5 + a + 0.5; .. index:: literal, literal;string, string +.. _string_literals: String Literals --------------- @@ -501,7 +502,7 @@ Hexadecimal Literals Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. -Hexadecimal literals behave like string literals and have the same convertibility restrictions. +Hexadecimal literals behave like :ref:`string literals <string_literals>` and have the same convertibility restrictions. .. index:: enum diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 39520bec..9ba6caa5 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -41,14 +41,26 @@ If there are multiple matches due to remappings, the one with the longest common For security reasons the compiler has restrictions what directories it can access. Paths (and their subdirectories) of source files specified on the commandline and paths defined by remappings are allowed for import statements, but everything else is rejected. Additional paths (and their subdirectories) can be allowed via the ``--allow-paths /sample/path,/another/sample/path`` switch. -If your contracts use :ref:`libraries <libraries>`, you will notice that the bytecode contains substrings of the form ``__LibraryName______``. You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points: +If your contracts use :ref:`libraries <libraries>`, you will notice that the bytecode contains substrings of the form ``__$53aea86b7d70b31448b230b20ae141a537$__``. These are placeholders for the actual library addresses. +The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name. +The bytecode file will also contain lines of the form ``// <placeholder> -> <fq library name>`` at the end to help +identify which libraries the placeholders represent. Note that the fully qualified library name +is the path of its source file and the library name separated by ``:``. +You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points: -Either add ``--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. +Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. -If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__LibraryName____``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. +If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. +.. note:: + The library placeholder used to be the fully qualified name of the library itself + instead of the hash of it. This format is still supported by ``solc --link`` but + the compiler will no longer output it. This change was made to reduce + the likelihood of a collision between libraries, since only the first 36 characters + of the fully qualified library name could be used. + .. _evm-version: .. index:: ! EVM version, compile target diff --git a/docs/yul.rst b/docs/yul.rst index cfeec4db..9e9fac8e 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -44,7 +44,7 @@ and ``mod`` are available either natively or as functions and computes exponenti switch exponent case 0:u256 { result := 1:u256 } case 1:u256 { result := base } - default: + default { result := power(mul(base, base), div(exponent, 2:u256)) switch mod(exponent, 2:u256) @@ -415,14 +415,14 @@ The following functions must be available: +---------------------------------------------+-----------------------------------------------------------------+ | *Execution control* | +---------------------------------------------+-----------------------------------------------------------------+ -| create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei | +| create(v:u256, p:u256, n:u256) | create new contract with code mem[p..(p+n)) and send v wei | | | and return the new address | +---------------------------------------------+-----------------------------------------------------------------+ -| create2(v:u256, n:u256, p:u256, s:u256) | create new contract with code mem[p...(p+s)) at address | -| | keccak256(0xff . self . n . keccak256(mem[p...(p+s))) | +| create2(v:u256, p:u256, n:u256, s:u256) | create new contract with code mem[p...(p+n)) at address | +| | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) | | | and send v wei and return the new address, where ``0xff`` is a | -| | 8 byte value, ``self`` is the current contract's address | -| | as a 20 byte value and ``n`` is a big-endian 256-bit value | +| | 8 byte value, ``this`` is the current contract's address | +| | as a 20 byte value and ``s`` is a big-endian 256-bit value | +---------------------------------------------+-----------------------------------------------------------------+ | call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) | | insize:u256, out:u256, | providing g gas and v wei and output area | diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index 445d11cd..6d7c74d7 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -76,18 +76,18 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) bool dev::passesAddressChecksum(string const& _str, bool _strict) { - string s = _str.substr(0, 2) == "0x" ? _str.substr(2) : _str; + string s = _str.substr(0, 2) == "0x" ? _str : "0x" + _str; - if (s.length() != 40) + if (s.length() != 42) return false; if (!_strict && ( - _str.find_first_of("abcdef") == string::npos || - _str.find_first_of("ABCDEF") == string::npos + s.find_first_of("abcdef") == string::npos || + s.find_first_of("ABCDEF") == string::npos )) return true; - return _str == dev::getChecksummedAddress(_str); + return s == dev::getChecksummedAddress(s); } string dev::getChecksummedAddress(string const& _addr) diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index e410af5c..98136b44 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -25,11 +25,14 @@ #include <libdevcore/Common.h> +#include <boost/optional.hpp> + #include <vector> #include <type_traits> #include <cstring> #include <string> #include <set> +#include <functional> namespace dev { @@ -229,6 +232,36 @@ bool contains(T const& _t, V const& _v) return std::end(_t) != std::find(std::begin(_t), std::end(_t), _v); } + +/// Function that iterates over a vector, calling a function on each of its +/// elements. If that function returns a vector, the element is replaced by +/// the returned vector. During the iteration, the original vector is only valid +/// on the current element and after that. The actual replacement takes +/// place at the end, but already visited elements might be invalidated. +/// If nothing is replaced, no copy is performed. +template <class T> +void iterateReplacing(std::vector<T>& _vector, std::function<boost::optional<std::vector<T>>(T&)> _f) +{ + bool useModified = false; + std::vector<T> modifiedVector; + for (size_t i = 0; i < _vector.size(); ++i) + { + if (boost::optional<std::vector<T>> r = _f(_vector[i])) + { + if (!useModified) + { + std::move(_vector.begin(), _vector.begin() + i, back_inserter(modifiedVector)); + useModified = true; + } + modifiedVector += std::move(*r); + } + else if (useModified) + modifiedVector.emplace_back(std::move(_vector[i])); + } + if (useModified) + _vector = std::move(modifiedVector); +} + /// @returns true iff @a _str passess the hex address checksum test. /// @param _strict if false, hex strings with only uppercase or only lowercase letters /// are considered valid. diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 9693d02a..1aa3504c 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -23,7 +23,6 @@ #include <iostream> #include <cstdlib> #include <fstream> -#include <stdio.h> #if defined(_WIN32) #include <windows.h> #else @@ -95,7 +94,7 @@ void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDe { // create directory if not existent fs::path p(_file); - if (!fs::exists(p.parent_path())) + if (!p.parent_path().empty() && !fs::exists(p.parent_path())) { fs::create_directories(p.parent_path()); try diff --git a/libdevcore/Visitor.h b/libdevcore/Visitor.h new file mode 100644 index 00000000..4030c928 --- /dev/null +++ b/libdevcore/Visitor.h @@ -0,0 +1,128 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Visitor templates. + */ + +#pragma once + +#include <functional> +#include <boost/variant/static_visitor.hpp> + +namespace dev +{ + +/// Generic visitor used as follows: +/// boost::apply_visitor(GenericVisitor<Class1, Class2>( +/// [](Class1& _c) { _c.f(); }, +/// [](Class2& _c) { _c.g(); } +/// ), variant); +/// This one does not have a fallback and will fail at +/// compile-time if you do not specify all variants. + +template <class...> +struct GenericVisitor{}; + +template <class Visitable, class... Others> +struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...> +{ + using GenericVisitor<Others...>::operator (); + explicit GenericVisitor( + std::function<void(Visitable&)> _visitor, + std::function<void(Others&)>... _otherVisitors + ): + GenericVisitor<Others...>(std::move(_otherVisitors)...), + m_visitor(std::move(_visitor)) + {} + + void operator()(Visitable& _v) const { m_visitor(_v); } + + std::function<void(Visitable&)> m_visitor; +}; +template <> +struct GenericVisitor<>: public boost::static_visitor<> { + void operator()() const {} +}; + +/// Generic visitor with fallback: +/// boost::apply_visitor(GenericFallbackVisitor<Class1, Class2>( +/// [](Class1& _c) { _c.f(); }, +/// [](Class2& _c) { _c.g(); } +/// ), variant); +/// This one DOES have a fallback and will NOT fail at +/// compile-time if you do not specify all variants. + +template <class...> +struct GenericFallbackVisitor{}; + +template <class Visitable, class... Others> +struct GenericFallbackVisitor<Visitable, Others...>: public GenericFallbackVisitor<Others...> +{ + explicit GenericFallbackVisitor( + std::function<void(Visitable&)> _visitor, + std::function<void(Others&)>... _otherVisitors + ): + GenericFallbackVisitor<Others...>(std::move(_otherVisitors)...), + m_visitor(std::move(_visitor)) + {} + + using GenericFallbackVisitor<Others...>::operator (); + void operator()(Visitable& _v) const { m_visitor(_v); } + + std::function<void(Visitable&)> m_visitor; +}; +template <> +struct GenericFallbackVisitor<>: public boost::static_visitor<> { + template <class T> + void operator()(T&) const { } +}; + +/// Generic visitor with fallback that can return a value: +/// boost::apply_visitor(GenericFallbackReturnsVisitor<ReturnType, Class1, Class2>( +/// [](Class1& _c) { return _c.f(); }, +/// [](Class2& _c) { return _c.g(); } +/// ), variant); +/// This one DOES have a fallback and will NOT fail at +/// compile-time if you do not specify all variants. +/// The fallback {}-constructs the return value. + +template <class R, class...> +struct GenericFallbackReturnsVisitor{}; + +template <class R, class Visitable, class... Others> +struct GenericFallbackReturnsVisitor<R, Visitable, Others...>: public GenericFallbackReturnsVisitor<R, Others...> +{ + explicit GenericFallbackReturnsVisitor( + std::function<R(Visitable&)> _visitor, + std::function<R(Others&)>... _otherVisitors + ): + GenericFallbackReturnsVisitor<R, Others...>(std::move(_otherVisitors)...), + m_visitor(std::move(_visitor)) + {} + + using GenericFallbackReturnsVisitor<R, Others...>::operator (); + R operator()(Visitable& _v) const { return m_visitor(_v); } + + std::function<R(Visitable&)> m_visitor; +}; +template <class R> +struct GenericFallbackReturnsVisitor<R>: public boost::static_visitor<R> { + template <class T> + R operator()(T&) const { return {}; } +}; + +} diff --git a/libevmasm/LinkerObject.cpp b/libevmasm/LinkerObject.cpp index 1d5efecb..a11f2378 100644 --- a/libevmasm/LinkerObject.cpp +++ b/libevmasm/LinkerObject.cpp @@ -21,6 +21,7 @@ #include <libevmasm/LinkerObject.h> #include <libdevcore/CommonData.h> +#include <libdevcore/SHA3.h> using namespace dev; using namespace dev::eth; @@ -50,14 +51,19 @@ string LinkerObject::toHex() const for (auto const& ref: linkReferences) { size_t pos = ref.first * 2; - string const& name = ref.second; + string hash = libraryPlaceholder(ref.second); hex[pos] = hex[pos + 1] = hex[pos + 38] = hex[pos + 39] = '_'; for (size_t i = 0; i < 36; ++i) - hex[pos + 2 + i] = i < name.size() ? name[i] : '_'; + hex[pos + 2 + i] = hash.at(i); } return hex; } +string LinkerObject::libraryPlaceholder(string const& _libraryName) +{ + return "$" + keccak256(_libraryName).hex().substr(0, 34) + "$"; +} + h160 const* LinkerObject::matchLibrary( string const& _linkRefName, diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index 152487b4..92890803 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -50,6 +50,11 @@ struct LinkerObject /// addresses by placeholders. std::string toHex() const; + /// @returns a 36 character string that is used as a placeholder for the library + /// address (enclosed by `__` on both sides). The placeholder is the hex representation + /// of the first 18 bytes of the keccak-256 hash of @a _libraryName. + static std::string libraryPlaceholder(std::string const& _libraryName); + private: static h160 const* matchLibrary( std::string const& _linkRefName, diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 91a4678a..136d39b1 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -1,6 +1,6 @@ -# Until we have a clear separation, libjulia has to be included here -file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp") -file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") +# Until we have a clear separation, libyul has to be included here +file(GLOB_RECURSE sources "*.cpp" "../libyul/*.cpp") +file(GLOB_RECURSE headers "*.h" "../libyul/*.h") find_package(Z3 QUIET) if (${Z3_FOUND}) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 5f980788..cf12a49d 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -137,23 +137,23 @@ vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _n vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const { - static size_t const MAXIMUM_EDIT_DISTANCE = 2; + // because the function below has quadratic runtime - it will not magically improve once a better algorithm is discovered ;) // since 80 is the suggested line length limit, we use 80^2 as length threshold static size_t const MAXIMUM_LENGTH_THRESHOLD = 80 * 80; vector<ASTString> similar; - + size_t maximumEditDistance = _name.size() > 3 ? 2 : _name.size() / 2; for (auto const& declaration: m_declarations) { string const& declarationName = declaration.first; - if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD)) + if (stringWithinDistance(_name, declarationName, maximumEditDistance, MAXIMUM_LENGTH_THRESHOLD)) similar.push_back(declarationName); } for (auto const& declaration: m_invisibleDeclarations) { string const& declarationName = declaration.first; - if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD)) + if (stringWithinDistance(_name, declarationName, maximumEditDistance, MAXIMUM_LENGTH_THRESHOLD)) similar.push_back(declarationName); } diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 81de3c43..f62d9c3b 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -269,8 +269,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // external references. ErrorList errors; ErrorReporter errorsIgnored(errors); - julia::ExternalIdentifierAccess::Resolver resolver = - [&](assembly::Identifier const& _identifier, julia::IdentifierContext, bool _crossesFunctionBoundary) { + yul::ExternalIdentifierAccess::Resolver resolver = + [&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name); bool isSlot = boost::algorithm::ends_with(_identifier.name, "_slot"); bool isOffset = boost::algorithm::ends_with(_identifier.name, "_offset"); diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 0bc20f2e..ab544388 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -53,7 +53,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) SemVerVersion recommendedVersion{string(VersionString)}; if (!recommendedVersion.isPrerelease()) errorString += - "Consider adding \"pragma solidity ^" + + " Consider adding \"pragma solidity ^" + to_string(recommendedVersion.major()) + string(".") + to_string(recommendedVersion.minor()) + @@ -76,7 +76,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) { solAssert(m_sourceUnit, ""); vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); - if (literals.size() == 0) + if (literals.empty()) m_errorReporter.syntaxError( _pragma.location(), "Experimental feature name is missing." diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index bc040623..3830935f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -952,9 +952,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { // External references have already been resolved in a prior stage and stored in the annotation. // We run the resolve step again regardless. - julia::ExternalIdentifierAccess::Resolver identifierAccess = [&]( + yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( assembly::Identifier const& _identifier, - julia::IdentifierContext _context, + yul::IdentifierContext _context, bool ) { @@ -978,7 +978,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); return size_t(-1); } - else if (_context != julia::IdentifierContext::RValue) + else if (_context != yul::IdentifierContext::RValue) { m_errorReporter.typeError(_identifier.location, "Storage variables cannot be assigned to."); return size_t(-1); @@ -1008,13 +1008,13 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); return size_t(-1); } - else if (_context == julia::IdentifierContext::LValue) + else if (_context == yul::IdentifierContext::LValue) { m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly."); return size_t(-1); } - if (_context == julia::IdentifierContext::RValue) + if (_context == yul::IdentifierContext::RValue) { solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast<FunctionDefinition const*>(declaration)) @@ -1444,8 +1444,12 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& auto const* tupleType = dynamic_cast<TupleType const*>(&_type); auto const& types = tupleType ? tupleType->components() : vector<TypePointer> { _type.shared_from_this() }; - solAssert(tupleExpression->components().size() == types.size(), ""); - for (size_t i = 0; i < types.size(); i++) + solAssert( + tupleExpression->components().size() == types.size() || m_errorReporter.hasErrors(), + "Array sizes don't match or no errors generated." + ); + + for (size_t i = 0; i < min(tupleExpression->components().size(), types.size()); i++) if (types[i]) { solAssert(!!tupleExpression->components()[i], ""); @@ -2123,7 +2127,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) auto& annotation = _memberAccess.annotation(); - if (possibleMembers.size() == 0) + if (possibleMembers.empty()) { if (initialMemberCount == 0) { @@ -2141,8 +2145,25 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ); } string errorMsg = "Member \"" + memberName + "\" not found or not visible " - "after argument-dependent lookup in " + exprType->toString() + - (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "."); + "after argument-dependent lookup in " + exprType->toString() + "."; + if (memberName == "value") + { + errorMsg.pop_back(); + errorMsg += " - did you forget the \"payable\" modifier?"; + } + else if (exprType->category() == Type::Category::Function) + { + if (auto const& funType = dynamic_pointer_cast<FunctionType const>(exprType)) + { + auto const& t = funType->returnParameterTypes(); + if (t.size() == 1) + if ( + t.front()->category() == Type::Category::Contract || + t.front()->category() == Type::Category::Struct + ) + errorMsg += " Did you intend to call the function?"; + } + } if (exprType->category() == Type::Category::Contract) for (auto const& addressMember: AddressType::addressPayable().nativeMembers(nullptr)) if (addressMember.name == memberName) @@ -2299,7 +2320,8 @@ bool TypeChecker::visit(IndexAccess const& _access) m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); else { - expectType(*index, IntegerType(256)); + if (!expectType(*index, IntegerType(256))) + m_errorReporter.fatalTypeError(_access.location(), "Index expression cannot be represented as an unsigned integer."); if (auto integerType = dynamic_cast<RationalNumberType const*>(type(*index).get())) if (bytesType.numBytes() <= integerType->literalValue(nullptr)) m_errorReporter.typeError(_access.location(), "Out of bounds array access."); @@ -2470,7 +2492,7 @@ Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName return *_typeName.annotation().referencedDeclaration; } -void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) +bool TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) { _expression.accept(*this); if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType)) @@ -2499,7 +2521,9 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte _expectedType.toString() + "." ); + return false; } + return true; } void TypeChecker::requireLValue(Expression const& _expression) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 8d25a88e..6ea99ca2 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -143,7 +143,7 @@ private: /// Runs type checks on @a _expression to infer its type and then checks that it is implicitly /// convertible to @a _expectedType. - void expectType(Expression const& _expression, Type const& _expectedType); + bool expectType(Expression const& _expression, Type const& _expectedType); /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. void requireLValue(Expression const& _expression); diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index cadc5f28..4b282d85 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -325,17 +325,19 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), + make_pair("kind", _node.isConstructor() ? "constructor" : (_node.isFallback() ? "fallback" : "function")), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("superFunction", idOrNull(_node.annotation().superFunction)), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), - make_pair("isConstructor", _node.isConstructor()), make_pair("returnParameters", toJson(*_node.returnParameterList())), make_pair("modifiers", toJson(_node.modifiers())), make_pair("body", _node.isImplemented() ? toJson(_node.body()) : Json::nullValue), make_pair("implemented", _node.isImplemented()), make_pair("scope", idOrNull(_node.scope())) }; + if (m_legacy) + attributes.emplace_back("isConstructor", _node.isConstructor()); setJsonNode(_node, "FunctionDefinition", std::move(attributes)); return false; } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e45fc81d..195b2e2d 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -841,13 +841,8 @@ TypePointer RationalNumberType::forLiteral(Literal const& _literal) TypePointer compatibleBytesType; if (_literal.isHexNumber()) { - size_t digitCount = count_if( - _literal.value().begin() + 2, // skip "0x" - _literal.value().end(), - [](unsigned char _c) -> bool { return isxdigit(_c); } - ); - // require even number of digits - if (!(digitCount & 1)) + size_t const digitCount = _literal.valueWithoutUnderscores().length() - 2; + if (digitCount % 2 == 0 && (digitCount / 2) <= 32) compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2); } @@ -861,8 +856,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal rational value; try { - ASTString valueString = _literal.value(); - boost::erase_all(valueString, "_");// Remove underscore separators + ASTString valueString = _literal.valueWithoutUnderscores(); auto expPoint = find(valueString.begin(), valueString.end(), 'e'); if (expPoint == valueString.end()) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 71b615b8..089386b5 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -318,10 +318,10 @@ void CompilerContext::appendInlineAssembly( { int startStackHeight = stackHeight(); - julia::ExternalIdentifierAccess identifierAccess; + yul::ExternalIdentifierAccess identifierAccess; identifierAccess.resolve = [&]( assembly::Identifier const& _identifier, - julia::IdentifierContext, + yul::IdentifierContext, bool ) { @@ -330,15 +330,15 @@ void CompilerContext::appendInlineAssembly( }; identifierAccess.generateCode = [&]( assembly::Identifier const& _identifier, - julia::IdentifierContext _context, - julia::AbstractAssembly& _assembly + yul::IdentifierContext _context, + yul::AbstractAssembly& _assembly ) { auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); solAssert(it != _localVariables.end(), ""); int stackDepth = _localVariables.end() - it; int stackDiff = _assembly.stackHeight() - startStackHeight + stackDepth; - if (_context == julia::IdentifierContext::LValue) + if (_context == yul::IdentifierContext::LValue) stackDiff -= 1; if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( @@ -346,7 +346,7 @@ void CompilerContext::appendInlineAssembly( errinfo_sourceLocation(_identifier.location) << errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); - if (_context == julia::IdentifierContext::RValue) + if (_context == yul::IdentifierContext::RValue) _assembly.appendInstruction(dupInstruction(stackDiff)); else { diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index e26bc13a..c845da8f 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -494,21 +494,21 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { unsigned startStackHeight = m_context.stackHeight(); - julia::ExternalIdentifierAccess identifierAccess; - identifierAccess.resolve = [&](assembly::Identifier const& _identifier, julia::IdentifierContext, bool) + yul::ExternalIdentifierAccess identifierAccess; + identifierAccess.resolve = [&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); if (ref == _inlineAssembly.annotation().externalReferences.end()) return size_t(-1); return ref->second.valueSize; }; - identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, julia::IdentifierContext _context, julia::AbstractAssembly& _assembly) + identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, yul::IdentifierContext _context, yul::AbstractAssembly& _assembly) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), ""); Declaration const* decl = ref->second.declaration; solAssert(!!decl, ""); - if (_context == julia::IdentifierContext::RValue) + if (_context == yul::IdentifierContext::RValue) { int const depositBefore = _assembly.stackHeight(); solAssert(!!decl->type(), "Type of declaration required but not yet determined."); @@ -965,7 +965,7 @@ void ContractCompiler::popScopedVariables(ASTNode const* _node) unsigned stackDiff = m_context.stackHeight() - blockHeight; CompilerUtils(m_context).popStackSlots(stackDiff); m_scopeStackHeight[m_modifierDepth].erase(_node); - if (m_scopeStackHeight[m_modifierDepth].size() == 0) + if (m_scopeStackHeight[m_modifierDepth].empty()) m_scopeStackHeight.erase(m_modifierDepth); } diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 19785817..a7973852 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -894,7 +894,7 @@ void SMTChecker::pushPathCondition(smt::Expression const& _e) smt::Expression SMTChecker::currentPathConditions() { - if (m_pathConditions.size() == 0) + if (m_pathConditions.empty()) return smt::Expression(true); return m_pathConditions.back(); } diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 947b6d05..04b5d1a8 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -145,7 +145,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) if (m_resolver) { bool insideFunction = m_currentScope->insideFunction(); - stackSize = m_resolver(_identifier, julia::IdentifierContext::RValue, insideFunction); + stackSize = m_resolver(_identifier, yul::IdentifierContext::RValue, insideFunction); } if (stackSize == size_t(-1)) { @@ -512,7 +512,7 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t else if (m_resolver) { bool insideFunction = m_currentScope->insideFunction(); - variableSize = m_resolver(_variable, julia::IdentifierContext::LValue, insideFunction); + variableSize = m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction); } if (variableSize == size_t(-1)) { diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 8d2a71f0..a8673efa 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -25,7 +25,7 @@ #include <libsolidity/inlineasm/AsmScope.h> -#include <libjulia/backends/evm/AbstractAssembly.h> +#include <libyul/backends/evm/AbstractAssembly.h> #include <libsolidity/inlineasm/AsmDataForward.h> @@ -59,7 +59,7 @@ public: EVMVersion _evmVersion, boost::optional<Error::Type> _errorTypeForLoose, AsmFlavour _flavour = AsmFlavour::Loose, - julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() + yul::ExternalIdentifierAccess::Resolver const& _resolver = yul::ExternalIdentifierAccess::Resolver() ): m_resolver(_resolver), m_info(_analysisInfo), @@ -106,7 +106,7 @@ private: void checkLooseFeature(SourceLocation const& _location, std::string const& _description); int m_stackHeight = 0; - julia::ExternalIdentifierAccess::Resolver m_resolver; + yul::ExternalIdentifierAccess::Resolver m_resolver; Scope* m_currentScope = nullptr; /// Variables that are active at the current point in assembly (as opposed to /// "part of the scope but not yet declared") diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index dded9f76..3a62b232 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -32,8 +32,8 @@ #include <libevmasm/SourceLocation.h> #include <libevmasm/Instruction.h> -#include <libjulia/backends/evm/AbstractAssembly.h> -#include <libjulia/backends/evm/EVMCodeTransform.h> +#include <libyul/backends/evm/AbstractAssembly.h> +#include <libyul/backends/evm/EVMCodeTransform.h> #include <libdevcore/CommonIO.h> @@ -49,7 +49,7 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; -class EthAssemblyAdapter: public julia::AbstractAssembly +class EthAssemblyAdapter: public yul::AbstractAssembly { public: explicit EthAssemblyAdapter(eth::Assembly& _assembly): @@ -145,12 +145,12 @@ void assembly::CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - julia::ExternalIdentifierAccess const& _identifierAccess, + yul::ExternalIdentifierAccess const& _identifierAccess, bool _useNamedLabelsForFunctions ) { EthAssemblyAdapter assemblyAdapter(_assembly); - julia::CodeTransform( + yul::CodeTransform( assemblyAdapter, _analysisInfo, false, diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index 277e1879..bbc31397 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -46,7 +46,7 @@ public: Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess(), + yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(), bool _useNamedLabelsForFunctions = false ); }; diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index f34bbffe..54cdc1c6 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -26,7 +26,7 @@ #include <boost/algorithm/string.hpp> -#include <ctype.h> +#include <cctype> #include <algorithm> using namespace std; @@ -97,7 +97,7 @@ assembly::Statement Parser::parseStatement() fatalParserError("Only one default case allowed."); else if (m_scanner->currentToken() == Token::Case) fatalParserError("Case not allowed after default case."); - if (_switch.cases.size() == 0) + if (_switch.cases.empty()) fatalParserError("Switch statement without any cases."); _switch.location.end = _switch.cases.back().body.location.end; return _switch; diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h index c8c63f8f..fc674e71 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libsolidity/inlineasm/AsmScope.h @@ -22,6 +22,8 @@ #include <libsolidity/interface/Exceptions.h> +#include <libdevcore/Visitor.h> + #include <boost/variant.hpp> #include <boost/optional.hpp> @@ -35,31 +37,6 @@ namespace solidity namespace assembly { -template <class...> -struct GenericVisitor{}; - -template <class Visitable, class... Others> -struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...> -{ - using GenericVisitor<Others...>::operator (); - explicit GenericVisitor( - std::function<void(Visitable&)> _visitor, - std::function<void(Others&)>... _otherVisitors - ): - GenericVisitor<Others...>(_otherVisitors...), - m_visitor(_visitor) - {} - - void operator()(Visitable& _v) const { m_visitor(_v); } - - std::function<void(Visitable&)> m_visitor; -}; -template <> -struct GenericVisitor<>: public boost::static_visitor<> { - void operator()() const {} -}; - - struct Scope { using YulType = std::string; diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 46fa1d6b..26496de7 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -31,8 +31,8 @@ #include <libevmasm/Assembly.h> -#include <libjulia/backends/evm/EVMCodeTransform.h> -#include <libjulia/backends/evm/EVMAssembly.h> +#include <libyul/backends/evm/EVMCodeTransform.h> +#include <libyul/backends/evm/EVMAssembly.h> using namespace std; using namespace dev; @@ -116,8 +116,8 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const case Machine::EVM15: { MachineAssemblyObject object; - julia::EVMAssembly assembly(true); - julia::CodeTransform(assembly, *m_analysisInfo, m_language == Language::Yul, true)(*m_parserResult); + yul::EVMAssembly assembly(true); + yul::CodeTransform(assembly, *m_analysisInfo, m_language == Language::Yul, true)(*m_parserResult); object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize()); /// TODO: fill out text representation return object; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d1001c80..1f58245f 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -780,7 +780,7 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa // To provide a measure of backward-compatibility, if a contract is not located by its // fully-qualified name, a lookup will be attempted purely on the contract's name to see // if anything will satisfy. - if (_contractName.find(":") == string::npos) + if (_contractName.find(':') == string::npos) { for (auto const& contractEntry: m_contracts) { diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 2305da13..8300e8db 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -509,7 +509,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) bool const compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful; /// Inconsistent state - stop here to receive error reports from users - if (!compilationSuccess && (errors.size() == 0)) + if (!compilationSuccess && errors.empty()) return formatFatalError("InternalCompilerError", "No error reported, but compilation failed."); Json::Value output = Json::objectValue; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 1228b833..ca9a9b57 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -20,7 +20,7 @@ * Solidity parser. */ -#include <ctype.h> +#include <cctype> #include <vector> #include <libevmasm/SourceLocation.h> #include <libsolidity/parsing/Parser.h> @@ -554,7 +554,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition() if (m_scanner->currentToken() != Token::Identifier) fatalParserError(string("Expected identifier after ','")); } - if (members.size() == 0) + if (members.empty()) parserError({"enum with no members is not allowed."}); nodeFactory.markEndPosition(); @@ -1554,6 +1554,9 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() expression = nodeFactory.createNode<TupleExpression>(components, isArray); break; } + case Token::IllegalHex: + fatalParserError("Expected even number of hex-nibbles within double-quotes."); + break; default: if (Token::isElementaryTypeName(token)) { diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index 9a7f85cb..87d7c535 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -612,7 +612,7 @@ void Scanner::scanToken() if (m_char == '"' || m_char == '\'') token = scanHexString(); else - token = Token::Illegal; + token = Token::IllegalHex; } } else if (isDecimalDigit(m_char)) @@ -736,11 +736,11 @@ Token::Value Scanner::scanHexString() { char c = m_char; if (!scanHexByte(c)) - return Token::Illegal; + return Token::IllegalHex; addLiteralChar(c); } if (m_char != quote) - return Token::Illegal; + return Token::IllegalHex; literal.complete(); advance(); // consume quote return Token::StringLiteral; diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 73c85482..8ecc850a 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -261,6 +261,8 @@ namespace solidity \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ + /* Illegal hex token */ \ + T(IllegalHex, "ILLEGAL_HEX", 0) \ \ /* Scanner-internal use only. */ \ T(Whitespace, NULL, 0) diff --git a/libjulia/ASTDataForward.h b/libyul/ASTDataForward.h index 143b9c46..7f131b5e 100644 --- a/libjulia/ASTDataForward.h +++ b/libyul/ASTDataForward.h @@ -25,7 +25,7 @@ namespace dev { -namespace julia +namespace yul { using Instruction = solidity::assembly::Instruction; diff --git a/libjulia/Exceptions.h b/libyul/Exceptions.h index 48624a56..c423b66f 100644 --- a/libjulia/Exceptions.h +++ b/libyul/Exceptions.h @@ -25,7 +25,7 @@ namespace dev { -namespace julia +namespace yul { struct YulException: virtual Exception {}; diff --git a/libjulia/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index b6818923..d75058f7 100644 --- a/libjulia/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -38,7 +38,7 @@ struct Instruction; struct Identifier; } } -namespace julia +namespace yul { /// @@ -106,7 +106,7 @@ struct ExternalIdentifierAccess /// Resolve an external reference given by the identifier in the given context. /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. Resolver resolve; - using CodeGenerator = std::function<void(solidity::assembly::Identifier const&, IdentifierContext, julia::AbstractAssembly&)>; + using CodeGenerator = std::function<void(solidity::assembly::Identifier const&, IdentifierContext, yul::AbstractAssembly&)>; /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. diff --git a/libjulia/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index 07ad05c9..b2f0878f 100644 --- a/libjulia/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -18,7 +18,7 @@ * Assembly interface for EVM and EVM1.5. */ -#include <libjulia/backends/evm/EVMAssembly.h> +#include <libyul/backends/evm/EVMAssembly.h> #include <libevmasm/Instruction.h> @@ -26,7 +26,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; namespace { diff --git a/libjulia/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h index 56ae7655..556ed5a5 100644 --- a/libjulia/backends/evm/EVMAssembly.h +++ b/libyul/backends/evm/EVMAssembly.h @@ -20,7 +20,7 @@ #pragma once -#include <libjulia/backends/evm/AbstractAssembly.h> +#include <libyul/backends/evm/AbstractAssembly.h> #include <libevmasm/LinkerObject.h> @@ -28,7 +28,7 @@ namespace dev { -namespace julia +namespace yul { class EVMAssembly: public AbstractAssembly diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index dc536f77..89086b4e 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -18,7 +18,7 @@ * Common code generator for translating Yul / inline assembly to EVM and EVM1.5. */ -#include <libjulia/backends/evm/EVMCodeTransform.h> +#include <libyul/backends/evm/EVMCodeTransform.h> #include <libsolidity/inlineasm/AsmAnalysisInfo.h> #include <libsolidity/inlineasm/AsmData.h> @@ -29,7 +29,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; using Scope = dev::solidity::assembly::Scope; diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index ed0785d3..22ebbf43 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -18,9 +18,9 @@ * Common code generator for translating Yul / inline assembly to EVM and EVM1.5. */ -#include <libjulia/backends/evm/EVMAssembly.h> +#include <libyul/backends/evm/EVMAssembly.h> -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> #include <libsolidity/inlineasm/AsmScope.h> @@ -37,7 +37,7 @@ namespace assembly struct AsmAnalysisInfo; } } -namespace julia +namespace yul { class EVMAssembly; @@ -47,7 +47,7 @@ public: /// Create the code transformer. /// @param _identifierAccess used to resolve identifiers external to the inline assembly CodeTransform( - julia::AbstractAssembly& _assembly, + yul::AbstractAssembly& _assembly, solidity::assembly::AsmAnalysisInfo& _analysisInfo, bool _yul = false, bool _evm15 = false, @@ -76,7 +76,7 @@ protected: }; CodeTransform( - julia::AbstractAssembly& _assembly, + yul::AbstractAssembly& _assembly, solidity::assembly::AsmAnalysisInfo& _analysisInfo, bool _yul, bool _evm15, @@ -139,7 +139,7 @@ private: void checkStackHeight(void const* _astElement) const; - julia::AbstractAssembly& m_assembly; + yul::AbstractAssembly& m_assembly; solidity::assembly::AsmAnalysisInfo& m_info; solidity::assembly::Scope* m_scope = nullptr; bool m_yul = false; diff --git a/libjulia/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp index a8a1e30f..4b7f21f8 100644 --- a/libjulia/optimiser/ASTCopier.cpp +++ b/libyul/optimiser/ASTCopier.cpp @@ -18,9 +18,9 @@ * Creates an independent copy of an AST, renaming identifiers to be unique. */ -#include <libjulia/optimiser/ASTCopier.h> +#include <libyul/optimiser/ASTCopier.h> -#include <libjulia/Exceptions.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> @@ -28,7 +28,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; Statement ASTCopier::operator()(Instruction const&) { diff --git a/libjulia/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index cb2925e3..13369cef 100644 --- a/libjulia/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -20,7 +20,7 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> #include <boost/variant.hpp> #include <boost/optional.hpp> @@ -31,7 +31,7 @@ namespace dev { -namespace julia +namespace yul { class ExpressionCopier: public boost::static_visitor<Expression> diff --git a/libjulia/optimiser/ASTWalker.cpp b/libyul/optimiser/ASTWalker.cpp index dc94cc60..e29dda6b 100644 --- a/libjulia/optimiser/ASTWalker.cpp +++ b/libyul/optimiser/ASTWalker.cpp @@ -18,7 +18,7 @@ * Generic AST walker. */ -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> #include <libsolidity/inlineasm/AsmData.h> @@ -26,7 +26,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; diff --git a/libjulia/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h index e1f0f5bd..41617d55 100644 --- a/libjulia/optimiser/ASTWalker.h +++ b/libyul/optimiser/ASTWalker.h @@ -20,9 +20,9 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> -#include <libjulia/Exceptions.h> +#include <libyul/Exceptions.h> #include <boost/variant.hpp> #include <boost/optional.hpp> @@ -33,7 +33,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 3122280b..23d15cad 100644 --- a/libjulia/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -19,23 +19,45 @@ * in scope by a reference to that variable. */ -#include <libjulia/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/CommonSubexpressionEliminator.h> -#include <libjulia/optimiser/Metrics.h> -#include <libjulia/optimiser/SyntacticalEquality.h> -#include <libjulia/Exceptions.h> +#include <libyul/optimiser/Metrics.h> +#include <libyul/optimiser/SyntacticalEquality.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; void CommonSubexpressionEliminator::visit(Expression& _e) { - // Single exception for substitution: We do not substitute one variable for another. - if (_e.type() != typeid(Identifier)) - // TODO this search rather inefficient. + // We visit the inner expression first to first simplify inner expressions, + // which hopefully allows more matches. + // Note that the DataFlowAnalyzer itself only has code for visiting Statements, + // so this basically invokes the AST walker directly and thus post-visiting + // is also fine with regards to data flow analysis. + DataFlowAnalyzer::visit(_e); + + if (_e.type() == typeid(Identifier)) + { + Identifier& identifier = boost::get<Identifier>(_e); + string const& name = identifier.name; + if (m_value.count(name)) + { + assertThrow(m_value.at(name), OptimizerException, ""); + if (m_value.at(name)->type() == typeid(Identifier)) + { + string const& value = boost::get<Identifier>(*m_value.at(name)).name; + if (inScope(value)) + _e = Identifier{locationOf(_e), value}; + } + } + } + else + { + // TODO this search is rather inefficient. for (auto const& var: m_value) { assertThrow(var.second, OptimizerException, ""); @@ -45,5 +67,5 @@ void CommonSubexpressionEliminator::visit(Expression& _e) break; } } - DataFlowAnalyzer::visit(_e); + } } diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.h b/libyul/optimiser/CommonSubexpressionEliminator.h index a8ca3abb..f8aa0ee1 100644 --- a/libjulia/optimiser/CommonSubexpressionEliminator.h +++ b/libyul/optimiser/CommonSubexpressionEliminator.h @@ -21,11 +21,11 @@ #pragma once -#include <libjulia/optimiser/DataFlowAnalyzer.h> +#include <libyul/optimiser/DataFlowAnalyzer.h> namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 0ad0eac9..ca1e5153 100644 --- a/libjulia/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -20,11 +20,11 @@ * Common Subexpression Eliminator. */ -#include <libjulia/optimiser/DataFlowAnalyzer.h> +#include <libyul/optimiser/DataFlowAnalyzer.h> -#include <libjulia/optimiser/NameCollector.h> -#include <libjulia/optimiser/Semantics.h> -#include <libjulia/Exceptions.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/Semantics.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> @@ -34,7 +34,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; void DataFlowAnalyzer::operator()(Assignment& _assignment) { diff --git a/libjulia/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index 66df2f48..f998eadf 100644 --- a/libjulia/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -22,7 +22,7 @@ #pragma once -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> #include <string> #include <map> @@ -30,7 +30,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/Disambiguator.cpp b/libyul/optimiser/Disambiguator.cpp index 687be9b9..af3507e1 100644 --- a/libjulia/optimiser/Disambiguator.cpp +++ b/libyul/optimiser/Disambiguator.cpp @@ -18,16 +18,16 @@ * Optimiser component that makes all identifiers unique. */ -#include <libjulia/optimiser/Disambiguator.h> +#include <libyul/optimiser/Disambiguator.h> -#include <libjulia/Exceptions.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> #include <libsolidity/inlineasm/AsmScope.h> using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; using Scope = dev::solidity::assembly::Scope; diff --git a/libjulia/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h index 4ef43736..0829fd58 100644 --- a/libjulia/optimiser/Disambiguator.h +++ b/libyul/optimiser/Disambiguator.h @@ -20,10 +20,10 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/optimiser/NameDispenser.h> +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/optimiser/NameDispenser.h> #include <libsolidity/inlineasm/AsmAnalysisInfo.h> @@ -34,7 +34,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/ExpressionInliner.cpp b/libyul/optimiser/ExpressionInliner.cpp index 450769fd..9bf0a3fb 100644 --- a/libjulia/optimiser/ExpressionInliner.cpp +++ b/libyul/optimiser/ExpressionInliner.cpp @@ -18,11 +18,11 @@ * Optimiser component that performs function inlining inside expressions. */ -#include <libjulia/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/ExpressionInliner.h> -#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h> -#include <libjulia/optimiser/Substitution.h> -#include <libjulia/optimiser/Semantics.h> +#include <libyul/optimiser/InlinableExpressionFunctionFinder.h> +#include <libyul/optimiser/Substitution.h> +#include <libyul/optimiser/Semantics.h> #include <libsolidity/inlineasm/AsmData.h> @@ -30,7 +30,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; void ExpressionInliner::run() diff --git a/libjulia/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h index 3d24ef5d..971a2ee0 100644 --- a/libjulia/optimiser/ExpressionInliner.h +++ b/libyul/optimiser/ExpressionInliner.h @@ -19,9 +19,9 @@ */ #pragma once -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> #include <boost/variant.hpp> #include <boost/optional.hpp> @@ -30,7 +30,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp new file mode 100644 index 00000000..c3957497 --- /dev/null +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -0,0 +1,166 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that undoes what the ExpressionSplitter did, i.e. + * it more or less inlines variable declarations. + */ + +#include <libyul/optimiser/ExpressionJoiner.h> + +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/Utilities.h> +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void ExpressionJoiner::operator()(FunctionalInstruction& _instruction) +{ + handleArguments(_instruction.arguments); +} + +void ExpressionJoiner::operator()(FunctionCall& _funCall) +{ + handleArguments(_funCall.arguments); +} + +void ExpressionJoiner::operator()(If& _if) +{ + visit(*_if.condition); + (*this)(_if.body); +} + +void ExpressionJoiner::operator()(Switch& _switch) +{ + visit(*_switch.expression); + for (auto& _case: _switch.cases) + // Do not visit the case expression, nothing to join there. + (*this)(_case.body); +} + +void ExpressionJoiner::operator()(Block& _block) +{ + resetLatestStatementPointer(); + for (size_t i = 0; i < _block.statements.size(); ++i) + { + visit(_block.statements[i]); + m_currentBlock = &_block; + m_latestStatementInBlock = i; + } + + removeEmptyBlocks(_block); + resetLatestStatementPointer(); +} + +void ExpressionJoiner::visit(Expression& _e) +{ + if (_e.type() == typeid(Identifier)) + { + Identifier const& identifier = boost::get<Identifier>(_e); + if (isLatestStatementVarDeclOf(identifier) && m_references[identifier.name] == 1) + { + VariableDeclaration& varDecl = boost::get<VariableDeclaration>(*latestStatement()); + assertThrow(varDecl.variables.size() == 1, OptimizerException, ""); + assertThrow(varDecl.value, OptimizerException, ""); + + _e = std::move(*varDecl.value); + // Delete the variable declaration (also get the moved-from structure back into a sane state) + *latestStatement() = Block(); + + decrementLatestStatementPointer(); + } + } + else + ASTModifier::visit(_e); +} + +void ExpressionJoiner::run(Block& _ast) +{ + ExpressionJoiner{_ast}(_ast); +} + +ExpressionJoiner::ExpressionJoiner(Block& _ast) +{ + ReferencesCounter counter; + counter(_ast); + m_references = counter.references(); +} + +void ExpressionJoiner::handleArguments(vector<Expression>& _arguments) +{ + // We have to fill from left to right, but we can only + // fill if everything to the right is just an identifier + // or a literal. + // Also we only descend into function calls if everything + // on the right is an identifier or literal. + + size_t i = _arguments.size(); + for (Expression const& arg: _arguments | boost::adaptors::reversed) + { + --i; + if (arg.type() != typeid(Identifier) && arg.type() != typeid(Literal)) + break; + } + // i points to the last element that is neither an identifier nor a literal, + // or to the first element if all of them are identifiers or literals. + + for (; i < _arguments.size(); ++i) + visit(_arguments.at(i)); +} + +void ExpressionJoiner::decrementLatestStatementPointer() +{ + if (!m_currentBlock) + return; + if (m_latestStatementInBlock > 0) + --m_latestStatementInBlock; + else + resetLatestStatementPointer(); +} + +void ExpressionJoiner::resetLatestStatementPointer() +{ + m_currentBlock = nullptr; + m_latestStatementInBlock = size_t(-1); +} + +Statement* ExpressionJoiner::latestStatement() +{ + if (!m_currentBlock) + return nullptr; + else + return &m_currentBlock->statements.at(m_latestStatementInBlock); +} + +bool ExpressionJoiner::isLatestStatementVarDeclOf(Identifier const& _identifier) +{ + Statement const* statement = latestStatement(); + if (!statement || statement->type() != typeid(VariableDeclaration)) + return false; + VariableDeclaration const& varDecl = boost::get<VariableDeclaration>(*statement); + if (varDecl.variables.size() != 1 || !varDecl.value) + return false; + return varDecl.variables.at(0).name == _identifier.name; +} diff --git a/libyul/optimiser/ExpressionJoiner.h b/libyul/optimiser/ExpressionJoiner.h new file mode 100644 index 00000000..df18e58f --- /dev/null +++ b/libyul/optimiser/ExpressionJoiner.h @@ -0,0 +1,102 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that undoes what the ExpressionSplitter did, i.e. + * it more or less inlines variable declarations. + */ +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> + +#include <map> + +namespace dev +{ +namespace yul +{ + +class NameCollector; + + +/** + * Optimiser component that modifies an AST in place, turning sequences + * of variable declarations into complex expressions, if the variables + * are declared in the right order. This component does the opposite + * of ExpressionSplitter. + * Since the order of opcode or function evaluation is unchanged, + * this transformation does not need to care about conflicting opcodes. + * + * Code of the form + * + * let a1 := mload(y) + * let a2 := mul(x, 4) + * sstore(a2, a1) + * + * is transformed into + * + * sstore(mul(x, 4), mload(y)) + * + * The transformation is not applied to loop conditions, because those are + * evaluated with each loop. + * + * The component can be applied to sub-blocks of the AST, you do not + * need to pass a full AST. + * + * Prerequisites: Disambiguator + * + * Implementation note: We visit the AST, modifying it in place. + * The class starts counting references and will only replace variables + * that have exactly one reference. It keeps a "latest statement pointer" + * which always points to the statement right before the current statement. + * Any function call or opcode will reset this pointer. If an identifier + * is encountered that was declared in the "latest statement", it is replaced + * by the value of the declaration, the "latest statement" is replaced + * by an empty block and the pointer is decremented. + * A block also resets the latest statement pointer. + */ +class ExpressionJoiner: public ASTModifier +{ +public: + virtual void operator()(FunctionalInstruction&) override; + virtual void operator()(FunctionCall&) override; + virtual void operator()(If&) override; + virtual void operator()(Switch&) override; + virtual void operator()(Block& _block) override; + + using ASTModifier::visit; + virtual void visit(Expression& _e) override; + + static void run(Block& _ast); +private: + explicit ExpressionJoiner(Block& _ast); + + void handleArguments(std::vector<Expression>& _arguments); + + void decrementLatestStatementPointer(); + void resetLatestStatementPointer(); + Statement* latestStatement(); + bool isLatestStatementVarDeclOf(Identifier const& _identifier); + + Block* m_currentBlock = nullptr; + size_t m_latestStatementInBlock = 0; + std::map<std::string, size_t> m_references; +}; + +} +} diff --git a/libjulia/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index 8bd6b1c7..c95fb3d5 100644 --- a/libjulia/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -18,10 +18,10 @@ * Optimiser component that uses the simplification rules to simplify expressions. */ -#include <libjulia/optimiser/ExpressionSimplifier.h> +#include <libyul/optimiser/ExpressionSimplifier.h> -#include <libjulia/optimiser/SimplificationRules.h> -#include <libjulia/optimiser/Semantics.h> +#include <libyul/optimiser/SimplificationRules.h> +#include <libyul/optimiser/Semantics.h> #include <libsolidity/inlineasm/AsmData.h> @@ -29,7 +29,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; diff --git a/libjulia/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index 8a9e0e20..1b9d6960 100644 --- a/libjulia/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -20,13 +20,13 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> namespace dev { -namespace julia +namespace yul { /** diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp new file mode 100644 index 00000000..a2ecc546 --- /dev/null +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -0,0 +1,105 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that turns complex expressions into multiple variable + * declarations. + */ + +#include <libyul/optimiser/ExpressionSplitter.h> + +#include <libyul/optimiser/ASTWalker.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void ExpressionSplitter::operator()(FunctionalInstruction& _instruction) +{ + for (auto& arg: _instruction.arguments | boost::adaptors::reversed) + outlineExpression(arg); +} + +void ExpressionSplitter::operator()(FunctionCall& _funCall) +{ + for (auto& arg: _funCall.arguments | boost::adaptors::reversed) + outlineExpression(arg); +} + +void ExpressionSplitter::operator()(If& _if) +{ + outlineExpression(*_if.condition); + (*this)(_if.body); +} + +void ExpressionSplitter::operator()(Switch& _switch) +{ + outlineExpression(*_switch.expression); + for (auto& _case: _switch.cases) + // Do not visit the case expression, nothing to split there. + (*this)(_case.body); +} + +void ExpressionSplitter::operator()(ForLoop& _loop) +{ + (*this)(_loop.pre); + // Do not visit the condition because we cannot split expressions there. + (*this)(_loop.post); + (*this)(_loop.body); +} + +void ExpressionSplitter::operator()(Block& _block) +{ + vector<Statement> saved; + swap(saved, m_statementsToPrefix); + + function<boost::optional<vector<Statement>>(Statement&)> f = + [&](Statement& _statement) -> boost::optional<vector<Statement>> { + m_statementsToPrefix.clear(); + visit(_statement); + if (m_statementsToPrefix.empty()) + return {}; + m_statementsToPrefix.emplace_back(std::move(_statement)); + return std::move(m_statementsToPrefix); + }; + iterateReplacing(_block.statements, f); + + swap(saved, m_statementsToPrefix); +} + +void ExpressionSplitter::outlineExpression(Expression& _expr) +{ + if (_expr.type() == typeid(Identifier)) + return; + + visit(_expr); + + SourceLocation location = locationOf(_expr); + string var = m_nameDispenser.newName(""); + m_statementsToPrefix.emplace_back(VariableDeclaration{ + location, + {{TypedName{location, var, ""}}}, + make_shared<Expression>(std::move(_expr)) + }); + _expr = Identifier{location, var}; +} diff --git a/libyul/optimiser/ExpressionSplitter.h b/libyul/optimiser/ExpressionSplitter.h new file mode 100644 index 00000000..339acbf0 --- /dev/null +++ b/libyul/optimiser/ExpressionSplitter.h @@ -0,0 +1,86 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that turns complex expressions into multiple variable + * declarations. + */ +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/optimiser/NameDispenser.h> + +#include <vector> + +namespace dev +{ +namespace yul +{ + +class NameCollector; + + +/** + * Optimiser component that modifies an AST in place, turning complex + * expressions into simple expressions and multiple variable declarations. + * + * Code of the form + * + * sstore(mul(x, 4), mload(y)) + * + * is transformed into + * + * let a1 := mload(y) + * let a2 := mul(x, 4) + * sstore(a2, a1) + * + * The transformation is not applied to loop conditions, because the loop control flow + * does not allow "outlining" the inner expressions in all cases. + * + * The final program should be in a form such that with the exception of a loop condition, + * function calls can only appear in the right-hand side of a variable declaration, + * assignments or expression statements and all arguments have to be constants or variables. + */ +class ExpressionSplitter: public ASTModifier +{ +public: + explicit ExpressionSplitter(NameDispenser& _nameDispenser): + m_nameDispenser(_nameDispenser) + { } + + virtual void operator()(FunctionalInstruction&) override; + virtual void operator()(FunctionCall&) override; + virtual void operator()(If&) override; + virtual void operator()(Switch&) override; + virtual void operator()(ForLoop&) override; + virtual void operator()(Block& _block) override; + +private: + /// Replaces the expression by a variable if it is a function call or functional + /// instruction. The declaration of the variable is appended to m_statementsToPrefix. + /// Recurses via visit(). + void outlineExpression(Expression& _expr); + + /// List of statements that should go in front of the currently visited AST element, + /// at the statement level. + std::vector<Statement> m_statementsToPrefix; + NameDispenser& m_nameDispenser; +}; + +} +} diff --git a/libjulia/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index f41dc198..4e419987 100644 --- a/libjulia/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -18,13 +18,13 @@ * Optimiser component that performs function inlining for arbitrary functions. */ -#include <libjulia/optimiser/FullInliner.h> +#include <libyul/optimiser/FullInliner.h> -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/optimiser/ASTWalker.h> -#include <libjulia/optimiser/NameCollector.h> -#include <libjulia/optimiser/Semantics.h> -#include <libjulia/Exceptions.h> +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/Semantics.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> @@ -34,7 +34,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; FullInliner::FullInliner(Block& _ast): diff --git a/libjulia/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index ff9e6854..8112fb4b 100644 --- a/libjulia/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -19,12 +19,12 @@ */ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/optimiser/ASTWalker.h> -#include <libjulia/optimiser/NameDispenser.h> -#include <libjulia/Exceptions.h> +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/optimiser/NameDispenser.h> +#include <libyul/Exceptions.h> #include <libevmasm/SourceLocation.h> @@ -35,7 +35,7 @@ namespace dev { -namespace julia +namespace yul { class NameCollector; diff --git a/libjulia/optimiser/FunctionGrouper.cpp b/libyul/optimiser/FunctionGrouper.cpp index f1e99e6b..3d2e5322 100644 --- a/libjulia/optimiser/FunctionGrouper.cpp +++ b/libyul/optimiser/FunctionGrouper.cpp @@ -19,7 +19,7 @@ * statements are moved to a block of their own followed by all function definitions. */ -#include <libjulia/optimiser/FunctionGrouper.h> +#include <libyul/optimiser/FunctionGrouper.h> #include <libsolidity/inlineasm/AsmData.h> @@ -27,7 +27,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; diff --git a/libjulia/optimiser/FunctionGrouper.h b/libyul/optimiser/FunctionGrouper.h index 64a71318..63cfbfb1 100644 --- a/libjulia/optimiser/FunctionGrouper.h +++ b/libyul/optimiser/FunctionGrouper.h @@ -21,11 +21,11 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/FunctionHoister.cpp b/libyul/optimiser/FunctionHoister.cpp index 98fc714c..c196dead 100644 --- a/libjulia/optimiser/FunctionHoister.cpp +++ b/libyul/optimiser/FunctionHoister.cpp @@ -20,8 +20,8 @@ * anywhere else. */ -#include <libjulia/optimiser/FunctionHoister.h> -#include <libjulia/optimiser/Utilities.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/Utilities.h> #include <libsolidity/inlineasm/AsmData.h> @@ -29,7 +29,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; void FunctionHoister::operator()(Block& _block) diff --git a/libjulia/optimiser/FunctionHoister.h b/libyul/optimiser/FunctionHoister.h index f9f8bce0..823b9e2b 100644 --- a/libjulia/optimiser/FunctionHoister.h +++ b/libyul/optimiser/FunctionHoister.h @@ -21,13 +21,13 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp index e237063d..69dd2095 100644 --- a/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp @@ -18,15 +18,15 @@ * Optimiser component that identifies functions to be inlined. */ -#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h> +#include <libyul/optimiser/InlinableExpressionFunctionFinder.h> -#include <libjulia/optimiser/Utilities.h> +#include <libyul/optimiser/Utilities.h> #include <libsolidity/inlineasm/AsmData.h> using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; void InlinableExpressionFunctionFinder::operator()(Identifier const& _identifier) { diff --git a/libjulia/optimiser/InlinableExpressionFunctionFinder.h b/libyul/optimiser/InlinableExpressionFunctionFinder.h index d11160d7..3887e6e5 100644 --- a/libjulia/optimiser/InlinableExpressionFunctionFinder.h +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.h @@ -20,14 +20,14 @@ #pragma once -#include <libjulia/ASTDataForward.h> -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/ASTDataForward.h> +#include <libyul/optimiser/ASTWalker.h> #include <set> namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp index bcd2f178..c8f35207 100644 --- a/libjulia/optimiser/MainFunction.cpp +++ b/libyul/optimiser/MainFunction.cpp @@ -19,10 +19,10 @@ * inputs nor outputs. */ -#include <libjulia/optimiser/MainFunction.h> +#include <libyul/optimiser/MainFunction.h> -#include <libjulia/optimiser/NameCollector.h> -#include <libjulia/Exceptions.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> @@ -30,7 +30,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; void MainFunction::operator()(Block& _block) diff --git a/libjulia/optimiser/MainFunction.h b/libyul/optimiser/MainFunction.h index 7201d89a..4a73283a 100644 --- a/libjulia/optimiser/MainFunction.h +++ b/libyul/optimiser/MainFunction.h @@ -21,11 +21,11 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index eaa1494f..eb2d39e8 100644 --- a/libjulia/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -18,12 +18,12 @@ * Module providing metrics for the optimizer. */ -#include <libjulia/optimiser/Metrics.h> +#include <libyul/optimiser/Metrics.h> #include <libsolidity/inlineasm/AsmData.h> using namespace dev; -using namespace dev::julia; +using namespace dev::yul; size_t CodeSize::codeSize(Statement const& _statement) { diff --git a/libjulia/optimiser/Metrics.h b/libyul/optimiser/Metrics.h index ff058fc3..8ed73cca 100644 --- a/libjulia/optimiser/Metrics.h +++ b/libyul/optimiser/Metrics.h @@ -20,11 +20,11 @@ #pragma once -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> namespace dev { -namespace julia +namespace yul { class CodeSize: public ASTWalker diff --git a/libjulia/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index c0d0b707..b71fa982 100644 --- a/libjulia/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -18,13 +18,13 @@ * Specific AST walker that collects all defined names. */ -#include <libjulia/optimiser/NameCollector.h> +#include <libyul/optimiser/NameCollector.h> #include <libsolidity/inlineasm/AsmData.h> using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; void NameCollector::operator()(VariableDeclaration const& _varDecl) { diff --git a/libjulia/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index 29856172..b8f6c1d7 100644 --- a/libjulia/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -20,7 +20,7 @@ #pragma once -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> #include <string> #include <map> @@ -28,7 +28,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index cae19381..f7385471 100644 --- a/libjulia/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -18,11 +18,11 @@ * Optimiser component that can create new unique names. */ -#include <libjulia/optimiser/NameDispenser.h> +#include <libyul/optimiser/NameDispenser.h> using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; string NameDispenser::newName(string const& _prefix) { diff --git a/libjulia/optimiser/NameDispenser.h b/libyul/optimiser/NameDispenser.h index 91c43d54..64ec318f 100644 --- a/libjulia/optimiser/NameDispenser.h +++ b/libyul/optimiser/NameDispenser.h @@ -24,7 +24,7 @@ namespace dev { -namespace julia +namespace yul { struct NameDispenser diff --git a/libjulia/optimiser/README.md b/libyul/optimiser/README.md index 877f8255..faef818b 100644 --- a/libjulia/optimiser/README.md +++ b/libyul/optimiser/README.md @@ -56,6 +56,61 @@ depend on any side-effects. As an example, neither ``mload`` nor ``mstore`` would be allowed. +## Expression Splitter + +The expression splitter turns expressions like ``add(mload(x), mul(mload(y), 0x20))`` +into a sequence of declarations of unique variables that are assigned sub-expressions +of that expression so that each function call has only variables or literals +as arguments. + +The above would be transformed into + + { + let _1 := mload(y) + let _2 := mul(_1, 0x20) + let _3 := mload(x) + let z := add(_3, _2) + } + +Note that this transformation does not change the order of opcodes or function calls. + +It is not applied to loop conditions, because the loop control flow does not allow +this "outlining" of the inner expressions in all cases. + +The final program should be in a form such that with the exception of loop conditions, +function calls can only appear in the right-hand side of a variable declaration, +assignments or expression statements and all arguments have to be constants or variables. + +The benefits of this form are that it is much easier to re-order the sequence of opcodes +and it is also easier to perform function call inlining. The drawback is that +such code is much harder to read for humans. + +## Expression Joiner + +This is the opposite operation of the expression splitter. It turns a sequence of +variable declarations that have exactly one reference into a complex expression. +This stage again fully preserves the order of function calls and opcode executions. +It does not make use of any information concerning the commutability of opcodes; +if moving the value of a variable to its place of use would change the order +of any function call or opcode execution, the transformation is not performed. + +Note that the component will not move the assigned value of a variable assignment +or a variable that is referenced more than once. + +## Common Subexpression Eliminator + +This step replaces a subexpression by the value of a pre-existing variable +that currently has the same value (only if the value is movable), based +on a syntactic comparison. + +This can be used to compute a local value numbering, especially if the +expression splitter is used before. + +The expression simplifier will be able to perform better replacements +if the common subexpression eliminator was run right before it. + +Prerequisites: Disambiguator + ## Full Function Inliner ## Rematerialisation diff --git a/libjulia/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index 392099fb..dd6653ea 100644 --- a/libjulia/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -18,17 +18,17 @@ * Optimisation stage that replaces variables by their most recently assigned expressions. */ -#include <libjulia/optimiser/Rematerialiser.h> +#include <libyul/optimiser/Rematerialiser.h> -#include <libjulia/optimiser/Metrics.h> -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/Exceptions.h> +#include <libyul/optimiser/Metrics.h> +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; void Rematerialiser::visit(Expression& _e) { diff --git a/libjulia/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index 60dbfada..afcfab3e 100644 --- a/libjulia/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -20,7 +20,7 @@ #pragma once -#include <libjulia/optimiser/DataFlowAnalyzer.h> +#include <libyul/optimiser/DataFlowAnalyzer.h> #include <string> #include <map> @@ -28,7 +28,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index f28925a4..33f3af77 100644 --- a/libjulia/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -18,9 +18,9 @@ * Specific AST walkers that collect semantical facts. */ -#include <libjulia/optimiser/Semantics.h> +#include <libyul/optimiser/Semantics.h> -#include <libjulia/Exceptions.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> @@ -30,7 +30,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; MovableChecker::MovableChecker(Expression const& _expression) { diff --git a/libjulia/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 6df5f01a..1caa12fb 100644 --- a/libjulia/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -20,7 +20,7 @@ #pragma once -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> #include <string> #include <map> @@ -28,7 +28,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 56cb96ac..762473e5 100644 --- a/libjulia/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -18,12 +18,12 @@ * Module for applying replacement rules against Expressions. */ -#include <libjulia/optimiser/SimplificationRules.h> +#include <libyul/optimiser/SimplificationRules.h> -#include <libjulia/optimiser/Utilities.h> -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/optimiser/Semantics.h> -#include <libjulia/optimiser/SyntacticalEquality.h> +#include <libyul/optimiser/Utilities.h> +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/optimiser/Semantics.h> +#include <libyul/optimiser/SyntacticalEquality.h> #include <libsolidity/inlineasm/AsmData.h> @@ -31,7 +31,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch(Expression const& _expr) diff --git a/libjulia/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index e35e6466..25d91573 100644 --- a/libjulia/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -23,7 +23,7 @@ #include <libevmasm/ExpressionClasses.h> #include <libevmasm/SimplificationRule.h> -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> #include <libsolidity/inlineasm/AsmData.h> @@ -34,7 +34,7 @@ namespace dev { -namespace julia +namespace yul { class Pattern; diff --git a/libjulia/optimiser/Substitution.cpp b/libyul/optimiser/Substitution.cpp index 668b6cb6..4a000a85 100644 --- a/libjulia/optimiser/Substitution.cpp +++ b/libyul/optimiser/Substitution.cpp @@ -18,13 +18,13 @@ * Specific AST copier that replaces certain identifiers with expressions. */ -#include <libjulia/optimiser/Substitution.h> +#include <libyul/optimiser/Substitution.h> #include <libsolidity/inlineasm/AsmData.h> using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; Expression Substitution::translate(Expression const& _expression) { diff --git a/libjulia/optimiser/Substitution.h b/libyul/optimiser/Substitution.h index 313a08d7..b734cdca 100644 --- a/libjulia/optimiser/Substitution.h +++ b/libyul/optimiser/Substitution.h @@ -20,7 +20,7 @@ #pragma once -#include <libjulia/optimiser/ASTCopier.h> +#include <libyul/optimiser/ASTCopier.h> #include <string> #include <map> @@ -28,7 +28,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index c497336d..f22b5c31 100644 --- a/libjulia/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -18,9 +18,9 @@ * Component that can compare ASTs for equality on a syntactic basis. */ -#include <libjulia/optimiser/SyntacticalEquality.h> +#include <libyul/optimiser/SyntacticalEquality.h> -#include <libjulia/Exceptions.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> @@ -28,7 +28,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& _e2) { diff --git a/libjulia/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h index b7c09330..e9fbebe0 100644 --- a/libjulia/optimiser/SyntacticalEquality.h +++ b/libyul/optimiser/SyntacticalEquality.h @@ -20,13 +20,13 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> #include <vector> namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index af503712..74b6bee4 100644 --- a/libjulia/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -18,12 +18,12 @@ * Optimisation stage that removes unused variables and functions. */ -#include <libjulia/optimiser/UnusedPruner.h> +#include <libyul/optimiser/UnusedPruner.h> -#include <libjulia/optimiser/NameCollector.h> -#include <libjulia/optimiser/Semantics.h> -#include <libjulia/optimiser/Utilities.h> -#include <libjulia/Exceptions.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/Semantics.h> +#include <libyul/optimiser/Utilities.h> +#include <libyul/Exceptions.h> #include <libsolidity/inlineasm/AsmData.h> @@ -31,7 +31,7 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; UnusedPruner::UnusedPruner(Block& _ast) { diff --git a/libjulia/optimiser/UnusedPruner.h b/libyul/optimiser/UnusedPruner.h index 73e8de7c..327921ea 100644 --- a/libjulia/optimiser/UnusedPruner.h +++ b/libyul/optimiser/UnusedPruner.h @@ -20,7 +20,7 @@ #pragma once -#include <libjulia/optimiser/ASTWalker.h> +#include <libyul/optimiser/ASTWalker.h> #include <string> #include <map> @@ -28,7 +28,7 @@ namespace dev { -namespace julia +namespace yul { /** diff --git a/libjulia/optimiser/Utilities.cpp b/libyul/optimiser/Utilities.cpp index ff108b89..df01ed39 100644 --- a/libjulia/optimiser/Utilities.cpp +++ b/libyul/optimiser/Utilities.cpp @@ -18,7 +18,7 @@ * Some useful snippets for the optimiser. */ -#include <libjulia/optimiser/Utilities.h> +#include <libyul/optimiser/Utilities.h> #include <libsolidity/inlineasm/AsmData.h> @@ -28,9 +28,9 @@ using namespace std; using namespace dev; -using namespace dev::julia; +using namespace dev::yul; -void dev::julia::removeEmptyBlocks(Block& _block) +void dev::yul::removeEmptyBlocks(Block& _block) { auto isEmptyBlock = [](Statement const& _st) -> bool { return _st.type() == typeid(Block) && boost::get<Block>(_st).statements.empty(); diff --git a/libjulia/optimiser/Utilities.h b/libyul/optimiser/Utilities.h index 88ba3f47..5b18a27c 100644 --- a/libjulia/optimiser/Utilities.h +++ b/libyul/optimiser/Utilities.h @@ -20,11 +20,11 @@ #pragma once -#include <libjulia/ASTDataForward.h> +#include <libyul/ASTDataForward.h> namespace dev { -namespace julia +namespace yul { /// Removes statements that are just empty blocks (non-recursive). diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index 4e930707..632c1daa 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -23,7 +23,6 @@ REPO_ROOT="$(dirname "$0")"/.. mkdir "$SOLDIR" # Store the current source git checkout-index -a --prefix="$SOLDIR" - git submodule foreach 'git checkout-index -a --prefix="'"$SOLDIR"'/$path/"' # Store the commit hash echo "$commithash" > "$SOLDIR/commit_hash.txt" if [ -e prerelease.txt -a ! -s prerelease.txt ] diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8fd0d6ef..4052ed13 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -226,21 +226,21 @@ void CommandLineInterface::handleBinary(string const& _contract) if (m_args.count(g_argBinary)) { if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", m_compiler->object(_contract).toHex()); + createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); else { cout << "Binary: " << endl; - cout << m_compiler->object(_contract).toHex() << endl; + cout << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; } } if (m_args.count(g_argBinaryRuntime)) { if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", m_compiler->runtimeObject(_contract).toHex()); + createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); else { cout << "Binary of the runtime part: " << endl; - cout << m_compiler->runtimeObject(_contract).toHex() << endl; + cout << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl; } } } @@ -482,9 +482,23 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) string addrString(lib.begin() + colon + 1, lib.end()); boost::trim(libName); boost::trim(addrString); + if (addrString.substr(0, 2) == "0x") + addrString = addrString.substr(2); + if (addrString.empty()) + { + cerr << "Empty address provided for library \"" << libName << "\": " << endl; + cerr << "Note that there should not be any whitespace after the colon." << endl; + return false; + } + else if (addrString.length() != 40) + { + cerr << "Invalid length for address for library \"" << libName << "\": " << addrString.length() << " instead of 40 characters." << endl; + return false; + } if (!passesAddressChecksum(addrString, false)) { - cerr << "Invalid checksum on library address \"" << libName << "\": " << addrString << endl; + cerr << "Invalid checksum on address for library \"" << libName << "\": " << addrString << endl; + cerr << "The correct checksum is " << dev::getChecksummedAddress(addrString) << endl; return false; } bytes binAddr = fromHex(addrString); @@ -569,7 +583,7 @@ Allowed options)", g_argLibraries.c_str(), po::value<vector<string>>()->value_name("libs"), "Direct string or file containing library addresses. Syntax: " - "<libraryName>: <address> [, or whitespace] ...\n" + "<libraryName>:<address> [, or whitespace] ...\n" "Address is interpreted as a hex string optionally prefixed by 0x." ) ( @@ -1056,8 +1070,12 @@ bool CommandLineInterface::link() { string const& name = library.first; // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'. - // This leaves 36 characters for the library name, while too short library names are - // padded on the right with '_' and too long names are truncated. + // This leaves 36 characters for the library identifier. The identifier used to + // be just the cropped or '_'-padded library name, but this changed to + // the cropped hex representation of the hash of the library name. + // We support both ways of linking here. + librariesReplacements["__" + eth::LinkerObject::libraryPlaceholder(name) + "__"] = library.second; + string replacement = "__"; for (size_t i = 0; i < placeholderSize - 4; ++i) replacement.push_back(i < name.size() ? name[i] : '_'); @@ -1087,6 +1105,11 @@ bool CommandLineInterface::link() cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl; it += placeholderSize; } + // Remove hints for resolved libraries. + for (auto const& library: m_libraries) + boost::algorithm::erase_all(src.second, "\n" + libraryPlaceholderHint(library.first)); + while (!src.second.empty() && *prev(src.second.end()) == '\n') + src.second.resize(src.second.size() - 1); } return true; } @@ -1100,6 +1123,23 @@ void CommandLineInterface::writeLinkedFiles() writeFile(src.first, src.second); } +string CommandLineInterface::libraryPlaceholderHint(string const& _libraryName) +{ + return "// " + eth::LinkerObject::libraryPlaceholder(_libraryName) + " -> " + _libraryName; +} + +string CommandLineInterface::objectWithLinkRefsHex(eth::LinkerObject const& _obj) +{ + string out = _obj.toHex(); + if (!_obj.linkReferences.empty()) + { + out += "\n"; + for (auto const& linkRef: _obj.linkReferences) + out += "\n" + libraryPlaceholderHint(linkRef.second); + } + return out; +} + bool CommandLineInterface::assemble( AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 010dce34..aa49383a 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -54,6 +54,10 @@ public: private: bool link(); void writeLinkedFiles(); + /// @returns the ``// <identifier> -> name`` hint for library placeholders. + static std::string libraryPlaceholderHint(std::string const& _libraryName); + /// @returns the full object with library placeholder hints in hex. + static std::string objectWithLinkRefsHex(eth::LinkerObject const& _obj); bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 701d53e5..91c1b200 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,8 +7,8 @@ file(GLOB libdevcore_sources "libdevcore/*.cpp") file(GLOB libdevcore_headers "libdevcore/*.h") file(GLOB libevmasm_sources "libevmasm/*.cpp") file(GLOB libevmasm_headers "libevmasm/*.h") -file(GLOB libjulia_sources "libjulia/*.cpp") -file(GLOB libjulia_headers "libjulia/*.h") +file(GLOB libyul_sources "libyul/*.cpp") +file(GLOB libyul_headers "libyul/*.h") file(GLOB liblll_sources "liblll/*.cpp") file(GLOB liblll_headers "liblll/*.h") file(GLOB libsolidity_sources "libsolidity/*.cpp") @@ -18,7 +18,7 @@ add_executable(soltest ${sources} ${headers} ${contracts_sources} ${contracts_headers} ${libdevcore_sources} ${libdevcore_headers} ${libevmasm_sources} ${libevmasm_headers} - ${libjulia_sources} ${libjulia_headers} + ${libyul_sources} ${libyul_headers} ${liblll_sources} ${liblll_headers} ${libsolidity_sources} ${libsolidity_headers} ) diff --git a/test/Common.cpp b/test/Common.cpp new file mode 100644 index 00000000..a80c55d4 --- /dev/null +++ b/test/Common.cpp @@ -0,0 +1,50 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <test/Common.h> + +#include <boost/filesystem.hpp> + +namespace fs = boost::filesystem; + +namespace dev +{ +namespace test +{ + +boost::filesystem::path discoverTestPath() +{ + auto const searchPath = + { + fs::current_path() / ".." / ".." / ".." / "test", + fs::current_path() / ".." / ".." / "test", + fs::current_path() / ".." / "test", + fs::current_path() / "test", + fs::current_path() + }; + for (auto const& basePath: searchPath) + { + fs::path syntaxTestPath = basePath / "libsolidity" / "syntaxTests"; + if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) + return basePath; + } + return {}; +} + + +} +} diff --git a/test/Common.h b/test/Common.h new file mode 100644 index 00000000..e87faa0e --- /dev/null +++ b/test/Common.h @@ -0,0 +1,35 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <boost/filesystem/path.hpp> + +namespace dev +{ +namespace test +{ + +/// Tries to find a path that contains the directories "libsolidity/syntaxTests" +/// and returns it if found. +/// The routine searches in the current directory, and inside the "test" directory +/// starting from the current directory and up to three levels up. +/// @returns the path of the first match or an empty path if not found. +boost::filesystem::path discoverTestPath(); + +} +} diff --git a/test/Options.cpp b/test/Options.cpp index ff4a7c98..da08eb37 100644 --- a/test/Options.cpp +++ b/test/Options.cpp @@ -21,13 +21,17 @@ #include <test/Options.h> +#include <test/Common.h> + #include <libsolidity/interface/EVMVersion.h> #include <libsolidity/interface/Exceptions.h> #include <boost/test/framework.hpp> +#include <boost/filesystem.hpp> using namespace std; using namespace dev::test; +namespace fs = boost::filesystem; Options const& Options::get() { @@ -70,6 +74,9 @@ Options::Options() if (testPath.empty()) if (auto path = getenv("ETH_TEST_PATH")) testPath = path; + + if (testPath.empty()) + testPath = discoverTestPath(); } void Options::validate() const diff --git a/test/boostTest.cpp b/test/boostTest.cpp index d9e939eb..d27ba1f6 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -38,6 +38,7 @@ #include <test/Options.h> #include <test/libsolidity/ASTJSONTest.h> #include <test/libsolidity/SyntaxTest.h> +#include <test/libyul/YulOptimizerTest.h> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/predicate.hpp> @@ -138,6 +139,12 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) "ASTJSON", ASTJSONTest::create ) > 0, "no JSON AST tests found"); + solAssert(registerTests( + master, + dev::test::Options::get().testPath / "libyul", + "yulOptimizerTests", + dev::yul::test::YulOptimizerTest::create + ) > 0, "no Yul Optimizer tests found"); if (dev::test::Options::get().disableIPC) { for (auto suite: { diff --git a/test/cmdlineErrorReports/too_long_line.sol.ref b/test/cmdlineErrorReports/too_long_line.sol.ref index 7cad93ee..55cd1935 100644 --- a/test/cmdlineErrorReports/too_long_line.sol.ref +++ b/test/cmdlineErrorReports/too_long_line.sol.ref @@ -1,4 +1,3 @@ - too_long_line.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref b/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref index f2ea427a..9a5ebfba 100644 --- a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref @@ -1,4 +1,3 @@ - too_long_line_both_sides_short.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref b/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref index b6699933..ad3b7805 100644 --- a/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref @@ -1,4 +1,3 @@ - too_long_line_edge_in.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref b/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref index 76df589a..d8495c11 100644 --- a/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref @@ -1,4 +1,3 @@ - too_long_line_edge_out.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_left_short.sol.ref b/test/cmdlineErrorReports/too_long_line_left_short.sol.ref index efaa559d..00b6be5c 100644 --- a/test/cmdlineErrorReports/too_long_line_left_short.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_left_short.sol.ref @@ -1,4 +1,3 @@ - too_long_line_left_short.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineErrorReports/too_long_line_right_short.sol.ref b/test/cmdlineErrorReports/too_long_line_right_short.sol.ref index 2b0c6d8c..88072d95 100644 --- a/test/cmdlineErrorReports/too_long_line_right_short.sol.ref +++ b/test/cmdlineErrorReports/too_long_line_right_short.sol.ref @@ -1,4 +1,3 @@ - too_long_line_right_short.sol:1:1: Warning: Source file does not specify required compiler version! contract C { ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index d7b95e8a..20254ef4 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -123,7 +123,8 @@ test_solc_file_input_failures() { exitCode=$? set -e - stderr=`sed 's/.*This is a pre-release compiler version, please do not use it in production.*$//' $stderr_path` + sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" + sed -i -e 's/ \?Consider adding "pragma .*$//' "$stderr_path" if [[ $exitCode -eq 0 ]]; then printError "Incorrect exit code. Expected failure (non-zero) but got success (0)." @@ -141,12 +142,12 @@ test_solc_file_input_failures() { exit 1 fi - if [[ "$stderr" != "${stderr_expected}" ]]; then + if [[ "$(cat $stderr_path)" != "${stderr_expected}" ]]; then printError "Incorrect output on stderr received. Expected:" echo -e "${stderr_expected}" printError "But got:" - echo $stderr + cat $stderr_path rm -f $stdout_path $stderr_path exit 1 fi @@ -232,6 +233,24 @@ echo '' | "$SOLC" - --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd printTask "Testing long library names..." echo '' | "$SOLC" - --link --libraries aveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylonglibraryname:0x90f20564390eAe531E810af625A22f51385Cd222 >/dev/null +printTask "Testing linking itself..." +SOLTMPDIR=$(mktemp -d) +( + cd "$SOLTMPDIR" + set -e + echo 'library L { function f() public pure {} } contract C { function f() public pure { L.f(); } }' > x.sol + "$SOLC" --bin -o . x.sol 2>/dev/null + # Explanation and placeholder should be there + grep -q '//' C.bin && grep -q '__' C.bin + # But not in library file. + grep -q -v '[/_]' L.bin + # Now link + "$SOLC" --link --libraries x.sol:L:0x90f20564390eAe531E810af625A22f51385Cd222 C.bin + # Now the placeholder and explanation should be gone. + grep -q -v '[/_]' C.bin +) +rm -rf "$SOLTMPDIR" + printTask "Testing overwriting files..." SOLTMPDIR=$(mktemp -d) ( diff --git a/test/libdevcore/IterateReplacing.cpp b/test/libdevcore/IterateReplacing.cpp new file mode 100644 index 00000000..08cd1e22 --- /dev/null +++ b/test/libdevcore/IterateReplacing.cpp @@ -0,0 +1,97 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Unit tests for the iterateReplacing function + */ + +#include <libdevcore/CommonData.h> + +#include <test/Options.h> + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(IterateReplacing) + +BOOST_AUTO_TEST_CASE(no_replacement) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string&) -> boost::optional<vector<string>> { return {}; }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "def", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(empty_input) +{ + vector<string> v; + function<boost::optional<vector<string>>(string&)> f = [](string&) -> boost::optional<vector<string>> { return {}; }; + iterateReplacing(v, f); + vector<string> expectation; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(delete_some) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "def") + return vector<string>(); + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(inject_some_start) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "abc") + return vector<string>{"x", "y"}; + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"x", "y", "def", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(inject_some_end) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "ghi") + return vector<string>{"x", "y"}; + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "def", "x", "y"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index bc652f56..1c041596 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) BOOST_CHECK_EQUAL( _assembly.assemble().toHex(), - "5b6001600220606773__someLibrary___________________________" + "5b6001600220606773__$bf005014d9d0f534b8fcb268bd84c491a2$__" "6000567f556e75736564206665617475726520666f722070757368696e" "6720737472696e605f6001605e73000000000000000000000000000000000000000000fe" "fe010203044266eeaa" diff --git a/test/libjulia/CommonSubexpression.cpp b/test/libjulia/CommonSubexpression.cpp deleted file mode 100644 index 6c8edf1f..00000000 --- a/test/libjulia/CommonSubexpression.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Unit tests for the common subexpression eliminator optimizer stage. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/CommonSubexpressionEliminator.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p;\ - Block b = disambiguate(_original, false);\ - (CommonSubexpressionEliminator{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation, false));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(YulCSE) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(trivial) -{ - CHECK( - "{ let a := mul(1, codesize()) let b := mul(1, codesize()) }", - "{ let a := mul(1, codesize()) let b := a }" - ); -} - -BOOST_AUTO_TEST_CASE(non_movable_instr) -{ - CHECK( - "{ let a := mload(1) let b := mload(1) }", - "{ let a := mload(1) let b := mload(1) }" - ); -} - -BOOST_AUTO_TEST_CASE(non_movable_instr2) -{ - CHECK( - "{ let a := gas() let b := gas() }", - "{ let a := gas() let b := gas() }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_if) -{ - CHECK( - "{ let b := 1 if b { b := 1 } let c := 1 }", - "{ let b := 1 if b { b := b } let c := 1 }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_for) -{ - CHECK( - "{ let a := 1 let b := codesize()" - "for { } lt(1, codesize()) { mstore(1, codesize()) a := add(a, codesize()) }" - "{ mstore(1, codesize()) } mstore(1, codesize()) }", - - "{ let a := 1 let b := codesize()" - "for { } lt(1, b) { mstore(1, b) a := add(a, b) }" - "{ mstore(1, b) } mstore(1, b) }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp deleted file mode 100644 index 48e02c7e..00000000 --- a/test/libjulia/Disambiguator.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the Yul name disambiguator. - */ - -#include <test/libjulia/Common.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia::test; -using namespace dev::solidity; - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p(true);\ - string result = p(disambiguate(_original));\ - BOOST_CHECK_EQUAL(result, format(_expectation));\ - BOOST_CHECK_EQUAL(result, p(disambiguate(result)));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(YulDisambiguator) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(variables) -{ - CHECK( - "{ { let a:u256 } { let a:u256 } }", - "{ { let a:u256 } { let a_1:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(variables_clash) -{ - CHECK( - "{ { let a:u256 let a_1:u256 } { let a:u256 } }", - "{ { let a:u256 let a_1:u256 } { let a_2:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(variables_inside_functions) -{ - CHECK( - "{ { let c:u256 let b:u256 } function f(a:u256, c:u256) -> b:u256 { let x:u256 } { let a:u256 let x:u256 } }", - "{ { let c:u256 let b:u256 } function f(a:u256, c_1:u256) -> b_1:u256 { let x:u256 } { let a_1:u256 let x_1:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(function_call) -{ - CHECK( - "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f(a:u256) -> c:u256, d:u256 { let b:u256, c_1:u256 := f(a) } } }", - "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f_1(a_1:u256) -> c_1:u256, d_1:u256 { let b_1:u256, c_1_1:u256 := f_1(a_1) } } }" - ); -} - -BOOST_AUTO_TEST_CASE(for_statement) -{ - CHECK( - "{ { let a:u256, b:u256 } { for { let a:u256 } a { a := a } { let b:u256 := a } } }", - "{ { let a:u256, b:u256 } { for { let a_1:u256 } a_1 { a_1 := a_1 } { let b_1:u256 := a_1 } } }" - ); -} - -BOOST_AUTO_TEST_CASE(switch_statement) -{ - CHECK( - "{ { let a:u256, b:u256, c:u256 } { let a:u256 switch a case 0:u256 { let b:u256 := a } default { let c:u256 := a } } }", - "{ { let a:u256, b:u256, c:u256 } { let a_1:u256 switch a_1 case 0:u256 { let b_1:u256 := a_1 } default { let c_1:u256 := a_1 } } }" - ); -} - -BOOST_AUTO_TEST_CASE(if_statement) -{ - CHECK( - "{ { let a:u256, b:u256, c:u256 } { let a:bool if a { let b:bool := a } } }", - "{ { let a:u256, b:u256, c:u256 } { let a_1:bool if a_1 { let b_1:bool := a_1 } } }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/FunctionGrouper.cpp b/test/libjulia/FunctionGrouper.cpp deleted file mode 100644 index f1e83449..00000000 --- a/test/libjulia/FunctionGrouper.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the Yul function grouper. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/FunctionGrouper.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p(true);\ - Block b = disambiguate(_original);\ - (FunctionGrouper{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(YulFunctionGrouper) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ { } }"); -} - -BOOST_AUTO_TEST_CASE(single_fun) -{ - CHECK( - "{ let a:u256 function f() {} }", - "{ { let a:u256 } function f() {} }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_fun_mixed) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", - "{ { let a:u256 let c:u256 let e:u256 } function f() { let b:u256 } function g() { let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(nested_fun) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", - "{ { let a:u256 } function f() { let b:u256 function g() { let c:u256} let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(empty_block) -{ - CHECK( - "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", - "{ { let a:u256 { } } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/FunctionHoister.cpp b/test/libjulia/FunctionHoister.cpp deleted file mode 100644 index 348963b4..00000000 --- a/test/libjulia/FunctionHoister.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the Yul function hoister. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/FunctionHoister.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p(true);\ - Block b = disambiguate(_original);\ - (FunctionHoister{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(YulFunctionHoister) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(single_fun) -{ - CHECK( - "{ let a:u256 function f() {} }", - "{ let a:u256 function f() {} }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_fun_mixed) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", - "{ let a:u256 let c:u256 let e:u256 function f() { let b:u256 } function g() { let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(nested_fun) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", - "{ let a:u256 function g() { let c:u256 } function f() { let b:u256 let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(empty_block) -{ - CHECK( - "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", - "{ let a:u256 function f() -> x:bool { let b:u256 := 4:u256 for {} f() {} {} } }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Inliner.cpp b/test/libjulia/Inliner.cpp deleted file mode 100644 index d0ecd42f..00000000 --- a/test/libjulia/Inliner.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the Yul function inliner. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/ExpressionInliner.h> -#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h> -#include <libjulia/optimiser/FullInliner.h> -#include <libjulia/optimiser/FunctionHoister.h> -#include <libjulia/optimiser/FunctionGrouper.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - -namespace -{ -string inlinableFunctions(string const& _source) -{ - auto ast = disambiguate(_source); - - InlinableExpressionFunctionFinder funFinder; - funFinder(ast); - - return boost::algorithm::join( - funFinder.inlinableFunctions() | boost::adaptors::map_keys, - "," - ); -} - -string inlineFunctions(string const& _source, bool _yul = true) -{ - auto ast = disambiguate(_source, _yul); - ExpressionInliner(ast).run(); - return assembly::AsmPrinter(_yul)(ast); -} -string fullInline(string const& _source, bool _yul = true) -{ - Block ast = disambiguate(_source, _yul); - (FunctionHoister{})(ast); - (FunctionGrouper{})(ast);\ - FullInliner(ast).run(); - return assembly::AsmPrinter(_yul)(ast); -} -} - - -BOOST_AUTO_TEST_SUITE(YulInlinableFunctionFilter) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - BOOST_CHECK_EQUAL(inlinableFunctions("{ }"), ""); -} - -BOOST_AUTO_TEST_CASE(simple) -{ - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := 2:u256 } }"), "f"); - BOOST_CHECK_EQUAL(inlinableFunctions("{" - "function g(a:u256) -> b:u256 { b := a }" - "function f() -> x:u256 { x := g(2:u256) }" - "}"), "f,g"); -} - -BOOST_AUTO_TEST_CASE(simple_inside_structures) -{ - BOOST_CHECK_EQUAL(inlinableFunctions("{" - "switch 2:u256 " - "case 2:u256 {" - "function g(a:u256) -> b:u256 { b := a }" - "function f() -> x:u256 { x := g(2:u256) }" - "}" - "}"), "f,g"); - BOOST_CHECK_EQUAL(inlinableFunctions("{" - "for {" - "function g(a:u256) -> b:u256 { b := a }" - "} 1:u256 {" - "function f() -> x:u256 { x := g(2:u256) }" - "}" - "{" - "function h() -> y:u256 { y := 2:u256 }" - "}" - "}"), "f,g,h"); -} - -BOOST_AUTO_TEST_CASE(negative) -{ - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := 2:u256 {} } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := f() } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := x } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256, y:u256 { x := 2:u256 } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions( - "{ function g() -> x:u256, y:u256 {} function f(y:u256) -> x:u256 { x,y := g() } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f(y:u256) -> x:u256 { y := 2:u256 } }"), ""); -} - - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(YulFunctionInliner) - -BOOST_AUTO_TEST_CASE(simple) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{ function f() -> x:u256 { x := 2:u256 } let y:u256 := f() }"), - format("{ function f() -> x:u256 { x := 2:u256 } let y:u256 := 2:u256 }") - ); -} - -BOOST_AUTO_TEST_CASE(with_args) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{ function f(a:u256) -> x:u256 { x := a } let y:u256 := f(7:u256) }"), - format("{ function f(a:u256) -> x:u256 { x := a } let y:u256 := 7:u256 }") - ); -} - -BOOST_AUTO_TEST_CASE(no_inline_with_mload) -{ - // Does not inline because mload could be moved out of sequence - BOOST_CHECK_EQUAL( - inlineFunctions("{ function f(a) -> x { x := a } let y := f(mload(2)) }", false), - format("{ function f(a) -> x { x := a } let y := f(mload(2)) }", false) - ); -} - -BOOST_AUTO_TEST_CASE(no_move_with_side_effects) -{ - // The calls to g and h cannot be moved because g and h are not movable. Therefore, the call - // to f is not inlined. - BOOST_CHECK_EQUAL( - inlineFunctions("{" - "function f(a, b) -> x { x := add(b, a) }" - "function g() -> y { y := mload(0) mstore(0, 4) }" - "function h() -> z { mstore(0, 4) z := mload(0) }" - "let r := f(g(), h())" - "}", false), - format("{" - "function f(a, b) -> x { x := add(b, a) }" - "function g() -> y { y := mload(0) mstore(0, 4) }" - "function h() -> z { mstore(0, 4) z := mload(0) }" - "let r := f(g(), h())" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(complex_with_evm) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{ function f(a) -> x { x := add(a, a) } let y := f(calldatasize()) }", false), - format("{ function f(a) -> x { x := add(a, a) } let y := add(calldatasize(), calldatasize()) }", false) - ); -} - -BOOST_AUTO_TEST_CASE(double_calls) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{" - "function f(a) -> x { x := add(a, a) }" - "function g(b, c) -> y { y := mul(mload(c), f(b)) }" - "let y := g(calldatasize(), 7)" - "}", false), - format("{" - "function f(a) -> x { x := add(a, a) }" - "function g(b, c) -> y { y := mul(mload(c), add(b, b)) }" - "let y_1 := mul(mload(7), add(calldatasize(), calldatasize()))" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(double_recursive_calls) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{" - "function f(a, r) -> x { x := g(a, g(r, r)) }" - "function g(b, s) -> y { y := f(b, f(s, s)) }" - "let y := g(calldatasize(), 7)" - "}", false), - format("{" - "function f(a, r) -> x { x := g(a, f(r, f(r, r))) }" - "function g(b, s) -> y { y := f(b, g(s, f(s, f(s, s))))}" - "let y_1 := f(calldatasize(), g(7, f(7, f(7, 7))))" - "}", false) - ); -} - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(YulFullInliner) - -BOOST_AUTO_TEST_CASE(simple) -{ - BOOST_CHECK_EQUAL( - fullInline("{" - "function f(a) -> x { let r := mul(a, a) x := add(r, r) }" - "let y := add(f(sload(mload(2))), mload(7))" - "}", false), - format("{" - "{" - "let _1 := mload(7)" - "let f_a := sload(mload(2))" - "let f_x" - "{" - "let f_r := mul(f_a, f_a)" - "f_x := add(f_r, f_r)" - "}" - "let y := add(f_x, _1)" - "}" - "function f(a) -> x" - "{" - "let r := mul(a, a)" - "x := add(r, r)" - "}" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(multi_fun) -{ - BOOST_CHECK_EQUAL( - fullInline("{" - "function f(a) -> x { x := add(a, a) }" - "function g(b, c) -> y { y := mul(mload(c), f(b)) }" - "let y := g(f(3), 7)" - "}", false), - format("{" - "{" - "let g_c := 7 " - "let f_a_1 := 3 " - "let f_x_1 " - "{ f_x_1 := add(f_a_1, f_a_1) } " - "let g_y " - "{" - "let g_f_a := f_x_1 " - "let g_f_x " - "{" - "g_f_x := add(g_f_a, g_f_a)" - "}" - "g_y := mul(mload(g_c), g_f_x)" - "}" - "let y_1 := g_y" - "}" - "function f(a) -> x" - "{" - "x := add(a, a)" - "}" - "function g(b, c) -> y" - "{" - "let f_a := b " - "let f_x " - "{" - "f_x := add(f_a, f_a)" - "}" - "y := mul(mload(c), f_x)" - "}" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(move_up_rightwards_arguments) -{ - BOOST_CHECK_EQUAL( - fullInline("{" - "function f(a, b, c) -> x { x := add(a, b) x := mul(x, c) }" - "let y := add(mload(1), add(f(mload(2), mload(3), mload(4)), mload(5)))" - "}", false), - format("{" - "{" - "let _1 := mload(5)" - "let f_c := mload(4)" - "let f_b := mload(3)" - "let f_a := mload(2)" - "let f_x" - "{" - "f_x := add(f_a, f_b)" - "f_x := mul(f_x, f_c)" - "}" - "let y := add(mload(1), add(f_x, _1))" - "}" - "function f(a, b, c) -> x" - "{" - "x := add(a, b)" - "x := mul(x, c)" - "}" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(pop_result) -{ - // This tests that `pop(r)` is removed. - BOOST_CHECK_EQUAL( - fullInline("{" - "function f(a) -> x { let r := mul(a, a) x := add(r, r) }" - "pop(add(f(7), 2))" - "}", false), - format("{" - "{" - "let _1 := 2 " - "let f_a := 7 " - "let f_x " - "{" - "let f_r := mul(f_a, f_a) " - "f_x := add(f_r, f_r)" - "}" - "{" - "}" - "}" - "function f(a) -> x" - "{" - "let r := mul(a, a) " - "x := add(r, r)" - "}" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(inside_condition) -{ - // This tests that breaking the expression inside the condition works properly. - BOOST_CHECK_EQUAL( - fullInline("{" - "if gt(f(mload(1)), mload(0)) {" - "sstore(0, 2)" - "}" - "function f(a) -> r {" - "a := mload(a)" - "r := add(a, calldatasize())" - "}" - "}", false), - format("{" - "{" - "let _1 := mload(0)" - "let f_a := mload(1)" - "let f_r" - "{" - "f_a := mload(f_a)" - "f_r := add(f_a, calldatasize())" - "}" - "if gt(f_r, _1)" - "{" - "sstore(0, 2)" - "}" - "}" - "function f(a) -> r" - "{" - "a := mload(a)" - "r := add(a, calldatasize())" - "}" - "}", false) - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/MainFunction.cpp b/test/libjulia/MainFunction.cpp deleted file mode 100644 index e7263d13..00000000 --- a/test/libjulia/MainFunction.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2018 - * Unit tests for the Yul MainFunction transformation. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/FunctionGrouper.h> -#include <libjulia/optimiser/MainFunction.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p(true);\ - Block b = disambiguate(_original);\ - (FunctionGrouper{})(b);\ - (MainFunction{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(YulMainFunction) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ function main() { } }"); -} - -BOOST_AUTO_TEST_CASE(single_fun) -{ - CHECK( - "{ let a:u256 function f() {} }", - "{ function main() { let a:u256 } function f() {} }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_fun_mixed) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", - "{ function main() { let a:u256 let c:u256 let e:u256 } function f() { let b:u256 } function g() { let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(nested_fun) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", - "{ function main() { let a:u256 } function f() { let b:u256 function g() { let c:u256} let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(empty_block) -{ - CHECK( - "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", - "{ function main() { let a:u256 { } } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Rematerialiser.cpp b/test/libjulia/Rematerialiser.cpp deleted file mode 100644 index 63e525d5..00000000 --- a/test/libjulia/Rematerialiser.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the rematerialiser optimizer stage. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/Rematerialiser.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p;\ - Block b = disambiguate(_original, false);\ - (Rematerialiser{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation, false));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(YulRematerialiser) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(trivial) -{ - CHECK( - "{ let a := 1 let b := a mstore(0, b) }", - "{ let a := 1 let b := 1 mstore(0, 1) }" - ); -} - -BOOST_AUTO_TEST_CASE(expression) -{ - CHECK( - "{ let a := add(mul(calldatasize(), 2), number()) let b := add(a, a) }", - "{ let a := add(mul(calldatasize(), 2), number()) let b := add(" - "add(mul(calldatasize(), 2), number())," - "add(mul(calldatasize(), 2), number())" - ") }" - ); -} - -BOOST_AUTO_TEST_CASE(reassign) -{ - CHECK( - "{ let a := extcodesize(0) let b := a let c := b a := 2 let d := add(b, c) pop(a) pop(b) pop(c) pop(d) }", - "{ let a := extcodesize(0) let b := a let c := a a := 2 let d := add(b, c) pop(2) pop(b) pop(c) pop(add(b, c)) }" - ); -} - -BOOST_AUTO_TEST_CASE(non_movable_instr) -{ - CHECK( - "{ let a := 1 let b := mload(a) let c := a mstore(add(a, b), c) }", - "{ let a := 1 let b := mload(1) let c := 1 mstore(add(1, b), 1) }" - ); -} - -BOOST_AUTO_TEST_CASE(non_movable_fun) -{ - CHECK( - "{ function f(x) -> y {} let a := 1 let b := f(a) let c := a mstore(add(a, b), c) }", - "{ function f(x) -> y {} let a := 1 let b := f(1) let c := 1 mstore(add(1, b), 1) }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_if) -{ - CHECK( - "{ let a := 1 let b := 2 if b { pop(b) b := a } let c := b }", - "{ let a := 1 let b := 2 if 2 { pop(2) b := 1 } let c := b }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_switch) -{ - CHECK( - "{ let a := 1 let b := 2 switch number() case 1 { b := a } default { let x := a let y := b b := a } pop(add(a, b)) }", - "{ let a := 1 let b := 2 switch number() case 1 { b := 1 } default { let x := 1 let y := b b := 1 } pop(add(1, b)) }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_for) -{ - CHECK( - "{ let a := 1 for { pop(a) } a { pop(a) } { pop(a) } }", - "{ let a := 1 for { pop(1) } 1 { pop(1) } { pop(1) } }" - ); - CHECK( - "{ let a := 1 for { pop(a) } a { pop(a) } { a := 7 let c := a } let x := a }", - "{ let a := 1 for { pop(1) } a { pop(7) } { a := 7 let c := 7 } let x := a }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_for_declared_in_init) -{ - CHECK( - "{ let b := 0 for { let a := 1 pop(a) } a { pop(a) } { b := 1 pop(a) } }", - "{ let b := 0 for { let a := 1 pop(1) } 1 { pop(1) } { b := 1 pop(1) } }" - ); - CHECK( - "{ let b := 0 for { let a := 1 pop(a) } lt(a, 0) { pop(a) a := add(a, 3) } { b := 1 pop(a) } }", - "{ let b := 0 for { let a := 1 pop(1) } lt(a, 0) { pop(a) a := add(a, 3) } { b := 1 pop(a) } }" - ); -} - -BOOST_AUTO_TEST_CASE(reassignment) -{ - CHECK( - "{ let a := 1 pop(a) if a { a := 2 } let b := mload(a) pop(b) }", - "{ let a := 1 pop(1) if 1 { a := 2 } let b := mload(a) pop(b) }" - ); -} - -BOOST_AUTO_TEST_CASE(update_assignment_remat) -{ - // We cannot substitute `a` in `let b := a` - CHECK( - "{ let a := extcodesize(0) a := mul(a, 2) let b := a }", - "{ let a := extcodesize(0) a := mul(a, 2) let b := a }" - ); -} - -BOOST_AUTO_TEST_CASE(do_not_move_out_of_scope) -{ - // Cannot replace by `let b := x` by `let b := a` since a is out of scope. - CHECK( - "{ let x { let a := sload(0) x := a } let b := x }", - "{ let x { let a := sload(0) x := a } let b := x }" - ); -} - -BOOST_AUTO_TEST_CASE(do_not_remat_large_amounts_of_code) -{ - CHECK( - "{ let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) let b := x }", - "{ let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) let b := x }" - ); - CHECK( - "{ let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) let b := x }", - "{ let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) let b := add(mul(calldataload(2), calldataload(4)), calldatasize()) }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Simplifier.cpp b/test/libjulia/Simplifier.cpp deleted file mode 100644 index 3cc95b7a..00000000 --- a/test/libjulia/Simplifier.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the expression simplifier optimizer stage. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/ExpressionSimplifier.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p;\ - Block b = *(parse(_original, false).first);\ - (ExpressionSimplifier{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation, false));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(YulSimplifier) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(constants) -{ - CHECK( - "{ let a := add(1, mul(3, 4)) }", - "{ let a := 13 }" - ); -} - -BOOST_AUTO_TEST_CASE(invariant) -{ - CHECK( - "{ let a := mload(sub(7, 7)) let b := sub(a, 0) }", - "{ let a := mload(0) let b := a }" - ); -} - -BOOST_AUTO_TEST_CASE(reversed) -{ - CHECK( - "{ let a := add(0, mload(0)) }", - "{ let a := mload(0) }" - ); -} - -BOOST_AUTO_TEST_CASE(constant_propagation) -{ - CHECK( - "{ let a := add(7, sub(mload(0), 7)) }", - "{ let a := mload(0) }" - ); -} - -BOOST_AUTO_TEST_CASE(identity_rules_simple) -{ - CHECK( - "{ let a := mload(0) let b := sub(a, a) }", - "{ let a := mload(0) let b := 0 }" - ); -} - -BOOST_AUTO_TEST_CASE(identity_rules_complex) -{ - CHECK( - "{ let a := sub(calldataload(0), calldataload(0)) }", - "{ let a := 0 }" - ); -} - -BOOST_AUTO_TEST_CASE(identity_rules_negative) -{ - CHECK( - "{ let a := sub(calldataload(1), calldataload(0)) }", - "{ let a := sub(calldataload(1), calldataload(0)) }" - ); -} - -BOOST_AUTO_TEST_CASE(including_function_calls) -{ - CHECK( - "{ function f() -> a {} let b := add(7, sub(f(), 7)) }", - "{ function f() -> a {} let b := f() }" - ); -} - -BOOST_AUTO_TEST_CASE(inside_for) -{ - CHECK( - "{ for { let a := 10 } iszero(eq(a, 0)) { a := add(a, 1) } {} }", - "{ for { let a := 10 } iszero(iszero(a)) { a := add(a, 1) } {} }" - ); -} - -BOOST_AUTO_TEST_CASE(mod_and) -{ - CHECK( - "{ mstore(0, mod(calldataload(0), exp(2, 8))) }", - "{ mstore(0, and(calldataload(0), 255)) }" - ); - CHECK( - "{ mstore(0, mod(calldataload(0), exp(2, 255))) }", - "{ mstore(0, and(calldataload(0), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) }" - ); -} - -BOOST_AUTO_TEST_CASE(not_applied_removes_non_constant_and_not_movable) -{ - CHECK( - // The first argument of div is not constant. - // keccak256 is not movable. - "{ let a := div(keccak256(0, 0), 0) }", - "{ let a := div(keccak256(0, 0), 0) }" - ); -} - -BOOST_AUTO_TEST_CASE(not_applied_function_call_different_names) -{ - CHECK( - "{ function f1() -> a { } function f2() -> b {} let c := sub(f1(), f2()) }", - "{ function f1() -> a { } function f2() -> b {} let c := sub(f1(), f2()) }" - ); -} - -BOOST_AUTO_TEST_CASE(not_applied_function_call_different_arguments) -{ - CHECK( - "{ function f(a) -> b { } let c := sub(f(0), f(1)) }", - "{ function f(a) -> b { } let c := sub(f(0), f(1)) }" - ); -} - -BOOST_AUTO_TEST_CASE(not_applied_function_call_equality_not_movable) -{ - CHECK( - // Even if the functions pass the equality check, they are not movable. - "{ function f() -> a { } let b := sub(f(), f()) }", - "{ function f() -> a { } let b := sub(f(), f()) }" - ); -} - - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/UnusedPruner.cpp b/test/libjulia/UnusedPruner.cpp deleted file mode 100644 index 649ee149..00000000 --- a/test/libjulia/UnusedPruner.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the pruning of unused variables and functions. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/UnusedPruner.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p;\ - Block b = disambiguate(_original, false);\ - UnusedPruner::runUntilStabilised(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation, false));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(YulUnusedPruner) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(trivial) -{ - CHECK( - "{ let a := 1 let b := 1 mstore(0, 1) }", - "{ mstore(0, 1) }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_declarations) -{ - CHECK( - "{ let x, y }", - "{ }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_assignments) -{ - CHECK( - "{ let x, y x := 1 y := 2 }", - "{ let x, y x := 1 y := 2 }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_partial_assignments) -{ - CHECK( - "{ let x, y x := 1 }", - "{ let x, y x := 1 }" - ); -} - -BOOST_AUTO_TEST_CASE(functions) -{ - CHECK( - "{ function f() { let a := 1 } function g() { f() } }", - "{ }" - ); -} - -BOOST_AUTO_TEST_CASE(intermediate_assignment) -{ - CHECK( - "{ let a := 1 a := 4 let b := 1 }", - "{ let a := 1 a := 4 }" - ); -} - -BOOST_AUTO_TEST_CASE(intermediate_multi_assignment){ - CHECK( - "{ let a, b function f() -> x { } a := f() b := 1 }", - "{ let a, b function f() -> x { } a := f() b := 1 }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_declare) -{ - CHECK( - "{ function f() -> x, y { } let a, b := f() }", - "{ function f() -> x, y { } let a, b := f() }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_assign) -{ - CHECK( - "{ let a let b function f() -> x, y { } a, b := f() }", - "{ let a let b function f() -> x, y { } a, b := f() }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json index 42ad33e5..0f30e8e8 100644 --- a/test/libsolidity/ASTJSON/address_payable.json +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -463,7 +463,7 @@ "documentation" : null, "id" : 36, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "f", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json index 11a634c3..dd8a5582 100644 --- a/test/libsolidity/ASTJSON/address_payable_legacy.json +++ b/test/libsolidity/ASTJSON/address_payable_legacy.json @@ -93,6 +93,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSON/constructor.json b/test/libsolidity/ASTJSON/constructor.json new file mode 100644 index 00000000..b0bc4201 --- /dev/null +++ b/test/libsolidity/ASTJSON/constructor.json @@ -0,0 +1,70 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + }, + "id" : 6, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 5, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 3, + "nodeType" : "Block", + "src" : "35:4:1", + "statements" : [] + }, + "documentation" : null, + "id" : 4, + "implemented" : true, + "kind" : "constructor", + "modifiers" : [], + "name" : "", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "25:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "35:0:1" + }, + "scope" : 5, + "src" : "14:25:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 6, + "src" : "0:41:1" + } + ], + "src" : "0:42:1" +} diff --git a/test/libsolidity/ASTJSON/constructor.sol b/test/libsolidity/ASTJSON/constructor.sol new file mode 100644 index 00000000..79d04eb5 --- /dev/null +++ b/test/libsolidity/ASTJSON/constructor.sol @@ -0,0 +1,4 @@ +contract C { + constructor() public { + } +} diff --git a/test/libsolidity/ASTJSON/constructor_legacy.json b/test/libsolidity/ASTJSON/constructor_legacy.json new file mode 100644 index 00000000..0617073e --- /dev/null +++ b/test/libsolidity/ASTJSON/constructor_legacy.json @@ -0,0 +1,110 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "scope" : 6 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : true, + "kind" : "constructor", + "modifiers" : + [ + null + ], + "name" : "", + "scope" : 5, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "25:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "35:0:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 3, + "name" : "Block", + "src" : "35:4:1" + } + ], + "id" : 4, + "name" : "FunctionDefinition", + "src" : "14:25:1" + } + ], + "id" : 5, + "name" : "ContractDefinition", + "src" : "0:41:1" + } + ], + "id" : 6, + "name" : "SourceUnit", + "src" : "0:42:1" +} diff --git a/test/libsolidity/ASTJSON/documentation.json b/test/libsolidity/ASTJSON/documentation.json index 403d4e72..ce1e0b57 100644 --- a/test/libsolidity/ASTJSON/documentation.json +++ b/test/libsolidity/ASTJSON/documentation.json @@ -147,7 +147,7 @@ "documentation" : "Some comment on fn.", "id" : 14, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "fn", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json index 5a890e50..0277902f 100644 --- a/test/libsolidity/ASTJSON/documentation_legacy.json +++ b/test/libsolidity/ASTJSON/documentation_legacy.json @@ -108,6 +108,7 @@ "documentation" : "Some comment on fn.", "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSON/fallback.json b/test/libsolidity/ASTJSON/fallback.json new file mode 100644 index 00000000..a9c85b2f --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback.json @@ -0,0 +1,70 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + }, + "id" : 6, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 5, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 3, + "nodeType" : "Block", + "src" : "43:5:1", + "statements" : [] + }, + "documentation" : null, + "id" : 4, + "implemented" : true, + "kind" : "fallback", + "modifiers" : [], + "name" : "", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "23:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "43:0:1" + }, + "scope" : 5, + "src" : "15:33:1", + "stateMutability" : "payable", + "superFunction" : null, + "visibility" : "external" + } + ], + "scope" : 6, + "src" : "0:50:1" + } + ], + "src" : "0:51:1" +} diff --git a/test/libsolidity/ASTJSON/fallback.sol b/test/libsolidity/ASTJSON/fallback.sol new file mode 100644 index 00000000..4e318892 --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback.sol @@ -0,0 +1,4 @@ +contract C { + function() external payable { + } +} diff --git a/test/libsolidity/ASTJSON/fallback_legacy.json b/test/libsolidity/ASTJSON/fallback_legacy.json new file mode 100644 index 00000000..0aca3128 --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_legacy.json @@ -0,0 +1,110 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "scope" : 6 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "fallback", + "modifiers" : + [ + null + ], + "name" : "", + "scope" : 5, + "stateMutability" : "payable", + "superFunction" : null, + "visibility" : "external" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "23:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "43:0:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 3, + "name" : "Block", + "src" : "43:5:1" + } + ], + "id" : 4, + "name" : "FunctionDefinition", + "src" : "15:33:1" + } + ], + "id" : 5, + "name" : "ContractDefinition", + "src" : "0:50:1" + } + ], + "id" : 6, + "name" : "SourceUnit", + "src" : "0:51:1" +} diff --git a/test/libsolidity/ASTJSON/fallback_payable.json b/test/libsolidity/ASTJSON/fallback_payable.json new file mode 100644 index 00000000..9d56f74b --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_payable.json @@ -0,0 +1,70 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + }, + "id" : 6, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 5, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 3, + "nodeType" : "Block", + "src" : "34:2:1", + "statements" : [] + }, + "documentation" : null, + "id" : 4, + "implemented" : true, + "kind" : "fallback", + "modifiers" : [], + "name" : "", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "22:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "34:0:1" + }, + "scope" : 5, + "src" : "14:22:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "external" + } + ], + "scope" : 6, + "src" : "0:38:1" + } + ], + "src" : "0:39:1" +} diff --git a/test/libsolidity/ASTJSON/fallback_payable.sol b/test/libsolidity/ASTJSON/fallback_payable.sol new file mode 100644 index 00000000..21db99ec --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_payable.sol @@ -0,0 +1,3 @@ +contract C { + function() external {} +} diff --git a/test/libsolidity/ASTJSON/fallback_payable_legacy.json b/test/libsolidity/ASTJSON/fallback_payable_legacy.json new file mode 100644 index 00000000..7320f574 --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_payable_legacy.json @@ -0,0 +1,110 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "scope" : 6 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "fallback", + "modifiers" : + [ + null + ], + "name" : "", + "scope" : 5, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "external" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "22:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "34:0:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 3, + "name" : "Block", + "src" : "34:2:1" + } + ], + "id" : 4, + "name" : "FunctionDefinition", + "src" : "14:22:1" + } + ], + "id" : 5, + "name" : "ContractDefinition", + "src" : "0:38:1" + } + ], + "id" : 6, + "name" : "SourceUnit", + "src" : "0:39:1" +} diff --git a/test/libsolidity/ASTJSON/function_type.json b/test/libsolidity/ASTJSON/function_type.json index 5dbc5b80..b78d8446 100644 --- a/test/libsolidity/ASTJSON/function_type.json +++ b/test/libsolidity/ASTJSON/function_type.json @@ -37,7 +37,7 @@ "documentation" : null, "id" : 16, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "f", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/function_type_legacy.json b/test/libsolidity/ASTJSON/function_type_legacy.json index af0c42dd..72ceec81 100644 --- a/test/libsolidity/ASTJSON/function_type_legacy.json +++ b/test/libsolidity/ASTJSON/function_type_legacy.json @@ -41,6 +41,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json index fe3e73d2..c6d40af2 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json @@ -142,7 +142,7 @@ "documentation" : null, "id" : 10, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "f", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json index d78d01ff..b5333286 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json @@ -41,6 +41,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier.json b/test/libsolidity/ASTJSON/long_type_name_identifier.json index 0579967c..505d260c 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier.json @@ -148,7 +148,7 @@ "documentation" : null, "id" : 13, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "f", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json index a96ccef3..d3bcda56 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json @@ -83,6 +83,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSON/modifier_definition.json b/test/libsolidity/ASTJSON/modifier_definition.json index 95554f03..66359453 100644 --- a/test/libsolidity/ASTJSON/modifier_definition.json +++ b/test/libsolidity/ASTJSON/modifier_definition.json @@ -97,7 +97,7 @@ "documentation" : null, "id" : 13, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [ { diff --git a/test/libsolidity/ASTJSON/modifier_definition_legacy.json b/test/libsolidity/ASTJSON/modifier_definition_legacy.json index e1e797ba..5186912c 100644 --- a/test/libsolidity/ASTJSON/modifier_definition_legacy.json +++ b/test/libsolidity/ASTJSON/modifier_definition_legacy.json @@ -105,6 +105,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "name" : "F", "scope" : 14, "stateMutability" : "nonpayable", diff --git a/test/libsolidity/ASTJSON/modifier_invocation.json b/test/libsolidity/ASTJSON/modifier_invocation.json index 95554f03..66359453 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation.json +++ b/test/libsolidity/ASTJSON/modifier_invocation.json @@ -97,7 +97,7 @@ "documentation" : null, "id" : 13, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [ { diff --git a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json index e1e797ba..5186912c 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json +++ b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json @@ -105,6 +105,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "name" : "F", "scope" : 14, "stateMutability" : "nonpayable", diff --git a/test/libsolidity/ASTJSON/non_utf8.json b/test/libsolidity/ASTJSON/non_utf8.json index 307259e9..1852bd38 100644 --- a/test/libsolidity/ASTJSON/non_utf8.json +++ b/test/libsolidity/ASTJSON/non_utf8.json @@ -89,7 +89,7 @@ "documentation" : null, "id" : 7, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "f", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/non_utf8_legacy.json b/test/libsolidity/ASTJSON/non_utf8_legacy.json index b1f847f7..df105096 100644 --- a/test/libsolidity/ASTJSON/non_utf8_legacy.json +++ b/test/libsolidity/ASTJSON/non_utf8_legacy.json @@ -41,6 +41,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSON/short_type_name.json b/test/libsolidity/ASTJSON/short_type_name.json index 502c1e31..acb46157 100644 --- a/test/libsolidity/ASTJSON/short_type_name.json +++ b/test/libsolidity/ASTJSON/short_type_name.json @@ -93,7 +93,7 @@ "documentation" : null, "id" : 9, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "f", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/short_type_name_legacy.json b/test/libsolidity/ASTJSON/short_type_name_legacy.json index 761bcd3b..1f9b1968 100644 --- a/test/libsolidity/ASTJSON/short_type_name_legacy.json +++ b/test/libsolidity/ASTJSON/short_type_name_legacy.json @@ -41,6 +41,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.json b/test/libsolidity/ASTJSON/short_type_name_ref.json index b0c3ad97..b6b7bca5 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref.json @@ -105,7 +105,7 @@ "documentation" : null, "id" : 10, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "f", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json index d426a384..420b0f60 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json @@ -41,6 +41,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSON/source_location.json b/test/libsolidity/ASTJSON/source_location.json index 8d8acb0f..f0ed216d 100644 --- a/test/libsolidity/ASTJSON/source_location.json +++ b/test/libsolidity/ASTJSON/source_location.json @@ -127,7 +127,7 @@ "documentation" : null, "id" : 10, "implemented" : true, - "isConstructor" : false, + "kind" : "function", "modifiers" : [], "name" : "f", "nodeType" : "FunctionDefinition", diff --git a/test/libsolidity/ASTJSON/source_location_legacy.json b/test/libsolidity/ASTJSON/source_location_legacy.json index 327cd6da..a65979d6 100644 --- a/test/libsolidity/ASTJSON/source_location_legacy.json +++ b/test/libsolidity/ASTJSON/source_location_legacy.json @@ -41,6 +41,7 @@ "documentation" : null, "implemented" : true, "isConstructor" : false, + "kind" : "function", "modifiers" : [ null diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index 05839c1f..be482d99 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -22,7 +22,6 @@ #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/predicate.hpp> #include <boost/throw_exception.hpp> -#include <cctype> #include <fstream> #include <memory> #include <stdexcept> diff --git a/test/libsolidity/LibSolc.cpp b/test/libsolidity/LibSolc.cpp index 61e5ebba..94fed7e8 100644 --- a/test/libsolidity/LibSolc.cpp +++ b/test/libsolidity/LibSolc.cpp @@ -52,7 +52,7 @@ Json::Value compileMulti(string const& _input, bool _callback) { string output( _callback ? - compileJSONCallback(_input.c_str(), dev::test::Options::get().optimize, NULL) : + compileJSONCallback(_input.c_str(), dev::test::Options::get().optimize, nullptr) : compileJSONMulti(_input.c_str(), dev::test::Options::get().optimize) ); Json::Value ret; @@ -62,7 +62,7 @@ Json::Value compileMulti(string const& _input, bool _callback) Json::Value compile(string const& _input) { - string output(compileStandard(_input.c_str(), NULL)); + string output(compileStandard(_input.c_str(), nullptr)); Json::Value ret; BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f65c8b27..7a496e64 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4519,6 +4519,31 @@ BOOST_AUTO_TEST_CASE(library_call_protection) ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); } + +BOOST_AUTO_TEST_CASE(library_staticcall_delegatecall) +{ + char const* sourceCode = R"( + library Lib { + function x() public view returns (uint) { + return 1; + } + } + contract Test { + uint t; + function f() public returns (uint) { + t = 2; + return this.g(); + } + function g() public view returns (uint) { + return Lib.x(); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); +} + BOOST_AUTO_TEST_CASE(store_bytes) { // this test just checks that the copy loop does not mess up the stack diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 93db236b..6965d886 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -471,6 +471,8 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } +// HEX STRING LITERAL + BOOST_AUTO_TEST_CASE(valid_hex_literal) { Scanner scanner(CharStream("{ hex\"00112233FF\"")); @@ -483,30 +485,32 @@ BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) { Scanner scanner(CharStream("{ hex\"00112233F\"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) { Scanner scanner(CharStream("{ hex\"00112233FF \"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) { Scanner scanner(CharStream("{ hex\"00112233FF'")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) { Scanner scanner(CharStream("{ hex\"hello\"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } +// COMMENTS + BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) { // This used to parse as "comment", "identifier" diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 1de42300..91d1681f 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -20,7 +20,6 @@ #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/predicate.hpp> #include <boost/throw_exception.hpp> -#include <cctype> #include <fstream> #include <memory> #include <stdexcept> diff --git a/test/libsolidity/TestCase.cpp b/test/libsolidity/TestCase.cpp index 6c4e0aea..17972269 100644 --- a/test/libsolidity/TestCase.cpp +++ b/test/libsolidity/TestCase.cpp @@ -29,7 +29,8 @@ using namespace std; bool TestCase::isTestFilename(boost::filesystem::path const& _filename) { - return _filename.extension().string() == ".sol" && + string extension = _filename.extension().string(); + return (extension == ".sol" || extension == ".yul") && !boost::starts_with(_filename.string(), "~") && !boost::starts_with(_filename.string(), "."); } diff --git a/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol b/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol new file mode 100644 index 00000000..1742c80d --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol @@ -0,0 +1,5 @@ +contract C { + bytes32[8**90] ids; +} +// ---- +// TypeError: (25-30): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol new file mode 100644 index 00000000..1344574c --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol @@ -0,0 +1,5 @@ +contract C { + bytes32[8**90][500] ids; +} +// ---- +// TypeError: (25-30): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol new file mode 100644 index 00000000..901bc28a --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol @@ -0,0 +1,5 @@ +contract C { + uint[8**90][500] ids; +} +// ---- +// TypeError: (22-27): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol b/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol new file mode 100644 index 00000000..df9f8223 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + bytes[32] memory a; + a[8**90][8**90][8**90*0.1]; + } +} +// ---- +// TypeError: (67-72): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError: (74-79): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError: (81-90): Type rational_const 9485...(73 digits omitted)...5712 / 5 is not implicitly convertible to expected type uint256. +// TypeError: (65-91): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol b/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol new file mode 100644 index 00000000..9c98ad45 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + bytes[32] memory a; + a[8**90][8**90][1 - 8**90]; + } +} +// ---- +// TypeError: (67-72): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError: (74-79): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError: (81-90): Type int_const -189...(75 digits omitted)...1423 is not implicitly convertible to expected type uint256. +// TypeError: (65-91): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/array_negative_index.sol b/test/libsolidity/syntaxTests/indexing/array_negative_index.sol new file mode 100644 index 00000000..019d023b --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_negative_index.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + bytes[32] memory a; + a[-1]; + } +} +// ---- +// TypeError: (67-69): Type int_const -1 is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol b/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol new file mode 100644 index 00000000..7c0ac9fe --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + bytes[32] memory a; + a[888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888]; + } +} +// ---- +// TypeError: (67-178): Type int_const 8888...(103 digits omitted)...8888 is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol b/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol new file mode 100644 index 00000000..12399317 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol @@ -0,0 +1,9 @@ +contract C { + function f() public { + bytes32 b; + b[-1]; + } +} +// ---- +// TypeError: (58-60): Type int_const -1 is not implicitly convertible to expected type uint256. +// TypeError: (56-61): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol b/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol new file mode 100644 index 00000000..adf7db61 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol @@ -0,0 +1,9 @@ +contract C { + function f() public { + bytes32 b; + b[888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888]; + } +} +// ---- +// TypeError: (58-169): Type int_const 8888...(103 digits omitted)...8888 is not implicitly convertible to expected type uint256. +// TypeError: (56-170): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/575_member_member_getter_call_without_parentheses.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/575_member_member_getter_call_without_parentheses.sol new file mode 100644 index 00000000..61f43103 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/575_member_member_getter_call_without_parentheses.sol @@ -0,0 +1,19 @@ +contract A{ + function f() public pure{ + + } +} +contract B{ + A public a; +} +contract C{ + B public b; +} +contract D{ + C c; + function f() public view{ + c.b.a.f(); + } +} +// ---- +// TypeError: (170-175): Member "a" not found or not visible after argument-dependent lookup in function () view external returns (contract B). Did you intend to call the function? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/576_member_getter_call_without_parentheses.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/576_member_getter_call_without_parentheses.sol new file mode 100644 index 00000000..bdc5fdc7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/576_member_getter_call_without_parentheses.sol @@ -0,0 +1,17 @@ +contract A{ + function f() public pure{ + + } +} +contract B{ + A public a; +} +contract C{ + B b; + function f() public view{ + b.a.f(); + } +} + +// ---- +// TypeError: (140-145): Member "f" not found or not visible after argument-dependent lookup in function () view external returns (contract A). Did you intend to call the function? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/577_member_getter_call_without_parentheses_missing_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/577_member_getter_call_without_parentheses_missing_function.sol new file mode 100644 index 00000000..d204d926 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/577_member_getter_call_without_parentheses_missing_function.sol @@ -0,0 +1,15 @@ +contract A{ + +} +contract B{ + A public a; +} +contract C{ + B b; + function f() public view{ + b.a.f(); + } +} + +// ---- +// TypeError: (104-109): Member "f" not found or not visible after argument-dependent lookup in function () view external returns (contract A). Did you intend to call the function? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/578_private_member_getter_call_without_parentheses.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/578_private_member_getter_call_without_parentheses.sol new file mode 100644 index 00000000..e71da372 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/578_private_member_getter_call_without_parentheses.sol @@ -0,0 +1,17 @@ +contract A{ + function f() public pure{ + + } +} +contract B{ + A private a; +} +contract C{ + B b; + function f() public view{ + b.a.f(); + } +} + +// ---- +// TypeError: (141-144): Member "a" not found or not visible after argument-dependent lookup in contract B. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/579_member_getter_call_without_parentheses_private_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/579_member_getter_call_without_parentheses_private_function.sol new file mode 100644 index 00000000..18932238 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/579_member_getter_call_without_parentheses_private_function.sol @@ -0,0 +1,17 @@ +contract A{ + function f() private pure{ + + } +} +contract B{ + A public a; +} +contract C{ + B b; + function f() public view{ + b.a.f(); + } +} + +// ---- +// TypeError: (141-146): Member "f" not found or not visible after argument-dependent lookup in function () view external returns (contract A). Did you intend to call the function? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/580_improve_name_suggestion_one_and_two_letters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/580_improve_name_suggestion_one_and_two_letters.sol new file mode 100644 index 00000000..8d2d071b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/580_improve_name_suggestion_one_and_two_letters.sol @@ -0,0 +1,14 @@ +contract c { + function f () public + { + a = ac; + a = cd; + a = b; + } + uint256 a; + uint256 ab; +} +// ---- +// DeclarationError: (56-58): Undeclared identifier. Did you mean "ab"? +// DeclarationError: (72-74): Undeclared identifier. +// DeclarationError: (88-89): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/581_improve_name_suggestion_three_letters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/581_improve_name_suggestion_three_letters.sol new file mode 100644 index 00000000..69f5a7e2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/581_improve_name_suggestion_three_letters.sol @@ -0,0 +1,12 @@ +contract c { + function f () public + { + a = abd; + a = ade; + } + uint256 a; + uint256 abc; +} +// ---- +// DeclarationError: (56-59): Undeclared identifier. Did you mean "abc" or "abi"? +// DeclarationError: (73-76): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol new file mode 100644 index 00000000..547ea308 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol @@ -0,0 +1,17 @@ +contract c { + function f () public + { + a = land; + a = lost; + a = lang; + } + uint256 long; + uint256 abc; +} +// ---- +// DeclarationError: (52-53): Undeclared identifier. +// DeclarationError: (56-60): Undeclared identifier. Did you mean "long"? +// DeclarationError: (70-71): Undeclared identifier. +// DeclarationError: (74-78): Undeclared identifier. Did you mean "long", "log0", "log1", "log2", "log3" or "log4"? +// DeclarationError: (88-89): Undeclared identifier. +// DeclarationError: (92-96): Undeclared identifier. Did you mean "long"? diff --git a/test/libsolidity/syntaxTests/types/function_call_fail.sol b/test/libsolidity/syntaxTests/types/function_call_fail.sol new file mode 100644 index 00000000..ef52ab44 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_call_fail.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint y) public pure { + (4(y)) = 2; + } +} +// ---- +// TypeError: (59-63): Type is not callable +// TypeError: (59-63): Expression has to be an lvalue. +// TypeError: (67-68): Type int_const 2 is not implicitly convertible to expected type tuple(). diff --git a/test/libsolidity/syntaxTests/types/function_call_fail2.sol b/test/libsolidity/syntaxTests/types/function_call_fail2.sol new file mode 100644 index 00000000..389ffce9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_call_fail2.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint y) public pure returns (uint) { + (f(y)) = 2; + } +} +// ---- +// TypeError: (74-78): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/types/rational_number_huge.sol b/test/libsolidity/syntaxTests/types/rational_number_huge.sol new file mode 100644 index 00000000..378de201 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_huge.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint y) public pure { + // fits FixedBytes with exactly 32-bytes + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000; // FixedBytes (32) + + // fits exactly into FixedBytes (32), ensures underscored literals won't hurt + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff_00000000; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol b/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol new file mode 100644 index 00000000..08e50656 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint y) public pure { + // one byte too long for storing in Fixedbytes (would require 33 bytes) + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff000000001; + } +} +// ---- +// TypeError: (142-209): Type int_const 1852...(71 digits omitted)...7281 is not implicitly convertible to expected type uint256. diff --git a/test/libjulia/Common.cpp b/test/libyul/Common.cpp index a0592667..4c50180a 100644 --- a/test/libjulia/Common.cpp +++ b/test/libyul/Common.cpp @@ -19,11 +19,11 @@ * Common functions the Yul tests. */ -#include <test/libjulia/Common.h> +#include <test/libyul/Common.h> #include <test/Options.h> -#include <libjulia/optimiser/Disambiguator.h> +#include <libyul/optimiser/Disambiguator.h> #include <libsolidity/parsing/Scanner.h> @@ -37,10 +37,10 @@ #include <boost/test/unit_test.hpp> using namespace std; -using namespace dev::julia; +using namespace dev::yul; using namespace dev::solidity; -void dev::julia::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) +void dev::yul::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) { SourceReferenceFormatter formatter(cout, [&](std::string const&) -> Scanner const& { return _scanner; }); @@ -52,7 +52,7 @@ void dev::julia::test::printErrors(ErrorList const& _errors, Scanner const& _sca } -pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test::parse(string const& _source, bool _yul) +pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::yul::test::parse(string const& _source, bool _yul) { auto flavour = _yul ? assembly::AsmFlavour::Yul : assembly::AsmFlavour::Strict; ErrorList errors; @@ -83,13 +83,13 @@ pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test: return {}; } -assembly::Block dev::julia::test::disambiguate(string const& _source, bool _yul) +assembly::Block dev::yul::test::disambiguate(string const& _source, bool _yul) { auto result = parse(_source, _yul); return boost::get<Block>(Disambiguator(*result.second)(*result.first)); } -string dev::julia::test::format(string const& _source, bool _yul) +string dev::yul::test::format(string const& _source, bool _yul) { return assembly::AsmPrinter(_yul)(*parse(_source, _yul).first); } diff --git a/test/libjulia/Common.h b/test/libyul/Common.h index b9c3d2fb..ee191494 100644 --- a/test/libjulia/Common.h +++ b/test/libyul/Common.h @@ -39,7 +39,7 @@ namespace assembly struct AsmAnalysisInfo; } } -namespace julia +namespace yul { namespace test { diff --git a/test/libyul/Inliner.cpp b/test/libyul/Inliner.cpp new file mode 100644 index 00000000..44c6411a --- /dev/null +++ b/test/libyul/Inliner.cpp @@ -0,0 +1,111 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Unit tests for the Yul function inliner. + */ + +#include <test/libyul/Common.h> + +#include <libyul/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/InlinableExpressionFunctionFinder.h> +#include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/FunctionGrouper.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <boost/test/unit_test.hpp> + +#include <boost/range/adaptors.hpp> +#include <boost/algorithm/string/join.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::yul::test; +using namespace dev::solidity; + +namespace +{ +string inlinableFunctions(string const& _source) +{ + auto ast = disambiguate(_source); + + InlinableExpressionFunctionFinder funFinder; + funFinder(ast); + + return boost::algorithm::join( + funFinder.inlinableFunctions() | boost::adaptors::map_keys, + "," + ); +} + +} + + +BOOST_AUTO_TEST_SUITE(YulInlinableFunctionFilter) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + BOOST_CHECK_EQUAL(inlinableFunctions("{ }"), ""); +} + +BOOST_AUTO_TEST_CASE(simple) +{ + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := 2:u256 } }"), "f"); + BOOST_CHECK_EQUAL(inlinableFunctions("{" + "function g(a:u256) -> b:u256 { b := a }" + "function f() -> x:u256 { x := g(2:u256) }" + "}"), "f,g"); +} + +BOOST_AUTO_TEST_CASE(simple_inside_structures) +{ + BOOST_CHECK_EQUAL(inlinableFunctions("{" + "switch 2:u256 " + "case 2:u256 {" + "function g(a:u256) -> b:u256 { b := a }" + "function f() -> x:u256 { x := g(2:u256) }" + "}" + "}"), "f,g"); + BOOST_CHECK_EQUAL(inlinableFunctions("{" + "for {" + "function g(a:u256) -> b:u256 { b := a }" + "} 1:u256 {" + "function f() -> x:u256 { x := g(2:u256) }" + "}" + "{" + "function h() -> y:u256 { y := 2:u256 }" + "}" + "}"), "f,g,h"); +} + +BOOST_AUTO_TEST_CASE(negative) +{ + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := 2:u256 {} } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := f() } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := x } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256, y:u256 { x := 2:u256 } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions( + "{ function g() -> x:u256, y:u256 {} function f(y:u256) -> x:u256 { x,y := g() } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f(y:u256) -> x:u256 { y := 2:u256 } }"), ""); +} + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Parser.cpp b/test/libyul/Parser.cpp index 3f329d28..3f329d28 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libyul/Parser.cpp diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp new file mode 100644 index 00000000..8b37830f --- /dev/null +++ b/test/libyul/YulOptimizerTest.cpp @@ -0,0 +1,239 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <test/libyul/YulOptimizerTest.h> + +#include <test/libsolidity/FormattedScope.h> + +#include <test/Options.h> + +#include <libyul/optimiser/Disambiguator.h> +#include <libyul/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/ExpressionSplitter.h> +#include <libyul/optimiser/FunctionGrouper.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/MainFunction.h> +#include <libyul/optimiser/Rematerialiser.h> +#include <libyul/optimiser/ExpressionSimplifier.h> +#include <libyul/optimiser/UnusedPruner.h> +#include <libyul/optimiser/ExpressionJoiner.h> + +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <boost/test/unit_test.hpp> +#include <boost/algorithm/string.hpp> + +#include <fstream> + +using namespace dev; +using namespace dev::yul; +using namespace dev::yul::test; +using namespace dev::solidity; +using namespace dev::solidity::test; +using namespace std; + +YulOptimizerTest::YulOptimizerTest(string const& _filename) +{ + boost::filesystem::path path(_filename); + + if (path.empty() || std::next(path.begin()) == path.end() || std::next(std::next(path.begin())) == path.end()) + BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); + m_optimizerStep = std::prev(std::prev(path.end()))->string(); + + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\".")); + file.exceptions(ios::badbit); + + string line; + while (getline(file, line)) + { + if (boost::algorithm::starts_with(line, "// ----")) + break; + if (m_source.empty() && boost::algorithm::starts_with(line, "// yul")) + m_yul = true; + m_source += line + "\n"; + } + while (getline(file, line)) + if (boost::algorithm::starts_with(line, "// ")) + m_expectation += line.substr(3) + "\n"; + else + m_expectation += line + "\n"; +} + +bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + assembly::AsmPrinter printer{m_yul}; + shared_ptr<Block> ast; + shared_ptr<assembly::AsmAnalysisInfo> analysisInfo; + if (!parse(_stream, _linePrefix, _formatted)) + return false; + + if (m_optimizerStep == "disambiguator") + disambiguate(); + else if (m_optimizerStep == "commonSubexpressionEliminator") + { + disambiguate(); + (CommonSubexpressionEliminator{})(*m_ast); + } + else if (m_optimizerStep == "expressionSplitter") + { + NameDispenser nameDispenser; + nameDispenser.m_usedNames = NameCollector(*m_ast).names(); + ExpressionSplitter{nameDispenser}(*m_ast); + } + else if (m_optimizerStep == "functionGrouper") + { + disambiguate(); + (FunctionGrouper{})(*m_ast); + } + else if (m_optimizerStep == "functionHoister") + { + disambiguate(); + (FunctionHoister{})(*m_ast); + } + else if (m_optimizerStep == "expressionInliner") + { + disambiguate(); + ExpressionInliner(*m_ast).run(); + } + else if (m_optimizerStep == "fullInliner") + { + disambiguate(); + (FunctionHoister{})(*m_ast); + (FunctionGrouper{})(*m_ast); + FullInliner(*m_ast).run(); + } + else if (m_optimizerStep == "mainFunction") + { + disambiguate(); + (FunctionGrouper{})(*m_ast); + (MainFunction{})(*m_ast); + } + else if (m_optimizerStep == "rematerialiser") + { + disambiguate(); + (Rematerialiser{})(*m_ast); + } + else if (m_optimizerStep == "expressionSimplifier") + { + disambiguate(); + (ExpressionSimplifier{})(*m_ast); + } + else if (m_optimizerStep == "unusedPruner") + { + disambiguate(); + UnusedPruner::runUntilStabilised(*m_ast); + } + else if (m_optimizerStep == "expressionJoiner") + { + disambiguate(); + ExpressionJoiner::run(*m_ast);\ + } + else + { + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl; + return false; + } + + m_obtainedResult = m_optimizerStep + "\n" + printer(*m_ast) + "\n"; + + if (m_expectation != m_obtainedResult) + { + string nextIndentLevel = _linePrefix + " "; + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Expected result:" << endl; + // TODO could compute a simple diff with highlighted lines + printIndented(_stream, m_expectation, nextIndentLevel); + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Obtained result:" << endl; + printIndented(_stream, m_obtainedResult, nextIndentLevel); + return false; + } + return true; +} + +void YulOptimizerTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const +{ + printIndented(_stream, m_source, _linePrefix); +} + +void YulOptimizerTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const +{ + printIndented(_stream, m_obtainedResult, _linePrefix); +} + +void YulOptimizerTest::printIndented(ostream& _stream, string const& _output, string const& _linePrefix) const +{ + stringstream output(_output); + string line; + while (getline(output, line)) + _stream << _linePrefix << line << endl; +} + +bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + assembly::AsmFlavour flavour = m_yul ? assembly::AsmFlavour::Yul : assembly::AsmFlavour::Strict; + ErrorList errors; + ErrorReporter errorReporter(errors); + shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(m_source), ""); + m_ast = assembly::Parser(errorReporter, flavour).parse(scanner, false); + if (!m_ast || !errorReporter.errors().empty()) + { + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + printErrors(_stream, errorReporter.errors(), *scanner); + return false; + } + m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + assembly::AsmAnalyzer analyzer( + *m_analysisInfo, + errorReporter, + dev::test::Options::get().evmVersion(), + boost::none, + flavour + ); + if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) + { + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error analyzing source." << endl; + printErrors(_stream, errorReporter.errors(), *scanner); + return false; + } + return true; +} + +void YulOptimizerTest::disambiguate() +{ + *m_ast = boost::get<Block>(Disambiguator(*m_analysisInfo)(*m_ast)); + m_analysisInfo.reset(); +} + +void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors, Scanner const& _scanner) +{ + SourceReferenceFormatter formatter(_stream, [&](string const&) -> Scanner const& { return _scanner; }); + + for (auto const& error: _errors) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); +} diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h new file mode 100644 index 00000000..7db17ceb --- /dev/null +++ b/test/libyul/YulOptimizerTest.h @@ -0,0 +1,75 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <test/libsolidity/TestCase.h> + + +namespace dev +{ +namespace solidity +{ +class Scanner; +class Error; +using ErrorList = std::vector<std::shared_ptr<Error const>>; +namespace assembly +{ +struct AsmAnalysisInfo; +struct Block; +} +} +namespace yul +{ +namespace test +{ + +class YulOptimizerTest: public solidity::test::TestCase +{ +public: + static std::unique_ptr<TestCase> create(std::string const& _filename) + { + return std::unique_ptr<TestCase>(new YulOptimizerTest(_filename)); + } + + explicit YulOptimizerTest(std::string const& _filename); + + bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; + + void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool const _formatted = false) const override; + void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; + +private: + void printIndented(std::ostream& _stream, std::string const& _output, std::string const& _linePrefix = "") const; + bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); + void disambiguate(); + + static void printErrors(std::ostream& _stream, solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); + + std::string m_source; + bool m_yul = false; + std::string m_optimizerStep; + std::string m_expectation; + + std::shared_ptr<solidity::assembly::Block> m_ast; + std::shared_ptr<solidity::assembly::AsmAnalysisInfo> m_analysisInfo; + std::string m_obtainedResult; +}; + +} +} +} diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul new file mode 100644 index 00000000..c59bced7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul @@ -0,0 +1,24 @@ +{ + let a := 1 let b := codesize() + for { } lt(1, codesize()) { mstore(1, codesize()) a := add(a, codesize()) } { + mstore(1, codesize()) + } + mstore(1, codesize()) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := 1 +// let b := codesize() +// for { +// } +// lt(1, b) +// { +// mstore(1, b) +// a := add(a, b) +// } +// { +// mstore(1, b) +// } +// mstore(1, b) +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul new file mode 100644 index 00000000..5b8200d9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul @@ -0,0 +1,15 @@ +{ + let b := 1 + if b { b := 1 } + let c := 1 +} +// ---- +// commonSubexpressionEliminator +// { +// let b := 1 +// if b +// { +// b := b +// } +// let c := 1 +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul new file mode 100644 index 00000000..cb0ca38d --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul @@ -0,0 +1,10 @@ +{ + let a := mload(1) + let b := mload(1) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := mload(1) +// let b := mload(1) +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul new file mode 100644 index 00000000..ebc17446 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul @@ -0,0 +1,10 @@ +{ + let a := gas() + let b := gas() +} +// ---- +// commonSubexpressionEliminator +// { +// let a := gas() +// let b := gas() +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul new file mode 100644 index 00000000..b9457229 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// commonSubexpressionEliminator +// { +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul new file mode 100644 index 00000000..684272f5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul @@ -0,0 +1,10 @@ +{ + let a := mul(1, codesize()) + let b := mul(1, codesize()) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := mul(1, codesize()) +// let b := a +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul new file mode 100644 index 00000000..ab94afc2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul @@ -0,0 +1,27 @@ +{ + + let a := mload(0) + let b := add(a, 7) + let c := a + let d := c + let x := add(a, b) + // CSE has to recognize equality with x here. + let y := add(d, add(c, 7)) + // some reassignments + b := mload(a) + a := b + mstore(2, a) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := mload(0) +// let b := add(a, 7) +// let c := a +// let d := a +// let x := add(a, b) +// let y := x +// b := mload(a) +// a := b +// mstore(2, b) +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul new file mode 100644 index 00000000..0d2a38c5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul @@ -0,0 +1,28 @@ +// yul +{ + { let a:u256, b:u256 } + { + for { let a:u256 } a { a := a } { + let b:u256 := a + } + } +} +// ---- +// disambiguator +// { +// { +// let a:u256, b:u256 +// } +// { +// for { +// let a_1:u256 +// } +// a_1 +// { +// a_1 := a_1 +// } +// { +// let b_1:u256 := a_1 +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul new file mode 100644 index 00000000..f917bb68 --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul @@ -0,0 +1,22 @@ +// yul +{ + { let a:u256, b:u256, c:u256, d:u256, f:u256 } + { + function f(a:u256) -> c:u256, d:u256 { + let b:u256, c_1:u256 := f(a) + } + } +} +// ---- +// disambiguator +// { +// { +// let a:u256, b:u256, c:u256, d:u256, f:u256 +// } +// { +// function f_1(a_1:u256) -> c_1:u256, d_1:u256 +// { +// let b_1:u256, c_1_1:u256 := f_1(a_1) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul new file mode 100644 index 00000000..14f53757 --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul @@ -0,0 +1,22 @@ +// yul +{ + { let a:u256, b:u256, c:u256 } + { + let a:bool + if a { let b:bool := a } + } +} +// ---- +// disambiguator +// { +// { +// let a:u256, b:u256, c:u256 +// } +// { +// let a_1:bool +// if a_1 +// { +// let b_1:bool := a_1 +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/smoke.yul b/test/libyul/yulOptimizerTests/disambiguator/smoke.yul new file mode 100644 index 00000000..d6cd8a61 --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// disambiguator +// { +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul b/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul new file mode 100644 index 00000000..e55f4cd3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul @@ -0,0 +1,6 @@ +// yul +{ } +// ---- +// disambiguator +// { +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul new file mode 100644 index 00000000..340ecccf --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul @@ -0,0 +1,27 @@ +// yul +{ + { let a:u256, b:u256, c:u256 } + { + let a:u256 + switch a + case 0:u256 { let b:u256 := a } + default { let c:u256 := a } + } +} +// ---- +// disambiguator +// { +// { +// let a:u256, b:u256, c:u256 +// } +// { +// let a_1:u256 +// switch a_1 +// case 0:u256 { +// let b_1:u256 := a_1 +// } +// default { +// let c_1:u256 := a_1 +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables.yul b/test/libyul/yulOptimizerTests/disambiguator/variables.yul new file mode 100644 index 00000000..65bd4c8f --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/variables.yul @@ -0,0 +1,12 @@ +// yul +{ { let a:u256 } { let a:u256 } } +// ---- +// disambiguator +// { +// { +// let a:u256 +// } +// { +// let a_1:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul new file mode 100644 index 00000000..e462442a --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul @@ -0,0 +1,13 @@ +// yul +{ { let a:u256 let a_1:u256 } { let a:u256 } } +// ---- +// disambiguator +// { +// { +// let a:u256 +// let a_1:u256 +// } +// { +// let a_2:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul new file mode 100644 index 00000000..e80959f6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul @@ -0,0 +1,24 @@ +// yul +{ + { let c:u256 let b:u256 } + function f(a:u256, c:u256) -> b:u256 { let x:u256 } + { + let a:u256 let x:u256 + } +} +// ---- +// disambiguator +// { +// { +// let c:u256 +// let b:u256 +// } +// function f(a:u256, c_1:u256) -> b_1:u256 +// { +// let x:u256 +// } +// { +// let a_1:u256 +// let x_1:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul b/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul new file mode 100644 index 00000000..519a2af8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul @@ -0,0 +1,13 @@ +{ + function f(a) -> x { x := add(a, a) } + let y := f(calldatasize()) +} +// ---- +// expressionInliner +// { +// function f(a) -> x +// { +// x := add(a, a) +// } +// let y := add(calldatasize(), calldatasize()) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul b/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul new file mode 100644 index 00000000..e1da8e07 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul @@ -0,0 +1,18 @@ +{ + function f(a) -> x { x := add(a, a) } + function g(b, c) -> y { y := mul(mload(c), f(b)) } + let y := g(calldatasize(), 7) +} +// ---- +// expressionInliner +// { +// function f(a) -> x +// { +// x := add(a, a) +// } +// function g(b, c) -> y +// { +// y := mul(mload(c), add(b, b)) +// } +// let y_1 := mul(mload(7), add(calldatasize(), calldatasize())) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul b/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul new file mode 100644 index 00000000..082cb53f --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul @@ -0,0 +1,18 @@ +{ + function f(a, r) -> x { x := g(a, g(r, r)) } + function g(b, s) -> y { y := f(b, f(s, s)) } + let y := g(calldatasize(), 7) +} +// ---- +// expressionInliner +// { +// function f(a, r) -> x +// { +// x := g(a, f(r, f(r, r))) +// } +// function g(b, s) -> y +// { +// y := f(b, g(s, f(s, f(s, s)))) +// } +// let y_1 := f(calldatasize(), g(7, f(7, f(7, 7)))) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul b/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul new file mode 100644 index 00000000..0fb43a9d --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul @@ -0,0 +1,14 @@ +// Does not inline because mload could be moved out of sequence +{ + function f(a) -> x { x := a } + let y := f(mload(2)) +} +// ---- +// expressionInliner +// { +// function f(a) -> x +// { +// x := a +// } +// let y := f(mload(2)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul b/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul new file mode 100644 index 00000000..7fdad6c4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul @@ -0,0 +1,27 @@ +// The calls to g and h cannot be moved because g and h are not movable. Therefore, the call +// to f is not inlined. +{ + function f(a, b) -> x { x := add(b, a) } + function g() -> y { y := mload(0) mstore(0, 4) } + function h() -> z { mstore(0, 4) z := mload(0) } + let r := f(g(), h()) +} +// ---- +// expressionInliner +// { +// function f(a, b) -> x +// { +// x := add(b, a) +// } +// function g() -> y +// { +// y := mload(0) +// mstore(0, 4) +// } +// function h() -> z +// { +// mstore(0, 4) +// z := mload(0) +// } +// let r := f(g(), h()) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/simple.yul b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul new file mode 100644 index 00000000..c186eafd --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul @@ -0,0 +1,14 @@ +// yul +{ + function f() -> x:u256 { x := 2:u256 } + let y:u256 := f() +} +// ---- +// expressionInliner +// { +// function f() -> x:u256 +// { +// x := 2:u256 +// } +// let y:u256 := 2:u256 +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul new file mode 100644 index 00000000..b5f4d515 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul @@ -0,0 +1,14 @@ +// yul +{ + function f(a:u256) -> x:u256 { x := a } + let y:u256 := f(7:u256) +} +// ---- +// expressionInliner +// { +// function f(a:u256) -> x:u256 +// { +// x := a +// } +// let y:u256 := 7:u256 +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul b/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul new file mode 100644 index 00000000..a1349511 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul @@ -0,0 +1,21 @@ +{ + let a := mload(3) + let b := sload(a) + let c := mload(7) + let d := add(c, b) + if d { + let x := mload(3) + let y := add(x, 3) + } + let z := 3 + let t := add(z, 9) +} +// ---- +// expressionJoiner +// { +// if add(mload(7), sload(mload(3))) +// { +// let y := add(mload(3), 3) +// } +// let t := add(3, 9) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul b/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul new file mode 100644 index 00000000..0d407c7c --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul @@ -0,0 +1,13 @@ +{ + let a := mload(3) + let b := mload(6) + let x := mul(add(b, a), mload(2)) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// let a := mload(3) +// let b := mload(6) +// sstore(mul(add(b, a), mload(2)), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul new file mode 100644 index 00000000..fd53ca51 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul @@ -0,0 +1,11 @@ +{ + let a := mload(2) + let b := mload(6) + let x := mul(add(b, a), 2) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// sstore(mul(add(mload(6), mload(2)), 2), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul new file mode 100644 index 00000000..078a12a5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul @@ -0,0 +1,11 @@ +{ + // This is not joined because a is referenced multiple times + let a := mload(2) + let b := add(a, a) +} +// ---- +// expressionJoiner +// { +// let a := mload(2) +// let b := add(a, a) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul new file mode 100644 index 00000000..965e07e9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul @@ -0,0 +1,15 @@ +{ + // We have an interleaved "add" here, so we cannot inline "a" + // (note that this component does not analyze whether + // functions are pure or not) + let a := mload(2) + let b := mload(6) + let x := mul(a, add(2, b)) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// let a := mload(2) +// sstore(mul(a, add(2, mload(6))), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul new file mode 100644 index 00000000..c577e182 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul @@ -0,0 +1,12 @@ +{ + let a := mload(2) + let b := mload(6) + let x := mul(add(a, b), 2) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// let a := mload(2) +// sstore(mul(add(a, mload(6)), 2), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul new file mode 100644 index 00000000..a781bb2a --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul @@ -0,0 +1,19 @@ +{ + // The component will remove the empty block after + // it has handled the outer block. + // The idea behind this test is that the component + // does not perform replacements across blocks because + // they usually have contents, but adding contents + // will reduce the scope of the test. + let a := mload(2) + let x := calldataload(a) + { + } + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// let x := calldataload(mload(2)) +// sstore(x, 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul new file mode 100644 index 00000000..75218a5c --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul @@ -0,0 +1,15 @@ +{ + for { let b := mload(1) } b {} {} +} +// ---- +// expressionJoiner +// { +// for { +// let b := mload(1) +// } +// b +// { +// } +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul new file mode 100644 index 00000000..d5f7d8fa --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul @@ -0,0 +1,16 @@ +{ + let a := mload(0) + for { } a {} {} +} +// ---- +// expressionJoiner +// { +// let a := mload(0) +// for { +// } +// a +// { +// } +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul b/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul new file mode 100644 index 00000000..c7411211 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul @@ -0,0 +1,16 @@ +{ + // This is not joined because a is referenced multiple times + function f(a) -> x { + a := mload(2) + x := add(a, 3) + } +} +// ---- +// expressionJoiner +// { +// function f(a) -> x +// { +// a := mload(2) +// x := add(a, 3) +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul b/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul new file mode 100644 index 00000000..1e502353 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul @@ -0,0 +1,13 @@ +{ + // This is not joined because a is referenced multiple times + let a := mload(2) + let b := mload(a) + a := 4 +} +// ---- +// expressionJoiner +// { +// let a := mload(2) +// let b := mload(a) +// a := 4 +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul b/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul new file mode 100644 index 00000000..b03bcf45 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul @@ -0,0 +1,10 @@ +{ + let a := mload(2) + let x := calldataload(a) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// sstore(calldataload(mload(2)), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul b/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul new file mode 100644 index 00000000..3b433f78 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul @@ -0,0 +1,13 @@ +{ + let a := mload(3) + let b := sload(a) + let c := mload(7) + let d := add(b, c) + sstore(d, 0) +} +// ---- +// expressionJoiner +// { +// let b := sload(mload(3)) +// sstore(add(b, mload(7)), 0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul b/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul new file mode 100644 index 00000000..c0e2c6f2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// expressionJoiner +// { +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul b/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul new file mode 100644 index 00000000..0e4e540e --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul @@ -0,0 +1,28 @@ +{ + let a := mload(3) + let b := sload(a) + let c := mload(7) + let d := add(c, b) + switch d + case 3 { + let x := mload(3) + let y := add(x, 3) + } + default { + sstore(1, 0) + } + let z := 3 + let t := add(z, 9) +} +// ---- +// expressionJoiner +// { +// switch add(mload(7), sload(mload(3))) +// case 3 { +// let y := add(mload(3), 3) +// } +// default { +// sstore(1, 0) +// } +// let t := add(3, 9) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul b/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul new file mode 100644 index 00000000..7b722be1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul @@ -0,0 +1,12 @@ +{ + let a := mload(2) + let b := mload(6) + let c := mload(7) + let x := mul(add(c, b), a) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// sstore(mul(add(mload(7), mload(6)), mload(2)), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul new file mode 100644 index 00000000..0b55adc5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul @@ -0,0 +1,6 @@ +{ let a := add(7, sub(mload(0), 7)) } +// ---- +// expressionSimplifier +// { +// let a := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul new file mode 100644 index 00000000..bd1a5a53 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul @@ -0,0 +1,6 @@ +{ let a := add(1, mul(3, 4)) } +// ---- +// expressionSimplifier +// { +// let a := 13 +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul new file mode 100644 index 00000000..f6190622 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul @@ -0,0 +1,6 @@ +{ let a := sub(calldataload(0), calldataload(0)) } +// ---- +// expressionSimplifier +// { +// let a := 0 +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul new file mode 100644 index 00000000..e91403cd --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul @@ -0,0 +1,6 @@ +{ let a := sub(calldataload(1), calldataload(0)) } +// ---- +// expressionSimplifier +// { +// let a := sub(calldataload(1), calldataload(0)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul new file mode 100644 index 00000000..d35686cd --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul @@ -0,0 +1,10 @@ +{ + let a := mload(0) + let b := sub(a, a) +} +// ---- +// expressionSimplifier +// { +// let a := mload(0) +// let b := 0 +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul new file mode 100644 index 00000000..c2ca504a --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul @@ -0,0 +1,12 @@ +{ + function f() -> a {} + let b := add(7, sub(f(), 7)) +} +// ---- +// expressionSimplifier +// { +// function f() -> a +// { +// } +// let b := f() +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul new file mode 100644 index 00000000..42c37826 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul @@ -0,0 +1,16 @@ +{ + for { let a := 10 } iszero(eq(a, 0)) { a := add(a, 1) } {} +} +// ---- +// expressionSimplifier +// { +// for { +// let a := 10 +// } +// iszero(iszero(a)) +// { +// a := add(a, 1) +// } +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul new file mode 100644 index 00000000..e6d84552 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul @@ -0,0 +1,10 @@ +{ + let a := mload(sub(7, 7)) + let b := sub(a, 0) +} +// ---- +// expressionSimplifier +// { +// let a := mload(0) +// let b := a +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul new file mode 100644 index 00000000..88714ce0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul @@ -0,0 +1,8 @@ +{ + mstore(0, mod(calldataload(0), exp(2, 8))) +} +// ---- +// expressionSimplifier +// { +// mstore(0, and(calldataload(0), 255)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul new file mode 100644 index 00000000..4d52abe8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul @@ -0,0 +1,8 @@ +{ + mstore(0, mod(calldataload(0), exp(2, 255))) +} +// ---- +// expressionSimplifier +// { +// mstore(0, and(calldataload(0), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul new file mode 100644 index 00000000..53270b72 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul @@ -0,0 +1,12 @@ +{ + function f(a) -> b { } + let c := sub(f(0), f(1)) +} +// ---- +// expressionSimplifier +// { +// function f(a) -> b +// { +// } +// let c := sub(f(0), f(1)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul new file mode 100644 index 00000000..6ab65d29 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul @@ -0,0 +1,16 @@ +{ + function f1() -> a { } + function f2() -> b { } + let c := sub(f1(), f2()) +} +// ---- +// expressionSimplifier +// { +// function f1() -> a +// { +// } +// function f2() -> b +// { +// } +// let c := sub(f1(), f2()) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul new file mode 100644 index 00000000..ab1bd128 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul @@ -0,0 +1,13 @@ +// Even if the functions pass the equality check, they are not movable. +{ + function f() -> a { } + let b := sub(f(), f()) +} +// ---- +// expressionSimplifier +// { +// function f() -> a +// { +// } +// let b := sub(f(), f()) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul new file mode 100644 index 00000000..fc61c3df --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul @@ -0,0 +1,10 @@ +// The first argument of div is not constant. +// keccak256 is not movable. +{ + let a := div(keccak256(0, 0), 0) +} +// ---- +// expressionSimplifier +// { +// let a := div(keccak256(0, 0), 0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul new file mode 100644 index 00000000..6353cda9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul @@ -0,0 +1,8 @@ +{ + let a := add(0, mload(0)) +} +// ---- +// expressionSimplifier +// { +// let a := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul new file mode 100644 index 00000000..88420e92 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// expressionSimplifier +// { +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul b/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul new file mode 100644 index 00000000..d021129f --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul @@ -0,0 +1,40 @@ +{ + let x := calldataload(0) + if mul(add(x, 2), 3) { + for { let a := 2 } lt(a, mload(a)) { a := add(a, mul(a, 2)) } { + let b := mul(add(a, 2), 4) + sstore(b, mul(b, 2)) + } + } +} +// ---- +// expressionSplitter +// { +// let _1 := 0 +// let x := calldataload(_1) +// let _2 := 3 +// let _3 := 2 +// let _4 := add(x, _3) +// let _5 := mul(_4, _2) +// if _5 +// { +// for { +// let a := 2 +// } +// lt(a, mload(a)) +// { +// let _6 := 2 +// let _7 := mul(a, _6) +// a := add(a, _7) +// } +// { +// let _8 := 4 +// let _9 := 2 +// let _10 := add(a, _9) +// let b := mul(_10, _8) +// let _11 := 2 +// let _12 := mul(b, _11) +// sstore(b, _12) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul b/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul new file mode 100644 index 00000000..53bbcea7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul @@ -0,0 +1,29 @@ +{ + let x := mul(f(0, mload(7)), 3) + function f(a, b) -> c { + c := mul(a, mload(add(b, c))) + } + sstore(x, f(mload(2), mload(2))) +} +// ---- +// expressionSplitter +// { +// let _1 := 3 +// let _2 := 7 +// let _3 := mload(_2) +// let _4 := 0 +// let _5 := f(_4, _3) +// let x := mul(_5, _1) +// function f(a, b) -> c +// { +// let _6 := add(b, c) +// let _7 := mload(_6) +// c := mul(a, _7) +// } +// let _8 := 2 +// let _9 := mload(_8) +// let _10 := 2 +// let _11 := mload(_10) +// let _12 := f(_11, _9) +// sstore(x, _12) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul b/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul new file mode 100644 index 00000000..f69f60b6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// expressionSplitter +// { +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul b/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul new file mode 100644 index 00000000..aee7976f --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul @@ -0,0 +1,33 @@ +{ + let x := 8 + switch add(2, calldataload(0)) + case 0 { sstore(0, mload(2)) } + default { mstore(0, mload(3)) } + x := add(mload(3), 4) +} +// ---- +// expressionSplitter +// { +// let x := 8 +// let _1 := 0 +// let _2 := calldataload(_1) +// let _3 := 2 +// let _4 := add(_3, _2) +// switch _4 +// case 0 { +// let _5 := 2 +// let _6 := mload(_5) +// let _7 := 0 +// sstore(_7, _6) +// } +// default { +// let _8 := 3 +// let _9 := mload(_8) +// let _10 := 0 +// mstore(_10, _9) +// } +// let _11 := 4 +// let _12 := 3 +// let _13 := mload(_12) +// x := add(_13, _11) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul b/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul new file mode 100644 index 00000000..bff70cd8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul @@ -0,0 +1,14 @@ +{ + mstore(add(calldataload(2), mload(3)), 8) +} +// ---- +// expressionSplitter +// { +// let _1 := 8 +// let _2 := 3 +// let _3 := mload(_2) +// let _4 := 2 +// let _5 := calldataload(_4) +// let _6 := add(_5, _3) +// mstore(_6, _1) +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul new file mode 100644 index 00000000..76b6054b --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul @@ -0,0 +1,32 @@ +// This tests that splitting the expression inside the condition works properly. +{ + if gt(f(mload(1)), mload(0)) { + sstore(0, 2) + } + function f(a) -> r { + a := mload(a) + r := add(a, calldatasize()) + } +} +// ---- +// fullInliner +// { +// { +// let _1 := mload(0) +// let f_a := mload(1) +// let f_r +// { +// f_a := mload(f_a) +// f_r := add(f_a, calldatasize()) +// } +// if gt(f_r, _1) +// { +// sstore(0, 2) +// } +// } +// function f(a) -> r +// { +// a := mload(a) +// r := add(a, calldatasize()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul new file mode 100644 index 00000000..e1def585 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul @@ -0,0 +1,28 @@ +{ + function f(a, b, c) -> x { + x := add(a, b) + x := mul(x, c) + } + let y := add(mload(1), add(f(mload(2), mload(3), mload(4)), mload(5))) +} +// ---- +// fullInliner +// { +// { +// let _1 := mload(5) +// let f_c := mload(4) +// let f_b := mload(3) +// let f_a := mload(2) +// let f_x +// { +// f_x := add(f_a, f_b) +// f_x := mul(f_x, f_c) +// } +// let y := add(mload(1), add(f_x, _1)) +// } +// function f(a, b, c) -> x +// { +// x := add(a, b) +// x := mul(x, c) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul new file mode 100644 index 00000000..94bbe5dc --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul @@ -0,0 +1,40 @@ +{ + function f(a) -> x { x := add(a, a) } + function g(b, c) -> y { y := mul(mload(c), f(b)) } + let y := g(f(3), 7) +} +// ---- +// fullInliner +// { +// { +// let g_c := 7 +// let f_a_1 := 3 +// let f_x_1 +// { +// f_x_1 := add(f_a_1, f_a_1) +// } +// let g_y +// { +// let g_f_a := f_x_1 +// let g_f_x +// { +// g_f_x := add(g_f_a, g_f_a) +// } +// g_y := mul(mload(g_c), g_f_x) +// } +// let y_1 := g_y +// } +// function f(a) -> x +// { +// x := add(a, a) +// } +// function g(b, c) -> y +// { +// let f_a := b +// let f_x +// { +// f_x := add(f_a, f_a) +// } +// y := mul(mload(c), f_x) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul new file mode 100644 index 00000000..f3c5b0ee --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul @@ -0,0 +1,21 @@ +// The full inliner currently does not work with +// functions returning multiple values. +{ + function f(a) -> x, y { + x := mul(a, a) + y := add(a, x) + } + let a, b := f(mload(0)) +} +// ---- +// fullInliner +// { +// { +// let a_1, b := f(mload(0)) +// } +// function f(a) -> x, y +// { +// x := mul(a, a) +// y := add(a, x) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_return.yul b/test/libyul/yulOptimizerTests/fullInliner/no_return.yul new file mode 100644 index 00000000..53fe3527 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/no_return.yul @@ -0,0 +1,22 @@ +{ + function f(a) { + sstore(a, a) + } + f(mload(0)) +} +// ---- +// fullInliner +// { +// { +// let f_a := mload(0) +// { +// sstore(f_a, f_a) +// } +// { +// } +// } +// function f(a) +// { +// sstore(a, a) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul new file mode 100644 index 00000000..3883c67c --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul @@ -0,0 +1,28 @@ +// This tests that `pop(r)` is removed. +{ + function f(a) -> x { + let r := mul(a, a) + x := add(r, r) + } + pop(add(f(7), 2)) +} +// ---- +// fullInliner +// { +// { +// let _1 := 2 +// let f_a := 7 +// let f_x +// { +// let f_r := mul(f_a, f_a) +// f_x := add(f_r, f_r) +// } +// { +// } +// } +// function f(a) -> x +// { +// let r := mul(a, a) +// x := add(r, r) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/simple.yul b/test/libyul/yulOptimizerTests/fullInliner/simple.yul new file mode 100644 index 00000000..dd1a4e0a --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/simple.yul @@ -0,0 +1,26 @@ +{ + function f(a) -> x { + let r := mul(a, a) + x := add(r, r) + } + let y := add(f(sload(mload(2))), mload(7)) +} +// ---- +// fullInliner +// { +// { +// let _1 := mload(7) +// let f_a := sload(mload(2)) +// let f_x +// { +// let f_r := mul(f_a, f_a) +// f_x := add(f_r, f_r) +// } +// let y := add(f_x, _1) +// } +// function f(a) -> x +// { +// let r := mul(a, a) +// x := add(r, r) +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul new file mode 100644 index 00000000..f0d49d7b --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul @@ -0,0 +1,24 @@ +// yul +{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } } +// ---- +// functionGrouper +// { +// { +// let a:u256 +// { +// } +// } +// function f() -> x:bool +// { +// let b:u256 := 4:u256 +// { +// } +// for { +// } +// f() +// { +// } +// { +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul new file mode 100644 index 00000000..c830d5da --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul @@ -0,0 +1,24 @@ +// yul +{ + let a:u256 + function f() { let b:u256 } + let c:u256 function g() { let d:u256 } + let e:u256 +} +// ---- +// functionGrouper +// { +// { +// let a:u256 +// let c:u256 +// let e:u256 +// } +// function f() +// { +// let b:u256 +// } +// function g() +// { +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul new file mode 100644 index 00000000..4a8be86a --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul @@ -0,0 +1,27 @@ +// yul +{ + let a:u256 + function f() { + let b:u256 + function g() { + let c:u256 + } + let d:u256 + } +} +// ---- +// functionGrouper +// { +// { +// let a:u256 +// } +// function f() +// { +// let b:u256 +// function g() +// { +// let c:u256 +// } +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul new file mode 100644 index 00000000..149a44eb --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul @@ -0,0 +1,14 @@ +// yul +{ + let a:u256 function f() {} +} +// ---- +// functionGrouper +// { +// { +// let a:u256 +// } +// function f() +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul b/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul new file mode 100644 index 00000000..650a163e --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul @@ -0,0 +1,7 @@ +{ } +// ---- +// functionGrouper +// { +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul new file mode 100644 index 00000000..6ea9f59d --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul @@ -0,0 +1,26 @@ +// yul +{ + let a:u256 + { } + function f() -> x:bool { + let b:u256 := 4:u256 + { } + for {} f() {} {} + } +} +// ---- +// functionHoister +// { +// let a:u256 +// function f() -> x:bool +// { +// let b:u256 := 4:u256 +// for { +// } +// f() +// { +// } +// { +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul new file mode 100644 index 00000000..1e3bc5a1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul @@ -0,0 +1,23 @@ +// yul +{ + let a:u256 + function f() { let b:u256 } + let c:u256 + function g() { let d:u256 } + let e:u256 +} +// ---- +// functionHoister +// { +// let a:u256 +// let c:u256 +// let e:u256 +// function f() +// { +// let b:u256 +// } +// function g() +// { +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/nested.yul b/test/libyul/yulOptimizerTests/functionHoister/nested.yul new file mode 100644 index 00000000..20f094f1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/nested.yul @@ -0,0 +1,23 @@ +// yul +{ + let a:u256 + function f() { + let b:u256 + function g() { let c:u256 } + let d:u256 + } +} +// ---- +// functionHoister +// { +// let a:u256 +// function g() +// { +// let c:u256 +// } +// function f() +// { +// let b:u256 +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/single.yul b/test/libyul/yulOptimizerTests/functionHoister/single.yul new file mode 100644 index 00000000..ba922612 --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/single.yul @@ -0,0 +1,13 @@ +// yul +{ + let a:u256 + function f() {} +} +// ---- +// functionHoister +// { +// let a:u256 +// function f() +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/smoke.yul b/test/libyul/yulOptimizerTests/functionHoister/smoke.yul new file mode 100644 index 00000000..35c1ce5f --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/smoke.yul @@ -0,0 +1,6 @@ +{ +} +// ---- +// functionHoister +// { +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul new file mode 100644 index 00000000..bae6bd48 --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul @@ -0,0 +1,33 @@ +// yul +{ + let a:u256 + { } + function f() -> x:bool { + let b:u256 := 4:u256 + {} + for {} f() {} {} + } +} +// ---- +// mainFunction +// { +// function main() +// { +// let a:u256 +// { +// } +// } +// function f() -> x:bool +// { +// let b:u256 := 4:u256 +// { +// } +// for { +// } +// f() +// { +// } +// { +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul new file mode 100644 index 00000000..dd5caaec --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul @@ -0,0 +1,26 @@ +// yul +{ + let a:u256 + function f() { let b:u256 } + let c:u256 + function g() { let d:u256 } + let e:u256 +} +// ---- +// mainFunction +// { +// function main() +// { +// let a:u256 +// let c:u256 +// let e:u256 +// } +// function f() +// { +// let b:u256 +// } +// function g() +// { +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul new file mode 100644 index 00000000..309b97cc --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul @@ -0,0 +1,26 @@ +// yul +{ + let a:u256 + function f() { + let b:u256 + function g() { let c:u256} + let d:u256 + } +} +// ---- +// mainFunction +// { +// function main() +// { +// let a:u256 +// } +// function f() +// { +// let b:u256 +// function g() +// { +// let c:u256 +// } +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul new file mode 100644 index 00000000..fa9a8f41 --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul @@ -0,0 +1,16 @@ +// yul +{ + let a:u256 + function f() {} +} +// ---- +// mainFunction +// { +// function main() +// { +// let a:u256 +// } +// function f() +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/smoke.yul b/test/libyul/yulOptimizerTests/mainFunction/smoke.yul new file mode 100644 index 00000000..7be14746 --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/smoke.yul @@ -0,0 +1,9 @@ +// yul +{} +// ---- +// mainFunction +// { +// function main() +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul new file mode 100644 index 00000000..dbd1ee63 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul @@ -0,0 +1,21 @@ +{ + let a := 1 + for { pop(a) } a { pop(a) } { + pop(a) + } +} +// ---- +// rematerialiser +// { +// let a := 1 +// for { +// pop(1) +// } +// 1 +// { +// pop(1) +// } +// { +// pop(1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul new file mode 100644 index 00000000..6a52e045 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul @@ -0,0 +1,25 @@ +{ + let a := 1 + for { pop(a) } a { pop(a) } { + a := 7 + let c := a + } + let x := a +} +// ---- +// rematerialiser +// { +// let a := 1 +// for { +// pop(1) +// } +// a +// { +// pop(7) +// } +// { +// a := 7 +// let c := 7 +// } +// let x := a +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul new file mode 100644 index 00000000..fc816419 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul @@ -0,0 +1,23 @@ +{ + let b := 0 + for { let a := 1 pop(a) } a { pop(a) } { + b := 1 pop(a) + } +} +// ---- +// rematerialiser +// { +// let b := 0 +// for { +// let a := 1 +// pop(1) +// } +// 1 +// { +// pop(1) +// } +// { +// b := 1 +// pop(1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul new file mode 100644 index 00000000..3d916890 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul @@ -0,0 +1,24 @@ +{ + let b := 0 + for { let a := 1 pop(a) } lt(a, 0) { pop(a) a := add(a, 3) } { + b := 1 pop(a) + } +} +// ---- +// rematerialiser +// { +// let b := 0 +// for { +// let a := 1 +// pop(1) +// } +// lt(a, 0) +// { +// pop(a) +// a := add(a, 3) +// } +// { +// b := 1 +// pop(a) +// } +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul new file mode 100644 index 00000000..c148c2f2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul @@ -0,0 +1,18 @@ +{ + let a := 1 + let b := 2 + if b { pop(b) b := a } + let c := b +} +// ---- +// rematerialiser +// { +// let a := 1 +// let b := 2 +// if 2 +// { +// pop(2) +// b := 1 +// } +// let c := b +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul new file mode 100644 index 00000000..8f70a79d --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul @@ -0,0 +1,24 @@ +{ + let a := 1 + let b := 2 + switch number() + case 1 { b := a } + default { let x := a let y := b b := a } + pop(add(a, b)) +} +// ---- +// rematerialiser +// { +// let a := 1 +// let b := 2 +// switch number() +// case 1 { +// b := 1 +// } +// default { +// let x := 1 +// let y := b +// b := 1 +// } +// pop(add(1, b)) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul new file mode 100644 index 00000000..891a5043 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul @@ -0,0 +1,19 @@ +// Cannot replace `let b := x` by `let b := a` since a is out of scope. +{ + let x + { + let a := sload(0) + x := a + } + let b := x +} +// ---- +// rematerialiser +// { +// let x +// { +// let a := sload(0) +// x := a +// } +// let b := x +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul new file mode 100644 index 00000000..016fa0d7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul @@ -0,0 +1,10 @@ +{ + let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) + let b := x +} +// ---- +// rematerialiser +// { +// let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) +// let b := x +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul new file mode 100644 index 00000000..d95dc1fc --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul @@ -0,0 +1,10 @@ +{ + let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) + let b := x +} +// ---- +// rematerialiser +// { +// let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) +// let b := add(mul(calldataload(2), calldataload(4)), calldatasize()) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/expression.yul b/test/libyul/yulOptimizerTests/rematerialiser/expression.yul new file mode 100644 index 00000000..a801677d --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/expression.yul @@ -0,0 +1,10 @@ +{ + let a := add(mul(calldatasize(), 2), number()) + let b := add(a, a) +} +// ---- +// rematerialiser +// { +// let a := add(mul(calldatasize(), 2), number()) +// let b := add(add(mul(calldatasize(), 2), number()), add(mul(calldatasize(), 2), number())) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul new file mode 100644 index 00000000..9a041dfc --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul @@ -0,0 +1,18 @@ +{ + function f(x) -> y {} + let a := 1 + let b := f(a) + let c := a + mstore(add(a, b), c) +} +// ---- +// rematerialiser +// { +// function f(x) -> y +// { +// } +// let a := 1 +// let b := f(1) +// let c := 1 +// mstore(add(1, b), 1) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul new file mode 100644 index 00000000..8767abc9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul @@ -0,0 +1,14 @@ +{ + let a := 1 + let b := mload(a) + let c := a + mstore(add(a, b), c) +} +// ---- +// rematerialiser +// { +// let a := 1 +// let b := mload(1) +// let c := 1 +// mstore(add(1, b), 1) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul b/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul new file mode 100644 index 00000000..47124658 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul @@ -0,0 +1,21 @@ +{ + let a := extcodesize(0) + let b := a + let c := b + a := 2 + let d := add(b, c) + pop(a) pop(b) pop(c) pop(d) +} +// ---- +// rematerialiser +// { +// let a := extcodesize(0) +// let b := a +// let c := a +// a := 2 +// let d := add(b, c) +// pop(2) +// pop(b) +// pop(c) +// pop(add(b, c)) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul b/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul new file mode 100644 index 00000000..13238780 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul @@ -0,0 +1,19 @@ +{ + let a := 1 + pop(a) + if a { a := 2 } + let b := mload(a) + pop(b) +} +// ---- +// rematerialiser +// { +// let a := 1 +// pop(1) +// if 1 +// { +// a := 2 +// } +// let b := mload(a) +// pop(b) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul b/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul new file mode 100644 index 00000000..2423db32 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul @@ -0,0 +1,5 @@ +{} +// ---- +// rematerialiser +// { +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul b/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul new file mode 100644 index 00000000..d29ea98a --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul @@ -0,0 +1,12 @@ +{ + let a := 1 + let b := a + mstore(0, b) +} +// ---- +// rematerialiser +// { +// let a := 1 +// let b := 1 +// mstore(0, 1) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul b/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul new file mode 100644 index 00000000..7d35fee0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul @@ -0,0 +1,13 @@ +// We cannot substitute `a` in `let b := a` +{ + let a := extcodesize(0) + a := mul(a, 2) + let b := a +} +// ---- +// rematerialiser +// { +// let a := extcodesize(0) +// a := mul(a, 2) +// let b := a +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/functions.yul b/test/libyul/yulOptimizerTests/unusedPruner/functions.yul new file mode 100644 index 00000000..ec9cdda8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/functions.yul @@ -0,0 +1,8 @@ +{ + function f() { let a := 1 } + function g() { f() } +} +// ---- +// unusedPruner +// { +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul new file mode 100644 index 00000000..4ed6dd2c --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul @@ -0,0 +1,11 @@ +{ + let a := 1 + a := 4 + let b := 1 +} +// ---- +// unusedPruner +// { +// let a := 1 +// a := 4 +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul new file mode 100644 index 00000000..94d101e9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul @@ -0,0 +1,16 @@ +{ + let a, b + function f() -> x { } + a := f() + b := 1 +} +// ---- +// unusedPruner +// { +// let a, b +// function f() -> x +// { +// } +// a := f() +// b := 1 +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul new file mode 100644 index 00000000..a14dc28c --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul @@ -0,0 +1,16 @@ +{ + let a + let b + function f() -> x, y { } + a, b := f() +} +// ---- +// unusedPruner +// { +// let a +// let b +// function f() -> x, y +// { +// } +// a, b := f() +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul new file mode 100644 index 00000000..fe94edb8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul @@ -0,0 +1,12 @@ +{ + let x, y + x := 1 + y := 2 +} +// ---- +// unusedPruner +// { +// let x, y +// x := 1 +// y := 2 +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul new file mode 100644 index 00000000..3cf35007 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul @@ -0,0 +1,7 @@ +{ + let x, y +} +// ---- +// unusedPruner +// { +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul new file mode 100644 index 00000000..adabac87 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul @@ -0,0 +1,12 @@ +{ + function f() -> x, y { } + let a, b := f() +} +// ---- +// unusedPruner +// { +// function f() -> x, y +// { +// } +// let a, b := f() +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul new file mode 100644 index 00000000..5db0ade9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul @@ -0,0 +1,10 @@ +{ + let x, y + x := 1 +} +// ---- +// unusedPruner +// { +// let x, y +// x := 1 +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul b/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul new file mode 100644 index 00000000..ca2ed942 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// unusedPruner +// { +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul b/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul new file mode 100644 index 00000000..9b4cf9fd --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul @@ -0,0 +1,10 @@ +{ + let a := 1 + let b := 1 + mstore(0, 1) +} +// ---- +// unusedPruner +// { +// mstore(0, 1) +// } diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index d6df0ac8..65054fca 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) -add_executable(isoltest isoltest.cpp ../Options.cpp ../libsolidity/TestCase.cpp ../libsolidity/SyntaxTest.cpp +add_executable(isoltest isoltest.cpp ../Options.cpp ../Common.cpp ../libsolidity/TestCase.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/AnalysisFramework.cpp ../libsolidity/SolidityExecutionFramework.cpp ../ExecutionFramework.cpp - ../RPCSession.cpp ../libsolidity/ASTJSONTest.cpp) + ../RPCSession.cpp ../libsolidity/ASTJSONTest.cpp ../libyul/YulOptimizerTest.cpp) target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) diff --git a/test/tools/fuzzer.cpp b/test/tools/fuzzer.cpp index ce4b721f..bb020f8c 100644 --- a/test/tools/fuzzer.cpp +++ b/test/tools/fuzzer.cpp @@ -28,6 +28,7 @@ #include <boost/program_options.hpp> #include <string> +#include <sstream> #include <iostream> using namespace std; @@ -48,15 +49,17 @@ string contains(string const& _haystack, vector<string> const& _needles) return ""; } -void testConstantOptimizer() +void testConstantOptimizer(string const& input) { if (!quiet) cout << "Testing constant optimizer" << endl; vector<u256> numbers; - while (!cin.eof()) + stringstream sin(input); + + while (!sin.eof()) { h256 data; - cin.read(reinterpret_cast<char*>(data.data()), 32); + sin.read(reinterpret_cast<char*>(data.data()), 32); numbers.push_back(u256(data)); } if (!quiet) @@ -86,7 +89,7 @@ void testConstantOptimizer() void runCompiler(string input) { - string outputString(compileStandard(input.c_str(), NULL)); + string outputString(compileStandard(input.c_str(), nullptr)); Json::Value output; if (!jsonParseStrict(outputString, output)) { @@ -108,20 +111,18 @@ void runCompiler(string input) } } -void testStandardCompiler() +void testStandardCompiler(string const& input) { if (!quiet) cout << "Testing compiler via JSON interface." << endl; - string input = readStandardInput(); runCompiler(input); } -void testCompiler(bool optimize) +void testCompiler(string const& input, bool optimize) { if (!quiet) cout << "Testing compiler " << (optimize ? "with" : "without") << " optimizer." << endl; - string input = readStandardInput(); Json::Value config = Json::objectValue; config["language"] = "Solidity"; @@ -168,15 +169,24 @@ Allowed options)", "Expects a binary string of up to 32 bytes on stdin." ) ( + "input-file", + po::value<string>(), + "input file" + ) + ( "without-optimizer", "Run without optimizations. Cannot be used together with standard-json." ); + // All positional options should be interpreted as input files + po::positional_options_description filesPositions; + filesPositions.add("input-file", 1); + po::variables_map arguments; try { po::command_line_parser cmdLineParser(argc, argv); - cmdLineParser.options(options); + cmdLineParser.options(options).positional(filesPositions); po::store(cmdLineParser.run(), arguments); } catch (po::error const& _exception) @@ -185,17 +195,23 @@ Allowed options)", return 1; } + string input; + if (arguments.count("input-file")) + input = readFileAsString(arguments["input-file"].as<string>()); + else + input = readStandardInput(); + if (arguments.count("quiet")) quiet = true; if (arguments.count("help")) cout << options; else if (arguments.count("const-opt")) - testConstantOptimizer(); + testConstantOptimizer(input); else if (arguments.count("standard-json")) - testStandardCompiler(); + testStandardCompiler(input); else - testCompiler(!arguments.count("without-optimizer")); + testCompiler(input, !arguments.count("without-optimizer")); return 0; } diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index ad6b456d..bdc89fb3 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -16,9 +16,12 @@ */ #include <libdevcore/CommonIO.h> + +#include <test/Common.h> #include <test/libsolidity/AnalysisFramework.h> #include <test/libsolidity/SyntaxTest.h> #include <test/libsolidity/ASTJSONTest.h> +#include <test/libyul/YulOptimizerTest.h> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/replace.hpp> @@ -47,6 +50,12 @@ struct TestStats int successCount; int testCount; operator bool() const { return successCount == testCount; } + TestStats& operator+=(TestStats const& _other) noexcept + { + successCount += _other.successCount; + testCount += _other.testCount; + return *this; + } }; class TestTool @@ -256,6 +265,9 @@ TestStats TestTool::processPath( } +namespace +{ + void setupTerminal() { #if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) @@ -276,6 +288,36 @@ void setupTerminal() #endif } +boost::optional<TestStats> runTestSuite( + string const& _name, + fs::path const& _basePath, + fs::path const& _subdirectory, + TestCase::TestCaseCreator _testCaseCreator, + bool _formatted +) +{ + fs::path testPath = _basePath / _subdirectory; + + if (!fs::exists(testPath) || !fs::is_directory(testPath)) + { + cerr << _name << " tests not found. Use the --testpath argument." << endl; + return {}; + } + + TestStats stats = TestTool::processPath(_testCaseCreator, _basePath, _subdirectory, _formatted); + + cout << endl << _name << " Test Summary: "; + FormattedScope(cout, _formatted, {BOLD, stats ? GREEN : RED}) << + stats.successCount << + "/" << + stats.testCount; + cout << " tests successful." << endl << endl; + + return stats; +} + +} + int main(int argc, char *argv[]) { setupTerminal(); @@ -326,73 +368,37 @@ Allowed options)", } if (testPath.empty()) - { - auto const searchPath = - { - fs::current_path() / ".." / ".." / ".." / "test", - fs::current_path() / ".." / ".." / "test", - fs::current_path() / ".." / "test", - fs::current_path() / "test", - fs::current_path() - }; - for (auto const& basePath : searchPath) - { - fs::path syntaxTestPath = basePath / "libsolidity" / "syntaxTests"; - if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) - { - testPath = basePath; - break; - } - } - } - - TestStats global_stats { 0, 0 }; + testPath = dev::test::discoverTestPath(); - fs::path syntaxTestPath = testPath / "libsolidity" / "syntaxTests"; + TestStats global_stats{0, 0}; - if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) - { - auto stats = TestTool::processPath(SyntaxTest::create, testPath / "libsolidity", "syntaxTests", formatted); - - cout << endl << "Syntax Test Summary: "; - FormattedScope(cout, formatted, {BOLD, stats ? GREEN : RED}) << - stats.successCount << "/" << stats.testCount; - cout << " tests successful." << endl << endl; - - global_stats.testCount += stats.testCount; - global_stats.successCount += stats.successCount; - } + // Actually run the tests. + // If you add new tests here, you also have to add them in boostTest.cpp + if (auto stats = runTestSuite("Syntax", testPath / "libsolidity", "syntaxTests", SyntaxTest::create, formatted)) + global_stats += *stats; else - { - cerr << "Syntax tests not found. Use the --testpath argument." << endl; return 1; - } - - fs::path astJsonTestPath = testPath / "libsolidity" / "ASTJSON"; - if (fs::exists(astJsonTestPath) && fs::is_directory(astJsonTestPath)) - { - auto stats = TestTool::processPath(ASTJSONTest::create, testPath / "libsolidity", "ASTJSON", formatted); - - cout << endl << "JSON AST Test Summary: "; - FormattedScope(cout, formatted, {BOLD, stats ? GREEN : RED}) << - stats.successCount << "/" << stats.testCount; - cout << " tests successful." << endl << endl; + if (auto stats = runTestSuite("JSON AST", testPath / "libsolidity", "ASTJSON", ASTJSONTest::create, formatted)) + global_stats += *stats; + else + return 1; - global_stats.testCount += stats.testCount; - global_stats.successCount += stats.successCount; - } + if (auto stats = runTestSuite( + "Yul Optimizer", + testPath / "libyul", + "yulOptimizerTests", + yul::test::YulOptimizerTest::create, + formatted + )) + global_stats += *stats; else - { - cerr << "JSON AST tests not found." << endl; return 1; - } cout << endl << "Summary: "; FormattedScope(cout, formatted, {BOLD, global_stats ? GREEN : RED}) << global_stats.successCount << "/" << global_stats.testCount; cout << " tests successful." << endl; - return global_stats ? 0 : 1; } |