mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/develop' into develop_060
This commit is contained in:
		
						commit
						b0db64ff5b
					
				| @ -41,9 +41,11 @@ Compiler Features: | |||||||
| 
 | 
 | ||||||
| Language Features: | Language Features: | ||||||
|  * Allow to obtain the selector of public or external library functions via a member ``.selector``. |  * Allow to obtain the selector of public or external library functions via a member ``.selector``. | ||||||
|  |  * Parser: Allow splitting string and hexadecimal string literals into multiple parts. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Compiler Features: | Compiler Features: | ||||||
|  |  * Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  | |||||||
| @ -4,6 +4,25 @@ | |||||||
| Safe Remote Purchase | Safe Remote Purchase | ||||||
| ******************** | ******************** | ||||||
| 
 | 
 | ||||||
|  | Purchasing goods remotely currently requires multiple parties that need to trust each other. | ||||||
|  | The simplest configuration involves a seller and a buyer. The buyer would like to receive | ||||||
|  | an item from the seller and the seller would like to get money (or an equivalent) | ||||||
|  | in return. The problematic part is the shipment here: There is no way to determine for | ||||||
|  | sure that the item arrived at the buyer. | ||||||
|  | 
 | ||||||
|  | There are multiple ways to solve this problem, but all fall short in one or the other way. | ||||||
|  | In the following example, both parties have to put twice the value of the item into the | ||||||
|  | contract as escrow. As soon as this happened, the money will stay locked inside | ||||||
|  | the contract until the buyer confirms that they received the item. After that, | ||||||
|  | the buyer is returned the value (half of their deposit) and the seller gets three | ||||||
|  | times the value (their deposit plus the value). The idea behind | ||||||
|  | this is that both parties have an incentive to resolve the situation or otherwise | ||||||
|  | their money is locked forever. | ||||||
|  | 
 | ||||||
|  | This contract of course does not solve the problem, but gives an overview of how | ||||||
|  | you can use state machine-like constructs inside a contract. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|     pragma solidity >=0.4.22 <0.7.0; |     pragma solidity >=0.4.22 <0.7.0; | ||||||
| @ -12,7 +31,7 @@ Safe Remote Purchase | |||||||
|         uint public value; |         uint public value; | ||||||
|         address payable public seller; |         address payable public seller; | ||||||
|         address payable public buyer; |         address payable public buyer; | ||||||
|         enum State { Created, Locked, Inactive } |         enum State { Created, Locked, Release, Inactive } | ||||||
|         // The state variable has a default value of the first member, `State.created` |         // The state variable has a default value of the first member, `State.created` | ||||||
|         State public state; |         State public state; | ||||||
| 
 | 
 | ||||||
| @ -57,6 +76,7 @@ Safe Remote Purchase | |||||||
|         event Aborted(); |         event Aborted(); | ||||||
|         event PurchaseConfirmed(); |         event PurchaseConfirmed(); | ||||||
|         event ItemReceived(); |         event ItemReceived(); | ||||||
|  |         event SellerRefunded(); | ||||||
| 
 | 
 | ||||||
|         /// Abort the purchase and reclaim the ether. |         /// Abort the purchase and reclaim the ether. | ||||||
|         /// Can only be called by the seller before |         /// Can only be called by the seller before | ||||||
| @ -68,6 +88,10 @@ Safe Remote Purchase | |||||||
|         { |         { | ||||||
|             emit Aborted(); |             emit Aborted(); | ||||||
|             state = State.Inactive; |             state = State.Inactive; | ||||||
|  |             // We use transfer here directly. It is | ||||||
|  |             // reentrancy-safe, because it is the | ||||||
|  |             // last call in this function and we | ||||||
|  |             // already changed the state. | ||||||
|             seller.transfer(address(this).balance); |             seller.transfer(address(this).balance); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -97,12 +121,24 @@ Safe Remote Purchase | |||||||
|             // It is important to change the state first because |             // It is important to change the state first because | ||||||
|             // otherwise, the contracts called using `send` below |             // otherwise, the contracts called using `send` below | ||||||
|             // can call in again here. |             // can call in again here. | ||||||
|             state = State.Inactive; |             state = State.Release; | ||||||
| 
 |  | ||||||
|             // NOTE: This actually allows both the buyer and the seller to |  | ||||||
|             // block the refund - the withdraw pattern should be used. |  | ||||||
| 
 | 
 | ||||||
|             buyer.transfer(value); |             buyer.transfer(value); | ||||||
|             seller.transfer(address(this).balance); |         } | ||||||
|  | 
 | ||||||
|  |         /// This function refunds the seller, i.e. | ||||||
|  |         /// pays back the locked funds of the seller. | ||||||
|  |         function refundSeller() | ||||||
|  |             public | ||||||
|  |             onlySeller | ||||||
|  |             inState(State.Release) | ||||||
|  |         { | ||||||
|  |             emit SellerRefunded(); | ||||||
|  |             // It is important to change the state first because | ||||||
|  |             // otherwise, the contracts called using `send` below | ||||||
|  |             // can call in again here. | ||||||
|  |             state = State.Inactive; | ||||||
|  | 
 | ||||||
|  |             seller.transfer(3 * value); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -464,7 +464,7 @@ a non-rational number). | |||||||
| String Literals and Types | String Literals and Types | ||||||
| ------------------------- | ------------------------- | ||||||
| 
 | 
 | ||||||
| String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``).  They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four.  As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. | String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``), and they can also be split into multiple consecutive parts (``"foo" "bar"`` is equivalent to ``"foobar"``) which can be helpful when dealing with long strings.  They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four.  As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. | ||||||
| 
 | 
 | ||||||
| For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. | For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. | ||||||
| 
 | 
 | ||||||
| @ -503,7 +503,14 @@ terminate the string literal. Newline only terminates the string literal if it i | |||||||
| Hexadecimal Literals | Hexadecimal Literals | ||||||
| -------------------- | -------------------- | ||||||
| 
 | 
 | ||||||
| Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``, ``hex'0011_22_FF'``). Their content must be hexadecimal digits which can optionally use a single underscore as separator between byte boundaries. The value of the literal will be the binary representation of the hexadecimal sequence. | Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double | ||||||
|  | or single-quotes (``hex"001122FF"``, ``hex'0011_22_FF'``). Their content must be | ||||||
|  | hexadecimal digits which can optionally use a single underscore as separator between | ||||||
|  | byte boundaries. The value of the literal will be the binary representation | ||||||
|  | of the hexadecimal sequence. | ||||||
|  | 
 | ||||||
|  | Multiple hexadecimal literals separated by whitespace are concatenated into a single literal: | ||||||
|  | ``hex"00112233" hex"44556677"`` is equivalent to ``hex"0011223344556677"`` | ||||||
| 
 | 
 | ||||||
| Hexadecimal literals behave like :ref:`string literals <string_literals>` and have the same convertibility restrictions. | Hexadecimal literals behave like :ref:`string literals <string_literals>` and have the same convertibility restrictions. | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -262,7 +262,15 @@ Contract Related | |||||||
|     the current contract, explicitly convertible to :ref:`address` |     the current contract, explicitly convertible to :ref:`address` | ||||||
| 
 | 
 | ||||||
| ``selfdestruct(address payable recipient)``: | ``selfdestruct(address payable recipient)``: | ||||||
|     destroy the current contract, sending its funds to the given :ref:`address` |     Destroy the current contract, sending its funds to the given :ref:`address` | ||||||
|  |     and end execution. | ||||||
|  |     Note that ``selfdestruct`` has some peculiarities inherited from the EVM: | ||||||
|  | 
 | ||||||
|  |     - the receiving contract's receive function is not executed. | ||||||
|  |     - the contract is only really destroyed at the end of the transaction and ``revert`` s might "undo" the destruction. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| Furthermore, all functions of the current contract are callable directly including the current function. | Furthermore, all functions of the current contract are callable directly including the current function. | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -23,32 +23,59 @@ | |||||||
| #include <libdevcore/Exceptions.h> | #include <libdevcore/Exceptions.h> | ||||||
| #include <libdevcore/Assertions.h> | #include <libdevcore/Assertions.h> | ||||||
| #include <libdevcore/Keccak256.h> | #include <libdevcore/Keccak256.h> | ||||||
|  | #include <libdevcore/FixedHash.h> | ||||||
| 
 | 
 | ||||||
| #include <boost/algorithm/string.hpp> | #include <boost/algorithm/string.hpp> | ||||||
| 
 | 
 | ||||||
| using namespace std; | using namespace std; | ||||||
| using namespace dev; | using namespace dev; | ||||||
| 
 | 
 | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static char const* upperHexChars = "0123456789ABCDEF"; | ||||||
|  | static char const* lowerHexChars = "0123456789abcdef"; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string dev::toHex(uint8_t _data, HexCase _case) | ||||||
|  | { | ||||||
|  | 	assertThrow(_case != HexCase::Mixed, BadHexCase, "Mixed case can only be used for byte arrays."); | ||||||
|  | 
 | ||||||
|  | 	char const* chars = _case == HexCase::Upper ? upperHexChars : lowerHexChars; | ||||||
|  | 
 | ||||||
|  | 	return std::string{ | ||||||
|  | 		chars[(unsigned(_data) / 16) & 0xf], | ||||||
|  | 		chars[unsigned(_data) & 0xf] | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| string dev::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case) | string dev::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case) | ||||||
| { | { | ||||||
| 	std::ostringstream ret; | 	std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0); | ||||||
| 	if (_prefix == HexPrefix::Add) |  | ||||||
| 		ret << "0x"; |  | ||||||
| 
 | 
 | ||||||
|  | 	size_t i = 0; | ||||||
|  | 	if (_prefix == HexPrefix::Add) | ||||||
|  | 	{ | ||||||
|  | 		ret[i++] = '0'; | ||||||
|  | 		ret[i++] = 'x'; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Mixed case will be handled inside the loop.
 | ||||||
|  | 	char const* chars = _case == HexCase::Upper ? upperHexChars : lowerHexChars; | ||||||
| 	int rix = _data.size() - 1; | 	int rix = _data.size() - 1; | ||||||
| 	for (uint8_t c: _data) | 	for (uint8_t c: _data) | ||||||
| 	{ | 	{ | ||||||
| 		// switch hex case every four hexchars
 | 		// switch hex case every four hexchars
 | ||||||
| 		auto hexcase = std::nouppercase; | 		if (_case == HexCase::Mixed) | ||||||
| 		if (_case == HexCase::Upper) | 			chars = (rix-- & 2) == 0 ? lowerHexChars : upperHexChars; | ||||||
| 			hexcase = std::uppercase; |  | ||||||
| 		else if (_case == HexCase::Mixed) |  | ||||||
| 			hexcase = (rix-- & 2) == 0 ? std::nouppercase : std::uppercase; |  | ||||||
| 
 | 
 | ||||||
| 		ret << std::hex << hexcase << std::setfill('0') << std::setw(2) << size_t(c); | 		ret[i++] = chars[(unsigned(c) / 16) & 0xf]; | ||||||
|  | 		ret[i++] = chars[unsigned(c) & 0xf]; | ||||||
| 	} | 	} | ||||||
|  | 	assertThrow(i == ret.size(), Exception, ""); | ||||||
| 
 | 
 | ||||||
| 	return ret.str(); | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int dev::fromHex(char _i, WhenError _throw) | int dev::fromHex(char _i, WhenError _throw) | ||||||
| @ -60,7 +87,7 @@ int dev::fromHex(char _i, WhenError _throw) | |||||||
| 	if (_i >= 'A' && _i <= 'F') | 	if (_i >= 'A' && _i <= 'F') | ||||||
| 		return _i - 'A' + 10; | 		return _i - 'A' + 10; | ||||||
| 	if (_throw == WhenError::Throw) | 	if (_throw == WhenError::Throw) | ||||||
| 		BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); | 		assertThrow(false, BadHexCharacter, to_string(_i)); | ||||||
| 	else | 	else | ||||||
| 		return -1; | 		return -1; | ||||||
| } | } | ||||||
| @ -73,22 +100,18 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) | |||||||
| 
 | 
 | ||||||
| 	if (_s.size() % 2) | 	if (_s.size() % 2) | ||||||
| 	{ | 	{ | ||||||
| 		int h = fromHex(_s[s++], WhenError::DontThrow); | 		int h = fromHex(_s[s++], _throw); | ||||||
| 		if (h != -1) | 		if (h != -1) | ||||||
| 			ret.push_back(h); | 			ret.push_back(h); | ||||||
| 		else if (_throw == WhenError::Throw) |  | ||||||
| 			BOOST_THROW_EXCEPTION(BadHexCharacter()); |  | ||||||
| 		else | 		else | ||||||
| 			return bytes(); | 			return bytes(); | ||||||
| 	} | 	} | ||||||
| 	for (unsigned i = s; i < _s.size(); i += 2) | 	for (unsigned i = s; i < _s.size(); i += 2) | ||||||
| 	{ | 	{ | ||||||
| 		int h = fromHex(_s[i], WhenError::DontThrow); | 		int h = fromHex(_s[i], _throw); | ||||||
| 		int l = fromHex(_s[i + 1], WhenError::DontThrow); | 		int l = fromHex(_s[i + 1], _throw); | ||||||
| 		if (h != -1 && l != -1) | 		if (h != -1 && l != -1) | ||||||
| 			ret.push_back((uint8_t)(h * 16 + l)); | 			ret.push_back((uint8_t)(h * 16 + l)); | ||||||
| 		else if (_throw == WhenError::Throw) |  | ||||||
| 			BOOST_THROW_EXCEPTION(BadHexCharacter()); |  | ||||||
| 		else | 		else | ||||||
| 			return bytes(); | 			return bytes(); | ||||||
| 	} | 	} | ||||||
| @ -155,3 +178,15 @@ bool dev::isValidDecimal(string const& _string) | |||||||
| 		return false; | 		return false; | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // Returns a quoted string if all characters are printable ASCII chars,
 | ||||||
|  | // or its hex representation otherwise.
 | ||||||
|  | std::string dev::formatAsStringOrNumber(std::string const& _value) | ||||||
|  | { | ||||||
|  | 	if (_value.length() <= 32) | ||||||
|  | 		for (auto const& c: _value) | ||||||
|  | 			if (c <= 0x1f || c >= 0x7f || c == '"') | ||||||
|  | 				return "0x" + h256(_value, h256::AlignLeft).hex(); | ||||||
|  | 
 | ||||||
|  | 	return "\"" + _value + "\""; | ||||||
|  | } | ||||||
|  | |||||||
| @ -169,9 +169,12 @@ enum class HexCase | |||||||
| 	Mixed = 2, | 	Mixed = 2, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Convert a series of bytes to the corresponding string of hex duplets.
 | /// Convert a single byte to a string of hex characters (of length two),
 | ||||||
| /// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
 | /// optionally with uppercase hex letters.
 | ||||||
| /// @example toHex("A\x69") == "4169"
 | std::string toHex(uint8_t _data, HexCase _case = HexCase::Lower); | ||||||
|  | 
 | ||||||
|  | /// Convert a series of bytes to the corresponding string of hex duplets,
 | ||||||
|  | /// optionally with "0x" prefix and with uppercase hex letters.
 | ||||||
| std::string toHex(bytes const& _data, HexPrefix _prefix = HexPrefix::DontAdd, HexCase _case = HexCase::Lower); | std::string toHex(bytes const& _data, HexPrefix _prefix = HexPrefix::DontAdd, HexCase _case = HexCase::Lower); | ||||||
| 
 | 
 | ||||||
| /// Converts a (printable) ASCII hex character into the corresponding integer value.
 | /// Converts a (printable) ASCII hex character into the corresponding integer value.
 | ||||||
