diff --git a/Changelog.md b/Changelog.md index 575e040d6..65b115f84 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Important Bugfixes: Language Features: * Code Generation: Implement copying recursive structs from storage to memory. + * ABIEncoderV2: Implement encoding of calldata arrays and structs. Compiler Features: diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index d5a5dde0e..41ec073f0 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(); + } }); } @@ -784,29 +831,34 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); - solAssert(_from.dataStoredIn(DataLocation::Memory) || _from.dataStoredIn(DataLocation::Storage), ""); solAssert(!_from.isByteArray(), ""); - solAssert(_from.dataStoredIn(DataLocation::Memory) || _from.baseType()->storageBytes() > 16, ""); + if (_from.dataStoredIn(DataLocation::Storage)) + solAssert(_from.baseType()->storageBytes() > 16, ""); return createFunction(functionName, [&]() { bool dynamic = _to.isDynamicallyEncoded(); bool dynamicBase = _to.baseType()->isDynamicallyEncoded(); - bool inMemory = _from.dataStoredIn(DataLocation::Memory); bool const usesTail = dynamicBase && !_options.dynamicInplace; + EncodingOptions subOptions(_options); + subOptions.encodeFunctionFromStack = false; + subOptions.padded = true; + string elementValues = m_utils.suffixedVariableNameList("elementValue", 0, numVariablesForType(*_from.baseType(), subOptions)); Whiskers templ( usesTail ? R"( // -> - function (value, pos) { - let length := (value) + function (value, pos) { + pos := (pos, length) let headStart := pos let tail := add(pos, mul(length, 0x20)) - let srcPtr := (value) + let baseRef := (value) + let srcPtr := baseRef for { let i := 0 } lt(i, length) { i := add(i, 1) } { mstore(pos, sub(tail, headStart)) - tail := (, tail) + let := + tail := (, tail) srcPtr := (srcPtr) pos := add(pos, 0x20) } @@ -816,13 +868,15 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( )" : R"( // -> - function (value, pos) { - let length := (value) + function (value, pos) { + pos := (pos, length) - let srcPtr := (value) + let baseRef := (value) + let srcPtr := baseRef for { let i := 0 } lt(i, length) { i := add(i, 1) } { - pos := (, pos) + let := + pos := (, pos) srcPtr := (srcPtr) } @@ -830,27 +884,43 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( )" ); templ("functionName", functionName); + templ("elementValues", elementValues); + bool lengthAsArgument = _from.dataStoredIn(DataLocation::CallData) && _from.isDynamicallySized(); + if (lengthAsArgument) + { + templ("maybeLength", " length,"); + templ("declareLength", ""); + } + else + { + templ("maybeLength", ""); + templ("declareLength", "let length := " + m_utils.arrayLengthFunction(_from) + "(value)"); + } templ("readableTypeNameFrom", _from.toString(true)); templ("readableTypeNameTo", _to.toString(true)); templ("return", dynamic ? " -> end " : ""); templ("assignEnd", dynamic ? "end := pos" : ""); - templ("lengthFun", m_utils.arrayLengthFunction(_from)); templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options)); templ("dataAreaFun", m_utils.arrayDataAreaFunction(_from)); - EncodingOptions subOptions(_options); - subOptions.encodeFunctionFromStack = false; - subOptions.padded = true; templ("encodeToMemoryFun", abiEncodeAndReturnUpdatedPosFunction(*_from.baseType(), *_to.baseType(), subOptions)); - if (inMemory) - templ("arrayElementAccess", "mload(srcPtr)"); - else if (_from.baseType()->isValueType()) + switch (_from.location()) { - solAssert(_from.dataStoredIn(DataLocation::Storage), ""); - templ("arrayElementAccess", readFromStorage(*_from.baseType(), 0, false) + "(srcPtr)"); + case DataLocation::Memory: + templ("arrayElementAccess", "mload(srcPtr)"); + break; + case DataLocation::Storage: + if (_from.baseType()->isValueType()) + templ("arrayElementAccess", readFromStorage(*_from.baseType(), 0, false) + "(srcPtr)"); + else + templ("arrayElementAccess", "srcPtr"); + break; + case DataLocation::CallData: + templ("arrayElementAccess", calldataAccessFunction(*_from.baseType()) + "(baseRef, srcPtr)"); + break; + default: + solAssert(false, ""); } - else - templ("arrayElementAccess", "srcPtr"); templ("nextArrayElement", m_utils.nextArrayElementFunction(_from)); return templ.render(); }); @@ -1059,11 +1129,9 @@ string ABIFunctions::abiEncodingFunctionStruct( _to.identifier() + _options.toFunctionNameSuffix(); - solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "Encoding struct from calldata is not yet supported."); solAssert(&_from.structDefinition() == &_to.structDefinition(), ""); return createFunction(functionName, [&]() { - bool fromStorage = _from.location() == DataLocation::Storage; bool dynamic = _to.isDynamicallyEncoded(); Whiskers templ(R"( // -> @@ -1074,7 +1142,7 @@ string ABIFunctions::abiEncodingFunctionStruct( { // - let memberValue := + let := } @@ -1092,7 +1160,7 @@ string ABIFunctions::abiEncodingFunctionStruct( else templ("assignEnd", ""); // to avoid multiple loads from the same slot for subsequent members - templ("init", fromStorage ? "let slotValue := 0" : ""); + templ("init", _from.dataStoredIn(DataLocation::Storage) ? "let slotValue := 0" : ""); u256 previousSlotOffset(-1); u256 encodingOffset = 0; vector> members; @@ -1112,32 +1180,45 @@ string ABIFunctions::abiEncodingFunctionStruct( members.push_back({}); members.back()["preprocess"] = ""; - if (fromStorage) + switch (_from.location()) { - solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), ""); - u256 storageSlotOffset; - size_t intraSlotOffset; - tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name); - if (memberTypeFrom->isValueType()) + case DataLocation::Storage: { - if (storageSlotOffset != previousSlotOffset) + solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), ""); + u256 storageSlotOffset; + size_t intraSlotOffset; + tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name); + if (memberTypeFrom->isValueType()) { - members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; - previousSlotOffset = storageSlotOffset; + if (storageSlotOffset != previousSlotOffset) + { + members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; + previousSlotOffset = storageSlotOffset; + } + members.back()["retrieveValue"] = extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)"; } - members.back()["retrieveValue"] = extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)"; + else + { + solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), ""); + solAssert(intraSlotOffset == 0, ""); + members.back()["retrieveValue"] = "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")"; + } + break; } - else + case DataLocation::Memory: { - solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), ""); - solAssert(intraSlotOffset == 0, ""); - members.back()["retrieveValue"] = "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")"; + string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name)); + members.back()["retrieveValue"] = "mload(add(value, " + sourceOffset + "))"; + break; } - } - else - { - string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name)); - members.back()["retrieveValue"] = "mload(add(value, " + sourceOffset + "))"; + case DataLocation::CallData: + { + string sourceOffset = toCompactHexWithPrefix(_from.calldataOffsetOfMember(member.name)); + members.back()["retrieveValue"] = calldataAccessFunction(*memberTypeFrom) + "(value, add(value, " + sourceOffset + "))"; + break; + } + default: + solAssert(false, ""); } EncodingOptions subOptions(_options); @@ -1145,10 +1226,14 @@ string ABIFunctions::abiEncodingFunctionStruct( // Like with arrays, struct members are always padded. subOptions.padded = true; + string memberValues = m_utils.suffixedVariableNameList("memberValue", 0, numVariablesForType(*memberTypeFrom, subOptions)); + members.back()["memberValues"] = memberValues; + string encode; if (_options.dynamicInplace) - encode = Whiskers{"pos := (memberValue, pos)"} + encode = Whiskers{"pos := (, pos)"} ("encode", abiEncodeAndReturnUpdatedPosFunction(*memberTypeFrom, *memberTypeTo, subOptions)) + ("memberValues", memberValues) .render(); else { @@ -1156,10 +1241,11 @@ string ABIFunctions::abiEncodingFunctionStruct( dynamicMember ? string(R"( mstore(add(pos, ), sub(tail, pos)) - tail := (memberValue, tail) + tail := (, tail) )") : - "(memberValue, add(pos, ))" + "(, add(pos, ))" ); + encodeTempl("memberValues", memberValues); encodeTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset)); encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize(); encodeTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions)); @@ -1616,7 +1702,6 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type, }); } - string ABIFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes) { solUnimplementedAssert(!_splitFunctionTypes, ""); @@ -1662,6 +1747,80 @@ string ABIFunctions::extractFromStorageValue(Type const& _type, size_t _offset, }); } +string ABIFunctions::calldataAccessFunction(Type const& _type) +{ + solAssert(_type.isValueType() || _type.dataStoredIn(DataLocation::CallData), ""); + string functionName = "calldata_access_" + _type.identifier(); + return createFunction(functionName, [&]() { + if (_type.isDynamicallyEncoded()) + { + unsigned int baseEncodedSize = _type.calldataEncodedSize(); + solAssert(baseEncodedSize > 1, ""); + Whiskers w(R"( + function (base_ref, ptr) -> { + let rel_offset_of_tail := calldataload(ptr) + if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { revert(0, 0) } + value := add(rel_offset_of_tail, base_ref) + + } + )"); + if (_type.isDynamicallySized()) + { + auto const* arrayType = dynamic_cast(&_type); + solAssert(!!arrayType, ""); + unsigned int calldataStride = arrayType->calldataStride(); + w("handleLength", Whiskers(R"( + length := calldataload(value) + value := add(value, 0x20) + if gt(length, 0xffffffffffffffff) { revert(0, 0) } + if sgt(base_ref, sub(calldatasize(), mul(length, ))) { revert(0, 0) } + )")("calldataStride", toCompactHexWithPrefix(calldataStride)).render()); + w("return", "value, length"); + } + else + { + w("handleLength", ""); + w("return", "value"); + } + w("neededLength", toCompactHexWithPrefix(baseEncodedSize)); + w("functionName", functionName); + return w.render(); + } + else if (_type.isValueType()) + { + string decodingFunction; + if (auto const* functionType = dynamic_cast(&_type)) + decodingFunction = abiDecodingFunctionFunctionType(*functionType, false, false); + else + decodingFunction = abiDecodingFunctionValueType(_type, false); + // Note that the second argument to the decoding function should be discarded after inlining. + return Whiskers(R"( + function (baseRef, ptr) -> value { + value := (ptr, add(ptr, 32)) + } + )") + ("functionName", functionName) + ("decodingFunction", decodingFunction) + .render(); + } + else + { + solAssert( + _type.category() == Type::Category::Array || + _type.category() == Type::Category::Struct, + "" + ); + return Whiskers(R"( + function (baseRef, ptr) -> value { + value := ptr + } + )") + ("functionName", functionName) + .render(); + } + }); +} + string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options) { string functionName = "array_storeLengthForEncoding_" + _type.identifier() + _options.toFunctionNameSuffix(); diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 079168720..272ca3607 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 @@ -263,6 +265,9 @@ private: /// single variable. std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes); + /// @returns the name of a function that retrieves an element from calldata. + std::string calldataAccessFunction(Type const& _type); + /// @returns the name of a function used during encoding that stores the length /// if the array is dynamically sized (and the options do not request in-place encoding). /// It returns the new encoding position. diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 25a5fce9c..43596189a 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -330,40 +330,53 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type) { string functionName = "array_dataslot_" + _type.identifier(); return m_functionCollector->createFunction(functionName, [&]() { - if (_type.dataStoredIn(DataLocation::Memory)) + switch (_type.location()) { - if (_type.isDynamicallySized()) - return Whiskers(R"( - function (memPtr) -> dataPtr { - dataPtr := add(memPtr, 0x20) - } - )") - ("functionName", functionName) - .render(); - else - return Whiskers(R"( - function (memPtr) -> dataPtr { - dataPtr := memPtr - } - )") - ("functionName", functionName) - .render(); - } - else if (_type.dataStoredIn(DataLocation::Storage)) - { - if (_type.isDynamicallySized()) - { - Whiskers w(R"( - function (slot) -> dataSlot { - mstore(0, slot) - dataSlot := keccak256(0, 0x20) - } - )"); - w("functionName", functionName); - return w.render(); - } - else + case DataLocation::Memory: + if (_type.isDynamicallySized()) + return Whiskers(R"( + function (memPtr) -> dataPtr { + dataPtr := add(memPtr, 0x20) + } + )") + ("functionName", functionName) + .render(); + else + return Whiskers(R"( + function (memPtr) -> dataPtr { + dataPtr := memPtr + } + )") + ("functionName", functionName) + .render(); + case DataLocation::Storage: + if (_type.isDynamicallySized()) + { + Whiskers w(R"( + function (slot) -> dataSlot { + mstore(0, slot) + dataSlot := keccak256(0, 0x20) + } + )"); + w("functionName", functionName); + return w.render(); + } + else + { + Whiskers w(R"( + function (slot) -> dataSlot { + dataSlot := slot + } + )"); + w("functionName", functionName); + return w.render(); + } + case DataLocation::CallData: { + // Calldata arrays are stored as offset of the data area and length + // on the stack, so the offset already points to the data area. + // This might change, if calldata arrays are stored in a single + // stack slot at some point. Whiskers w(R"( function (slot) -> dataSlot { dataSlot := slot @@ -372,11 +385,8 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type) w("functionName", functionName); return w.render(); } - } - else - { - // Not used for calldata - solAssert(false, ""); + default: + solAssert(false, ""); } }); } @@ -384,36 +394,40 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type) string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) { solAssert(!_type.isByteArray(), ""); - solAssert( - _type.location() == DataLocation::Memory || - _type.location() == DataLocation::Storage, - "" - ); - solAssert( - _type.location() == DataLocation::Memory || - _type.baseType()->storageBytes() > 16, - "" - ); + if (_type.dataStoredIn(DataLocation::Storage)) + solAssert(_type.baseType()->storageBytes() > 16, ""); string functionName = "array_nextElement_" + _type.identifier(); return m_functionCollector->createFunction(functionName, [&]() { - if (_type.location() == DataLocation::Memory) - return Whiskers(R"( - function (memPtr) -> nextPtr { - nextPtr := add(memPtr, 0x20) - } - )") - ("functionName", functionName) - .render(); - else if (_type.location() == DataLocation::Storage) - return Whiskers(R"( - function (slot) -> nextSlot { - nextSlot := add(slot, 1) - } - )") - ("functionName", functionName) - .render(); - else - solAssert(false, ""); + switch (_type.location()) + { + case DataLocation::Memory: + return Whiskers(R"( + function (memPtr) -> nextPtr { + nextPtr := add(memPtr, 0x20) + } + )") + ("functionName", functionName) + .render(); + case DataLocation::Storage: + return Whiskers(R"( + function (slot) -> nextSlot { + nextSlot := add(slot, 1) + } + )") + ("functionName", functionName) + .render(); + case DataLocation::CallData: + return Whiskers(R"( + function (calldataPtr) -> nextPtr { + nextPtr := add(calldataPtr, ) + } + )") + ("stride", toCompactHexWithPrefix(_type.baseType()->isDynamicallyEncoded() ? 32 : _type.baseType()->calldataEncodedSize())) + ("functionName", functionName) + .render(); + default: + solAssert(false, ""); + } }); } diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index da5a17cf2..cffa9efbc 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -79,11 +79,11 @@ public: /// The function reverts for too large lengths. std::string arrayAllocationSizeFunction(ArrayType const& _type); /// @returns the name of a function that converts a storage slot number - /// or a memory pointer to the slot number / memory pointer for the data position of an array - /// which is stored in that slot / memory area. + /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer + /// for the data position of an array which is stored in that slot / memory area / calldata area. std::string arrayDataAreaFunction(ArrayType const& _type); /// @returns the name of a function that advances an array data pointer to the next element. - /// Only works for memory arrays and storage arrays that store one item per slot. + /// Only works for memory arrays, calldata arrays and storage arrays that store one item per slot. std::string nextArrayElementFunction(ArrayType const& _type); /// @returns the name of a function that allocates memory. diff --git a/test/cmdlineTests/gas_test_abiv2/output b/test/cmdlineTests/gas_test_abiv2/output index 8815bd743..609d6669f 100644 --- a/test/cmdlineTests/gas_test_abiv2/output +++ b/test/cmdlineTests/gas_test_abiv2/output @@ -2,7 +2,7 @@ ======= gas_test_abiv2/input.sol:C ======= Gas estimation: construction: - 1160 + 1115800 = 1116960 + 1160 + 1119000 = 1120160 external: a(): 530 b(uint256): infinite diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 626876e67..3c913fc01 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -8447,6 +8447,9 @@ BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional) function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { return a[i][j]; } + function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { + return this.test(a, i, j); + } } )"; compileAndRun(sourceCode, 0, "C"); @@ -8464,7 +8467,10 @@ BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional) { ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size())); for (size_t j = 0; j < data[i].size(); j++) + { ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); + ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); + } // out of bounds access ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs()); } @@ -8514,6 +8520,9 @@ BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) function test()" + arrayType + R"( calldata a, uint256 i, uint256 j, uint256 k) external returns (uint256) { return a[i][j][k]; } + function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j, uint256 k) external returns (uint256) { + return this.test(a, i, j, k); + } } )"; compileAndRun(sourceCode, 0, "C"); @@ -8540,7 +8549,10 @@ BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) { ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j].size())); for (size_t k = 0; k < data[i][j].size(); k++) + { ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, k, encoding), encodeArgs(data[i][j][k])); + ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, k, encoding), encodeArgs(data[i][j][k])); + } // out of bounds access ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, data[i][j].size(), encoding), encodeArgs()); } diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol new file mode 100644 index 000000000..6a1bbcffa --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol @@ -0,0 +1,22 @@ +pragma experimental ABIEncoderV2; + +contract C { + function g(uint256[] calldata) external pure returns (bytes memory) { + return msg.data; + } + function f(uint256[][1] calldata s) external view returns (bool) { + bytes memory a = this.g(s[0]); + uint256[] memory m = s[0]; + bytes memory b = this.g(m); + assert(a.length == b.length); + for (uint i = 0; i < a.length; i++) + assert(a[i] == b[i]); + return true; + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256[][1]): 32, 32, 0 -> true +// f(uint256[][1]): 32, 32, 1, 42 -> true +// f(uint256[][1]): 32, 32, 8, 421, 422, 423, 424, 425, 426, 427, 428 -> true diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol new file mode 100644 index 000000000..b2076b370 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol @@ -0,0 +1,33 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint256[] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function g(uint256[] calldata s) external view returns (bytes memory) { + return this.f(s); + } + function h(uint8[] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function i(uint8[] calldata s) external view returns (bytes memory) { + return this.h(s); + } + function j(bytes calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function k(bytes calldata s) external view returns (bytes memory) { + return this.j(s); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256[]): 32, 3, 23, 42, 87 -> 32, 160, 32, 3, 23, 42, 87 +// g(uint256[]): 32, 3, 23, 42, 87 -> 32, 160, 32, 3, 23, 42, 87 +// h(uint8[]): 32, 3, 23, 42, 87 -> 32, 160, 32, 3, 23, 42, 87 +// i(uint8[]): 32, 3, 23, 42, 87 -> 32, 160, 32, 3, 23, 42, 87 +// h(uint8[]): 32, 3, 0xFF23, 0x1242, 0xAB87 -> FAILURE +// i(uint8[]): 32, 3, 0xAB23, 0x1242, 0xFF87 -> FAILURE +// j(bytes): 32, 3, hex"123456" -> 32, 96, 32, 3, left(0x123456) +// k(bytes): 32, 3, hex"AB33FF" -> 32, 96, 32, 3, left(0xAB33FF) diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol new file mode 100644 index 000000000..f2224c803 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol @@ -0,0 +1,34 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint256[] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function g(uint256[][2] calldata s, uint256 which) external view returns (bytes memory) { + return this.f(s[which]); + } + function h(uint8[] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function i(uint8[][2] calldata s, uint256 which) external view returns (bytes memory) { + return this.h(s[which]); + } + function j(bytes calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function k(bytes[2] calldata s, uint256 which) external view returns (bytes memory) { + return this.j(s[which]); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256[]): 32, 3, 42, 23, 87 -> 32, 160, 32, 3, 42, 23, 87 +// g(uint256[][2],uint256): 0x40, 0, 0x40, 0xC0, 3, 42, 23, 87, 4, 11, 13, 17 -> 32, 160, 32, 3, 42, 23, 87 +// g(uint256[][2],uint256): 0x40, 1, 0x40, 0xC0, 3, 42, 23, 87, 4, 11, 13, 17, 27 -> 32, 192, 32, 4, 11, 13, 17, 27 +// h(uint8[]): 32, 3, 42, 23, 87 -> 32, 160, 32, 3, 42, 23, 87 +// i(uint8[][2],uint256): 0x40, 0, 0x40, 0xC0, 3, 42, 23, 87, 4, 11, 13, 17 -> 32, 160, 32, 3, 42, 23, 87 +// i(uint8[][2],uint256): 0x40, 1, 0x40, 0xC0, 3, 42, 23, 87, 4, 11, 13, 17, 27 -> 32, 192, 32, 4, 11, 13, 17, 27 +// j(bytes): 32, 3, hex"AB11FF" -> 32, 96, 32, 3, left(0xAB11FF) +// k(bytes[2],uint256): 0x40, 0, 0x40, 0x63, 3, hex"AB11FF", 4, hex"FF791432" -> 32, 96, 32, 3, left(0xAB11FF) +// k(bytes[2],uint256): 0x40, 1, 0x40, 0x63, 3, hex"AB11FF", 4, hex"FF791432" -> 32, 96, 32, 4, left(0xFF791432) diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_dynamic.sol new file mode 100644 index 000000000..6bb10c143 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_dynamic.sol @@ -0,0 +1,49 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint8[][1][] calldata s) external pure returns (bytes memory) { + return msg.data; + } + function f2(uint256[][2][] calldata s) external pure returns (bytes memory) { + return msg.data; + } + function reenc_f(uint8[][1][] calldata s) external view returns (bytes memory) { + return this.f(s); + } + function reenc_f2(uint256[][2][] calldata s) external view returns (bytes memory) { + return this.f2(s); + } + function g() external returns (bytes memory) { + uint8[][1][] memory m = new uint8[][1][](1); + m[0][0] = new uint8[](1); + m[0][0][0] = 42; + return this.f(m); + } + function h() external returns (bytes memory) { + uint8[][1][] memory m = new uint8[][1][](1); + m[0][0] = new uint8[](1); + m[0][0][0] = 42; + return this.reenc_f(m); + } + function i() external returns (bytes memory) { + uint256[][2][] memory m = new uint256[][2][](1); + m[0][0] = new uint256[](1); + m[0][1] = new uint256[](1); + m[0][0][0] = 42; + m[0][1][0] = 42; + return this.f2(m); + } + function j() external returns (bytes memory) { + uint256[][2][] memory m = new uint256[][2][](1); + m[0][0] = new uint256[](1); + m[0][1] = new uint256[](1); + m[0][0][0] = 42; + m[0][1][0] = 42; + return this.reenc_f2(m); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// g() -> 32, 196, hex"eccb829a", 32, 1, 32, 32, 1, 42, hex"00000000000000000000000000000000000000000000000000000000" +// h() -> 32, 196, hex"eccb829a", 32, 1, 32, 32, 1, 42, hex"00000000000000000000000000000000000000000000000000000000" diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_function_types.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_function_types.sol new file mode 100644 index 000000000..c9ccc88a8 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_function_types.sol @@ -0,0 +1,30 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(function() external returns (uint)[] calldata s) external returns (uint, uint, uint) { + assert(s.length == 3); + return (s[0](), s[1](), s[2]()); + } + function f_reenc(function() external returns (uint)[] calldata s) external returns (uint, uint, uint) { + return this.f(s); + } + function getter1() external returns (uint) { + return 23; + } + function getter2() external returns (uint) { + return 37; + } + function getter3() external returns (uint) { + return 71; + } + function g(bool reenc) external returns (uint, uint, uint) { + function() external returns (uint)[] memory a = new function() external returns (uint)[](3); + a[0] = this.getter1; + a[1] = this.getter2; + a[2] = this.getter3; + return reenc ? this.f_reenc(a) : this.f(a); + } +} +// ---- +// g(bool): false -> 23, 37, 71 +// g(bool): true -> 23, 37, 71 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol new file mode 100644 index 000000000..f981a6e46 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol @@ -0,0 +1,31 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint256[][] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function g(uint256[][] calldata s) external view returns (bytes memory) { + return this.f(s); + } + function h(uint8[][] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function i(uint8[][] calldata s) external view returns (bytes memory) { + return this.h(s); + } + function j(bytes[] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function k(bytes[] calldata s) external view returns (bytes memory) { + return this.j(s); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 13, 17, 23, 4, 27, 31, 37, 41 -> 32, 416, 32, 2, 64, 192, 3, 13, 17, 23, 4, 27, 31, 37, 41 +// g(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 13, 17, 23, 4, 27, 31, 37, 41 -> 32, 416, 32, 2, 64, 192, 3, 13, 17, 23, 4, 27, 31, 37, 41 +// h(uint8[][]): 0x20, 2, 0x40, 0xC0, 3, 13, 17, 23, 4, 27, 31, 37, 41 -> 32, 416, 32, 2, 64, 192, 3, 13, 17, 23, 4, 27, 31, 37, 41 +// i(uint8[][]): 0x20, 2, 0x40, 0xC0, 3, 13, 17, 23, 4, 27, 31, 37, 41 -> 32, 416, 32, 2, 64, 192, 3, 13, 17, 23, 4, 27, 31, 37, 41 +// j(bytes[]): 0x20, 2, 0x40, 0x63, 3, hex"131723", 4, hex"27313741" -> 32, 256, 32, 2, 64, 128, 3, left(0x131723), 4, left(0x27313741) +// k(bytes[]): 0x20, 2, 0x40, 0x63, 3, hex"131723", 4, hex"27313741" -> 32, 256, 32, 2, 64, 128, 3, left(0x131723), 4, left(0x27313741) diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol new file mode 100644 index 000000000..9c6386e31 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol @@ -0,0 +1,25 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint256[3] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function g(uint256[3] calldata s) external view returns (bytes memory) { + return this.f(s); + } + function h(uint8[3] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function i(uint8[3] calldata s) external view returns (bytes memory) { + return this.h(s); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 +// g(uint256[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 +// h(uint8[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 +// i(uint8[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 +// h(uint8[3]): 0xFF23, 0x1242, 0xAB87 -> FAILURE +// i(uint8[3]): 0xAB23, 0x1242, 0xFF87 -> FAILURE diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_dynamic_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_dynamic_static.sol new file mode 100644 index 000000000..6e8361d4a --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_dynamic_static.sol @@ -0,0 +1,49 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint8[1][][1] calldata s) external pure returns (bytes memory) { + return msg.data; + } + function f2(uint256[2][][2] calldata s) external pure returns (bytes memory) { + return msg.data; + } + function reenc_f(uint8[1][][1] calldata s) external view returns (bytes memory) { + return this.f(s); + } + function reenc_f2(uint256[2][][2] calldata s) external view returns (bytes memory) { + return this.f2(s); + } + function g() external returns (bytes memory) { + uint8[1][][1] memory m = [new uint8[1][](1)]; + m[0][0][0] = 42; + return this.f(m); + } + function h() external returns (bytes memory) { + uint8[1][][1] memory m = [new uint8[1][](1)]; + m[0][0][0] = 42; + return this.reenc_f(m); + } + function i() external returns (bytes memory) { + uint256[2][][2] memory m = [new uint256[2][](1),new uint256[2][](1)]; + m[0][0][0] = 0x00042; + m[0][0][1] = 0x00142; + m[1][0][0] = 0x10042; + m[1][0][1] = 0x10142; + return this.f2(m); + } + function j() external returns (bytes memory) { + uint256[2][][2] memory m = [new uint256[2][](1),new uint256[2][](1)]; + m[0][0][0] = 0x00042; + m[0][0][1] = 0x00142; + m[1][0][0] = 0x10042; + m[1][0][1] = 0x10142; + return this.reenc_f2(m); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// g() -> 32, 132, hex"15cfcc01", 32, 32, 1, 42, hex"00000000000000000000000000000000000000000000000000000000" +// h() -> 32, 132, hex"15cfcc01", 32, 32, 1, 42, hex"00000000000000000000000000000000000000000000000000000000" +// i() -> 32, 292, hex"dc0ee233", 32, 64, 160, 1, 0x42, 0x000142, 1, 0x010042, 0x010142, hex"00000000000000000000000000000000000000000000000000000000" +// j() -> 32, 292, hex"dc0ee233", 32, 64, 160, 1, 0x42, 0x000142, 1, 0x010042, 0x010142, hex"00000000000000000000000000000000000000000000000000000000" diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol new file mode 100644 index 000000000..4bf181db3 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol @@ -0,0 +1,25 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint256[3] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function g(uint256[3][2] calldata s, uint256 which) external view returns (bytes memory) { + return this.f(s[which]); + } + function h(uint8[3] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function i(uint8[3][2] calldata s, uint256 which) external view returns (bytes memory) { + return this.h(s[which]); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 +// g(uint256[3][2],uint256): 23, 42, 87, 123, 142, 187, 0 -> 32, 96, 23, 42, 87 +// g(uint256[3][2],uint256): 23, 42, 87, 123, 142, 187, 1 -> 32, 96, 123, 142, 187 +// h(uint8[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 +// i(uint8[3][2],uint256): 23, 42, 87, 123, 142, 187, 0 -> 32, 96, 23, 42, 87 +// i(uint8[3][2],uint256): 23, 42, 87, 123, 142, 187, 1 -> 32, 96, 123, 142, 187 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol new file mode 100644 index 000000000..02322d504 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol @@ -0,0 +1,16 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { uint256[] a; } + function f(S[] calldata s) external pure returns (bytes memory) { + return abi.encode(s); + } + function g(S[] calldata s) external view returns (bytes memory) { + return this.f(s); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f((uint256[])[]): 32, 1, 32, 32, 3, 17, 42, 23 -> 32, 256, 32, 1, 32, 32, 3, 17, 42, 23 +// g((uint256[])[]): 32, 1, 32, 32, 3, 17, 42, 23 -> 32, 256, 32, 1, 32, 32, 3, 17, 42, 23 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol new file mode 100644 index 000000000..16ba40630 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol @@ -0,0 +1,20 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint256[] calldata s1, uint256[] calldata s2, bool which) external pure returns (bytes memory) { + if (which) + return abi.encode(s1); + else + return abi.encode(s2); + } + function g(uint256[] calldata s1, uint256[] calldata s2, bool which) external view returns (bytes memory) { + return this.f(s1, s2, which); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256[],uint256[],bool): 0x60, 0xE0, true, 3, 23, 42, 87, 2, 51, 72 -> 32, 160, 0x20, 3, 23, 42, 87 +// f(uint256[],uint256[],bool): 0x60, 0xE0, false, 3, 23, 42, 87, 2, 51, 72 -> 32, 128, 0x20, 2, 51, 72 +// g(uint256[],uint256[],bool): 0x60, 0xE0, true, 3, 23, 42, 87, 2, 51, 72 -> 32, 160, 0x20, 3, 23, 42, 87 +// g(uint256[],uint256[],bool): 0x60, 0xE0, false, 3, 23, 42, 87, 2, 51, 72 -> 32, 128, 0x20, 2, 51, 72 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol new file mode 100644 index 000000000..a4d1af66d --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol @@ -0,0 +1,20 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(uint256[3] calldata s1, uint256[2] calldata s2, bool which) external pure returns (bytes memory) { + if (which) + return abi.encode(s1); + else + return abi.encode(s2); + } + function g(uint256[3] calldata s1, uint256[2] calldata s2, bool which) external view returns (bytes memory) { + return this.f(s1, s2, which); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256[3],uint256[2],bool): 23, 42, 87, 51, 72, true -> 32, 96, 23, 42, 87 +// f(uint256[3],uint256[2],bool): 23, 42, 87, 51, 72, false -> 32, 64, 51, 72 +// g(uint256[3],uint256[2],bool): 23, 42, 87, 51, 72, true -> 32, 96, 23, 42, 87 +// g(uint256[3],uint256[2],bool): 23, 42, 87, 51, 72, false -> 32, 64, 51, 72 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol new file mode 100644 index 000000000..29ff15692 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { uint256[] a; } + + function f(S calldata s) external returns (bytes memory) { + return abi.encode(s); + } + + function g(S calldata s) external returns (bytes memory) { + return this.f(s); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f((uint256[])): 0x20, 0x20, 3, 42, 23, 17 -> 32, 192, 0x20, 0x20, 3, 42, 23, 17 +// g((uint256[])): 0x20, 0x20, 3, 42, 23, 17 -> 32, 192, 0x20, 0x20, 3, 42, 23, 17 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol new file mode 100644 index 000000000..f369321c3 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { uint256 a; } + + function f(S calldata s) external returns (bytes memory) { + return abi.encode(s); + } + + function g(S calldata s) external returns (bytes memory) { + return this.f(s); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f((uint256)): 3 -> 32, 32, 3 +// g((uint256)): 3 -> 32, 32, 3