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