| @ -246,10 +249,6 @@ inline bytes toCompactBigEndian(T _val, unsigned _min = 0) | |||||||
| 	toBigEndian(_val, ret); | 	toBigEndian(_val, ret); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0) |  | ||||||
| { |  | ||||||
| 	return (_min || _val) ? bytes{ _val } : bytes{}; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| /// Convenience function for conversion of a u256 to hex
 | /// Convenience function for conversion of a u256 to hex
 | ||||||
| inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) | inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) | ||||||
| @ -258,13 +257,18 @@ inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) | |||||||
| 	return (prefix == HexPrefix::Add) ? "0x" + str : str; | 	return (prefix == HexPrefix::Add) ? "0x" + str : str; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | inline std::string toCompactHexWithPrefix(u256 const& _value) | ||||||
|  | { | ||||||
|  | 	return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Returns decimal representation for small numbers and hex for large numbers.
 | /// Returns decimal representation for small numbers and hex for large numbers.
 | ||||||
| inline std::string formatNumber(bigint const& _value) | inline std::string formatNumber(bigint const& _value) | ||||||
| { | { | ||||||
| 	if (_value < 0) | 	if (_value < 0) | ||||||
| 		return "-" + formatNumber(-_value); | 		return "-" + formatNumber(-_value); | ||||||
| 	if (_value > 0x1000000) | 	if (_value > 0x1000000) | ||||||
| 		return toHex(toCompactBigEndian(_value), HexPrefix::Add); | 		return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add); | ||||||
| 	else | 	else | ||||||
| 		return _value.str(); | 		return _value.str(); | ||||||
| } | } | ||||||
| @ -272,17 +276,11 @@ inline std::string formatNumber(bigint const& _value) | |||||||
| inline std::string formatNumber(u256 const& _value) | inline std::string formatNumber(u256 const& _value) | ||||||
| { | { | ||||||
| 	if (_value > 0x1000000) | 	if (_value > 0x1000000) | ||||||
| 		return toHex(toCompactBigEndian(_value), HexPrefix::Add); | 		return toCompactHexWithPrefix(_value); | ||||||
| 	else | 	else | ||||||
| 		return _value.str(); | 		return _value.str(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline std::string toCompactHexWithPrefix(u256 val) |  | ||||||
| { |  | ||||||
| 	std::ostringstream ret; |  | ||||||
| 	ret << std::hex << val; |  | ||||||
| 	return "0x" + ret.str(); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // Algorithms for string and string-like collections.
 | // Algorithms for string and string-like collections.
 | ||||||
| 
 | 
 | ||||||
| @ -337,7 +335,6 @@ void iterateReplacing(std::vector<T>& _vector, F const& _f) | |||||||
| 		_vector = std::move(modifiedVector); | 		_vector = std::move(modifiedVector); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| namespace detail | namespace detail | ||||||
| { | { | ||||||
| template <typename T, typename F, std::size_t... I> | template <typename T, typename F, std::size_t... I> | ||||||
| @ -402,6 +399,10 @@ std::string getChecksummedAddress(std::string const& _addr); | |||||||
| bool isValidHex(std::string const& _string); | bool isValidHex(std::string const& _string); | ||||||
| bool isValidDecimal(std::string const& _string); | bool isValidDecimal(std::string const& _string); | ||||||
| 
 | 
 | ||||||
|  | /// @returns a quoted string if all characters are printable ASCII chars,
 | ||||||
|  | /// or its hex representation otherwise.
 | ||||||
|  | std::string formatAsStringOrNumber(std::string const& _value); | ||||||
|  | 
 | ||||||
| template<typename Container, typename Compare> | template<typename Container, typename Compare> | ||||||
| bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) | bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) | ||||||
| { | { | ||||||
|  | |||||||
| @ -46,11 +46,11 @@ private: | |||||||
| 
 | 
 | ||||||
| DEV_SIMPLE_EXCEPTION(InvalidAddress); | DEV_SIMPLE_EXCEPTION(InvalidAddress); | ||||||
| DEV_SIMPLE_EXCEPTION(BadHexCharacter); | DEV_SIMPLE_EXCEPTION(BadHexCharacter); | ||||||
|  | DEV_SIMPLE_EXCEPTION(BadHexCase); | ||||||
| DEV_SIMPLE_EXCEPTION(FileError); | DEV_SIMPLE_EXCEPTION(FileError); | ||||||
| DEV_SIMPLE_EXCEPTION(DataTooLong); | DEV_SIMPLE_EXCEPTION(DataTooLong); | ||||||
| 
 | 
 | ||||||
| // error information to be added to exceptions
 | // error information to be added to exceptions
 | ||||||
| using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>; |  | ||||||
| using errinfo_comment = boost::error_info<struct tag_comment, std::string>; | using errinfo_comment = boost::error_info<struct tag_comment, std::string>; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <libdevcore/IpfsHash.h> | #include <libdevcore/IpfsHash.h> | ||||||
| 
 | 
 | ||||||
|  | #include <libdevcore/Assertions.h> | ||||||
| #include <libdevcore/Exceptions.h> | #include <libdevcore/Exceptions.h> | ||||||
| #include <libdevcore/picosha2.h> | #include <libdevcore/picosha2.h> | ||||||
| #include <libdevcore/CommonData.h> | #include <libdevcore/CommonData.h> | ||||||
| @ -55,11 +56,7 @@ string base58Encode(bytes const& _data) | |||||||
| 
 | 
 | ||||||
| bytes dev::ipfsHash(string _data) | bytes dev::ipfsHash(string _data) | ||||||
| { | { | ||||||
| 	if (_data.length() >= 1024 * 256) | 	assertThrow(_data.length() < 1024 * 256, DataTooLong, "IPFS hash for large (chunked) files not yet implemented."); | ||||||
| 		BOOST_THROW_EXCEPTION( |  | ||||||
| 			DataTooLong() << |  | ||||||
| 			errinfo_comment("Ipfs hash for large (chunked) files not yet implemented.") |  | ||||||
| 		); |  | ||||||
| 
 | 
 | ||||||
| 	bytes lengthAsVarint = varintEncoding(_data.size()); | 	bytes lengthAsVarint = varintEncoding(_data.size()); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -20,109 +20,41 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <functional> |  | ||||||
| #include <boost/variant/static_visitor.hpp> |  | ||||||
| 
 |  | ||||||
| namespace dev | namespace dev | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| /// Generic visitor used as follows:
 | /**
 | ||||||
| /// std::visit(GenericVisitor<Class1, Class2>(
 |  * Generic visitor used as follows: | ||||||
| ///     [](Class1& _c) { _c.f(); },
 |  * std::visit(GenericVisitor{ | ||||||
| ///     [](Class2& _c) { _c.g(); }
 |  *     [](Class1& _c) { _c.f(); }, | ||||||
| /// ), variant);
 |  *     [](Class2& _c) { _c.g(); } | ||||||
| /// This one does not have a fallback and will fail at
 |  * }, variant); | ||||||
| /// compile-time if you do not specify all variants.
 |  * This one does not have a fallback and will fail at | ||||||
|  |  * compile-time if you do not specify all variants. | ||||||
|  |  * | ||||||
|  |  * Fallback with no return (it will not fail if you do not specify all variants): | ||||||
|  |  * std::visit(GenericVisitor{ | ||||||
|  |  *     VisitorFallback<>{}, | ||||||
|  |  *     [](Class1& _c) { _c.f(); }, | ||||||
|  |  *     [](Class2& _c) { _c.g(); } | ||||||
|  |  * }, variant); | ||||||
|  |  * | ||||||
|  |  * Fallback with return type R (the fallback returns `R{}`: | ||||||
|  |  * std::visit(GenericVisitor{ | ||||||
|  |  *     VisitorFallback<R>{}, | ||||||
|  |  *     [](Class1& _c) { _c.f(); }, | ||||||
|  |  *     [](Class2& _c) { _c.g(); } | ||||||
|  |  * }, variant); | ||||||
|  |  */ | ||||||
| 
 | 
 | ||||||
| template <class...> | template <typename...> struct VisitorFallback; | ||||||
| struct GenericVisitor{}; |  | ||||||
| 
 | 
 | ||||||
| template <class Visitable, class... Others> | template <typename R> | ||||||
| struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...> | struct VisitorFallback<R> { template<typename T> R operator()(T&&) const { return {}; } }; | ||||||
| { |  | ||||||
| 	using GenericVisitor<Others...>::operator (); |  | ||||||
| 	explicit GenericVisitor( |  | ||||||
| 		std::function<void(Visitable&)> _visitor, |  | ||||||
| 		std::function<void(Others&)>... _otherVisitors |  | ||||||
| 	): |  | ||||||
| 		GenericVisitor<Others...>(std::move(_otherVisitors)...), |  | ||||||
| 		m_visitor(std::move(_visitor)) |  | ||||||
| 	{} |  | ||||||
| 
 | 
 | ||||||
| 	void operator()(Visitable& _v) const { m_visitor(_v); } | template<> | ||||||
| 
 | struct VisitorFallback<> { template<typename T> void operator()(T&&) const {} }; | ||||||
| 	std::function<void(Visitable&)> m_visitor; |  | ||||||
| }; |  | ||||||
| template <> |  | ||||||
| struct GenericVisitor<>: public boost::static_visitor<> { |  | ||||||
| 	void operator()() const {} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /// Generic visitor with fallback:
 |  | ||||||
| /// std::visit(GenericFallbackVisitor<Class1, Class2>(
 |  | ||||||
| ///     [](Class1& _c) { _c.f(); },
 |  | ||||||
| ///     [](Class2& _c) { _c.g(); }
 |  | ||||||
| /// ), variant);
 |  | ||||||
| /// This one DOES have a fallback and will NOT fail at
 |  | ||||||
| /// compile-time if you do not specify all variants.
 |  | ||||||
| 
 |  | ||||||
| template <class...> |  | ||||||
| struct GenericFallbackVisitor{}; |  | ||||||
| 
 |  | ||||||
| template <class Visitable, class... Others> |  | ||||||
| struct GenericFallbackVisitor<Visitable, Others...>: public GenericFallbackVisitor<Others...> |  | ||||||
| { |  | ||||||
| 	explicit GenericFallbackVisitor( |  | ||||||
| 		std::function<void(Visitable&)> _visitor, |  | ||||||
| 		std::function<void(Others&)>... _otherVisitors |  | ||||||
| 	): |  | ||||||
| 		GenericFallbackVisitor<Others...>(std::move(_otherVisitors)...), |  | ||||||
| 		m_visitor(std::move(_visitor)) |  | ||||||
| 	{} |  | ||||||
| 
 |  | ||||||
| 	using GenericFallbackVisitor<Others...>::operator (); |  | ||||||
| 	void operator()(Visitable& _v) const { m_visitor(_v); } |  | ||||||
| 
 |  | ||||||
| 	std::function<void(Visitable&)> m_visitor; |  | ||||||
| }; |  | ||||||
| template <> |  | ||||||
| struct GenericFallbackVisitor<>: public boost::static_visitor<> { |  | ||||||
| 	template <class T> |  | ||||||
| 	void operator()(T&) const { } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /// Generic visitor with fallback that can return a value:
 |  | ||||||
| /// std::visit(GenericFallbackReturnsVisitor<ReturnType, Class1, Class2>(
 |  | ||||||
| ///     [](Class1& _c) { return _c.f(); },
 |  | ||||||
| ///     [](Class2& _c) { return _c.g(); }
 |  | ||||||
| /// ), variant);
 |  | ||||||
| /// This one DOES have a fallback and will NOT fail at
 |  | ||||||
| /// compile-time if you do not specify all variants.
 |  | ||||||
| /// The fallback {}-constructs the return value.
 |  | ||||||
| 
 |  | ||||||
| template <class R, class...> |  | ||||||
| struct GenericFallbackReturnsVisitor{}; |  | ||||||
| 
 |  | ||||||
| template <class R, class Visitable, class... Others> |  | ||||||
| struct GenericFallbackReturnsVisitor<R, Visitable, Others...>: public GenericFallbackReturnsVisitor<R, Others...> |  | ||||||
| { |  | ||||||
| 	explicit GenericFallbackReturnsVisitor( |  | ||||||
| 		std::function<R(Visitable&)> _visitor, |  | ||||||
| 		std::function<R(Others&)>... _otherVisitors |  | ||||||
| 	): |  | ||||||
| 		GenericFallbackReturnsVisitor<R, Others...>(std::move(_otherVisitors)...), |  | ||||||
| 		m_visitor(std::move(_visitor)) |  | ||||||
| 	{} |  | ||||||
| 
 |  | ||||||
| 	using GenericFallbackReturnsVisitor<R, Others...>::operator (); |  | ||||||
| 	R operator()(Visitable& _v) const { return m_visitor(_v); } |  | ||||||
| 
 |  | ||||||
| 	std::function<R(Visitable&)> m_visitor; |  | ||||||
| }; |  | ||||||
| template <class R> |  | ||||||
| struct GenericFallbackReturnsVisitor<R>: public boost::static_visitor<R> { |  | ||||||
| 	template <class T> |  | ||||||
| 	R operator()(T&) const { return {}; } |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
|  | template <typename... Visitors> struct GenericVisitor: Visitors... { using Visitors::operator()...; }; | ||||||
|  | template <typename... Visitors> GenericVisitor(Visitors...) -> GenericVisitor<Visitors...>; | ||||||
| } | } | ||||||
|  | |||||||
| @ -324,7 +324,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const | |||||||
| 			collection.append(createJsonValue("PUSH data", i.location().start, i.location().end, toStringInHex(i.data()))); | 			collection.append(createJsonValue("PUSH data", i.location().start, i.location().end, toStringInHex(i.data()))); | ||||||
| 			break; | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			BOOST_THROW_EXCEPTION(InvalidOpcode()); | 			assertThrow(false, InvalidOpcode, ""); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -642,7 +642,7 @@ LinkerObject const& Assembly::assemble() const | |||||||
| 			ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST); | 			ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST); | ||||||
| 			break; | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			BOOST_THROW_EXCEPTION(InvalidOpcode()); | 			assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling."); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const | |||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	BOOST_THROW_EXCEPTION(InvalidOpcode()); | 	assertThrow(false, InvalidOpcode, ""); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int AssemblyItem::arguments() const | int AssemblyItem::arguments() const | ||||||
| @ -212,7 +212,7 @@ string AssemblyItem::toAssemblyText() const | |||||||
| 		assertThrow(false, AssemblyException, "Invalid assembly item."); | 		assertThrow(false, AssemblyException, "Invalid assembly item."); | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		BOOST_THROW_EXCEPTION(InvalidOpcode()); | 		assertThrow(false, InvalidOpcode, ""); | ||||||
| 	} | 	} | ||||||
| 	if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction) | 	if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction) | ||||||
| 	{ | 	{ | ||||||
| @ -277,7 +277,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) | |||||||
| 		_out << " ???"; | 		_out << " ???"; | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		BOOST_THROW_EXCEPTION(InvalidOpcode()); | 		assertThrow(false, InvalidOpcode, ""); | ||||||
| 	} | 	} | ||||||
| 	return _out; | 	return _out; | ||||||
| } | } | ||||||
|  | |||||||
| @ -158,11 +158,10 @@ AssemblyItems CSECodeGenerator::generateCode( | |||||||
| 		for (auto id: {p.first, p.second}) | 		for (auto id: {p.first, p.second}) | ||||||
| 			if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) | 			if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) | ||||||
| 			{ | 			{ | ||||||
| 				if (seqNr < _initialSequenceNumber) | 				// Invalid sequenced operation.
 | ||||||
| 					// Invalid sequenced operation.
 | 				// @todo quick fix for now. Proper fix needs to choose representative with higher
 | ||||||
| 					// @todo quick fix for now. Proper fix needs to choose representative with higher
 | 				// sequence number during dependency analysis.
 | ||||||
| 					// sequence number during dependency analysis.
 | 				assertThrow(seqNr >= _initialSequenceNumber, StackTooDeepException, ""); | ||||||
| 					BOOST_THROW_EXCEPTION(StackTooDeepException()); |  | ||||||
| 				sequencedExpressions.insert(make_pair(seqNr, id)); | 				sequencedExpressions.insert(make_pair(seqNr, id)); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| @ -222,12 +221,9 @@ void CSECodeGenerator::addDependencies(Id _c) | |||||||
| 		return; // we already computed the dependencies for _c
 | 		return; // we already computed the dependencies for _c
 | ||||||
| 	ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); | 	ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); | ||||||
| 	assertThrow(expr.item, OptimizerException, ""); | 	assertThrow(expr.item, OptimizerException, ""); | ||||||
| 	if (expr.item->type() == UndefinedItem) | 	// If this exception happens, we need to find a different way to generate the
 | ||||||
