diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index e3a7e6f18..e34abe852 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -126,7 +126,6 @@ public: /// stack slot, it takes exactly that number of values. std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false); -private: struct EncodingOptions { /// Pad/signextend value types and bytes/string to multiples of 32 bytes. @@ -146,6 +145,7 @@ private: std::string toFunctionNameSuffix() const; }; + /// Internal encoding function that is also used by some copying routines. /// @returns the name of the ABI encoding function with the given type /// and queues the generation of the function to the requested functions. /// @param _fromStack if false, the input value was just loaded from storage @@ -155,6 +155,7 @@ private: Type const& _targetType, EncodingOptions const& _options ); + /// Internal encoding function that is also used by some copying routines. /// @returns the name of a function that internally calls `abiEncodingFunction` /// but always returns the updated encoding position, even if the type is /// statically encoded. @@ -163,6 +164,8 @@ private: Type const& _targetType, EncodingOptions const& _options ); + +private: /// Part of @a abiEncodingFunction for array target type and given calldata array. /// Uses calldatacopy and does not perform cleanup or validation and can therefore only /// be used for byte arrays and arrays with the base type uint256 or bytes32. diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 285f05298..36a66a920 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1446,6 +1446,70 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) }); } +string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to) +{ + solAssert(_from.dataStoredIn(DataLocation::Storage), ""); + solAssert(_to.dataStoredIn(DataLocation::Memory), ""); + solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); + if (!_from.isDynamicallySized()) + solAssert(_from.length() == _to.length(), ""); + + string functionName = "copy_array_from_storage_to_memory_" + _from.identifier(); + + return m_functionCollector.createFunction(functionName, [&]() { + if (_from.baseType()->isValueType()) + { + solAssert(_from.baseType() == _to.baseType(), ""); + ABIFunctions abi(m_evmVersion, m_revertStrings, m_functionCollector); + return Whiskers(R"( + function (slot) -> memptr { + memptr := () + let end := (slot, memptr) + mstore(, end) + } + )") + ("functionName", functionName) + ("allocateTemp", allocationTemporaryMemoryFunction()) + ( + "encode", + abi.abiEncodeAndReturnUpdatedPosFunction(_from, _to, ABIFunctions::EncodingOptions{}) + ) + ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) + .render(); + } + else + { + solAssert(_to.memoryStride() == 32, ""); + solAssert(_to.baseType()->dataStoredIn(DataLocation::Memory), ""); + solAssert(_from.baseType()->dataStoredIn(DataLocation::Storage), ""); + solAssert(!_from.isByteArray(), ""); + solAssert(*_to.withLocation(DataLocation::Storage, _from.isPointer()) == _from, ""); + return Whiskers(R"( + function (slot) -> memptr { + let length := (slot) + memptr := (length) + let mpos := memptr + mpos := add(mpos, 0x20) + let spos := (slot) + for { let i := 0 } lt(i, length) { i := add(i, 1) } { + mstore(mpos, (spos)) + mpos := add(mpos, 0x20) + spos := add(spos, ) + } + } + )") + ("functionName", functionName) + ("lengthFunction", arrayLengthFunction(_from)) + ("allocateArray", allocateMemoryArrayFunction(_to)) + ("arrayDataArea", arrayDataAreaFunction(_from)) + ("dynamic", _to.isDynamicallySized()) + ("convert", conversionFunction(*_from.baseType(), *_to.baseType())) + ("baseStorageSize", _from.baseType()->storageSize().str()) + .render(); + } + }); +} + string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType) { solAssert(_keyType.sizeOnStack() <= 1, ""); @@ -2261,8 +2325,12 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) break; case DataLocation::Memory: // Copy the array to a free position in memory, unless it is already in memory. - solUnimplementedAssert(from.location() == DataLocation::Memory, "Not implemented yet."); - body = "converted := value"; + if (from.location() == DataLocation::Memory) + body = "converted := value"; + else if (from.location() == DataLocation::CallData) + solUnimplemented("Conversion of calldata types not yet implemented."); + else + body = "converted := " + copyArrayFromStorageToMemoryFunction(from, to) + "(value)"; break; case DataLocation::CallData: solUnimplemented("Conversion of calldata types not yet implemented."); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 351c857d4..255ddb0a5 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -220,6 +220,10 @@ public: /// Only works for memory arrays, calldata arrays and storage arrays that every item occupies one or multiple full slots. std::string nextArrayElementFunction(ArrayType const& _type); + /// @returns the name of a function that allocates a memory array and copies the contents + /// of the storage array into it. + std::string copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to); + /// @returns the name of a function that performs index access for mappings. /// @param _mappingType the type of the mapping /// @param _keyType the type of the value provided diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol b/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol index 0e225f1e5..1d2c978fb 100644 --- a/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol +++ b/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol @@ -16,5 +16,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 0x20, 0x8, -1, -1, 8, -16, -2, 6, 8, -1 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_to_memory.sol b/test/libsolidity/semanticTests/array/array_copy_storage_to_memory.sol index 8bc6b86a2..cd3c184d2 100644 --- a/test/libsolidity/semanticTests/array/array_copy_storage_to_memory.sol +++ b/test/libsolidity/semanticTests/array/array_copy_storage_to_memory.sol @@ -6,5 +6,7 @@ contract C { return (b[0], b.length); } } +// ==== +// compileViaYul: also // ---- // f() -> 1, 3 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol b/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol index 2589f1f55..49950aedc 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol +++ b/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol @@ -8,5 +8,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol b/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol index 1f6d500bd..1d4657073 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol +++ b/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol @@ -8,5 +8,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested.sol new file mode 100644 index 000000000..59f5ecffe --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested.sol @@ -0,0 +1,21 @@ +contract C { + uint72[5][] a; + + function f() public returns (uint72, uint72, uint72, uint72, uint72, uint72, uint72) { + for (uint i = 0; i < 4; i++) + a.push(); + a[0][0] = 1; + a[0][3] = 2; + a[1][1] = 3; + a[1][4] = 4; + a[2][0] = 5; + a[3][2] = 6; + a[3][3] = 7; + uint72[5][] memory m = a; + return (m[0][0], m[0][3], m[1][1], m[1][4], m[2][0], m[3][2], m[3][3]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2, 3, 4, 5, 6, 7 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol new file mode 100644 index 000000000..e71a68c76 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol @@ -0,0 +1,13 @@ +pragma experimental ABIEncoderV2; +contract C { + bytes[] a; + + function f() public returns (bytes[] memory) { + a.push("abc"); + a.push("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ"); + bytes[] memory m = a; + return m; + } +} +// ---- +// f() -> 0x20, 0x02, 0x40, 0x80, 3, 0x6162630000000000000000000000000000000000000000000000000000000000, 0x99, 44048183304486788312148433451363384677562265908331949128489393215789685032262, 32241931068525137014058842823026578386641954854143559838526554899205067598957, 49951309422467613961193228765530489307475214998374779756599339590522149884499, 0x54555658595a6162636465666768696a6b6c6d6e6f707172737475767778797a, 0x4142434445464748494a4b4c4d4e4f5051525354555658595a00000000000000 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_from_pointer.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_from_pointer.sol new file mode 100644 index 000000000..6a687bdcf --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_from_pointer.sol @@ -0,0 +1,22 @@ +contract C { + uint72[5][] a; + + function f() public returns (uint72, uint72, uint72, uint72, uint72, uint72, uint72) { + for (uint i = 0; i < 4; i++) + a.push(); + a[0][0] = 1; + a[0][3] = 2; + a[1][1] = 3; + a[1][4] = 4; + a[2][0] = 5; + a[3][2] = 6; + a[3][3] = 7; + uint72[5][] storage a_ = a; + uint72[5][] memory m = a_; + return (m[0][0], m[0][3], m[1][1], m[1][4], m[2][0], m[3][2], m[3][3]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2, 3, 4, 5, 6, 7 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_struct.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_struct.sol new file mode 100644 index 000000000..4da2fa359 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_struct.sol @@ -0,0 +1,26 @@ +contract C { + struct T { uint8 x; uint8 y; uint[] z; } + T[3][] a; + + function f() public returns (uint8, uint8, uint, uint, uint, uint8, uint8, uint, uint, uint) { + a.push(); + a.push(); + a[0][1].x = 11; + a[0][1].y = 12; + a[0][1].z.push(1); + a[0][1].z.push(2); + a[0][1].z.push(3); + a[1][2].x = 21; + a[1][2].y = 22; + a[1][2].z.push(4); + a[1][2].z.push(5); + a[1][2].z.push(6); + T[3][] memory m = a; + return ( + m[0][1].x, m[0][1].y, m[0][1].z[0], m[0][1].z[1], m[0][1].z[2], + m[1][2].x, m[1][2].y, m[1][2].z[0], m[1][2].z[1], m[1][2].z[2] + ); + } +} +// ---- +// f() -> 11, 0x0c, 1, 2, 3, 0x15, 22, 4, 5, 6 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_packed.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_packed.sol new file mode 100644 index 000000000..bd4273220 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_packed.sol @@ -0,0 +1,15 @@ +contract C { + uint8[33] a; + + function f() public returns (uint8, uint8, uint8) { + a[0] = 2; + a[16] = 3; + a[32] = 4; + uint8[33] memory m = a; + return (m[0], m[16], m[32]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_packed_dyn.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_packed_dyn.sol new file mode 100644 index 000000000..d4ef03fbd --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_packed_dyn.sol @@ -0,0 +1,17 @@ +contract C { + uint8[] a; + + function f() public returns (uint8, uint8, uint8) { + for (uint i = 0; i < 33; i++) + a.push(7); + a[0] = 2; + a[16] = 3; + a[32] = 4; + uint8[] memory m = a; + return (m[0], m[16], m[32]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol b/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol index be824b759..046be080c 100644 --- a/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol +++ b/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol @@ -15,5 +15,7 @@ contract Test { return set(data)[1]; } } +// ==== +// compileViaYul: also // ---- // f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol index 91a26945c..9f34a5282 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol @@ -8,5 +8,7 @@ contract c { return keccak256(abi.encodePacked("b", keccak256(data), "a")); } } +// ==== +// compileViaYul: also // ---- // foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol index 725d984d8..b284b7f01 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol @@ -9,5 +9,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // foo() -> true diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_2.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_2.sol index 8633d2bfa..f522ca6e5 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_2.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_2.sol @@ -23,6 +23,8 @@ contract test { return ret; } } +// ==== +// compileViaYul: also // ---- // f(bool): true -> 1 // f(bool): false -> 2