Allow encoding calldata arrays for base types that do not require cleanup.

This commit is contained in:
Daniel Kirchner 2019-03-15 14:08:38 +01:00
parent ee2f566207
commit b0cb330397
2 changed files with 83 additions and 34 deletions

View File

@ -607,19 +607,31 @@ string ABIFunctions::abiEncodingFunction(
solAssert(_from.category() == Type::Category::Array, ""); solAssert(_from.category() == Type::Category::Array, "");
solAssert(to.dataStoredIn(DataLocation::Memory), ""); solAssert(to.dataStoredIn(DataLocation::Memory), "");
ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from); ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from);
if (fromArray.location() == DataLocation::CallData)
return abiEncodingFunctionCalldataArray(fromArray, *toArray, _options); switch (fromArray.location())
else if (!fromArray.isByteArray() && ( {
fromArray.location() == DataLocation::Memory || case DataLocation::CallData:
fromArray.baseType()->storageBytes() > 16 if (
)) fromArray.isByteArray() ||
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options); *fromArray.baseType() == IntegerType::uint256() ||
else if (fromArray.location() == DataLocation::Memory) *fromArray.baseType() == FixedBytesType(32)
return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _options); )
else if (fromArray.location() == DataLocation::Storage) return abiEncodingFunctionCalldataArrayWithoutCleanup(fromArray, *toArray, _options);
return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _options); else
else return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
solAssert(false, ""); case DataLocation::Memory:
if (fromArray.isByteArray())
return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _options);
else
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
case DataLocation::Storage:
if (fromArray.baseType()->storageBytes() <= 16)
return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _options);
else
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
default:
solAssert(false, "");
}
} }
else if (auto const* toStruct = dynamic_cast<StructType const*>(&to)) else if (auto const* toStruct = dynamic_cast<StructType const*>(&to))
{ {
@ -721,19 +733,24 @@ string ABIFunctions::abiEncodeAndReturnUpdatedPosFunction(
}); });
} }
string ABIFunctions::abiEncodingFunctionCalldataArray( string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
Type const& _from, Type const& _from,
Type const& _to, Type const& _to,
EncodingOptions const& _options EncodingOptions const& _options
) )
{ {
solAssert(_to.isDynamicallySized(), "");
solAssert(_from.category() == Type::Category::Array, "Unknown dynamic type."); solAssert(_from.category() == Type::Category::Array, "Unknown dynamic type.");
solAssert(_to.category() == Type::Category::Array, "Unknown dynamic type."); solAssert(_to.category() == Type::Category::Array, "Unknown dynamic type.");
auto const& fromArrayType = dynamic_cast<ArrayType const&>(_from); auto const& fromArrayType = dynamic_cast<ArrayType const&>(_from);
auto const& toArrayType = dynamic_cast<ArrayType const&>(_to); auto const& toArrayType = dynamic_cast<ArrayType const&>(_to);
solAssert(fromArrayType.location() == DataLocation::CallData, ""); solAssert(fromArrayType.location() == DataLocation::CallData, "");
solAssert(
fromArrayType.isByteArray() ||
*fromArrayType.baseType() == IntegerType::uint256() ||
*fromArrayType.baseType() == FixedBytesType(32),
"");
solAssert(fromArrayType.calldataStride() == toArrayType.memoryStride(), "");
solAssert( solAssert(
*fromArrayType.copyForLocation(DataLocation::Memory, true) == *fromArrayType.copyForLocation(DataLocation::Memory, true) ==
@ -748,24 +765,54 @@ string ABIFunctions::abiEncodingFunctionCalldataArray(
_to.identifier() + _to.identifier() +
_options.toFunctionNameSuffix(); _options.toFunctionNameSuffix();
return createFunction(functionName, [&]() { return createFunction(functionName, [&]() {
solUnimplementedAssert(fromArrayType.isByteArray(), "Only byte arrays can be encoded from calldata currently."); bool needsPadding = _options.padded && fromArrayType.isByteArray();
// TODO if this is not a byte array, we might just copy byte-by-byte anyway, if (fromArrayType.isDynamicallySized())
// because the encoding is position-independent, but we have to check that. {
Whiskers templ(R"( Whiskers templ(R"(
// <readableTypeNameFrom> -> <readableTypeNameTo> // <readableTypeNameFrom> -> <readableTypeNameTo>
function <functionName>(start, length, pos) -> end { function <functionName>(start, length, pos) -> end {
pos := <storeLength>(pos, length) pos := <storeLength>(pos, length)
<copyFun>(start, pos, length) <scaleLengthByStride>
end := add(pos, <lengthPadded>) <copyFun>(start, pos, length)
} end := add(pos, <lengthPadded>)
)"); }
templ("storeLength", arrayStoreLengthForEncodingFunction(toArrayType, _options)); )");
templ("functionName", functionName); templ("storeLength", arrayStoreLengthForEncodingFunction(toArrayType, _options));
templ("readableTypeNameFrom", _from.toString(true)); templ("functionName", functionName);
templ("readableTypeNameTo", _to.toString(true)); if (fromArrayType.isByteArray() || fromArrayType.calldataStride() == 1)
templ("copyFun", m_utils.copyToMemoryFunction(true)); templ("scaleLengthByStride", "");
templ("lengthPadded", _options.padded ? m_utils.roundUpFunction() + "(length)" : "length"); else
return templ.render(); templ("scaleLengthByStride",
Whiskers(R"(
if gt(length, <maxLength>) { revert(0, 0) }
length := mul(length, <stride>)
)")
("stride", toCompactHexWithPrefix(fromArrayType.calldataStride()))
("maxLength", toCompactHexWithPrefix(u256(-1) / fromArrayType.calldataStride()))
.render()
);
templ("readableTypeNameFrom", _from.toString(true));
templ("readableTypeNameTo", _to.toString(true));
templ("copyFun", m_utils.copyToMemoryFunction(true));
templ("lengthPadded", needsPadding ? m_utils.roundUpFunction() + "(length)" : "length");
return templ.render();
}
else
{
solAssert(fromArrayType.calldataStride() == 32, "");
Whiskers templ(R"(
// <readableTypeNameFrom> -> <readableTypeNameTo>
function <functionName>(start, pos) {
<copyFun>(start, pos, <byteLength>)
}
)");
templ("functionName", functionName);
templ("readableTypeNameFrom", _from.toString(true));
templ("readableTypeNameTo", _to.toString(true));
templ("copyFun", m_utils.copyToMemoryFunction(true));
templ("byteLength", toCompactHexWithPrefix(fromArrayType.length() * fromArrayType.calldataStride()));
return templ.render();
}
}); });
} }

View File

@ -176,7 +176,9 @@ private:
EncodingOptions const& _options EncodingOptions const& _options
); );
/// Part of @a abiEncodingFunction for array target type and given calldata array. /// Part of @a abiEncodingFunction for array target type and given calldata array.
std::string abiEncodingFunctionCalldataArray( /// 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.
std::string abiEncodingFunctionCalldataArrayWithoutCleanup(
Type const& _givenType, Type const& _givenType,
Type const& _targetType, Type const& _targetType,
EncodingOptions const& _options EncodingOptions const& _options