| 		BOOST_THROW_EXCEPTION( | 	// compound expression.
 | ||||||
| 			// If this exception happens, we need to find a different way to generate the
 | 	assertThrow(expr.item->type() != UndefinedItem, ItemNotAvailableException, "Undefined item requested but not available."); | ||||||
| 			// compound expression.
 |  | ||||||
| 			ItemNotAvailableException() << errinfo_comment("Undefined item requested but not available.") |  | ||||||
| 		); |  | ||||||
| 	for (Id argument: expr.arguments) | 	for (Id argument: expr.arguments) | ||||||
| 	{ | 	{ | ||||||
| 		addDependencies(argument); | 		addDependencies(argument); | ||||||
|  | |||||||
| @ -33,5 +33,8 @@ struct OptimizerException: virtual AssemblyException {}; | |||||||
| struct StackTooDeepException: virtual OptimizerException {}; | struct StackTooDeepException: virtual OptimizerException {}; | ||||||
| struct ItemNotAvailableException: virtual OptimizerException {}; | struct ItemNotAvailableException: virtual OptimizerException {}; | ||||||
| 
 | 
 | ||||||
|  | DEV_SIMPLE_EXCEPTION(InvalidDeposit); | ||||||
|  | DEV_SIMPLE_EXCEPTION(InvalidOpcode); | ||||||
|  | 
 | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -31,9 +31,6 @@ namespace dev | |||||||
| namespace eth | namespace eth | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| DEV_SIMPLE_EXCEPTION(InvalidDeposit); |  | ||||||
| DEV_SIMPLE_EXCEPTION(InvalidOpcode); |  | ||||||
| 
 |  | ||||||
| /// Virtual machine bytecode instruction.
 | /// Virtual machine bytecode instruction.
 | ||||||
| enum class Instruction: uint8_t | enum class Instruction: uint8_t | ||||||
| { | { | ||||||
|  | |||||||
| @ -810,7 +810,7 @@ Token Scanner::scanHexString() | |||||||
| 
 | 
 | ||||||
| 	literal.complete(); | 	literal.complete(); | ||||||
| 	advance();  // consume quote
 | 	advance();  // consume quote
 | ||||||
| 	return Token::StringLiteral; | 	return Token::HexStringLiteral; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Parse for regex [:digit:]+(_[:digit:]+)*
 | // Parse for regex [:digit:]+(_[:digit:]+)*
 | ||||||
|  | |||||||
| @ -226,6 +226,7 @@ namespace langutil | |||||||
| 	K(FalseLiteral, "false", 0)                                        \ | 	K(FalseLiteral, "false", 0)                                        \ | ||||||
| 	T(Number, nullptr, 0)                                              \ | 	T(Number, nullptr, 0)                                              \ | ||||||
| 	T(StringLiteral, nullptr, 0)                                       \ | 	T(StringLiteral, nullptr, 0)                                       \ | ||||||
|  | 	T(HexStringLiteral, nullptr, 0)                                    \ | ||||||
| 	T(CommentLiteral, nullptr, 0)                                      \ | 	T(CommentLiteral, nullptr, 0)                                      \ | ||||||
| 	\ | 	\ | ||||||
| 	/* Identifiers (not keywords or future reserved words). */         \ | 	/* Identifiers (not keywords or future reserved words). */         \ | ||||||
|  | |||||||
| @ -851,6 +851,7 @@ string ASTJsonConverter::literalTokenKind(Token _token) | |||||||
| 	case dev::solidity::Token::Number: | 	case dev::solidity::Token::Number: | ||||||
| 		return "number"; | 		return "number"; | ||||||
| 	case dev::solidity::Token::StringLiteral: | 	case dev::solidity::Token::StringLiteral: | ||||||
|  | 	case dev::solidity::Token::HexStringLiteral: | ||||||
| 		return "string"; | 		return "string"; | ||||||
| 	case dev::solidity::Token::TrueLiteral: | 	case dev::solidity::Token::TrueLiteral: | ||||||
| 	case dev::solidity::Token::FalseLiteral: | 	case dev::solidity::Token::FalseLiteral: | ||||||
|  | |||||||
| @ -347,6 +347,7 @@ TypePointer TypeProvider::forLiteral(Literal const& _literal) | |||||||
| 	case Token::Number: | 	case Token::Number: | ||||||
| 		return rationalNumber(_literal); | 		return rationalNumber(_literal); | ||||||
| 	case Token::StringLiteral: | 	case Token::StringLiteral: | ||||||
|  | 	case Token::HexStringLiteral: | ||||||
| 		return stringLiteral(_literal.value()); | 		return stringLiteral(_literal.value()); | ||||||
| 	default: | 	default: | ||||||
| 		return nullptr; | 		return nullptr; | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ | |||||||
| #include <libsolidity/codegen/ABIFunctions.h> | #include <libsolidity/codegen/ABIFunctions.h> | ||||||
| 
 | 
 | ||||||
| #include <libsolidity/codegen/CompilerUtils.h> | #include <libsolidity/codegen/CompilerUtils.h> | ||||||
|  | #include <libdevcore/CommonData.h> | ||||||
| #include <libdevcore/Whiskers.h> | #include <libdevcore/Whiskers.h> | ||||||
| #include <libdevcore/StringUtils.h> | #include <libdevcore/StringUtils.h> | ||||||
| 
 | 
 | ||||||
| @ -975,7 +976,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( | |||||||
| 			for (size_t i = 0; i < words; ++i) | 			for (size_t i = 0; i < words; ++i) | ||||||
| 			{ | 			{ | ||||||
| 				wordParams[i]["offset"] = to_string(i * 32); | 				wordParams[i]["offset"] = to_string(i * 32); | ||||||
| 				wordParams[i]["wordValue"] = "0x" + h256(value.substr(32 * i, 32), h256::AlignLeft).hex(); | 				wordParams[i]["wordValue"] = formatAsStringOrNumber(value.substr(32 * i, 32)); | ||||||
| 			} | 			} | ||||||
| 			templ("word", wordParams); | 			templ("word", wordParams); | ||||||
| 			return templ.render(); | 			return templ.render(); | ||||||
| @ -990,7 +991,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( | |||||||
| 				} | 				} | ||||||
| 			)"); | 			)"); | ||||||
| 			templ("functionName", functionName); | 			templ("functionName", functionName); | ||||||
| 			templ("wordValue", "0x" + h256(value, h256::AlignLeft).hex()); | 			templ("wordValue", formatAsStringOrNumber(value)); | ||||||
| 			return templ.render(); | 			return templ.render(); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  | |||||||
| @ -23,6 +23,8 @@ | |||||||
| #include <libsolidity/codegen/MultiUseYulFunctionCollector.h> | #include <libsolidity/codegen/MultiUseYulFunctionCollector.h> | ||||||
| #include <libsolidity/ast/AST.h> | #include <libsolidity/ast/AST.h> | ||||||
| #include <libsolidity/codegen/CompilerUtils.h> | #include <libsolidity/codegen/CompilerUtils.h> | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/CommonData.h> | ||||||
| #include <libdevcore/Whiskers.h> | #include <libdevcore/Whiskers.h> | ||||||
| #include <libdevcore/StringUtils.h> | #include <libdevcore/StringUtils.h> | ||||||
| 
 | 
 | ||||||
| @ -1807,7 +1809,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const | |||||||
| 			for (size_t i = 0; i < words; ++i) | 			for (size_t i = 0; i < words; ++i) | ||||||
| 			{ | 			{ | ||||||
| 				wordParams[i]["offset"] = to_string(32 + i * 32); | 				wordParams[i]["offset"] = to_string(32 + i * 32); | ||||||
| 				wordParams[i]["wordValue"] = "0x" + h256(data.substr(32 * i, 32), h256::AlignLeft).hex(); | 				wordParams[i]["wordValue"] = formatAsStringOrNumber(data.substr(32 * i, 32)); | ||||||
| 			} | 			} | ||||||
| 			templ("word", wordParams); | 			templ("word", wordParams); | ||||||
| 			return templ.render(); | 			return templ.render(); | ||||||
|  | |||||||
| @ -1783,9 +1783,22 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() | |||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
| 	case Token::StringLiteral: | 	case Token::StringLiteral: | ||||||
|  | 	case Token::HexStringLiteral: | ||||||
|  | 	{ | ||||||
|  | 		string literal = m_scanner->currentLiteral(); | ||||||
|  | 		Token firstToken = m_scanner->currentToken(); | ||||||
|  | 		while (m_scanner->peekNextToken() == firstToken) | ||||||
|  | 		{ | ||||||
|  | 			m_scanner->next(); | ||||||
|  | 			literal += m_scanner->currentLiteral(); | ||||||
|  | 		} | ||||||
| 		nodeFactory.markEndPosition(); | 		nodeFactory.markEndPosition(); | ||||||
| 		expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); | 		m_scanner->next(); | ||||||
|  | 		if (m_scanner->currentToken() == Token::Illegal) | ||||||
|  | 			fatalParserError(to_string(m_scanner->currentError())); | ||||||
|  | 		expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal)); | ||||||
| 		break; | 		break; | ||||||
|  | 	} | ||||||
| 	case Token::Identifier: | 	case Token::Identifier: | ||||||
| 		nodeFactory.markEndPosition(); | 		nodeFactory.markEndPosition(); | ||||||
| 		expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); | 		expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); | ||||||
|  | |||||||
| @ -130,7 +130,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) | |||||||
| 	solAssert(!_identifier.name.empty(), ""); | 	solAssert(!_identifier.name.empty(), ""); | ||||||
| 	size_t numErrorsBefore = m_errorReporter.errors().size(); | 	size_t numErrorsBefore = m_errorReporter.errors().size(); | ||||||
| 	bool success = true; | 	bool success = true; | ||||||
| 	if (m_currentScope->lookup(_identifier.name, Scope::Visitor( | 	if (m_currentScope->lookup(_identifier.name, GenericVisitor{ | ||||||
| 		[&](Scope::Variable const& _var) | 		[&](Scope::Variable const& _var) | ||||||
| 		{ | 		{ | ||||||
| 			if (!m_activeVariables.count(&_var)) | 			if (!m_activeVariables.count(&_var)) | ||||||
| @ -155,7 +155,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) | |||||||
| 			); | 			); | ||||||
| 			success = false; | 			success = false; | ||||||
| 		} | 		} | ||||||
| 	))) | 	})) | ||||||
| 	{ | 	{ | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| @ -304,7 +304,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) | |||||||
| 		if (f->literalArguments) | 		if (f->literalArguments) | ||||||
| 			needsLiteralArguments = true; | 			needsLiteralArguments = true; | ||||||
| 	} | 	} | ||||||
| 	else if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor( | 	else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{ | ||||||
| 		[&](Scope::Variable const&) | 		[&](Scope::Variable const&) | ||||||
| 		{ | 		{ | ||||||
| 			m_errorReporter.typeError( | 			m_errorReporter.typeError( | ||||||
| @ -327,7 +327,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) | |||||||
| 			parameters = _fun.arguments.size(); | 			parameters = _fun.arguments.size(); | ||||||
| 			returns = _fun.returns.size(); | 			returns = _fun.returns.size(); | ||||||
| 		} | 		} | ||||||
| 	))) | 	})) | ||||||
| 	{ | 	{ | ||||||
| 		if (!warnOnInstructions(_funCall.functionName.name.str(), _funCall.functionName.location)) | 		if (!warnOnInstructions(_funCall.functionName.name.str(), _funCall.functionName.location)) | ||||||
| 			m_errorReporter.declarationError(_funCall.functionName.location, "Function not found."); | 			m_errorReporter.declarationError(_funCall.functionName.location, "Function not found."); | ||||||
|  | |||||||
| @ -48,8 +48,6 @@ struct Scope | |||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	using Identifier = std::variant<Variable, Label, Function>; | 	using Identifier = std::variant<Variable, Label, Function>; | ||||||
| 	using Visitor = dev::GenericVisitor<Variable const, Label const, Function const>; |  | ||||||
| 	using NonconstVisitor = dev::GenericVisitor<Variable, Label, Function>; |  | ||||||
| 
 | 
 | ||||||
| 	bool registerVariable(YulString _name, YulType const& _type); | 	bool registerVariable(YulString _name, YulType const& _type); | ||||||
| 	bool registerLabel(YulString _name); | 	bool registerLabel(YulString _name); | ||||||
|  | |||||||
| @ -120,7 +120,10 @@ void ObjectParser::parseData(Object& _containingObject) | |||||||
| 
 | 
 | ||||||
| 	YulString name = parseUniqueName(&_containingObject); | 	YulString name = parseUniqueName(&_containingObject); | ||||||
| 
 | 
 | ||||||
| 	expectToken(Token::StringLiteral, false); | 	if (currentToken() == Token::HexStringLiteral) | ||||||
|  | 		expectToken(Token::HexStringLiteral, false); | ||||||
|  | 	else | ||||||
|  | 		expectToken(Token::StringLiteral, false); | ||||||
| 	addNamedSubObject(_containingObject, name, make_shared<Data>(name, asBytes(currentLiteral()))); | 	addNamedSubObject(_containingObject, name, make_shared<Data>(name, asBytes(currentLiteral()))); | ||||||
| 	advance(); | 	advance(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -207,11 +207,11 @@ void CodeGenerator::assemble( | |||||||
| 	} | 	} | ||||||
| 	catch (StackTooDeepError const& _e) | 	catch (StackTooDeepError const& _e) | ||||||
| 	{ | 	{ | ||||||
| 		BOOST_THROW_EXCEPTION( | 		solAssert( | ||||||
| 			InternalCompilerError() << errinfo_comment( | 			false, | ||||||
| 				"Stack too deep when compiling inline assembly" + | 			"Stack too deep when compiling inline assembly" + | ||||||
| 				(_e.comment() ? ": " + *_e.comment() : ".") | 			(_e.comment() ? ": " + *_e.comment() : ".") | ||||||
| 			)); | 		); | ||||||
| 	} | 	} | ||||||
| 	solAssert(transform.stackErrors().empty(), "Stack errors present but not thrown."); | 	solAssert(transform.stackErrors().empty(), "Stack errors present but not thrown."); | ||||||
| } | } | ||||||
|  | |||||||
| @ -82,14 +82,14 @@ void VariableReferenceCounter::operator()(Block const& _block) | |||||||
| 
 | 
 | ||||||
