Copying of arrays from storage to memory.

This commit is contained in:
chriseth
2020-10-08 15:16:13 +02:00
parent 93df3d43df
commit fd6196af16
17 changed files with 208 additions and 3 deletions
+4 -1
View File
@@ -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.
+70 -2
View File
@@ -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)
{
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.");
+4
View File
@@ -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