diff options
312 files changed, 4266 insertions, 3524 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9734fe81..9fdbf158 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,6 +5,14 @@ Please review the [guidelines for contributing](http://solidity.readthedocs.io/e Please also note that this project is released with a [Contributor Code of Conduct](CONDUCT.md). By participating in this project you agree to abide by its terms. --> +### Description + +<!-- +Please explain the changes you made here. + +Thank you for your help! +--> + ### Checklist - [ ] Code compiles correctly - [ ] All tests are passing @@ -12,11 +20,3 @@ Please also note that this project is released with a [Contributor Code of Condu - [ ] README / documentation was extended, if necessary - [ ] Changelog entry (if change is visible to the user) - [ ] Used meaningful commit messages - -### Description - -<!-- -Please explain the changes you made here. - -Thank you for your help! --->
\ No newline at end of file diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 8101db0c..a0fe9864 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -146,7 +146,7 @@ for (auto i = x->begin(); i != x->end(); ++i) {} ``` No: -```cp +```cpp const double d = 0; int i, j; char *s; diff --git a/Changelog.md b/Changelog.md index 5bf194e4..ff95a89a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -128,6 +128,7 @@ Bugfixes: * Type Checker: Fix freeze for negative fixed-point literals very close to ``0``, such as ``-1e-100``. * 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 when fixed-size array is too large to be encoded. * 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. diff --git a/cmake/FindZ3.cmake b/cmake/FindZ3.cmake index 704f367e..c017cac2 100644 --- a/cmake/FindZ3.cmake +++ b/cmake/FindZ3.cmake @@ -1,5 +1,5 @@ if (USE_Z3) - find_path(Z3_INCLUDE_DIR z3++.h) + find_path(Z3_INCLUDE_DIR NAMES z3++.h PATH_SUFFIXES z3) find_library(Z3_LIBRARY NAMES z3 ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR) diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 7b227297..48112cd9 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -12,7 +12,8 @@ For the full list check Contracts compiled with Solidity v0.5.0 can still interface with contracts and even libraries compiled with older versions without recompiling or redeploying them. Changing the interfaces to include data locations and - visibility and mutability specifiers suffices. + visibility and mutability specifiers suffices. See the + :ref:`Interoperability With Older Contracts <interoperability>` section below. Semantic Only Changes ===================== @@ -101,6 +102,14 @@ For most of the topics the compiler will provide suggestions. ``c.transfer(...)`` to ``address(c).transfer(...)``, and ``c.balance`` to ``address(c).balance``. +* Explicit conversions between unrelated contract types are now disallowed. You can only + convert from a contract type to one of its base or ancestor types. If you are sure that + a contract is compatible with the contract type you want to convert to, although it does not + inherit from it, you can work around this by converting to ``address`` first. + Example: if ``A`` and ``B`` are contract types, ``B`` does not inherit from ``A`` and + ``b`` is a contract of type ``B``, you can still convert ``b`` to type ``A`` using ``A(address(b))``. + Note that you still need to watch out for matching payable fallback functions, as explained below. + * The ``address`` type was split into ``address`` and ``address payable``, where only ``address payable`` provides the ``transfer`` function. An ``address payable`` can be directly converted to an ``address``, but the @@ -272,6 +281,83 @@ Syntax ``override``, ``partial``, ``promise``, ``reference``, ``sealed``, ``sizeof``, ``supports``, ``typedef`` and ``unchecked``. +.. _interoperability: + +Interoperability With Older Contracts +===================================== + +It is still possible to interface with contracts written for Solidity versions prior to +v0.5.0 (or the other way around) by defining interfaces for them. +Consider you have the following pre-0.5.0 contract already deployed: + +:: + + // This will not compile with the current version of the compiler + pragma solidity ^0.4.25; + contract OldContract { + function someOldFunction(uint8 a) { + //... + } + function anotherOldFunction() constant returns (bool) { + //... + } + // ... + } + +This will no longer compile with Solidity v0.5.0. However, you can define a compatible interface for it: + +:: + + pragma solidity >0.4.99 <0.6.0; + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } + +Note that we did not declare ``anotherOldFunction`` to be ``view``, despite it being declared ``constant`` in the original +contract. This is due to the fact that starting with Solidity v0.5.0 ``staticcall`` is used to call ``view`` functions. +Prior to v0.5.0 the ``constant`` keyword was not enforced, so calling a function declared ``constant`` with ``staticcall`` +may still revert, since the ``constant`` function may still attempt to modify storage. Consequently, when defining an +interface for older contracts, you should only use ``view`` in place of ``constant`` in case you are absolutely sure that +the function will work with ``staticcall``. + +Given the interface defined above, you can now easily use the already deployed pre-0.5.0 contract: + +:: + + pragma solidity >0.4.99 <0.6.0; + + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } + + contract NewContract { + function doSomething(OldContract a) public returns (bool) { + a.someOldFunction(0x42); + return a.anotherOldFunction(); + } + } + +Similarly, pre-0.5.0 libraries can be used by defining the functions of the library without implementation and +supplying the address of the pre-0.5.0 library during linking (see :ref:`commandline-compiler` for how to use the +commandline compiler for linking): + +:: + + pragma solidity >0.4.99 <0.6.0; + + library OldLibrary { + function someFunction(uint8 a) public returns(bool); + } + + contract NewContract { + function f(uint8 a) public returns (bool) { + return OldLibrary.someFunction(a); + } + } + + Example ======= diff --git a/docs/contributing.rst b/docs/contributing.rst index 32696520..e626e5c0 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -16,7 +16,7 @@ In particular, we need help in the following areas: <https://gitter.im/ethereum/solidity>`_ * Fixing and responding to `Solidity's GitHub issues <https://github.com/ethereum/solidity/issues>`_, especially those tagged as - `up-for-grabs <https://github.com/ethereum/solidity/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs>`_ which are + `good first issue <https://github.com/ethereum/solidity/labels/good%20first%20issue>`_ which are meant as introductory issues for external contributors. Please note that this project is released with a `Contributor Code of Conduct <https://raw.githubusercontent.com/ethereum/solidity/develop/CODE_OF_CONDUCT.md>`_. By participating in this project - in the issues, pull requests, or Gitter channels - you agree to abide by its terms. @@ -199,14 +199,13 @@ Running the Fuzzer via AFL ========================== Fuzzing is a technique that runs programs on more or less random inputs to find exceptional execution -states (segmentation faults, exceptions, etc). Modern fuzzers are clever and do a directed search +states (segmentation faults, exceptions, etc). Modern fuzzers are clever and run a directed search inside the input. We have a specialized binary called ``solfuzzer`` which takes source code as input and fails whenever it encounters an internal compiler error, segmentation fault or similar, but -does not fail if e.g. the code contains an error. This way, internal problems in the compiler -can be found by fuzzing tools. +does not fail if e.g., the code contains an error. This way, fuzzing tools can find internal problems in the compiler. We mainly use `AFL <http://lcamtuf.coredump.cx/afl/>`_ for fuzzing. You need to download and -install AFL packages from your repos (afl, afl-clang) or build them manually. +install the AFL packages from your repositories (afl, afl-clang) or build them manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler: :: @@ -237,7 +236,7 @@ If the instrumentation messages did not appear, try switching the cmake flags po cmake .. -DCMAKE_C_COMPILER=path/to/afl-clang -DCMAKE_CXX_COMPILER=path/to/afl-clang++ make solfuzzer -Othwerise, upon execution the fuzzer will halt with an error saying binary is not instrumented: +Otherwise, upon execution the fuzzer halts with an error saying binary is not instrumented: :: @@ -259,7 +258,7 @@ Othwerise, upon execution the fuzzer will halt with an error saying binary is no Location : check_binary(), afl-fuzz.c:6920 -Next, you need some example source files. This will make it much easier for the fuzzer +Next, you need some example source files. This makes it much easier for the fuzzer to find errors. You can either copy some files from the syntax tests or extract test files from the documentation or the other tests: @@ -274,8 +273,8 @@ from the documentation or the other tests: The AFL documentation states that the corpus (the initial input files) should not be too large. The files themselves should not be larger than 1 kB and there should be -at most one input file per functionality, so better start with a small number of -input files. There is also a tool called ``afl-cmin`` that can trim input files +at most one input file per functionality, so better start with a small number of. +There is also a tool called ``afl-cmin`` that can trim input files that result in similar behaviour of the binary. Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB): @@ -284,7 +283,7 @@ Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB): afl-fuzz -m 60 -i /tmp/test_cases -o /tmp/fuzzer_reports -- /path/to/solfuzzer -The fuzzer will create source files that lead to failures in ``/tmp/fuzzer_reports``. +The fuzzer creates source files that lead to failures in ``/tmp/fuzzer_reports``. Often it finds many similar source files that produce the same error. You can use the tool ``scripts/uniqueErrors.sh`` to filter out the unique errors. @@ -294,10 +293,10 @@ Whiskers *Whiskers* is a string templating system similar to `Mustache <https://mustache.github.io>`_. It is used by the compiler in various places to aid readability, and thus maintainability and verifiability, of the code. -The syntax comes with a substantial difference to Mustache: the template markers ``{{`` and ``}}`` are +The syntax comes with a substantial difference to Mustache. The template markers ``{{`` and ``}}`` are replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`inline-assembly` (The symbols ``<`` and ``>`` are invalid in inline assembly, while ``{`` and ``}`` are used to delimit blocks). -Another limitation is that lists are only resolved one depth and they will not recurse. This may change in the future. +Another limitation is that lists are only resolved one depth and they do not recurse. This may change in the future. A rough specification is the following: diff --git a/docs/control-structures.rst b/docs/control-structures.rst index c2d60df1..9da29d14 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -126,6 +126,10 @@ actual contract has not been created yet. Functions of other contracts have to be called externally. For an external call, all function arguments have to be copied to memory. +.. note:: + A function call from one contract to another does not create its own transaction, + it is a message call as part of the overall transaction. + When calling functions of other contracts, the amount of Wei sent with the call and the gas can be specified with special options ``.value()`` and ``.gas()``, respectively:: diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 0f8b34f8..3e9a6aca 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -213,14 +213,6 @@ It gets added to the total balance of the contract, just like when you send ethe You can only send ether along to a function that has the ``payable`` modifier, otherwise an exception is thrown. -Is it possible to get a tx receipt for a transaction executed contract-to-contract? -=================================================================================== - -No, a function call from one contract to another does not create its own transaction, -you have to look in the overall transaction. This is also the reason why several -block explorer do not show Ether sent between contracts correctly. - - ****************** Advanced Questions ****************** diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index fb18f8a9..9a8bdc3e 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -147,6 +147,10 @@ Another syntax is not part of ES6, but probably convenient: which is equivalent to ``import * as symbolName from "filename";``. +.. note:: + If you use `import "filename.sol" as moduleName;`, you access a contract called `C` + from inside `"filename.sol"` as `moduleName.C` and not by using `C` directly. + Paths ----- diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 8f58f339..752e8641 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -152,6 +152,7 @@ of votes. /// to proposal `proposals[proposal].name`. function vote(uint proposal) public { Voter storage sender = voters[msg.sender]; + require(sender.weight != 0, "Has no right to vote"); require(!sender.voted, "Already voted."); sender.voted = true; sender.vote = proposal; @@ -876,7 +877,7 @@ two parties (Alice and Bob). Using it involves three steps: 2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment. 3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender. -Not ethat only steps 1 and 3 require Ethereum transactions, step 2 means that +Note that only steps 1 and 3 require Ethereum transactions, step 2 means that the sender transmits a cryptographically signed message to the recipient via off chain ways (e.g. email). This means only two transactions are required to support any number of transfers. diff --git a/docs/types.rst b/docs/types.rst index bd5d1734..34b94b88 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -1225,7 +1225,6 @@ is possible if it makes sense semantically and no information is lost: ``uint8`` is convertible to ``uint16`` and ``int128`` to ``int256``, but ``int8`` is not convertible to ``uint256`` (because ``uint256`` cannot hold e.g. ``-1``). -Any integer type that can be converted to ``uint160`` can also be converted to ``address``. For more details, please consult the sections about the types themselves. @@ -1335,4 +1334,4 @@ Addresses As described in :ref:`address_literals`, hex literals of the correct size that pass the checksum test are of ``address`` type. No other literals can be implicitly converted to the ``address`` type. -Explicit conversions from ``bytes20`` or any integer type to ``address`` results in ``address payable``.
\ No newline at end of file +Explicit conversions from ``bytes20`` or any integer type to ``address`` result in ``address payable``. diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 0363d9a2..6208424e 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -64,15 +64,13 @@ #include <functional> #include <string> -using byte = uint8_t; - namespace dev { // Binary data types. -using bytes = std::vector<byte>; -using bytesRef = vector_ref<byte>; -using bytesConstRef = vector_ref<byte const>; +using bytes = std::vector<uint8_t>; +using bytesRef = vector_ref<uint8_t>; +using bytesConstRef = vector_ref<uint8_t const>; // Numeric types. using bigint = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<>>; diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index cb79fa98..8d2639c9 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -64,7 +64,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) int h = fromHex(_s[i], WhenError::DontThrow); int l = fromHex(_s[i + 1], WhenError::DontThrow); if (h != -1 && l != -1) - ret.push_back((byte)(h * 16 + l)); + ret.push_back((uint8_t)(h * 16 + l)); else if (_throw == WhenError::Throw) BOOST_THROW_EXCEPTION(BadHexCharacter()); else @@ -110,3 +110,26 @@ string dev::getChecksummedAddress(string const& _addr) } return ret; } + +bool dev::isValidHex(string const& _string) +{ + if (_string.substr(0, 2) != "0x") + return false; + if (_string.find_first_not_of("0123456789abcdefABCDEF", 2) != string::npos) + return false; + return true; +} + +bool dev::isValidDecimal(string const& _string) +{ + if (_string.empty()) + return false; + if (_string == "0") + return true; + // No leading zeros + if (_string.front() == '0') + return false; + if (_string.find_first_not_of("0123456789") != string::npos) + return false; + return true; +} diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index f208c425..fedd3af2 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -88,7 +88,7 @@ inline std::string asString(bytesConstRef _b) /// Converts a string to a byte array containing the string's (byte) data. inline bytes asBytes(std::string const& _b) { - return bytes((byte const*)_b.data(), (byte const*)(_b.data() + _b.size())); + return bytes((uint8_t const*)_b.data(), (uint8_t const*)(_b.data() + _b.size())); } // Big-endian to/from host endian conversion functions. @@ -117,7 +117,7 @@ inline T fromBigEndian(_In const& _bytes) { T ret = (T)0; for (auto i: _bytes) - ret = (T)((ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i); + ret = (T)((ret << 8) | (uint8_t)(typename std::make_unsigned<typename _In::value_type>::type)i); return ret; } inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } @@ -135,7 +135,7 @@ inline bytes toCompactBigEndian(T _val, unsigned _min = 0) toBigEndian(_val, ret); return ret; } -inline bytes toCompactBigEndian(byte _val, unsigned _min = 0) +inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0) { return (_min || _val) ? bytes{ _val } : bytes{}; } @@ -272,4 +272,7 @@ bool passesAddressChecksum(std::string const& _str, bool _strict); /// @param hex strings that look like an address std::string getChecksummedAddress(std::string const& _addr); +bool isValidHex(std::string const& _string); +bool isValidDecimal(std::string const& _string); + } diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 1aa3504c..cc730575 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -80,45 +80,6 @@ string dev::readStandardInput() return ret; } -void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename) -{ - namespace fs = boost::filesystem; - if (_writeDeleteRename) - { - fs::path tempPath = fs::unique_path(_file + "-%%%%%%"); - writeFile(tempPath.string(), _data, false); - // will delete _file if it exists - fs::rename(tempPath, _file); - } - else - { - // create directory if not existent - fs::path p(_file); - if (!p.parent_path().empty() && !fs::exists(p.parent_path())) - { - fs::create_directories(p.parent_path()); - try - { - fs::permissions(p.parent_path(), fs::owner_all); - } - catch (...) - { - } - } - - ofstream s(_file, ios::trunc | ios::binary); - s.write(reinterpret_cast<char const*>(_data.data()), _data.size()); - assertThrow(s, FileError, "Could not write to file: " + _file); - try - { - fs::permissions(_file, fs::owner_read|fs::owner_write); - } - catch (...) - { - } - } -} - #if defined(_WIN32) class DisableConsoleBuffering { diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 928b6d15..b9f941ea 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -41,14 +41,6 @@ std::string readStandardInput(); /// Retrieve and returns a character from standard input (without waiting for EOL). int readStandardInputChar(); -/// Write the given binary data into the given file, replacing the file if it pre-exists. -/// Throws exception on error. -/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in -/// the same directory and then moves that file. -void writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename = false); -/// Write the given binary data into the given file, replacing the file if it pre-exists. -inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); } -inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); } /// Converts arbitrary value to string representation using std::stringstream. template <class _T> std::string toString(_T const& _t) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index cd6e1da1..24b89840 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -79,7 +79,7 @@ public: operator Arith() const { return fromBigEndian<Arith>(m_data); } /// @returns true iff this is the empty hash. - explicit operator bool() const { return std::any_of(m_data.begin(), m_data.end(), [](byte _b) { return _b != 0; }); } + explicit operator bool() const { return std::any_of(m_data.begin(), m_data.end(), [](uint8_t _b) { return _b != 0; }); } // The obvious comparison operators. bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } @@ -90,9 +90,9 @@ public: FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; } /// @returns a particular byte from the hash. - byte& operator[](unsigned _i) { return m_data[_i]; } + uint8_t& operator[](unsigned _i) { return m_data[_i]; } /// @returns a particular byte from the hash. - byte operator[](unsigned _i) const { return m_data[_i]; } + uint8_t operator[](unsigned _i) const { return m_data[_i]; } /// @returns the hash as a user-readable hex string. std::string hex() const { return toHex(ref()); } @@ -104,19 +104,19 @@ public: bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); } /// @returns a mutable byte pointer to the object's data. - byte* data() { return m_data.data(); } + uint8_t* data() { return m_data.data(); } /// @returns a constant byte pointer to the object's data. - byte const* data() const { return m_data.data(); } + uint8_t const* data() const { return m_data.data(); } /// @returns a copy of the object's data as a byte vector. bytes asBytes() const { return bytes(data(), data() + N); } /// @returns a mutable reference to the object's data as an STL array. - std::array<byte, N>& asArray() { return m_data; } + std::array<uint8_t, N>& asArray() { return m_data; } /// @returns a constant reference to the object's data as an STL array. - std::array<byte, N> const& asArray() const { return m_data; } + std::array<uint8_t, N> const& asArray() const { return m_data; } /// Returns the index of the first bit set to one, or size() * 8 if no bits are set. inline unsigned firstBitSet() const @@ -137,7 +137,7 @@ public: void clear() { m_data.fill(0); } private: - std::array<byte, N> m_data; ///< The binary data. + std::array<uint8_t, N> m_data; ///< The binary data. }; /// Stream I/O for the FixedHash class. diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index c2eaa1ca..e63194a0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -525,14 +525,14 @@ LinkerObject const& Assembly::assemble() const multimap<size_t, size_t> subRef; vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted unsigned bytesPerTag = dev::bytesRequired(bytesRequiredForCode); - byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; + uint8_t tagPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerTag; unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + m_auxiliaryData.size(); for (auto const& sub: m_subs) bytesRequiredIncludingData += sub->assemble().bytecode.size(); unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData); - byte dataRefPush = (byte)Instruction::PUSH1 - 1 + bytesPerDataRef; + uint8_t dataRefPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerDataRef; ret.bytecode.reserve(bytesRequiredIncludingData); for (AssemblyItem const& i: m_items) @@ -544,25 +544,25 @@ LinkerObject const& Assembly::assemble() const switch (i.type()) { case Operation: - ret.bytecode.push_back((byte)i.instruction()); + ret.bytecode.push_back((uint8_t)i.instruction()); break; case PushString: { - ret.bytecode.push_back((byte)Instruction::PUSH32); + ret.bytecode.push_back((uint8_t)Instruction::PUSH32); unsigned ii = 0; for (auto j: m_strings.at((h256)i.data())) if (++ii > 32) break; else - ret.bytecode.push_back((byte)j); + ret.bytecode.push_back((uint8_t)j); while (ii++ < 32) ret.bytecode.push_back(0); break; } case Push: { - byte b = max<unsigned>(1, dev::bytesRequired(i.data())); - ret.bytecode.push_back((byte)Instruction::PUSH1 - 1 + b); + uint8_t b = max<unsigned>(1, dev::bytesRequired(i.data())); + ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b); ret.bytecode.resize(ret.bytecode.size() + b); bytesRef byr(&ret.bytecode.back() + 1 - b, b); toBigEndian(i.data(), byr); @@ -589,8 +589,8 @@ LinkerObject const& Assembly::assemble() const { auto s = m_subs.at(size_t(i.data()))->assemble().bytecode.size(); i.setPushedValue(u256(s)); - byte b = max<unsigned>(1, dev::bytesRequired(s)); - ret.bytecode.push_back((byte)Instruction::PUSH1 - 1 + b); + uint8_t b = max<unsigned>(1, dev::bytesRequired(s)); + ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b); ret.bytecode.resize(ret.bytecode.size() + b); bytesRef byr(&ret.bytecode.back() + 1 - b, b); toBigEndian(s, byr); @@ -604,12 +604,12 @@ LinkerObject const& Assembly::assemble() const break; } case PushLibraryAddress: - ret.bytecode.push_back(byte(Instruction::PUSH20)); + ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data()); ret.bytecode.resize(ret.bytecode.size() + 20); break; case PushDeployTimeAddress: - ret.bytecode.push_back(byte(Instruction::PUSH20)); + ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); ret.bytecode.resize(ret.bytecode.size() + 20); break; case Tag: @@ -618,7 +618,7 @@ LinkerObject const& Assembly::assemble() const assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large."); assertThrow(m_tagPositionsInBytecode[size_t(i.data())] == size_t(-1), AssemblyException, "Duplicate tag position."); m_tagPositionsInBytecode[size_t(i.data())] = ret.bytecode.size(); - ret.bytecode.push_back((byte)Instruction::JUMPDEST); + ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST); break; default: BOOST_THROW_EXCEPTION(InvalidOpcode()); @@ -627,7 +627,7 @@ LinkerObject const& Assembly::assemble() const if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty()) // Append an INVALID here to help tests find miscompilation. - ret.bytecode.push_back(byte(Instruction::INVALID)); + ret.bytecode.push_back(uint8_t(Instruction::INVALID)); for (size_t i = 0; i < m_subs.size(); ++i) { diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 5319a2b6..6187e18f 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -69,7 +69,7 @@ public: m_location(_location) { if (m_type == Operation) - m_instruction = Instruction(byte(_data)); + m_instruction = Instruction(uint8_t(_data)); else m_data = std::make_shared<u256>(_data); } diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 63424eeb..539a83b0 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -228,25 +228,25 @@ inline bool isLogInstruction(Instruction _inst) /// @returns the number of PUSH Instruction _inst inline unsigned getPushNumber(Instruction _inst) { - return (byte)_inst - unsigned(Instruction::PUSH1) + 1; + return (uint8_t)_inst - unsigned(Instruction::PUSH1) + 1; } /// @returns the number of DUP Instruction _inst inline unsigned getDupNumber(Instruction _inst) { - return (byte)_inst - unsigned(Instruction::DUP1) + 1; + return (uint8_t)_inst - unsigned(Instruction::DUP1) + 1; } /// @returns the number of SWAP Instruction _inst inline unsigned getSwapNumber(Instruction _inst) { - return (byte)_inst - unsigned(Instruction::SWAP1) + 1; + return (uint8_t)_inst - unsigned(Instruction::SWAP1) + 1; } /// @returns the number of LOG Instruction _inst inline unsigned getLogNumber(Instruction _inst) { - return (byte)_inst - unsigned(Instruction::LOG0); + return (uint8_t)_inst - unsigned(Instruction::LOG0); } /// @returns the PUSH<_number> instruction diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index ba13a611..120d1787 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -48,7 +48,7 @@ SimplificationRule<Pattern> const* Rules::findFirstMatch( resetMatchGroups(); assertThrow(_expr.item, OptimizerException, ""); - for (auto const& rule: m_rules[byte(_expr.item->instruction())]) + for (auto const& rule: m_rules[uint8_t(_expr.item->instruction())]) { if (rule.pattern.matches(_expr, _classes)) return &rule; @@ -59,7 +59,7 @@ SimplificationRule<Pattern> const* Rules::findFirstMatch( bool Rules::isInitialized() const { - return !m_rules[byte(Instruction::ADD)].empty(); + return !m_rules[uint8_t(Instruction::ADD)].empty(); } void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules) @@ -70,7 +70,7 @@ void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules) void Rules::addRule(SimplificationRule<Pattern> const& _rule) { - m_rules[byte(_rule.pattern.instruction())].push_back(_rule); + m_rules[uint8_t(_rule.pattern.instruction())].push_back(_rule); } Rules::Rules() diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 0aef05a9..85480119 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -348,7 +348,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else if (i.which() == sp::utree_type::string_type) { auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>(); - data.insert(data.end(), (byte const *)sr.begin(), (byte const*)sr.end()); + data.insert(data.end(), (uint8_t const *)sr.begin(), (uint8_t const*)sr.end()); } else if (i.which() == sp::utree_type::any_type) { diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 8659bbfd..f9b00927 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -46,7 +46,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) m_errorReporter.fatalTypeError( _operation.location(), "Operator " + - string(Token::toString(_operation.getOperator())) + + string(TokenTraits::toString(_operation.getOperator())) + " not compatible with types " + left->toString() + " and " + @@ -54,7 +54,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) ); setType( _operation, - Token::isCompareOp(_operation.getOperator()) ? + TokenTraits::isCompareOp(_operation.getOperator()) ? make_shared<BoolType>() : commonType ); diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index f62d9c3b..2adc8e77 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -271,16 +271,16 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) ErrorReporter errorsIgnored(errors); 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"); + auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); + bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); + bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); if (isSlot || isOffset) { // special mode to access storage variables if (!declarations.empty()) // the special identifier exists itself, we should not allow that. return size_t(-1); - string realName = _identifier.name.substr(0, _identifier.name.size() - ( + string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - ( isSlot ? string("_slot").size() : string("_offset").size() diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp index 29f6d5de..64fa17b3 100644 --- a/libsolidity/analysis/SemVerHandler.cpp +++ b/libsolidity/analysis/SemVerHandler.cpp @@ -199,7 +199,7 @@ void SemVerMatchExpressionParser::parseMatchExpression() SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchComponent() { SemVerMatchExpression::MatchComponent component; - Token::Value token = currentToken(); + Token token = currentToken(); switch (token) { @@ -280,7 +280,7 @@ char SemVerMatchExpressionParser::nextChar() return currentChar(); } -Token::Value SemVerMatchExpressionParser::currentToken() const +Token SemVerMatchExpressionParser::currentToken() const { if (m_pos < m_tokens.size()) return m_tokens[m_pos]; diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h index 76b70c5b..03a557c5 100644 --- a/libsolidity/analysis/SemVerHandler.h +++ b/libsolidity/analysis/SemVerHandler.h @@ -61,7 +61,7 @@ struct SemVerMatchExpression struct MatchComponent { /// Prefix from < > <= >= ~ ^ - Token::Value prefix = Token::Illegal; + Token prefix = Token::Illegal; /// Version, where unsigned(-1) in major, minor or patch denotes '*', 'x' or 'X' SemVerVersion version; /// Whether we have 1, 1.2 or 1.2.4 @@ -81,7 +81,7 @@ struct SemVerMatchExpression class SemVerMatchExpressionParser { public: - SemVerMatchExpressionParser(std::vector<Token::Value> const& _tokens, std::vector<std::string> const& _literals): + SemVerMatchExpressionParser(std::vector<Token> const& _tokens, std::vector<std::string> const& _literals): m_tokens(_tokens), m_literals(_literals) {} SemVerMatchExpression parse(); @@ -95,10 +95,10 @@ private: char currentChar() const; char nextChar(); - Token::Value currentToken() const; + Token currentToken() const; void nextToken(); - std::vector<Token::Value> m_tokens; + std::vector<Token> m_tokens; std::vector<std::string> m_literals; unsigned m_pos = 0; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index ab544388..3f9f8373 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -106,7 +106,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) } else if (_pragma.literals()[0] == "solidity") { - vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); + vector<Token> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); SemVerMatchExpressionParser parser(tokens, literals); auto matchExpression = parser.parse(); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 3830935f..3774cf86 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1499,14 +1499,14 @@ bool TypeChecker::visit(Assignment const& _assignment) // compound assignment _assignment.rightHandSide().accept(*this); TypePointer resultType = t->binaryOperatorResult( - Token::AssignmentToBinaryOp(_assignment.assignmentOperator()), + TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()), type(_assignment.rightHandSide()) ); if (!resultType || *resultType != *t) m_errorReporter.typeError( _assignment.location(), "Operator " + - string(Token::toString(_assignment.assignmentOperator())) + + string(TokenTraits::toString(_assignment.assignmentOperator())) + " not compatible with types " + t->toString() + " and " + @@ -1607,8 +1607,8 @@ bool TypeChecker::visit(TupleExpression const& _tuple) bool TypeChecker::visit(UnaryOperation const& _operation) { // Inc, Dec, Add, Sub, Not, BitNot, Delete - Token::Value op = _operation.getOperator(); - bool const modifying = (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete); + Token op = _operation.getOperator(); + bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete); if (modifying) requireLValue(_operation.subExpression()); else @@ -1620,7 +1620,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation) m_errorReporter.typeError( _operation.location(), "Unary operator " + - string(Token::toString(op)) + + string(TokenTraits::toString(op)) + " cannot be applied to type " + subExprType->toString() ); @@ -1641,7 +1641,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) m_errorReporter.typeError( _operation.location(), "Operator " + - string(Token::toString(_operation.getOperator())) + + string(TokenTraits::toString(_operation.getOperator())) + " not compatible with types " + leftType->toString() + " and " + @@ -1651,7 +1651,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) } _operation.annotation().commonType = commonType; _operation.annotation().type = - Token::isCompareOp(_operation.getOperator()) ? + TokenTraits::isCompareOp(_operation.getOperator()) ? make_shared<BoolType>() : commonType; _operation.annotation().isPure = diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index b84f9730..d819402e 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -237,7 +237,7 @@ class PragmaDirective: public ASTNode public: PragmaDirective( SourceLocation const& _location, - std::vector<Token::Value> const& _tokens, + std::vector<Token> const& _tokens, std::vector<ASTString> const& _literals ): ASTNode(_location), m_tokens(_tokens), m_literals(_literals) {} @@ -245,13 +245,13 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - std::vector<Token::Value> const& tokens() const { return m_tokens; } + std::vector<Token> const& tokens() const { return m_tokens; } std::vector<ASTString> const& literals() const { return m_literals; } private: /// Sequence of tokens following the "pragma" keyword. - std::vector<Token::Value> m_tokens; + std::vector<Token> m_tokens; /// Sequence of literals following the "pragma" keyword. std::vector<ASTString> m_literals; }; @@ -1379,7 +1379,7 @@ public: Assignment( SourceLocation const& _location, ASTPointer<Expression> const& _leftHandSide, - Token::Value _assignmentOperator, + Token _assignmentOperator, ASTPointer<Expression> const& _rightHandSide ): Expression(_location), @@ -1387,18 +1387,18 @@ public: m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) { - solAssert(Token::isAssignmentOp(_assignmentOperator), ""); + solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& leftHandSide() const { return *m_leftHandSide; } - Token::Value assignmentOperator() const { return m_assigmentOperator; } + Token assignmentOperator() const { return m_assigmentOperator; } Expression const& rightHandSide() const { return *m_rightHandSide; } private: ASTPointer<Expression> m_leftHandSide; - Token::Value m_assigmentOperator; + Token m_assigmentOperator; ASTPointer<Expression> m_rightHandSide; }; @@ -1441,7 +1441,7 @@ class UnaryOperation: public Expression public: UnaryOperation( SourceLocation const& _location, - Token::Value _operator, + Token _operator, ASTPointer<Expression> const& _subExpression, bool _isPrefix ): @@ -1450,17 +1450,17 @@ public: m_subExpression(_subExpression), m_isPrefix(_isPrefix) { - solAssert(Token::isUnaryOp(_operator), ""); + solAssert(TokenTraits::isUnaryOp(_operator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - Token::Value getOperator() const { return m_operator; } + Token getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } Expression const& subExpression() const { return *m_subExpression; } private: - Token::Value m_operator; + Token m_operator; ASTPointer<Expression> m_subExpression; bool m_isPrefix; }; @@ -1475,25 +1475,25 @@ public: BinaryOperation( SourceLocation const& _location, ASTPointer<Expression> const& _left, - Token::Value _operator, + Token _operator, ASTPointer<Expression> const& _right ): Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) { - solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), ""); + solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& leftExpression() const { return *m_left; } Expression const& rightExpression() const { return *m_right; } - Token::Value getOperator() const { return m_operator; } + Token getOperator() const { return m_operator; } BinaryOperationAnnotation& annotation() const override; private: ASTPointer<Expression> m_left; - Token::Value m_operator; + Token m_operator; ASTPointer<Expression> m_right; }; @@ -1653,21 +1653,21 @@ class Literal: public PrimaryExpression public: enum class SubDenomination { - None = Token::Illegal, - Wei = Token::SubWei, - Szabo = Token::SubSzabo, - Finney = Token::SubFinney, - Ether = Token::SubEther, - Second = Token::SubSecond, - Minute = Token::SubMinute, - Hour = Token::SubHour, - Day = Token::SubDay, - Week = Token::SubWeek, - Year = Token::SubYear + None = static_cast<int>(Token::Illegal), + Wei = static_cast<int>(Token::SubWei), + Szabo = static_cast<int>(Token::SubSzabo), + Finney = static_cast<int>(Token::SubFinney), + Ether = static_cast<int>(Token::SubEther), + Second = static_cast<int>(Token::SubSecond), + Minute = static_cast<int>(Token::SubMinute), + Hour = static_cast<int>(Token::SubHour), + Day = static_cast<int>(Token::SubDay), + Week = static_cast<int>(Token::SubWeek), + Year = static_cast<int>(Token::SubYear) }; Literal( SourceLocation const& _location, - Token::Value _token, + Token _token, ASTPointer<ASTString> const& _value, SubDenomination _sub = SubDenomination::None ): @@ -1675,7 +1675,7 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - Token::Value token() const { return m_token; } + Token token() const { return m_token; } /// @returns the non-parsed value of the literal ASTString const& value() const { return *m_value; } @@ -1694,7 +1694,7 @@ public: std::string getChecksummedAddress() const; private: - Token::Value m_token; + Token m_token; ASTPointer<ASTString> m_value; SubDenomination m_subDenomination; }; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 4b282d85..2d26ce8a 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -459,7 +459,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node) if (it.first) { Json::Value tuple(Json::objectValue); - tuple[it.first->name] = inlineAssemblyIdentifierToJson(it); + tuple[it.first->name.str()] = inlineAssemblyIdentifierToJson(it); externalReferences.append(tuple); } } @@ -589,7 +589,7 @@ bool ASTJsonConverter::visit(Conditional const& _node) bool ASTJsonConverter::visit(Assignment const& _node) { std::vector<pair<string, Json::Value>> attributes = { - make_pair("operator", Token::toString(_node.assignmentOperator())), + make_pair("operator", TokenTraits::toString(_node.assignmentOperator())), make_pair("leftHandSide", toJson(_node.leftHandSide())), make_pair("rightHandSide", toJson(_node.rightHandSide())) }; @@ -613,7 +613,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node) { std::vector<pair<string, Json::Value>> attributes = { make_pair("prefix", _node.isPrefixOperation()), - make_pair("operator", Token::toString(_node.getOperator())), + make_pair("operator", TokenTraits::toString(_node.getOperator())), make_pair("subExpression", toJson(_node.subExpression())) }; appendExpressionAttributes(attributes, _node.annotation()); @@ -624,7 +624,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node) bool ASTJsonConverter::visit(BinaryOperation const& _node) { std::vector<pair<string, Json::Value>> attributes = { - make_pair("operator", Token::toString(_node.getOperator())), + make_pair("operator", TokenTraits::toString(_node.getOperator())), make_pair("leftExpression", toJson(_node.leftExpression())), make_pair("rightExpression", toJson(_node.rightExpression())), make_pair("commonType", typePointerToJson(_node.annotation().commonType)), @@ -719,7 +719,7 @@ bool ASTJsonConverter::visit(Literal const& _node) Json::Value value{_node.value()}; if (!dev::validateUTF8(_node.value())) value = Json::nullValue; - Token::Value subdenomination = Token::Value(_node.subDenomination()); + Token subdenomination = Token(_node.subDenomination()); std::vector<pair<string, Json::Value>> attributes = { make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())), make_pair("value", value), @@ -728,7 +728,7 @@ bool ASTJsonConverter::visit(Literal const& _node) "subdenomination", subdenomination == Token::Illegal ? Json::nullValue : - Json::Value{Token::toString(subdenomination)} + Json::Value{TokenTraits::toString(subdenomination)} ) }; appendExpressionAttributes(attributes, _node.annotation()); @@ -790,7 +790,7 @@ string ASTJsonConverter::functionCallKind(FunctionCallKind _kind) } } -string ASTJsonConverter::literalTokenKind(Token::Value _token) +string ASTJsonConverter::literalTokenKind(Token _token) { switch (_token) { diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 29712f3b..8429708c 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -134,7 +134,7 @@ private: static std::string location(VariableDeclaration::Location _location); static std::string contractKind(ContractDefinition::ContractKind _kind); static std::string functionCallKind(FunctionCallKind _kind); - static std::string literalTokenKind(Token::Value _token); + static std::string literalTokenKind(Token _token); static std::string type(Expression const& _expression); static std::string type(VariableDeclaration const& _varDecl); static int nodeId(ASTNode const& _node) diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 4273f225..255cb9be 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -289,7 +289,7 @@ bool ASTPrinter::visit(Conditional const& _node) bool ASTPrinter::visit(Assignment const& _node) { - writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator())); + writeLine(string("Assignment using operator ") + TokenTraits::toString(_node.assignmentOperator())); printType(_node); printSourcePart(_node); return goDeeper(); @@ -306,7 +306,7 @@ bool ASTPrinter::visit(TupleExpression const& _node) bool ASTPrinter::visit(UnaryOperation const& _node) { writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + - ") " + Token::toString(_node.getOperator())); + ") " + TokenTraits::toString(_node.getOperator())); printType(_node); printSourcePart(_node); return goDeeper(); @@ -314,7 +314,7 @@ bool ASTPrinter::visit(UnaryOperation const& _node) bool ASTPrinter::visit(BinaryOperation const& _node) { - writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); + writeLine(string("BinaryOperation using operator ") + TokenTraits::toString(_node.getOperator())); printType(_node); printSourcePart(_node); return goDeeper(); @@ -370,7 +370,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node) bool ASTPrinter::visit(Literal const& _node) { - char const* tokenString = Token::toString(_node.token()); + char const* tokenString = TokenTraits::toString(_node.token()); if (!tokenString) tokenString = "[no token]"; writeLine(string("Literal, token: ") + tokenString + " value: " + _node.value()); diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp deleted file mode 100644 index 7d180490..00000000 --- a/libsolidity/ast/ASTUtils.cpp +++ /dev/null @@ -1,48 +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/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2015 - * Utilities to work with the AST. - */ - -#include <libsolidity/ast/ASTUtils.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - - - -ASTNode const* LocationFinder::leastUpperBound() -{ - m_bestMatch = nullptr; - for (ASTNode const* rootNode: m_rootNodes) - rootNode->accept(*this); - - return m_bestMatch; -} - -bool LocationFinder::visitNode(const ASTNode& _node) -{ - if (_node.location().contains(m_location)) - { - m_bestMatch = &_node; - return true; - } - return false; -} diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h deleted file mode 100644 index f7cacf3e..00000000 --- a/libsolidity/ast/ASTUtils.h +++ /dev/null @@ -1,54 +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/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2015 - * Utilities to work with the AST. - */ - -#pragma once - -#include <libevmasm/SourceLocation.h> -#include <libsolidity/ast/ASTVisitor.h> - -namespace dev -{ -namespace solidity -{ - -class LocationFinder: private ASTConstVisitor -{ -public: - LocationFinder(SourceLocation const& _location, std::vector<ASTNode const*> _rootNodes): - m_rootNodes(_rootNodes), m_location(_location) - { - } - - /// @returns the "closest" (in the sense of most-leafward) AST node which is a descendant of - /// _node and whose source location contains _location. - ASTNode const* leastUpperBound(); - -private: - bool visitNode(ASTNode const& _node); - - std::vector<ASTNode const*> m_rootNodes; - SourceLocation m_location; - ASTNode const* m_bestMatch = nullptr; -}; - -} -} diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 4094af63..4b31d2e8 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -268,11 +268,11 @@ string Type::identifier() const TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { - solAssert(Token::isElementaryTypeName(_type.token()), + solAssert(TokenTraits::isElementaryTypeName(_type.token()), "Expected an elementary type name but got " + _type.toString() ); - Token::Value token = _type.token(); + Token token = _type.token(); unsigned m = _type.firstNumber(); unsigned n = _type.secondNumber(); @@ -320,9 +320,9 @@ TypePointer Type::fromElementaryTypeName(string const& _name) vector<string> nameParts; boost::split(nameParts, _name, boost::is_any_of(" ")); solAssert(nameParts.size() == 1 || nameParts.size() == 2, "Cannot parse elementary type: " + _name); - Token::Value token; + Token token; unsigned short firstNum, secondNum; - tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(nameParts[0]); + tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(nameParts[0]); auto t = fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum)); if (auto* ref = dynamic_cast<ReferenceType const*>(t.get())) { @@ -502,16 +502,16 @@ u256 AddressType::literalValue(Literal const* _literal) const return u256(_literal->valueWithoutUnderscores()); } -TypePointer AddressType::unaryOperatorResult(Token::Value _operator) const +TypePointer AddressType::unaryOperatorResult(Token _operator) const { return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } -TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { // Addresses can only be compared. - if (!Token::isCompareOp(_operator)) + if (!TokenTraits::isCompareOp(_operator)) return TypePointer(); return Type::commonType(shared_from_this(), _other); @@ -545,7 +545,7 @@ MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) cons namespace { -bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountType) +bool isValidShiftAndAmountType(Token _operator, Type const& _shiftAmountType) { // Disable >>> here. if (_operator == Token::SHR) @@ -605,7 +605,7 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const _convertTo.category() == Category::FixedPoint; } -TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const +TypePointer IntegerType::unaryOperatorResult(Token _operator) const { // "delete" is ok for all integer types if (_operator == Token::Delete) @@ -649,7 +649,7 @@ bigint IntegerType::maxValue() const return (bigint(1) << m_bits) - 1; } -TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if ( _other->category() != Category::RationalNumber && @@ -657,7 +657,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe _other->category() != category() ) return TypePointer(); - if (Token::isShiftOp(_operator)) + if (TokenTraits::isShiftOp(_operator)) { // Shifts are not symmetric with respect to the type if (isValidShiftAndAmountType(_operator, *_other)) @@ -671,9 +671,9 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe return TypePointer(); // All integer types can be compared - if (Token::isCompareOp(_operator)) + if (TokenTraits::isCompareOp(_operator)) return commonType; - if (Token::isBooleanOp(_operator)) + if (TokenTraits::isBooleanOp(_operator)) return TypePointer(); if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType)) { @@ -720,7 +720,7 @@ bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const return _convertTo.category() == category() || _convertTo.category() == Category::Integer; } -TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const +TypePointer FixedPointType::unaryOperatorResult(Token _operator) const { switch(_operator) { @@ -769,7 +769,7 @@ bigint FixedPointType::minIntegerValue() const return bigint(0); } -TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { auto commonType = Type::commonType(shared_from_this(), _other); @@ -777,9 +777,9 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi return TypePointer(); // All fixed types can be compared - if (Token::isCompareOp(_operator)) + if (TokenTraits::isCompareOp(_operator)) return commonType; - if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator) || _operator == Token::Exp) + if (TokenTraits::isBitOp(_operator) || TokenTraits::isBooleanOp(_operator) || _operator == Token::Exp) return TypePointer(); return commonType; } @@ -1006,7 +1006,7 @@ bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const return false; } -TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const +TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const { rational value; switch (_operator) @@ -1030,7 +1030,7 @@ TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) cons return make_shared<RationalNumberType>(value); } -TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) { @@ -1043,7 +1043,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ return TypePointer(); RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(*_other); - if (Token::isCompareOp(_operator)) + if (TokenTraits::isCompareOp(_operator)) { // Since we do not have a "BoolConstantType", we have to do the actual comparison // at runtime and convert to mobile typse first. Such a comparison is not a very common @@ -1144,10 +1144,10 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ bigint denominator = optimizedPow(m_value.denominator(), absExp); if (exp >= 0) - value = rational(numerator, denominator); + value = makeRational(numerator, denominator); else // invert - value = rational(denominator, numerator); + value = makeRational(denominator, numerator); } break; } @@ -1423,7 +1423,7 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const _convertTo.category() == category(); } -TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const +TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const { // "delete" and "~" is okay for FixedBytesType if (_operator == Token::Delete) @@ -1434,9 +1434,9 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const return TypePointer(); } -TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { - if (Token::isShiftOp(_operator)) + if (TokenTraits::isShiftOp(_operator)) { if (isValidShiftAndAmountType(_operator, *_other)) return shared_from_this(); @@ -1449,7 +1449,7 @@ TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePoi return TypePointer(); // FixedBytes can be compared and have bitwise operators applied to them - if (Token::isCompareOp(_operator) || Token::isBitOp(_operator)) + if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator)) return commonType; return TypePointer(); @@ -1484,14 +1484,14 @@ u256 BoolType::literalValue(Literal const* _literal) const solAssert(false, "Bool type constructed from non-boolean literal."); } -TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const +TypePointer BoolType::unaryOperatorResult(Token _operator) const { if (_operator == Token::Delete) return make_shared<TupleType>(); return (_operator == Token::Not) ? shared_from_this() : TypePointer(); } -TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (category() != _other->category()) return TypePointer(); @@ -1531,14 +1531,14 @@ bool ContractType::isPayable() const return fallbackFunction && fallbackFunction->isPayable(); } -TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const +TypePointer ContractType::unaryOperatorResult(Token _operator) const { if (isSuper()) return TypePointer{}; return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } -TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const +TypePointer ReferenceType::unaryOperatorResult(Token _operator) const { if (_operator != Token::Delete) return TypePointer(); @@ -1698,6 +1698,9 @@ bool ArrayType::operator==(Type const& _other) const bool ArrayType::validForCalldata() const { + if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType().get())) + if (!arrayBaseType->validForCalldata()) + return false; return unlimitedCalldataEncodedSize(true) <= numeric_limits<unsigned>::max(); } @@ -2237,7 +2240,7 @@ bool StructType::recursive() const return *m_recursive; } -TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const +TypePointer EnumType::unaryOperatorResult(Token _operator) const { return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } @@ -2668,14 +2671,14 @@ bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const return true; } -TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const +TypePointer FunctionType::unaryOperatorResult(Token _operator) const { - if (_operator == Token::Value::Delete) + if (_operator == Token::Delete) return make_shared<TupleType>(); return TypePointer(); } -TypePointer FunctionType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual)) return TypePointer(); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 0f3373a1..24ace447 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -51,6 +51,15 @@ using FunctionTypePointer = std::shared_ptr<FunctionType const>; using TypePointers = std::vector<TypePointer>; using rational = boost::rational<dev::bigint>; +inline rational makeRational(bigint const& _numerator, bigint const& _denominator) +{ + solAssert(_denominator != 0, "division by zero"); + // due to a bug in certain versions of boost the denominator has to be positive + if (_denominator < 0) + return rational(-_numerator, -_denominator); + else + return rational(_numerator, _denominator); +} enum class DataLocation { Storage, CallData, Memory }; @@ -188,13 +197,13 @@ public: /// @returns the resulting type of applying the given unary operator or an empty pointer if /// this is not possible. /// The default implementation does not allow any unary operator. - virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); } + virtual TypePointer unaryOperatorResult(Token) const { return TypePointer(); } /// @returns the resulting type of applying the given binary operator or an empty pointer if /// this is not possible. /// The default implementation allows comparison operators if a common type exists - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const { - return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer(); + return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer(); } virtual bool operator==(Type const& _other) const { return category() == _other.category(); } @@ -329,8 +338,8 @@ public: virtual std::string richIdentifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual bool operator==(Type const& _other) const override; @@ -372,8 +381,8 @@ public: virtual std::string richIdentifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual bool operator==(Type const& _other) const override; @@ -414,8 +423,8 @@ public: virtual std::string richIdentifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual bool operator==(Type const& _other) const override; @@ -468,8 +477,8 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; @@ -526,7 +535,7 @@ public: explicit StringLiteralType(Literal const& _literal); virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -563,8 +572,8 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } virtual unsigned storageBytes() const override { return m_bytes; } @@ -590,8 +599,8 @@ public: BoolType() {} virtual Category category() const override { return Category::Bool; } virtual std::string richIdentifier() const override { return "t_bool"; } - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; } virtual unsigned storageBytes() const override { return 1; } @@ -613,8 +622,8 @@ public: explicit ReferenceType(DataLocation _location): m_location(_location) {} DataLocation location() const { return m_location; } - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -749,7 +758,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; /// Contracts can only be explicitly converted to address types and base contracts. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override @@ -865,7 +874,7 @@ class EnumType: public Type public: virtual Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override @@ -909,7 +918,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual std::string toString(bool) const override; virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; @@ -1056,8 +1065,8 @@ public: virtual bool operator==(Type const& _other) const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override; virtual std::string canonicalName() const override; virtual std::string toString(bool _short) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; @@ -1187,7 +1196,7 @@ public: virtual std::string toString(bool _short) const override; virtual std::string canonicalName() const override; virtual bool canLiveOutsideStorage() const override { return false; } - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual TypePointer encodingType() const override { return std::make_shared<IntegerType>(256); @@ -1220,7 +1229,7 @@ public: explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {} TypePointer const& actualType() const { return m_actualType; } - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -1245,7 +1254,7 @@ public: virtual Category category() const override { return Category::Modifier; } explicit ModifierType(ModifierDefinition const& _modifier); - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } @@ -1271,7 +1280,7 @@ public: explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {} - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -1298,7 +1307,7 @@ public: explicit MagicType(Kind _kind): m_kind(_kind) {} - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -1331,7 +1340,7 @@ public: virtual std::string richIdentifier() const override { return "t_inaccessible"; } virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; } diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 210b613d..6e14d68a 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -32,6 +32,7 @@ #include <libsolidity/inlineasm/AsmCodeGen.h> #include <libsolidity/inlineasm/AsmAnalysis.h> #include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libyul/YulString.h> #include <boost/algorithm/string/replace.hpp> @@ -326,7 +327,7 @@ void CompilerContext::appendInlineAssembly( bool ) { - auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); + auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str()); return it == _localVariables.end() ? size_t(-1) : 1; }; identifierAccess.generateCode = [&]( @@ -335,7 +336,7 @@ void CompilerContext::appendInlineAssembly( yul::AbstractAssembly& _assembly ) { - auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); + auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str()); solAssert(it != _localVariables.end(), ""); int stackDepth = _localVariables.end() - it; int stackDiff = _assembly.stackHeight() - startStackHeight + stackDepth; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 2bdf88e3..90eb74fe 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -142,9 +142,13 @@ void CompilerUtils::storeInMemory(unsigned _offset) void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries) { + // process special types (Reference, StringLiteral, Function) if (auto ref = dynamic_cast<ReferenceType const*>(&_type)) { - solUnimplementedAssert(ref->location() == DataLocation::Memory, "Only in-memory reference type can be stored."); + solUnimplementedAssert( + ref->location() == DataLocation::Memory, + "Only in-memory reference type can be stored." + ); storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries); } else if (auto str = dynamic_cast<StringLiteralType const*>(&_type)) @@ -166,18 +170,18 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound m_context << Instruction::DUP2 << Instruction::MSTORE; m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD; } - else + else if (_type.isValueType()) { unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries); - if (numBytes > 0) - { - solUnimplementedAssert( - _type.sizeOnStack() == 1, - "Memory store of types with stack size != 1 not implemented." - ); - m_context << Instruction::DUP2 << Instruction::MSTORE; - m_context << u256(numBytes) << Instruction::ADD; - } + m_context << Instruction::DUP2 << Instruction::MSTORE; + m_context << u256(numBytes) << Instruction::ADD; + } + else // Should never happen + { + solAssert( + false, + "Memory store of type " + _type.toString(true) + " not allowed." + ); } } @@ -1012,8 +1016,22 @@ void CompilerUtils::convertType( } else { - // All other types should not be convertible to non-equal types. - solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function) + { + FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack); + FunctionType const& targetType = dynamic_cast<FunctionType const&>(_targetType); + solAssert( + typeOnStack.isImplicitlyConvertibleTo(targetType) && + typeOnStack.sizeOnStack() == targetType.sizeOnStack() && + (typeOnStack.kind() == FunctionType::Kind::Internal || typeOnStack.kind() == FunctionType::Kind::External) && + typeOnStack.kind() == targetType.kind(), + "Invalid function type conversion requested." + ); + } + else + // All other types should not be convertible to non-equal types. + solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32) m_context << ((u256(1) << (8 * _targetType.storageBytes())) - 1) @@ -1266,18 +1284,30 @@ void CompilerUtils::rightShiftNumberOnStack(unsigned _bits) unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords) { + solAssert( + _type.sizeOnStack() == 1, + "Memory store of types with stack size != 1 not allowed (Type: " + _type.toString(true) + ")." + ); + unsigned numBytes = _type.calldataEncodedSize(_padToWords); + + solAssert( + numBytes > 0, + "Memory store of 0 bytes requested (Type: " + _type.toString(true) + ")." + ); + + solAssert( + numBytes <= 32, + "Memory store of more than 32 bytes requested (Type: " + _type.toString(true) + ")." + ); + bool leftAligned = _type.category() == Type::Category::FixedBytes; - if (numBytes == 0) - m_context << Instruction::POP; - else - { - solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested."); - convertType(_type, _type, true); - if (numBytes != 32 && !leftAligned && !_padToWords) - // shift the value accordingly before storing - leftShiftNumberOnStack((32 - numBytes) * 8); - } + + convertType(_type, _type, true); + if (numBytes != 32 && !leftAligned && !_padToWords) + // shift the value accordingly before storing + leftShiftNumberOnStack((32 - numBytes) * 8); + return numBytes; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 3383d8ae..bdf91fbf 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -206,8 +206,8 @@ bool ExpressionCompiler::visit(Conditional const& _condition) bool ExpressionCompiler::visit(Assignment const& _assignment) { CompilerContext::LocationSetter locationSetter(m_context, _assignment); - Token::Value op = _assignment.assignmentOperator(); - Token::Value binOp = op == Token::Assign ? op : Token::AssignmentToBinaryOp(op); + Token op = _assignment.assignmentOperator(); + Token binOp = op == Token::Assign ? op : TokenTraits::AssignmentToBinaryOp(op); Type const& leftType = *_assignment.leftHandSide().annotation().type; if (leftType.category() == Type::Category::Tuple) { @@ -223,7 +223,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) // Perform some conversion already. This will convert storage types to memory and literals // to their actual type, but will not convert e.g. memory to storage. TypePointer rightIntermediateType; - if (op != Token::Assign && Token::isShiftOp(binOp)) + if (op != Token::Assign && TokenTraits::isShiftOp(binOp)) rightIntermediateType = _assignment.rightHandSide().annotation().type->mobileType(); else rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType( @@ -251,7 +251,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) m_currentLValue->retrieveValue(_assignment.location(), true); utils().convertType(leftType, leftType, cleanupNeeded); - if (Token::isShiftOp(binOp)) + if (TokenTraits::isShiftOp(binOp)) appendShiftOperatorCode(binOp, leftType, *rightIntermediateType); else { @@ -384,7 +384,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) m_context << u256(0) << Instruction::SUB; break; default: - solAssert(false, "Invalid unary operator: " + string(Token::toString(_unaryOperation.getOperator()))); + solAssert(false, "Invalid unary operator: " + string(TokenTraits::toString(_unaryOperation.getOperator()))); } return false; } @@ -396,7 +396,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) Expression const& rightExpression = _binaryOperation.rightExpression(); solAssert(!!_binaryOperation.annotation().commonType, ""); TypePointer const& commonType = _binaryOperation.annotation().commonType; - Token::Value const c_op = _binaryOperation.getOperator(); + Token const c_op = _binaryOperation.getOperator(); if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); @@ -407,7 +407,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op); TypePointer leftTargetType = commonType; - TypePointer rightTargetType = Token::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType; + TypePointer rightTargetType = TokenTraits::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType; solAssert(rightTargetType, ""); // for commutative operators, push the literal as late as possible to allow improved optimization @@ -415,7 +415,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) { return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber; }; - bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); + bool swap = m_optimize && TokenTraits::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) { leftExpression.accept(*this); @@ -430,10 +430,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) leftExpression.accept(*this); utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded); } - if (Token::isShiftOp(c_op)) + if (TokenTraits::isShiftOp(c_op)) // shift only cares about the signedness of both sides appendShiftOperatorCode(c_op, *leftTargetType, *rightTargetType); - else if (Token::isCompareOp(c_op)) + else if (TokenTraits::isCompareOp(c_op)) appendCompareOperatorCode(c_op, *commonType); else appendOrdinaryBinaryOperatorCode(c_op, *commonType); @@ -568,12 +568,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case FunctionType::Kind::External: case FunctionType::Kind::DelegateCall: case FunctionType::Kind::BareCall: - case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::BareStaticCall: _functionCall.expression().accept(*this); appendExternalFunctionCall(function, arguments); break; + case FunctionType::Kind::BareCallCode: + solAssert(false, "Callcode has been removed."); case FunctionType::Kind::Creation: { _functionCall.expression().accept(*this); @@ -1328,8 +1329,6 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::CALLVALUE; else if (member == "origin") m_context << Instruction::ORIGIN; - else if (member == "gas") - m_context << Instruction::GAS; else if (member == "gasprice") m_context << Instruction::GASPRICE; else if (member == "data") @@ -1337,9 +1336,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "sig") m_context << u256(0) << Instruction::CALLDATALOAD << (u256(0xffffffff) << (256 - 32)) << Instruction::AND; + else if (member == "gas") + solAssert(false, "Gas has been removed."); else if (member == "blockhash") - { - } + solAssert(false, "Blockhash has been removed."); else solAssert(false, "Unknown magic member."); break; @@ -1602,7 +1602,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation) { - Token::Value const c_op = _binaryOperation.getOperator(); + Token const c_op = _binaryOperation.getOperator(); solAssert(c_op == Token::Or || c_op == Token::And, ""); _binaryOperation.leftExpression().accept(*this); @@ -1615,7 +1615,7 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO m_context << endLabel; } -void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type) +void ExpressionCompiler::appendCompareOperatorCode(Token _operator, Type const& _type) { solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types."); if (_operator == Token::Equal || _operator == Token::NotEqual) @@ -1665,17 +1665,17 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type } } -void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type) +void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token _operator, Type const& _type) { - if (Token::isArithmeticOp(_operator)) + if (TokenTraits::isArithmeticOp(_operator)) appendArithmeticOperatorCode(_operator, _type); - else if (Token::isBitOp(_operator)) + else if (TokenTraits::isBitOp(_operator)) appendBitOperatorCode(_operator); else solAssert(false, "Unknown binary operator."); } -void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type) +void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type const& _type) { if (_type.category() == Type::Category::FixedPoint) solUnimplemented("Not yet implemented - FixedPointType."); @@ -1715,7 +1715,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty } } -void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) +void ExpressionCompiler::appendBitOperatorCode(Token _operator) { switch (_operator) { @@ -1733,7 +1733,7 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) } } -void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType) +void ExpressionCompiler::appendShiftOperatorCode(Token _operator, Type const& _valueType, Type const& _shiftAmountType) { // stack: shift_amount value_to_shift @@ -1844,8 +1844,9 @@ void ExpressionCompiler::appendExternalFunctionCall( solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); - bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall; - bool isCallCode = funKind == FunctionType::Kind::BareCallCode; + solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); + + bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall; bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall()); @@ -1930,7 +1931,7 @@ void ExpressionCompiler::appendExternalFunctionCall( parameterTypes, _functionType.padArguments(), _functionType.takesArbitraryParameters() || _functionType.isBareCall(), - isCallCode || isDelegateCall + isDelegateCall ); // Stack now: @@ -2001,8 +2002,6 @@ void ExpressionCompiler::appendExternalFunctionCall( // Order is important here, STATICCALL might overlap with DELEGATECALL. if (isDelegateCall) m_context << Instruction::DELEGATECALL; - else if (isCallCode) - m_context << Instruction::CALLCODE; else if (useStaticCall) m_context << Instruction::STATICCALL; else @@ -2141,9 +2140,9 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue<StorageItem>(_expression, *_expression.annotation().type); } -bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) +bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op) { - if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) + if (TokenTraits::isCompareOp(_op) || TokenTraits::isShiftOp(_op)) return true; else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod || _op == Token::Exp)) // We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0 diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index cdfa096e..3d8e8682 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -86,12 +86,12 @@ private: ///@{ ///@name Append code for various operator types void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation); - void appendCompareOperatorCode(Token::Value _operator, Type const& _type); - void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type); + void appendCompareOperatorCode(Token _operator, Type const& _type); + void appendOrdinaryBinaryOperatorCode(Token _operator, Type const& _type); - void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type); - void appendBitOperatorCode(Token::Value _operator); - void appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType); + void appendArithmeticOperatorCode(Token _operator, Type const& _type); + void appendBitOperatorCode(Token _operator); + void appendShiftOperatorCode(Token _operator, Type const& _valueType, Type const& _shiftAmountType); /// @} /// Appends code to call a function of the given type with the given arguments. @@ -119,7 +119,7 @@ private: /// @returns true if the operator applied to the given type requires a cleanup prior to the /// operation. - static bool cleanupNeededForOp(Type::Category _type, Token::Value _op); + static bool cleanupNeededForOp(Type::Category _type, Token _op); /// @returns the CompilerUtils object containing the current context. CompilerUtils utils(); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 77684683..790ab309 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -134,8 +134,7 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const { CompilerUtils utils(m_context); - if (!_removeReference) - m_context << Instruction::DUP1; + solAssert(_removeReference, ""); utils.pushZeroValue(*m_dataType); utils.storeInMemoryDynamic(*m_dataType, m_padded); m_context << Instruction::POP; @@ -460,8 +459,7 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeReference) const { // stack: ref byte_number - if (!_removeReference) - m_context << Instruction::DUP2 << Instruction::DUP2; + solAssert(_removeReference, ""); m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP; // stack: ref (1<<(8*(31-byte_number))) m_context << Instruction::DUP2 << Instruction::SLOAD; @@ -498,8 +496,7 @@ void StorageArrayLength::storeValue(Type const&, SourceLocation const&, bool _mo void StorageArrayLength::setToZero(SourceLocation const&, bool _removeReference) const { - if (!_removeReference) - m_context << Instruction::DUP1; + solAssert(_removeReference, ""); ArrayUtils(m_context).clearDynamicArray(m_arrayType); } @@ -521,24 +518,9 @@ unsigned TupleObject::sizeOnStack() const return size; } -void TupleObject::retrieveValue(SourceLocation const& _location, bool _remove) const +void TupleObject::retrieveValue(SourceLocation const&, bool) const { - unsigned initialDepth = sizeOnStack(); - unsigned initialStack = m_context.stackHeight(); - for (auto const& lv: m_lvalues) - if (lv) - { - solAssert(initialDepth + m_context.stackHeight() >= initialStack, ""); - unsigned depth = initialDepth + m_context.stackHeight() - initialStack; - if (lv->sizeOnStack() > 0) - { - if (_remove && depth > lv->sizeOnStack()) - CompilerUtils(m_context).moveToStackTop(depth, depth - lv->sizeOnStack()); - else if (!_remove && depth > 0) - CompilerUtils(m_context).copyToStackTop(depth, lv->sizeOnStack()); - } - lv->retrieveValue(_location, true); - } + solAssert(false, "Tried to retrieve value of tuple."); } void TupleObject::storeValue(Type const& _sourceType, SourceLocation const& _location, bool) const @@ -569,24 +551,7 @@ void TupleObject::storeValue(Type const& _sourceType, SourceLocation const& _loc CompilerUtils(m_context).popStackElement(_sourceType); } -void TupleObject::setToZero(SourceLocation const& _location, bool _removeReference) const +void TupleObject::setToZero(SourceLocation const&, bool) const { - if (_removeReference) - { - for (size_t i = 0; i < m_lvalues.size(); ++i) - if (m_lvalues[m_lvalues.size() - i]) - m_lvalues[m_lvalues.size() - i]->setToZero(_location, true); - } - else - { - unsigned depth = sizeOnStack(); - for (auto const& val: m_lvalues) - if (val) - { - if (val->sizeOnStack() > 0) - CompilerUtils(m_context).copyToStackTop(depth, val->sizeOnStack()); - val->setToZero(_location, false); - depth -= val->sizeOnStack(); - } - } + solAssert(false, "Tried to delete tuple."); } diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index e5648eb3..51b310ae 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -19,8 +19,6 @@ #include <libsolidity/formal/SMTPortfolio.h> -#include <libsolidity/formal/SSAVariable.h> -#include <libsolidity/formal/SymbolicIntVariable.h> #include <libsolidity/formal/VariableUsage.h> #include <libsolidity/formal/SymbolicTypes.h> @@ -212,7 +210,7 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl) void SMTChecker::endVisit(Assignment const& _assignment) { - if (_assignment.assignmentOperator() != Token::Value::Assign) + if (_assignment.assignmentOperator() != Token::Assign) m_errorReporter.warning( _assignment.location(), "Assertion checker does not yet implement compound assignment." @@ -331,11 +329,11 @@ void SMTChecker::endVisit(UnaryOperation const& _op) void SMTChecker::endVisit(BinaryOperation const& _op) { - if (Token::isArithmeticOp(_op.getOperator())) + if (TokenTraits::isArithmeticOp(_op.getOperator())) arithmeticOperation(_op); - else if (Token::isCompareOp(_op.getOperator())) + else if (TokenTraits::isCompareOp(_op.getOperator())) compareOperation(_op); - else if (Token::isBooleanOp(_op.getOperator())) + else if (TokenTraits::isBooleanOp(_op.getOperator())) booleanOperation(_op); else m_errorReporter.warning( @@ -363,6 +361,10 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) visitAssert(_funCall); else if (funType.kind() == FunctionType::Kind::Require) visitRequire(_funCall); + else if (funType.kind() == FunctionType::Kind::GasLeft) + visitGasLeft(_funCall); + else if (funType.kind() == FunctionType::Kind::BlockHash) + visitBlockHash(_funCall); else if (funType.kind() == FunctionType::Kind::Internal) inlineFunctionCall(_funCall); else @@ -393,6 +395,27 @@ void SMTChecker::visitRequire(FunctionCall const& _funCall) addPathImpliedExpression(expr(*args[0])); } +void SMTChecker::visitGasLeft(FunctionCall const& _funCall) +{ + string gasLeft = "gasleft()"; + // We increase the variable index since gasleft changes + // inside a tx. + defineSpecialVariable(gasLeft, _funCall, true); + auto const& symbolicVar = m_specialVariables.at(gasLeft); + unsigned index = symbolicVar->index(); + // We set the current value to unknown anyway to add type constraints. + symbolicVar->setUnknownValue(); + if (index > 0) + m_interface->addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1)); +} + +void SMTChecker::visitBlockHash(FunctionCall const& _funCall) +{ + string blockHash = "blockhash()"; + // TODO Define blockhash as an uninterpreted function + defineSpecialVariable(blockHash, _funCall); +} + void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall) { FunctionDefinition const* _funDef = nullptr; @@ -460,7 +483,12 @@ void SMTChecker::endVisit(Identifier const& _identifier) } else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get())) { - if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require) + if ( + fun->kind() == FunctionType::Kind::Assert || + fun->kind() == FunctionType::Kind::Require || + fun->kind() == FunctionType::Kind::GasLeft || + fun->kind() == FunctionType::Kind::BlockHash + ) return; createExpr(_identifier); } @@ -468,6 +496,8 @@ void SMTChecker::endVisit(Identifier const& _identifier) { if (VariableDeclaration const* decl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) defineExpr(_identifier, currentValue(*decl)); + else if (_identifier.name() == "now") + defineSpecialVariable(_identifier.name(), _identifier); else // TODO: handle MagicVariableDeclaration here m_errorReporter.warning( @@ -496,7 +526,7 @@ void SMTChecker::endVisit(Literal const& _literal) void SMTChecker::endVisit(Return const& _return) { - if (hasExpr(*_return.expression())) + if (knownExpr(*_return.expression())) { auto returnParams = m_functionPath.back()->returnParameters(); if (returnParams.size() > 1) @@ -509,6 +539,54 @@ void SMTChecker::endVisit(Return const& _return) } } +bool SMTChecker::visit(MemberAccess const& _memberAccess) +{ + auto const& exprType = _memberAccess.expression().annotation().type; + solAssert(exprType, ""); + if (exprType->category() == Type::Category::Magic) + { + auto identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression()); + string accessedName; + if (identifier) + accessedName = identifier->name(); + else + m_errorReporter.warning( + _memberAccess.location(), + "Assertion checker does not yet support this expression." + ); + defineSpecialVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess); + return false; + } + else + m_errorReporter.warning( + _memberAccess.location(), + "Assertion checker does not yet support this expression." + ); + + return true; +} + +void SMTChecker::defineSpecialVariable(string const& _name, Expression const& _expr, bool _increaseIndex) +{ + if (!knownSpecialVariable(_name)) + { + auto result = newSymbolicVariable(*_expr.annotation().type, _name, *m_interface); + m_specialVariables.emplace(_name, result.second); + result.second->setUnknownValue(); + if (result.first) + m_errorReporter.warning( + _expr.location(), + "Assertion checker does not yet support this special variable." + ); + } + else if (_increaseIndex) + m_specialVariables.at(_name)->increaseIndex(); + // The default behavior is not to increase the index since + // most of the special values stay the same throughout a tx. + defineExpr(_expr, m_specialVariables.at(_name)->currentValue()); +} + + void SMTChecker::arithmeticOperation(BinaryOperation const& _op) { switch (_op.getOperator()) @@ -530,7 +608,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) auto const& intType = dynamic_cast<IntegerType const&>(*_op.annotation().commonType); smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); - Token::Value op = _op.getOperator(); + Token op = _op.getOperator(); smt::Expression value( op == Token::Add ? left + right : op == Token::Sub ? left - right : @@ -564,7 +642,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op) { smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); - Token::Value op = _op.getOperator(); + Token op = _op.getOperator(); shared_ptr<smt::Expression> value; if (isNumber(_op.annotation().commonType->category())) { @@ -681,11 +759,15 @@ void SMTChecker::checkCondition( expressionNames.push_back(_additionalValueName); } for (auto const& var: m_variables) - if (knownVariable(*var.first)) - { - expressionsToEvaluate.emplace_back(currentValue(*var.first)); - expressionNames.push_back(var.first->name()); - } + { + expressionsToEvaluate.emplace_back(currentValue(*var.first)); + expressionNames.push_back(var.first->name()); + } + for (auto const& var: m_specialVariables) + { + expressionsToEvaluate.emplace_back(var.second->currentValue()); + expressionNames.push_back(var.first); + } } smt::CheckResult result; vector<string> values; @@ -910,7 +992,7 @@ void SMTChecker::mergeVariables(vector<VariableDeclaration const*> const& _varia bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { // This might be the case for multiple calls to the same function. - if (hasVariable(_varDecl)) + if (knownVariable(_varDecl)) return true; auto const& type = _varDecl.type(); solAssert(m_variables.count(&_varDecl) == 0, ""); @@ -927,11 +1009,6 @@ bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) return true; } -string SMTChecker::uniqueSymbol(Expression const& _expr) -{ - return "expr_" + to_string(_expr.id()); -} - bool SMTChecker::knownVariable(VariableDeclaration const& _decl) { return m_variables.count(&_decl); @@ -969,7 +1046,7 @@ void SMTChecker::setUnknownValue(VariableDeclaration const& _decl) smt::Expression SMTChecker::expr(Expression const& _e) { - if (!hasExpr(_e)) + if (!knownExpr(_e)) { m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." ); createExpr(_e); @@ -977,20 +1054,20 @@ smt::Expression SMTChecker::expr(Expression const& _e) return m_expressions.at(&_e)->currentValue(); } -bool SMTChecker::hasExpr(Expression const& _e) const +bool SMTChecker::knownExpr(Expression const& _e) const { return m_expressions.count(&_e); } -bool SMTChecker::hasVariable(VariableDeclaration const& _var) const +bool SMTChecker::knownSpecialVariable(string const& _var) const { - return m_variables.count(&_var); + return m_specialVariables.count(_var); } void SMTChecker::createExpr(Expression const& _e) { solAssert(_e.annotation().type, ""); - if (hasExpr(_e)) + if (knownExpr(_e)) m_expressions.at(&_e)->increaseIndex(); else { diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index f66693d2..376b73fd 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -19,7 +19,7 @@ #include <libsolidity/formal/SolverInterface.h> -#include <libsolidity/formal/SymbolicVariable.h> +#include <libsolidity/formal/SymbolicVariables.h> #include <libsolidity/ast/ASTVisitor.h> @@ -66,6 +66,7 @@ private: virtual void endVisit(Identifier const& _node) override; virtual void endVisit(Literal const& _node) override; virtual void endVisit(Return const& _node) override; + virtual bool visit(MemberAccess const& _node) override; void arithmeticOperation(BinaryOperation const& _op); void compareOperation(BinaryOperation const& _op); @@ -73,10 +74,14 @@ private: void visitAssert(FunctionCall const&); void visitRequire(FunctionCall const&); + void visitGasLeft(FunctionCall const&); + void visitBlockHash(FunctionCall const&); /// Visits the FunctionDefinition of the called function /// if available and inlines the return value. void inlineFunctionCall(FunctionCall const&); + void defineSpecialVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false); + /// Division expression in the given type. Requires special treatment because /// of rounding for signed division. smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type); @@ -129,8 +134,6 @@ private: /// This fails if the type is not supported. bool createVariable(VariableDeclaration const& _varDecl); - static std::string uniqueSymbol(Expression const& _expr); - /// @returns true if _delc is a variable that is known at the current point, i.e. /// has a valid index bool knownVariable(VariableDeclaration const& _decl); @@ -154,10 +157,13 @@ private: /// Creates the expression (value can be arbitrary) void createExpr(Expression const& _e); /// Checks if expression was created - bool hasExpr(Expression const& _e) const; + bool knownExpr(Expression const& _e) const; /// Creates the expression and sets its value. void defineExpr(Expression const& _e, smt::Expression _value); + /// Checks if special variable was seen. + bool knownSpecialVariable(std::string const& _var) const; + /// Adds a new path condition void pushPathCondition(smt::Expression const& _e); /// Remove the last path condition @@ -172,9 +178,6 @@ private: /// Removes local variables from the context. void removeLocalVariables(); - /// Checks if VariableDeclaration was seen. - bool hasVariable(VariableDeclaration const& _e) const; - /// Copy the SSA indices of m_variables. VariableIndices copyVariableIndices(); /// Resets the variable indices. @@ -187,6 +190,7 @@ private: /// repeated calls to the same function. std::unordered_map<Expression const*, std::shared_ptr<SymbolicVariable>> m_expressions; std::unordered_map<VariableDeclaration const*, std::shared_ptr<SymbolicVariable>> m_variables; + std::unordered_map<std::string, std::shared_ptr<SymbolicVariable>> m_specialVariables; std::vector<smt::Expression> m_pathConditions; ErrorReporter& m_errorReporter; diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index dbc58664..36e15508 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -28,6 +28,6 @@ SSAVariable::SSAVariable() void SSAVariable::resetIndex() { m_currentIndex = 0; - m_nextFreeIndex.reset (new int); + m_nextFreeIndex.reset (new unsigned); *m_nextFreeIndex = 1; } diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index d357740d..46935472 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -34,19 +34,19 @@ public: void resetIndex(); /// This function returns the current index of this SSA variable. - int index() const { return m_currentIndex; } - int& index() { return m_currentIndex; } + unsigned index() const { return m_currentIndex; } + unsigned& index() { return m_currentIndex; } - int operator++() + unsigned operator++() { return m_currentIndex = (*m_nextFreeIndex)++; } private: - int m_currentIndex; + unsigned m_currentIndex; /// The next free index is a shared pointer because we want /// the copy and the copied to share it. - std::shared_ptr<int> m_nextFreeIndex; + std::shared_ptr<unsigned> m_nextFreeIndex; }; } diff --git a/libsolidity/formal/SymbolicAddressVariable.cpp b/libsolidity/formal/SymbolicAddressVariable.cpp deleted file mode 100644 index 68b95080..00000000 --- a/libsolidity/formal/SymbolicAddressVariable.cpp +++ /dev/null @@ -1,41 +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/>. -*/ - -#include <libsolidity/formal/SymbolicAddressVariable.h> - -#include <libsolidity/formal/SymbolicTypes.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -SymbolicAddressVariable::SymbolicAddressVariable( - Type const& _type, - string const& _uniqueName, - smt::SolverInterface& _interface -): - SymbolicIntVariable(_type, _uniqueName, _interface) -{ - solAssert(isAddress(_type.category()), ""); -} - -void SymbolicAddressVariable::setUnknownValue() -{ - IntegerType intType{160}; - m_interface.addAssertion(currentValue() >= minValue(intType)); - m_interface.addAssertion(currentValue() <= maxValue(intType)); -} diff --git a/libsolidity/formal/SymbolicAddressVariable.h b/libsolidity/formal/SymbolicAddressVariable.h deleted file mode 100644 index 4a0f2361..00000000 --- a/libsolidity/formal/SymbolicAddressVariable.h +++ /dev/null @@ -1,44 +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/>. -*/ - -#pragma once - -#include <libsolidity/formal/SymbolicIntVariable.h> - -namespace dev -{ -namespace solidity -{ - -/** - * Specialization of SymbolicVariable for Address - */ -class SymbolicAddressVariable: public SymbolicIntVariable -{ -public: - SymbolicAddressVariable( - Type const& _type, - std::string const& _uniqueName, - smt::SolverInterface& _interface - ); - - /// Sets the variable to the full valid value range. - void setUnknownValue(); -}; - -} -} diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp deleted file mode 100644 index 9c41ca9d..00000000 --- a/libsolidity/formal/SymbolicBoolVariable.cpp +++ /dev/null @@ -1,46 +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/>. -*/ - -#include <libsolidity/formal/SymbolicBoolVariable.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -SymbolicBoolVariable::SymbolicBoolVariable( - Type const& _type, - string const& _uniqueName, - smt::SolverInterface&_interface -): - SymbolicVariable(_type, _uniqueName, _interface) -{ - solAssert(_type.category() == Type::Category::Bool, ""); -} - -smt::Expression SymbolicBoolVariable::valueAtIndex(int _index) const -{ - return m_interface.newBool(uniqueSymbol(_index)); -} - -void SymbolicBoolVariable::setZeroValue() -{ - m_interface.addAssertion(currentValue() == smt::Expression(false)); -} - -void SymbolicBoolVariable::setUnknownValue() -{ -} diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp deleted file mode 100644 index cf1a7486..00000000 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ /dev/null @@ -1,52 +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/>. -*/ - -#include <libsolidity/formal/SymbolicIntVariable.h> - -#include <libsolidity/formal/SymbolicTypes.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -SymbolicIntVariable::SymbolicIntVariable( - Type const& _type, - string const& _uniqueName, - smt::SolverInterface& _interface -): - SymbolicVariable(_type, _uniqueName, _interface) -{ - solAssert(isNumber(_type.category()), ""); -} - -smt::Expression SymbolicIntVariable::valueAtIndex(int _index) const -{ - return m_interface.newInteger(uniqueSymbol(_index)); -} - -void SymbolicIntVariable::setZeroValue() -{ - m_interface.addAssertion(currentValue() == 0); -} - -void SymbolicIntVariable::setUnknownValue() -{ - auto intType = dynamic_cast<IntegerType const*>(&m_type); - solAssert(intType, ""); - m_interface.addAssertion(currentValue() >= minValue(*intType)); - m_interface.addAssertion(currentValue() <= maxValue(*intType)); -} diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h deleted file mode 100644 index 110ebb09..00000000 --- a/libsolidity/formal/SymbolicIntVariable.h +++ /dev/null @@ -1,49 +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/>. -*/ - -#pragma once - -#include <libsolidity/formal/SymbolicVariable.h> - -namespace dev -{ -namespace solidity -{ - -/** - * Specialization of SymbolicVariable for Integers - */ -class SymbolicIntVariable: public SymbolicVariable -{ -public: - SymbolicIntVariable( - Type const& _type, - std::string const& _uniqueName, - smt::SolverInterface& _interface - ); - - /// Sets the var to 0. - void setZeroValue(); - /// Sets the variable to the full valid value range. - virtual void setUnknownValue(); - -protected: - smt::Expression valueAtIndex(int _index) const; -}; - -} -} diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index d47869db..3eb1c1ce 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -17,10 +17,6 @@ #include <libsolidity/formal/SymbolicTypes.h> -#include <libsolidity/formal/SymbolicBoolVariable.h> -#include <libsolidity/formal/SymbolicIntVariable.h> -#include <libsolidity/formal/SymbolicAddressVariable.h> - #include <libsolidity/ast/Types.h> #include <memory> @@ -43,27 +39,34 @@ pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable( { bool abstract = false; shared_ptr<SymbolicVariable> var; + TypePointer type = _type.shared_from_this(); if (!isSupportedType(_type)) { abstract = true; - var = make_shared<SymbolicIntVariable>(IntegerType{256}, _uniqueName, _solver); + var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver); } else if (isBool(_type.category())) - var = make_shared<SymbolicBoolVariable>(_type, _uniqueName, _solver); + var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _solver); else if (isFunction(_type.category())) - var = make_shared<SymbolicIntVariable>(IntegerType{256}, _uniqueName, _solver); + var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver); else if (isInteger(_type.category())) - var = make_shared<SymbolicIntVariable>(_type, _uniqueName, _solver); + var = make_shared<SymbolicIntVariable>(type, _uniqueName, _solver); + else if (isFixedBytes(_type.category())) + { + auto fixedBytesType = dynamic_cast<FixedBytesType const*>(type.get()); + solAssert(fixedBytesType, ""); + var = make_shared<SymbolicFixedBytesVariable>(fixedBytesType->numBytes(), _uniqueName, _solver); + } else if (isAddress(_type.category())) - var = make_shared<SymbolicAddressVariable>(_type, _uniqueName, _solver); + var = make_shared<SymbolicAddressVariable>(_uniqueName, _solver); else if (isRational(_type.category())) { auto rational = dynamic_cast<RationalNumberType const*>(&_type); solAssert(rational, ""); if (rational->isFractional()) - var = make_shared<SymbolicIntVariable>(IntegerType{256}, _uniqueName, _solver); + var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver); else - var = make_shared<SymbolicIntVariable>(_type, _uniqueName, _solver); + var = make_shared<SymbolicIntVariable>(type, _uniqueName, _solver); } else solAssert(false, ""); @@ -85,6 +88,11 @@ bool dev::solidity::isRational(Type::Category _category) return _category == Type::Category::RationalNumber; } +bool dev::solidity::isFixedBytes(Type::Category _category) +{ + return _category == Type::Category::FixedBytes; +} + bool dev::solidity::isAddress(Type::Category _category) { return _category == Type::Category::Address; @@ -94,6 +102,7 @@ bool dev::solidity::isNumber(Type::Category _category) { return isInteger(_category) || isRational(_category) || + isFixedBytes(_category) || isAddress(_category); } diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h index 0887fa41..dcdd9ea4 100644 --- a/libsolidity/formal/SymbolicTypes.h +++ b/libsolidity/formal/SymbolicTypes.h @@ -18,7 +18,7 @@ #pragma once #include <libsolidity/formal/SolverInterface.h> -#include <libsolidity/formal/SymbolicVariable.h> +#include <libsolidity/formal/SymbolicVariables.h> #include <libsolidity/ast/AST.h> #include <libsolidity/ast/Types.h> @@ -35,6 +35,7 @@ bool isSupportedType(Type const& _type); bool isInteger(Type::Category _category); bool isRational(Type::Category _category); +bool isFixedBytes(Type::Category _category); bool isAddress(Type::Category _category); bool isNumber(Type::Category _category); bool isBool(Type::Category _category); diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp deleted file mode 100644 index c042ec48..00000000 --- a/libsolidity/formal/SymbolicVariable.cpp +++ /dev/null @@ -1,43 +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/>. -*/ - -#include <libsolidity/formal/SymbolicVariable.h> - -#include <libsolidity/ast/AST.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -SymbolicVariable::SymbolicVariable( - Type const& _type, - string const& _uniqueName, - smt::SolverInterface& _interface -): - m_type(_type), - m_uniqueName(_uniqueName), - m_interface(_interface), - m_ssa(make_shared<SSAVariable>()) -{ -} - -string SymbolicVariable::uniqueSymbol(int _index) const -{ - return m_uniqueName + "_" + to_string(_index); -} - - diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp new file mode 100644 index 00000000..85818ba0 --- /dev/null +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -0,0 +1,112 @@ +/* + 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 <libsolidity/formal/SymbolicVariables.h> + +#include <libsolidity/formal/SymbolicTypes.h> + +#include <libsolidity/ast/AST.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SymbolicVariable::SymbolicVariable( + TypePointer _type, + string const& _uniqueName, + smt::SolverInterface& _interface +): + m_type(move(_type)), + m_uniqueName(_uniqueName), + m_interface(_interface), + m_ssa(make_shared<SSAVariable>()) +{ +} + +string SymbolicVariable::uniqueSymbol(unsigned _index) const +{ + return m_uniqueName + "_" + to_string(_index); +} + +SymbolicBoolVariable::SymbolicBoolVariable( + TypePointer _type, + string const& _uniqueName, + smt::SolverInterface&_interface +): + SymbolicVariable(move(_type), _uniqueName, _interface) +{ + solAssert(m_type->category() == Type::Category::Bool, ""); +} + +smt::Expression SymbolicBoolVariable::valueAtIndex(int _index) const +{ + return m_interface.newBool(uniqueSymbol(_index)); +} + +void SymbolicBoolVariable::setZeroValue() +{ + m_interface.addAssertion(currentValue() == smt::Expression(false)); +} + +void SymbolicBoolVariable::setUnknownValue() +{ +} + +SymbolicIntVariable::SymbolicIntVariable( + TypePointer _type, + string const& _uniqueName, + smt::SolverInterface& _interface +): + SymbolicVariable(move(_type), _uniqueName, _interface) +{ + solAssert(isNumber(m_type->category()), ""); +} + +smt::Expression SymbolicIntVariable::valueAtIndex(int _index) const +{ + return m_interface.newInteger(uniqueSymbol(_index)); +} + +void SymbolicIntVariable::setZeroValue() +{ + m_interface.addAssertion(currentValue() == 0); +} + +void SymbolicIntVariable::setUnknownValue() +{ + auto intType = dynamic_cast<IntegerType const*>(m_type.get()); + solAssert(intType, ""); + m_interface.addAssertion(currentValue() >= minValue(*intType)); + m_interface.addAssertion(currentValue() <= maxValue(*intType)); +} + +SymbolicAddressVariable::SymbolicAddressVariable( + string const& _uniqueName, + smt::SolverInterface& _interface +): + SymbolicIntVariable(make_shared<IntegerType>(160), _uniqueName, _interface) +{ +} + +SymbolicFixedBytesVariable::SymbolicFixedBytesVariable( + unsigned _numBytes, + string const& _uniqueName, + smt::SolverInterface& _interface +): + SymbolicIntVariable(make_shared<IntegerType>(_numBytes * 8), _uniqueName, _interface) +{ +} diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariables.h index 417e1f92..4fd9b245 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -33,16 +33,17 @@ namespace solidity class Type; /** - * This class represents the symbolic version of a program variable. + * This abstract class represents the symbolic version of a program variable. */ class SymbolicVariable { public: SymbolicVariable( - Type const& _type, + TypePointer _type, std::string const& _uniqueName, smt::SolverInterface& _interface ); + virtual ~SymbolicVariable() = default; smt::Expression currentValue() const @@ -58,8 +59,8 @@ public: return currentValue(); } - int index() const { return m_ssa->index(); } - int& index() { return m_ssa->index(); } + unsigned index() const { return m_ssa->index(); } + unsigned& index() { return m_ssa->index(); } /// Sets the var to the default value of its type. /// Inherited types must implement. @@ -69,13 +70,80 @@ public: virtual void setUnknownValue() {} protected: - std::string uniqueSymbol(int _index) const; + std::string uniqueSymbol(unsigned _index) const; - Type const& m_type; + TypePointer m_type = nullptr; std::string m_uniqueName; smt::SolverInterface& m_interface; std::shared_ptr<SSAVariable> m_ssa = nullptr; }; +/** + * Specialization of SymbolicVariable for Bool + */ +class SymbolicBoolVariable: public SymbolicVariable +{ +public: + SymbolicBoolVariable( + TypePointer _type, + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); + + /// Sets the var to false. + void setZeroValue(); + /// Does nothing since the SMT solver already knows the valid values for Bool. + void setUnknownValue(); + +protected: + smt::Expression valueAtIndex(int _index) const; +}; + +/** + * Specialization of SymbolicVariable for Integers + */ +class SymbolicIntVariable: public SymbolicVariable +{ +public: + SymbolicIntVariable( + TypePointer _type, + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); + + /// Sets the var to 0. + void setZeroValue(); + /// Sets the variable to the full valid value range. + void setUnknownValue(); + +protected: + smt::Expression valueAtIndex(int _index) const; +}; + +/** + * Specialization of SymbolicVariable for Address + */ +class SymbolicAddressVariable: public SymbolicIntVariable +{ +public: + SymbolicAddressVariable( + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); +}; + +/** + * Specialization of SymbolicVariable for FixedBytes + */ +class SymbolicFixedBytesVariable: public SymbolicIntVariable +{ +public: + SymbolicFixedBytesVariable( + unsigned _numBytes, + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); +}; + } } diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 04b5d1a8..ac019c06 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -79,17 +79,17 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) bool AsmAnalyzer::operator()(assembly::Literal const& _literal) { - expectValidType(_literal.type, _literal.location); + expectValidType(_literal.type.str(), _literal.location); ++m_stackHeight; - if (_literal.kind == assembly::LiteralKind::String && _literal.value.size() > 32) + if (_literal.kind == assembly::LiteralKind::String && _literal.value.str().size() > 32) { m_errorReporter.typeError( _literal.location, - "String literal too long (" + to_string(_literal.value.size()) + " > 32)" + "String literal too long (" + to_string(_literal.value.str().size()) + " > 32)" ); return false; } - else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value) > u256(-1)) + else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value.str()) > u256(-1)) { m_errorReporter.typeError( _literal.location, @@ -100,7 +100,7 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) else if (_literal.kind == assembly::LiteralKind::Boolean) { solAssert(m_flavour == AsmFlavour::Yul, ""); - solAssert(_literal.value == "true" || _literal.value == "false", ""); + solAssert(_literal.value == YulString{string("true")} || _literal.value == YulString{string("false")}, ""); } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; @@ -118,7 +118,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) { m_errorReporter.declarationError( _identifier.location, - "Variable " + _identifier.name + " used before it was declared." + "Variable " + _identifier.name.str() + " used before it was declared." ); success = false; } @@ -132,7 +132,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) { m_errorReporter.typeError( _identifier.location, - "Function " + _identifier.name + " used without being called." + "Function " + _identifier.name.str() + " used without being called." ); success = false; } @@ -253,7 +253,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) for (auto const& variable: _varDecl.variables) { - expectValidType(variable.type, variable.location); + expectValidType(variable.type.str(), variable.location); m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name))); } m_info.stackHeightInfo[&_varDecl] = m_stackHeight; @@ -268,7 +268,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) Scope& varScope = scope(virtualBlock); for (auto const& var: _funDef.parameters + _funDef.returnVariables) { - expectValidType(var.type, var.location); + expectValidType(var.type.str(), var.location); m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name))); } @@ -361,7 +361,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch) if (!expectExpression(*_switch.expression)) success = false; - set<tuple<LiteralKind, string>> cases; + set<tuple<LiteralKind, YulString>> cases; for (auto const& _case: _switch.cases) { if (_case.value) @@ -503,7 +503,7 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t { m_errorReporter.declarationError( _variable.location, - "Variable " + _variable.name + " used before it was declared." + "Variable " + _variable.name.str() + " used before it was declared." ); success = false; } diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index 2982d5e0..a8d5e327 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -27,7 +27,13 @@ #include <libevmasm/Instruction.h> #include <libevmasm/SourceLocation.h> +#include <libyul/YulString.h> + #include <boost/variant.hpp> +#include <boost/noncopyable.hpp> + +#include <map> +#include <memory> namespace dev { @@ -36,20 +42,21 @@ namespace solidity namespace assembly { -using Type = std::string; +using YulString = dev::yul::YulString; +using Type = YulString; -struct TypedName { SourceLocation location; std::string name; Type type; }; +struct TypedName { SourceLocation location; YulString name; Type type; }; using TypedNameList = std::vector<TypedName>; /// Direct EVM instruction (except PUSHi and JUMPDEST) struct Instruction { SourceLocation location; solidity::Instruction instruction; }; /// Literal number or string (up to 32 bytes) enum class LiteralKind { Number, Boolean, String }; -struct Literal { SourceLocation location; LiteralKind kind; std::string value; Type type; }; +struct Literal { SourceLocation location; LiteralKind kind; YulString value; Type type; }; /// External / internal identifier or label reference -struct Identifier { SourceLocation location; std::string name; }; +struct Identifier { SourceLocation location; YulString name; }; /// Jump label ("name:") -struct Label { SourceLocation location; std::string name; }; +struct Label { SourceLocation location; YulString name; }; /// Assignment from stack (":= x", moves stack top into x, potentially multiple slots) struct StackAssignment { SourceLocation location; Identifier variableName; }; /// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand @@ -69,7 +76,7 @@ struct VariableDeclaration { SourceLocation location; TypedNameList variables; s /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector<Statement> statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; +struct FunctionDefinition { SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. struct If { SourceLocation location; std::shared_ptr<Expression> condition; Block body; }; /// Switch case or default case diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 54cdc1c6..1f399edc 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -112,8 +112,8 @@ assembly::Statement Parser::parseStatement() advance(); expectToken(Token::Colon); assignment.variableName.location = location(); - assignment.variableName.name = currentLiteral(); - if (instructions().count(assignment.variableName.name)) + assignment.variableName.name = YulString(currentLiteral()); + if (instructions().count(assignment.variableName.name.str())) fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); @@ -173,7 +173,7 @@ assembly::Statement Parser::parseStatement() if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { assembly::Assignment assignment = createWithLocation<assembly::Assignment>(identifier.location); - if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name)) + if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name.str())) fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); @@ -363,7 +363,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() ret = Instruction{location(), instr}; } else - ret = Identifier{location(), literal}; + ret = Identifier{location(), YulString{literal}}; advance(); break; } @@ -394,15 +394,15 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() Literal literal{ location(), kind, - currentLiteral(), - "" + YulString{currentLiteral()}, + {} }; advance(); if (m_flavour == AsmFlavour::Yul) { expectToken(Token::Colon); literal.location.end = endPosition(); - literal.type = expectAsmIdentifier(); + literal.type = YulString{expectAsmIdentifier()}; } else if (kind == LiteralKind::Boolean) fatalParserError("True and false are not valid literals."); @@ -449,7 +449,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() RecursionGuard recursionGuard(*this); FunctionDefinition funDef = createWithLocation<FunctionDefinition>(); expectToken(Token::Function); - funDef.name = expectAsmIdentifier(); + funDef.name = YulString{expectAsmIdentifier()}; expectToken(Token::LParen); while (currentToken() != Token::RParen) { @@ -564,12 +564,12 @@ TypedName Parser::parseTypedName() { RecursionGuard recursionGuard(*this); TypedName typedName = createWithLocation<TypedName>(); - typedName.name = expectAsmIdentifier(); + typedName.name = YulString{expectAsmIdentifier()}; if (m_flavour == AsmFlavour::Yul) { expectToken(Token::Colon); typedName.location.end = endPosition(); - typedName.type = expectAsmIdentifier(); + typedName.type = YulString{expectAsmIdentifier()}; } return typedName; } diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 1a15d7eb..ae0bd1eb 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -24,6 +24,8 @@ #include <libsolidity/inlineasm/AsmData.h> #include <libsolidity/interface/Exceptions.h> +#include <libdevcore/CommonData.h> + #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/range/adaptor/transformed.hpp> @@ -41,6 +43,7 @@ using namespace dev::solidity::assembly; string AsmPrinter::operator()(assembly::Instruction const& _instruction) { solAssert(!m_yul, ""); + solAssert(isValidInstruction(_instruction.instruction), "Invalid instruction"); return boost::to_lower_copy(instructionInfo(_instruction.instruction).name); } @@ -49,15 +52,17 @@ string AsmPrinter::operator()(assembly::Literal const& _literal) switch (_literal.kind) { case LiteralKind::Number: - return _literal.value + appendTypeName(_literal.type); + solAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal"); + return _literal.value.str() + appendTypeName(_literal.type); case LiteralKind::Boolean: - return ((_literal.value == "true") ? "true" : "false") + appendTypeName(_literal.type); + solAssert(_literal.value.str() == "true" || _literal.value.str() == "false", "Invalid bool literal."); + return ((_literal.value.str() == "true") ? "true" : "false") + appendTypeName(_literal.type); case LiteralKind::String: break; } string out; - for (char c: _literal.value) + for (char c: _literal.value.str()) if (c == '\\') out += "\\\\"; else if (c == '"') @@ -87,18 +92,20 @@ string AsmPrinter::operator()(assembly::Literal const& _literal) string AsmPrinter::operator()(assembly::Identifier const& _identifier) { - return _identifier.name; + solAssert(!_identifier.name.empty(), "Invalid identifier."); + return _identifier.name.str(); } string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functionalInstruction) { solAssert(!m_yul, ""); + solAssert(isValidInstruction(_functionalInstruction.instruction), "Invalid instruction"); return boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) + "(" + boost::algorithm::join( _functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), - ", " ) + + ", ") + ")"; } @@ -110,12 +117,14 @@ string AsmPrinter::operator()(ExpressionStatement const& _statement) string AsmPrinter::operator()(assembly::Label const& _label) { solAssert(!m_yul, ""); - return _label.name + ":"; + solAssert(!_label.name.empty(), "Invalid label."); + return _label.name.str() + ":"; } string AsmPrinter::operator()(assembly::StackAssignment const& _assignment) { solAssert(!m_yul, ""); + solAssert(!_assignment.variableName.name.empty(), "Invalid variable name."); return "=: " + (*this)(_assignment.variableName); } @@ -133,7 +142,7 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl string out = "let "; out += boost::algorithm::join( _variableDeclaration.variables | boost::adaptors::transformed( - [this](TypedName variable) { return variable.name + appendTypeName(variable.type); } + [this](TypedName argument) { return formatTypedName(argument); } ), ", " ); @@ -147,10 +156,11 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition) { - string out = "function " + _functionDefinition.name + "("; + solAssert(!_functionDefinition.name.empty(), "Invalid function name."); + string out = "function " + _functionDefinition.name.str() + "("; out += boost::algorithm::join( _functionDefinition.parameters | boost::adaptors::transformed( - [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } + [this](TypedName argument) { return formatTypedName(argument); } ), ", " ); @@ -160,7 +170,7 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin out += " -> "; out += boost::algorithm::join( _functionDefinition.returnVariables | boost::adaptors::transformed( - [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } + [this](TypedName argument) { return formatTypedName(argument); } ), ", " ); @@ -181,11 +191,13 @@ string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall) string AsmPrinter::operator()(If const& _if) { + solAssert(_if.condition, "Invalid if condition."); return "if " + boost::apply_visitor(*this, *_if.condition) + "\n" + (*this)(_if.body); } string AsmPrinter::operator()(Switch const& _switch) { + solAssert(_switch.expression, "Invalid expression pointer."); string out = "switch " + boost::apply_visitor(*this, *_switch.expression); for (auto const& _case: _switch.cases) { @@ -200,6 +212,7 @@ string AsmPrinter::operator()(Switch const& _switch) string AsmPrinter::operator()(assembly::ForLoop const& _forLoop) { + solAssert(_forLoop.condition, "Invalid for loop condition."); string out = "for "; out += (*this)(_forLoop.pre); out += "\n"; @@ -223,9 +236,15 @@ string AsmPrinter::operator()(Block const& _block) return "{\n " + body + "\n}"; } -string AsmPrinter::appendTypeName(std::string const& _type) const +string AsmPrinter::formatTypedName(TypedName _variable) const +{ + solAssert(!_variable.name.empty(), "Invalid variable name."); + return _variable.name.str() + appendTypeName(_variable.type); +} + +string AsmPrinter::appendTypeName(YulString _type) const { if (m_yul) - return ":" + _type; + return ":" + _type.str(); return ""; } diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index 9f2b842a..72048975 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -24,6 +24,8 @@ #include <libsolidity/inlineasm/AsmDataForward.h> +#include <libyul/YulString.h> + #include <boost/variant.hpp> namespace dev @@ -55,7 +57,8 @@ public: std::string operator()(assembly::Block const& _block); private: - std::string appendTypeName(std::string const& _type) const; + std::string formatTypedName(TypedName _variable) const; + std::string appendTypeName(yul::YulString _type) const; bool m_yul = false; }; diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp index af81b301..10893b96 100644 --- a/libsolidity/inlineasm/AsmScope.cpp +++ b/libsolidity/inlineasm/AsmScope.cpp @@ -21,10 +21,10 @@ #include <libsolidity/inlineasm/AsmScope.h> using namespace std; +using namespace dev; using namespace dev::solidity::assembly; - -bool Scope::registerLabel(string const& _name) +bool Scope::registerLabel(yul::YulString _name) { if (exists(_name)) return false; @@ -32,7 +32,7 @@ bool Scope::registerLabel(string const& _name) return true; } -bool Scope::registerVariable(string const& _name, YulType const& _type) +bool Scope::registerVariable(yul::YulString _name, YulType const& _type) { if (exists(_name)) return false; @@ -42,7 +42,7 @@ bool Scope::registerVariable(string const& _name, YulType const& _type) return true; } -bool Scope::registerFunction(string const& _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns) +bool Scope::registerFunction(yul::YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns) { if (exists(_name)) return false; @@ -50,7 +50,7 @@ bool Scope::registerFunction(string const& _name, std::vector<YulType> const& _a return true; } -Scope::Identifier* Scope::lookup(string const& _name) +Scope::Identifier* Scope::lookup(yul::YulString _name) { bool crossedFunctionBoundary = false; for (Scope* s = this; s; s = s->superScope) @@ -70,7 +70,7 @@ Scope::Identifier* Scope::lookup(string const& _name) return nullptr; } -bool Scope::exists(string const& _name) const +bool Scope::exists(yul::YulString _name) const { if (identifiers.count(_name)) return true; diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h index fc674e71..65848018 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libsolidity/inlineasm/AsmScope.h @@ -22,6 +22,8 @@ #include <libsolidity/interface/Exceptions.h> +#include <libyul/YulString.h> + #include <libdevcore/Visitor.h> #include <boost/variant.hpp> @@ -39,7 +41,7 @@ namespace assembly struct Scope { - using YulType = std::string; + using YulType = yul::YulString; using LabelID = size_t; struct Variable { YulType type; }; @@ -54,10 +56,10 @@ struct Scope using Visitor = GenericVisitor<Variable const, Label const, Function const>; using NonconstVisitor = GenericVisitor<Variable, Label, Function>; - bool registerVariable(std::string const& _name, YulType const& _type); - bool registerLabel(std::string const& _name); + bool registerVariable(yul::YulString _name, YulType const& _type); + bool registerLabel(yul::YulString _name); bool registerFunction( - std::string const& _name, + yul::YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns ); @@ -67,12 +69,12 @@ struct Scope /// will any lookups across assembly boundaries. /// The pointer will be invalidated if the scope is modified. /// @param _crossedFunction if true, we already crossed a function boundary during recursive lookup - Identifier* lookup(std::string const& _name); + Identifier* lookup(yul::YulString _name); /// Looks up the identifier in this and super scopes (will not find variables across function /// boundaries and generally stops at assembly boundaries) and calls the visitor, returns /// false if not found. template <class V> - bool lookup(std::string const& _name, V const& _visitor) + bool lookup(yul::YulString _name, V const& _visitor) { if (Identifier* id = lookup(_name)) { @@ -84,7 +86,7 @@ struct Scope } /// @returns true if the name exists in this scope or in super scopes (also searches /// across function and assembly boundaries). - bool exists(std::string const& _name) const; + bool exists(yul::YulString _name) const; /// @returns the number of variables directly registered inside the scope. size_t numberOfVariables() const; @@ -95,7 +97,7 @@ struct Scope /// If true, variables from the super scope are not visible here (other identifiers are), /// but they are still taken into account to prevent shadowing. bool functionScope = false; - std::map<std::string, Identifier> identifiers; + std::map<yul::YulString, Identifier> identifiers; }; } diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 2d15c820..d1f98083 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -57,7 +57,7 @@ bool ScopeFiller::operator()(Label const& _item) //@TODO secondary location m_errorReporter.declarationError( _item.location, - "Label name " + _item.name + " already taken in this scope." + "Label name " + _item.name.str() + " already taken in this scope." ); return false; } @@ -77,16 +77,16 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) bool success = true; vector<Scope::YulType> arguments; for (auto const& _argument: _funDef.parameters) - arguments.push_back(_argument.type); + arguments.emplace_back(_argument.type.str()); vector<Scope::YulType> returns; for (auto const& _return: _funDef.returnVariables) - returns.push_back(_return.type); + returns.emplace_back(_return.type.str()); if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) { //@TODO secondary location m_errorReporter.declarationError( _funDef.location, - "Function name " + _funDef.name + " already taken in this scope." + "Function name " + _funDef.name.str() + " already taken in this scope." ); success = false; } @@ -164,7 +164,7 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& //@TODO secondary location m_errorReporter.declarationError( _location, - "Variable name " + _name.name + " already taken in this scope." + "Variable name " + _name.name.str() + " already taken in this scope." ); return false; } diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index a35bfd29..b5f68ce8 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -55,13 +55,13 @@ bytes dev::solidity::binaryVersion() ret = ret * 10 + (VersionString[i] - '0'); return ret; }; - ret.push_back(byte(parseDecimal())); + ret.push_back(uint8_t(parseDecimal())); solAssert(i < VersionString.size() && VersionString[i] == '.', ""); ++i; - ret.push_back(byte(parseDecimal())); + ret.push_back(uint8_t(parseDecimal())); solAssert(i < VersionString.size() && VersionString[i] == '.', ""); ++i; - ret.push_back(byte(parseDecimal())); + ret.push_back(uint8_t(parseDecimal())); solAssert(i < VersionString.size() && (VersionString[i] == '-' || VersionString[i] == '+'), ""); ++i; size_t commitpos = VersionString.find("commit."); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index ca9a9b57..f99b9ea4 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -112,17 +112,17 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective() ASTNodeFactory nodeFactory(*this); expectToken(Token::Pragma); vector<string> literals; - vector<Token::Value> tokens; + vector<Token> tokens; do { - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (token == Token::Illegal) parserError("Token incompatible with Solidity parser as part of pragma directive."); else { string literal = m_scanner->currentLiteral(); - if (literal.empty() && Token::toString(token)) - literal = Token::toString(token); + if (literal.empty() && TokenTraits::toString(token)) + literal = TokenTraits::toString(token); literals.push_back(literal); tokens.push_back(token); } @@ -240,7 +240,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() expectToken(Token::LBrace); while (true) { - Token::Value currentTokenValue = m_scanner->currentToken(); + Token currentTokenValue = m_scanner->currentToken(); if (currentTokenValue == Token::RBrace) break; else if (currentTokenValue == Token::Function || currentTokenValue == Token::Constructor) @@ -254,7 +254,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() else if ( currentTokenValue == Token::Identifier || currentTokenValue == Token::Mapping || - Token::isElementaryTypeName(currentTokenValue) + TokenTraits::isElementaryTypeName(currentTokenValue) ) { VarDeclParserOptions options; @@ -304,7 +304,7 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() Declaration::Visibility Parser::parseVisibilitySpecifier() { Declaration::Visibility visibility(Declaration::Visibility::Default); - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); switch (token) { case Token::Public: @@ -329,7 +329,7 @@ Declaration::Visibility Parser::parseVisibilitySpecifier() StateMutability Parser::parseStateMutability() { StateMutability stateMutability(StateMutability::NonPayable); - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); switch(token) { case Token::Payable: @@ -386,7 +386,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN result.parameters = parseParameterList(options); while (true) { - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (_allowModifiers && token == Token::Identifier) { // If the name is empty (and this is not a constructor), @@ -401,7 +401,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN else result.modifiers.push_back(parseModifierInvocation()); } - else if (Token::isVisibilitySpecifier(token)) + else if (TokenTraits::isVisibilitySpecifier(token)) { if (result.visibility != Declaration::Visibility::Default) { @@ -423,7 +423,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN else result.visibility = parseVisibilitySpecifier(); } - else if (Token::isStateMutabilitySpecifier(token)) + else if (TokenTraits::isStateMutabilitySpecifier(token)) { if (result.stateMutability != StateMutability::NonPayable) { @@ -587,8 +587,8 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( while (true) { - Token::Value token = m_scanner->currentToken(); - if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token)) + Token token = m_scanner->currentToken(); + if (_options.isStateVariable && TokenTraits::isVariableVisibilitySpecifier(token)) { nodeFactory.markEndPosition(); if (visibility != Declaration::Visibility::Default) @@ -609,7 +609,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( isIndexed = true; else if (token == Token::Constant) isDeclaredConst = true; - else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token)) + else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { if (location != VariableDeclaration::Location::Unspecified) parserError(string("Location already specified.")); @@ -806,8 +806,8 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer<TypeName> type; - Token::Value token = m_scanner->currentToken(); - if (Token::isElementaryTypeName(token)) + Token token = m_scanner->currentToken(); + if (TokenTraits::isElementaryTypeName(token)) { unsigned firstSize; unsigned secondSize; @@ -817,7 +817,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) nodeFactory.markEndPosition(); m_scanner->next(); auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable); - if (Token::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) + if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) { if (elemTypeName.token() == Token::Address) { @@ -874,8 +874,8 @@ ASTPointer<Mapping> Parser::parseMapping() expectToken(Token::Mapping); expectToken(Token::LParen); ASTPointer<ElementaryTypeName> keyType; - Token::Value token = m_scanner->currentToken(); - if (!Token::isElementaryTypeName(token)) + Token token = m_scanner->currentToken(); + if (!TokenTraits::isElementaryTypeName(token)) fatalParserError(string("Expected elementary type name for mapping key type")); unsigned firstSize; unsigned secondSize; @@ -1252,7 +1252,7 @@ pair<Parser::LookAheadInfo, Parser::IndexAccessedPath> Parser::tryParseIndexAcce // VariableDeclarationStatement out of it. IndexAccessedPath iap = parseIndexAccessedPath(); - if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken())) + if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken())) return make_pair(LookAheadInfo::VariableDeclaration, move(iap)); else return make_pair(LookAheadInfo::Expression, move(iap)); @@ -1342,16 +1342,16 @@ ASTPointer<Expression> Parser::parseExpression( { RecursionGuard recursionGuard(*this); ASTPointer<Expression> expression = parseBinaryExpression(4, _partiallyParsedExpression); - if (Token::isAssignmentOp(m_scanner->currentToken())) + if (TokenTraits::isAssignmentOp(m_scanner->currentToken())) { - Token::Value assignmentOperator = m_scanner->currentToken(); + Token assignmentOperator = m_scanner->currentToken(); m_scanner->next(); ASTPointer<Expression> rightHandSide = parseExpression(); ASTNodeFactory nodeFactory(*this, expression); nodeFactory.setEndPositionFromNode(rightHandSide); return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); } - else if (m_scanner->currentToken() == Token::Value::Conditional) + else if (m_scanner->currentToken() == Token::Conditional) { m_scanner->next(); ASTPointer<Expression> trueExpression = parseExpression(); @@ -1373,11 +1373,11 @@ ASTPointer<Expression> Parser::parseBinaryExpression( RecursionGuard recursionGuard(*this); ASTPointer<Expression> expression = parseUnaryExpression(_partiallyParsedExpression); ASTNodeFactory nodeFactory(*this, expression); - int precedence = Token::precedence(m_scanner->currentToken()); + int precedence = TokenTraits::precedence(m_scanner->currentToken()); for (; precedence >= _minPrecedence; --precedence) - while (Token::precedence(m_scanner->currentToken()) == precedence) + while (TokenTraits::precedence(m_scanner->currentToken()) == precedence) { - Token::Value op = m_scanner->currentToken(); + Token op = m_scanner->currentToken(); m_scanner->next(); ASTPointer<Expression> right = parseBinaryExpression(precedence + 1); nodeFactory.setEndPositionFromNode(right); @@ -1393,8 +1393,8 @@ ASTPointer<Expression> Parser::parseUnaryExpression( RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _partiallyParsedExpression ? ASTNodeFactory(*this, _partiallyParsedExpression) : ASTNodeFactory(*this); - Token::Value token = m_scanner->currentToken(); - if (!_partiallyParsedExpression && (Token::isUnaryOp(token) || Token::isCountOp(token))) + Token token = m_scanner->currentToken(); + if (!_partiallyParsedExpression && (TokenTraits::isUnaryOp(token) || TokenTraits::isCountOp(token))) { // prefix expression m_scanner->next(); @@ -1407,7 +1407,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression( // potential postfix expression ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_partiallyParsedExpression); token = m_scanner->currentToken(); - if (!Token::isCountOp(token)) + if (!TokenTraits::isCountOp(token)) return subExpression; nodeFactory.markEndPosition(); m_scanner->next(); @@ -1482,7 +1482,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); ASTPointer<Expression> expression; switch (token) @@ -1493,7 +1493,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); break; case Token::Number: - if (Token::isEtherSubdenomination(m_scanner->peekNextToken())) + if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken())) { ASTPointer<ASTString> literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); @@ -1501,7 +1501,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() m_scanner->next(); expression = nodeFactory.createNode<Literal>(token, literal, subdenomination); } - else if (Token::isTimeSubdenomination(m_scanner->peekNextToken())) + else if (TokenTraits::isTimeSubdenomination(m_scanner->peekNextToken())) { ASTPointer<ASTString> literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); @@ -1531,7 +1531,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() // (x,) is one-dimensional tuple, elements in arrays cannot be left out, only in tuples. m_scanner->next(); vector<ASTPointer<Expression>> components; - Token::Value oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack); + Token oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack); bool isArray = (token == Token::LBrack); if (m_scanner->currentToken() != oppositeToken) @@ -1558,7 +1558,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() fatalParserError("Expected even number of hex-nibbles within double-quotes."); break; default: - if (Token::isElementaryTypeName(token)) + if (TokenTraits::isElementaryTypeName(token)) { //used for casts unsigned firstSize; @@ -1595,7 +1595,7 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars { RecursionGuard recursionGuard(*this); pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret; - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (token == Token::LBrace) { // call({arg1 : 1, arg2 : 2 }) @@ -1638,21 +1638,21 @@ Parser::LookAheadInfo Parser::peekStatementType() const // or a mutability specifier, we also have a variable declaration. // If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;"). // In all other cases, we have an expression statement. - Token::Value token(m_scanner->currentToken()); - bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); + Token token(m_scanner->currentToken()); + bool mightBeTypeName = (TokenTraits::isElementaryTypeName(token) || token == Token::Identifier); if (token == Token::Mapping || token == Token::Function || token == Token::Var) return LookAheadInfo::VariableDeclaration; if (mightBeTypeName) { - Token::Value next = m_scanner->peekNextToken(); + Token next = m_scanner->peekNextToken(); // So far we only allow ``address payable`` in variable declaration statements and in no other // kind of statement. This means, for example, that we do not allow type expressions of the form // ``address payable;``. // If we want to change this in the future, we need to consider another scanner token here. - if (Token::isElementaryTypeName(token) && Token::isStateMutabilitySpecifier(next, false)) + if (TokenTraits::isElementaryTypeName(token) && TokenTraits::isStateMutabilitySpecifier(next, false)) return LookAheadInfo::VariableDeclaration; - if (next == Token::Identifier || Token::isLocationSpecifier(next)) + if (next == Token::Identifier || TokenTraits::isLocationSpecifier(next)) return LookAheadInfo::VariableDeclaration; if (next == Token::LBrack || next == Token::Period) return LookAheadInfo::IndexAccessStructure; diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp index 71133746..1d4cb1e2 100644 --- a/libsolidity/parsing/ParserBase.cpp +++ b/libsolidity/parsing/ParserBase.cpp @@ -43,12 +43,12 @@ int ParserBase::endPosition() const return m_scanner->currentLocation().end; } -Token::Value ParserBase::currentToken() const +Token ParserBase::currentToken() const { return m_scanner->currentToken(); } -Token::Value ParserBase::peekNextToken() const +Token ParserBase::peekNextToken() const { return m_scanner->peekNextToken(); } @@ -58,31 +58,31 @@ std::string ParserBase::currentLiteral() const return m_scanner->currentLiteral(); } -Token::Value ParserBase::advance() +Token ParserBase::advance() { return m_scanner->next(); } -void ParserBase::expectToken(Token::Value _value, bool _advance) +void ParserBase::expectToken(Token _value, bool _advance) { - Token::Value tok = m_scanner->currentToken(); + Token tok = m_scanner->currentToken(); if (tok != _value) { - auto tokenName = [this](Token::Value _token) + auto tokenName = [this](Token _token) { if (_token == Token::Identifier) return string("identifier"); else if (_token == Token::EOS) return string("end of source"); - else if (Token::isReservedKeyword(_token)) - return string("reserved keyword '") + Token::friendlyName(_token) + "'"; - else if (Token::isElementaryTypeName(_token)) //for the sake of accuracy in reporting + else if (TokenTraits::isReservedKeyword(_token)) + return string("reserved keyword '") + TokenTraits::friendlyName(_token) + "'"; + else if (TokenTraits::isElementaryTypeName(_token)) //for the sake of accuracy in reporting { ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken(); return string("'") + elemTypeName.toString() + "'"; } else - return string("'") + Token::friendlyName(_token) + "'"; + return string("'") + TokenTraits::friendlyName(_token) + "'"; }; fatalParserError(string("Expected ") + tokenName(_value) + string(" but got ") + tokenName(tok)); diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h index b28e1b1b..e01f37d8 100644 --- a/libsolidity/parsing/ParserBase.h +++ b/libsolidity/parsing/ParserBase.h @@ -63,11 +63,11 @@ protected: ///@{ ///@name Helper functions /// If current token value is not _value, throw exception otherwise advance token. - void expectToken(Token::Value _value, bool _advance = true); - Token::Value currentToken() const; - Token::Value peekNextToken() const; + void expectToken(Token _value, bool _advance = true); + Token currentToken() const; + Token peekNextToken() const; std::string currentLiteral() const; - Token::Value advance(); + Token advance(); ///@} /// Increases the recursion depth and throws an exception if it is too deep. diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index 87d7c535..e9dad2ad 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -214,9 +214,9 @@ void Scanner::addUnicodeAsUTF8(unsigned codepoint) } // Ensure that tokens can be stored in a byte. -BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); +BOOST_STATIC_ASSERT(TokenTraits::count() <= 0x100); -Token::Value Scanner::next() +Token Scanner::next() { m_currentToken = m_nextToken; m_skippedComment = m_nextSkippedComment; @@ -225,7 +225,7 @@ Token::Value Scanner::next() return m_currentToken.token; } -Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else) +Token Scanner::selectToken(char _next, Token _then, Token _else) { advance(); if (m_char == _next) @@ -249,7 +249,7 @@ void Scanner::skipWhitespaceExceptUnicodeLinebreak() advance(); } -Token::Value Scanner::skipSingleLineComment() +Token Scanner::skipSingleLineComment() { // Line terminator is not part of the comment. If it is a // non-ascii line terminator, it will result in a parser error. @@ -259,7 +259,7 @@ Token::Value Scanner::skipSingleLineComment() return Token::Whitespace; } -Token::Value Scanner::scanSingleLineDocComment() +Token Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); advance(); //consume the last '/' at /// @@ -295,7 +295,7 @@ Token::Value Scanner::scanSingleLineDocComment() return Token::CommentLiteral; } -Token::Value Scanner::skipMultiLineComment() +Token Scanner::skipMultiLineComment() { advance(); while (!isSourcePastEndOfInput()) @@ -316,7 +316,7 @@ Token::Value Scanner::skipMultiLineComment() return Token::Illegal; } -Token::Value Scanner::scanMultiLineDocComment() +Token Scanner::scanMultiLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); bool endFound = false; @@ -369,7 +369,7 @@ Token::Value Scanner::scanMultiLineDocComment() return Token::CommentLiteral; } -Token::Value Scanner::scanSlash() +Token Scanner::scanSlash() { int firstSlashPosition = sourcePos(); advance(); @@ -380,7 +380,7 @@ Token::Value Scanner::scanSlash() else if (m_char == '/') { // doxygen style /// comment - Token::Value comment; + Token comment; m_nextSkippedComment.location.start = firstSlashPosition; comment = scanSingleLineDocComment(); m_nextSkippedComment.location.end = sourcePos(); @@ -406,7 +406,7 @@ Token::Value Scanner::scanSlash() return Token::Whitespace; } // we actually have a multiline documentation comment - Token::Value comment; + Token comment; m_nextSkippedComment.location.start = firstSlashPosition; comment = scanMultiLineDocComment(); m_nextSkippedComment.location.end = sourcePos(); @@ -432,7 +432,7 @@ void Scanner::scanToken() m_nextSkippedComment.literal.clear(); m_nextSkippedComment.extendedTokenInfo = make_tuple(0, 0); - Token::Value token; + Token token; // M and N are for the purposes of grabbing different type sizes unsigned m; unsigned n; @@ -703,7 +703,7 @@ bool Scanner::isUnicodeLinebreak() return false; } -Token::Value Scanner::scanString() +Token Scanner::scanString() { char const quote = m_char; advance(); // consume quote @@ -727,7 +727,7 @@ Token::Value Scanner::scanString() return Token::StringLiteral; } -Token::Value Scanner::scanHexString() +Token Scanner::scanHexString() { char const quote = m_char; advance(); // consume quote @@ -760,7 +760,7 @@ void Scanner::scanDecimalDigits() // Defer further validation of underscore to SyntaxChecker. } -Token::Value Scanner::scanNumber(char _charSeen) +Token Scanner::scanNumber(char _charSeen) { enum { DECIMAL, HEX, BINARY } kind = DECIMAL; LiteralScope literal(this, LITERAL_TYPE_NUMBER); @@ -854,7 +854,7 @@ Token::Value Scanner::scanNumber(char _charSeen) return Token::Number; } -tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() +tuple<Token, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() { solAssert(isIdentifierStart(m_char), ""); LiteralScope literal(this, LITERAL_TYPE_STRING); @@ -863,7 +863,7 @@ tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() while (isIdentifierPart(m_char)) //get full literal addLiteralCharAndAdvance(); literal.complete(); - return Token::fromIdentifierOrKeyword(m_nextToken.literal); + return TokenTraits::fromIdentifierOrKeyword(m_nextToken.literal); } char CharStream::advanceAndGet(size_t _chars) diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index 7564c788..02e0553f 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -112,13 +112,13 @@ public: void reset(); /// @returns the next token and advances input - Token::Value next(); + Token next(); ///@{ ///@name Information about the current token /// @returns the current token - Token::Value currentToken() const + Token currentToken() const { return m_currentToken.token; } @@ -149,7 +149,7 @@ public: ///@name Information about the next token /// @returns the next token without advancing input. - Token::Value peekNextToken() const { return m_nextToken.token; } + Token peekNextToken() const { return m_nextToken.token; } SourceLocation peekLocation() const { return m_nextToken.location; } std::string const& peekLiteral() const { return m_nextToken.literal; } ///@} @@ -168,7 +168,7 @@ private: /// Used for the current and look-ahead token and comments struct TokenDesc { - Token::Value token; + Token token; SourceLocation location; std::string literal; std::tuple<unsigned, unsigned> extendedTokenInfo; @@ -185,9 +185,9 @@ private: bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } void rollback(int _amount) { m_char = m_source.rollback(_amount); } - inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; } + inline Token selectToken(Token _tok) { advance(); return _tok; } /// If the next character is _next, advance and return _then, otherwise return _else. - inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else); + inline Token selectToken(char _next, Token _then, Token _else); bool scanHexByte(char& o_scannedByte); bool scanUnicode(unsigned& o_codepoint); @@ -199,19 +199,19 @@ private: bool skipWhitespace(); /// Skips all whitespace that are neither '\r' nor '\n'. void skipWhitespaceExceptUnicodeLinebreak(); - Token::Value skipSingleLineComment(); - Token::Value skipMultiLineComment(); + Token skipSingleLineComment(); + Token skipMultiLineComment(); void scanDecimalDigits(); - Token::Value scanNumber(char _charSeen = 0); - std::tuple<Token::Value, unsigned, unsigned> scanIdentifierOrKeyword(); + Token scanNumber(char _charSeen = 0); + std::tuple<Token, unsigned, unsigned> scanIdentifierOrKeyword(); - Token::Value scanString(); - Token::Value scanHexString(); - Token::Value scanSingleLineDocComment(); - Token::Value scanMultiLineDocComment(); + Token scanString(); + Token scanHexString(); + Token scanSingleLineDocComment(); + Token scanMultiLineDocComment(); /// Scans a slash '/' and depending on the characters returns the appropriate token - Token::Value scanSlash(); + Token scanSlash(); /// Scans an escape-sequence which is part of a string and adds the /// decoded character to the current literal. Returns true if a pattern diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 27acb7d4..dccd9037 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -51,9 +51,9 @@ namespace dev namespace solidity { -void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second) +void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second) { - solAssert(Token::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(Token::toString(_baseType))); + solAssert(TokenTraits::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(TokenTraits::toString(_baseType))); if (_baseType == Token::BytesM) { solAssert(_second == 0, "There should not be a second size argument to type bytesM."); @@ -61,17 +61,17 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con } else if (_baseType == Token::UIntM || _baseType == Token::IntM) { - solAssert(_second == 0, "There should not be a second size argument to type " + string(Token::toString(_baseType)) + "."); + solAssert(_second == 0, "There should not be a second size argument to type " + string(TokenTraits::toString(_baseType)) + "."); solAssert( _first <= 256 && _first % 8 == 0, - "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "." + "No elementary type " + string(TokenTraits::toString(_baseType)) + to_string(_first) + "." ); } else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN) { solAssert( _first >= 8 && _first <= 256 && _first % 8 == 0 && _second <= 80, - "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "." + "No elementary type " + string(TokenTraits::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "." ); } m_token = _baseType; @@ -79,38 +79,54 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con m_secondNumber = _second; } -#define T(name, string, precedence) #name, -char const* const Token::m_name[NUM_TOKENS] = +namespace TokenTraits { - TOKEN_LIST(T, T) -}; -#undef T +char const* toString(Token tok) +{ + switch (tok) + { +#define T(name, string, precedence) case Token::name: return string; + TOKEN_LIST(T, T) +#undef T + default: // Token::NUM_TOKENS: + return ""; + } +} -#define T(name, string, precedence) string, -char const* const Token::m_string[NUM_TOKENS] = +char const* name(Token tok) { - TOKEN_LIST(T, T) -}; +#define T(name, string, precedence) #name, + static char const* const names[TokenTraits::count()] = { TOKEN_LIST(T, T) }; #undef T + solAssert(static_cast<size_t>(tok) < TokenTraits::count(), ""); + return names[static_cast<size_t>(tok)]; +} -#define T(name, string, precedence) precedence, -int8_t const Token::m_precedence[NUM_TOKENS] = +std::string friendlyName(Token tok) { - TOKEN_LIST(T, T) -}; -#undef T + char const* ret = toString(tok); + if (ret) + return std::string(ret); + ret = name(tok); + solAssert(ret != nullptr, ""); + return std::string(ret); +} -#define KT(a, b, c) 'T', -#define KK(a, b, c) 'K', -char const Token::m_tokenType[] = +#define T(name, string, precedence) precedence, +int precedence(Token tok) { - TOKEN_LIST(KT, KK) -}; + int8_t const static precs[TokenTraits::count()] = + { + TOKEN_LIST(T, T) + }; + return precs[static_cast<size_t>(tok)]; +} +#undef T -int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) +int parseSize(string::const_iterator _begin, string::const_iterator _end) { try { @@ -123,7 +139,20 @@ int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) } } -tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(string const& _literal) +static Token keywordByName(string const& _name) +{ + // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored + // and keywords to be put inside the keywords variable. +#define KEYWORD(name, string, precedence) {string, Token::name}, +#define TOKEN(name, string, precedence) + static const map<string, Token> keywords({TOKEN_LIST(TOKEN, KEYWORD)}); +#undef KEYWORD +#undef TOKEN + auto it = keywords.find(_name); + return it == keywords.end() ? Token::Identifier : it->second; +} + +tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); if (positionM != _literal.end()) @@ -131,7 +160,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s string baseType(_literal.begin(), positionM); auto positionX = find_if_not(positionM, _literal.end(), ::isdigit); int m = parseSize(positionM, positionX); - Token::Value keyword = keywordByName(baseType); + Token keyword = keywordByName(baseType); if (keyword == Token::Bytes) { if (0 < m && m <= 32 && positionX == _literal.end()) @@ -172,20 +201,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s return make_tuple(keywordByName(_literal), 0, 0); } -Token::Value Token::keywordByName(string const& _name) -{ - // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored - // and keywords to be put inside the keywords variable. -#define KEYWORD(name, string, precedence) {string, Token::name}, -#define TOKEN(name, string, precedence) - static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)}); -#undef KEYWORD -#undef TOKEN - auto it = keywords.find(_name); - return it == keywords.end() ? Token::Identifier : it->second; -} -#undef KT -#undef KK +} } } diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 8ecc850a..81e8dd98 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -45,6 +45,7 @@ #include <libdevcore/Common.h> #include <libsolidity/interface/Exceptions.h> #include <libsolidity/parsing/UndefMacros.h> +#include <iosfwd> namespace dev { @@ -267,119 +268,95 @@ namespace solidity /* Scanner-internal use only. */ \ T(Whitespace, NULL, 0) - -class Token -{ -public: - // All token values. - // attention! msvc issue: - // http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 - // @todo: avoid TOKEN_LIST macro +// All token values. +// attention! msvc issue: +// http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 +// @todo: avoid TOKEN_LIST macro +enum class Token : unsigned int { #define T(name, string, precedence) name, - enum Value - { - TOKEN_LIST(T, T) - NUM_TOKENS - }; + TOKEN_LIST(T, T) + NUM_TOKENS #undef T +}; - // @returns a string corresponding to the C++ token name - // (e.g. "LT" for the token LT). - static char const* name(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_name[tok]; - } +namespace TokenTraits +{ + constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); } // Predicates - static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; } - static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; } - static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; } - static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd || - op == Add || op == Mul || op == Equal || op == NotEqual; } - static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; } - static bool isCompareOp(Value op) { return Equal <= op && op <= GreaterThanOrEqual; } + constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; } + constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; } + constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } + constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd || + op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; } + constexpr bool isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; } + constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; } - static Value AssignmentToBinaryOp(Value op) - { - solAssert(isAssignmentOp(op) && op != Assign, ""); - return Value(op + (BitOr - AssignBitOr)); - } + constexpr bool isBitOp(Token op) { return (Token::BitOr <= op && op <= Token::BitAnd) || op == Token::BitNot; } + constexpr bool isBooleanOp(Token op) { return (Token::Or <= op && op <= Token::And) || op == Token::Not; } + constexpr bool isUnaryOp(Token op) { return (Token::Not <= op && op <= Token::Delete) || op == Token::Add || op == Token::Sub; } + constexpr bool isCountOp(Token op) { return op == Token::Inc || op == Token::Dec; } + constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); } + constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; } + constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; } + constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; } - static bool isBitOp(Value op) { return (BitOr <= op && op <= BitAnd) || op == BitNot; } - static bool isBooleanOp(Value op) { return (Or <= op && op <= And) || op == Not; } - static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; } - static bool isCountOp(Value op) { return op == Inc || op == Dec; } - static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } - static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } - static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } - static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage || op == CallData; } - static bool isStateMutabilitySpecifier(Value op, bool _allowConstant = true) + constexpr bool isStateMutabilitySpecifier(Token op, bool _allowConstant = true) { - if (op == Constant && _allowConstant) - return true; - return op == Pure || op == View || op == Payable; + return (op == Token::Constant && _allowConstant) + || op == Token::Pure || op == Token::View || op == Token::Payable; } - static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } - static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } - static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= Unchecked); } - // @returns a string corresponding to the JS token string - // (.e., "<" for the token LT) or NULL if the token doesn't - // have a (unique) string (e.g. an IDENTIFIER). - static char const* toString(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_string[tok]; - } + constexpr bool isEtherSubdenomination(Token op) { return op == Token::SubWei || op == Token::SubSzabo || op == Token::SubFinney || op == Token::SubEther; } + constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; } + constexpr bool isReservedKeyword(Token op) { return (Token::Abstract <= op && op <= Token::Unchecked); } - static std::string friendlyName(Value tok) + inline Token AssignmentToBinaryOp(Token op) { - char const* ret = toString(tok); - if (ret == nullptr) - { - ret = name(tok); - solAssert(ret != nullptr, ""); - } - return std::string(ret); + solAssert(isAssignmentOp(op) && op != Token::Assign, ""); + return static_cast<Token>(static_cast<int>(op) + (static_cast<int>(Token::BitOr) - static_cast<int>(Token::AssignBitOr))); } // @returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. - static int precedence(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_precedence[tok]; - } + int precedence(Token tok); - static std::tuple<Token::Value, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal); + std::tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal); -private: - // @returns -1 on error (invalid digit or number too large) - static int parseSize(std::string::const_iterator _begin, std::string::const_iterator _end); - // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. - static Token::Value keywordByName(std::string const& _name); - static char const* const m_name[NUM_TOKENS]; - static char const* const m_string[NUM_TOKENS]; - static int8_t const m_precedence[NUM_TOKENS]; - static char const m_tokenType[NUM_TOKENS]; -}; + // @returns a string corresponding to the C++ token name + // (e.g. "LT" for the token LT). + char const* name(Token tok); + + // @returns a string corresponding to the JS token string + // (.e., "<" for the token LT) or NULL if the token doesn't + // have a (unique) string (e.g. an IDENTIFIER). + char const* toString(Token tok); + + std::string friendlyName(Token tok); +} + +inline std::ostream& operator<<(std::ostream& os, Token token) +{ + os << TokenTraits::friendlyName(token); + return os; +} class ElementaryTypeNameToken { public: - ElementaryTypeNameToken(Token::Value _token, unsigned const& _firstNumber, unsigned const& _secondNumber) + ElementaryTypeNameToken(Token _token, unsigned const& _firstNumber, unsigned const& _secondNumber) { assertDetails(_token, _firstNumber, _secondNumber); } unsigned int firstNumber() const { return m_firstNumber; } unsigned int secondNumber() const { return m_secondNumber; } - Token::Value token() const { return m_token; } + Token token() const { return m_token; } + ///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type std::string toString(bool const& tokenValue = false) const { - std::string name = Token::toString(m_token); + std::string name = TokenTraits::toString(m_token); if (tokenValue || (firstNumber() == 0 && secondNumber() == 0)) return name; solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here."); @@ -390,11 +367,11 @@ public: } private: - Token::Value m_token; + Token m_token; unsigned int m_firstNumber; unsigned int m_secondNumber; /// throws if type is not properly sized - void assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second); + void assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second); }; } diff --git a/libyul/ASTDataForward.h b/libyul/ASTDataForward.h index 7f131b5e..8c49e68f 100644 --- a/libyul/ASTDataForward.h +++ b/libyul/ASTDataForward.h @@ -46,6 +46,7 @@ using ExpressionStatement = solidity::assembly::ExpressionStatement; using Block = solidity::assembly::Block; using TypedName = solidity::assembly::TypedName; +class YulString; using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h index c423b66f..0c421dbf 100644 --- a/libyul/Exceptions.h +++ b/libyul/Exceptions.h @@ -30,6 +30,11 @@ namespace yul struct YulException: virtual Exception {}; struct OptimizerException: virtual YulException {}; +struct YulAssertion: virtual YulException {}; + +/// Assertion that throws an YulAssertion containing the given description if it is not met. +#define yulAssert(CONDITION, DESCRIPTION) \ + assertThrow(CONDITION, ::dev::yul::YulException, DESCRIPTION) } } diff --git a/libyul/YulString.h b/libyul/YulString.h new file mode 100644 index 00000000..ae01c83f --- /dev/null +++ b/libyul/YulString.h @@ -0,0 +1,96 @@ +/* + 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/>. +*/ +/** + * String abstraction that avoids copies. + */ + +#pragma once + +#include <boost/noncopyable.hpp> + +#include <map> +#include <memory> +#include <vector> +#include <string> + +namespace dev +{ +namespace yul +{ + +class YulStringRepository: boost::noncopyable +{ +public: + YulStringRepository(): m_strings{std::make_shared<std::string>()} + { + m_ids[std::string{}] = 0; + } + static YulStringRepository& instance() + { + static YulStringRepository inst; + return inst; + } + size_t stringToId(std::string const& _string) + { + if (_string.empty()) + return 0; + size_t& id = m_ids[_string]; + if (id == 0) + { + m_strings.emplace_back(std::make_shared<std::string>(_string)); + id = m_strings.size() - 1; + } + return id; + } + std::string const& idToString(size_t _id) const + { + return *m_strings.at(_id); + } + +private: + std::vector<std::shared_ptr<std::string>> m_strings; + std::map<std::string, size_t> m_ids; +}; + +class YulString +{ +public: + YulString() = default; + explicit YulString(std::string const& _s): m_id(YulStringRepository::instance().stringToId(_s)) {} + YulString(YulString const&) = default; + YulString(YulString&&) = default; + YulString& operator=(YulString const&) = default; + YulString& operator=(YulString&&) = default; + + /// This is not consistent with the string <-operator! + bool operator<(YulString const& _other) const { return m_id < _other.m_id; } + bool operator==(YulString const& _other) const { return m_id == _other.m_id; } + bool operator!=(YulString const& _other) const { return m_id != _other.m_id; } + + bool empty() const { return m_id == 0; } + std::string const& str() const + { + return YulStringRepository::instance().idToString(m_id); + } + +private: + /// ID of the string. Assumes that the empty string has ID zero. + size_t m_id = 0; +}; + +} +} diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index b2f0878f..b37a3231 100644 --- a/libyul/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -44,7 +44,7 @@ void EVMAssembly::setSourceLocation(SourceLocation const&) void EVMAssembly::appendInstruction(solidity::Instruction _instr) { - m_bytecode.push_back(byte(_instr)); + m_bytecode.push_back(uint8_t(_instr)); m_stackHeight += solidity::instructionInfo(_instr).ret - solidity::instructionInfo(_instr).args; } @@ -101,7 +101,7 @@ void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter) { if (m_evm15) { - m_bytecode.push_back(byte(solidity::Instruction::JUMPTO)); + m_bytecode.push_back(uint8_t(solidity::Instruction::JUMPTO)); appendLabelReferenceInternal(_labelId); m_stackHeight += _stackDiffAfter; } @@ -116,7 +116,7 @@ void EVMAssembly::appendJumpToIf(LabelID _labelId) { if (m_evm15) { - m_bytecode.push_back(byte(solidity::Instruction::JUMPIF)); + m_bytecode.push_back(uint8_t(solidity::Instruction::JUMPIF)); appendLabelReferenceInternal(_labelId); m_stackHeight--; } @@ -132,7 +132,7 @@ void EVMAssembly::appendBeginsub(LabelID _labelId, int _arguments) solAssert(m_evm15, "BEGINSUB used for EVM 1.0"); solAssert(_arguments >= 0, ""); setLabelToCurrentPosition(_labelId); - m_bytecode.push_back(byte(solidity::Instruction::BEGINSUB)); + m_bytecode.push_back(uint8_t(solidity::Instruction::BEGINSUB)); m_stackHeight += _arguments; } @@ -140,7 +140,7 @@ void EVMAssembly::appendJumpsub(LabelID _labelId, int _arguments, int _returns) { solAssert(m_evm15, "JUMPSUB used for EVM 1.0"); solAssert(_arguments >= 0 && _returns >= 0, ""); - m_bytecode.push_back(byte(solidity::Instruction::JUMPSUB)); + m_bytecode.push_back(uint8_t(solidity::Instruction::JUMPSUB)); appendLabelReferenceInternal(_labelId); m_stackHeight += _returns - _arguments; } @@ -149,7 +149,7 @@ void EVMAssembly::appendReturnsub(int _returns, int _stackDiffAfter) { solAssert(m_evm15, "RETURNSUB used for EVM 1.0"); solAssert(_returns >= 0, ""); - m_bytecode.push_back(byte(solidity::Instruction::RETURNSUB)); + m_bytecode.push_back(uint8_t(solidity::Instruction::RETURNSUB)); m_stackHeight += _stackDiffAfter - _returns; } @@ -198,5 +198,5 @@ void EVMAssembly::updateReference(size_t pos, size_t size, u256 value) solAssert(m_bytecode.size() >= size && pos <= m_bytecode.size() - size, ""); solAssert(value < (u256(1) << (8 * size)), ""); for (size_t i = 0; i < size; i++) - m_bytecode[pos + i] = byte((value >> (8 * (size - i - 1))) & 0xff); + m_bytecode[pos + i] = uint8_t((value >> (8 * (size - i - 1))) & 0xff); } diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 89086b4e..650a8c0a 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -201,18 +201,18 @@ void CodeTransform::operator()(assembly::Literal const& _literal) { m_assembly.setSourceLocation(_literal.location); if (_literal.kind == assembly::LiteralKind::Number) - m_assembly.appendConstant(u256(_literal.value)); + m_assembly.appendConstant(u256(_literal.value.str())); else if (_literal.kind == assembly::LiteralKind::Boolean) { - if (_literal.value == "true") + if (_literal.value.str() == "true") m_assembly.appendConstant(u256(1)); else m_assembly.appendConstant(u256(0)); } else { - solAssert(_literal.value.size() <= 32, ""); - m_assembly.appendConstant(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft))); + solAssert(_literal.value.str().size() <= 32, ""); + m_assembly.appendConstant(u256(h256(_literal.value.str(), h256::FromBinary, h256::AlignLeft))); } checkStackHeight(&_literal); } @@ -454,13 +454,13 @@ AbstractAssembly::LabelID CodeTransform::labelID(Scope::Label const& _label) return m_context->labelIDs[&_label]; } -AbstractAssembly::LabelID CodeTransform::functionEntryID(string const& _name, Scope::Function const& _function) +AbstractAssembly::LabelID CodeTransform::functionEntryID(YulString _name, Scope::Function const& _function) { if (!m_context->functionEntryIDs.count(&_function)) { AbstractAssembly::LabelID id = m_useNamedLabelsForFunctions ? - m_assembly.namedLabel(_name) : + m_assembly.namedLabel(_name.str()) : m_assembly.newLabelId(); m_context->functionEntryIDs[&_function] = id; } diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index 22ebbf43..c0de8ad6 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -117,7 +117,7 @@ private: /// @returns the label ID corresponding to the given label, allocating a new one if /// necessary. AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); - AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function); + AbstractAssembly::LabelID functionEntryID(YulString _name, solidity::assembly::Scope::Function const& _function); /// Generates code for an expression that is supposed to return a single value. void visitExpression(Expression const& _expression); diff --git a/libyul/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp index 4b7f21f8..d0c8dd45 100644 --- a/libyul/optimiser/ASTCopier.cpp +++ b/libyul/optimiser/ASTCopier.cpp @@ -111,14 +111,14 @@ Statement ASTCopier::operator()(Switch const& _switch) Statement ASTCopier::operator()(FunctionDefinition const& _function) { - string translatedName = translateIdentifier(_function.name); + YulString translatedName = translateIdentifier(_function.name); enterFunction(_function); ScopeGuard g([&]() { this->leaveFunction(_function); }); return FunctionDefinition{ _function.location, - move(translatedName), + translatedName, translateVector(_function.parameters), translateVector(_function.returnVariables), translate(_function.body) diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index 13369cef..b6aceee3 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -22,6 +22,8 @@ #include <libyul/ASTDataForward.h> +#include <libyul/YulString.h> + #include <boost/variant.hpp> #include <boost/optional.hpp> @@ -107,7 +109,7 @@ protected: virtual void leaveScope(Block const&) { } virtual void enterFunction(FunctionDefinition const&) { } virtual void leaveFunction(FunctionDefinition const&) { } - virtual std::string translateIdentifier(std::string const& _name) { return _name; } + virtual YulString translateIdentifier(YulString _name) { return _name; } }; template <typename T> diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h index 41617d55..38cb85ea 100644 --- a/libyul/optimiser/ASTWalker.h +++ b/libyul/optimiser/ASTWalker.h @@ -23,6 +23,7 @@ #include <libyul/ASTDataForward.h> #include <libyul/Exceptions.h> +#include <libyul/YulString.h> #include <boost/variant.hpp> #include <boost/optional.hpp> diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 23d15cad..64605362 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -43,15 +43,15 @@ void CommonSubexpressionEliminator::visit(Expression& _e) if (_e.type() == typeid(Identifier)) { Identifier& identifier = boost::get<Identifier>(_e); - string const& name = identifier.name; + YulString 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}; + YulString value = boost::get<Identifier>(*m_value.at(name)).name; + assertThrow(inScope(value), OptimizerException, ""); + _e = Identifier{locationOf(_e), value}; } } } @@ -61,6 +61,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e) for (auto const& var: m_value) { assertThrow(var.second, OptimizerException, ""); + assertThrow(inScope(var.first), OptimizerException, ""); if (SyntacticalEqualityChecker::equal(_e, *var.second)) { _e = Identifier{locationOf(_e), var.first}; diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index ca1e5153..134777d0 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -38,9 +38,9 @@ using namespace dev::yul; void DataFlowAnalyzer::operator()(Assignment& _assignment) { - set<string> names; + set<YulString> names; for (auto const& var: _assignment.variableNames) - names.insert(var.name); + names.emplace(var.name); assertThrow(_assignment.value, OptimizerException, ""); visit(*_assignment.value); handleAssignment(names, _assignment.value.get()); @@ -48,9 +48,9 @@ void DataFlowAnalyzer::operator()(Assignment& _assignment) void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl) { - set<string> names; + set<YulString> names; for (auto const& var: _varDecl.variables) - names.insert(var.name); + names.emplace(var.name); m_variableScopes.back().variables += names; if (_varDecl.value) visit(*_varDecl.value); @@ -69,7 +69,7 @@ void DataFlowAnalyzer::operator()(If& _if) void DataFlowAnalyzer::operator()(Switch& _switch) { visit(*_switch.expression); - set<string> assignedVariables; + set<YulString> assignedVariables; for (auto& _case: _switch.cases) { (*this)(_case.body); @@ -84,19 +84,32 @@ void DataFlowAnalyzer::operator()(Switch& _switch) void DataFlowAnalyzer::operator()(FunctionDefinition& _fun) { - m_variableScopes.emplace_back(true); + // Save all information. We might rather reinstantiate this class, + // but this could be difficult if it is subclassed. + map<YulString, Expression const*> value; + map<YulString, set<YulString>> references; + map<YulString, set<YulString>> referencedBy; + m_value.swap(value); + m_references.swap(references); + m_referencedBy.swap(referencedBy); + pushScope(true); + for (auto const& parameter: _fun.parameters) - m_variableScopes.back().variables.insert(parameter.name); + m_variableScopes.back().variables.emplace(parameter.name); for (auto const& var: _fun.returnVariables) - m_variableScopes.back().variables.insert(var.name); + m_variableScopes.back().variables.emplace(var.name); ASTModifier::operator()(_fun); - m_variableScopes.pop_back(); + + popScope(); + m_value.swap(value); + m_references.swap(references); + m_referencedBy.swap(referencedBy); } void DataFlowAnalyzer::operator()(ForLoop& _for) { // Special scope handling of the pre block. - m_variableScopes.emplace_back(false); + pushScope(false); for (auto& statement: _for.pre.statements) visit(statement); @@ -110,20 +123,19 @@ void DataFlowAnalyzer::operator()(ForLoop& _for) (*this)(_for.post); clearValues(assignments.names()); - - m_variableScopes.pop_back(); + popScope(); } void DataFlowAnalyzer::operator()(Block& _block) { size_t numScopes = m_variableScopes.size(); - m_variableScopes.emplace_back(false); + pushScope(false); ASTModifier::operator()(_block); - m_variableScopes.pop_back(); + popScope(); assertThrow(numScopes == m_variableScopes.size(), OptimizerException, ""); } -void DataFlowAnalyzer::handleAssignment(set<string> const& _variables, Expression* _value) +void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value) { clearValues(_variables); @@ -132,7 +144,7 @@ void DataFlowAnalyzer::handleAssignment(set<string> const& _variables, Expressio movableChecker.visit(*_value); if (_variables.size() == 1) { - string const& name = *_variables.begin(); + YulString name = *_variables.begin(); // Expression has to be movable and cannot contain a reference // to the variable that will be assigned to. if (_value && movableChecker.movable() && !movableChecker.referencedVariables().count(name)) @@ -144,11 +156,22 @@ void DataFlowAnalyzer::handleAssignment(set<string> const& _variables, Expressio { m_references[name] = referencedVariables; for (auto const& ref: referencedVariables) - m_referencedBy[ref].insert(name); + m_referencedBy[ref].emplace(name); } } -void DataFlowAnalyzer::clearValues(set<string> const& _variables) +void DataFlowAnalyzer::pushScope(bool _functionScope) +{ + m_variableScopes.emplace_back(_functionScope); +} + +void DataFlowAnalyzer::popScope() +{ + clearValues(std::move(m_variableScopes.back().variables)); + m_variableScopes.pop_back(); +} + +void DataFlowAnalyzer::clearValues(set<YulString> _variables) { // All variables that reference variables to be cleared also have to be // cleared, but not recursively, since only the value of the original @@ -163,16 +186,15 @@ void DataFlowAnalyzer::clearValues(set<string> const& _variables) // This cannot be easily tested since the substitutions will be done // one by one on the fly, and the last line will just be add(1, 1) - set<string> variables = _variables; // Clear variables that reference variables to be cleared. - for (auto const& name: variables) + for (auto const& name: _variables) for (auto const& ref: m_referencedBy[name]) - variables.insert(ref); + _variables.emplace(ref); // Clear the value and update the reference relation. - for (auto const& name: variables) + for (auto const& name: _variables) m_value.erase(name); - for (auto const& name: variables) + for (auto const& name: _variables) { for (auto const& ref: m_references[name]) m_referencedBy[ref].erase(name); @@ -180,7 +202,7 @@ void DataFlowAnalyzer::clearValues(set<string> const& _variables) } } -bool DataFlowAnalyzer::inScope(string const& _variableName) const +bool DataFlowAnalyzer::inScope(YulString _variableName) const { for (auto const& scope: m_variableScopes | boost::adaptors::reversed) { diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index f998eadf..a0c21eee 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -24,7 +24,8 @@ #include <libyul/optimiser/ASTWalker.h> -#include <string> +#include <libyul/YulString.h> + #include <map> #include <set> @@ -54,26 +55,32 @@ public: protected: /// Registers the assignment. - void handleAssignment(std::set<std::string> const& _names, Expression* _value); + void handleAssignment(std::set<YulString> const& _names, Expression* _value); + + /// Creates a new inner scope. + void pushScope(bool _functionScope); + + /// Removes the innermost scope and clears all variables in it. + void popScope(); /// Clears information about the values assigned to the given variables, /// for example at points where control flow is merged. - void clearValues(std::set<std::string> const& _names); + void clearValues(std::set<YulString> _names); /// Returns true iff the variable is in scope. - bool inScope(std::string const& _variableName) const; + bool inScope(YulString _variableName) const; /// Current values of variables, always movable. - std::map<std::string, Expression const*> m_value; + std::map<YulString, Expression const*> m_value; /// m_references[a].contains(b) <=> the current expression assigned to a references b - std::map<std::string, std::set<std::string>> m_references; + std::map<YulString, std::set<YulString>> m_references; /// m_referencedBy[b].contains(a) <=> the current expression assigned to a references b - std::map<std::string, std::set<std::string>> m_referencedBy; + std::map<YulString, std::set<YulString>> m_referencedBy; struct Scope { explicit Scope(bool _isFunction): isFunction(_isFunction) {} - std::set<std::string> variables; + std::set<YulString> variables; bool isFunction; }; /// List of scopes. diff --git a/libyul/optimiser/Disambiguator.cpp b/libyul/optimiser/Disambiguator.cpp index dcba97c9..4303f412 100644 --- a/libyul/optimiser/Disambiguator.cpp +++ b/libyul/optimiser/Disambiguator.cpp @@ -32,7 +32,7 @@ using namespace dev::solidity; using Scope = dev::solidity::assembly::Scope; -string Disambiguator::translateIdentifier(string const& _originalName) +YulString Disambiguator::translateIdentifier(YulString _originalName) { if ((m_externallyUsedIdentifiers.count(_originalName))) return _originalName; diff --git a/libyul/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h index 74a491ab..bfb65682 100644 --- a/libyul/optimiser/Disambiguator.h +++ b/libyul/optimiser/Disambiguator.h @@ -45,7 +45,7 @@ class Disambiguator: public ASTCopier public: explicit Disambiguator( solidity::assembly::AsmAnalysisInfo const& _analysisInfo, - std::set<std::string> const& _externallyUsedIdentifiers = {} + std::set<YulString> const& _externallyUsedIdentifiers = {} ): m_info(_analysisInfo), m_externallyUsedIdentifiers(_externallyUsedIdentifiers), m_nameDispenser(m_externallyUsedIdentifiers) { @@ -56,16 +56,16 @@ protected: virtual void leaveScope(Block const& _block) override; virtual void enterFunction(FunctionDefinition const& _function) override; virtual void leaveFunction(FunctionDefinition const& _function) override; - virtual std::string translateIdentifier(std::string const& _name) override; + virtual YulString translateIdentifier(YulString _name) override; void enterScopeInternal(solidity::assembly::Scope& _scope); void leaveScopeInternal(solidity::assembly::Scope& _scope); solidity::assembly::AsmAnalysisInfo const& m_info; - std::set<std::string> const& m_externallyUsedIdentifiers; + std::set<YulString> const& m_externallyUsedIdentifiers; std::vector<solidity::assembly::Scope*> m_scopes; - std::map<void const*, std::string> m_translations; + std::map<void const*, YulString> m_translations; NameDispenser m_nameDispenser; }; diff --git a/libyul/optimiser/ExpressionInliner.cpp b/libyul/optimiser/ExpressionInliner.cpp index 9bf0a3fb..07e88191 100644 --- a/libyul/optimiser/ExpressionInliner.cpp +++ b/libyul/optimiser/ExpressionInliner.cpp @@ -62,7 +62,7 @@ void ExpressionInliner::visit(Expression& _expression) if (m_inlinableFunctions.count(funCall.functionName.name) && movable) { FunctionDefinition const& fun = *m_inlinableFunctions.at(funCall.functionName.name); - map<string, Expression const*> substitutions; + map<YulString, Expression const*> substitutions; for (size_t i = 0; i < fun.parameters.size(); ++i) substitutions[fun.parameters[i].name] = &funCall.arguments[i]; _expression = Substitution(substitutions).translate(*boost::get<Assignment>(fun.body.statements.front()).value); diff --git a/libyul/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h index 971a2ee0..d903664f 100644 --- a/libyul/optimiser/ExpressionInliner.h +++ b/libyul/optimiser/ExpressionInliner.h @@ -59,10 +59,10 @@ public: virtual void visit(Expression& _expression) override; private: - std::map<std::string, FunctionDefinition const*> m_inlinableFunctions; - std::map<std::string, std::string> m_varReplacements; + std::map<YulString, FunctionDefinition const*> m_inlinableFunctions; + std::map<YulString, YulString> m_varReplacements; /// Set of functions we are currently visiting inside. - std::set<std::string> m_currentFunctions; + std::set<YulString> m_currentFunctions; Block& m_block; }; diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp index c3957497..7e57a629 100644 --- a/libyul/optimiser/ExpressionJoiner.cpp +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -46,20 +46,6 @@ 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(); @@ -79,13 +65,11 @@ 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) + if (isLatestStatementVarDeclJoinable(identifier)) { 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(); @@ -103,9 +87,7 @@ void ExpressionJoiner::run(Block& _ast) ExpressionJoiner::ExpressionJoiner(Block& _ast) { - ReferencesCounter counter; - counter(_ast); - m_references = counter.references(); + m_references = ReferencesCounter::countReferences(_ast); } void ExpressionJoiner::handleArguments(vector<Expression>& _arguments) @@ -154,7 +136,7 @@ Statement* ExpressionJoiner::latestStatement() return &m_currentBlock->statements.at(m_latestStatementInBlock); } -bool ExpressionJoiner::isLatestStatementVarDeclOf(Identifier const& _identifier) +bool ExpressionJoiner::isLatestStatementVarDeclJoinable(Identifier const& _identifier) { Statement const* statement = latestStatement(); if (!statement || statement->type() != typeid(VariableDeclaration)) @@ -162,5 +144,7 @@ bool ExpressionJoiner::isLatestStatementVarDeclOf(Identifier const& _identifier) VariableDeclaration const& varDecl = boost::get<VariableDeclaration>(*statement); if (varDecl.variables.size() != 1 || !varDecl.value) return false; - return varDecl.variables.at(0).name == _identifier.name; + assertThrow(varDecl.variables.size() == 1, OptimizerException, ""); + assertThrow(varDecl.value, OptimizerException, ""); + return varDecl.variables.at(0).name == _identifier.name && m_references[_identifier.name] == 1; } diff --git a/libyul/optimiser/ExpressionJoiner.h b/libyul/optimiser/ExpressionJoiner.h index df18e58f..0cc61981 100644 --- a/libyul/optimiser/ExpressionJoiner.h +++ b/libyul/optimiser/ExpressionJoiner.h @@ -73,29 +73,29 @@ class NameCollector; 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 operator()(Block& _block) override; + void operator()(FunctionalInstruction&) override; + void operator()(FunctionCall&) override; + + using ASTModifier::visit; + void visit(Expression& _e) override; + void handleArguments(std::vector<Expression>& _arguments); void decrementLatestStatementPointer(); void resetLatestStatementPointer(); Statement* latestStatement(); - bool isLatestStatementVarDeclOf(Identifier const& _identifier); + bool isLatestStatementVarDeclJoinable(Identifier const& _identifier); - Block* m_currentBlock = nullptr; - size_t m_latestStatementInBlock = 0; - std::map<std::string, size_t> m_references; +private: + Block* m_currentBlock = nullptr; ///< Pointer to current block holding the statement being visited. + size_t m_latestStatementInBlock = 0; ///< Offset to m_currentBlock's statements of the last visited statement. + std::map<YulString, size_t> m_references; ///< Holds reference counts to all variable declarations in current block. }; } diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index 5419ff6a..5965a1bb 100644 --- a/libyul/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -44,11 +44,11 @@ public: static void run(Block& _ast); private: - explicit ExpressionSimplifier(std::map<std::string, Expression const*> _ssaValues): + explicit ExpressionSimplifier(std::map<YulString, Expression const*> _ssaValues): m_ssaValues(std::move(_ssaValues)) {} - std::map<std::string, Expression const*> m_ssaValues; + std::map<YulString, Expression const*> m_ssaValues; }; } diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index a2ecc546..a4b7a909 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -95,10 +95,10 @@ void ExpressionSplitter::outlineExpression(Expression& _expr) visit(_expr); SourceLocation location = locationOf(_expr); - string var = m_nameDispenser.newName(""); + YulString var = m_nameDispenser.newName({}); m_statementsToPrefix.emplace_back(VariableDeclaration{ location, - {{TypedName{location, var, ""}}}, + {{TypedName{location, var, {}}}}, make_shared<Expression>(std::move(_expr)) }); _expr = Identifier{location, var}; diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index ce71eda5..c9057cf3 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -40,37 +40,36 @@ using namespace dev; using namespace dev::yul; using namespace dev::solidity; -FullInliner::FullInliner(Block& _ast): - m_ast(_ast), m_nameDispenser(_ast) +FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): + m_ast(_ast), m_nameDispenser(_dispenser) { - assertThrow(m_ast.statements.size() >= 1, OptimizerException, ""); - assertThrow(m_ast.statements.front().type() == typeid(Block), OptimizerException, ""); - // Determine constants SSAValueTracker tracker; tracker(m_ast); for (auto const& ssaValue: tracker.values()) if (ssaValue.second && ssaValue.second->type() == typeid(Literal)) - m_constants.insert(ssaValue.first); + m_constants.emplace(ssaValue.first); - map<string, size_t> references = ReferencesCounter::countReferences(m_ast); - for (size_t i = 1; i < m_ast.statements.size(); ++i) + map<YulString, size_t> references = ReferencesCounter::countReferences(m_ast); + for (auto& statement: m_ast.statements) { - assertThrow(m_ast.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, ""); - FunctionDefinition& fun = boost::get<FunctionDefinition>(m_ast.statements.at(i)); + if (statement.type() != typeid(FunctionDefinition)) + continue; + FunctionDefinition& fun = boost::get<FunctionDefinition>(statement); m_functions[fun.name] = &fun; // Always inline functions that are only called once. if (references[fun.name] == 1) - m_alwaysInline.insert(fun.name); + m_alwaysInline.emplace(fun.name); updateCodeSize(fun); } } void FullInliner::run() { - assertThrow(m_ast.statements[0].type() == typeid(Block), OptimizerException, ""); + for (auto& statement: m_ast.statements) + if (statement.type() == typeid(Block)) + handleBlock({}, boost::get<Block>(statement)); - handleBlock("", boost::get<Block>(m_ast.statements[0])); // TODO it might be good to determine a visiting order: // first handle functions that are called from many places. for (auto const& fun: m_functions) @@ -85,12 +84,12 @@ void FullInliner::updateCodeSize(FunctionDefinition& fun) m_functionSizes[fun.name] = CodeSize::codeSize(fun.body); } -void FullInliner::handleBlock(string const& _currentFunctionName, Block& _block) +void FullInliner::handleBlock(YulString _currentFunctionName, Block& _block) { InlineModifier{*this, m_nameDispenser, _currentFunctionName}(_block); } -bool FullInliner::shallInline(FunctionCall const& _funCall, string const& _callSite) +bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) { // No recursive inlining if (_funCall.functionName.name == _callSite) @@ -149,14 +148,14 @@ boost::optional<vector<Statement>> InlineModifier::tryInlineStatement(Statement& vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionCall& _funCall) { vector<Statement> newStatements; - map<string, string> variableReplacements; + map<YulString, YulString> variableReplacements; FunctionDefinition& function = m_driver.function(_funCall.functionName.name); // helper function to create a new variable that is supposed to model // an existing variable. auto newVariable = [&](TypedName const& _existingVariable, Expression* _value) { - string newName = m_nameDispenser.newName(_existingVariable.name, function.name); + YulString newName = m_nameDispenser.newName(_existingVariable.name, function.name); variableReplacements[_existingVariable.name] = newName; VariableDeclaration varDecl{_funCall.location, {{_funCall.location, newName, _existingVariable.type}}, {}}; if (_value) @@ -215,7 +214,7 @@ Statement BodyCopier::operator()(FunctionDefinition const& _funDef) return _funDef; } -string BodyCopier::translateIdentifier(string const& _name) +YulString BodyCopier::translateIdentifier(YulString _name) { if (m_variableReplacements.count(_name)) return m_variableReplacements.at(_name); diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index cd59ab46..66ce8e2f 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -71,30 +71,30 @@ class NameCollector; class FullInliner: public ASTModifier { public: - explicit FullInliner(Block& _ast); + explicit FullInliner(Block& _ast, NameDispenser& _dispenser); void run(); /// Inlining heuristic. /// @param _callSite the name of the function in which the function call is located. - bool shallInline(FunctionCall const& _funCall, std::string const& _callSite); + bool shallInline(FunctionCall const& _funCall, YulString _callSite); - FunctionDefinition& function(std::string _name) { return *m_functions.at(_name); } + FunctionDefinition& function(YulString _name) { return *m_functions.at(_name); } private: void updateCodeSize(FunctionDefinition& fun); - void handleBlock(std::string const& _currentFunctionName, Block& _block); + void handleBlock(YulString _currentFunctionName, Block& _block); /// The AST to be modified. The root block itself will not be modified, because /// we store pointers to functions. Block& m_ast; - std::map<std::string, FunctionDefinition*> m_functions; + std::map<YulString, FunctionDefinition*> m_functions; /// Names of functions to always inline. - std::set<std::string> m_alwaysInline; + std::set<YulString> m_alwaysInline; /// Variables that are constants (used for inlining heuristic) - std::set<std::string> m_constants; - std::map<std::string, size_t> m_functionSizes; - NameDispenser m_nameDispenser; + std::set<YulString> m_constants; + std::map<YulString, size_t> m_functionSizes; + NameDispenser& m_nameDispenser; }; /** @@ -104,7 +104,7 @@ private: class InlineModifier: public ASTModifier { public: - InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, std::string _functionName): + InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, YulString _functionName): m_currentFunction(std::move(_functionName)), m_driver(_driver), m_nameDispenser(_nameDispenser) @@ -116,7 +116,7 @@ private: boost::optional<std::vector<Statement>> tryInlineStatement(Statement& _statement); std::vector<Statement> performInline(Statement& _statement, FunctionCall& _funCall); - std::string m_currentFunction; + YulString m_currentFunction; FullInliner& m_driver; NameDispenser& m_nameDispenser; }; @@ -131,8 +131,8 @@ class BodyCopier: public ASTCopier public: BodyCopier( NameDispenser& _nameDispenser, - std::string const& _varNamePrefix, - std::map<std::string, std::string> const& _variableReplacements + YulString _varNamePrefix, + std::map<YulString, YulString> const& _variableReplacements ): m_nameDispenser(_nameDispenser), m_varNamePrefix(_varNamePrefix), @@ -144,11 +144,11 @@ public: virtual Statement operator()(VariableDeclaration const& _varDecl) override; virtual Statement operator()(FunctionDefinition const& _funDef) override; - virtual std::string translateIdentifier(std::string const& _name) override; + virtual YulString translateIdentifier(YulString _name) override; NameDispenser& m_nameDispenser; - std::string const& m_varNamePrefix; - std::map<std::string, std::string> m_variableReplacements; + YulString m_varNamePrefix; + std::map<YulString, YulString> m_variableReplacements; }; diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp index 69dd2095..deaaee97 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp @@ -44,7 +44,7 @@ void InlinableExpressionFunctionFinder::operator()(FunctionDefinition const& _fu { if (_function.returnVariables.size() == 1 && _function.body.statements.size() == 1) { - string const& retVariable = _function.returnVariables.front().name; + YulString retVariable = _function.returnVariables.front().name; Statement const& bodyStatement = _function.body.statements.front(); if (bodyStatement.type() == typeid(Assignment)) { @@ -57,7 +57,7 @@ void InlinableExpressionFunctionFinder::operator()(FunctionDefinition const& _fu // would not be valid here if we were searching inside a functionally inlinable // function body. assertThrow(m_disallowedIdentifiers.empty() && !m_foundDisallowedIdentifier, OptimizerException, ""); - m_disallowedIdentifiers = set<string>{retVariable, _function.name}; + m_disallowedIdentifiers = set<YulString>{retVariable, _function.name}; boost::apply_visitor(*this, *assignment.value); if (!m_foundDisallowedIdentifier) m_inlinableFunctions[_function.name] = &_function; diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.h b/libyul/optimiser/InlinableExpressionFunctionFinder.h index 3887e6e5..baf4bbfc 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.h +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.h @@ -43,7 +43,7 @@ class InlinableExpressionFunctionFinder: public ASTWalker { public: - std::map<std::string, FunctionDefinition const*> const& inlinableFunctions() const + std::map<YulString, FunctionDefinition const*> const& inlinableFunctions() const { return m_inlinableFunctions; } @@ -54,15 +54,15 @@ public: virtual void operator()(FunctionDefinition const& _function) override; private: - void checkAllowed(std::string const& _name) + void checkAllowed(YulString _name) { if (m_disallowedIdentifiers.count(_name)) m_foundDisallowedIdentifier = true; } bool m_foundDisallowedIdentifier = false; - std::set<std::string> m_disallowedIdentifiers; - std::map<std::string, FunctionDefinition const*> m_inlinableFunctions; + std::set<YulString> m_disallowedIdentifiers; + std::map<YulString, FunctionDefinition const*> m_inlinableFunctions; }; } diff --git a/libyul/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp index c8f35207..f3306598 100644 --- a/libyul/optimiser/MainFunction.cpp +++ b/libyul/optimiser/MainFunction.cpp @@ -40,12 +40,12 @@ void MainFunction::operator()(Block& _block) for (size_t i = 1; i < _block.statements.size(); ++i) assertThrow(_block.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, ""); /// @todo this should handle scopes properly and instead of an assertion it should rename the conflicting function - assertThrow(NameCollector(_block).names().count("main") == 0, OptimizerException, ""); + assertThrow(NameCollector(_block).names().count(YulString{"main"}) == 0, OptimizerException, ""); Block& block = boost::get<Block>(_block.statements[0]); FunctionDefinition main{ block.location, - "main", + YulString{"main"}, {}, {}, std::move(block) diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index b71fa982..36f55b99 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -29,16 +29,16 @@ using namespace dev::yul; void NameCollector::operator()(VariableDeclaration const& _varDecl) { for (auto const& var: _varDecl.variables) - m_names.insert(var.name); + m_names.emplace(var.name); } void NameCollector::operator ()(FunctionDefinition const& _funDef) { - m_names.insert(_funDef.name); + m_names.emplace(_funDef.name); for (auto const arg: _funDef.parameters) - m_names.insert(arg.name); + m_names.emplace(arg.name); for (auto const ret: _funDef.returnVariables) - m_names.insert(ret.name); + m_names.emplace(ret.name); ASTWalker::operator ()(_funDef); } @@ -53,14 +53,14 @@ void ReferencesCounter::operator()(FunctionCall const& _funCall) ASTWalker::operator()(_funCall); } -map<string, size_t> ReferencesCounter::countReferences(Block const& _block) +map<YulString, size_t> ReferencesCounter::countReferences(Block const& _block) { ReferencesCounter counter; counter(_block); return counter.references(); } -map<string, size_t> ReferencesCounter::countReferences(Expression const& _expression) +map<YulString, size_t> ReferencesCounter::countReferences(Expression const& _expression) { ReferencesCounter counter; counter.visit(_expression); @@ -70,5 +70,5 @@ map<string, size_t> ReferencesCounter::countReferences(Expression const& _expres void Assignments::operator()(Assignment const& _assignment) { for (auto const& var: _assignment.variableNames) - m_names.insert(var.name); + m_names.emplace(var.name); } diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index b8f6c1d7..b76eec30 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -22,7 +22,6 @@ #include <libyul/optimiser/ASTWalker.h> -#include <string> #include <map> #include <set> @@ -46,9 +45,9 @@ public: virtual void operator()(VariableDeclaration const& _varDecl) override; virtual void operator()(FunctionDefinition const& _funDef) override; - std::set<std::string> names() const { return m_names; } + std::set<YulString> names() const { return m_names; } private: - std::set<std::string> m_names; + std::set<YulString> m_names; }; /** @@ -61,12 +60,12 @@ public: virtual void operator()(Identifier const& _identifier); virtual void operator()(FunctionCall const& _funCall); - static std::map<std::string, size_t> countReferences(Block const& _block); - static std::map<std::string, size_t> countReferences(Expression const& _expression); + static std::map<YulString, size_t> countReferences(Block const& _block); + static std::map<YulString, size_t> countReferences(Expression const& _expression); - std::map<std::string, size_t> const& references() const { return m_references; } + std::map<YulString, size_t> const& references() const { return m_references; } private: - std::map<std::string, size_t> m_references; + std::map<YulString, size_t> m_references; }; /** @@ -78,9 +77,9 @@ public: using ASTWalker::operator (); virtual void operator()(Assignment const& _assignment) override; - std::set<std::string> const& names() const { return m_names; } + std::set<YulString> const& names() const { return m_names; } private: - std::set<std::string> m_names; + std::set<YulString> m_names; }; } diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index d3f10bc2..3c870fa5 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -33,31 +33,30 @@ NameDispenser::NameDispenser(Block const& _ast): { } -NameDispenser::NameDispenser(set<string> _usedNames): +NameDispenser::NameDispenser(set<YulString> _usedNames): m_usedNames(std::move(_usedNames)) { } -string NameDispenser::newName(string const& _nameHint, string const& _context) +YulString NameDispenser::newName(YulString _nameHint, YulString _context) { // Shortening rules: Use a suffix of _prefix and a prefix of _context. - string prefix = _nameHint; + YulString prefix = _nameHint; if (!_context.empty()) - prefix = _context.substr(0, 10) + "_" + prefix; + prefix = YulString{_context.str().substr(0, 10) + "_" + prefix.str()}; return newNameInternal(prefix); } -string NameDispenser::newNameInternal(string const& _nameHint) +YulString NameDispenser::newNameInternal(YulString _nameHint) { - size_t suffix = 0; - string name = _nameHint; + YulString name = _nameHint; while (name.empty() || m_usedNames.count(name)) { - suffix++; - name = _nameHint + "_" + to_string(suffix); + m_counter++; + name = YulString(_nameHint.str() + "_" + to_string(m_counter)); } - m_usedNames.insert(name); + m_usedNames.emplace(name); return name; } diff --git a/libyul/optimiser/NameDispenser.h b/libyul/optimiser/NameDispenser.h index 5fbf5f8e..7311440b 100644 --- a/libyul/optimiser/NameDispenser.h +++ b/libyul/optimiser/NameDispenser.h @@ -21,8 +21,9 @@ #include <libyul/ASTDataForward.h> +#include <libyul/YulString.h> + #include <set> -#include <string> namespace dev { @@ -41,18 +42,19 @@ public: /// Initialize the name dispenser with all the names used in the given AST. explicit NameDispenser(Block const& _ast); /// Initialize the name dispenser with the given used names. - explicit NameDispenser(std::set<std::string> _usedNames); + explicit NameDispenser(std::set<YulString> _usedNames); /// @returns a currently unused name that should be similar to _nameHint /// and prefixed by _context if present. /// If the resulting name would be too long, trims the context at the end /// and the name hint at the start. - std::string newName(std::string const& _nameHint, std::string const& _context = {}); + YulString newName(YulString _nameHint, YulString _context = {}); private: - std::string newNameInternal(std::string const& _nameHint); + YulString newNameInternal(YulString _nameHint); - std::set<std::string> m_usedNames; + std::set<YulString> m_usedNames; + size_t m_counter = 0; }; } diff --git a/libyul/optimiser/README.md b/libyul/optimiser/README.md index faef818b..c2575179 100644 --- a/libyul/optimiser/README.md +++ b/libyul/optimiser/README.md @@ -135,6 +135,8 @@ If there are two assignments to a variable where the first one is a movable expr and the variable is not used between the two assignments (and the second is not inside a loop or conditional, the first one is not inside), the first assignment is removed. +This step also removes movable expression statements. + ## Function Unifier diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp new file mode 100644 index 00000000..775b7673 --- /dev/null +++ b/libyul/optimiser/RedundantAssignEliminator.cpp @@ -0,0 +1,193 @@ +/* + 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 removes assignments to variables that are not used + * until they go out of scope or are re-assigned. + */ + +#include <libyul/optimiser/RedundantAssignEliminator.h> + +#include <libyul/optimiser/Semantics.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/algorithm_ext/erase.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void RedundantAssignEliminator::operator()(Identifier const& _identifier) +{ + changeUndecidedTo(_identifier.name, State::Used); +} + +void RedundantAssignEliminator::operator()(VariableDeclaration const& _variableDeclaration) +{ + ASTWalker::operator()(_variableDeclaration); + + for (auto const& var: _variableDeclaration.variables) + m_declaredVariables.emplace(var.name); +} + +void RedundantAssignEliminator::operator()(Assignment const& _assignment) +{ + visit(*_assignment.value); + for (auto const& var: _assignment.variableNames) + changeUndecidedTo(var.name, State::Unused); + + if (_assignment.variableNames.size() == 1) + // Default-construct it in "Undecided" state if it does not yet exist. + m_assignments[_assignment.variableNames.front().name][&_assignment]; +} + +void RedundantAssignEliminator::operator()(If const& _if) +{ + visit(*_if.condition); + + RedundantAssignEliminator branch{*this}; + branch(_if.body); + + join(branch); +} + +void RedundantAssignEliminator::operator()(Switch const& _switch) +{ + visit(*_switch.expression); + + bool hasDefault = false; + vector<RedundantAssignEliminator> branches; + for (auto const& c: _switch.cases) + { + if (!c.value) + hasDefault = true; + branches.emplace_back(*this); + branches.back()(c.body); + } + + if (hasDefault) + { + *this = std::move(branches.back()); + branches.pop_back(); + } + for (auto& branch: branches) + join(branch); +} + +void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDefinition) +{ + (*this)(_functionDefinition.body); + + for (auto const& param: _functionDefinition.parameters) + changeUndecidedTo(param.name, State::Unused); + for (auto const& retParam: _functionDefinition.returnVariables) + changeUndecidedTo(retParam.name, State::Used); +} + +void RedundantAssignEliminator::operator()(ForLoop const& _forLoop) +{ + // This will set all variables that are declared in this + // block to "unused" when it is destroyed. + BlockScope scope(*this); + + // We need to visit the statements directly because of the + // scoping rules. + walkVector(_forLoop.pre.statements); + + // We just run the loop twice to account for the + // back edge. + // There need not be more runs because we only have three different states. + + visit(*_forLoop.condition); + + RedundantAssignEliminator zeroRuns{*this}; + + (*this)(_forLoop.body); + (*this)(_forLoop.post); + + visit(*_forLoop.condition); + + RedundantAssignEliminator oneRun{*this}; + + (*this)(_forLoop.body); + (*this)(_forLoop.post); + + visit(*_forLoop.condition); + + // Order does not matter because "max" is commutative and associative. + join(oneRun); + join(zeroRuns); +} + +void RedundantAssignEliminator::operator()(Block const& _block) +{ + // This will set all variables that are declared in this + // block to "unused" when it is destroyed. + BlockScope scope(*this); + + ASTWalker::operator()(_block); +} + +void RedundantAssignEliminator::run(Block& _ast) +{ + RedundantAssignEliminator rae; + rae(_ast); + + std::set<Assignment const*> assignmentsToRemove; + for (auto const& variables: rae.m_assignments) + for (auto const& assignment: variables.second) + { + assertThrow(assignment.second != State::Undecided, OptimizerException, ""); + if (assignment.second == State::Unused && MovableChecker{*assignment.first->value}.movable()) + assignmentsToRemove.emplace(assignment.first); + } + + AssignmentRemover remover{assignmentsToRemove}; + remover(_ast); +} + +void RedundantAssignEliminator::join(RedundantAssignEliminator& _other) +{ + for (auto& var: _other.m_assignments) + if (m_assignments.count(var.first)) + { + map<Assignment const*, State>& assignmentsHere = m_assignments[var.first]; + for (auto& assignment: var.second) + assignmentsHere[assignment.first].join(assignment.second); + } + else + m_assignments[var.first] = std::move(var.second); +} + +void RedundantAssignEliminator::changeUndecidedTo(YulString _variable, RedundantAssignEliminator::State _newState) +{ + for (auto& assignment: m_assignments[_variable]) + if (assignment.second == State{State::Undecided}) + assignment.second = _newState; +} + +void AssignmentRemover::operator()(Block& _block) +{ + boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool { + return _statement.type() == typeid(Assignment) && m_toRemove.count(&boost::get<Assignment>(_statement)); + }); + + ASTModifier::operator()(_block); +} diff --git a/libyul/optimiser/RedundantAssignEliminator.h b/libyul/optimiser/RedundantAssignEliminator.h new file mode 100644 index 00000000..805a1f63 --- /dev/null +++ b/libyul/optimiser/RedundantAssignEliminator.h @@ -0,0 +1,186 @@ +/* + 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 removes assignments to variables that are not used + * until they go out of scope or are re-assigned. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> + +#include <map> + +namespace dev +{ +namespace yul +{ + +/** + * Optimiser component that removes assignments to variables that are not used + * until they go out of scope or are re-assigned. This component + * respects the control-flow and takes it into account for removal. + * + * Example: + * + * { + * let a + * a := 1 + * a := 2 + * b := 2 + * if calldataload(0) + * { + * b := mload(a) + * } + * a := b + * } + * + * In the example, "a := 1" can be removed because the value from this assignment + * is not used in any control-flow branch (it is replaced right away). + * The assignment "a := 2" is also overwritten by "a := b" at the end, + * but there is a control-flow path (through the condition body) which uses + * the value from "a := 2" and thus, this assignment cannot be removed. + * + * Detailed rules: + * + * The AST is traversed twice: in an information gathering step and in the + * actual removal step. During information gathering, we maintain a + * mapping from assignment statements to the three states + * "unused", "undecided" and "used". + * When an assignment is visited, it is added to the mapping in the "undecided" state + * (see remark about for loops below) and every other assignment to the same variable + * that is still in the "undecided" state is changed to "unused". + * When a variable is referenced, the state of any assignment to that variable still + * in the "undecided" state is changed to "used". + * At points where control flow splits, a copy + * of the mapping is handed over to each branch. At points where control flow + * joins, the two mappings coming from the two branches are combined in the following way: + * Statements that are only in one mapping or have the same state are used unchanged. + * Conflicting values are resolved in the following way: + * "unused", "undecided" -> "undecided" + * "unused", "used" -> "used" + * "undecided, "used" -> "used". + * + * For for-loops, the condition, body and post-part are visited twice, taking + * the joining control-flow at the condition into account. + * In other words, we create three control flow paths: Zero runs of the loop, + * one run and two runs and then combine them at the end. + * Running at most twice is enough because there are only three different states. + * + * For switch statements that have a "default"-case, there is no control-flow + * part that skips the switch. + * + * When a variable goes out of scope, all statements still in the "undecided" + * state are changed to "unused", unless the variable is the return + * parameter of a function - there, the state changes to "used". + * + * In the second traversal, all assignments that are in the "unused" state are removed. + * + * + * This step is usually run right after the SSA transform to complete + * the generation of the pseudo-SSA. + * + * Prerequisite: Disambiguator. + */ +class RedundantAssignEliminator: public ASTWalker +{ +public: + RedundantAssignEliminator(RedundantAssignEliminator const&) = default; + RedundantAssignEliminator& operator=(RedundantAssignEliminator const&) = default; + RedundantAssignEliminator(RedundantAssignEliminator&&) = default; + RedundantAssignEliminator& operator=(RedundantAssignEliminator&&) = default; + + void operator()(Identifier const& _identifier) override; + void operator()(VariableDeclaration const& _variableDeclaration) override; + void operator()(Assignment const& _assignment) override; + void operator()(If const& _if) override; + void operator()(Switch const& _switch) override; + void operator()(FunctionDefinition const&) override; + void operator()(ForLoop const&) override; + void operator()(Block const& _block) override; + + static void run(Block& _ast); + +private: + RedundantAssignEliminator() {} + + class State + { + public: + enum Value { Unused, Undecided, Used }; + State(Value _value = Undecided): m_value(_value) {} + inline bool operator==(State _other) const { return m_value == _other.m_value; } + inline bool operator!=(State _other) const { return !operator==(_other); } + inline void join(State const& _other) + { + // Using "max" works here because of the order of the values in the enum. + m_value = Value(std::max(int(_other.m_value), int(m_value))); + } + private: + Value m_value = Undecided; + }; + + /** + * Takes care about storing the list of declared variables and + * sets them to "unused" when it is destroyed. + */ + class BlockScope + { + public: + explicit BlockScope(RedundantAssignEliminator& _rae): m_rae(_rae) + { + swap(m_rae.m_declaredVariables, m_outerDeclaredVariables); + } + ~BlockScope() + { + for (auto const& var: m_rae.m_declaredVariables) + m_rae.changeUndecidedTo(var, State::Unused); + swap(m_rae.m_declaredVariables, m_outerDeclaredVariables); + } + + private: + RedundantAssignEliminator& m_rae; + std::set<YulString> m_outerDeclaredVariables; + }; + + /// Joins the assignment mapping with @a _other according to the rules laid out + /// above. + /// Will destroy @a _other. + void join(RedundantAssignEliminator& _other); + void changeUndecidedTo(YulString _variable, State _newState); + + std::set<YulString> m_declaredVariables; + // TODO check that this does not cause nondeterminism! + std::map<YulString, std::map<Assignment const*, State>> m_assignments; +}; + +class AssignmentRemover: public ASTModifier +{ +public: + explicit AssignmentRemover(std::set<Assignment const*> const& _toRemove): + m_toRemove(_toRemove) + {} + void operator()(Block& _block) override; + +private: + std::set<Assignment const*> const& m_toRemove; +}; + +} +} diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index dd6653ea..38d50ef4 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -37,17 +37,12 @@ void Rematerialiser::visit(Expression& _e) Identifier& identifier = boost::get<Identifier>(_e); if (m_value.count(identifier.name)) { - string name = identifier.name; - bool expressionValid = true; + YulString name = identifier.name; for (auto const& ref: m_references[name]) - if (!inScope(ref)) - { - expressionValid = false; - break; - } + assertThrow(inScope(ref), OptimizerException, ""); assertThrow(m_value.at(name), OptimizerException, ""); auto const& value = *m_value.at(name); - if (expressionValid && CodeSize::codeSize(value) <= 7) + if (CodeSize::codeSize(value) <= 7) _e = (ASTCopier{}).translate(value); } } diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index afcfab3e..f82465eb 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -22,10 +22,6 @@ #include <libyul/optimiser/DataFlowAnalyzer.h> -#include <string> -#include <map> -#include <set> - namespace dev { namespace yul diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp new file mode 100644 index 00000000..f209ee7b --- /dev/null +++ b/libyul/optimiser/SSATransform.cpp @@ -0,0 +1,130 @@ +/* + 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 subsequent assignments to variable declarations + * and assignments. + */ + +#include <libyul/optimiser/SSATransform.h> + +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/NameDispenser.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void SSATransform::operator()(Identifier& _identifier) +{ + if (m_currentVariableValues.count(_identifier.name)) + _identifier.name = m_currentVariableValues[_identifier.name]; +} + +void SSATransform::operator()(ForLoop& _for) +{ + // This will clear the current value in case of a reassignment inside the + // init part, although the new variable would still be in scope inside the whole loop. + // This small inefficiency is fine if we move the pre part of all for loops out + // of the for loop. + (*this)(_for.pre); + + Assignments assignments; + assignments(_for.body); + assignments(_for.post); + for (auto const& var: assignments.names()) + m_currentVariableValues.erase(var); + + visit(*_for.condition); + (*this)(_for.body); + (*this)(_for.post); +} + + +void SSATransform::operator()(Block& _block) +{ + set<YulString> variablesToClearAtEnd; + + // Creates a new variable (and returns its declaration) with value _value + // and replaces _value by a reference to that new variable. + auto replaceByNew = [&](SourceLocation _loc, YulString _varName, YulString _type, shared_ptr<Expression>& _value) -> VariableDeclaration + { + YulString newName = m_nameDispenser.newName(_varName); + m_currentVariableValues[_varName] = newName; + variablesToClearAtEnd.emplace(_varName); + shared_ptr<Expression> v = make_shared<Expression>(Identifier{_loc, newName}); + _value.swap(v); + return VariableDeclaration{_loc, {TypedName{_loc, std::move(newName), std::move(_type)}}, std::move(v)}; + }; + + iterateReplacing( + _block.statements, + [&](Statement& _s) -> boost::optional<vector<Statement>> + { + if (_s.type() == typeid(VariableDeclaration)) + { + VariableDeclaration& varDecl = boost::get<VariableDeclaration>(_s); + if (varDecl.value) + visit(*varDecl.value); + if (varDecl.variables.size() != 1 || !m_variablesToReplace.count(varDecl.variables.front().name)) + return {}; + // Replace "let a := v" by "let a_1 := v let a := v" + VariableDeclaration newVarDecl = replaceByNew( + varDecl.location, + varDecl.variables.front().name, + varDecl.variables.front().type, + varDecl.value + ); + return vector<Statement>{std::move(newVarDecl), std::move(varDecl)}; + } + else if (_s.type() == typeid(Assignment)) + { + Assignment& assignment = boost::get<Assignment>(_s); + visit(*assignment.value); + if (assignment.variableNames.size() != 1) + return {}; + assertThrow(m_variablesToReplace.count(assignment.variableNames.front().name), OptimizerException, ""); + // Replace "a := v" by "let a_1 := v a := v" + VariableDeclaration newVarDecl = replaceByNew( + assignment.location, + assignment.variableNames.front().name, + {}, // TODO determine type + assignment.value + ); + return vector<Statement>{std::move(newVarDecl), std::move(assignment)}; + } + else + visit(_s); + return {}; + } + ); + for (auto const& var: variablesToClearAtEnd) + m_currentVariableValues.erase(var); +} + +void SSATransform::run(Block& _ast, NameDispenser& _nameDispenser) +{ + Assignments assignments; + assignments(_ast); + SSATransform{_nameDispenser, assignments.names()}(_ast); +} + diff --git a/libyul/optimiser/SSATransform.h b/libyul/optimiser/SSATransform.h new file mode 100644 index 00000000..bb642549 --- /dev/null +++ b/libyul/optimiser/SSATransform.h @@ -0,0 +1,98 @@ +/* + 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 subsequent assignments to variable declarations + * and assignments. + */ +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> + +#include <vector> + +namespace dev +{ +namespace yul +{ + +class NameDispenser; + +/** + * Optimizer stage that tries to replace repeated assignments to + * existing variables by declarations of new variables as much as + * possible. + * The reassignments are still there, but all references to the + * reassigned variables are replaced by the newly declared variables. + * + * Example: + * { + * let a := 1 + * mstore(a, 2) + * a := 3 + * } + * is transformed to + * { + * let a_1 := 1 + * let a := a_1 + * mstore(a_1, 2) + * let a_3 := 3 + * a := a_3 + * } + * + * Exact semantics: + * + * For any variable a that is assigned to somewhere in the code (assignment with + * declaration does not count) perform the following transforms: + * - replace "let a := v" by "let a_1 := v let a := a_1" + * - replace "a := v" by "let a_1 := v a := a_1" + * Furthermore, always note the current variable/value assigned to a and replace each + * reference to a by this variable. + * The current value mapping is cleared for a variable a at the end of each block + * in which it was assigned and just after the for loop init block if it is assigned + * inside the for loop. + * + * After this stage, redundantAssignmentRemover is recommended to remove the unnecessary + * intermediate assignments. + * + * This stage provides best results if CSE is run right before it, because + * then it does not generate excessive amounts of variables. + * + * TODO Which transforms are required to keep this idempotent? + */ +class SSATransform: public ASTModifier +{ +public: + void operator()(Identifier&) override; + void operator()(ForLoop&) override; + void operator()(Block& _block) override; + + static void run(Block& _ast, NameDispenser& _nameDispenser); + +private: + explicit SSATransform(NameDispenser& _nameDispenser, std::set<YulString> const& _variablesToReplace): + m_nameDispenser(_nameDispenser), m_variablesToReplace(_variablesToReplace) + { } + + NameDispenser& m_nameDispenser; + std::set<YulString> const& m_variablesToReplace; + std::map<YulString, YulString> m_currentVariableValues; +}; + +} +} diff --git a/libyul/optimiser/SSAValueTracker.cpp b/libyul/optimiser/SSAValueTracker.cpp index a1291d67..491117da 100644 --- a/libyul/optimiser/SSAValueTracker.cpp +++ b/libyul/optimiser/SSAValueTracker.cpp @@ -42,7 +42,7 @@ void SSAValueTracker::operator()(VariableDeclaration const& _varDecl) setValue(var.name, nullptr); } -void SSAValueTracker::setValue(string const& _name, Expression const* _value) +void SSAValueTracker::setValue(YulString _name, Expression const* _value) { assertThrow( m_values.count(_name) == 0, diff --git a/libyul/optimiser/SSAValueTracker.h b/libyul/optimiser/SSAValueTracker.h index 8c39a98e..d1539c86 100644 --- a/libyul/optimiser/SSAValueTracker.h +++ b/libyul/optimiser/SSAValueTracker.h @@ -23,7 +23,6 @@ #include <libyul/optimiser/ASTWalker.h> -#include <string> #include <map> #include <set> @@ -45,13 +44,13 @@ public: virtual void operator()(VariableDeclaration const& _varDecl) override; virtual void operator()(Assignment const& _assignment) override; - std::map<std::string, Expression const*> const& values() const { return m_values; } - Expression const* value(std::string const& _name) const { return m_values.at(_name); } + std::map<YulString, Expression const*> const& values() const { return m_values; } + Expression const* value(YulString _name) const { return m_values.at(_name); } private: - void setValue(std::string const& _name, Expression const* _value); + void setValue(YulString _name, Expression const* _value); - std::map<std::string, Expression const*> m_values; + std::map<YulString, Expression const*> m_values; }; } diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 33f3af77..3c49016e 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -40,7 +40,7 @@ MovableChecker::MovableChecker(Expression const& _expression) void MovableChecker::operator()(Identifier const& _identifier) { ASTWalker::operator()(_identifier); - m_variableReferences.insert(_identifier.name); + m_variableReferences.emplace(_identifier.name); } void MovableChecker::operator()(FunctionalInstruction const& _instr) diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 1caa12fb..620a91cb 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -22,8 +22,6 @@ #include <libyul/optimiser/ASTWalker.h> -#include <string> -#include <map> #include <set> namespace dev @@ -49,11 +47,11 @@ public: using ASTWalker::visit; bool movable() const { return m_movable; } - std::set<std::string> const& referencedVariables() const { return m_variableReferences; } + std::set<YulString> const& referencedVariables() const { return m_variableReferences; } private: /// Which variables the current expression references. - std::set<std::string> m_variableReferences; + std::set<YulString> m_variableReferences; /// Is the current expression movable or not. bool m_movable = true; }; diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 4d0468c7..5721042f 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -36,7 +36,7 @@ using namespace dev::yul; SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch( Expression const& _expr, - map<string, Expression const*> const& _ssaValues + map<YulString, Expression const*> const& _ssaValues ) { if (_expr.type() != typeid(FunctionalInstruction)) @@ -46,7 +46,7 @@ SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch( assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized."); FunctionalInstruction const& instruction = boost::get<FunctionalInstruction>(_expr); - for (auto const& rule: rules.m_rules[byte(instruction.instruction)]) + for (auto const& rule: rules.m_rules[uint8_t(instruction.instruction)]) { rules.resetMatchGroups(); if (rule.pattern.matches(_expr, _ssaValues)) @@ -57,7 +57,7 @@ SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch( bool SimplificationRules::isInitialized() const { - return !m_rules[byte(solidity::Instruction::ADD)].empty(); + return !m_rules[uint8_t(solidity::Instruction::ADD)].empty(); } void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _rules) @@ -68,7 +68,7 @@ void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _r void SimplificationRules::addRule(SimplificationRule<Pattern> const& _rule) { - m_rules[byte(_rule.pattern.instruction())].push_back(_rule); + m_rules[uint8_t(_rule.pattern.instruction())].push_back(_rule); } SimplificationRules::SimplificationRules() @@ -104,7 +104,7 @@ void Pattern::setMatchGroup(unsigned _group, map<unsigned, Expression const*>& _ m_matchGroups = &_matchGroups; } -bool Pattern::matches(Expression const& _expr, map<string, Expression const*> const& _ssaValues) const +bool Pattern::matches(Expression const& _expr, map<YulString, Expression const*> const& _ssaValues) const { Expression const* expr = &_expr; @@ -112,7 +112,7 @@ bool Pattern::matches(Expression const& _expr, map<string, Expression const*> co // Do not do it for "Any" because we can check identity better for variables. if (m_kind != PatternKind::Any && _expr.type() == typeid(Identifier)) { - string const& varName = boost::get<Identifier>(_expr).name; + YulString varName = boost::get<Identifier>(_expr).name; if (_ssaValues.count(varName)) expr = _ssaValues.at(varName); } @@ -125,7 +125,7 @@ bool Pattern::matches(Expression const& _expr, map<string, Expression const*> co Literal const& literal = boost::get<Literal>(*expr); if (literal.kind != assembly::LiteralKind::Number) return false; - if (m_data && *m_data != u256(literal.value)) + if (m_data && *m_data != u256(literal.value.str())) return false; assertThrow(m_arguments.empty(), OptimizerException, ""); } @@ -193,7 +193,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const if (m_kind == PatternKind::Constant) { assertThrow(m_data, OptimizerException, "No match group and no constant value given."); - return Literal{_location, assembly::LiteralKind::Number, formatNumber(*m_data), ""}; + return Literal{_location, assembly::LiteralKind::Number, YulString{formatNumber(*m_data)}, {}}; } else if (m_kind == PatternKind::Operation) { @@ -209,7 +209,8 @@ u256 Pattern::d() const { Literal const& literal = boost::get<Literal>(matchGroupValue()); assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); - return u256(literal.value); + assertThrow(isValidDecimal(literal.value.str()) || isValidHex(literal.value.str()), OptimizerException, ""); + return u256(literal.value.str()); } Expression const& Pattern::matchGroupValue() const diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index 82ae5d22..b608ca91 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -52,7 +52,7 @@ public: /// @param _ssaValues values of variables that are assigned exactly once. static SimplificationRule<Pattern> const* findFirstMatch( Expression const& _expr, - std::map<std::string, Expression const*> const& _ssaValues + std::map<YulString, Expression const*> const& _ssaValues ); /// Checks whether the rulelist is non-empty. This is usually enforced @@ -96,7 +96,7 @@ public: /// same expression equivalence class. void setMatchGroup(unsigned _group, std::map<unsigned, Expression const*>& _matchGroups); unsigned matchGroup() const { return m_matchGroup; } - bool matches(Expression const& _expr, std::map<std::string, Expression const*> const& _ssaValues) const; + bool matches(Expression const& _expr, std::map<YulString, Expression const*> const& _ssaValues) const; std::vector<Pattern> arguments() const { return m_arguments; } diff --git a/libyul/optimiser/Substitution.cpp b/libyul/optimiser/Substitution.cpp index 4a000a85..9b3d4c03 100644 --- a/libyul/optimiser/Substitution.cpp +++ b/libyul/optimiser/Substitution.cpp @@ -30,7 +30,7 @@ Expression Substitution::translate(Expression const& _expression) { if (_expression.type() == typeid(Identifier)) { - string const& name = boost::get<Identifier>(_expression).name; + YulString name = boost::get<Identifier>(_expression).name; if (m_substitutions.count(name)) // No recursive substitution return ASTCopier().translate(*m_substitutions.at(name)); diff --git a/libyul/optimiser/Substitution.h b/libyul/optimiser/Substitution.h index b734cdca..59ee4620 100644 --- a/libyul/optimiser/Substitution.h +++ b/libyul/optimiser/Substitution.h @@ -22,9 +22,9 @@ #include <libyul/optimiser/ASTCopier.h> -#include <string> +#include <libyul/YulString.h> + #include <map> -#include <set> namespace dev { @@ -37,13 +37,13 @@ namespace yul class Substitution: public ASTCopier { public: - Substitution(std::map<std::string, Expression const*> const& _substitutions): + Substitution(std::map<YulString, Expression const*> const& _substitutions): m_substitutions(_substitutions) {} virtual Expression translate(Expression const& _expression) override; private: - std::map<std::string, Expression const*> const& m_substitutions; + std::map<YulString, Expression const*> const& m_substitutions; }; } diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp new file mode 100644 index 00000000..7d52a5a8 --- /dev/null +++ b/libyul/optimiser/Suite.cpp @@ -0,0 +1,120 @@ +/* + 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 suite that combines all steps and also provides the settings for the heuristics. + */ + +#include <libyul/optimiser/Suite.h> + +#include <libyul/optimiser/Disambiguator.h> +#include <libyul/optimiser/FunctionGrouper.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/ExpressionSplitter.h> +#include <libyul/optimiser/ExpressionJoiner.h> +#include <libyul/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/Rematerialiser.h> +#include <libyul/optimiser/UnusedPruner.h> +#include <libyul/optimiser/ExpressionSimplifier.h> +#include <libyul/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/SSATransform.h> +#include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/VarDeclPropagator.h> + +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/inlineasm/AsmData.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void OptimiserSuite::run( + Block& _ast, + solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + set<YulString> const& _externallyUsedIdentifiers +) +{ + set<YulString> reservedIdentifiers = _externallyUsedIdentifiers; + + Block ast = boost::get<Block>(Disambiguator(_analysisInfo, reservedIdentifiers)(_ast)); + + (FunctionHoister{})(ast); + (FunctionGrouper{})(ast); + + NameDispenser dispenser{ast}; + + for (size_t i = 0; i < 4; i++) + { + ExpressionSplitter{dispenser}(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); + RedundantAssignEliminator::run(ast); + + CommonSubexpressionEliminator{}(ast); + ExpressionSimplifier::run(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + RedundantAssignEliminator::run(ast); + UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); + CommonSubexpressionEliminator{}(ast); + UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + RedundantAssignEliminator::run(ast); + + ExpressionJoiner::run(ast); + ExpressionJoiner::run(ast); + ExpressionInliner(ast).run(); + UnusedPruner::runUntilStabilised(ast); + + ExpressionSplitter{dispenser}(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + RedundantAssignEliminator::run(ast); + CommonSubexpressionEliminator{}(ast); + FullInliner{ast, dispenser}.run(); + VarDeclPropagator{}(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); + RedundantAssignEliminator::run(ast); + ExpressionSimplifier::run(ast); + CommonSubexpressionEliminator{}(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); + RedundantAssignEliminator::run(ast); + UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); + } + ExpressionJoiner::run(ast); + VarDeclPropagator{}(ast); + UnusedPruner::runUntilStabilised(ast); + ExpressionJoiner::run(ast); + UnusedPruner::runUntilStabilised(ast); + ExpressionJoiner::run(ast); + VarDeclPropagator{}(ast); + UnusedPruner::runUntilStabilised(ast); + ExpressionJoiner::run(ast); + UnusedPruner::runUntilStabilised(ast); + + _ast = std::move(ast); +} diff --git a/libsolidity/formal/SymbolicBoolVariable.h b/libyul/optimiser/Suite.h index 85e41b3b..5b564c56 100644 --- a/libsolidity/formal/SymbolicBoolVariable.h +++ b/libyul/optimiser/Suite.h @@ -14,35 +14,41 @@ You should have received a copy of the GNU General Public License along with solidity. If not, see <http://www.gnu.org/licenses/>. */ +/** + * Optimiser suite that combines all steps and also provides the settings for the heuristics. + */ #pragma once -#include <libsolidity/formal/SymbolicVariable.h> +#include <libyul/ASTDataForward.h> +#include <libyul/YulString.h> + +#include <set> namespace dev { namespace solidity { +namespace assembly +{ +struct AsmAnalysisInfo; +} +} +namespace yul +{ /** - * Specialization of SymbolicVariable for Bool + * Optimiser suite that combines all steps and also provides the settings for the heuristics */ -class SymbolicBoolVariable: public SymbolicVariable +class OptimiserSuite { public: - SymbolicBoolVariable( - Type const& _type, - std::string const& _uniqueName, - smt::SolverInterface& _interface - ); + static void run( + Block& _ast, + solidity::assembly::AsmAnalysisInfo const& _analysisInfo, - /// Sets the var to false. - void setZeroValue(); - /// Does nothing since the SMT solver already knows the valid values for Bool. - void setUnknownValue(); - -protected: - smt::Expression valueAtIndex(int _index) const; + std::set<YulString> const& _externallyUsedIdentifiers = {} + ); }; } diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index f22b5c31..66912383 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -34,6 +34,7 @@ bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& { if (_e1.type() != _e2.type()) return false; + // TODO This somehow calls strcmp - WHERE? // TODO This should be replaced by some kind of AST walker as soon as it gets // more complex. diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index 37a74553..71e86798 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -33,7 +33,7 @@ using namespace std; using namespace dev; using namespace dev::yul; -UnusedPruner::UnusedPruner(Block& _ast, set<string> const& _externallyUsedFunctions) +UnusedPruner::UnusedPruner(Block& _ast, set<YulString> const& _externallyUsedFunctions) { ReferencesCounter counter; counter(_ast); @@ -85,13 +85,23 @@ void UnusedPruner::operator()(Block& _block) }}; } } + else if (statement.type() == typeid(ExpressionStatement)) + { + ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement); + if (MovableChecker(exprStmt.expression).movable()) + { + // pop(x) should be movable! + subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); + statement = Block{std::move(exprStmt.location), {}}; + } + } removeEmptyBlocks(_block); ASTModifier::operator()(_block); } -void UnusedPruner::runUntilStabilised(Block& _ast, set<string> const& _externallyUsedFunctions) +void UnusedPruner::runUntilStabilised(Block& _ast, set<YulString> const& _externallyUsedFunctions) { while (true) { @@ -102,12 +112,12 @@ void UnusedPruner::runUntilStabilised(Block& _ast, set<string> const& _externall } } -bool UnusedPruner::used(string const& _name) const +bool UnusedPruner::used(YulString _name) const { return m_references.count(_name) && m_references.at(_name) > 0; } -void UnusedPruner::subtractReferences(map<string, size_t> const& _subtrahend) +void UnusedPruner::subtractReferences(map<YulString, size_t> const& _subtrahend) { for (auto const& ref: _subtrahend) { diff --git a/libyul/optimiser/UnusedPruner.h b/libyul/optimiser/UnusedPruner.h index 30617ff3..b5aea3dd 100644 --- a/libyul/optimiser/UnusedPruner.h +++ b/libyul/optimiser/UnusedPruner.h @@ -21,8 +21,8 @@ #pragma once #include <libyul/optimiser/ASTWalker.h> +#include <libyul/YulString.h> -#include <string> #include <map> #include <set> @@ -32,10 +32,8 @@ namespace yul { /** - * Optimisation stage that removes unused variables and functions. - * - * TODO: Also remove intermediate variable assignments from movable expressions - * which are not referenced until after the next assignment to the same variable. + * Optimisation stage that removes unused variables and functions and also + * removes movable expression statements. * * Note that this does not remove circular references. * @@ -44,7 +42,7 @@ namespace yul class UnusedPruner: public ASTModifier { public: - explicit UnusedPruner(Block& _ast, std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>()); + explicit UnusedPruner(Block& _ast, std::set<YulString> const& _externallyUsedFunctions = {}); using ASTModifier::operator(); virtual void operator()(Block& _block) override; @@ -53,14 +51,14 @@ public: bool shouldRunAgain() const { return m_shouldRunAgain; } // Run the pruner until the code does not change anymore. - static void runUntilStabilised(Block& _ast, std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>()); + static void runUntilStabilised(Block& _ast, std::set<YulString> const& _externallyUsedFunctions = {}); private: - bool used(std::string const& _name) const; - void subtractReferences(std::map<std::string, size_t> const& _subtrahend); + bool used(YulString _name) const; + void subtractReferences(std::map<YulString, size_t> const& _subtrahend); bool m_shouldRunAgain = false; - std::map<std::string, size_t> m_references; + std::map<YulString, size_t> m_references; }; } diff --git a/libyul/optimiser/VarDeclPropagator.cpp b/libyul/optimiser/VarDeclPropagator.cpp new file mode 100644 index 00000000..537b7020 --- /dev/null +++ b/libyul/optimiser/VarDeclPropagator.cpp @@ -0,0 +1,129 @@ +/* + 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 <libyul/optimiser/VarDeclPropagator.h> +#include <libsolidity/inlineasm/AsmData.h> +#include <libdevcore/CommonData.h> +#include <boost/range/algorithm_ext/erase.hpp> +#include <algorithm> +#include <map> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +using dev::solidity::assembly::TypedName; +using dev::solidity::assembly::TypedNameList; + +void VarDeclPropagator::operator()(Block& _block) +{ + map<YulString, TypedName> outerEmptyVarDecls; + map<YulString, TypedName> outerLazyInitializedVarDecls; + swap(m_emptyVarDecls, outerEmptyVarDecls); + swap(m_lazyInitializedVarDecls, outerLazyInitializedVarDecls); + + ASTModifier::operator()(_block); + + iterateReplacing( + _block.statements, + [this](Statement& _stmt) -> boost::optional<vector<Statement>> + { + if (_stmt.type() == typeid(VariableDeclaration)) + { + VariableDeclaration& varDecl = boost::get<VariableDeclaration>(_stmt); + boost::remove_erase_if( + varDecl.variables, + [&](TypedName const& _typedName) { return m_lazyInitializedVarDecls.count(_typedName.name); } + ); + if (varDecl.variables.empty()) + return vector<Statement>{}; + else + return {}; + } + else if (_stmt.type() == typeid(Assignment)) + { + Assignment& assignment = boost::get<Assignment>(_stmt); + if (isFullyLazyInitialized(assignment.variableNames)) + return vector<Statement>{recreateVariableDeclaration(assignment)}; + else + return {}; + } + else + return {}; + } + ); + + swap(m_emptyVarDecls, outerEmptyVarDecls); + swap(m_lazyInitializedVarDecls, outerLazyInitializedVarDecls); +} + +void VarDeclPropagator::operator()(VariableDeclaration& _varDecl) +{ + if (_varDecl.value) + visit(*_varDecl.value); + else + for (TypedName const& typedName: _varDecl.variables) + m_emptyVarDecls[typedName.name] = typedName; +} + +void VarDeclPropagator::operator()(Assignment& _assignment) +{ + visit(*_assignment.value); + + if (allVarNamesUninitialized(_assignment.variableNames)) + for (Identifier const& ident: _assignment.variableNames) + m_lazyInitializedVarDecls[ident.name] = m_emptyVarDecls[ident.name]; + + for (Identifier& name: _assignment.variableNames) + (*this)(name); +} + +void VarDeclPropagator::operator()(Identifier& _ident) +{ + m_emptyVarDecls.erase(_ident.name); +} + +bool VarDeclPropagator::allVarNamesUninitialized(vector<Identifier> const& _variableNames) const +{ + return all_of( + begin(_variableNames), + end(_variableNames), + [&](Identifier const& _ident) -> bool { return m_emptyVarDecls.count(_ident.name); } + ); +} + +bool VarDeclPropagator::isFullyLazyInitialized(vector<Identifier> const& _variableNames) const +{ + return all_of( + begin(_variableNames), + end(_variableNames), + [&](Identifier const& ident) -> bool { return m_lazyInitializedVarDecls.count(ident.name); } + ); +} + +VariableDeclaration VarDeclPropagator::recreateVariableDeclaration(Assignment& _assignment) +{ + TypedNameList variables; + + for (Identifier const& varName: _assignment.variableNames) + { + variables.emplace_back(move(m_lazyInitializedVarDecls.at(varName.name))); + m_lazyInitializedVarDecls.erase(varName.name); + } + + return VariableDeclaration{move(_assignment.location), move(variables), std::move(_assignment.value)}; +} diff --git a/libyul/optimiser/VarDeclPropagator.h b/libyul/optimiser/VarDeclPropagator.h new file mode 100644 index 00000000..4522d23a --- /dev/null +++ b/libyul/optimiser/VarDeclPropagator.h @@ -0,0 +1,63 @@ +/* + 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 <libyul/ASTDataForward.h> +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/Exceptions.h> +#include <libsolidity/inlineasm/AsmDataForward.h> +#include <vector> +#include <set> +#include <map> + +namespace dev +{ +namespace yul +{ + +/** + * Rewrites Assignment statements into VariableDeclaration when the assignment's LHS + * variables had no value yet. + * + * It recursively walks through the AST and moves each declaration of variables to + * the first assignment within the same block (if possible).. + */ +class VarDeclPropagator: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(Block& _block) override; + void operator()(VariableDeclaration& _varDecl) override; + void operator()(Assignment& _assignment) override; + void operator()(Identifier& _ident) override; + +private: + bool allVarNamesUninitialized(std::vector<Identifier> const& _variableNames) const; + bool isFullyLazyInitialized(std::vector<Identifier> const& _variableNames) const; + VariableDeclaration recreateVariableDeclaration(Assignment& _assignment); + +private: + /// Holds a list of variables from current Block that have no value assigned yet. + std::map<YulString, TypedName> m_emptyVarDecls; + + /// Holds a list variables (and their TypedName) within the current block. + std::map<YulString, TypedName> m_lazyInitializedVarDecls; +}; + +} +} diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 1cfbf716..36a8ef7f 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -54,7 +54,7 @@ keyid=70D110489D66E2F6 email=builds@ethereum.org packagename=solc -for distribution in trusty xenial artful bionic +for distribution in trusty xenial bionic cosmic do cd /tmp/ rm -rf $distribution diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e0c6a2b6..844cef90 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1118,7 +1118,15 @@ void CommandLineInterface::writeLinkedFiles() if (src.first == g_stdinFileName) cout << src.second << endl; else - writeFile(src.first, src.second); + { + ofstream outFile(src.first); + outFile << src.second; + if (!outFile) + { + cerr << "Could not write to file " << src.first << ". Aborting." << endl; + return; + } + } } string CommandLineInterface::libraryPlaceholderHint(string const& _libraryName) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 713817bb..d6de95a3 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -147,11 +147,11 @@ public: static std::pair<bool, std::string> compareAndCreateMessage(bytes const& _result, bytes const& _expectation); - static bytes encode(bool _value) { return encode(byte(_value)); } + static bytes encode(bool _value) { return encode(uint8_t(_value)); } static bytes encode(int _value) { return encode(u256(_value)); } static bytes encode(size_t _value) { return encode(u256(_value)); } static bytes encode(char const* _value) { return encode(std::string(_value)); } - static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; } + static bytes encode(uint8_t _value) { return bytes(31, 0) + bytes{_value}; } static bytes encode(u256 const& _value) { return toBigEndian(_value); } /// @returns the fixed-point encoding of a rational number with a given /// number of fractional bits. diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index a8261693..b0b545f3 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -184,8 +184,6 @@ do then echo " - $dir" cd "$dir" - # Replace version pragmas - find . -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity [\^0-9\.]*/pragma solidity >=0.0/' compileFull -w *.sol */*.sol cd .. fi diff --git a/test/compilationTests/MultiSigWallet/MultiSigWallet.sol b/test/compilationTests/MultiSigWallet/MultiSigWallet.sol index dc6e98e4..78194b7b 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWallet.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWallet.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. diff --git a/test/compilationTests/MultiSigWallet/MultiSigWalletFactory.sol b/test/compilationTests/MultiSigWallet/MultiSigWalletFactory.sol index 8d0c1a3f..00c165ea 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWalletFactory.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWalletFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; import "Factory.sol"; import "MultiSigWallet.sol"; diff --git a/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimit.sol b/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimit.sol index df2a1400..fc0a3bc5 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimit.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimit.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; import "MultiSigWallet.sol"; diff --git a/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimitFactory.sol b/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimitFactory.sol index f897d938..4a038864 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimitFactory.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimitFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; import "Factory.sol"; import "MultiSigWalletWithDailyLimit.sol"; diff --git a/test/compilationTests/MultiSigWallet/TestToken.sol b/test/compilationTests/MultiSigWallet/TestToken.sol index a100b449..c9dd64f7 100644 --- a/test/compilationTests/MultiSigWallet/TestToken.sol +++ b/test/compilationTests/MultiSigWallet/TestToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; /// @title Test token contract - Allows testing of token transfers with multisig wallet. diff --git a/test/compilationTests/corion/announcementTypes.sol b/test/compilationTests/corion/announcementTypes.sol index 94f4a9dc..1619e182 100644 --- a/test/compilationTests/corion/announcementTypes.sol +++ b/test/compilationTests/corion/announcementTypes.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; contract announcementTypes { diff --git a/test/compilationTests/corion/ico.sol b/test/compilationTests/corion/ico.sol index e660389b..c550fbef 100644 --- a/test/compilationTests/corion/ico.sol +++ b/test/compilationTests/corion/ico.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./safeMath.sol"; import "./token.sol"; diff --git a/test/compilationTests/corion/module.sol b/test/compilationTests/corion/module.sol index bd6952b1..f663d9c2 100644 --- a/test/compilationTests/corion/module.sol +++ b/test/compilationTests/corion/module.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; contract abstractModuleHandler { function transfer(address payable from, address payable to, uint256 value, bool fee) external returns (bool success) {} diff --git a/test/compilationTests/corion/moduleHandler.sol b/test/compilationTests/corion/moduleHandler.sol index 6b0daf0d..76061a31 100644 --- a/test/compilationTests/corion/moduleHandler.sol +++ b/test/compilationTests/corion/moduleHandler.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./module.sol"; import "./announcementTypes.sol"; diff --git a/test/compilationTests/corion/multiOwner.sol b/test/compilationTests/corion/multiOwner.sol index efda554a..b61289dd 100644 --- a/test/compilationTests/corion/multiOwner.sol +++ b/test/compilationTests/corion/multiOwner.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./safeMath.sol"; diff --git a/test/compilationTests/corion/owned.sol b/test/compilationTests/corion/owned.sol index f5a11c44..f273e3b5 100644 --- a/test/compilationTests/corion/owned.sol +++ b/test/compilationTests/corion/owned.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; contract ownedDB { address private owner; diff --git a/test/compilationTests/corion/premium.sol b/test/compilationTests/corion/premium.sol index 4952a740..091e291a 100644 --- a/test/compilationTests/corion/premium.sol +++ b/test/compilationTests/corion/premium.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./safeMath.sol"; import "./tokenDB.sol"; diff --git a/test/compilationTests/corion/provider.sol b/test/compilationTests/corion/provider.sol index b3b5e8ca..48ec3adb 100644 --- a/test/compilationTests/corion/provider.sol +++ b/test/compilationTests/corion/provider.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./module.sol"; import "./moduleHandler.sol"; diff --git a/test/compilationTests/corion/publisher.sol b/test/compilationTests/corion/publisher.sol index 48090d02..991fc8f4 100644 --- a/test/compilationTests/corion/publisher.sol +++ b/test/compilationTests/corion/publisher.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./announcementTypes.sol"; import "./module.sol"; diff --git a/test/compilationTests/corion/safeMath.sol b/test/compilationTests/corion/safeMath.sol index a6680f27..3e7e5578 100644 --- a/test/compilationTests/corion/safeMath.sol +++ b/test/compilationTests/corion/safeMath.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; contract safeMath { function safeAdd(uint256 a, uint256 b) internal returns(uint256) { diff --git a/test/compilationTests/corion/schelling.sol b/test/compilationTests/corion/schelling.sol index 2a327ba0..e70716bd 100644 --- a/test/compilationTests/corion/schelling.sol +++ b/test/compilationTests/corion/schelling.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./announcementTypes.sol"; import "./module.sol"; diff --git a/test/compilationTests/corion/token.sol b/test/compilationTests/corion/token.sol index f55dc9e6..6ab22530 100644 --- a/test/compilationTests/corion/token.sol +++ b/test/compilationTests/corion/token.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./announcementTypes.sol"; import "./safeMath.sol"; diff --git a/test/compilationTests/corion/tokenDB.sol b/test/compilationTests/corion/tokenDB.sol index 1632a89b..a77a1efa 100644 --- a/test/compilationTests/corion/tokenDB.sol +++ b/test/compilationTests/corion/tokenDB.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./safeMath.sol"; import "./owned.sol"; diff --git a/test/compilationTests/gnosis/Events/CategoricalEvent.sol b/test/compilationTests/gnosis/Events/CategoricalEvent.sol index 4815e315..0d6d1b45 100644 --- a/test/compilationTests/gnosis/Events/CategoricalEvent.sol +++ b/test/compilationTests/gnosis/Events/CategoricalEvent.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; diff --git a/test/compilationTests/gnosis/Events/Event.sol b/test/compilationTests/gnosis/Events/Event.sol index 0a40cf7e..b11dd15d 100644 --- a/test/compilationTests/gnosis/Events/Event.sol +++ b/test/compilationTests/gnosis/Events/Event.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Tokens/Token.sol"; import "../Tokens/OutcomeToken.sol"; import "../Oracles/Oracle.sol"; diff --git a/test/compilationTests/gnosis/Events/EventFactory.sol b/test/compilationTests/gnosis/Events/EventFactory.sol index cfe772ec..4f4efe35 100644 --- a/test/compilationTests/gnosis/Events/EventFactory.sol +++ b/test/compilationTests/gnosis/Events/EventFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/CategoricalEvent.sol"; import "../Events/ScalarEvent.sol"; diff --git a/test/compilationTests/gnosis/Events/ScalarEvent.sol b/test/compilationTests/gnosis/Events/ScalarEvent.sol index 832c2ab1..325f4302 100644 --- a/test/compilationTests/gnosis/Events/ScalarEvent.sol +++ b/test/compilationTests/gnosis/Events/ScalarEvent.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; diff --git a/test/compilationTests/gnosis/MarketMakers/LMSRMarketMaker.sol b/test/compilationTests/gnosis/MarketMakers/LMSRMarketMaker.sol index 4ad285eb..c305d168 100644 --- a/test/compilationTests/gnosis/MarketMakers/LMSRMarketMaker.sol +++ b/test/compilationTests/gnosis/MarketMakers/LMSRMarketMaker.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Utils/Math.sol"; import "../MarketMakers/MarketMaker.sol"; diff --git a/test/compilationTests/gnosis/MarketMakers/MarketMaker.sol b/test/compilationTests/gnosis/MarketMakers/MarketMaker.sol index ef5942cd..f61cdba8 100644 --- a/test/compilationTests/gnosis/MarketMakers/MarketMaker.sol +++ b/test/compilationTests/gnosis/MarketMakers/MarketMaker.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Markets/Market.sol"; diff --git a/test/compilationTests/gnosis/Markets/Campaign.sol b/test/compilationTests/gnosis/Markets/Campaign.sol index 119f8df2..b16d102c 100644 --- a/test/compilationTests/gnosis/Markets/Campaign.sol +++ b/test/compilationTests/gnosis/Markets/Campaign.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; import "../Markets/StandardMarketFactory.sol"; import "../Utils/Math.sol"; diff --git a/test/compilationTests/gnosis/Markets/CampaignFactory.sol b/test/compilationTests/gnosis/Markets/CampaignFactory.sol index d80d7d63..c7afeded 100644 --- a/test/compilationTests/gnosis/Markets/CampaignFactory.sol +++ b/test/compilationTests/gnosis/Markets/CampaignFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Markets/Campaign.sol"; diff --git a/test/compilationTests/gnosis/Markets/Market.sol b/test/compilationTests/gnosis/Markets/Market.sol index 7bcecfe5..963c2c94 100644 --- a/test/compilationTests/gnosis/Markets/Market.sol +++ b/test/compilationTests/gnosis/Markets/Market.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; import "../MarketMakers/MarketMaker.sol"; diff --git a/test/compilationTests/gnosis/Markets/MarketFactory.sol b/test/compilationTests/gnosis/Markets/MarketFactory.sol index d368e8db..40d80db6 100644 --- a/test/compilationTests/gnosis/Markets/MarketFactory.sol +++ b/test/compilationTests/gnosis/Markets/MarketFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; import "../MarketMakers/MarketMaker.sol"; import "../Markets/Market.sol"; diff --git a/test/compilationTests/gnosis/Markets/StandardMarket.sol b/test/compilationTests/gnosis/Markets/StandardMarket.sol index 2f52a896..99648c90 100644 --- a/test/compilationTests/gnosis/Markets/StandardMarket.sol +++ b/test/compilationTests/gnosis/Markets/StandardMarket.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Markets/Market.sol"; import "../Tokens/Token.sol"; import "../Events/Event.sol"; diff --git a/test/compilationTests/gnosis/Markets/StandardMarketFactory.sol b/test/compilationTests/gnosis/Markets/StandardMarketFactory.sol index 88dcbe79..7245da89 100644 --- a/test/compilationTests/gnosis/Markets/StandardMarketFactory.sol +++ b/test/compilationTests/gnosis/Markets/StandardMarketFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Markets/MarketFactory.sol"; import "../Markets/StandardMarket.sol"; diff --git a/test/compilationTests/gnosis/Migrations.sol b/test/compilationTests/gnosis/Migrations.sol index f1a3ea9d..396e3649 100644 --- a/test/compilationTests/gnosis/Migrations.sol +++ b/test/compilationTests/gnosis/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; contract Migrations { address public owner; diff --git a/test/compilationTests/gnosis/Oracles/CentralizedOracle.sol b/test/compilationTests/gnosis/Oracles/CentralizedOracle.sol index e175dfdb..ee0d50a3 100644 --- a/test/compilationTests/gnosis/Oracles/CentralizedOracle.sol +++ b/test/compilationTests/gnosis/Oracles/CentralizedOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/CentralizedOracleFactory.sol b/test/compilationTests/gnosis/Oracles/CentralizedOracleFactory.sol index be632070..649aaee5 100644 --- a/test/compilationTests/gnosis/Oracles/CentralizedOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/CentralizedOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/CentralizedOracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/DifficultyOracle.sol b/test/compilationTests/gnosis/Oracles/DifficultyOracle.sol index 3d801da1..c1b43235 100644 --- a/test/compilationTests/gnosis/Oracles/DifficultyOracle.sol +++ b/test/compilationTests/gnosis/Oracles/DifficultyOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/DifficultyOracleFactory.sol b/test/compilationTests/gnosis/Oracles/DifficultyOracleFactory.sol index fc5dcc3b..e04f904a 100644 --- a/test/compilationTests/gnosis/Oracles/DifficultyOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/DifficultyOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/DifficultyOracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol b/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol index 83d10b2e..ad56dcdc 100644 --- a/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol +++ b/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; import "../Events/EventFactory.sol"; import "../Markets/MarketFactory.sol"; diff --git a/test/compilationTests/gnosis/Oracles/FutarchyOracleFactory.sol b/test/compilationTests/gnosis/Oracles/FutarchyOracleFactory.sol index 3c6e5c15..87bdf54b 100644 --- a/test/compilationTests/gnosis/Oracles/FutarchyOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/FutarchyOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/FutarchyOracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/MajorityOracle.sol b/test/compilationTests/gnosis/Oracles/MajorityOracle.sol index 4dc1760d..6913713b 100644 --- a/test/compilationTests/gnosis/Oracles/MajorityOracle.sol +++ b/test/compilationTests/gnosis/Oracles/MajorityOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/MajorityOracleFactory.sol b/test/compilationTests/gnosis/Oracles/MajorityOracleFactory.sol index dbbccc4c..a4845a36 100644 --- a/test/compilationTests/gnosis/Oracles/MajorityOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/MajorityOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/MajorityOracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/Oracle.sol b/test/compilationTests/gnosis/Oracles/Oracle.sol index 450aff00..835d889e 100644 --- a/test/compilationTests/gnosis/Oracles/Oracle.sol +++ b/test/compilationTests/gnosis/Oracles/Oracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; /// @title Abstract oracle contract - Functions to be implemented by oracles diff --git a/test/compilationTests/gnosis/Oracles/SignedMessageOracle.sol b/test/compilationTests/gnosis/Oracles/SignedMessageOracle.sol index 284f420e..900067cd 100644 --- a/test/compilationTests/gnosis/Oracles/SignedMessageOracle.sol +++ b/test/compilationTests/gnosis/Oracles/SignedMessageOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/SignedMessageOracleFactory.sol b/test/compilationTests/gnosis/Oracles/SignedMessageOracleFactory.sol index ea70b2aa..18617faa 100644 --- a/test/compilationTests/gnosis/Oracles/SignedMessageOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/SignedMessageOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/SignedMessageOracle.sol"; diff --git a/test/compilationTests/gnosis/Oracles/UltimateOracle.sol b/test/compilationTests/gnosis/Oracles/UltimateOracle.sol index 452a34ec..d4574715 100644 --- a/test/compilationTests/gnosis/Oracles/UltimateOracle.sol +++ b/test/compilationTests/gnosis/Oracles/UltimateOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; import "../Tokens/Token.sol"; import "../Utils/Math.sol"; diff --git a/test/compilationTests/gnosis/Oracles/UltimateOracleFactory.sol b/test/compilationTests/gnosis/Oracles/UltimateOracleFactory.sol index 51f5610e..352872ac 100644 --- a/test/compilationTests/gnosis/Oracles/UltimateOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/UltimateOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/UltimateOracle.sol"; diff --git a/test/compilationTests/gnosis/Tokens/EtherToken.sol b/test/compilationTests/gnosis/Tokens/EtherToken.sol index 32e64583..987b62c6 100644 --- a/test/compilationTests/gnosis/Tokens/EtherToken.sol +++ b/test/compilationTests/gnosis/Tokens/EtherToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Tokens/StandardToken.sol"; diff --git a/test/compilationTests/gnosis/Tokens/OutcomeToken.sol b/test/compilationTests/gnosis/Tokens/OutcomeToken.sol index fccf05e5..11f4ece1 100644 --- a/test/compilationTests/gnosis/Tokens/OutcomeToken.sol +++ b/test/compilationTests/gnosis/Tokens/OutcomeToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Tokens/StandardToken.sol"; diff --git a/test/compilationTests/gnosis/Tokens/StandardToken.sol b/test/compilationTests/gnosis/Tokens/StandardToken.sol index 5fb20210..09916cc1 100644 --- a/test/compilationTests/gnosis/Tokens/StandardToken.sol +++ b/test/compilationTests/gnosis/Tokens/StandardToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Tokens/Token.sol"; import "../Utils/Math.sol"; diff --git a/test/compilationTests/gnosis/Tokens/Token.sol b/test/compilationTests/gnosis/Tokens/Token.sol index 70ecdff7..777a8d7c 100644 --- a/test/compilationTests/gnosis/Tokens/Token.sol +++ b/test/compilationTests/gnosis/Tokens/Token.sol @@ -1,5 +1,5 @@ /// Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20 -pragma solidity ^0.4.11; +pragma solidity >=0.0; /// @title Abstract token contract - Functions to be implemented by token contracts diff --git a/test/compilationTests/gnosis/Utils/Math.sol b/test/compilationTests/gnosis/Utils/Math.sol index 47edcba4..2a741e53 100644 --- a/test/compilationTests/gnosis/Utils/Math.sol +++ b/test/compilationTests/gnosis/Utils/Math.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; /// @title Math library - Allows calculation of logarithmic and exponential functions diff --git a/test/compilationTests/milestonetracker/MilestoneTracker.sol b/test/compilationTests/milestonetracker/MilestoneTracker.sol index 41fa7404..891afcb1 100644 --- a/test/compilationTests/milestonetracker/MilestoneTracker.sol +++ b/test/compilationTests/milestonetracker/MilestoneTracker.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.6; +pragma solidity >=0.0; /* Copyright 2016, Jordi Baylina diff --git a/test/compilationTests/milestonetracker/RLP.sol b/test/compilationTests/milestonetracker/RLP.sol index 75e3902e..799233a1 100644 --- a/test/compilationTests/milestonetracker/RLP.sol +++ b/test/compilationTests/milestonetracker/RLP.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.6; +pragma solidity >=0.0; /** * @title RLPReader diff --git a/test/compilationTests/stringutils/strings.sol b/test/compilationTests/stringutils/strings.sol index f89a2527..9e0518a7 100644 --- a/test/compilationTests/stringutils/strings.sol +++ b/test/compilationTests/stringutils/strings.sol @@ -34,7 +34,7 @@ * corresponding to the left and right parts of the string. */ -pragma solidity ^0.4.14; +pragma solidity >=0.0; library strings { struct slice { diff --git a/test/compilationTests/zeppelin/Bounty.sol b/test/compilationTests/zeppelin/Bounty.sol deleted file mode 100644 index d45e130c..00000000 --- a/test/compilationTests/zeppelin/Bounty.sol +++ /dev/null @@ -1,78 +0,0 @@ -pragma solidity ^0.4.11; - - -import './payment/PullPayment.sol'; -import './lifecycle/Destructible.sol'; - - -/** - * @title Bounty - * @dev This bounty will pay out to a researcher if they break invariant logic of the contract. - */ -contract Bounty is PullPayment, Destructible { - bool public claimed; - mapping(address => address) public researchers; - - event TargetCreated(address createdAddress); - - /** - * @dev Fallback function allowing the contract to receive funds, if they haven't already been claimed. - */ - function() external payable { - if (claimed) { - revert(); - } - } - - /** - * @dev Create and deploy the target contract (extension of Target contract), and sets the - * msg.sender as a researcher - * @return A target contract - */ - function createTarget() public returns(Target) { - Target target = Target(deployContract()); - researchers[address(target)] = msg.sender; - emit TargetCreated(address(target)); - return target; - } - - /** - * @dev Internal function to deploy the target contract. - * @return A target contract address - */ - function deployContract() internal returns(address); - - /** - * @dev Sends the contract funds to the researcher that proved the contract is broken. - * @param target contract - */ - function claim(Target target) public { - address researcher = researchers[address(target)]; - if (researcher == address(0)) { - revert(); - } - // Check Target contract invariants - if (target.checkInvariant()) { - revert(); - } - asyncSend(researcher, address(this).balance); - claimed = true; - } - -} - - -/** - * @title Target - * @dev Your main contract should inherit from this class and implement the checkInvariant method. - */ -contract Target { - - /** - * @dev Checks all values a contract assumes to be true all the time. If this function returns - * false, the contract is broken in some way and is in an inconsistent state. - * In order to win the bounty, security researchers will try to cause this broken state. - * @return True if all invariant values are correct, false otherwise. - */ - function checkInvariant() public returns(bool); -} diff --git a/test/compilationTests/zeppelin/DayLimit.sol b/test/compilationTests/zeppelin/DayLimit.sol deleted file mode 100644 index bc576c89..00000000 --- a/test/compilationTests/zeppelin/DayLimit.sol +++ /dev/null @@ -1,75 +0,0 @@ -pragma solidity ^0.4.11; - -/** - * @title DayLimit - * @dev Base contract that enables methods to be protected by placing a linear limit (specifiable) - * on a particular resource per calendar day. Is multiowned to allow the limit to be altered. - */ -contract DayLimit { - - uint256 public dailyLimit; - uint256 public spentToday; - uint256 public lastDay; - - /** - * @dev Constructor that sets the passed value as a dailyLimit. - * @param _limit uint256 to represent the daily limit. - */ - constructor(uint256 _limit) public { - dailyLimit = _limit; - lastDay = today(); - } - - /** - * @dev sets the daily limit. Does not alter the amount already spent today. - * @param _newLimit uint256 to represent the new limit. - */ - function _setDailyLimit(uint256 _newLimit) internal { - dailyLimit = _newLimit; - } - - /** - * @dev Resets the amount already spent today. - */ - function _resetSpentToday() internal { - spentToday = 0; - } - - /** - * @dev Checks to see if there is enough resource to spend today. If true, the resource may be expended. - * @param _value uint256 representing the amount of resource to spend. - * @return A boolean that is True if the resource was spended and false otherwise. - */ - function underLimit(uint256 _value) internal returns (bool) { - // reset the spend limit if we're on a different day to last time. - if (today() > lastDay) { - spentToday = 0; - lastDay = today(); - } - // check to see if there's enough left - if so, subtract and return true. - // overflow protection // dailyLimit check - if (spentToday + _value >= spentToday && spentToday + _value <= dailyLimit) { - spentToday += _value; - return true; - } - return false; - } - - /** - * @dev Private function to determine today's index - * @return uint256 of today's index. - */ - function today() private view returns (uint256) { - return now / 1 days; - } - - /** - * @dev Simple modifier for daily limit. - */ - modifier limitedDaily(uint256 _value) { - if (!underLimit(_value)) { - revert(); - } - _; - } -} diff --git a/test/compilationTests/zeppelin/LICENSE b/test/compilationTests/zeppelin/LICENSE deleted file mode 100644 index 85f53321..00000000 --- a/test/compilationTests/zeppelin/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Smart Contract Solutions, Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/compilationTests/zeppelin/LimitBalance.sol b/test/compilationTests/zeppelin/LimitBalance.sol deleted file mode 100644 index d07b3c2c..00000000 --- a/test/compilationTests/zeppelin/LimitBalance.sol +++ /dev/null @@ -1,33 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title LimitBalance - * @dev Simple contract to limit the balance of child contract. - * @dev Note this doesn't prevent other contracts to send funds by using selfdestruct(address); - * @dev See: https://github.com/ConsenSys/smart-contract-best-practices#remember-that-ether-can-be-forcibly-sent-to-an-account - */ -contract LimitBalance { - - uint256 public limit; - - /** - * @dev Constructor that sets the passed value as a limit. - * @param _limit uint256 to represent the limit. - */ - constructor(uint256 _limit) public { - limit = _limit; - } - - /** - * @dev Checks if limit was reached. Case true, it throws. - */ - modifier limitedPayable() { - if (address(this).balance > limit) { - revert(); - } - _; - - } - -} diff --git a/test/compilationTests/zeppelin/MultisigWallet.sol b/test/compilationTests/zeppelin/MultisigWallet.sol deleted file mode 100644 index 74a54c7f..00000000 --- a/test/compilationTests/zeppelin/MultisigWallet.sol +++ /dev/null @@ -1,125 +0,0 @@ -pragma solidity ^0.4.11; - - -import "./ownership/Multisig.sol"; -import "./ownership/Shareable.sol"; -import "./DayLimit.sol"; - - -/** - * MultisigWallet - * Usage: - * bytes32 h = Wallet(w).from(oneOwner).execute(to, value, data); - * Wallet(w).from(anotherOwner).confirm(h); - */ -contract MultisigWallet is Multisig, Shareable, DayLimit { - - struct Transaction { - address to; - uint256 value; - bytes data; - } - - /** - * Constructor, sets the owners addresses, number of approvals required, and daily spending limit - * @param _owners A list of owners. - * @param _required The amount required for a transaction to be approved. - */ - constructor(address[] memory _owners, uint256 _required, uint256 _daylimit) - Shareable(_owners, _required) - DayLimit(_daylimit) public { } - - /** - * @dev destroys the contract sending everything to `_to`. - */ - function destroy(address payable _to) onlymanyowners(keccak256(msg.data)) external { - selfdestruct(_to); - } - - /** - * @dev Fallback function, receives value and emits a deposit event. - */ - function() external payable { - // just being sent some cash? - if (msg.value > 0) - emit Deposit(msg.sender, msg.value); - } - - /** - * @dev Outside-visible transaction entry point. Executes transaction immediately if below daily - * spending limit. If not, goes into multisig process. We provide a hash on return to allow the - * sender to provide shortcuts for the other confirmations (allowing them to avoid replicating - * the _to, _value, and _data arguments). They still get the option of using them if they want, - * anyways. - * @param _to The receiver address - * @param _value The value to send - * @param _data The data part of the transaction - */ - function execute(address _to, uint256 _value, bytes calldata _data) external onlyOwner returns (bytes32 _r) { - // first, take the opportunity to check that we're under the daily limit. - if (underLimit(_value)) { - emit SingleTransact(msg.sender, _value, _to, _data); - // yes - just execute the call. - (bool success,) = _to.call.value(_value)(_data); - require(success); - return 0; - } - // determine our operation hash. - _r = keccak256(abi.encodePacked(msg.data, block.number)); - if (!confirm(_r) && txs[_r].to == address(0)) { - txs[_r].to = _to; - txs[_r].value = _value; - txs[_r].data = _data; - emit ConfirmationNeeded(_r, msg.sender, _value, _to, _data); - } - } - - /** - * @dev Confirm a transaction by providing just the hash. We use the previous transactions map, - * txs, in order to determine the body of the transaction from the hash provided. - * @param _h The transaction hash to approve. - */ - function confirm(bytes32 _h) onlymanyowners(_h) public returns (bool) { - if (txs[_h].to != address(0)) { - (bool success,) = txs[_h].to.call.value(txs[_h].value)(txs[_h].data); - require(success); - emit MultiTransact(msg.sender, _h, txs[_h].value, txs[_h].to, txs[_h].data); - delete txs[_h]; - return true; - } - } - - /** - * @dev Updates the daily limit value. - * @param _newLimit uint256 to represent the new limit. - */ - function setDailyLimit(uint256 _newLimit) onlymanyowners(keccak256(msg.data)) external { - _setDailyLimit(_newLimit); - } - - /** - * @dev Resets the value spent to enable more spending - */ - function resetSpentToday() onlymanyowners(keccak256(msg.data)) external { - _resetSpentToday(); - } - - - // INTERNAL METHODS - /** - * @dev Clears the list of transactions pending approval. - */ - function clearPending() internal { - uint256 length = pendingsIndex.length; - for (uint256 i = 0; i < length; ++i) { - delete txs[pendingsIndex[i]]; - } - super.clearPending(); - } - - - // FIELDS - - // pending transactions we have at present. - mapping (bytes32 => Transaction) txs; -} diff --git a/test/compilationTests/zeppelin/README.md b/test/compilationTests/zeppelin/README.md deleted file mode 100644 index dee2f5ca..00000000 --- a/test/compilationTests/zeppelin/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Zeppelin contracts, originally from - -https://github.com/OpenZeppelin/zeppelin-solidity diff --git a/test/compilationTests/zeppelin/ReentrancyGuard.sol b/test/compilationTests/zeppelin/ReentrancyGuard.sol deleted file mode 100644 index f7abb698..00000000 --- a/test/compilationTests/zeppelin/ReentrancyGuard.sol +++ /dev/null @@ -1,34 +0,0 @@ -pragma solidity ^0.4.11; - -/** - * @title Helps contracts guard against rentrancy attacks. - * @author Remco Bloemen <remco@2Ï€.com> - * @notice If you mark a function `nonReentrant`, you should also - * mark it `external`. - */ -contract ReentrancyGuard { - - /** - * @dev We use a single lock for the whole contract. - */ - bool private rentrancy_lock = false; - - /** - * @dev Prevents a contract from calling itself, directly or indirectly. - * @notice If you mark a function `nonReentrant`, you should also - * mark it `external`. Calling one nonReentrant function from - * another is not supported. Instead, you can implement a - * `private` function doing the actual work, and a `external` - * wrapper marked as `nonReentrant`. - */ - modifier nonReentrant() { - if(rentrancy_lock == false) { - rentrancy_lock = true; - _; - rentrancy_lock = false; - } else { - revert(); - } - } - -} diff --git a/test/compilationTests/zeppelin/crowdsale/CappedCrowdsale.sol b/test/compilationTests/zeppelin/crowdsale/CappedCrowdsale.sol deleted file mode 100644 index 98c8c3d4..00000000 --- a/test/compilationTests/zeppelin/crowdsale/CappedCrowdsale.sol +++ /dev/null @@ -1,33 +0,0 @@ -pragma solidity ^0.4.11; - -import '../math/SafeMath.sol'; -import './Crowdsale.sol'; - -/** - * @title CappedCrowdsale - * @dev Extension of Crowsdale with a max amount of funds raised - */ -contract CappedCrowdsale is Crowdsale { - using SafeMath for uint256; - - uint256 public cap; - - constructor(uint256 _cap) public { - cap = _cap; - } - - // overriding Crowdsale#validPurchase to add extra cap logic - // @return true if investors can buy at the moment - function validPurchase() internal view returns (bool) { - bool withinCap = weiRaised.add(msg.value) <= cap; - return super.validPurchase() && withinCap; - } - - // overriding Crowdsale#hasEnded to add cap logic - // @return true if crowdsale event has ended - function hasEnded() public view returns (bool) { - bool capReached = weiRaised >= cap; - return super.hasEnded() || capReached; - } - -} diff --git a/test/compilationTests/zeppelin/crowdsale/Crowdsale.sol b/test/compilationTests/zeppelin/crowdsale/Crowdsale.sol deleted file mode 100644 index 612afc25..00000000 --- a/test/compilationTests/zeppelin/crowdsale/Crowdsale.sol +++ /dev/null @@ -1,108 +0,0 @@ -pragma solidity ^0.4.11; - -import '../token/MintableToken.sol'; -import '../math/SafeMath.sol'; - -/** - * @title Crowdsale - * @dev Crowdsale is a base contract for managing a token crowdsale. - * Crowdsales have a start and end block, where investors can make - * token purchases and the crowdsale will assign them tokens based - * on a token per ETH rate. Funds collected are forwarded to a wallet - * as they arrive. - */ -contract Crowdsale { - using SafeMath for uint256; - - // The token being sold - MintableToken public token; - - // start and end block where investments are allowed (both inclusive) - uint256 public startBlock; - uint256 public endBlock; - - // address where funds are collected - address payable public wallet; - - // how many token units a buyer gets per wei - uint256 public rate; - - // amount of raised money in wei - uint256 public weiRaised; - - /** - * event for token purchase logging - * @param purchaser who paid for the tokens - * @param beneficiary who got the tokens - * @param value weis paid for purchase - * @param amount amount of tokens purchased - */ - event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount); - - - constructor(uint256 _startBlock, uint256 _endBlock, uint256 _rate, address payable _wallet) public { - require(_startBlock >= block.number); - require(_endBlock >= _startBlock); - require(_rate > 0); - require(_wallet != address(0x0)); - - token = createTokenContract(); - startBlock = _startBlock; - endBlock = _endBlock; - rate = _rate; - wallet = _wallet; - } - - // creates the token to be sold. - // override this method to have crowdsale of a specific mintable token. - function createTokenContract() internal returns (MintableToken) { - return new MintableToken(); - } - - - // fallback function can be used to buy tokens - function () external payable { - buyTokens(msg.sender); - } - - // low level token purchase function - function buyTokens(address beneficiary) public payable { - require(beneficiary != address(0x0)); - require(validPurchase()); - - uint256 weiAmount = msg.value; - uint256 updatedWeiRaised = weiRaised.add(weiAmount); - - // calculate token amount to be created - uint256 tokens = weiAmount.mul(rate); - - // update state - weiRaised = updatedWeiRaised; - - token.mint(beneficiary, tokens); - emit TokenPurchase(msg.sender, beneficiary, weiAmount, tokens); - - forwardFunds(); - } - - // send ether to the fund collection wallet - // override to create custom fund forwarding mechanisms - function forwardFunds() internal { - wallet.transfer(msg.value); - } - - // @return true if the transaction can buy tokens - function validPurchase() internal view returns (bool) { - uint256 current = block.number; - bool withinPeriod = current >= startBlock && current <= endBlock; - bool nonZeroPurchase = msg.value != 0; - return withinPeriod && nonZeroPurchase; - } - - // @return true if crowdsale event has ended - function hasEnded() public view returns (bool) { - return block.number > endBlock; - } - - -} diff --git a/test/compilationTests/zeppelin/crowdsale/FinalizableCrowdsale.sol b/test/compilationTests/zeppelin/crowdsale/FinalizableCrowdsale.sol deleted file mode 100644 index e94fc9fb..00000000 --- a/test/compilationTests/zeppelin/crowdsale/FinalizableCrowdsale.sol +++ /dev/null @@ -1,39 +0,0 @@ -pragma solidity ^0.4.11; - -import '../math/SafeMath.sol'; -import '../ownership/Ownable.sol'; -import './Crowdsale.sol'; - -/** - * @title FinalizableCrowdsale - * @dev Extension of Crowsdale where an owner can do extra work - * after finishing. By default, it will end token minting. - */ -contract FinalizableCrowdsale is Crowdsale, Ownable { - using SafeMath for uint256; - - bool public isFinalized = false; - - event Finalized(); - - // should be called after crowdsale ends, to do - // some extra finalization work - function finalize() public onlyOwner { - require(!isFinalized); - require(hasEnded()); - - finalization(); - emit Finalized(); - - isFinalized = true; - } - - // end token minting on finalization - // override this with custom logic if needed - function finalization() internal { - token.finishMinting(); - } - - - -} diff --git a/test/compilationTests/zeppelin/crowdsale/RefundVault.sol b/test/compilationTests/zeppelin/crowdsale/RefundVault.sol deleted file mode 100644 index ef1d8061..00000000 --- a/test/compilationTests/zeppelin/crowdsale/RefundVault.sol +++ /dev/null @@ -1,56 +0,0 @@ -pragma solidity ^0.4.11; - -import '../math/SafeMath.sol'; -import '../ownership/Ownable.sol'; - -/** - * @title RefundVault - * @dev This contract is used for storing funds while a crowdsale - * is in progress. Supports refunding the money if crowdsale fails, - * and forwarding it if crowdsale is successful. - */ -contract RefundVault is Ownable { - using SafeMath for uint256; - - enum State { Active, Refunding, Closed } - - mapping (address => uint256) public deposited; - address payable public wallet; - State public state; - - event Closed(); - event RefundsEnabled(); - event Refunded(address indexed beneficiary, uint256 weiAmount); - - constructor(address payable _wallet) public { - require(_wallet != address(0x0)); - wallet = _wallet; - state = State.Active; - } - - function deposit(address payable investor) public onlyOwner payable { - require(state == State.Active); - deposited[investor] = deposited[investor].add(msg.value); - } - - function close() public onlyOwner { - require(state == State.Active); - state = State.Closed; - emit Closed(); - wallet.transfer(address(this).balance); - } - - function enableRefunds() public onlyOwner { - require(state == State.Active); - state = State.Refunding; - emit RefundsEnabled(); - } - - function refund(address payable investor) public { - require(state == State.Refunding); - uint256 depositedValue = deposited[investor]; - deposited[investor] = 0; - investor.transfer(depositedValue); - emit Refunded(investor, depositedValue); - } -} diff --git a/test/compilationTests/zeppelin/crowdsale/RefundableCrowdsale.sol b/test/compilationTests/zeppelin/crowdsale/RefundableCrowdsale.sol deleted file mode 100644 index 94e3af99..00000000 --- a/test/compilationTests/zeppelin/crowdsale/RefundableCrowdsale.sol +++ /dev/null @@ -1,59 +0,0 @@ -pragma solidity ^0.4.11; - - -import '../math/SafeMath.sol'; -import './FinalizableCrowdsale.sol'; -import './RefundVault.sol'; - - -/** - * @title RefundableCrowdsale - * @dev Extension of Crowdsale contract that adds a funding goal, and - * the possibility of users getting a refund if goal is not met. - * Uses a RefundVault as the crowdsale's vault. - */ -contract RefundableCrowdsale is FinalizableCrowdsale { - using SafeMath for uint256; - - // minimum amount of funds to be raised in weis - uint256 public goal; - - // refund vault used to hold funds while crowdsale is running - RefundVault public vault; - - constructor(uint256 _goal) public { - vault = new RefundVault(wallet); - goal = _goal; - } - - // We're overriding the fund forwarding from Crowdsale. - // In addition to sending the funds, we want to call - // the RefundVault deposit function - function forwardFunds() internal { - vault.deposit.value(msg.value)(msg.sender); - } - - // if crowdsale is unsuccessful, investors can claim refunds here - function claimRefund() public { - require(isFinalized); - require(!goalReached()); - - vault.refund(msg.sender); - } - - // vault finalization task, called when owner calls finalize() - function finalization() internal { - if (goalReached()) { - vault.close(); - } else { - vault.enableRefunds(); - } - - super.finalization(); - } - - function goalReached() public view returns (bool) { - return weiRaised >= goal; - } - -} diff --git a/test/compilationTests/zeppelin/lifecycle/Destructible.sol b/test/compilationTests/zeppelin/lifecycle/Destructible.sol deleted file mode 100644 index 9b9d8223..00000000 --- a/test/compilationTests/zeppelin/lifecycle/Destructible.sol +++ /dev/null @@ -1,25 +0,0 @@ -pragma solidity ^0.4.11; - - -import "../ownership/Ownable.sol"; - - -/** - * @title Destructible - * @dev Base contract that can be destroyed by owner. All funds in contract will be sent to the owner. - */ -contract Destructible is Ownable { - - constructor() public payable { } - - /** - * @dev Transfers the current balance to the owner and terminates the contract. - */ - function destroy() public onlyOwner { - selfdestruct(owner); - } - - function destroyAndSend(address payable _recipient) public onlyOwner { - selfdestruct(_recipient); - } -} diff --git a/test/compilationTests/zeppelin/lifecycle/Migrations.sol b/test/compilationTests/zeppelin/lifecycle/Migrations.sol deleted file mode 100644 index 4ca95d36..00000000 --- a/test/compilationTests/zeppelin/lifecycle/Migrations.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.4.11; - - -import '../ownership/Ownable.sol'; - -/** - * @title Migrations - * @dev This is a truffle contract, needed for truffle integration, not meant for use by Zeppelin users. - */ -contract Migrations is Ownable { - uint256 public lastCompletedMigration; - - function setCompleted(uint256 completed) public onlyOwner { - lastCompletedMigration = completed; - } - - function upgrade(address newAddress) public onlyOwner { - Migrations upgraded = Migrations(newAddress); - upgraded.setCompleted(lastCompletedMigration); - } -} diff --git a/test/compilationTests/zeppelin/lifecycle/Pausable.sol b/test/compilationTests/zeppelin/lifecycle/Pausable.sol deleted file mode 100644 index 0c48f2f6..00000000 --- a/test/compilationTests/zeppelin/lifecycle/Pausable.sol +++ /dev/null @@ -1,51 +0,0 @@ -pragma solidity ^0.4.11; - - -import "../ownership/Ownable.sol"; - - -/** - * @title Pausable - * @dev Base contract which allows children to implement an emergency stop mechanism. - */ -contract Pausable is Ownable { - event Pause(); - event Unpause(); - - bool public paused = false; - - - /** - * @dev modifier to allow actions only when the contract IS paused - */ - modifier whenNotPaused() { - if (paused) revert(); - _; - } - - /** - * @dev modifier to allow actions only when the contract IS NOT paused - */ - modifier whenPaused { - if (!paused) revert(); - _; - } - - /** - * @dev called by the owner to pause, triggers stopped state - */ - function pause() public onlyOwner whenNotPaused returns (bool) { - paused = true; - emit Pause(); - return true; - } - - /** - * @dev called by the owner to unpause, returns to normal state - */ - function unpause() public onlyOwner whenPaused returns (bool) { - paused = false; - emit Unpause(); - return true; - } -} diff --git a/test/compilationTests/zeppelin/lifecycle/TokenDestructible.sol b/test/compilationTests/zeppelin/lifecycle/TokenDestructible.sol deleted file mode 100644 index eb141587..00000000 --- a/test/compilationTests/zeppelin/lifecycle/TokenDestructible.sol +++ /dev/null @@ -1,36 +0,0 @@ -pragma solidity ^0.4.11; - - -import "../ownership/Ownable.sol"; -import "../token/ERC20Basic.sol"; - -/** - * @title TokenDestructible: - * @author Remco Bloemen <remco@2Ï€.com> - * @dev Base contract that can be destroyed by owner. All funds in contract including - * listed tokens will be sent to the owner. - */ -contract TokenDestructible is Ownable { - - constructor() public payable { } - - /** - * @notice Terminate contract and refund to owner - * @param tokens List of addresses of ERC20 or ERC20Basic token contracts to - refund. - * @notice The called token contracts could try to re-enter this contract. Only - supply token contracts you trust. - */ - function destroy(address[] memory tokens) public onlyOwner { - - // Transfer tokens to owner - for(uint256 i = 0; i < tokens.length; i++) { - ERC20Basic token = ERC20Basic(tokens[i]); - uint256 balance = token.balanceOf(address(this)); - token.transfer(owner, balance); - } - - // Transfer Eth to owner and terminate contract - selfdestruct(owner); - } -} diff --git a/test/compilationTests/zeppelin/math/Math.sol b/test/compilationTests/zeppelin/math/Math.sol deleted file mode 100644 index 9997998a..00000000 --- a/test/compilationTests/zeppelin/math/Math.sol +++ /dev/null @@ -1,24 +0,0 @@ -pragma solidity ^0.4.11; - -/** - * @title Math - * @dev Assorted math operations - */ - -library Math { - function max64(uint64 a, uint64 b) internal pure returns (uint64) { - return a >= b ? a : b; - } - - function min64(uint64 a, uint64 b) internal pure returns (uint64) { - return a < b ? a : b; - } - - function max256(uint256 a, uint256 b) internal pure returns (uint256) { - return a >= b ? a : b; - } - - function min256(uint256 a, uint256 b) internal pure returns (uint256) { - return a < b ? a : b; - } -} diff --git a/test/compilationTests/zeppelin/math/SafeMath.sol b/test/compilationTests/zeppelin/math/SafeMath.sol deleted file mode 100644 index a98635e2..00000000 --- a/test/compilationTests/zeppelin/math/SafeMath.sol +++ /dev/null @@ -1,32 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title SafeMath - * @dev Math operations with safety checks that throw on error - */ -library SafeMath { - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 c = a * b; - assert(a == 0 || c / a == b); - return c; - } - - function div(uint256 a, uint256 b) internal pure returns (uint256) { - // assert(b > 0); // Solidity automatically throws when dividing by 0 - uint256 c = a / b; - // assert(a == b * c + a % b); // There is no case in which this doesn't hold - return c; - } - - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - assert(b <= a); - return a - b; - } - - function add(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 c = a + b; - assert(c >= a); - return c; - } -} diff --git a/test/compilationTests/zeppelin/ownership/Claimable.sol b/test/compilationTests/zeppelin/ownership/Claimable.sol deleted file mode 100644 index 148ad535..00000000 --- a/test/compilationTests/zeppelin/ownership/Claimable.sol +++ /dev/null @@ -1,40 +0,0 @@ -pragma solidity ^0.4.11; - - -import './Ownable.sol'; - - -/** - * @title Claimable - * @dev Extension for the Ownable contract, where the ownership needs to be claimed. - * This allows the new owner to accept the transfer. - */ -contract Claimable is Ownable { - address payable public pendingOwner; - - /** - * @dev Modifier throws if called by any account other than the pendingOwner. - */ - modifier onlyPendingOwner() { - if (msg.sender != pendingOwner) { - revert(); - } - _; - } - - /** - * @dev Allows the current owner to set the pendingOwner address. - * @param newOwner The address to transfer ownership to. - */ - function transferOwnership(address payable newOwner) public onlyOwner { - pendingOwner = newOwner; - } - - /** - * @dev Allows the pendingOwner address to finalize the transfer. - */ - function claimOwnership() public onlyPendingOwner { - owner = pendingOwner; - pendingOwner = address(0x0); - } -} diff --git a/test/compilationTests/zeppelin/ownership/Contactable.sol b/test/compilationTests/zeppelin/ownership/Contactable.sol deleted file mode 100644 index 5053494d..00000000 --- a/test/compilationTests/zeppelin/ownership/Contactable.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.4.11; - -import './Ownable.sol'; - -/** - * @title Contactable token - * @dev Basic version of a contactable contract, allowing the owner to provide a string with their - * contact information. - */ -contract Contactable is Ownable{ - - string public contactInformation; - - /** - * @dev Allows the owner to set a string with their contact information. - * @param info The contact information to attach to the contract. - */ - function setContactInformation(string memory info) public onlyOwner{ - contactInformation = info; - } -} diff --git a/test/compilationTests/zeppelin/ownership/DelayedClaimable.sol b/test/compilationTests/zeppelin/ownership/DelayedClaimable.sol deleted file mode 100644 index f4c8a681..00000000 --- a/test/compilationTests/zeppelin/ownership/DelayedClaimable.sol +++ /dev/null @@ -1,43 +0,0 @@ -pragma solidity ^0.4.11; - - -import './Claimable.sol'; - - -/** - * @title DelayedClaimable - * @dev Extension for the Claimable contract, where the ownership needs to be claimed before/after - * a certain block number. - */ -contract DelayedClaimable is Claimable { - - uint256 public end; - uint256 public start; - - /** - * @dev Used to specify the time period during which a pending - * owner can claim ownership. - * @param _start The earliest time ownership can be claimed. - * @param _end The latest time ownership can be claimed. - */ - function setLimits(uint256 _start, uint256 _end) public onlyOwner { - if (_start > _end) - revert(); - end = _end; - start = _start; - } - - - /** - * @dev Allows the pendingOwner address to finalize the transfer, as long as it is called within - * the specified start and end time. - */ - function claimOwnership() public onlyPendingOwner { - if ((block.number > end) || (block.number < start)) - revert(); - owner = pendingOwner; - pendingOwner = address(0x0); - end = 0; - } - -} diff --git a/test/compilationTests/zeppelin/ownership/HasNoContracts.sol b/test/compilationTests/zeppelin/ownership/HasNoContracts.sol deleted file mode 100644 index 19b363d4..00000000 --- a/test/compilationTests/zeppelin/ownership/HasNoContracts.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.4.11; - -import "./Ownable.sol"; - -/** - * @title Contracts that should not own Contracts - * @author Remco Bloemen <remco@2Ï€.com> - * @dev Should contracts (anything Ownable) end up being owned by this contract, it allows the owner - * of this contract to reclaim ownership of the contracts. - */ -contract HasNoContracts is Ownable { - - /** - * @dev Reclaim ownership of Ownable contracts - * @param contractAddr The address of the Ownable to be reclaimed. - */ - function reclaimContract(address contractAddr) external onlyOwner { - Ownable contractInst = Ownable(contractAddr); - contractInst.transferOwnership(owner); - } -} diff --git a/test/compilationTests/zeppelin/ownership/HasNoEther.sol b/test/compilationTests/zeppelin/ownership/HasNoEther.sol deleted file mode 100644 index 5e3d27d2..00000000 --- a/test/compilationTests/zeppelin/ownership/HasNoEther.sol +++ /dev/null @@ -1,44 +0,0 @@ -pragma solidity ^0.4.11; - -import "./Ownable.sol"; - -/** - * @title Contracts that should not own Ether - * @author Remco Bloemen <remco@2Ï€.com> - * @dev This tries to block incoming ether to prevent accidental loss of Ether. Should Ether end up - * in the contract, it will allow the owner to reclaim this ether. - * @notice Ether can still be send to this contract by: - * calling functions labeled `payable` - * `selfdestruct(contract_address)` - * mining directly to the contract address -*/ -contract HasNoEther is Ownable { - - /** - * @dev Constructor that rejects incoming Ether - * @dev The `payable` flag is added so we can access `msg.value` without compiler warning. If we - * leave out payable, then Solidity will allow inheriting contracts to implement a payable - * constructor. By doing it this way we prevent a payable constructor from working. Alternatively - * we could use assembly to access msg.value. - */ - constructor() public payable { - if(msg.value > 0) { - revert(); - } - } - - /** - * @dev Disallows direct send by settings a default function without the `payable` flag. - */ - function() external { - } - - /** - * @dev Transfer all Ether held by the contract to the owner. - */ - function reclaimEther() external onlyOwner { - if(!owner.send(address(this).balance)) { - revert(); - } - } -} diff --git a/test/compilationTests/zeppelin/ownership/HasNoTokens.sol b/test/compilationTests/zeppelin/ownership/HasNoTokens.sol deleted file mode 100644 index 079cef7c..00000000 --- a/test/compilationTests/zeppelin/ownership/HasNoTokens.sol +++ /dev/null @@ -1,34 +0,0 @@ -pragma solidity ^0.4.11; - -import "./Ownable.sol"; -import "../token/ERC20Basic.sol"; - -/** - * @title Contracts that should not own Tokens - * @author Remco Bloemen <remco@2Ï€.com> - * @dev This blocks incoming ERC23 tokens to prevent accidental loss of tokens. - * Should tokens (any ERC20Basic compatible) end up in the contract, it allows the - * owner to reclaim the tokens. - */ -contract HasNoTokens is Ownable { - - /** - * @dev Reject all ERC23 compatible tokens - * @param from_ address The address that is transferring the tokens - * @param value_ uint256 the amount of the specified token - * @param data_ Bytes The data passed from the caller. - */ - function tokenFallback(address from_, uint256 value_, bytes calldata data_) external { - revert(); - } - - /** - * @dev Reclaim all ERC20Basic compatible tokens - * @param tokenAddr address The address of the token contract - */ - function reclaimToken(address tokenAddr) external onlyOwner { - ERC20Basic tokenInst = ERC20Basic(tokenAddr); - uint256 balance = tokenInst.balanceOf(address(this)); - tokenInst.transfer(owner, balance); - } -} diff --git a/test/compilationTests/zeppelin/ownership/Multisig.sol b/test/compilationTests/zeppelin/ownership/Multisig.sol deleted file mode 100644 index 2eb0f4bc..00000000 --- a/test/compilationTests/zeppelin/ownership/Multisig.sol +++ /dev/null @@ -1,28 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title Multisig - * @dev Interface contract for multisig proxy contracts; see below for docs. - */ -contract Multisig { - // EVENTS - - // logged events: - // Funds has arrived into the wallet (record how much). - event Deposit(address _from, uint256 value); - // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going). - event SingleTransact(address owner, uint256 value, address to, bytes data); - // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going). - event MultiTransact(address owner, bytes32 operation, uint256 value, address to, bytes data); - // Confirmation still needed for a transaction. - event ConfirmationNeeded(bytes32 operation, address initiator, uint256 value, address to, bytes data); - - - // FUNCTIONS - - // TODO: document - function changeOwner(address _from, address _to) external; - function execute(address _to, uint256 _value, bytes calldata _data) external returns (bytes32); - function confirm(bytes32 _h) public returns (bool); -} diff --git a/test/compilationTests/zeppelin/ownership/NoOwner.sol b/test/compilationTests/zeppelin/ownership/NoOwner.sol deleted file mode 100644 index c0ef7f4d..00000000 --- a/test/compilationTests/zeppelin/ownership/NoOwner.sol +++ /dev/null @@ -1,14 +0,0 @@ -pragma solidity ^0.4.11; - -import "./HasNoEther.sol"; -import "./HasNoTokens.sol"; -import "./HasNoContracts.sol"; - -/** - * @title Base contract for contracts that should not own things. - * @author Remco Bloemen <remco@2Ï€.com> - * @dev Solves a class of errors where a contract accidentally becomes owner of Ether, Tokens or - * Owned contracts. See respective base contracts for details. - */ -contract NoOwner is HasNoEther, HasNoTokens, HasNoContracts { -} diff --git a/test/compilationTests/zeppelin/ownership/Ownable.sol b/test/compilationTests/zeppelin/ownership/Ownable.sol deleted file mode 100644 index 3c95127d..00000000 --- a/test/compilationTests/zeppelin/ownership/Ownable.sol +++ /dev/null @@ -1,43 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title Ownable - * @dev The Ownable contract has an owner address, and provides basic authorization control - * functions, this simplifies the implementation of "user permissions". - */ -contract Ownable { - address payable public owner; - - - /** - * @dev The Ownable constructor sets the original `owner` of the contract to the sender - * account. - */ - constructor() public { - owner = msg.sender; - } - - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - if (msg.sender != owner) { - revert(); - } - _; - } - - - /** - * @dev Allows the current owner to transfer control of the contract to a newOwner. - * @param newOwner The address to transfer ownership to. - */ - function transferOwnership(address payable newOwner) public onlyOwner { - if (newOwner != address(0)) { - owner = newOwner; - } - } - -} diff --git a/test/compilationTests/zeppelin/ownership/Shareable.sol b/test/compilationTests/zeppelin/ownership/Shareable.sol deleted file mode 100644 index b2cb165d..00000000 --- a/test/compilationTests/zeppelin/ownership/Shareable.sol +++ /dev/null @@ -1,189 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title Shareable - * @dev inheritable "property" contract that enables methods to be protected by requiring the - * acquiescence of either a single, or, crucially, each of a number of, designated owners. - * @dev Usage: use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by some number (specified in constructor) of the set of owners (specified in the constructor) before the interior is executed. - */ -contract Shareable { - - // struct for the status of a pending operation. - struct PendingState { - uint256 yetNeeded; - uint256 ownersDone; - uint256 index; - } - - // the number of owners that must confirm the same operation before it is run. - uint256 public required; - - // list of owners - address[256] owners; - // index on the list of owners to allow reverse lookup - mapping(address => uint256) ownerIndex; - // the ongoing operations. - mapping(bytes32 => PendingState) pendings; - bytes32[] pendingsIndex; - - - // this contract only has six types of events: it can accept a confirmation, in which case - // we record owner and operation (hash) alongside it. - event Confirmation(address owner, bytes32 operation); - event Revoke(address owner, bytes32 operation); - - - // simple single-sig function modifier. - modifier onlyOwner { - if (!isOwner(msg.sender)) { - revert(); - } - _; - } - - /** - * @dev Modifier for multisig functions. - * @param _operation The operation must have an intrinsic hash in order that later attempts can be - * realised as the same underlying operation and thus count as confirmations. - */ - modifier onlymanyowners(bytes32 _operation) { - if (confirmAndCheck(_operation)) { - _; - } - } - - /** - * @dev Constructor is given the number of sigs required to do protected "onlymanyowners" - * transactions as well as the selection of addresses capable of confirming them. - * @param _owners A list of owners. - * @param _required The amount required for a transaction to be approved. - */ - constructor(address[] memory _owners, uint256 _required) public { - owners[1] = msg.sender; - ownerIndex[msg.sender] = 1; - for (uint256 i = 0; i < _owners.length; ++i) { - owners[2 + i] = _owners[i]; - ownerIndex[_owners[i]] = 2 + i; - } - required = _required; - if (required > owners.length) { - revert(); - } - } - - /** - * @dev Revokes a prior confirmation of the given operation. - * @param _operation A string identifying the operation. - */ - function revoke(bytes32 _operation) external { - uint256 index = ownerIndex[msg.sender]; - // make sure they're an owner - if (index == 0) { - return; - } - uint256 ownerIndexBit = 2**index; - PendingState memory pending = pendings[_operation]; - if (pending.ownersDone & ownerIndexBit > 0) { - pending.yetNeeded++; - pending.ownersDone -= ownerIndexBit; - emit Revoke(msg.sender, _operation); - } - } - - /** - * @dev Gets an owner by 0-indexed position (using numOwners as the count) - * @param ownerIndex uint256 The index of the owner - * @return The address of the owner - */ - function getOwner(uint256 ownerIndex) external view returns (address) { - return address(owners[ownerIndex + 1]); - } - - /** - * @dev Checks if given address is an owner. - * @param _addr address The address which you want to check. - * @return True if the address is an owner and fase otherwise. - */ - function isOwner(address _addr) public view returns (bool) { - return ownerIndex[_addr] > 0; - } - - /** - * @dev Function to check is specific owner has already confirme the operation. - * @param _operation The operation identifier. - * @param _owner The owner address. - * @return True if the owner has confirmed and false otherwise. - */ - function hasConfirmed(bytes32 _operation, address _owner) public view returns (bool) { - PendingState memory pending = pendings[_operation]; - uint256 index = ownerIndex[_owner]; - - // make sure they're an owner - if (index == 0) { - return false; - } - - // determine the bit to set for this owner. - uint256 ownerIndexBit = 2**index; - return !(pending.ownersDone & ownerIndexBit == 0); - } - - /** - * @dev Confirm and operation and checks if it's already executable. - * @param _operation The operation identifier. - * @return Returns true when operation can be executed. - */ - function confirmAndCheck(bytes32 _operation) internal returns (bool) { - // determine what index the present sender is: - uint256 index = ownerIndex[msg.sender]; - // make sure they're an owner - if (index == 0) { - revert(); - } - - PendingState memory pending = pendings[_operation]; - // if we're not yet working on this operation, switch over and reset the confirmation status. - if (pending.yetNeeded == 0) { - // reset count of confirmations needed. - pending.yetNeeded = required; - // reset which owners have confirmed (none) - set our bitmap to 0. - pending.ownersDone = 0; - pending.index = pendingsIndex.length++; - pendingsIndex[pending.index] = _operation; - } - // determine the bit to set for this owner. - uint256 ownerIndexBit = 2**index; - // make sure we (the message sender) haven't confirmed this operation previously. - if (pending.ownersDone & ownerIndexBit == 0) { - emit Confirmation(msg.sender, _operation); - // ok - check if count is enough to go ahead. - if (pending.yetNeeded <= 1) { - // enough confirmations: reset and run interior. - delete pendingsIndex[pendings[_operation].index]; - delete pendings[_operation]; - return true; - } else { - // not enough: record that this owner in particular confirmed. - pending.yetNeeded--; - pending.ownersDone |= ownerIndexBit; - } - } - return false; - } - - - /** - * @dev Clear the pending list. - */ - function clearPending() internal { - uint256 length = pendingsIndex.length; - for (uint256 i = 0; i < length; ++i) { - if (pendingsIndex[i] != 0) { - delete pendings[pendingsIndex[i]]; - } - } - delete pendingsIndex; - } - -} diff --git a/test/compilationTests/zeppelin/payment/PullPayment.sol b/test/compilationTests/zeppelin/payment/PullPayment.sol deleted file mode 100644 index 5682d3c2..00000000 --- a/test/compilationTests/zeppelin/payment/PullPayment.sol +++ /dev/null @@ -1,50 +0,0 @@ -pragma solidity ^0.4.11; - - -import '../math/SafeMath.sol'; - - -/** - * @title PullPayment - * @dev Base contract supporting async send for pull payments. Inherit from this - * contract and use asyncSend instead of send. - */ -contract PullPayment { - using SafeMath for uint256; - - mapping(address => uint256) public payments; - uint256 public totalPayments; - - /** - * @dev Called by the payer to store the sent amount as credit to be pulled. - * @param dest The destination address of the funds. - * @param amount The amount to transfer. - */ - function asyncSend(address dest, uint256 amount) internal { - payments[dest] = payments[dest].add(amount); - totalPayments = totalPayments.add(amount); - } - - /** - * @dev withdraw accumulated balance, called by payee. - */ - function withdrawPayments() public { - address payable payee = msg.sender; - uint256 payment = payments[payee]; - - if (payment == 0) { - revert(); - } - - if (address(this).balance < payment) { - revert(); - } - - totalPayments = totalPayments.sub(payment); - payments[payee] = 0; - - if (!payee.send(payment)) { - revert(); - } - } -} diff --git a/test/compilationTests/zeppelin/token/BasicToken.sol b/test/compilationTests/zeppelin/token/BasicToken.sol deleted file mode 100644 index 3d5646a4..00000000 --- a/test/compilationTests/zeppelin/token/BasicToken.sol +++ /dev/null @@ -1,37 +0,0 @@ -pragma solidity ^0.4.11; - - -import './ERC20Basic.sol'; -import '../math/SafeMath.sol'; - - -/** - * @title Basic token - * @dev Basic version of StandardToken, with no allowances. - */ -contract BasicToken is ERC20Basic { - using SafeMath for uint256; - - mapping(address => uint256) balances; - - /** - * @dev transfer token for a specified address - * @param _to The address to transfer to. - * @param _value The amount to be transferred. - */ - function transfer(address _to, uint256 _value) public { - balances[msg.sender] = balances[msg.sender].sub(_value); - balances[_to] = balances[_to].add(_value); - emit Transfer(msg.sender, _to, _value); - } - - /** - * @dev Gets the balance of the specified address. - * @param _owner The address to query the the balance of. - * @return An uint256 representing the amount owned by the passed address. - */ - function balanceOf(address _owner) public view returns (uint256 balance) { - return balances[_owner]; - } - -} diff --git a/test/compilationTests/zeppelin/token/ERC20.sol b/test/compilationTests/zeppelin/token/ERC20.sol deleted file mode 100644 index 5b5dc748..00000000 --- a/test/compilationTests/zeppelin/token/ERC20.sol +++ /dev/null @@ -1,16 +0,0 @@ -pragma solidity ^0.4.11; - - -import './ERC20Basic.sol'; - - -/** - * @title ERC20 interface - * @dev see https://github.com/ethereum/EIPs/issues/20 - */ -contract ERC20 is ERC20Basic { - function allowance(address owner, address spender) public view returns (uint256); - function transferFrom(address from, address to, uint256 value) public; - function approve(address spender, uint256 value) public; - event Approval(address indexed owner, address indexed spender, uint256 value); -} diff --git a/test/compilationTests/zeppelin/token/ERC20Basic.sol b/test/compilationTests/zeppelin/token/ERC20Basic.sol deleted file mode 100644 index fbe33134..00000000 --- a/test/compilationTests/zeppelin/token/ERC20Basic.sol +++ /dev/null @@ -1,14 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title ERC20Basic - * @dev Simpler version of ERC20 interface - * @dev see https://github.com/ethereum/EIPs/issues/20 - */ -contract ERC20Basic { - uint256 public totalSupply; - function balanceOf(address who) public view returns (uint256); - function transfer(address to, uint256 value) public; - event Transfer(address indexed from, address indexed to, uint256 value); -} diff --git a/test/compilationTests/zeppelin/token/LimitedTransferToken.sol b/test/compilationTests/zeppelin/token/LimitedTransferToken.sol deleted file mode 100644 index d668b86f..00000000 --- a/test/compilationTests/zeppelin/token/LimitedTransferToken.sol +++ /dev/null @@ -1,57 +0,0 @@ -pragma solidity ^0.4.11; - -import "./ERC20.sol"; - -/** - * @title LimitedTransferToken - * @dev LimitedTransferToken defines the generic interface and the implementation to limit token - * transferability for different events. It is intended to be used as a base class for other token - * contracts. - * LimitedTransferToken has been designed to allow for different limiting factors, - * this can be achieved by recursively calling super.transferableTokens() until the base class is - * hit. For example: - * function transferableTokens(address holder, uint64 time) view public returns (uint256) { - * return min256(unlockedTokens, super.transferableTokens(holder, time)); - * } - * A working example is VestedToken.sol: - * https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/VestedToken.sol - */ - -contract LimitedTransferToken is ERC20 { - - /** - * @dev Checks whether it can transfer or otherwise throws. - */ - modifier canTransfer(address _sender, uint256 _value) { - if (_value > transferableTokens(_sender, uint64(now))) revert(); - _; - } - - /** - * @dev Checks modifier and allows transfer if tokens are not locked. - * @param _to The address that will receive the tokens. - * @param _value The amount of tokens to be transferred. - */ - function transfer(address _to, uint256 _value) canTransfer(msg.sender, _value) public { - super.transfer(_to, _value); - } - - /** - * @dev Checks modifier and allows transfer if tokens are not locked. - * @param _from The address that will send the tokens. - * @param _to The address that will receive the tokens. - * @param _value The amount of tokens to be transferred. - */ - function transferFrom(address _from, address _to, uint256 _value) public canTransfer(_from, _value) { - super.transferFrom(_from, _to, _value); - } - - /** - * @dev Default transferable tokens function returns all tokens for a holder (no limit). - * @dev Overwriting transferableTokens(address holder, uint64 time) is the way to provide the - * specific logic for limiting token transferability for a holder over time. - */ - function transferableTokens(address holder, uint64 time) view public returns (uint256) { - return balanceOf(holder); - } -} diff --git a/test/compilationTests/zeppelin/token/MintableToken.sol b/test/compilationTests/zeppelin/token/MintableToken.sol deleted file mode 100644 index 24b8c807..00000000 --- a/test/compilationTests/zeppelin/token/MintableToken.sol +++ /dev/null @@ -1,50 +0,0 @@ -pragma solidity ^0.4.11; - - -import './StandardToken.sol'; -import '../ownership/Ownable.sol'; - - - -/** - * @title Mintable token - * @dev Simple ERC20 Token example, with mintable token creation - * @dev Issue: * https://github.com/OpenZeppelin/zeppelin-solidity/issues/120 - * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol - */ - -contract MintableToken is StandardToken, Ownable { - event Mint(address indexed to, uint256 amount); - event MintFinished(); - - bool public mintingFinished = false; - - - modifier canMint() { - if(mintingFinished) revert(); - _; - } - - /** - * @dev Function to mint tokens - * @param _to The address that will receive the minted tokens. - * @param _amount The amount of tokens to mint. - * @return A boolean that indicates if the operation was successful. - */ - function mint(address _to, uint256 _amount) public onlyOwner canMint returns (bool) { - totalSupply = totalSupply.add(_amount); - balances[_to] = balances[_to].add(_amount); - emit Mint(_to, _amount); - return true; - } - - /** - * @dev Function to stop minting new tokens. - * @return True if the operation was successful. - */ - function finishMinting() public onlyOwner returns (bool) { - mintingFinished = true; - emit MintFinished(); - return true; - } -} diff --git a/test/compilationTests/zeppelin/token/PausableToken.sol b/test/compilationTests/zeppelin/token/PausableToken.sol deleted file mode 100644 index 66f80b80..00000000 --- a/test/compilationTests/zeppelin/token/PausableToken.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.4.11; - -import './StandardToken.sol'; -import '../lifecycle/Pausable.sol'; - -/** - * Pausable token - * - * Simple ERC20 Token example, with pausable token creation - **/ - -contract PausableToken is StandardToken, Pausable { - - function transfer(address _to, uint _value) public whenNotPaused { - super.transfer(_to, _value); - } - - function transferFrom(address _from, address _to, uint _value) public whenNotPaused { - super.transferFrom(_from, _to, _value); - } -} diff --git a/test/compilationTests/zeppelin/token/SimpleToken.sol b/test/compilationTests/zeppelin/token/SimpleToken.sol deleted file mode 100644 index d8717562..00000000 --- a/test/compilationTests/zeppelin/token/SimpleToken.sol +++ /dev/null @@ -1,28 +0,0 @@ -pragma solidity ^0.4.11; - - -import "./StandardToken.sol"; - - -/** - * @title SimpleToken - * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator. - * Note they can later distribute these tokens as they wish using `transfer` and other - * `StandardToken` functions. - */ -contract SimpleToken is StandardToken { - - string public name = "SimpleToken"; - string public symbol = "SIM"; - uint256 public decimals = 18; - uint256 public INITIAL_SUPPLY = 10000; - - /** - * @dev Constructor that gives msg.sender all of existing tokens. - */ - constructor() public { - totalSupply = INITIAL_SUPPLY; - balances[msg.sender] = INITIAL_SUPPLY; - } - -} diff --git a/test/compilationTests/zeppelin/token/StandardToken.sol b/test/compilationTests/zeppelin/token/StandardToken.sol deleted file mode 100644 index 56f4a5f3..00000000 --- a/test/compilationTests/zeppelin/token/StandardToken.sol +++ /dev/null @@ -1,65 +0,0 @@ -pragma solidity ^0.4.11; - - -import './BasicToken.sol'; -import './ERC20.sol'; - - -/** - * @title Standard ERC20 token - * - * @dev Implementation of the basic standard token. - * @dev https://github.com/ethereum/EIPs/issues/20 - * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol - */ -contract StandardToken is ERC20, BasicToken { - - mapping (address => mapping (address => uint256)) allowed; - - - /** - * @dev Transfer tokens from one address to another - * @param _from address The address which you want to send tokens from - * @param _to address The address which you want to transfer to - * @param _value uint256 the amount of tokens to be transferred - */ - function transferFrom(address _from, address _to, uint256 _value) public { - uint256 _allowance = allowed[_from][msg.sender]; - - // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met - // if (_value > _allowance) revert(); - - balances[_to] = balances[_to].add(_value); - balances[_from] = balances[_from].sub(_value); - allowed[_from][msg.sender] = _allowance.sub(_value); - emit Transfer(_from, _to, _value); - } - - /** - * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender. - * @param _spender The address which will spend the funds. - * @param _value The amount of tokens to be spent. - */ - function approve(address _spender, uint256 _value) public { - - // To change the approve amount you first have to reduce the addresses` - // allowance to zero by calling `approve(_spender, 0)` if it is not - // already 0 to mitigate the race condition described here: - // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) revert(); - - allowed[msg.sender][_spender] = _value; - emit Approval(msg.sender, _spender, _value); - } - - /** - * @dev Function to check the amount of tokens that an owner allowed to a spender. - * @param _owner address The address which owns the funds. - * @param _spender address The address which will spend the funds. - * @return A uint256 specifying the amount of tokens still available for the spender. - */ - function allowance(address _owner, address _spender) public view returns (uint256 remaining) { - return allowed[_owner][_spender]; - } - -} diff --git a/test/compilationTests/zeppelin/token/TokenTimelock.sol b/test/compilationTests/zeppelin/token/TokenTimelock.sol deleted file mode 100644 index 4847b648..00000000 --- a/test/compilationTests/zeppelin/token/TokenTimelock.sol +++ /dev/null @@ -1,41 +0,0 @@ -pragma solidity ^0.4.11; - - -import './ERC20Basic.sol'; - -/** - * @title TokenTimelock - * @dev TokenTimelock is a token holder contract that will allow a - * beneficiary to extract the tokens after a given release time - */ -contract TokenTimelock { - - // ERC20 basic token contract being held - ERC20Basic token; - - // beneficiary of tokens after they are released - address beneficiary; - - // timestamp when token release is enabled - uint releaseTime; - - constructor(ERC20Basic _token, address _beneficiary, uint _releaseTime) public { - require(_releaseTime > now); - token = _token; - beneficiary = _beneficiary; - releaseTime = _releaseTime; - } - - /** - * @dev beneficiary claims tokens held by time lock - */ - function claim() public { - require(msg.sender == beneficiary); - require(now >= releaseTime); - - uint amount = token.balanceOf(address(this)); - require(amount > 0); - - token.transfer(beneficiary, amount); - } -} diff --git a/test/compilationTests/zeppelin/token/VestedToken.sol b/test/compilationTests/zeppelin/token/VestedToken.sol deleted file mode 100644 index c9469f16..00000000 --- a/test/compilationTests/zeppelin/token/VestedToken.sol +++ /dev/null @@ -1,248 +0,0 @@ -pragma solidity ^0.4.11; - -import "../math/Math.sol"; -import "./StandardToken.sol"; -import "./LimitedTransferToken.sol"; - -/** - * @title Vested token - * @dev Tokens that can be vested for a group of addresses. - */ -contract VestedToken is StandardToken, LimitedTransferToken { - - uint256 MAX_GRANTS_PER_ADDRESS = 20; - - struct TokenGrant { - address granter; // 20 bytes - uint256 value; // 32 bytes - uint64 cliff; - uint64 vesting; - uint64 start; // 3 * 8 = 24 bytes - bool revokable; - bool burnsOnRevoke; // 2 * 1 = 2 bits? or 2 bytes? - } // total 78 bytes = 3 sstore per operation (32 per sstore) - - mapping (address => TokenGrant[]) public grants; - - event NewTokenGrant(address indexed from, address indexed to, uint256 value, uint256 grantId); - - /** - * @dev Grant tokens to a specified address - * @param _to address The address which the tokens will be granted to. - * @param _value uint256 The amount of tokens to be granted. - * @param _start uint64 Time of the beginning of the grant. - * @param _cliff uint64 Time of the cliff period. - * @param _vesting uint64 The vesting period. - */ - function grantVestedTokens( - address _to, - uint256 _value, - uint64 _start, - uint64 _cliff, - uint64 _vesting, - bool _revokable, - bool _burnsOnRevoke - ) public { - - // Check for date inconsistencies that may cause unexpected behavior - if (_cliff < _start || _vesting < _cliff) { - revert(); - } - - if (tokenGrantsCount(_to) > MAX_GRANTS_PER_ADDRESS) revert(); // To prevent a user being spammed and have his balance locked (out of gas attack when calculating vesting). - - uint256 count = grants[_to].push( - TokenGrant( - _revokable ? msg.sender : 0x0000000000000000000000000000000000000000, // avoid storing an extra 20 bytes when it is non-revokable - _value, - _cliff, - _vesting, - _start, - _revokable, - _burnsOnRevoke - ) - ); - - transfer(_to, _value); - - emit NewTokenGrant(msg.sender, _to, _value, count - 1); - } - - /** - * @dev Revoke the grant of tokens of a specified address. - * @param _holder The address which will have its tokens revoked. - * @param _grantId The id of the token grant. - */ - function revokeTokenGrant(address _holder, uint256 _grantId) public { - TokenGrant storage grant = grants[_holder][_grantId]; - - if (!grant.revokable) { // Check if grant was revokable - revert(); - } - - if (grant.granter != msg.sender) { // Only granter can revoke it - revert(); - } - - address receiver = grant.burnsOnRevoke ? 0x000000000000000000000000000000000000dEaD : msg.sender; - - uint256 nonVested = nonVestedTokens(grant, uint64(now)); - - // remove grant from array - delete grants[_holder][_grantId]; - grants[_holder][_grantId] = grants[_holder][grants[_holder].length.sub(1)]; - grants[_holder].length -= 1; - - balances[receiver] = balances[receiver].add(nonVested); - balances[_holder] = balances[_holder].sub(nonVested); - - emit Transfer(_holder, receiver, nonVested); - } - - - /** - * @dev Calculate the total amount of transferable tokens of a holder at a given time - * @param holder address The address of the holder - * @param time uint64 The specific time. - * @return An uint256 representing a holder's total amount of transferable tokens. - */ - function transferableTokens(address holder, uint64 time) view public returns (uint256) { - uint256 grantIndex = tokenGrantsCount(holder); - - if (grantIndex == 0) return balanceOf(holder); // shortcut for holder without grants - - // Iterate through all the grants the holder has, and add all non-vested tokens - uint256 nonVested = 0; - for (uint256 i = 0; i < grantIndex; i++) { - nonVested = SafeMath.add(nonVested, nonVestedTokens(grants[holder][i], time)); - } - - // Balance - totalNonVested is the amount of tokens a holder can transfer at any given time - uint256 vestedTransferable = SafeMath.sub(balanceOf(holder), nonVested); - - // Return the minimum of how many vested can transfer and other value - // in case there are other limiting transferability factors (default is balanceOf) - return Math.min256(vestedTransferable, super.transferableTokens(holder, time)); - } - - /** - * @dev Check the amount of grants that an address has. - * @param _holder The holder of the grants. - * @return A uint256 representing the total amount of grants. - */ - function tokenGrantsCount(address _holder) public view returns (uint256 index) { - return grants[_holder].length; - } - - /** - * @dev Calculate amount of vested tokens at a specific time. - * @param tokens uint256 The amount of tokens granted. - * @param time uint64 The time to be checked - * @param start uint64 A time representing the beginning of the grant - * @param cliff uint64 The cliff period. - * @param vesting uint64 The vesting period. - * @return An uint256 representing the amount of vested tokens of a specific grant. - * transferableTokens - * | _/-------- vestedTokens rect - * | _/ - * | _/ - * | _/ - * | _/ - * | / - * | .| - * | . | - * | . | - * | . | - * | . | - * | . | - * +===+===========+---------+----------> time - * Start Clift Vesting - */ - function calculateVestedTokens( - uint256 tokens, - uint256 time, - uint256 start, - uint256 cliff, - uint256 vesting) public view returns (uint256) - { - // Shortcuts for before cliff and after vesting cases. - if (time < cliff) return 0; - if (time >= vesting) return tokens; - - // Interpolate all vested tokens. - // As before cliff the shortcut returns 0, we can use just calculate a value - // in the vesting rect (as shown in above's figure) - - // vestedTokens = tokens * (time - start) / (vesting - start) - uint256 vestedTokens = SafeMath.div( - SafeMath.mul( - tokens, - SafeMath.sub(time, start) - ), - SafeMath.sub(vesting, start) - ); - - return vestedTokens; - } - - /** - * @dev Get all information about a specific grant. - * @param _holder The address which will have its tokens revoked. - * @param _grantId The id of the token grant. - * @return Returns all the values that represent a TokenGrant(address, value, start, cliff, - * revokability, burnsOnRevoke, and vesting) plus the vested value at the current time. - */ - function tokenGrant(address _holder, uint256 _grantId) public view returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting, bool revokable, bool burnsOnRevoke) { - TokenGrant storage grant = grants[_holder][_grantId]; - - granter = grant.granter; - value = grant.value; - start = grant.start; - cliff = grant.cliff; - vesting = grant.vesting; - revokable = grant.revokable; - burnsOnRevoke = grant.burnsOnRevoke; - - vested = vestedTokens(grant, uint64(now)); - } - - /** - * @dev Get the amount of vested tokens at a specific time. - * @param grant TokenGrant The grant to be checked. - * @param time The time to be checked - * @return An uint256 representing the amount of vested tokens of a specific grant at a specific time. - */ - function vestedTokens(TokenGrant memory grant, uint64 time) private view returns (uint256) { - return calculateVestedTokens( - grant.value, - uint256(time), - uint256(grant.start), - uint256(grant.cliff), - uint256(grant.vesting) - ); - } - - /** - * @dev Calculate the amount of non vested tokens at a specific time. - * @param grant TokenGrant The grant to be checked. - * @param time uint64 The time to be checked - * @return An uint256 representing the amount of non vested tokens of a specific grant on the - * passed time frame. - */ - function nonVestedTokens(TokenGrant memory grant, uint64 time) private view returns (uint256) { - return grant.value.sub(vestedTokens(grant, time)); - } - - /** - * @dev Calculate the date when the holder can transfer all its tokens - * @param holder address The address of the holder - * @return An uint256 representing the date of the last transferable tokens. - */ - function lastTokenIsTransferableDate(address holder) view public returns (uint64 date) { - date = uint64(now); - uint256 grantIndex = grants[holder].length; - for (uint256 i = 0; i < grantIndex; i++) { - date = Math.max64(grants[holder][i].vesting, date); - } - } -} diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index c7e60256..195004cb 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -133,23 +133,6 @@ BOOST_AUTO_TEST_CASE(assignment_in_declaration) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(function_call_does_not_clear_local_vars) -{ - string text = R"( - contract C { - function g() public pure {} - function f() public view { - uint a = 3; - this.g(); - assert(a == 3); - g(); - assert(a == 3); - } - } - )"; - CHECK_WARNING(text, "Assertion checker does not yet implement this type of function call"); -} - BOOST_AUTO_TEST_CASE(branches_merge_variables) { // Branch does not touch variable a diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 07f8fba6..43951f73 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -42,13 +42,13 @@ SemVerMatchExpression parseExpression(string const& _input) { Scanner scanner{CharStream(_input)}; vector<string> literals; - vector<Token::Value> tokens; + vector<Token> tokens; while (scanner.currentToken() != Token::EOS) { auto token = scanner.currentToken(); string literal = scanner.currentLiteral(); - if (literal.empty() && Token::toString(token)) - literal = Token::toString(token); + if (literal.empty() && TokenTraits::toString(token)) + literal = TokenTraits::toString(token); literals.push_back(literal); tokens.push_back(token); scanner.next(); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 7a496e64..87646737 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1259,14 +1259,14 @@ BOOST_AUTO_TEST_CASE(state_smoke_test) } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(0)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(0)); - ABI_CHECK(callContractFunction("set(uint8,uint256)", byte(0x00), 0x1234), encodeArgs()); - ABI_CHECK(callContractFunction("set(uint8,uint256)", byte(0x01), 0x8765), encodeArgs()); - ABI_CHECK(callContractFunction("get(uint8)", byte( 0x00)), encodeArgs(0x1234)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(0x8765)); - ABI_CHECK(callContractFunction("set(uint8,uint256)", byte(0x00), 0x3), encodeArgs()); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(0x3)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(0)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(0)); + ABI_CHECK(callContractFunction("set(uint8,uint256)", uint8_t(0x00), 0x1234), encodeArgs()); + ABI_CHECK(callContractFunction("set(uint8,uint256)", uint8_t(0x01), 0x8765), encodeArgs()); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t( 0x00)), encodeArgs(0x1234)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(0x8765)); + ABI_CHECK(callContractFunction("set(uint8,uint256)", uint8_t(0x00), 0x3), encodeArgs()); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(0x3)); } BOOST_AUTO_TEST_CASE(compound_assign) @@ -1321,21 +1321,21 @@ BOOST_AUTO_TEST_CASE(simple_mapping) )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("get(uint8)", byte(0)), encodeArgs(byte(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(byte(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0xa7)), encodeArgs(byte(0x00))); - callContractFunction("set(uint8,uint8)", byte(0x01), byte(0xa1)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(byte(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(byte(0xa1))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0xa7)), encodeArgs(byte(0x00))); - callContractFunction("set(uint8,uint8)", byte(0x00), byte(0xef)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(byte(0xef))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(byte(0xa1))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0xa7)), encodeArgs(byte(0x00))); - callContractFunction("set(uint8,uint8)", byte(0x01), byte(0x05)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(byte(0xef))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(byte(0x05))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0xa7)), encodeArgs(byte(0x00))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0)), encodeArgs(uint8_t(0x00))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0x00))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); + callContractFunction("set(uint8,uint8)", uint8_t(0x01), uint8_t(0xa1)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0x00))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0xa1))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); + callContractFunction("set(uint8,uint8)", uint8_t(0x00), uint8_t(0xef)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0xef))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0xa1))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); + callContractFunction("set(uint8,uint8)", uint8_t(0x01), uint8_t(0x05)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0xef))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0x05))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); } BOOST_AUTO_TEST_CASE(mapping_state) @@ -1496,7 +1496,7 @@ BOOST_AUTO_TEST_CASE(mapping_local_assignment) )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(byte(42), byte(0), byte(0), byte(21))); + ABI_CHECK(callContractFunction("f()"), encodeArgs(uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(21))); } BOOST_AUTO_TEST_CASE(mapping_local_tuple_assignment) @@ -1519,7 +1519,7 @@ BOOST_AUTO_TEST_CASE(mapping_local_tuple_assignment) )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(byte(42), byte(0), byte(0), byte(21))); + ABI_CHECK(callContractFunction("f()"), encodeArgs(uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(21))); } BOOST_AUTO_TEST_CASE(mapping_local_compound_assignment) @@ -1540,7 +1540,7 @@ BOOST_AUTO_TEST_CASE(mapping_local_compound_assignment) )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(byte(42), byte(0), byte(0), byte(21))); + ABI_CHECK(callContractFunction("f()"), encodeArgs(uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(21))); } BOOST_AUTO_TEST_CASE(mapping_internal_argument) @@ -1565,10 +1565,10 @@ BOOST_AUTO_TEST_CASE(mapping_internal_argument) )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set(uint8,uint8,uint8)", byte(1), byte(21), byte(42)), encodeArgs(byte(0), byte(0))); - ABI_CHECK(callContractFunction("get(uint8)", byte(1)), encodeArgs(byte(21), byte(42))); - ABI_CHECK(callContractFunction("set(uint8,uint8,uint8)", byte(1), byte(10), byte(11)), encodeArgs(byte(21), byte(42))); - ABI_CHECK(callContractFunction("get(uint8)", byte(1)), encodeArgs(byte(10), byte(11))); + ABI_CHECK(callContractFunction("set(uint8,uint8,uint8)", uint8_t(1), uint8_t(21), uint8_t(42)), encodeArgs(uint8_t(0), uint8_t(0))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(1)), encodeArgs(uint8_t(21), uint8_t(42))); + ABI_CHECK(callContractFunction("set(uint8,uint8,uint8)", uint8_t(1), uint8_t(10), uint8_t(11)), encodeArgs(uint8_t(21), uint8_t(42))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(1)), encodeArgs(uint8_t(10), uint8_t(11))); } BOOST_AUTO_TEST_CASE(mapping_array_internal_argument) @@ -1595,10 +1595,10 @@ BOOST_AUTO_TEST_CASE(mapping_array_internal_argument) )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set(uint8,uint8,uint8,uint8,uint8)", byte(1), byte(21), byte(22), byte(42), byte(43)), encodeArgs(byte(0), byte(0), byte(0), byte(0))); - ABI_CHECK(callContractFunction("get(uint8)", byte(1)), encodeArgs(byte(21), byte(22), byte(42), byte(43))); - ABI_CHECK(callContractFunction("set(uint8,uint8,uint8,uint8,uint8)", byte(1), byte(10), byte(30), byte(11), byte(31)), encodeArgs(byte(21), byte(22), byte(42), byte(43))); - ABI_CHECK(callContractFunction("get(uint8)", byte(1)), encodeArgs(byte(10), byte(30), byte(11), byte(31))); + ABI_CHECK(callContractFunction("set(uint8,uint8,uint8,uint8,uint8)", uint8_t(1), uint8_t(21), uint8_t(22), uint8_t(42), uint8_t(43)), encodeArgs(uint8_t(0), uint8_t(0), uint8_t(0), uint8_t(0))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(1)), encodeArgs(uint8_t(21), uint8_t(22), uint8_t(42), uint8_t(43))); + ABI_CHECK(callContractFunction("set(uint8,uint8,uint8,uint8,uint8)", uint8_t(1), uint8_t(10), uint8_t(30), uint8_t(11), uint8_t(31)), encodeArgs(uint8_t(21), uint8_t(22), uint8_t(42), uint8_t(43))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(1)), encodeArgs(uint8_t(10), uint8_t(30), uint8_t(11), uint8_t(31))); } BOOST_AUTO_TEST_CASE(mapping_internal_return) @@ -1626,8 +1626,8 @@ BOOST_AUTO_TEST_CASE(mapping_internal_return) )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g()"), encodeArgs(byte(0), byte(42), byte(0), byte(0), byte(84), byte (21))); - ABI_CHECK(callContractFunction("h()"), encodeArgs(byte(0), byte(42), byte(0), byte(0), byte(84), byte (17))); + ABI_CHECK(callContractFunction("g()"), encodeArgs(uint8_t(0), uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(84), uint8_t (21))); + ABI_CHECK(callContractFunction("h()"), encodeArgs(uint8_t(0), uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(84), uint8_t (17))); } BOOST_AUTO_TEST_CASE(structs) @@ -1787,6 +1787,24 @@ BOOST_AUTO_TEST_CASE(deleteLocals) ABI_CHECK(callContractFunction("delLocal()"), encodeArgs(6, 7)); } +BOOST_AUTO_TEST_CASE(deleteLength) +{ + char const* sourceCode = R"( + contract test { + uint[] x; + function f() public returns (uint){ + x.length = 1; + x[0] = 1; + delete x.length; + return x.length; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + BOOST_AUTO_TEST_CASE(constructor) { char const* sourceCode = R"( @@ -1801,7 +1819,7 @@ BOOST_AUTO_TEST_CASE(constructor) } )"; compileAndRun(sourceCode); - map<u256, byte> data; + map<u256, uint8_t> data; data[7] = 8; auto get = [&](u256 const& _x) -> u256 { @@ -2606,7 +2624,7 @@ BOOST_AUTO_TEST_CASE(ecrecover) )"; compileAndRun(sourceCode); u256 h("0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c"); - byte v = 28; + uint8_t v = 28; u256 r("0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f"); u256 s("0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"); u160 addr("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"); @@ -14010,6 +14028,21 @@ BOOST_AUTO_TEST_CASE(test_underscore_in_hex) ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(0x1234abcd1234))); } +BOOST_AUTO_TEST_CASE(flipping_sign_tests) +{ + char const* sourceCode = R"( + contract test { + function f() public returns (bool){ + int x = -2**255; + assert(-x == x); + return true; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 26b7914f..309cbf0b 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -180,7 +180,7 @@ BOOST_AUTO_TEST_CASE(literal_true) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x1}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x1}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(literal_false) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x0}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x0}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(int_literal) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90, + bytes expectation({uint8_t(Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -222,7 +222,7 @@ BOOST_AUTO_TEST_CASE(int_with_wei_ether_subdenomination) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x1}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x1}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -237,7 +237,7 @@ BOOST_AUTO_TEST_CASE(int_with_szabo_ether_subdenomination) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00}); + bytes expectation({uint8_t(Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -253,7 +253,7 @@ BOOST_AUTO_TEST_CASE(int_with_finney_ether_subdenomination) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00}); + bytes expectation({uint8_t(Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -268,7 +268,7 @@ BOOST_AUTO_TEST_CASE(int_with_ether_ether_subdenomination) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00}); + bytes expectation({uint8_t(Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -281,12 +281,12 @@ BOOST_AUTO_TEST_CASE(comparison) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x1, byte(Instruction::ISZERO), byte(Instruction::ISZERO), - byte(Instruction::PUSH2), 0x11, 0xaa, - byte(Instruction::PUSH2), 0x10, 0xaa, - byte(Instruction::LT), byte(Instruction::ISZERO), byte(Instruction::ISZERO), - byte(Instruction::EQ), - byte(Instruction::ISZERO)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), + uint8_t(Instruction::PUSH2), 0x11, 0xaa, + uint8_t(Instruction::PUSH2), 0x10, 0xaa, + uint8_t(Instruction::LT), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), + uint8_t(Instruction::EQ), + uint8_t(Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -299,23 +299,23 @@ BOOST_AUTO_TEST_CASE(short_circuiting) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x12, // 8 + 10 - byte(Instruction::PUSH1), 0x4, - byte(Instruction::GT), - byte(Instruction::ISZERO), // after this we have 4 <= 8 + 10 - byte(Instruction::DUP1), - byte(Instruction::PUSH1), 0x11, - byte(Instruction::JUMPI), // short-circuit if it is true - byte(Instruction::POP), - byte(Instruction::PUSH1), 0x2, - byte(Instruction::PUSH1), 0x9, - byte(Instruction::EQ), - byte(Instruction::ISZERO), // after this we have 9 != 2 - byte(Instruction::JUMPDEST), - byte(Instruction::ISZERO), byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x1, byte(Instruction::ISZERO), byte(Instruction::ISZERO), - byte(Instruction::EQ), - byte(Instruction::ISZERO)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x12, // 8 + 10 + uint8_t(Instruction::PUSH1), 0x4, + uint8_t(Instruction::GT), + uint8_t(Instruction::ISZERO), // after this we have 4 <= 8 + 10 + uint8_t(Instruction::DUP1), + uint8_t(Instruction::PUSH1), 0x11, + uint8_t(Instruction::JUMPI), // short-circuit if it is true + uint8_t(Instruction::POP), + uint8_t(Instruction::PUSH1), 0x2, + uint8_t(Instruction::PUSH1), 0x9, + uint8_t(Instruction::EQ), + uint8_t(Instruction::ISZERO), // after this we have 9 != 2 + uint8_t(Instruction::JUMPDEST), + uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), + uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), + uint8_t(Instruction::EQ), + uint8_t(Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -327,37 +327,37 @@ BOOST_AUTO_TEST_CASE(arithmetic) } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); - bytes expectation({byte(Instruction::PUSH1), 0x1, - byte(Instruction::PUSH1), 0x2, - byte(Instruction::PUSH1), 0x3, - byte(Instruction::PUSH1), 0x4, - byte(Instruction::PUSH1), 0x5, - byte(Instruction::PUSH1), 0x6, - byte(Instruction::PUSH1), 0x7, - byte(Instruction::PUSH1), 0x8, - byte(Instruction::DUP9), - byte(Instruction::XOR), - byte(Instruction::AND), - byte(Instruction::OR), - byte(Instruction::SUB), - byte(Instruction::ADD), - byte(Instruction::DUP2), - byte(Instruction::ISZERO), - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x1d, - byte(Instruction::JUMPI), - byte(Instruction::INVALID), - byte(Instruction::JUMPDEST), - byte(Instruction::MOD), - byte(Instruction::DUP2), - byte(Instruction::ISZERO), - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x26, - byte(Instruction::JUMPI), - byte(Instruction::INVALID), - byte(Instruction::JUMPDEST), - byte(Instruction::DIV), - byte(Instruction::MUL)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::PUSH1), 0x2, + uint8_t(Instruction::PUSH1), 0x3, + uint8_t(Instruction::PUSH1), 0x4, + uint8_t(Instruction::PUSH1), 0x5, + uint8_t(Instruction::PUSH1), 0x6, + uint8_t(Instruction::PUSH1), 0x7, + uint8_t(Instruction::PUSH1), 0x8, + uint8_t(Instruction::DUP9), + uint8_t(Instruction::XOR), + uint8_t(Instruction::AND), + uint8_t(Instruction::OR), + uint8_t(Instruction::SUB), + uint8_t(Instruction::ADD), + uint8_t(Instruction::DUP2), + uint8_t(Instruction::ISZERO), + uint8_t(Instruction::ISZERO), + uint8_t(Instruction::PUSH1), 0x1d, + uint8_t(Instruction::JUMPI), + uint8_t(Instruction::INVALID), + uint8_t(Instruction::JUMPDEST), + uint8_t(Instruction::MOD), + uint8_t(Instruction::DUP2), + uint8_t(Instruction::ISZERO), + uint8_t(Instruction::ISZERO), + uint8_t(Instruction::PUSH1), 0x26, + uint8_t(Instruction::JUMPI), + uint8_t(Instruction::INVALID), + uint8_t(Instruction::JUMPDEST), + uint8_t(Instruction::DIV), + uint8_t(Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -370,13 +370,13 @@ BOOST_AUTO_TEST_CASE(unary_operators) )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); - bytes expectation({byte(Instruction::PUSH1), 0x2, - byte(Instruction::DUP2), - byte(Instruction::PUSH1), 0x0, - byte(Instruction::SUB), - byte(Instruction::NOT), - byte(Instruction::EQ), - byte(Instruction::ISZERO)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x2, + uint8_t(Instruction::DUP2), + uint8_t(Instruction::PUSH1), 0x0, + uint8_t(Instruction::SUB), + uint8_t(Instruction::NOT), + uint8_t(Instruction::EQ), + uint8_t(Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -390,47 +390,47 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); // Stack: a, x - bytes expectation({byte(Instruction::DUP2), - byte(Instruction::DUP1), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::ADD), + bytes expectation({uint8_t(Instruction::DUP2), + uint8_t(Instruction::DUP1), + uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::ADD), // Stack here: a x a (a+1) - byte(Instruction::SWAP3), - byte(Instruction::POP), // first ++ + uint8_t(Instruction::SWAP3), + uint8_t(Instruction::POP), // first ++ // Stack here: (a+1) x a - byte(Instruction::DUP3), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::ADD), + uint8_t(Instruction::DUP3), + uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::ADD), // Stack here: (a+1) x a (a+2) - byte(Instruction::SWAP3), - byte(Instruction::POP), + uint8_t(Instruction::SWAP3), + uint8_t(Instruction::POP), // Stack here: (a+2) x a - byte(Instruction::DUP3), // second ++ - byte(Instruction::XOR), + uint8_t(Instruction::DUP3), // second ++ + uint8_t(Instruction::XOR), // Stack here: (a+2) x a^(a+2) - byte(Instruction::DUP3), - byte(Instruction::DUP1), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::SWAP1), - byte(Instruction::SUB), + uint8_t(Instruction::DUP3), + uint8_t(Instruction::DUP1), + uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::SWAP1), + uint8_t(Instruction::SUB), // Stack here: (a+2) x a^(a+2) (a+2) (a+1) - byte(Instruction::SWAP4), - byte(Instruction::POP), // first -- - byte(Instruction::XOR), + uint8_t(Instruction::SWAP4), + uint8_t(Instruction::POP), // first -- + uint8_t(Instruction::XOR), // Stack here: (a+1) x a^(a+2)^(a+2) - byte(Instruction::DUP3), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::SWAP1), - byte(Instruction::SUB), + uint8_t(Instruction::DUP3), + uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::SWAP1), + uint8_t(Instruction::SUB), // Stack here: (a+1) x a^(a+2)^(a+2) a - byte(Instruction::SWAP3), - byte(Instruction::POP), // second ++ + uint8_t(Instruction::SWAP3), + uint8_t(Instruction::POP), // second ++ // Stack here: a x a^(a+2)^(a+2) - byte(Instruction::DUP3), // will change - byte(Instruction::XOR), - byte(Instruction::SWAP1), - byte(Instruction::POP), - byte(Instruction::DUP1)}); + uint8_t(Instruction::DUP3), // will change + uint8_t(Instruction::XOR), + uint8_t(Instruction::SWAP1), + uint8_t(Instruction::POP), + uint8_t(Instruction::DUP1)}); // Stack here: a x a^(a+2)^(a+2)^a BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -445,16 +445,16 @@ BOOST_AUTO_TEST_CASE(assignment) bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - bytes expectation({byte(Instruction::PUSH1), 0x2, - byte(Instruction::DUP2), - byte(Instruction::DUP4), - byte(Instruction::ADD), + bytes expectation({uint8_t(Instruction::PUSH1), 0x2, + uint8_t(Instruction::DUP2), + uint8_t(Instruction::DUP4), + uint8_t(Instruction::ADD), // Stack here: a b 2 a+b - byte(Instruction::SWAP3), - byte(Instruction::POP), - byte(Instruction::DUP3), + uint8_t(Instruction::SWAP3), + uint8_t(Instruction::POP), + uint8_t(Instruction::DUP3), // Stack here: a+b b 2 a+b - byte(Instruction::MUL)}); + uint8_t(Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -467,7 +467,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_8bits) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80)); + bytes expectation(bytes({uint8_t(Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80)); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -480,7 +480,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_16bits) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43}); + bytes expectation(bytes({uint8_t(Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -495,7 +495,7 @@ BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(Instruction::PUSH1), 0xbf})); + bytes expectation(bytes({uint8_t(Instruction::PUSH1), 0xbf})); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -514,8 +514,8 @@ BOOST_AUTO_TEST_CASE(blockhash) bytes code = compileFirstExpression(sourceCode, {}, {}, {make_shared<MagicVariableDeclaration>("blockhash", blockhashFun)}); - bytes expectation({byte(Instruction::PUSH1), 0x03, - byte(Instruction::BLOCKHASH)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x03, + uint8_t(Instruction::BLOCKHASH)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -533,7 +533,7 @@ BOOST_AUTO_TEST_CASE(gas_left) {make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft))} ); - bytes expectation = bytes({byte(Instruction::GAS)}); + bytes expectation = bytes({uint8_t(Instruction::GAS)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index d6310921..6f9d4ce8 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -157,9 +157,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers) BOOST_CHECK_EQUAL(RationalNumberType(rational(7, 1)).identifier(), "t_rational_7_by_1"); BOOST_CHECK_EQUAL(RationalNumberType(rational(200, 77)).identifier(), "t_rational_200_by_77"); BOOST_CHECK_EQUAL(RationalNumberType(rational(2 * 200, 2 * 77)).identifier(), "t_rational_200_by_77"); - BOOST_CHECK_EQUAL(RationalNumberType(rational(-2 * 200, -2 * 77)).identifier(), "t_rational_200_by_77"); BOOST_CHECK_EQUAL(RationalNumberType(rational(-2 * 200, 2 * 77)).identifier(), "t_rational_minus_200_by_77"); - BOOST_CHECK_EQUAL(RationalNumberType(rational(2 * 200, -2 * 77)).identifier(), "t_rational_minus_200_by_77"); BOOST_CHECK_EQUAL( StringLiteralType(Literal(SourceLocation{}, Token::StringLiteral, make_shared<string>("abc - def"))).identifier(), "t_stringliteral_196a9142ee0d40e274a6482393c762b16dd8315713207365e1e13d8d85b74fc4" diff --git a/test/libsolidity/smtCheckerTests/special/blockhash.sol b/test/libsolidity/smtCheckerTests/special/blockhash.sol new file mode 100644 index 00000000..1c693914 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/blockhash.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public payable { + assert(blockhash(2) > 0); + } +} +// ---- +// Warning: (79-103): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/difficulty.sol b/test/libsolidity/smtCheckerTests/special/difficulty.sol new file mode 100644 index 00000000..4469d4e5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/difficulty.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint difficulty) public view { + assert(block.difficulty == difficulty); + } +} +// ---- +// Warning: (91-129): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/gasleft.sol b/test/libsolidity/smtCheckerTests/special/gasleft.sol new file mode 100644 index 00000000..857230fe --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/gasleft.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public view { + assert(gasleft() > 0); + uint g = gasleft(); + assert(g < gasleft()); + assert(g >= gasleft()); + } +} +// ---- +// Warning: (76-97): Assertion violation happens here +// Warning: (123-144): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol new file mode 100644 index 00000000..40e5d987 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/many.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public payable { + assert(msg.sender == block.coinbase); + assert(block.difficulty == block.gaslimit); + assert(block.number == block.timestamp); + assert(tx.gasprice == msg.value); + assert(tx.origin == msg.sender); + uint x = block.number; + assert(x + 2 > block.number); + assert(now > 10); + assert(gasleft() > 100); + } +} +// ---- +// Warning: (79-115): Assertion violation happens here +// Warning: (119-161): Assertion violation happens here +// Warning: (165-204): Assertion violation happens here +// Warning: (208-240): Assertion violation happens here +// Warning: (244-275): Assertion violation happens here +// Warning: (311-316): Overflow (resulting value larger than 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) happens here +// Warning: (336-352): Assertion violation happens here +// Warning: (356-379): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/msg_data.sol b/test/libsolidity/smtCheckerTests/special/msg_data.sol new file mode 100644 index 00000000..7e748f09 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_data.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public payable { + assert(msg.data.length > 0); + } +} +// ---- +// Warning: (86-101): Assertion checker does not yet support this expression. +// Warning: (86-94): Assertion checker does not yet support this special variable. +// Warning: (86-94): Assertion checker does not yet implement this type. +// Warning: (86-101): Internal error: Expression undefined for SMT solver. +// Warning: (79-106): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_1.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_1.sol new file mode 100644 index 00000000..dd2366e2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public view { + address a = msg.sender; + address b = msg.sender; + assert(a == b); + } +} diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol new file mode 100644 index 00000000..ad45d076 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public view { + require(msg.sender != address(0)); + address a = msg.sender; + address b = msg.sender; + assert(a == b); + } +} +// ---- +// Warning: (98-108): Assertion checker does not yet implement this expression. +// Warning: (98-108): Internal error: Expression undefined for SMT solver. diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol new file mode 100644 index 00000000..9a4eefd5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(address c) public view { + address a = msg.sender; + address b = msg.sender; + assert(a == b); + assert(c == msg.sender); + } +} +// ---- +// Warning: (155-178): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/msg_sig.sol b/test/libsolidity/smtCheckerTests/special/msg_sig.sol new file mode 100644 index 00000000..109470a8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_sig.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public payable { + assert(msg.sig == 0x00000000); + } +} +// ---- +// Warning: (79-108): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol new file mode 100644 index 00000000..541fff54 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C +{ + bytes32 x; + function f(bytes8 y) public view { + assert(x != y); + assert(x != g()); + } + function g() public view returns (bytes32) { + return x; + } +} +// ---- +// Warning: (96-110): Assertion violation happens here +// Warning: (114-130): Assertion violation happens here diff --git a/test/libsolidity/syntaxTests/array/length/parameter_too_large.sol b/test/libsolidity/syntaxTests/array/length/parameter_too_large.sol new file mode 100644 index 00000000..02e0a7cc --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/parameter_too_large.sol @@ -0,0 +1,5 @@ +contract C { + function f(bytes32[1263941234127518272] memory) public pure {} +} +// ---- +// TypeError: (26-61): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim.sol new file mode 100644 index 00000000..5f96ecd5 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim.sol @@ -0,0 +1,11 @@ +contract C { + function f(bytes32[1263941234127518272][500] memory) public pure {} + function f(uint[2**30][] memory) public pure {} + function f(uint[2**30][2**30][] memory) public pure {} + function f(uint[2**16][2**16][] memory) public pure {} +} +// ---- +// TypeError: (26-66): Array is too large to be encoded. +// TypeError: (96-116): Array is too large to be encoded. +// TypeError: (146-173): Array is too large to be encoded. +// TypeError: (203-230): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol new file mode 100644 index 00000000..de1fde3f --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(bytes32[1263941234127518272][500] memory) public pure {} + function f(uint[2**30][2**30][][] memory) public pure {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (61-101): Array is too large to be encoded. +// TypeError: (131-160): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol new file mode 100644 index 00000000..75f7a953 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-144): Type function () external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol new file mode 100644 index 00000000..8d1b08aa --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-141): Type function () external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol new file mode 100644 index 00000000..535d6c77 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-141): Type function () external is not implicitly convertible to expected type function () view external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol new file mode 100644 index 00000000..299d7e30 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol @@ -0,0 +1,8 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol new file mode 100644 index 00000000..78bada51 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (113-149): Type function () payable external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol new file mode 100644 index 00000000..f12cb301 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol @@ -0,0 +1,10 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (113-149): Type function () payable external is not implicitly convertible to expected type function () view external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol new file mode 100644 index 00000000..7742e0c1 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol @@ -0,0 +1,8 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol new file mode 100644 index 00000000..cd4e9b4e --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-149): Type function () pure external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol new file mode 100644 index 00000000..578ecdbd --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol @@ -0,0 +1,8 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_same.sol b/test/libsolidity/syntaxTests/conversion/function_type_same.sol new file mode 100644 index 00000000..c5ebe1ca --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_same.sol @@ -0,0 +1,14 @@ +contract C { + int dummy; + function h_nonpayable() external { dummy = 1; } + function h_payable() payable external {} + function h_view() view external { dummy; } + function h_pure() pure external {} + function f() view external { + function () external g_nonpayable = this.h_nonpayable; g_nonpayable; + function () payable external g_payable = this.h_payable; g_payable; + function () view external g_view = this.h_view; g_view; + function () pure external g_pure = this.h_pure; g_pure; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol new file mode 100644 index 00000000..f52aece0 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol @@ -0,0 +1,10 @@ +contract C { + int dummy; + function h() view external { + dummy; + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol new file mode 100644 index 00000000..3bf4bac2 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() view external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-149): Type function () view external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol new file mode 100644 index 00000000..c567a2c8 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() view external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-146): Type function () view external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg.sol new file mode 100644 index 00000000..e05277de --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { + function f(a, b) {} + f() + f(1,) + f(,1) + } + } +} +// ---- +// ParserError: (113-114): Literal, identifier or instruction expected. +// ParserError: (113-114): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_function_name.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_function_name.sol new file mode 100644 index 00000000..17995b09 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_function_name.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + assembly { + function (a, b) {} + } + } +} +// ---- +// ParserError: (72-73): Expected identifier but got '(' +// ParserError: (79-80): Expected ';' but got '{' diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol new file mode 100644 index 00000000..715913de --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + assembly { + let x := 0100 + } + } +} +// ---- +// ParserError: (72-73): Literal, identifier or instruction expected. +// ParserError: (72-73): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol new file mode 100644 index 00000000..c8984333 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + let x := mload(0) + := 1 + } + } +} +// ---- +// ParserError: (87-88): Literal, identifier or instruction expected. +// ParserError: (87-88): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol b/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol new file mode 100644 index 00000000..b694992c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns (int) { + return (-1 / 2) ** -1; + } +} diff --git a/test/libyul/Inliner.cpp b/test/libyul/Inliner.cpp index 44c6411a..4ed52b47 100644 --- a/test/libyul/Inliner.cpp +++ b/test/libyul/Inliner.cpp @@ -49,10 +49,10 @@ string inlinableFunctions(string const& _source) InlinableExpressionFunctionFinder funFinder; funFinder(ast); - return boost::algorithm::join( - funFinder.inlinableFunctions() | boost::adaptors::map_keys, - "," - ); + vector<string> functionNames; + for (auto const& f: funFinder.inlinableFunctions()) + functionNames.emplace_back(f.first.str()); + return boost::algorithm::join(functionNames, ","); } } diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index a89711a1..d455c892 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -22,6 +22,7 @@ #include <test/Options.h> #include <libyul/optimiser/BlockFlattener.h> +#include <libyul/optimiser/VarDeclPropagator.h> #include <libyul/optimiser/Disambiguator.h> #include <libyul/optimiser/CommonSubexpressionEliminator.h> #include <libyul/optimiser/NameCollector.h> @@ -35,6 +36,9 @@ #include <libyul/optimiser/ExpressionSimplifier.h> #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionJoiner.h> +#include <libyul/optimiser/SSATransform.h> +#include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/Suite.h> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/inlineasm/AsmPrinter.h> @@ -99,6 +103,11 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con disambiguate(); BlockFlattener{}(*m_ast); } + else if (m_optimizerStep == "varDeclPropagator") + { + disambiguate(); + VarDeclPropagator{}(*m_ast); + } else if (m_optimizerStep == "commonSubexpressionEliminator") { disambiguate(); @@ -131,7 +140,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con (FunctionGrouper{})(*m_ast); NameDispenser nameDispenser(*m_ast); ExpressionSplitter{nameDispenser}(*m_ast); - FullInliner(*m_ast).run(); + FullInliner(*m_ast, nameDispenser).run(); ExpressionJoiner::run(*m_ast); } else if (m_optimizerStep == "mainFunction") @@ -171,6 +180,26 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con disambiguate(); ExpressionJoiner::run(*m_ast);\ } + else if (m_optimizerStep == "ssaTransform") + { + disambiguate(); + NameDispenser nameDispenser(*m_ast); + SSATransform::run(*m_ast, nameDispenser); + } + else if (m_optimizerStep == "redundantAssignEliminator") + { + disambiguate(); + RedundantAssignEliminator::run(*m_ast); + } + else if (m_optimizerStep == "ssaPlusCleanup") + { + disambiguate(); + NameDispenser nameDispenser(*m_ast); + SSATransform::run(*m_ast, nameDispenser); + RedundantAssignEliminator::run(*m_ast); + } + else if (m_optimizerStep == "fullSuite") + OptimiserSuite::run(*m_ast, *m_analysisInfo); else { FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl; diff --git a/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul new file mode 100644 index 00000000..2df4f9d0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul @@ -0,0 +1,22 @@ +{ + let a := 1 + switch calldataload(0) + case 0 { { { mstore(0, 1) } } a := 8 } + default { a := 3 { a := 4 } } + a := 5 +} +// ---- +// blockFlattener +// { +// let a := 1 +// switch calldataload(0) +// case 0 { +// mstore(0, 1) +// a := 8 +// } +// default { +// a := 3 +// a := 4 +// } +// a := 5 +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul new file mode 100644 index 00000000..fd8b4bc8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul @@ -0,0 +1,52 @@ +{ + let _13 := 0x20 + let _14 := allocate(_13) + pop(_14) + let _15 := 2 + let _16 := 3 + let _17 := 0x40 + let _18 := allocate(_17) + let _19 := array_index_access(_18, _16) + mstore(_19, _15) + function allocate(size) -> p + { + let _1 := 0x40 + let p_2 := mload(_1) + p := p_2 + let _20 := add(p_2, size) + mstore(_1, _20) + } + function array_index_access(array, index) -> p_1 + { + let _21 := 0x20 + let _22 := mul(index, _21) + p_1 := add(array, _22) + } +} +// ---- +// commonSubexpressionEliminator +// { +// let _13 := 0x20 +// let _14 := allocate(_13) +// pop(_14) +// let _15 := 2 +// let _16 := 3 +// let _17 := 0x40 +// let _18 := allocate(_17) +// let _19 := array_index_access(_18, _16) +// mstore(_19, _15) +// function allocate(size) -> p +// { +// let _1 := 0x40 +// let p_2 := mload(_1) +// p := p_2 +// let _20 := add(p_2, size) +// mstore(_1, _20) +// } +// function array_index_access(array, index) -> p_1 +// { +// let _21 := 0x20 +// let _22 := mul(index, _21) +// p_1 := add(array, _22) +// } +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul new file mode 100644 index 00000000..28e840cf --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul @@ -0,0 +1,52 @@ +{ + function allocate(size) -> p + { + let _1 := 0x40 + p := mload(_1) + let _2 := add(p, size) + let _3 := 0x40 + mstore(_3, _2) + } + function array_index_access(array, index) -> p_1 + { + let _4 := 0x20 + let _5 := mul(index, _4) + p_1 := add(array, _5) + } + let _6 := 0x20 + let _7 := allocate(_6) + pop(_7) + let _8 := 0x40 + let x := allocate(_8) + let _9 := 2 + let _10 := 3 + let _11 := array_index_access(x, _10) + mstore(_11, _9) +} +// ---- +// commonSubexpressionEliminator +// { +// function allocate(size) -> p +// { +// let _1 := 0x40 +// p := mload(_1) +// let _2 := add(p, size) +// let _3 := _1 +// mstore(_1, _2) +// } +// function array_index_access(array, index) -> p_1 +// { +// let _4 := 0x20 +// let _5 := mul(index, _4) +// p_1 := add(array, _5) +// } +// let _6 := 0x20 +// let _7 := allocate(_6) +// pop(_7) +// let _8 := 0x40 +// let x := allocate(_8) +// let _9 := 2 +// let _10 := 3 +// let _11 := array_index_access(x, _10) +// mstore(_11, _9) +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul new file mode 100644 index 00000000..49b4c916 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul @@ -0,0 +1,25 @@ +{ + let a := 10 + let x := 20 + { + let b := calldataload(0) + let d := calldataload(1) + x := d + } + // We had a bug where "calldataload(0)" was incorrectly replaced by "b" + mstore(0, calldataload(0)) + mstore(0, x) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := 10 +// let x := 20 +// { +// let b := calldataload(0) +// let d := calldataload(1) +// x := d +// } +// mstore(0, calldataload(0)) +// mstore(0, x) +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul index 0d2a38c5..6875abec 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul @@ -22,7 +22,7 @@ // a_1 := a_1 // } // { -// let b_1:u256 := a_1 +// let b_2:u256 := a_1 // } // } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul index f917bb68..df49b92a 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul @@ -14,9 +14,9 @@ // let a:u256, b:u256, c:u256, d:u256, f:u256 // } // { -// function f_1(a_1:u256) -> c_1:u256, d_1:u256 +// function f_1(a_2:u256) -> c_3:u256, d_4:u256 // { -// let b_1:u256, c_1_1:u256 := f_1(a_1) +// let b_5:u256, c_1:u256 := f_1(a_2) // } // } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul index 14f53757..bc3aa30f 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul @@ -16,7 +16,7 @@ // let a_1:bool // if a_1 // { -// let b_1:bool := a_1 +// let b_2:bool := a_1 // } // } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul index 340ecccf..e62e957f 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul @@ -18,10 +18,10 @@ // let a_1:u256 // switch a_1 // case 0:u256 { -// let b_1:u256 := a_1 +// let b_2:u256 := a_1 // } // default { -// let c_1:u256 := a_1 +// let c_3:u256 := a_1 // } // } // } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul index e80959f6..839692bc 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul @@ -13,12 +13,12 @@ // let c:u256 // let b:u256 // } -// function f(a:u256, c_1:u256) -> b_1:u256 +// function f(a:u256, c_1:u256) -> b_2:u256 // { // let x:u256 // } // { -// let a_1:u256 -// let x_1:u256 +// let a_3:u256 +// let x_4:u256 // } // } diff --git a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul index dd1c1f8a..ee7f5bf5 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul @@ -14,13 +14,13 @@ // f_b := sload(mload(f_a)) // f_c := 3 // let b3 := f_b -// let f_a_1 := f_c -// let f_b_1 -// let f_c_1 -// f_b_1 := sload(mload(f_a_1)) -// f_c_1 := 3 -// let b4 := f_b_1 -// let c4 := f_c_1 +// let f_a_2 := f_c +// let f_b_3 +// let f_c_4 +// f_b_3 := sload(mload(f_a_2)) +// f_c_4 := 3 +// let b4 := f_b_3 +// let c4 := f_c_4 // } // function f(a) -> b, c // { diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul index c704944d..8bc6ec58 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul @@ -23,9 +23,9 @@ // } // function g(b, c) -> y // { -// let f_a_1 := b -// let f_x_1 -// f_x_1 := add(f_a_1, f_a_1) -// y := mul(mload(c), f_x_1) +// let f_a_6 := b +// let f_x_7 +// f_x_7 := add(f_a_6, f_a_6) +// y := mul(mload(c), f_x_7) // } // } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul index bcdba8e0..d09877de 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul @@ -46,14 +46,14 @@ // } // function g(x_1) // { -// let f_x_1 := 1 -// mstore(0, f_x_1) +// let f_x_8 := 1 +// mstore(0, f_x_8) // let f_h_t // f_h_t := 2 // mstore(7, f_h_t) // let f_g_x_1 := 10 // f(1) -// mstore(1, f_x_1) +// mstore(1, f_x_8) // } // function h() -> t // { diff --git a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul index 44fc7b21..9644e6c1 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul @@ -21,18 +21,18 @@ // } // f(x) // { -// let f_a_1 := x -// let f_r_1 -// sstore(f_a_1, 0) -// f_r_1 := f_a_1 -// x := f_r_1 +// let f_a_3 := x +// let f_r_4 +// sstore(f_a_3, 0) +// f_r_4 := f_a_3 +// x := f_r_4 // } // { -// let f_a_2 := x -// let f_r_2 -// sstore(f_a_2, 0) -// f_r_2 := f_a_2 -// let t := f_r_2 +// let f_a_6 := x +// let f_r_7 +// sstore(f_a_6, 0) +// f_r_7 := f_a_6 +// let t := f_r_7 // } // } // function f(a) -> r diff --git a/test/libyul/yulOptimizerTests/fullSimplify/operations.yul b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul new file mode 100644 index 00000000..25467b62 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul @@ -0,0 +1,44 @@ +{ + let x := mload(0) + mstore(1, mul(x, 0)) + mstore(2, div(x, 0)) + mstore(3, div(0, x)) + mstore(4, sdiv(x, 0)) + mstore(5, sdiv(0, x)) + mstore(6, and(0, x)) + mstore(7, and(x, 0)) + mstore(8, mod(0, x)) + mstore(9, mod(x, 0)) + mstore(10, lt(x, x)) + mstore(11, gt(x, x)) + mstore(12, slt(x, x)) + mstore(13, sgt(x, x)) + mstore(14, mod(x, x)) + mstore(15, and(x, not(x))) + mstore(16, and(not(x), x)) + mstore(17, or(x, not(x))) + mstore(18, or(not(x), x)) +} +// ---- +// fullSimplify +// { +// pop(mload(0)) +// mstore(1, 0) +// mstore(2, 0) +// mstore(3, 0) +// mstore(4, 0) +// mstore(5, 0) +// mstore(6, 0) +// mstore(7, 0) +// mstore(8, 0) +// mstore(9, 0) +// mstore(10, 0) +// mstore(11, 0) +// mstore(12, 0) +// mstore(13, 0) +// mstore(14, 0) +// mstore(15, 0) +// mstore(16, 0) +// mstore(17, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) +// mstore(18, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul new file mode 100644 index 00000000..714eb860 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul @@ -0,0 +1,12 @@ +{ + let x := 7 + mstore(0, signextend(50, x)) + let y := 255 + mstore(1, signextend(0, y)) +} +// ---- +// fullSimplify +// { +// mstore(0, 7) +// mstore(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul new file mode 100644 index 00000000..deb02068 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -0,0 +1,24 @@ +{ + function allocate(size) -> p { + p := mload(0x40) + mstore(0x40, add(p, size)) + } + function array_index_access(array, index) -> p { + p := add(array, mul(index, 0x20)) + } + pop(allocate(0x20)) + let x := allocate(0x40) + mstore(array_index_access(x, 3), 2) +} +// ---- +// fullSuite +// { +// { +// let _18 := 0x20 +// let allocate__7 := 0x40 +// mstore(allocate__7, add(mload(allocate__7), _18)) +// let allocate_p_12_31 := mload(allocate__7) +// mstore(allocate__7, add(allocate_p_12_31, allocate__7)) +// mstore(add(allocate_p_12_31, 96), 2) +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul new file mode 100644 index 00000000..d9bbd86d --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul @@ -0,0 +1,26 @@ +{ + for { + let a := 2 + // Should not be removed, even though you might think + // it goes out of scope + a := 3 + } a { a := add(a, 1) } + { + a := 7 + } +} +// ---- +// redundantAssignEliminator +// { +// for { +// let a := 2 +// a := 3 +// } +// a +// { +// a := add(a, 1) +// } +// { +// a := 7 +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul new file mode 100644 index 00000000..7f5e97ce --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul @@ -0,0 +1,31 @@ +{ + let x + let y + // Cannot be removed, because we might skip the loop + x := 1 + for { } calldataload(0) { } + { + // Cannot be removed + x := 2 + // Can be removed + y := 3 + } + y := 8 + mstore(x, 0) +} +// ---- +// redundantAssignEliminator +// { +// let x +// let y +// x := 1 +// for { +// } +// calldataload(0) +// { +// } +// { +// x := 2 +// } +// mstore(x, 0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul new file mode 100644 index 00000000..65eb2838 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul @@ -0,0 +1,27 @@ +{ + let x + // Cannot be removed, because we might run the loop only once + x := 1 + for { } calldataload(0) { } + { + mstore(x, 2) + // Cannot be removed because of the line above + x := 2 + } + x := 3 +} +// ---- +// redundantAssignEliminator +// { +// let x +// x := 1 +// for { +// } +// calldataload(0) +// { +// } +// { +// mstore(x, 2) +// x := 2 +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul new file mode 100644 index 00000000..5bb920ec --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul @@ -0,0 +1,23 @@ +{ + let r + r := 1 + function f(x, y) -> a, b { + // Can be removed, is param + x := 1 + y := 2 + // Cannot be removed, is return param + a := 3 + b := 4 + } + r := 2 +} +// ---- +// redundantAssignEliminator +// { +// let r +// function f(x, y) -> a, b +// { +// a := 3 +// b := 4 +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul new file mode 100644 index 00000000..958bfc66 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul @@ -0,0 +1,24 @@ +{ + let c + let d + c := calldataload(0) + d := 1 + if c { + d := 2 + } + // This enforces that none of the assignments above can be removed. + mstore(0, d) +} +// ---- +// redundantAssignEliminator +// { +// let c +// let d +// c := calldataload(0) +// d := 1 +// if c +// { +// d := 2 +// } +// mstore(0, d) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul new file mode 100644 index 00000000..e47c31d1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul @@ -0,0 +1,24 @@ +{ + let c + let d + c := calldataload(0) + // This assignment will be overwritten in all branches and thus can be removed. + d := 1 + if c { + d := 2 + } + d := 3 + mstore(0, d) +} +// ---- +// redundantAssignEliminator +// { +// let c +// let d +// c := calldataload(0) +// if c +// { +// } +// d := 3 +// mstore(0, d) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul new file mode 100644 index 00000000..00065ed2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul @@ -0,0 +1,25 @@ +{ + let c + let d + c := calldataload(0) + d := 1 + if c { + // Uses the assignment above + d := d + } + d := 3 + mstore(0, d) +} +// ---- +// redundantAssignEliminator +// { +// let c +// let d +// c := calldataload(0) +// d := 1 +// if c +// { +// } +// d := 3 +// mstore(0, d) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul new file mode 100644 index 00000000..26bcfc72 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul @@ -0,0 +1,19 @@ +{ + function f() -> a, b {} + let x, y + x := 1 + x := 2 + // Will not be used, but is a multi-assign, so not removed. + x, y := f() + x := 3 + y := 4 +} +// ---- +// redundantAssignEliminator +// { +// function f() -> a, b +// { +// } +// let x, y +// x, y := f() +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul new file mode 100644 index 00000000..cf646126 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul @@ -0,0 +1,15 @@ +{ + let a := 2 + a := 7 + let b := 8 + b := a + a := b +} +// ---- +// redundantAssignEliminator +// { +// let a := 2 +// a := 7 +// let b := 8 +// b := a +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul new file mode 100644 index 00000000..ae3e5226 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul @@ -0,0 +1,11 @@ +{ + let a + a := 0 + a := mload(0) +} +// ---- +// redundantAssignEliminator +// { +// let a +// a := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul new file mode 100644 index 00000000..702f854d --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul @@ -0,0 +1,16 @@ +{ + let a + { + let b + b := 2 + a := 2 + } +} +// ---- +// redundantAssignEliminator +// { +// let a +// { +// let b +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul new file mode 100644 index 00000000..913a7694 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul @@ -0,0 +1,10 @@ +{ + let a + a := 1 + a := 2 +} +// ---- +// redundantAssignEliminator +// { +// let a +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul new file mode 100644 index 00000000..96265576 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul @@ -0,0 +1,22 @@ +{ + let x + // Will be overwritten in all branches + x := 1 + switch calldataload(0) + case 0 { x := 2 } + default { x := 3 } + mstore(x, 0) +} +// ---- +// redundantAssignEliminator +// { +// let x +// switch calldataload(0) +// case 0 { +// x := 2 +// } +// default { +// x := 3 +// } +// mstore(x, 0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul new file mode 100644 index 00000000..cbe859ed --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul @@ -0,0 +1,19 @@ +{ + let x + // Will NOT be overwritten in all branches + x := 1 + switch calldataload(0) + case 0 { x := 2 } + mstore(x, 0) +} +// ---- +// redundantAssignEliminator +// { +// let x +// x := 1 +// switch calldataload(0) +// case 0 { +// x := 2 +// } +// mstore(x, 0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul new file mode 100644 index 00000000..1a3b26eb --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul @@ -0,0 +1,23 @@ +{ + let x + // Will be used in some and overwritten in others + x := 1 + switch calldataload(0) + case 0 { x := 2 } + default { mstore(x, 1) } + mstore(x, 0) +} +// ---- +// redundantAssignEliminator +// { +// let x +// x := 1 +// switch calldataload(0) +// case 0 { +// x := 2 +// } +// default { +// mstore(x, 1) +// } +// mstore(x, 0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul new file mode 100644 index 00000000..cc78b74d --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul @@ -0,0 +1,16 @@ +{ + let x + // Not referenced anywhere. + x := 1 + switch calldataload(0) + case 0 { mstore(0, 1) } +} +// ---- +// redundantAssignEliminator +// { +// let x +// switch calldataload(0) +// case 0 { +// mstore(0, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul new file mode 100644 index 00000000..d2408343 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul @@ -0,0 +1,35 @@ +{ + function copy(from, to) -> length { + length := mload(from) + mstore(to, length) + from := add(from, 0x20) + to := add(to, 0x20) + for { let x := 1 } lt(x, length) { x := add(x, 0x20) } { + mstore(add(to, x), mload(add(from, x))) + } + } +} +// ---- +// ssaPlusCleanup +// { +// function copy(from, to) -> length +// { +// let length_1 := mload(from) +// length := length_1 +// mstore(to, length_1) +// let from_2 := add(from, 0x20) +// let to_3 := add(to, 0x20) +// for { +// let x_4 := 1 +// let x := x_4 +// } +// lt(x, length_1) +// { +// let x_5 := add(x, 0x20) +// x := x_5 +// } +// { +// mstore(add(to_3, x), mload(add(from_2, x))) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul new file mode 100644 index 00000000..ddb33aa0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul @@ -0,0 +1,17 @@ +{ + let a := 1 + a := 2 + a := 3 + a := 4 + mstore(0, a) +} +// ---- +// ssaPlusCleanup +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := 2 +// let a_3 := 3 +// let a_4 := 4 +// mstore(0, a_4) +// } diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul new file mode 100644 index 00000000..67a6c5d3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul @@ -0,0 +1,17 @@ +{ + let a := 1 + a := add(a, 2) + a := add(a, 3) + a := mload(add(a, 4)) + mstore(0, a) +} +// ---- +// ssaPlusCleanup +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := add(a_1, 2) +// let a_3 := add(a_2, 3) +// let a_4 := mload(add(a_3, 4)) +// mstore(0, a_4) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/branches.yul b/test/libyul/yulOptimizerTests/ssaTransform/branches.yul new file mode 100644 index 00000000..c089fe70 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/branches.yul @@ -0,0 +1,25 @@ +{ + let a := 1 + a := add(a, 1) + if a { + a := add(a, 1) + } + a := add(a, 1) + mstore(a, 1) +} +// ---- +// ssaTransform +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := add(a_1, 1) +// a := a_2 +// if a_2 +// { +// let a_3 := add(a_2, 1) +// a := a_3 +// } +// let a_4 := add(a, 1) +// a := a_4 +// mstore(a_4, 1) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul new file mode 100644 index 00000000..41640346 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul @@ -0,0 +1,26 @@ +{ + let a := mload(0) + for { mstore(0, a) } a { mstore(0, a) } + { + a := add(a, 3) + } + mstore(0, a) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// for { +// mstore(0, a_1) +// } +// a +// { +// mstore(0, a) +// } +// { +// let a_2 := add(a, 3) +// a := a_2 +// } +// mstore(0, a) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul new file mode 100644 index 00000000..821a5b2a --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul @@ -0,0 +1,26 @@ +{ + let a := mload(0) + for { a := add(a, 3) } a { mstore(0, a) } + { + mstore(0, a) + } + mstore(0, a) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// for { +// let a_2 := add(a_1, 3) +// a := a_2 +// } +// a +// { +// mstore(0, a) +// } +// { +// mstore(0, a) +// } +// mstore(0, a) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul new file mode 100644 index 00000000..1fc075bc --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul @@ -0,0 +1,26 @@ +{ + let a := mload(0) + for { mstore(0, a) } a { a := add(a, 3) } + { + mstore(0, a) + } + mstore(0, a) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// for { +// mstore(0, a_1) +// } +// a +// { +// let a_2 := add(a, 3) +// a := a_2 +// } +// { +// mstore(0, a) +// } +// mstore(0, a) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul new file mode 100644 index 00000000..273d3811 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul @@ -0,0 +1,47 @@ +{ + let a := mload(0) + a := add(a, 1) + if a { + a := add(a, 2) + } + { + a := add(a, 4) + } + for { a := add(a, 3) } a { a := add(a, 6) } + { + a := add(a, 12) + } + a := add(a, 8) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// let a_2 := add(a_1, 1) +// a := a_2 +// if a_2 +// { +// let a_3 := add(a_2, 2) +// a := a_3 +// } +// { +// let a_4 := add(a, 4) +// a := a_4 +// } +// for { +// let a_5 := add(a, 3) +// a := a_5 +// } +// a +// { +// let a_7 := add(a, 6) +// a := a_7 +// } +// { +// let a_6 := add(a, 12) +// a := a_6 +// } +// let a_8 := add(a, 8) +// a := a_8 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/function.yul b/test/libyul/yulOptimizerTests/ssaTransform/function.yul new file mode 100644 index 00000000..995d16cc --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/function.yul @@ -0,0 +1,23 @@ +{ + function f(a, b) -> c, d { + b := add(b, a) + c := add(c, b) + d := add(d, c) + a := add(a, d) + } +} +// ---- +// ssaTransform +// { +// function f(a, b) -> c, d +// { +// let b_1 := add(b, a) +// b := b_1 +// let c_2 := add(c, b_1) +// c := c_2 +// let d_3 := add(d, c_2) +// d := d_3 +// let a_4 := add(a, d_3) +// a := a_4 +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/nested.yul b/test/libyul/yulOptimizerTests/ssaTransform/nested.yul new file mode 100644 index 00000000..49a76953 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/nested.yul @@ -0,0 +1,32 @@ +{ + let a := 1 + a := 2 + let b := 3 + b := 4 + { + // b is not reassigned here + a := 3 + a := 4 + } + a := add(b, a) +} +// ---- +// ssaTransform +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := 2 +// a := a_2 +// let b_3 := 3 +// let b := b_3 +// let b_4 := 4 +// b := b_4 +// { +// let a_5 := 3 +// a := a_5 +// let a_6 := 4 +// a := a_6 +// } +// let a_7 := add(b_4, a) +// a := a_7 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul b/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul new file mode 100644 index 00000000..297905c6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul @@ -0,0 +1,19 @@ +{ + let a := 1 + // this should not be transformed + let b := add(a, 2) + let c + mstore(c, 0) + c := add(a, b) +} +// ---- +// ssaTransform +// { +// let a := 1 +// let b := add(a, 2) +// let c_1 +// let c := c_1 +// mstore(c_1, 0) +// let c_2 := add(a, b) +// c := c_2 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/simple.yul b/test/libyul/yulOptimizerTests/ssaTransform/simple.yul new file mode 100644 index 00000000..6dbce729 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/simple.yul @@ -0,0 +1,18 @@ +{ + let a := 1 + a := 2 + a := 3 + a := 4 +} +// ---- +// ssaTransform +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := 2 +// a := a_2 +// let a_3 := 3 +// a := a_3 +// let a_4 := 4 +// a := a_4 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/switch.yul b/test/libyul/yulOptimizerTests/ssaTransform/switch.yul new file mode 100644 index 00000000..bc9b55bb --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/switch.yul @@ -0,0 +1,26 @@ +{ + let a := mload(0) + // This could be more efficient: + // all cases could use the value of the variable from just before + // the switch and not just the first + switch a + case 0 { a := add(a, 4) } + default { a := add(a, 8) } + mstore(0, a) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// switch a_1 +// case 0 { +// let a_2 := add(a_1, 4) +// a := a_2 +// } +// default { +// let a_3 := add(a, 8) +// a := a_3 +// } +// mstore(0, a) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/used.yul b/test/libyul/yulOptimizerTests/ssaTransform/used.yul new file mode 100644 index 00000000..ad686ca1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/used.yul @@ -0,0 +1,39 @@ +{ + let a := 1 + mstore(a, 0) + a := 2 + mstore(a, 0) + { + mstore(a, 0) + a := 3 + mstore(a, 0) + a := 4 + mstore(a, 0) + } + mstore(a, 0) + a := 4 + mstore(a, 0) +} +// ---- +// ssaTransform +// { +// let a_1 := 1 +// let a := a_1 +// mstore(a_1, 0) +// let a_2 := 2 +// a := a_2 +// mstore(a_2, 0) +// { +// mstore(a_2, 0) +// let a_3 := 3 +// a := a_3 +// mstore(a_3, 0) +// let a_4 := 4 +// a := a_4 +// mstore(a_4, 0) +// } +// mstore(a, 0) +// let a_5 := 4 +// a := a_5 +// mstore(a_5, 0) +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/pop.yul b/test/libyul/yulOptimizerTests/unusedPruner/pop.yul new file mode 100644 index 00000000..542070f9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/pop.yul @@ -0,0 +1,8 @@ +{ + let a := 1 + pop(a) +} +// ---- +// unusedPruner +// { +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul new file mode 100644 index 00000000..54fea2fb --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul @@ -0,0 +1,17 @@ +{ + let a := 4 + let x + if a { + x := 2 + } +} +// ---- +// varDeclPropagator +// { +// let a := 4 +// let x +// if a +// { +// x := 2 +// } +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul new file mode 100644 index 00000000..4ac07031 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul @@ -0,0 +1,13 @@ +{ + function f() -> a, b, c {} + let x, y, z + z, x, y := f() +} +// ---- +// varDeclPropagator +// { +// function f() -> a, b, c +// { +// } +// let z, x, y := f() +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul new file mode 100644 index 00000000..ca921500 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul @@ -0,0 +1,11 @@ +{ + let a + a := 4 + a := 5 +} +// ---- +// varDeclPropagator +// { +// let a := 4 +// a := 5 +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul new file mode 100644 index 00000000..3affcac6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul @@ -0,0 +1,10 @@ +{ + let a, b + a := mload(0) +} +// ---- +// varDeclPropagator +// { +// let b +// let a := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul new file mode 100644 index 00000000..117e0cc9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul @@ -0,0 +1,9 @@ +{ + let f + f := mload(0) +} +// ---- +// varDeclPropagator +// { +// let f := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul new file mode 100644 index 00000000..e8c91e10 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul @@ -0,0 +1,11 @@ +{ + let a, b + a := mload(0) + b := mload(1) +} +// ---- +// varDeclPropagator +// { +// let a := mload(0) +// let b := mload(1) +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul new file mode 100644 index 00000000..5312112a --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul @@ -0,0 +1,12 @@ +{ + let b + let a := b + b := 1 +} +// ---- +// varDeclPropagator +// { +// let b +// let a := b +// b := 1 +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul new file mode 100644 index 00000000..e27785dd --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul @@ -0,0 +1,16 @@ +{ + function f(x) {} + let a + f(a) + a := 4 +} +// ---- +// varDeclPropagator +// { +// function f(x) +// { +// } +// let a +// f(a) +// a := 4 +// } diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 65054fca..19a1d958 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -1,6 +1,9 @@ add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) +add_executable(yulopti yulopti.cpp) +target_link_libraries(yulopti PRIVATE solidity ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) + 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 ../libyul/YulOptimizerTest.cpp) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp new file mode 100644 index 00000000..348c5f4a --- /dev/null +++ b/test/tools/yulopti.cpp @@ -0,0 +1,233 @@ +/* + 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/>. +*/ +/** + * Interactive yul optimizer + */ + +#include <libdevcore/CommonIO.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/parsing/Parser.h> +#include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <libyul/optimiser/BlockFlattener.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 <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/SSATransform.h> +#include <libyul/optimiser/VarDeclPropagator.h> + +#include <libdevcore/JSON.h> + +#include <boost/program_options.hpp> + +#include <string> +#include <sstream> +#include <iostream> + +using namespace std; +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::assembly; +using namespace dev::yul; + +namespace po = boost::program_options; + +class YulOpti +{ +public: + void printErrors(Scanner const& _scanner) + { + SourceReferenceFormatter formatter(cout, [&](string const&) -> Scanner const& { return _scanner; }); + + for (auto const& error: m_errors) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); + } + + bool parse(string const& _input) + { + ErrorReporter errorReporter(m_errors); + shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(_input), ""); + m_ast = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false); + if (!m_ast || !errorReporter.errors().empty()) + { + cout << "Error parsing source." << endl; + printErrors(*scanner); + return false; + } + m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + AsmAnalyzer analyzer( + *m_analysisInfo, + errorReporter, + EVMVersion::byzantium(), + boost::none, + AsmFlavour::Strict + ); + if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) + { + cout << "Error analyzing source." << endl; + printErrors(*scanner); + return false; + } + return true; + } + + void runInteractive(string source) + { + bool disambiguated = false; + while (true) + { + cout << "----------------------" << endl; + cout << source << endl; + if (!parse(source)) + return; + if (!disambiguated) + { + *m_ast = boost::get<assembly::Block>(Disambiguator(*m_analysisInfo)(*m_ast)); + m_analysisInfo.reset(); + m_nameDispenser = make_shared<NameDispenser>(*m_ast); + disambiguated = true; + } + cout << "(q)quit/(f)flatten/(c)se/propagate var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; + cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; + cout << " (r)edundant assign elim./re(m)aterializer? "; + cout.flush(); + int option = readStandardInputChar(); + cout << ' ' << char(option) << endl; + switch (option) + { + case 'q': + return; + case 'f': + BlockFlattener{}(*m_ast); + break; + case 'c': + (CommonSubexpressionEliminator{})(*m_ast); + break; + case 'd': + (VarDeclPropagator{})(*m_ast); + break; + case 'x': + ExpressionSplitter{*m_nameDispenser}(*m_ast); + break; + case 'j': + ExpressionJoiner::run(*m_ast); + break; + case 'g': + (FunctionGrouper{})(*m_ast); + break; + case 'h': + (FunctionHoister{})(*m_ast); + break; + case 'e': + ExpressionInliner{*m_ast}.run(); + break; + case 'i': + FullInliner(*m_ast, *m_nameDispenser).run(); + break; + case 's': + ExpressionSimplifier::run(*m_ast); + break; + case 'u': + UnusedPruner::runUntilStabilised(*m_ast); + break; + case 'a': + SSATransform::run(*m_ast, *m_nameDispenser); + break; + case 'r': + RedundantAssignEliminator::run(*m_ast); + break; + case 'm': + Rematerialiser{}(*m_ast); + break; + default: + cout << "Unknown option." << endl; + } + source = AsmPrinter{}(*m_ast); + } + } + +private: + ErrorList m_errors; + shared_ptr<assembly::Block> m_ast; + shared_ptr<AsmAnalysisInfo> m_analysisInfo; + shared_ptr<NameDispenser> m_nameDispenser; +}; + +int main(int argc, char** argv) +{ + po::options_description options( + R"(yulopti, yul optimizer exploration tool. +Usage: yulopti [Options] <file> +Reads <file> as yul code and applies optimizer steps to it, +interactively read from stdin. + +Allowed options)", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23); + options.add_options() + ( + "input-file", + po::value<string>(), + "input file" + ) + ("help", "Show this help screen."); + + // 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).positional(filesPositions); + po::store(cmdLineParser.run(), arguments); + } + catch (po::error const& _exception) + { + cerr << _exception.what() << endl; + return 1; + } + + string input; + if (arguments.count("input-file")) + YulOpti{}.runInteractive(readFileAsString(arguments["input-file"].as<string>())); + else + cout << options; + + return 0; +} |