diff --git a/Changelog.md b/Changelog.md index 5a47d9814..e33cf0d71 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language 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: diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index ebdc81a38..1412dea77 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -181,3 +182,15 @@ bool dev::isValidDecimal(string const& _string) return false; 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 + "\""; +} diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index d6e8f349e..02b037ce8 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -290,7 +290,6 @@ void iterateReplacing(std::vector& _vector, F const& _f) _vector = std::move(modifiedVector); } - namespace detail { template @@ -355,6 +354,10 @@ std::string getChecksummedAddress(std::string const& _addr); bool isValidHex(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 bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) { diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 03dc40c5b..224d55449 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -975,7 +976,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( for (size_t i = 0; i < words; ++i) { 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); return templ.render(); @@ -990,7 +991,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( } )"); templ("functionName", functionName); - templ("wordValue", "0x" + h256(value, h256::AlignLeft).hex()); + templ("wordValue", formatAsStringOrNumber(value)); return templ.render(); } }); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 8910ad7e7..1699bf9d3 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -23,6 +23,8 @@ #include #include #include + +#include #include #include @@ -1756,7 +1758,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const for (size_t i = 0; i < words; ++i) { 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); return templ.render(); diff --git a/test/cmdlineTests/yul_string_format_ascii/input.json b/test/cmdlineTests/yul_string_format_ascii/input.json new file mode 100644 index 000000000..c23c65b5a --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii/input.json @@ -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"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json new file mode 100644 index 000000000..40ed5700f --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -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}}} diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json new file mode 100644 index 000000000..247f665cb --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json @@ -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"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json new file mode 100644 index 000000000..062cdfdc9 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -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}}} diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json new file mode 100644 index 000000000..c7309f2af --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json @@ -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"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json new file mode 100644 index 000000000..7c163ff12 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -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}}} diff --git a/test/cmdlineTests/yul_string_format_ascii_long/input.json b/test/cmdlineTests/yul_string_format_ascii_long/input.json new file mode 100644 index 000000000..cf3b2a854 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_long/input.json @@ -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"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json new file mode 100644 index 000000000..8245d0dd9 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -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}}} diff --git a/test/cmdlineTests/yul_string_format_hex/input.json b/test/cmdlineTests/yul_string_format_hex/input.json new file mode 100644 index 000000000..5ba723f56 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_hex/input.json @@ -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"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json new file mode 100644 index 000000000..38cbd08ea --- /dev/null +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -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}}} diff --git a/test/libsolidity/semanticTests/viaYul/string_format.sol b/test/libsolidity/semanticTests/viaYul/string_format.sol new file mode 100644 index 000000000..2d9d71d7c --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/string_format.sol @@ -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)