| void VariableReferenceCounter::increaseRefIfFound(YulString _variableName) | void VariableReferenceCounter::increaseRefIfFound(YulString _variableName) | ||||||
| { | { | ||||||
| 	m_scope->lookup(_variableName, Scope::Visitor( | 	m_scope->lookup(_variableName, GenericVisitor{ | ||||||
| 		[=](Scope::Variable const& _var) | 		[=](Scope::Variable const& _var) | ||||||
| 		{ | 		{ | ||||||
| 			++m_context.variableReferences[&_var]; | 			++m_context.variableReferences[&_var]; | ||||||
| 		}, | 		}, | ||||||
| 		[=](Scope::Label const&) { }, | 		[=](Scope::Label const&) { }, | ||||||
| 		[=](Scope::Function const&) { } | 		[=](Scope::Function const&) { } | ||||||
| 	)); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CodeTransform::CodeTransform( | CodeTransform::CodeTransform( | ||||||
| @ -283,11 +283,11 @@ void CodeTransform::operator()(FunctionCall const& _call) | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		Scope::Function* function = nullptr; | 		Scope::Function* function = nullptr; | ||||||
| 		solAssert(m_scope->lookup(_call.functionName.name, Scope::NonconstVisitor( | 		solAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{ | ||||||
| 			[=](Scope::Variable&) { solAssert(false, "Expected function name."); }, | 			[=](Scope::Variable&) { solAssert(false, "Expected function name."); }, | ||||||
| 			[=](Scope::Label&) { solAssert(false, "Expected function name."); }, | 			[=](Scope::Label&) { solAssert(false, "Expected function name."); }, | ||||||
| 			[&](Scope::Function& _function) { function = &_function; } | 			[&](Scope::Function& _function) { function = &_function; } | ||||||
| 		)), "Function name not found."); | 		}), "Function name not found."); | ||||||
| 		solAssert(function, ""); | 		solAssert(function, ""); | ||||||
| 		solAssert(function->arguments.size() == _call.arguments.size(), ""); | 		solAssert(function->arguments.size() == _call.arguments.size(), ""); | ||||||
| 		for (auto const& arg: _call.arguments | boost::adaptors::reversed) | 		for (auto const& arg: _call.arguments | boost::adaptors::reversed) | ||||||
| @ -310,7 +310,7 @@ void CodeTransform::operator()(Identifier const& _identifier) | |||||||
| 	m_assembly.setSourceLocation(_identifier.location); | 	m_assembly.setSourceLocation(_identifier.location); | ||||||
| 	// First search internals, then externals.
 | 	// First search internals, then externals.
 | ||||||
| 	solAssert(m_scope, ""); | 	solAssert(m_scope, ""); | ||||||
| 	if (m_scope->lookup(_identifier.name, Scope::NonconstVisitor( | 	if (m_scope->lookup(_identifier.name, GenericVisitor{ | ||||||
| 		[=](Scope::Variable& _var) | 		[=](Scope::Variable& _var) | ||||||
| 		{ | 		{ | ||||||
| 			// TODO: opportunity for optimization: Do not DUP if this is the last reference
 | 			// TODO: opportunity for optimization: Do not DUP if this is the last reference
 | ||||||
| @ -330,7 +330,7 @@ void CodeTransform::operator()(Identifier const& _identifier) | |||||||
| 		{ | 		{ | ||||||
| 			solAssert(false, "Function not removed during desugaring."); | 			solAssert(false, "Function not removed during desugaring."); | ||||||
| 		} | 		} | ||||||
| 	))) | 	})) | ||||||
| 	{ | 	{ | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| @ -646,14 +646,14 @@ void CodeTransform::operator()(Block const& _block) | |||||||
| AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _identifier) | AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _identifier) | ||||||
| { | { | ||||||
| 	AbstractAssembly::LabelID label = AbstractAssembly::LabelID(-1); | 	AbstractAssembly::LabelID label = AbstractAssembly::LabelID(-1); | ||||||
| 	if (!m_scope->lookup(_identifier.name, Scope::NonconstVisitor( | 	if (!m_scope->lookup(_identifier.name, GenericVisitor{ | ||||||
| 		[=](Scope::Variable&) { solAssert(false, "Expected label"); }, | 		[=](Scope::Variable&) { solAssert(false, "Expected label"); }, | ||||||
| 		[&](Scope::Label& _label) | 		[&](Scope::Label& _label) | ||||||
| 		{ | 		{ | ||||||
| 			label = labelID(_label); | 			label = labelID(_label); | ||||||
| 		}, | 		}, | ||||||
| 		[=](Scope::Function&) { solAssert(false, "Expected label"); } | 		[=](Scope::Function&) { solAssert(false, "Expected label"); } | ||||||
| 	))) | 	})) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(false, "Identifier not found."); | 		solAssert(false, "Identifier not found."); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -190,7 +190,8 @@ void ControlFlowSimplifier::visit(Statement& _st) | |||||||
| 
 | 
 | ||||||
| void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements) | void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements) | ||||||
| { | { | ||||||
| 	GenericFallbackReturnsVisitor<OptionalStatements, If, Switch> const visitor( | 	GenericVisitor visitor{ | ||||||
|  | 		VisitorFallback<OptionalStatements>{}, | ||||||
| 		[&](If& _ifStmt) -> OptionalStatements { | 		[&](If& _ifStmt) -> OptionalStatements { | ||||||
| 			if (_ifStmt.body.statements.empty() && m_dialect.discardFunction()) | 			if (_ifStmt.body.statements.empty() && m_dialect.discardFunction()) | ||||||
| 			{ | 			{ | ||||||
| @ -215,8 +216,7 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements) | |||||||
| 
 | 
 | ||||||
| 			return {}; | 			return {}; | ||||||
| 		} | 		} | ||||||
| 	); | 	}; | ||||||
| 
 |  | ||||||
| 	iterateReplacing( | 	iterateReplacing( | ||||||
| 		_statements, | 		_statements, | ||||||
| 		[&](Statement& _stmt) -> OptionalStatements | 		[&](Statement& _stmt) -> OptionalStatements | ||||||
|  | |||||||
| @ -160,17 +160,19 @@ void InlineModifier::operator()(Block& _block) | |||||||
| std::optional<vector<Statement>> InlineModifier::tryInlineStatement(Statement& _statement) | std::optional<vector<Statement>> InlineModifier::tryInlineStatement(Statement& _statement) | ||||||
| { | { | ||||||
| 	// Only inline for expression statements, assignments and variable declarations.
 | 	// Only inline for expression statements, assignments and variable declarations.
 | ||||||
| 	Expression* e = std::visit(GenericFallbackReturnsVisitor<Expression*, ExpressionStatement, Assignment, VariableDeclaration>( | 	Expression* e = std::visit(GenericVisitor{ | ||||||
|  | 		VisitorFallback<Expression*>{}, | ||||||
| 		[](ExpressionStatement& _s) { return &_s.expression; }, | 		[](ExpressionStatement& _s) { return &_s.expression; }, | ||||||
| 		[](Assignment& _s) { return _s.value.get(); }, | 		[](Assignment& _s) { return _s.value.get(); }, | ||||||
| 		[](VariableDeclaration& _s) { return _s.value.get(); } | 		[](VariableDeclaration& _s) { return _s.value.get(); } | ||||||
| 	), _statement); | 	}, _statement); | ||||||
| 	if (e) | 	if (e) | ||||||
| 	{ | 	{ | ||||||
| 		// Only inline direct function calls.
 | 		// Only inline direct function calls.
 | ||||||
| 		FunctionCall* funCall = std::visit(GenericFallbackReturnsVisitor<FunctionCall*, FunctionCall&>( | 		FunctionCall* funCall = std::visit(GenericVisitor{ | ||||||
|  | 			VisitorFallback<FunctionCall*>{}, | ||||||
| 			[](FunctionCall& _e) { return &_e; } | 			[](FunctionCall& _e) { return &_e; } | ||||||
| 		), *e); | 		}, *e); | ||||||
| 		if (funCall && m_driver.shallInline(*funCall, m_currentFunction)) | 		if (funCall && m_driver.shallInline(*funCall, m_currentFunction)) | ||||||
| 			return performInline(_statement, *funCall); | 			return performInline(_statement, *funCall); | ||||||
| 	} | 	} | ||||||
| @ -208,7 +210,8 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC | |||||||
| 	Statement newBody = BodyCopier(m_nameDispenser, variableReplacements)(function->body); | 	Statement newBody = BodyCopier(m_nameDispenser, variableReplacements)(function->body); | ||||||
| 	newStatements += std::move(std::get<Block>(newBody).statements); | 	newStatements += std::move(std::get<Block>(newBody).statements); | ||||||
| 
 | 
 | ||||||
| 	std::visit(GenericFallbackVisitor<Assignment, VariableDeclaration>{ | 	std::visit(GenericVisitor{ | ||||||
|  | 		VisitorFallback<>{}, | ||||||
| 		[&](Assignment& _assignment) | 		[&](Assignment& _assignment) | ||||||
| 		{ | 		{ | ||||||
| 			for (size_t i = 0; i < _assignment.variableNames.size(); ++i) | 			for (size_t i = 0; i < _assignment.variableNames.size(); ++i) | ||||||
|  | |||||||
| @ -71,7 +71,8 @@ void StructuralSimplifier::operator()(Block& _block) | |||||||
| 
 | 
 | ||||||
| void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements) | void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements) | ||||||
| { | { | ||||||
| 	GenericFallbackReturnsVisitor<OptionalStatements, If, Switch, ForLoop> const visitor( | 	GenericVisitor visitor{ | ||||||
|  | 		VisitorFallback<OptionalStatements>{}, | ||||||
| 		[&](If& _ifStmt) -> OptionalStatements { | 		[&](If& _ifStmt) -> OptionalStatements { | ||||||
| 			if (expressionAlwaysTrue(*_ifStmt.condition)) | 			if (expressionAlwaysTrue(*_ifStmt.condition)) | ||||||
| 				return {std::move(_ifStmt.body.statements)}; | 				return {std::move(_ifStmt.body.statements)}; | ||||||
| @ -89,7 +90,7 @@ void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements) | |||||||
| 				return {std::move(_forLoop.pre.statements)}; | 				return {std::move(_forLoop.pre.statements)}; | ||||||
| 			return {}; | 			return {}; | ||||||
| 		} | 		} | ||||||
| 	); | 	}; | ||||||
| 
 | 
 | ||||||
| 	iterateReplacing( | 	iterateReplacing( | ||||||
| 		_statements, | 		_statements, | ||||||
|  | |||||||
| @ -30,7 +30,8 @@ void VarDeclInitializer::operator()(Block& _block) | |||||||
| 	ASTModifier::operator()(_block); | 	ASTModifier::operator()(_block); | ||||||
| 
 | 
 | ||||||
| 	using OptionalStatements = std::optional<vector<Statement>>; | 	using OptionalStatements = std::optional<vector<Statement>>; | ||||||
| 	GenericFallbackReturnsVisitor<OptionalStatements, VariableDeclaration> visitor{ | 	GenericVisitor visitor{ | ||||||
|  | 		VisitorFallback<OptionalStatements>{}, | ||||||
| 		[](VariableDeclaration& _varDecl) -> OptionalStatements | 		[](VariableDeclaration& _varDecl) -> OptionalStatements | ||||||
| 		{ | 		{ | ||||||
| 			if (_varDecl.value) | 			if (_varDecl.value) | ||||||
| @ -51,5 +52,6 @@ void VarDeclInitializer::operator()(Block& _block) | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
|  | 
 | ||||||
| 	iterateReplacing(_block.statements, [&](auto&& _statement) { return std::visit(visitor, _statement); }); | 	iterateReplacing(_block.statements, [&](auto&& _statement) { return std::visit(visitor, _statement); }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -129,6 +129,8 @@ set(libyul_sources | |||||||
|     libyul/Common.cpp |     libyul/Common.cpp | ||||||
|     libyul/Common.h |     libyul/Common.h | ||||||
|     libyul/CompilabilityChecker.cpp |     libyul/CompilabilityChecker.cpp | ||||||
|  |     libyul/EWasmTranslationTest.cpp | ||||||
|  |     libyul/EWasmTranslationTest.h | ||||||
|     libyul/FunctionSideEffects.cpp |     libyul/FunctionSideEffects.cpp | ||||||
|     libyul/FunctionSideEffects.h |     libyul/FunctionSideEffects.h | ||||||
|     libyul/Inliner.cpp |     libyul/Inliner.cpp | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ | |||||||
| #include <test/libsolidity/SyntaxTest.h> | #include <test/libsolidity/SyntaxTest.h> | ||||||
| #include <test/libsolidity/SemanticTest.h> | #include <test/libsolidity/SemanticTest.h> | ||||||
| #include <test/libsolidity/SMTCheckerJSONTest.h> | #include <test/libsolidity/SMTCheckerJSONTest.h> | ||||||
|  | #include <test/libyul/EWasmTranslationTest.h> | ||||||
| #include <test/libyul/YulOptimizerTest.h> | #include <test/libyul/YulOptimizerTest.h> | ||||||
| #include <test/libyul/YulInterpreterTest.h> | #include <test/libyul/YulInterpreterTest.h> | ||||||
| #include <test/libyul/ObjectCompilerTest.h> | #include <test/libyul/ObjectCompilerTest.h> | ||||||
| @ -54,6 +55,7 @@ struct Testsuite | |||||||
| Testsuite const g_interactiveTestsuites[] = { | Testsuite const g_interactiveTestsuites[] = { | ||||||
| /*
 | /*
 | ||||||
| 	Title                   Path           Subpath                SMT   NeedsVM Creator function */ | 	Title                   Path           Subpath                SMT   NeedsVM Creator function */ | ||||||
|  | 	{"EWasm Translation",   "libyul",      "ewasmTranslationTests",false,false, &yul::test::EWasmTranslationTest::create}, | ||||||
| 	{"Yul Optimizer",       "libyul",      "yulOptimizerTests",   false, false, &yul::test::YulOptimizerTest::create}, | 	{"Yul Optimizer",       "libyul",      "yulOptimizerTests",   false, false, &yul::test::YulOptimizerTest::create}, | ||||||
| 	{"Yul Interpreter",     "libyul",      "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, | 	{"Yul Interpreter",     "libyul",      "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, | ||||||
| 	{"Yul Object Compiler", "libyul",      "objectCompiler",      false, false, &yul::test::ObjectCompilerTest::create}, | 	{"Yul Object Compiler", "libyul",      "objectCompiler",      false, false, &yul::test::ObjectCompilerTest::create}, | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								test/cmdlineTests/yul_string_format_ascii/input.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								test/cmdlineTests/yul_string_format_ascii/input.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | { | ||||||
|  | 	"language": "Solidity", | ||||||
|  | 	"sources": | ||||||
|  | 	{ | ||||||
|  | 		"A": | ||||||
|  | 		{ | ||||||
|  | 			"content": "pragma solidity >=0.0; contract C { function f() external pure returns (string memory) { return \"abcabc\"; } }" | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	"settings": | ||||||
|  | 	{ | ||||||
|  | 		"outputSelection": | ||||||
|  | 		{ | ||||||
|  | 			"*": { "*": ["ir"] } | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										161
									
								
								test/cmdlineTests/yul_string_format_ascii/output.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								test/cmdlineTests/yul_string_format_ascii/output.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,161 @@ | |||||||
|  | {"contracts":{"A":{"C":{"ir":"/******************************************************* | ||||||
|  |  *                       WARNING                       * | ||||||
|  |  *  Solidity to Yul compilation is still EXPERIMENTAL  * | ||||||
|  |  *       It can result in LOSS OF FUNDS or worse       * | ||||||
|  |  *                !USE AT YOUR OWN RISK!               * | ||||||
|  |  *******************************************************/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | object \"C_10\" { | ||||||
|  |     code { | ||||||
|  |         mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |         // Begin state variable initialization for contract \"C\" (0 variables) | ||||||
|  |         // End state variable initialization for contract \"C\". | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) | ||||||
|  |         return(0, datasize(\"C_10_deployed\")) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         function allocateMemory(size) -> memPtr { | ||||||
|  |             memPtr := mload(64) | ||||||
|  |             let newFreePtr := add(memPtr, size) | ||||||
|  |             // protect against overflow | ||||||
|  |             if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } | ||||||
|  |             mstore(64, newFreePtr) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() -> converted { | ||||||
|  |             converted := allocateMemory(64) | ||||||
|  |             mstore(converted, 6) | ||||||
|  | 
 | ||||||
|  |             mstore(add(converted, 32), \"abcabc\") | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function fun_f_9()  -> vloc__4 { | ||||||
|  |             for { let return_flag := 1 } return_flag {} { | ||||||
|  |                 vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() | ||||||
|  |                 return_flag := 0 | ||||||
|  |                 break | ||||||
|  | 
 | ||||||
|  |                 break | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     object \"C_10_deployed\" { | ||||||
|  |         code { | ||||||
|  |             mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |             if iszero(lt(calldatasize(), 4)) | ||||||
|  |             { | ||||||
|  |                 let selector := shift_right_224_unsigned(calldataload(0)) | ||||||
|  |                 switch selector | ||||||
|  | 
 | ||||||
|  |                 case 0x26121ff0 | ||||||
|  |                 { | ||||||
|  |                     // f() | ||||||
|  |                     if callvalue() { revert(0, 0) } | ||||||
|  |                     abi_decode_tuple_(4, calldatasize()) | ||||||
|  |                     let ret_0 :=  fun_f_9() | ||||||
|  |                     let memPos := allocateMemory(0) | ||||||
|  |                     let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos ,  ret_0) | ||||||
|  |                     return(memPos, sub(memEnd, memPos)) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 default {} | ||||||
|  |             } | ||||||
|  |             revert(0, 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             function abi_decode_tuple_(headStart, dataEnd)   { | ||||||
|  |                 if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value, pos) -> end { | ||||||
|  |                 let length := array_length_t_string_memory_ptr(value) | ||||||
|  |                 pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) | ||||||
|  |                 copy_memory_to_memory(add(value, 0x20), pos, length) | ||||||
|  |                 end := add(pos, round_up_to_mul_of_32(length)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(headStart , value0) -> tail { | ||||||
|  |                 tail := add(headStart, 32) | ||||||
|  | 
 | ||||||
|  |                 mstore(add(headStart, 0), sub(tail, headStart)) | ||||||
|  |                 tail := abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value0,  tail) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function allocateMemory(size) -> memPtr { | ||||||
|  |                 memPtr := mload(64) | ||||||
|  |                 let newFreePtr := add(memPtr, size) | ||||||
|  |                 // protect against overflow | ||||||
|  |                 if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } | ||||||
|  |                 mstore(64, newFreePtr) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function array_length_t_string_memory_ptr(value) -> length { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                 length := mload(value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { | ||||||
|  |                 mstore(pos, length) | ||||||
|  |                 updated_pos := add(pos, 0x20) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() -> converted { | ||||||
|  |                 converted := allocateMemory(64) | ||||||
|  |                 mstore(converted, 6) | ||||||
|  | 
 | ||||||
|  |                 mstore(add(converted, 32), \"abcabc\") | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function copy_memory_to_memory(src, dst, length) { | ||||||
|  |                 let i := 0 | ||||||
|  |                 for { } lt(i, length) { i := add(i, 32) } | ||||||
|  |                 { | ||||||
|  |                     mstore(add(dst, i), mload(add(src, i))) | ||||||
|  |                 } | ||||||
|  |                 if gt(i, length) | ||||||
|  |                 { | ||||||
|  |                     // clear end | ||||||
|  |                     mstore(add(dst, length), 0) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function fun_f_9()  -> vloc__4 { | ||||||
|  |                 for { let return_flag := 1 } return_flag {} { | ||||||
|  |                     vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() | ||||||
|  |                     return_flag := 0 | ||||||
|  |                     break | ||||||
|  | 
 | ||||||
|  |                     break | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function round_up_to_mul_of_32(value) -> result { | ||||||
|  |                 result := and(add(value, 31), not(31)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function shift_right_224_unsigned(value) -> newValue { | ||||||
|  |                 newValue := | ||||||
|  | 
 | ||||||
|  |                 shr(224, value) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | "}}},"sources":{"A":{"id":0}}} | ||||||
							
								
								
									
										17
									
								
								test/cmdlineTests/yul_string_format_ascii_bytes32/input.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								test/cmdlineTests/yul_string_format_ascii_bytes32/input.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | { | ||||||
|  | 	"language": "Solidity", | ||||||
|  | 	"sources": | ||||||
|  | 	{ | ||||||
|  | 		"A": | ||||||
|  | 		{ | ||||||
|  | 			"content": "pragma solidity >=0.0; contract C { function f() external pure returns (bytes32) { return \"abcabc\"; } }" | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	"settings": | ||||||
|  | 	{ | ||||||
|  | 		"outputSelection": | ||||||
|  | 		{ | ||||||
|  | 			"*": { "*": ["ir"] } | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										114
									
								
								test/cmdlineTests/yul_string_format_ascii_bytes32/output.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								test/cmdlineTests/yul_string_format_ascii_bytes32/output.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | |||||||
|  | {"contracts":{"A":{"C":{"ir":"/******************************************************* | ||||||
|  |  *                       WARNING                       * | ||||||
|  |  *  Solidity to Yul compilation is still EXPERIMENTAL  * | ||||||
|  |  *       It can result in LOSS OF FUNDS or worse       * | ||||||
|  |  *                !USE AT YOUR OWN RISK!               * | ||||||
|  |  *******************************************************/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | object \"C_10\" { | ||||||
|  |     code { | ||||||
|  |         mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |         // Begin state variable initialization for contract \"C\" (0 variables) | ||||||
|  |         // End state variable initialization for contract \"C\". | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) | ||||||
|  |         return(0, datasize(\"C_10_deployed\")) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() -> converted { | ||||||
|  |             converted := 0x6162636162630000000000000000000000000000000000000000000000000000 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function fun_f_9()  -> vloc__4 { | ||||||
|  |             for { let return_flag := 1 } return_flag {} { | ||||||
|  |                 vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() | ||||||
|  |                 return_flag := 0 | ||||||
|  |                 break | ||||||
|  | 
 | ||||||
|  |                 break | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     object \"C_10_deployed\" { | ||||||
|  |         code { | ||||||
|  |             mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |             if iszero(lt(calldatasize(), 4)) | ||||||
|  |             { | ||||||
|  |                 let selector := shift_right_224_unsigned(calldataload(0)) | ||||||
|  |                 switch selector | ||||||
|  | 
 | ||||||
|  |                 case 0x26121ff0 | ||||||
|  |                 { | ||||||
|  |                     // f() | ||||||
|  |                     if callvalue() { revert(0, 0) } | ||||||
|  |                     abi_decode_tuple_(4, calldatasize()) | ||||||
|  |                     let ret_0 :=  fun_f_9() | ||||||
|  |                     let memPos := allocateMemory(0) | ||||||
|  |                     let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos ,  ret_0) | ||||||
|  |                     return(memPos, sub(memEnd, memPos)) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 default {} | ||||||
|  |             } | ||||||
|  |             revert(0, 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             function abi_decode_tuple_(headStart, dataEnd)   { | ||||||
|  |                 if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_t_bytes32_to_t_bytes32_fromStack(value, pos) { | ||||||
|  |                 mstore(pos, cleanup_t_bytes32(value)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(headStart , value0) -> tail { | ||||||
|  |                 tail := add(headStart, 32) | ||||||
|  | 
 | ||||||
|  |                 abi_encode_t_bytes32_to_t_bytes32_fromStack(value0,  add(headStart, 0)) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function allocateMemory(size) -> memPtr { | ||||||
|  |                 memPtr := mload(64) | ||||||
|  |                 let newFreePtr := add(memPtr, size) | ||||||
|  |                 // protect against overflow | ||||||
|  |                 if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } | ||||||
|  |                 mstore(64, newFreePtr) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function cleanup_t_bytes32(value) -> cleaned { | ||||||
|  |                 cleaned := value | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() -> converted { | ||||||
|  |                 converted := 0x6162636162630000000000000000000000000000000000000000000000000000 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function fun_f_9()  -> vloc__4 { | ||||||
|  |                 for { let return_flag := 1 } return_flag {} { | ||||||
|  |                     vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() | ||||||
|  |                     return_flag := 0 | ||||||
|  |                     break | ||||||
|  | 
 | ||||||
|  |                     break | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function shift_right_224_unsigned(value) -> newValue { | ||||||
|  |                 newValue := | ||||||
|  | 
 | ||||||
|  |                 shr(224, value) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | "}}},"sources":{"A":{"id":0}}} | ||||||
| @ -0,0 +1,17 @@ | |||||||
|  | { | ||||||
|  | 	"language": "Solidity", | ||||||
|  | 	"sources": | ||||||
|  | 	{ | ||||||
|  | 		"A": | ||||||
|  | 		{ | ||||||
|  | 			"content": "pragma solidity >=0.0; contract C { function f() external pure returns (bytes4) { return 0x61626364; } }" | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	"settings": | ||||||
|  | 	{ | ||||||
|  | 		"outputSelection": | ||||||
|  | 		{ | ||||||
|  | 			"*": { "*": ["ir"] } | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,138 @@ | |||||||
|  | {"contracts":{"A":{"C":{"ir":"/******************************************************* | ||||||
|  |  *                       WARNING                       * | ||||||
|  |  *  Solidity to Yul compilation is still EXPERIMENTAL  * | ||||||
|  |  *       It can result in LOSS OF FUNDS or worse       * | ||||||
|  |  *                !USE AT YOUR OWN RISK!               * | ||||||
|  |  *******************************************************/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | object \"C_10\" { | ||||||
|  |     code { | ||||||
|  |         mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |         // Begin state variable initialization for contract \"C\" (0 variables) | ||||||
|  |         // End state variable initialization for contract \"C\". | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) | ||||||
|  |         return(0, datasize(\"C_10_deployed\")) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         function cleanup_t_rational_1633837924_by_1(value) -> cleaned { | ||||||
|  |             cleaned := value | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function convert_t_rational_1633837924_by_1_to_t_bytes4(value) -> converted { | ||||||
|  |             converted := shift_left_224(cleanup_t_rational_1633837924_by_1(value)) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function fun_f_9()  -> vloc__4 { | ||||||
|  |             for { let return_flag := 1 } return_flag {} { | ||||||
|  |                 let expr_6 := 0x61626364 | ||||||
|  |                 vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) | ||||||
|  |                 return_flag := 0 | ||||||
|  |                 break | ||||||
|  | 
 | ||||||
|  |                 break | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function shift_left_224(value) -> newValue { | ||||||
|  |             newValue := | ||||||
|  | 
 | ||||||
|  |             shl(224, value) | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     object \"C_10_deployed\" { | ||||||
|  |         code { | ||||||
|  |             mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |             if iszero(lt(calldatasize(), 4)) | ||||||
|  |             { | ||||||
|  |                 let selector := shift_right_224_unsigned(calldataload(0)) | ||||||
|  |                 switch selector | ||||||
|  | 
 | ||||||
|  |                 case 0x26121ff0 | ||||||
|  |                 { | ||||||
|  |                     // f() | ||||||
|  |                     if callvalue() { revert(0, 0) } | ||||||
|  |                     abi_decode_tuple_(4, calldatasize()) | ||||||
|  |                     let ret_0 :=  fun_f_9() | ||||||
|  |                     let memPos := allocateMemory(0) | ||||||
|  |                     let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos ,  ret_0) | ||||||
|  |                     return(memPos, sub(memEnd, memPos)) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 default {} | ||||||
|  |             } | ||||||
|  |             revert(0, 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             function abi_decode_tuple_(headStart, dataEnd)   { | ||||||
|  |                 if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_t_bytes4_to_t_bytes4_fromStack(value, pos) { | ||||||
|  |                 mstore(pos, cleanup_t_bytes4(value)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(headStart , value0) -> tail { | ||||||
|  |                 tail := add(headStart, 32) | ||||||
|  | 
 | ||||||
|  |                 abi_encode_t_bytes4_to_t_bytes4_fromStack(value0,  add(headStart, 0)) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function allocateMemory(size) -> memPtr { | ||||||
|  |                 memPtr := mload(64) | ||||||
|  |                 let newFreePtr := add(memPtr, size) | ||||||
|  |                 // protect against overflow | ||||||
|  |                 if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } | ||||||
|  |                 mstore(64, newFreePtr) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function cleanup_t_bytes4(value) -> cleaned { | ||||||
|  |                 cleaned := and(value, 0xffffffff00000000000000000000000000000000000000000000000000000000) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function cleanup_t_rational_1633837924_by_1(value) -> cleaned { | ||||||
|  |                 cleaned := value | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function convert_t_rational_1633837924_by_1_to_t_bytes4(value) -> converted { | ||||||
|  |                 converted := shift_left_224(cleanup_t_rational_1633837924_by_1(value)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function fun_f_9()  -> vloc__4 { | ||||||
|  |                 for { let return_flag := 1 } return_flag {} { | ||||||
|  |                     let expr_6 := 0x61626364 | ||||||
|  |                     vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) | ||||||
|  |                     return_flag := 0 | ||||||
|  |                     break | ||||||
|  | 
 | ||||||
|  |                     break | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function shift_left_224(value) -> newValue { | ||||||
|  |                 newValue := | ||||||
|  | 
 | ||||||
|  |                 shl(224, value) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function shift_right_224_unsigned(value) -> newValue { | ||||||
|  |                 newValue := | ||||||
|  | 
 | ||||||
|  |                 shr(224, value) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | "}}},"sources":{"A":{"id":0}}} | ||||||
							
								
								
									
										17
									
								
								test/cmdlineTests/yul_string_format_ascii_long/input.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								test/cmdlineTests/yul_string_format_ascii_long/input.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | { | ||||||
|  | 	"language": "Solidity", | ||||||
|  | 	"sources": | ||||||
|  | 	{ | ||||||
|  | 		"A": | ||||||
|  | 		{ | ||||||
|  | 			"content": "pragma solidity >=0.0; contract C { function f() external pure returns (string memory) { return \"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\"; } }" | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	"settings": | ||||||
|  | 	{ | ||||||
|  | 		"outputSelection": | ||||||
|  | 		{ | ||||||
|  | 			"*": { "*": ["ir"] } | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										169
									
								
								test/cmdlineTests/yul_string_format_ascii_long/output.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								test/cmdlineTests/yul_string_format_ascii_long/output.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,169 @@ | |||||||
|  | {"contracts":{"A":{"C":{"ir":"/******************************************************* | ||||||
|  |  *                       WARNING                       * | ||||||
|  |  *  Solidity to Yul compilation is still EXPERIMENTAL  * | ||||||
|  |  *       It can result in LOSS OF FUNDS or worse       * | ||||||
|  |  *                !USE AT YOUR OWN RISK!               * | ||||||
|  |  *******************************************************/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | object \"C_10\" { | ||||||
|  |     code { | ||||||
|  |         mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |         // Begin state variable initialization for contract \"C\" (0 variables) | ||||||
|  |         // End state variable initialization for contract \"C\". | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) | ||||||
|  |         return(0, datasize(\"C_10_deployed\")) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         function allocateMemory(size) -> memPtr { | ||||||
|  |             memPtr := mload(64) | ||||||
|  |             let newFreePtr := add(memPtr, size) | ||||||
|  |             // protect against overflow | ||||||
|  |             if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } | ||||||
|  |             mstore(64, newFreePtr) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() -> converted { | ||||||
|  |             converted := allocateMemory(128) | ||||||
|  |             mstore(converted, 85) | ||||||
|  | 
 | ||||||
|  |             mstore(add(converted, 32), \"abcdabcdcafecafeabcdabcdcafecafe\") | ||||||
|  | 
 | ||||||
|  |             mstore(add(converted, 64), \"ffffzzzzoooo0123456789,.<,>.?:;'\") | ||||||
|  | 
 | ||||||
|  |             mstore(add(converted, 96), \"[{]}|`~!@#$%^&*()-_=+\") | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function fun_f_9()  -> vloc__4 { | ||||||
|  |             for { let return_flag := 1 } return_flag {} { | ||||||
|  |                 vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() | ||||||
|  |                 return_flag := 0 | ||||||
|  |                 break | ||||||
|  | 
 | ||||||
|  |                 break | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     object \"C_10_deployed\" { | ||||||
|  |         code { | ||||||
|  |             mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |             if iszero(lt(calldatasize(), 4)) | ||||||
|  |             { | ||||||
|  |                 let selector := shift_right_224_unsigned(calldataload(0)) | ||||||
|  |                 switch selector | ||||||
|  | 
 | ||||||
|  |                 case 0x26121ff0 | ||||||
|  |                 { | ||||||
|  |                     // f() | ||||||
|  |                     if callvalue() { revert(0, 0) } | ||||||
|  |                     abi_decode_tuple_(4, calldatasize()) | ||||||
|  |                     let ret_0 :=  fun_f_9() | ||||||
|  |                     let memPos := allocateMemory(0) | ||||||
|  |                     let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos ,  ret_0) | ||||||
|  |                     return(memPos, sub(memEnd, memPos)) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 default {} | ||||||
|  |             } | ||||||
|  |             revert(0, 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             function abi_decode_tuple_(headStart, dataEnd)   { | ||||||
|  |                 if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value, pos) -> end { | ||||||
|  |                 let length := array_length_t_string_memory_ptr(value) | ||||||
|  |                 pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) | ||||||
|  |                 copy_memory_to_memory(add(value, 0x20), pos, length) | ||||||
|  |                 end := add(pos, round_up_to_mul_of_32(length)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(headStart , value0) -> tail { | ||||||
|  |                 tail := add(headStart, 32) | ||||||
|  | 
 | ||||||
|  |                 mstore(add(headStart, 0), sub(tail, headStart)) | ||||||
|  |                 tail := abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value0,  tail) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function allocateMemory(size) -> memPtr { | ||||||
|  |                 memPtr := mload(64) | ||||||
|  |                 let newFreePtr := add(memPtr, size) | ||||||
|  |                 // protect against overflow | ||||||
|  |                 if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } | ||||||
|  |                 mstore(64, newFreePtr) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function array_length_t_string_memory_ptr(value) -> length { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                 length := mload(value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { | ||||||
|  |                 mstore(pos, length) | ||||||
|  |                 updated_pos := add(pos, 0x20) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() -> converted { | ||||||
|  |                 converted := allocateMemory(128) | ||||||
|  |                 mstore(converted, 85) | ||||||
|  | 
 | ||||||
|  |                 mstore(add(converted, 32), \"abcdabcdcafecafeabcdabcdcafecafe\") | ||||||
|  | 
 | ||||||
|  |                 mstore(add(converted, 64), \"ffffzzzzoooo0123456789,.<,>.?:;'\") | ||||||
|  | 
 | ||||||
|  |                 mstore(add(converted, 96), \"[{]}|`~!@#$%^&*()-_=+\") | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function copy_memory_to_memory(src, dst, length) { | ||||||
|  |                 let i := 0 | ||||||
|  |                 for { } lt(i, length) { i := add(i, 32) } | ||||||
|  |                 { | ||||||
|  |                     mstore(add(dst, i), mload(add(src, i))) | ||||||
|  |                 } | ||||||
|  |                 if gt(i, length) | ||||||
|  |                 { | ||||||
|  |                     // clear end | ||||||
|  |                     mstore(add(dst, length), 0) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function fun_f_9()  -> vloc__4 { | ||||||
|  |                 for { let return_flag := 1 } return_flag {} { | ||||||
|  |                     vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() | ||||||
|  |                     return_flag := 0 | ||||||
|  |                     break | ||||||
|  | 
 | ||||||
|  |                     break | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function round_up_to_mul_of_32(value) -> result { | ||||||
|  |                 result := and(add(value, 31), not(31)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function shift_right_224_unsigned(value) -> newValue { | ||||||
|  |                 newValue := | ||||||
|  | 
 | ||||||
|  |                 shr(224, value) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | "}}},"sources":{"A":{"id":0}}} | ||||||
							
								
								
									
										17
									
								
								test/cmdlineTests/yul_string_format_hex/input.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								test/cmdlineTests/yul_string_format_hex/input.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | { | ||||||
|  | 	"language": "Solidity", | ||||||
|  | 	"sources": | ||||||
|  | 	{ | ||||||
|  | 		"A": | ||||||
|  | 		{ | ||||||
|  | 			"content": "pragma solidity >=0.0; contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }" | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	"settings": | ||||||
|  | 	{ | ||||||
|  | 		"outputSelection": | ||||||
|  | 		{ | ||||||
|  | 			"*": { "*": ["ir"] } | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										138
									
								
								test/cmdlineTests/yul_string_format_hex/output.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								test/cmdlineTests/yul_string_format_hex/output.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | |||||||
|  | {"contracts":{"A":{"C":{"ir":"/******************************************************* | ||||||
|  |  *                       WARNING                       * | ||||||
|  |  *  Solidity to Yul compilation is still EXPERIMENTAL  * | ||||||
|  |  *       It can result in LOSS OF FUNDS or worse       * | ||||||
|  |  *                !USE AT YOUR OWN RISK!               * | ||||||
|  |  *******************************************************/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | object \"C_10\" { | ||||||
|  |     code { | ||||||
|  |         mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |         // Begin state variable initialization for contract \"C\" (0 variables) | ||||||
|  |         // End state variable initialization for contract \"C\". | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) | ||||||
|  |         return(0, datasize(\"C_10_deployed\")) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         function cleanup_t_rational_2864434397_by_1(value) -> cleaned { | ||||||
|  |             cleaned := value | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function convert_t_rational_2864434397_by_1_to_t_bytes4(value) -> converted { | ||||||
|  |             converted := shift_left_224(cleanup_t_rational_2864434397_by_1(value)) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function fun_f_9()  -> vloc__4 { | ||||||
|  |             for { let return_flag := 1 } return_flag {} { | ||||||
|  |                 let expr_6 := 0xaabbccdd | ||||||
|  |                 vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) | ||||||
|  |                 return_flag := 0 | ||||||
|  |                 break | ||||||
|  | 
 | ||||||
|  |                 break | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function shift_left_224(value) -> newValue { | ||||||
|  |             newValue := | ||||||
|  | 
 | ||||||
|  |             shl(224, value) | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     object \"C_10_deployed\" { | ||||||
|  |         code { | ||||||
|  |             mstore(64, 128) | ||||||
|  | 
 | ||||||
|  |             if iszero(lt(calldatasize(), 4)) | ||||||
|  |             { | ||||||
|  |                 let selector := shift_right_224_unsigned(calldataload(0)) | ||||||
|  |                 switch selector | ||||||
|  | 
 | ||||||
|  |                 case 0x26121ff0 | ||||||
|  |                 { | ||||||
|  |                     // f() | ||||||
|  |                     if callvalue() { revert(0, 0) } | ||||||
|  |                     abi_decode_tuple_(4, calldatasize()) | ||||||
|  |                     let ret_0 :=  fun_f_9() | ||||||
|  |                     let memPos := allocateMemory(0) | ||||||
|  |                     let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos ,  ret_0) | ||||||
|  |                     return(memPos, sub(memEnd, memPos)) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 default {} | ||||||
|  |             } | ||||||
|  |             revert(0, 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             function abi_decode_tuple_(headStart, dataEnd)   { | ||||||
|  |                 if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_t_bytes4_to_t_bytes4_fromStack(value, pos) { | ||||||
|  |                 mstore(pos, cleanup_t_bytes4(value)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(headStart , value0) -> tail { | ||||||
|  |                 tail := add(headStart, 32) | ||||||
|  | 
 | ||||||
|  |                 abi_encode_t_bytes4_to_t_bytes4_fromStack(value0,  add(headStart, 0)) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function allocateMemory(size) -> memPtr { | ||||||
|  |                 memPtr := mload(64) | ||||||
|  |                 let newFreePtr := add(memPtr, size) | ||||||
|  |                 // protect against overflow | ||||||
|  |                 if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } | ||||||
|  |                 mstore(64, newFreePtr) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function cleanup_t_bytes4(value) -> cleaned { | ||||||
|  |                 cleaned := and(value, 0xffffffff00000000000000000000000000000000000000000000000000000000) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function cleanup_t_rational_2864434397_by_1(value) -> cleaned { | ||||||
|  |                 cleaned := value | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function convert_t_rational_2864434397_by_1_to_t_bytes4(value) -> converted { | ||||||
|  |                 converted := shift_left_224(cleanup_t_rational_2864434397_by_1(value)) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function fun_f_9()  -> vloc__4 { | ||||||
|  |                 for { let return_flag := 1 } return_flag {} { | ||||||
|  |                     let expr_6 := 0xaabbccdd | ||||||
|  |                     vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) | ||||||
|  |                     return_flag := 0 | ||||||
|  |                     break | ||||||
|  | 
 | ||||||
|  |                     break | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function shift_left_224(value) -> newValue { | ||||||
|  |                 newValue := | ||||||
|  | 
 | ||||||
|  |                 shl(224, value) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             function shift_right_224_unsigned(value) -> newValue { | ||||||
|  |                 newValue := | ||||||
|  | 
 | ||||||
|  |                 shr(224, value) | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | "}}},"sources":{"A":{"id":0}}} | ||||||
| @ -505,7 +505,7 @@ BOOST_AUTO_TEST_CASE(valid_hex_literal) | |||||||
| { | { | ||||||
| 	Scanner scanner(CharStream("{ hex\"00112233FF\"", "")); | 	Scanner scanner(CharStream("{ hex\"00112233FF\"", "")); | ||||||
| 	BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); | 	BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); | ||||||
| 	BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); | 	BOOST_CHECK_EQUAL(scanner.next(), Token::HexStringLiteral); | ||||||
| 	BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); | 	BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								test/libsolidity/semanticTests/viaYul/string_format.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								test/libsolidity/semanticTests/viaYul/string_format.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | contract C { | ||||||
|  | 	function f1() external pure returns (string memory) { return "abcabc"; } | ||||||
|  | 	function f2() external pure returns (string memory) { return "abcabc`~12345677890- _=+!@#$%^&*()[{]}|;:',<.>?"; } | ||||||
|  | 	function g() external pure returns (bytes32) { return "abcabc"; } | ||||||
|  | 	function h() external pure returns (bytes4) { return 0xcafecafe; } | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: only | ||||||
|  | // ---- | ||||||
|  | // f1() -> 0x20, 6, left(0x616263616263) | ||||||
|  | // f2() -> 32, 47, 44048183223289766195424279195050628400112610419087780792899004030957505095210, 18165586057823232067963737336409268114628061002662705707816940456850361417728 | ||||||
|  | // g() -> left(0x616263616263) | ||||||
|  | // h() -> left(0xcafecafe) | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | contract test { | ||||||
|  |     function f() public pure returns (bytes32) { | ||||||
|  |         bytes32 escapeCharacters = hex"aa" hex"b"; | ||||||
|  |         return escapeCharacters; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // ParserError: (108-112): Expected even number of hex-nibbles within double-quotes. | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | contract test { | ||||||
|  |     function f() public pure returns (bytes32) { | ||||||
|  |         bytes32 escapeCharacters = hex"0000" | ||||||
|  |         hex"deaf" | ||||||
|  |         hex"feed"; | ||||||
|  |         return escapeCharacters; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | contract test { | ||||||
|  |     function f() public pure returns (bytes32) { | ||||||
|  |         bytes32 escapeCharacters = hex"0000" | ||||||
|  |         "deaf" | ||||||
|  |         "feed"; | ||||||
|  |         return escapeCharacters; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // ParserError: (118-124): Expected ';' but got 'StringLiteral' | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | contract test { | ||||||
|  |     function f() public pure returns (bytes32) { | ||||||
|  |         bytes32 escapeCharacters = hex"aa" hex"bb" "cc"; | ||||||
|  |         return escapeCharacters; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // ParserError: (116-120): Expected ';' but got 'StringLiteral' | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | contract test { | ||||||
|  |     function f() public pure returns (bytes32) { | ||||||
|  |         bytes32 escapeCharacters = "foo" "bar" hex"aa"; | ||||||
|  |         return escapeCharacters; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // ParserError: (112-119): Expected ';' but got 'HexStringLiteral' | ||||||
| @ -0,0 +1,7 @@ | |||||||
|  | contract test { | ||||||
|  |     function f() public pure returns (bytes32) { | ||||||
|  |         bytes32 escapeCharacters = "first" "second" "third"; | ||||||
|  |         return escapeCharacters; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
							
								
								
									
										156
									
								
								test/libyul/EWasmTranslationTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								test/libyul/EWasmTranslationTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,156 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include <test/libyul/EWasmTranslationTest.h> | ||||||
|  | 
 | ||||||
|  | #include <test/tools/yulInterpreter/Interpreter.h> | ||||||
|  | 
 | ||||||
|  | #include <test/Options.h> | ||||||
|  | 
 | ||||||
|  | #include <libyul/backends/evm/EVMDialect.h> | ||||||
|  | #include <libyul/backends/wasm/WasmDialect.h> | ||||||
|  | #include <libyul/backends/wasm/EVMToEWasmTranslator.h> | ||||||
|  | #include <libyul/AsmParser.h> | ||||||
|  | #include <libyul/AssemblyStack.h> | ||||||
|  | #include <libyul/AsmAnalysisInfo.h> | ||||||
|  | 
 | ||||||
|  | #include <liblangutil/ErrorReporter.h> | ||||||
|  | #include <liblangutil/SourceReferenceFormatter.h> | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/AnsiColorized.h> | ||||||
|  | 
 | ||||||
|  | #include <boost/test/unit_test.hpp> | ||||||
|  | #include <boost/algorithm/string.hpp> | ||||||
|  | 
 | ||||||
|  | #include <fstream> | ||||||
|  | 
 | ||||||
|  | using namespace dev; | ||||||
|  | using namespace langutil; | ||||||
|  | using namespace yul; | ||||||
|  | using namespace yul::test; | ||||||
|  | using namespace dev::solidity; | ||||||
|  | using namespace dev::solidity::test; | ||||||
|  | using namespace std; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | EWasmTranslationTest::EWasmTranslationTest(string const& _filename) | ||||||
|  | { | ||||||
|  | 	boost::filesystem::path path(_filename); | ||||||
|  | 
 | ||||||
|  | 	ifstream file(_filename); | ||||||
|  | 	if (!file) | ||||||
|  | 		BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\".")); | ||||||
|  | 	file.exceptions(ios::badbit); | ||||||
|  | 
 | ||||||
|  | 	m_source = parseSourceAndSettings(file); | ||||||
|  | 	m_expectation = parseSimpleExpectations(file); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TestCase::TestResult EWasmTranslationTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) | ||||||
|  | { | ||||||
|  | 	if (!parse(_stream, _linePrefix, _formatted)) | ||||||
|  | 		return TestResult::FatalError; | ||||||
|  | 
 | ||||||
|  | 	*m_object = EVMToEWasmTranslator( | ||||||
|  | 		EVMDialect::strictAssemblyForEVMObjects(dev::test::Options::get().evmVersion()) | ||||||
|  | 	).run(*m_object); | ||||||
|  | 
 | ||||||
|  | 	// Add call to "main()".
 | ||||||
|  | 	m_object->code->statements.emplace_back( | ||||||
|  | 		ExpressionStatement{{}, FunctionCall{{}, Identifier{{}, "main"_yulstring}, {}}} | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	m_obtainedResult = interpret(); | ||||||
|  | 
 | ||||||
|  | 	if (m_expectation != m_obtainedResult) | ||||||
|  | 	{ | ||||||
|  | 		string nextIndentLevel = _linePrefix + "  "; | ||||||
|  | 		AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Expected result:" << endl; | ||||||
|  | 		// TODO could compute a simple diff with highlighted lines
 | ||||||
|  | 		printIndented(_stream, m_expectation, nextIndentLevel); | ||||||
|  | 		AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Obtained result:" << endl; | ||||||
|  | 		printIndented(_stream, m_obtainedResult, nextIndentLevel); | ||||||
|  | 		return TestResult::Failure; | ||||||
|  | 	} | ||||||
|  | 	return TestResult::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EWasmTranslationTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const | ||||||
|  | { | ||||||
|  | 	printIndented(_stream, m_source, _linePrefix); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EWasmTranslationTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const | ||||||
|  | { | ||||||
|  | 	printIndented(_stream, m_obtainedResult, _linePrefix); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EWasmTranslationTest::printIndented(ostream& _stream, string const& _output, string const& _linePrefix) const | ||||||
|  | { | ||||||
|  | 	stringstream output(_output); | ||||||
|  | 	string line; | ||||||
|  | 	while (getline(output, line)) | ||||||
|  | 		_stream << _linePrefix << line << endl; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool EWasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) | ||||||
|  | { | ||||||
|  | 	AssemblyStack stack( | ||||||
|  | 		dev::test::Options::get().evmVersion(), | ||||||
|  | 		AssemblyStack::Language::StrictAssembly, | ||||||
|  | 		dev::solidity::OptimiserSettings::none() | ||||||
|  | 	); | ||||||
|  | 	if (stack.parseAndAnalyze("", m_source)) | ||||||
|  | 	{ | ||||||
|  | 		m_object = stack.parserResult(); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; | ||||||
|  | 		printErrors(_stream, stack.errors()); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string EWasmTranslationTest::interpret() | ||||||
|  | { | ||||||
|  | 	InterpreterState state; | ||||||
|  | 	state.maxTraceSize = 10000; | ||||||
|  | 	state.maxSteps = 10000; | ||||||
|  | 	WasmDialect dialect; | ||||||
|  | 	Interpreter interpreter(state, dialect); | ||||||
|  | 	try | ||||||
|  | 	{ | ||||||
|  | 		interpreter(*m_object->code); | ||||||
|  | 	} | ||||||
|  | 	catch (InterpreterTerminatedGeneric const&) | ||||||
|  | 	{ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	stringstream result; | ||||||
|  | 	state.dumpTraceAndState(result); | ||||||
|  | 	return result.str(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EWasmTranslationTest::printErrors(ostream& _stream, ErrorList const& _errors) | ||||||
|  | { | ||||||
|  | 	SourceReferenceFormatter formatter(_stream); | ||||||
|  | 
 | ||||||
|  | 	for (auto const& error: _errors) | ||||||
|  | 		formatter.printErrorInformation(*error); | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								test/libyul/EWasmTranslationTest.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								test/libyul/EWasmTranslationTest.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <test/TestCase.h> | ||||||
|  | #include <libyul/Object.h> | ||||||
|  | 
 | ||||||
|  | namespace langutil | ||||||
|  | { | ||||||
|  | class Scanner; | ||||||
|  | class Error; | ||||||
|  | using ErrorList = std::vector<std::shared_ptr<Error const>>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace yul | ||||||
|  | { | ||||||
|  | namespace test | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class EWasmTranslationTest: public dev::solidity::test::EVMVersionRestrictedTestCase | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	static std::unique_ptr<TestCase> create(Config const& _config) | ||||||
|  | 	{ | ||||||
|  | 		return std::unique_ptr<TestCase>(new EWasmTranslationTest(_config.filename)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	explicit EWasmTranslationTest(std::string const& _filename); | ||||||
|  | 
 | ||||||
|  | 	TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; | ||||||
|  | 
 | ||||||
|  | 	void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool const _formatted = false) const override; | ||||||
|  | 	void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	void printIndented(std::ostream& _stream, std::string const& _output, std::string const& _linePrefix = "") const; | ||||||
|  | 	bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); | ||||||
|  | 	std::string interpret(); | ||||||
|  | 
 | ||||||
|  | 	static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); | ||||||
|  | 
 | ||||||
|  | 	std::string m_source; | ||||||
|  | 	std::string m_expectation; | ||||||
|  | 
 | ||||||
|  | 	std::shared_ptr<Object> m_object; | ||||||
|  | 	std::string m_obtainedResult; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								test/libyul/ewasmTranslationTests/datacopy.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								test/libyul/ewasmTranslationTests/datacopy.yul
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | object "main" | ||||||
|  | { | ||||||
|  |     code { | ||||||
|  |         datacopy(0, and(dataoffset("main"), 15), and(datasize("main"), 15)) | ||||||
|  |         datacopy(32, and(dataoffset("sub"), 15), and(datasize("sub"), 15)) | ||||||
|  |         sstore(0, mload(0)) | ||||||
|  |         sstore(1, mload(32)) | ||||||
|  |     } | ||||||
|  |     object "sub" { code { sstore(0, 1) } } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // Trace: | ||||||
|  | // Memory dump: | ||||||
|  | //      0: 0000000000000000000000000000000000000000000000000000000000000001 | ||||||
|  | //     20: 636f6465636f6465000000000000000000000000000000000000000000000000 | ||||||
|  | //     40: 6465636f00000000000000000000000000000000000000000000000000000000 | ||||||
|  | //     60: 636f6465636f6465000000000000000000000000000000000000000000000000 | ||||||
|  | // Storage dump: | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000000: 6465636f00000000000000000000000000000000000000000000000000000000 | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000001: 636f6465636f6465000000000000000000000000000000000000000000000000 | ||||||
							
								
								
									
										16
									
								
								test/libyul/ewasmTranslationTests/dataoffset.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								test/libyul/ewasmTranslationTests/dataoffset.yul
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | object "main" | ||||||
|  | { | ||||||
|  |     code { | ||||||
|  |         sstore(0, dataoffset("main")) | ||||||
|  |         sstore(1, dataoffset("sub")) | ||||||
|  |     } | ||||||
|  |     object "sub" { code { sstore(0, 1) } } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // Trace: | ||||||
|  | // Memory dump: | ||||||
|  | //      0: 0000000000000000000000000000000000000000000000000000000000000001 | ||||||
|  | //     20: 000000000000000000000000000000000000000000000000000000000000070c | ||||||
|  | // Storage dump: | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000000: 000000000000000000000000000000000000000000000000000000000000006e | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000001: 000000000000000000000000000000000000000000000000000000000000070c | ||||||
							
								
								
									
										16
									
								
								test/libyul/ewasmTranslationTests/datasize.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								test/libyul/ewasmTranslationTests/datasize.yul
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | object "main" | ||||||
|  | { | ||||||
|  |     code { | ||||||
|  |         sstore(0, datasize("main")) | ||||||
|  |         sstore(1, datasize("sub")) | ||||||
|  |     } | ||||||
|  |     object "sub" { code { sstore(0, 1) } } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // Trace: | ||||||
|  | // Memory dump: | ||||||
|  | //      0: 0000000000000000000000000000000000000000000000000000000000000001 | ||||||
|  | //     20: 0000000000000000000000000000000000000000000000000000000000000109 | ||||||
|  | // Storage dump: | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000b64 | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000109 | ||||||
							
								
								
									
										14
									
								
								test/libyul/ewasmTranslationTests/mstore_mload.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								test/libyul/ewasmTranslationTests/mstore_mload.yul
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | { | ||||||
|  |   mstore(0x20, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20) | ||||||
|  |   mstore(0x40, mload(0x20)) | ||||||
|  |   sstore(1, mload(0x40)) | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // Trace: | ||||||
|  | // Memory dump: | ||||||
|  | //      0: 0000000000000000000000000000000000000000000000000000000000000001 | ||||||
|  | //     20: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 | ||||||
|  | //     60: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 | ||||||
|  | //     80: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 | ||||||
|  | // Storage dump: | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000001: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 | ||||||
							
								
								
									
										17
									
								
								test/libyul/ewasmTranslationTests/shl.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								test/libyul/ewasmTranslationTests/shl.yul
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | { | ||||||
|  |   let x := 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 | ||||||
|  |   let y := shl(120, x) | ||||||
|  |   let z := shr(136, y) | ||||||
|  |   sstore(0, y) | ||||||
|  |   sstore(1, z) | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // EVMVersion: >=constantinople | ||||||
|  | // ---- | ||||||
|  | // Trace: | ||||||
|  | // Memory dump: | ||||||
|  | //      0: 0000000000000000000000000000000000000000000000000000000000000001 | ||||||
|  | //     20: 0000000000000000000000000000000000101112131415161718191a1b1c1d1e | ||||||
|  | // Storage dump: | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000000: 101112131415161718191a1b1c1d1e1f20000000000000000000000000000000 | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000101112131415161718191a1b1c1d1e | ||||||
							
								
								
									
										8
									
								
								test/libyul/ewasmTranslationTests/simple_mstore.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								test/libyul/ewasmTranslationTests/simple_mstore.yul
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | { | ||||||
|  |   mstore(0x20, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20) | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // Trace: | ||||||
|  | // Memory dump: | ||||||
|  | //     60: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 | ||||||
|  | // Storage dump: | ||||||
							
								
								
									
										12
									
								
								test/libyul/ewasmTranslationTests/simple_sstore.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								test/libyul/ewasmTranslationTests/simple_sstore.yul
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | { | ||||||
|  |   sstore(1, 7) | ||||||
|  |   sstore(2, sub(0, 1)) | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // Trace: | ||||||
|  | // Memory dump: | ||||||
|  | //      0: 0000000000000000000000000000000000000000000000000000000000000002 | ||||||
|  | //     20: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff | ||||||
|  | // Storage dump: | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000007 | ||||||
|  | //   0000000000000000000000000000000000000000000000000000000000000002: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff | ||||||
							
								
								
									
										5
									
								
								test/libyul/ewasmTranslationTests/smoke.yul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								test/libyul/ewasmTranslationTests/smoke.yul
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | {} | ||||||
|  | // ---- | ||||||
|  | // Trace: | ||||||
|  | // Memory dump: | ||||||
|  | // Storage dump: | ||||||
| @ -31,6 +31,7 @@ add_executable(isoltest | |||||||
| 	../libsolidity/ASTJSONTest.cpp | 	../libsolidity/ASTJSONTest.cpp | ||||||
| 	../libsolidity/SMTCheckerJSONTest.cpp | 	../libsolidity/SMTCheckerJSONTest.cpp | ||||||
| 	../libyul/Common.cpp | 	../libyul/Common.cpp | ||||||
|  | 	../libyul/EWasmTranslationTest.cpp | ||||||
| 	../libyul/FunctionSideEffects.cpp | 	../libyul/FunctionSideEffects.cpp | ||||||
| 	../libyul/ObjectCompilerTest.cpp | 	../libyul/ObjectCompilerTest.cpp | ||||||
| 	../libyul/YulOptimizerTest.cpp | 	../libyul/YulOptimizerTest.cpp | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| set(sources | set(sources | ||||||
| 	EVMInstructionInterpreter.h | 	EVMInstructionInterpreter.h | ||||||
| 	EVMInstructionInterpreter.cpp | 	EVMInstructionInterpreter.cpp | ||||||
|  | 	EWasmBuiltinInterpreter.h | ||||||
|  | 	EWasmBuiltinInterpreter.cpp | ||||||
| 	Interpreter.h | 	Interpreter.h | ||||||
| 	Interpreter.cpp | 	Interpreter.cpp | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										376
									
								
								test/tools/yulInterpreter/EWasmBuiltinInterpreter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										376
									
								
								test/tools/yulInterpreter/EWasmBuiltinInterpreter.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,376 @@ | |||||||
|  | /*
 | ||||||
|  | 	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/>.
 | ||||||
|  | */ | ||||||
|  | /**
 | ||||||
|  |  * Yul interpreter module that evaluates EWasm builtins. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <test/tools/yulInterpreter/EWasmBuiltinInterpreter.h> | ||||||
|  | 
 | ||||||
|  | #include <test/tools/yulInterpreter/Interpreter.h> | ||||||
|  | 
 | ||||||
|  | #include <libyul/backends/evm/EVMDialect.h> | ||||||
|  | #include <libyul/AsmData.h> | ||||||
|  | 
 | ||||||
|  | #include <libevmasm/Instruction.h> | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/Keccak256.h> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace dev; | ||||||
|  | using namespace yul; | ||||||
|  | using namespace yul::test; | ||||||
|  | 
 | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | /// Copy @a _size bytes of @a _source at offset @a _sourceOffset to
 | ||||||
|  | /// @a _target at offset @a _targetOffset. Behaves as if @a _source would
 | ||||||
|  | /// continue with an infinite sequence of zero bytes beyond its end.
 | ||||||
|  | void copyZeroExtended( | ||||||
|  | 	map<u256, uint8_t>& _target, bytes const& _source, | ||||||
|  | 	size_t _targetOffset, size_t _sourceOffset, size_t _size | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	for (size_t i = 0; i < _size; ++i) | ||||||
|  | 		_target[_targetOffset + i] = _sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | using u512 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<512, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; | ||||||
|  | 
 | ||||||
|  | u256 EWasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector<u256> const& _arguments) | ||||||
|  | { | ||||||
|  | 	vector<uint64_t> arg; | ||||||
|  | 	for (u256 const& a: _arguments) | ||||||
|  | 		arg.emplace_back(uint64_t(a & uint64_t(-1))); | ||||||
|  | 
 | ||||||
|  | 	if (_fun == "datasize"_yulstring) | ||||||
|  | 		return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; | ||||||
|  | 	else if (_fun == "dataoffset"_yulstring) | ||||||
|  | 		return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; | ||||||
|  | 	else if (_fun == "datacopy"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		// This is identical to codecopy.
 | ||||||
|  | 		if (accessMemory(_arguments.at(0), _arguments.at(2))) | ||||||
|  | 			copyZeroExtended( | ||||||
|  | 				m_state.memory, | ||||||
|  | 				m_state.code, | ||||||
|  | 				size_t(_arguments.at(0)), | ||||||
|  | 				size_t(_arguments.at(1) & size_t(-1)), | ||||||
|  | 				size_t(_arguments.at(2)) | ||||||
|  | 			); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "drop"_yulstring) | ||||||
|  | 		return {}; | ||||||
|  | 	else if (_fun == "unreachable"_yulstring) | ||||||
|  | 		throw ExplicitlyTerminated(); | ||||||
|  | 	else if (_fun == "i64.add"_yulstring) | ||||||
|  | 		return arg[0] + arg[1]; | ||||||
|  | 	else if (_fun == "i64.sub"_yulstring) | ||||||
|  | 		return arg[0] - arg[1]; | ||||||
|  | 	else if (_fun == "i64.mul"_yulstring) | ||||||
|  | 		return arg[0] * arg[1]; | ||||||
|  | 	else if (_fun == "i64.div_u"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		if (arg[1] == 0) | ||||||
|  | 			throw ExplicitlyTerminated(); | ||||||
|  | 		else | ||||||
|  | 			return arg[0] / arg[1]; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "i64.rem_u"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		if (arg[1] == 0) | ||||||
|  | 			throw ExplicitlyTerminated(); | ||||||
|  | 		else | ||||||
|  | 			return arg[0] % arg[1]; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "i64.and"_yulstring) | ||||||
|  | 		return arg[0] & arg[1]; | ||||||
|  | 	else if (_fun == "i64.or"_yulstring) | ||||||
|  | 		return arg[0] | arg[1]; | ||||||
|  | 	else if (_fun == "i64.xor"_yulstring) | ||||||
|  | 		return arg[0] ^ arg[1]; | ||||||
|  | 	else if (_fun == "i64.shl"_yulstring) | ||||||
|  | 		return arg[0] << arg[1]; | ||||||
|  | 	else if (_fun == "i64.shr_u"_yulstring) | ||||||
|  | 		return arg[0] >> arg[1]; | ||||||
|  | 	else if (_fun == "i64.eq"_yulstring) | ||||||
|  | 		return arg[0] == arg[1] ? 1 : 0; | ||||||
|  | 	else if (_fun == "i64.ne"_yulstring) | ||||||
|  | 		return arg[0] != arg[1] ? 1 : 0; | ||||||
|  | 	else if (_fun == "i64.eqz"_yulstring) | ||||||
|  | 		return arg[0] == 0 ? 1 : 0; | ||||||
|  | 	else if (_fun == "i64.lt_u"_yulstring) | ||||||
|  | 		return arg[0] < arg[1] ? 1 : 0; | ||||||
|  | 	else if (_fun == "i64.gt_u"_yulstring) | ||||||
|  | 		return arg[0] > arg[1] ? 1 : 0; | ||||||
|  | 	else if (_fun == "i64.le_u"_yulstring) | ||||||
|  | 		return arg[0] <= arg[1] ? 1 : 0; | ||||||
|  | 	else if (_fun == "i64.ge_u"_yulstring) | ||||||
|  | 		return arg[0] >= arg[1] ? 1 : 0; | ||||||
|  | 	else if (_fun == "i64.store"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		accessMemory(arg[0], 8); | ||||||
|  | 		writeMemoryWord(arg[0], arg[1]); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "i64.load"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		accessMemory(arg[0], 8); | ||||||
|  | 		return readMemoryWord(arg[0]); | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.getAddress"_yulstring) | ||||||
|  | 		return writeAddress(arg[0], m_state.address); | ||||||
|  | 	else if (_fun == "eth.getExternalBalance"_yulstring) | ||||||
|  | 		// TODO this does not read the address, but is consistent with
 | ||||||
|  | 		// EVM interpreter implementation.
 | ||||||
|  | 		// If we take the address into account, this needs to use readAddress.
 | ||||||
|  | 		return writeU128(arg[0], m_state.balance); | ||||||
|  | 	else if (_fun == "eth.getBlockHash"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber) | ||||||
|  | 			return 1; | ||||||
|  | 		else | ||||||
|  | 			return writeU256(arg[1], 0xaaaaaaaa + u256(arg[0] - m_state.blockNumber - 256)); | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.call"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		// TODO read args from memory
 | ||||||
|  | 		// TODO use readAddress to read address.
 | ||||||
|  | 		logTrace(eth::Instruction::CALL, {}); | ||||||
|  | 		return arg[0] & 1; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.callDataCopy"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		if (arg[1] + arg[2] < arg[1] || arg[1] + arg[2] > m_state.calldata.size()) | ||||||
|  | 			throw ExplicitlyTerminated(); | ||||||
|  | 		if (accessMemory(arg[0], arg[2])) | ||||||
|  | 			copyZeroExtended( | ||||||
|  | 				m_state.memory, m_state.calldata, | ||||||
|  | 				size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) | ||||||
|  | 			); | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.getCallDataSize"_yulstring) | ||||||
|  | 		return m_state.calldata.size(); | ||||||
|  | 	else if (_fun == "eth.callCode"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		// TODO read args from memory
 | ||||||
|  | 		// TODO use readAddress to read address.
 | ||||||
|  | 		logTrace(eth::Instruction::CALLCODE, {}); | ||||||
|  | 		return arg[0] & 1; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.callDelegate"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		// TODO read args from memory
 | ||||||
|  | 		// TODO use readAddress to read address.
 | ||||||
|  | 		logTrace(eth::Instruction::DELEGATECALL, {}); | ||||||
|  | 		return arg[0] & 1; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.callStatic"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		// TODO read args from memory
 | ||||||
|  | 		// TODO use readAddress to read address.
 | ||||||
|  | 		logTrace(eth::Instruction::STATICCALL, {}); | ||||||
|  | 		return arg[0] & 1; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.storageStore"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		m_state.storage[h256(readU256(arg[0]))] = readU256((arg[1])); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.storageLoad"_yulstring) | ||||||
|  | 		return writeU256(arg[1], m_state.storage[h256(readU256(arg[0]))]); | ||||||
|  | 	else if (_fun == "eth.getCaller"_yulstring) | ||||||
|  | 		// TODO should this only write 20 bytes?
 | ||||||
|  | 		return writeAddress(arg[0], m_state.caller); | ||||||
|  | 	else if (_fun == "eth.getCallValue"_yulstring) | ||||||
|  | 		return writeU128(arg[0], m_state.callvalue); | ||||||
|  | 	else if (_fun == "eth.codeCopy"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		if (accessMemory(arg[0], arg[2])) | ||||||
|  | 			copyZeroExtended( | ||||||
|  | 				m_state.memory, m_state.code, | ||||||
|  | 				size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) | ||||||
|  | 			); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.getCodeSize"_yulstring) | ||||||
|  | 		return writeU256(arg[0], m_state.code.size()); | ||||||
|  | 	else if (_fun == "eth.getBlockCoinbase"_yulstring) | ||||||
|  | 		return writeAddress(arg[0], m_state.coinbase); | ||||||
|  | 	else if (_fun == "eth.create"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		// TODO access memory
 | ||||||
|  | 		// TODO use writeAddress to store resulting address
 | ||||||
|  | 		logTrace(eth::Instruction::CREATE, {}); | ||||||
|  | 		return 0xcccccc + arg[1]; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.getBlockDifficulty"_yulstring) | ||||||
|  | 		return writeU256(arg[0], m_state.difficulty); | ||||||
|  | 	else if (_fun == "eth.externalCodeCopy"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		// TODO use readAddress to read address.
 | ||||||
|  | 		if (accessMemory(arg[1], arg[3])) | ||||||
|  | 			// TODO this way extcodecopy and codecopy do the same thing.
 | ||||||
|  | 			copyZeroExtended( | ||||||
|  | 				m_state.memory, m_state.code, | ||||||
|  | 				size_t(arg[1]), size_t(arg[2]), size_t(arg[3]) | ||||||
|  | 			); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.getExternalCodeSize"_yulstring) | ||||||
|  | 		return u256(keccak256(h256(readAddress(arg[0])))) & 0xffffff; | ||||||
|  | 	else if (_fun == "eth.getGasLeft"_yulstring) | ||||||
|  | 		return 0x99; | ||||||
|  | 	else if (_fun == "eth.getBlockGasLimit"_yulstring) | ||||||
|  | 		return uint64_t(m_state.gaslimit); | ||||||
|  | 	else if (_fun == "eth.getTxGasPrice"_yulstring) | ||||||
|  | 		return writeU128(arg[0], m_state.gasprice); | ||||||
|  | 	else if (_fun == "eth.log"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		logTrace(eth::Instruction::LOG0, {}); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.getBlockNumber"_yulstring) | ||||||
|  | 		return m_state.blockNumber; | ||||||
|  | 	else if (_fun == "eth.getTxOrigin"_yulstring) | ||||||
|  | 		return writeAddress(arg[0], m_state.origin); | ||||||
|  | 	else if (_fun == "eth.finish"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		bytes data; | ||||||
|  | 		if (accessMemory(arg[0], arg[1])) | ||||||
|  | 			data = readMemory(arg[0], arg[1]); | ||||||
|  | 		logTrace(eth::Instruction::RETURN, {}, data); | ||||||
|  | 		throw ExplicitlyTerminated(); | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.revert"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		bytes data; | ||||||
|  | 		if (accessMemory(arg[0], arg[1])) | ||||||
|  | 			data = readMemory(arg[0], arg[1]); | ||||||
|  | 		logTrace(eth::Instruction::REVERT, {}, data); | ||||||
|  | 		throw ExplicitlyTerminated(); | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.getReturnDataSize"_yulstring) | ||||||
|  | 		return m_state.returndata.size(); | ||||||
|  | 	else if (_fun == "eth.returnDataCopy"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		if (arg[1] + arg[2] < arg[1] || arg[1] + arg[2] > m_state.returndata.size()) | ||||||
|  | 			throw ExplicitlyTerminated(); | ||||||
|  | 		if (accessMemory(arg[0], arg[2])) | ||||||
|  | 			copyZeroExtended( | ||||||
|  | 				m_state.memory, m_state.calldata, | ||||||
|  | 				size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) | ||||||
|  | 			); | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.selfDestruct"_yulstring) | ||||||
|  | 	{ | ||||||
|  | 		// TODO use readAddress to read address.
 | ||||||
|  | 		logTrace(eth::Instruction::SELFDESTRUCT, {}); | ||||||
|  | 		throw ExplicitlyTerminated(); | ||||||
|  | 	} | ||||||
|  | 	else if (_fun == "eth.getBlockTimestamp"_yulstring) | ||||||
|  | 		return m_state.timestamp; | ||||||
|  | 
 | ||||||
|  | 	yulAssert(false, "Unknown builtin: " + _fun.str() + " (or implementation did not return)"); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool EWasmBuiltinInterpreter::accessMemory(u256 const& _offset, u256 const& _size) | ||||||
|  | { | ||||||
|  | 	if (((_offset + _size) >= _offset) && ((_offset + _size + 0x1f) >= (_offset + _size))) | ||||||
|  | 	{ | ||||||
|  | 		u256 newSize = (_offset + _size + 0x1f) & ~u256(0x1f); | ||||||
|  | 		m_state.msize = max(m_state.msize, newSize); | ||||||
|  | 		return _size <= 0xffff; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		m_state.msize = u256(-1); | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes EWasmBuiltinInterpreter::readMemory(uint64_t _offset, uint64_t _size) | ||||||
|  | { | ||||||
|  | 	yulAssert(_size <= 0xffff, "Too large read."); | ||||||
|  | 	bytes data(size_t(_size), uint8_t(0)); | ||||||
|  | 	for (size_t i = 0; i < data.size(); ++i) | ||||||
|  | 		data[i] = m_state.memory[_offset + i]; | ||||||
|  | 	return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint64_t EWasmBuiltinInterpreter::readMemoryWord(uint64_t _offset) | ||||||
|  | { | ||||||
|  | 	uint64_t r = 0; | ||||||
|  | 	for (size_t i = 0; i < 8; i++) | ||||||
|  | 		r |= uint64_t(m_state.memory[_offset + i]) << (i * 8); | ||||||
|  | 	return r; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EWasmBuiltinInterpreter::writeMemoryWord(uint64_t _offset, uint64_t _value) | ||||||
|  | { | ||||||
|  | 	for (size_t i = 0; i < 8; i++) | ||||||
|  | 		m_state.memory[_offset + i] = uint8_t((_value >> (i * 8)) & 0xff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u256 EWasmBuiltinInterpreter::writeU256(uint64_t _offset, u256 _value, size_t _croppedTo) | ||||||
|  | { | ||||||
|  | 	accessMemory(_offset, _croppedTo); | ||||||
|  | 	for (size_t i = 0; i < _croppedTo; i++) | ||||||
|  | 	{ | ||||||
|  | 		m_state.memory[_offset + _croppedTo - 1 - i] = uint8_t(_value & 0xff); | ||||||
|  | 		_value >>= 8; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u256 EWasmBuiltinInterpreter::readU256(uint64_t _offset, size_t _croppedTo) | ||||||
|  | { | ||||||
|  | 	accessMemory(_offset, _croppedTo); | ||||||
|  | 	u256 value; | ||||||
|  | 	for (size_t i = 0; i < _croppedTo; i++) | ||||||
|  | 		value = (value << 8) | m_state.memory[_offset + i]; | ||||||
|  | 
 | ||||||
|  | 	return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EWasmBuiltinInterpreter::logTrace(dev::eth::Instruction _instruction, std::vector<u256> const& _arguments, bytes const& _data) | ||||||
|  | { | ||||||
|  | 	logTrace(dev::eth::instructionInfo(_instruction).name, _arguments, _data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EWasmBuiltinInterpreter::logTrace(std::string const& _pseudoInstruction, std::vector<u256> const& _arguments, bytes const& _data) | ||||||
|  | { | ||||||
|  | 	string message = _pseudoInstruction + "("; | ||||||
|  | 	for (size_t i = 0; i < _arguments.size(); ++i) | ||||||
|  | 		message += (i > 0 ? ", " : "") + formatNumber(_arguments[i]); | ||||||
|  | 	message += ")"; | ||||||
|  | 	if (!_data.empty()) | ||||||
|  | 		message += " [" + toHex(_data) + "]"; | ||||||
|  | 	m_state.trace.emplace_back(std::move(message)); | ||||||
|  | 	if (m_state.maxTraceSize > 0 && m_state.trace.size() >= m_state.maxTraceSize) | ||||||
|  | 	{ | ||||||
|  | 		m_state.trace.emplace_back("Trace size limit reached."); | ||||||
|  | 		throw TraceLimitReached(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										108
									
								
								test/tools/yulInterpreter/EWasmBuiltinInterpreter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								test/tools/yulInterpreter/EWasmBuiltinInterpreter.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | /*
 | ||||||
|  | 	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/>.
 | ||||||
|  | */ | ||||||
|  | /**
 | ||||||
|  |  * Yul interpreter module that evaluates EWasm builtins. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <libyul/AsmDataForward.h> | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/CommonData.h> | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | namespace dev | ||||||
|  | { | ||||||
|  | namespace eth | ||||||
|  | { | ||||||
|  | enum class Instruction: uint8_t; | ||||||
|  | } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace yul | ||||||
|  | { | ||||||
|  | class YulString; | ||||||
|  | struct BuiltinFunctionForEVM; | ||||||
|  | 
 | ||||||
|  | namespace test | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | struct InterpreterState; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Interprets EWasm builtins based on the current state and logs instructions with | ||||||
|  |  * side-effects. | ||||||
|  |  * | ||||||
|  |  * Since this is mainly meant to be used for differential fuzz testing, it is focused | ||||||
|  |  * on a single contract only, does not do any gas counting and differs from the correct | ||||||
|  |  * implementation in many ways: | ||||||
|  |  * | ||||||
|  |  * - If memory access to a "large" memory position is performed, a deterministic | ||||||
|  |  *   value is returned. Data that is stored in a "large" memory position is not | ||||||
|  |  *   retained. | ||||||
|  |  * - The blockhash instruction returns a fixed value if the argument is in range. | ||||||
|  |  * - Extcodesize returns a deterministic value depending on the address. | ||||||
|  |  * - Extcodecopy copies a deterministic value depending on the address. | ||||||
|  |  * - And many other things | ||||||
|  |  * | ||||||
|  |  * The main focus is that the generated execution trace is the same for equivalent executions | ||||||
|  |  * and likely to be different for non-equivalent executions. | ||||||
|  |  */ | ||||||
|  | class EWasmBuiltinInterpreter | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	explicit EWasmBuiltinInterpreter(InterpreterState& _state): | ||||||
|  | 		m_state(_state) | ||||||
|  | 	{} | ||||||
|  | 	/// Evaluate builtin function
 | ||||||
|  | 	dev::u256 evalBuiltin(YulString _fun, std::vector<dev::u256> const& _arguments); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	/// Checks if the memory access is not too large for the interpreter and adjusts
 | ||||||
|  | 	/// msize accordingly.
 | ||||||
|  | 	/// @returns false if the amount of bytes read is lager than 0xffff
 | ||||||
|  | 	bool accessMemory(dev::u256 const& _offset, dev::u256 const& _size = 32); | ||||||
|  | 	/// @returns the memory contents at the provided address.
 | ||||||
|  | 	/// Does not adjust msize, use @a accessMemory for that
 | ||||||
|  | 	dev::bytes readMemory(uint64_t _offset, uint64_t _size = 32); | ||||||
|  | 	/// @returns the memory contents at the provided address (little-endian).
 | ||||||
|  | 	/// Does not adjust msize, use @a accessMemory for that
 | ||||||
|  | 	uint64_t readMemoryWord(uint64_t _offset); | ||||||
|  | 	/// Writes a word to memory (little-endian)
 | ||||||
|  | 	/// Does not adjust msize, use @a accessMemory for that
 | ||||||
|  | 	void writeMemoryWord(uint64_t _offset, uint64_t _value); | ||||||
|  | 
 | ||||||
|  | 	/// Helper for eth.* builtins. Writes to memory (big-endian) and always returns zero.
 | ||||||
|  | 	dev::u256 writeU256(uint64_t _offset, dev::u256 _value, size_t _croppedTo = 32); | ||||||
|  | 	dev::u256 writeU128(uint64_t _offset, dev::u256 _value) { return writeU256(_offset, std::move(_value), 16); } | ||||||
|  | 	dev::u256 writeAddress(uint64_t _offset, dev::u256 _value) { return writeU256(_offset, std::move(_value), 20); } | ||||||
|  | 	/// Helper for eth.* builtins. Reads from memory (big-endian) and returns the value;
 | ||||||
|  | 	dev::u256 readU256(uint64_t _offset, size_t _croppedTo = 32); | ||||||
|  | 	dev::u256 readU128(uint64_t _offset) { return readU256(_offset, 16); } | ||||||
|  | 	dev::u256 readAddress(uint64_t _offset) { return readU256(_offset, 20); } | ||||||
|  | 
 | ||||||
|  | 	void logTrace(dev::eth::Instruction _instruction, std::vector<dev::u256> const& _arguments = {}, dev::bytes const& _data = {}); | ||||||
|  | 	/// Appends a log to the trace representing an instruction or similar operation by string,
 | ||||||
|  | 	/// with arguments and auxiliary data (if nonempty).
 | ||||||
|  | 	void logTrace(std::string const& _pseudoInstruction, std::vector<dev::u256> const& _arguments = {}, dev::bytes const& _data = {}); | ||||||
|  | 
 | ||||||
|  | 	InterpreterState& m_state; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | } | ||||||
| @ -21,11 +21,13 @@ | |||||||
| #include <test/tools/yulInterpreter/Interpreter.h> | #include <test/tools/yulInterpreter/Interpreter.h> | ||||||
| 
 | 
 | ||||||
| #include <test/tools/yulInterpreter/EVMInstructionInterpreter.h> | #include <test/tools/yulInterpreter/EVMInstructionInterpreter.h> | ||||||
|  | #include <test/tools/yulInterpreter/EWasmBuiltinInterpreter.h> | ||||||
| 
 | 
 | ||||||
| #include <libyul/AsmData.h> | #include <libyul/AsmData.h> | ||||||
| #include <libyul/Dialect.h> | #include <libyul/Dialect.h> | ||||||
| #include <libyul/Utilities.h> | #include <libyul/Utilities.h> | ||||||
| #include <libyul/backends/evm/EVMDialect.h> | #include <libyul/backends/evm/EVMDialect.h> | ||||||
|  | #include <libyul/backends/wasm/WasmDialect.h> | ||||||
| 
 | 
 | ||||||
| #include <liblangutil/Exceptions.h> | #include <liblangutil/Exceptions.h> | ||||||
| 
 | 
 | ||||||
| @ -233,12 +235,21 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall) | |||||||
| 	evaluateArgs(_funCall.arguments); | 	evaluateArgs(_funCall.arguments); | ||||||
| 
 | 
 | ||||||
| 	if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect)) | 	if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect)) | ||||||
|  | 	{ | ||||||
| 		if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) | 		if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) | ||||||
| 		{ | 		{ | ||||||
| 			EVMInstructionInterpreter interpreter(m_state); | 			EVMInstructionInterpreter interpreter(m_state); | ||||||
| 			setValue(interpreter.evalBuiltin(*fun, values())); | 			setValue(interpreter.evalBuiltin(*fun, values())); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  | 	else if (WasmDialect const* dialect = dynamic_cast<WasmDialect const*>(&m_dialect)) | ||||||
|  | 		if (dialect->builtin(_funCall.functionName.name)) | ||||||
|  | 		{ | ||||||
|  | 			EWasmBuiltinInterpreter interpreter(m_state); | ||||||
|  | 			setValue(interpreter.evalBuiltin(_funCall.functionName.name, values())); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 	auto [functionScopes, fun] = findFunctionAndScope(_funCall.functionName.name); | 	auto [functionScopes, fun] = findFunctionAndScope(_funCall.functionName.name); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user