mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Copying of arrays from storage to memory.
This commit is contained in:
parent
93df3d43df
commit
fd6196af16
@ -126,7 +126,6 @@ public:
|
|||||||
/// stack slot, it takes exactly that number of values.
|
/// stack slot, it takes exactly that number of values.
|
||||||
std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false);
|
std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false);
|
||||||
|
|
||||||
private:
|
|
||||||
struct EncodingOptions
|
struct EncodingOptions
|
||||||
{
|
{
|
||||||
/// Pad/signextend value types and bytes/string to multiples of 32 bytes.
|
/// Pad/signextend value types and bytes/string to multiples of 32 bytes.
|
||||||
@ -146,6 +145,7 @@ private:
|
|||||||
std::string toFunctionNameSuffix() const;
|
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
|
/// @returns the name of the ABI encoding function with the given type
|
||||||
/// and queues the generation of the function to the requested functions.
|
/// and queues the generation of the function to the requested functions.
|
||||||
/// @param _fromStack if false, the input value was just loaded from storage
|
/// @param _fromStack if false, the input value was just loaded from storage
|
||||||
@ -155,6 +155,7 @@ private:
|
|||||||
Type const& _targetType,
|
Type const& _targetType,
|
||||||
EncodingOptions const& _options
|
EncodingOptions const& _options
|
||||||
);
|
);
|
||||||
|
/// Internal encoding function that is also used by some copying routines.
|
||||||
/// @returns the name of a function that internally calls `abiEncodingFunction`
|
/// @returns the name of a function that internally calls `abiEncodingFunction`
|
||||||
/// but always returns the updated encoding position, even if the type is
|
/// but always returns the updated encoding position, even if the type is
|
||||||
/// statically encoded.
|
/// statically encoded.
|
||||||
@ -163,6 +164,8 @@ private:
|
|||||||
Type const& _targetType,
|
Type const& _targetType,
|
||||||
EncodingOptions const& _options
|
EncodingOptions const& _options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
/// Part of @a abiEncodingFunction for array target type and given calldata array.
|
/// 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
|
/// 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.
|
/// be used for byte arrays and arrays with the base type uint256 or bytes32.
|
||||||
|
@ -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 <functionName>(slot) -> memptr {
|
||||||
|
memptr := <allocateTemp>()
|
||||||
|
let end := <encode>(slot, memptr)
|
||||||
|
mstore(<freeMemoryPointer>, 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 <functionName>(slot) -> memptr {
|
||||||
|
let length := <lengthFunction>(slot)
|
||||||
|
memptr := <allocateArray>(length)
|
||||||
|
let mpos := memptr
|
||||||
|
<?dynamic>mpos := add(mpos, 0x20)</dynamic>
|
||||||
|
let spos := <arrayDataArea>(slot)
|
||||||
|
for { let i := 0 } lt(i, length) { i := add(i, 1) } {
|
||||||
|
mstore(mpos, <convert>(spos))
|
||||||
|
mpos := add(mpos, 0x20)
|
||||||
|
spos := add(spos, <baseStorageSize>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("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)
|
string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType)
|
||||||
{
|
{
|
||||||
solAssert(_keyType.sizeOnStack() <= 1, "");
|
solAssert(_keyType.sizeOnStack() <= 1, "");
|
||||||
@ -2261,8 +2325,12 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
break;
|
break;
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
// Copy the array to a free position in memory, unless it is already in memory.
|
// Copy the array to a free position in memory, unless it is already in memory.
|
||||||
solUnimplementedAssert(from.location() == DataLocation::Memory, "Not implemented yet.");
|
if (from.location() == DataLocation::Memory)
|
||||||
body = "converted := value";
|
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;
|
break;
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
solUnimplemented("Conversion of calldata types not yet implemented.");
|
solUnimplemented("Conversion of calldata types not yet implemented.");
|
||||||
|
@ -220,6 +220,10 @@ public:
|
|||||||
/// Only works for memory arrays, calldata arrays and storage arrays that every item occupies one or multiple full slots.
|
/// 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);
|
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.
|
/// @returns the name of a function that performs index access for mappings.
|
||||||
/// @param _mappingType the type of the mapping
|
/// @param _mappingType the type of the mapping
|
||||||
/// @param _keyType the type of the value provided
|
/// @param _keyType the type of the value provided
|
||||||
|
@ -16,5 +16,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x20, 0x8, -1, -1, 8, -16, -2, 6, 8, -1
|
// test() -> 0x20, 0x8, -1, -1, 8, -16, -2, 6, 8, -1
|
||||||
|
@ -6,5 +6,7 @@ contract C {
|
|||||||
return (b[0], b.length);
|
return (b[0], b.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 1, 3
|
// f() -> 1, 3
|
||||||
|
@ -8,5 +8,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000
|
// test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000
|
||||||
|
@ -8,5 +8,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000
|
// test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000
|
||||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -15,5 +15,7 @@ contract Test {
|
|||||||
return set(data)[1];
|
return set(data)[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07
|
// f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07
|
||||||
|
@ -8,5 +8,7 @@ contract c {
|
|||||||
return keccak256(abi.encodePacked("b", keccak256(data), "a"));
|
return keccak256(abi.encodePacked("b", keccak256(data), "a"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc
|
// foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc
|
||||||
|
@ -9,5 +9,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// foo() -> true
|
// foo() -> true
|
||||||
|
@ -23,6 +23,8 @@ contract test {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(bool): true -> 1
|
// f(bool): true -> 1
|
||||||
// f(bool): false -> 2
|
// f(bool): false -> 2
|
||||||
|
Loading…
Reference in New Issue
Block a user