mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Implement ABI encoding of calldata arrays and structs.
This commit is contained in:
parent
d82157d46a
commit
91a2a9a9c3
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
* Code Generation: Implement copying recursive structs from storage to memory.
|
* Code Generation: Implement copying recursive structs from storage to memory.
|
||||||
|
* ABIEncoderV2: Implement encoding of calldata arrays and structs.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
@ -831,29 +831,34 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
|
|
||||||
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
|
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
|
||||||
solAssert(_from.length() == _to.length(), "");
|
solAssert(_from.length() == _to.length(), "");
|
||||||
solAssert(_from.dataStoredIn(DataLocation::Memory) || _from.dataStoredIn(DataLocation::Storage), "");
|
|
||||||
solAssert(!_from.isByteArray(), "");
|
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, [&]() {
|
return createFunction(functionName, [&]() {
|
||||||
bool dynamic = _to.isDynamicallyEncoded();
|
bool dynamic = _to.isDynamicallyEncoded();
|
||||||
bool dynamicBase = _to.baseType()->isDynamicallyEncoded();
|
bool dynamicBase = _to.baseType()->isDynamicallyEncoded();
|
||||||
bool inMemory = _from.dataStoredIn(DataLocation::Memory);
|
|
||||||
bool const usesTail = dynamicBase && !_options.dynamicInplace;
|
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(
|
Whiskers templ(
|
||||||
usesTail ?
|
usesTail ?
|
||||||
R"(
|
R"(
|
||||||
// <readableTypeNameFrom> -> <readableTypeNameTo>
|
// <readableTypeNameFrom> -> <readableTypeNameTo>
|
||||||
function <functionName>(value, pos) <return> {
|
function <functionName>(value,<maybeLength> pos) <return> {
|
||||||
let length := <lengthFun>(value)
|
<declareLength>
|
||||||
pos := <storeLength>(pos, length)
|
pos := <storeLength>(pos, length)
|
||||||
let headStart := pos
|
let headStart := pos
|
||||||
let tail := add(pos, mul(length, 0x20))
|
let tail := add(pos, mul(length, 0x20))
|
||||||
let srcPtr := <dataAreaFun>(value)
|
let baseRef := <dataAreaFun>(value)
|
||||||
|
let srcPtr := baseRef
|
||||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||||
{
|
{
|
||||||
mstore(pos, sub(tail, headStart))
|
mstore(pos, sub(tail, headStart))
|
||||||
tail := <encodeToMemoryFun>(<arrayElementAccess>, tail)
|
let <elementValues> := <arrayElementAccess>
|
||||||
|
tail := <encodeToMemoryFun>(<elementValues>, tail)
|
||||||
srcPtr := <nextArrayElement>(srcPtr)
|
srcPtr := <nextArrayElement>(srcPtr)
|
||||||
pos := add(pos, 0x20)
|
pos := add(pos, 0x20)
|
||||||
}
|
}
|
||||||
@ -863,13 +868,15 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
)" :
|
)" :
|
||||||
R"(
|
R"(
|
||||||
// <readableTypeNameFrom> -> <readableTypeNameTo>
|
// <readableTypeNameFrom> -> <readableTypeNameTo>
|
||||||
function <functionName>(value, pos) <return> {
|
function <functionName>(value,<maybeLength> pos) <return> {
|
||||||
let length := <lengthFun>(value)
|
<declareLength>
|
||||||
pos := <storeLength>(pos, length)
|
pos := <storeLength>(pos, length)
|
||||||
let srcPtr := <dataAreaFun>(value)
|
let baseRef := <dataAreaFun>(value)
|
||||||
|
let srcPtr := baseRef
|
||||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||||
{
|
{
|
||||||
pos := <encodeToMemoryFun>(<arrayElementAccess>, pos)
|
let <elementValues> := <arrayElementAccess>
|
||||||
|
pos := <encodeToMemoryFun>(<elementValues>, pos)
|
||||||
srcPtr := <nextArrayElement>(srcPtr)
|
srcPtr := <nextArrayElement>(srcPtr)
|
||||||
}
|
}
|
||||||
<assignEnd>
|
<assignEnd>
|
||||||
@ -877,27 +884,43 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
)"
|
)"
|
||||||
);
|
);
|
||||||
templ("functionName", functionName);
|
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("readableTypeNameFrom", _from.toString(true));
|
||||||
templ("readableTypeNameTo", _to.toString(true));
|
templ("readableTypeNameTo", _to.toString(true));
|
||||||
templ("return", dynamic ? " -> end " : "");
|
templ("return", dynamic ? " -> end " : "");
|
||||||
templ("assignEnd", dynamic ? "end := pos" : "");
|
templ("assignEnd", dynamic ? "end := pos" : "");
|
||||||
templ("lengthFun", m_utils.arrayLengthFunction(_from));
|
|
||||||
templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
|
templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
|
||||||
templ("dataAreaFun", m_utils.arrayDataAreaFunction(_from));
|
templ("dataAreaFun", m_utils.arrayDataAreaFunction(_from));
|
||||||
|
|
||||||
EncodingOptions subOptions(_options);
|
|
||||||
subOptions.encodeFunctionFromStack = false;
|
|
||||||
subOptions.padded = true;
|
|
||||||
templ("encodeToMemoryFun", abiEncodeAndReturnUpdatedPosFunction(*_from.baseType(), *_to.baseType(), subOptions));
|
templ("encodeToMemoryFun", abiEncodeAndReturnUpdatedPosFunction(*_from.baseType(), *_to.baseType(), subOptions));
|
||||||
if (inMemory)
|
switch (_from.location())
|
||||||
templ("arrayElementAccess", "mload(srcPtr)");
|
|
||||||
else if (_from.baseType()->isValueType())
|
|
||||||
{
|
{
|
||||||
solAssert(_from.dataStoredIn(DataLocation::Storage), "");
|
case DataLocation::Memory:
|
||||||
templ("arrayElementAccess", readFromStorage(*_from.baseType(), 0, false) + "(srcPtr)");
|
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));
|
templ("nextArrayElement", m_utils.nextArrayElementFunction(_from));
|
||||||
return templ.render();
|
return templ.render();
|
||||||
});
|
});
|
||||||
@ -1189,7 +1212,11 @@ string ABIFunctions::abiEncodingFunctionStruct(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
solUnimplementedAssert(false, "Encoding struct from calldata is not yet supported.");
|
{
|
||||||
|
string sourceOffset = toCompactHexWithPrefix(_from.calldataOffsetOfMember(member.name));
|
||||||
|
members.back()["retrieveValue"] = calldataAccessFunction(*memberTypeFrom) + "(value, add(value, " + sourceOffset + "))";
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
@ -1675,7 +1702,6 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string ABIFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
string ABIFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
@ -1721,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 <functionName>(base_ref, ptr) -> <return> {
|
||||||
|
let rel_offset_of_tail := calldataload(ptr)
|
||||||
|
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { revert(0, 0) }
|
||||||
|
value := add(rel_offset_of_tail, base_ref)
|
||||||
|
<handleLength>
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
if (_type.isDynamicallySized())
|
||||||
|
{
|
||||||
|
auto const* arrayType = dynamic_cast<ArrayType const*>(&_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, <calldataStride>))) { 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<FunctionType const*>(&_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 <functionName>(baseRef, ptr) -> value {
|
||||||
|
value := <decodingFunction>(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 <functionName>(baseRef, ptr) -> value {
|
||||||
|
value := ptr
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options)
|
string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options)
|
||||||
{
|
{
|
||||||
string functionName = "array_storeLengthForEncoding_" + _type.identifier() + _options.toFunctionNameSuffix();
|
string functionName = "array_storeLengthForEncoding_" + _type.identifier() + _options.toFunctionNameSuffix();
|
||||||
|
@ -265,6 +265,9 @@ private:
|
|||||||
/// single variable.
|
/// single variable.
|
||||||
std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
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
|
/// @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).
|
/// if the array is dynamically sized (and the options do not request in-place encoding).
|
||||||
/// It returns the new encoding position.
|
/// It returns the new encoding position.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
======= gas_test_abiv2/input.sol:C =======
|
======= gas_test_abiv2/input.sol:C =======
|
||||||
Gas estimation:
|
Gas estimation:
|
||||||
construction:
|
construction:
|
||||||
1160 + 1115800 = 1116960
|
1160 + 1119000 = 1120160
|
||||||
external:
|
external:
|
||||||
a(): 530
|
a(): 530
|
||||||
b(uint256): infinite
|
b(uint256): infinite
|
||||||
|
@ -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) {
|
function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) {
|
||||||
return a[i][j];
|
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");
|
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()));
|
ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size()));
|
||||||
for (size_t j = 0; j < data[i].size(); j++)
|
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("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
|
// out of bounds access
|
||||||
ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs());
|
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) {
|
function test()" + arrayType + R"( calldata a, uint256 i, uint256 j, uint256 k) external returns (uint256) {
|
||||||
return a[i][j][k];
|
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");
|
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()));
|
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++)
|
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("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
|
// out of bounds access
|
||||||
ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, data[i][j].size(), encoding), encodeArgs());
|
ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, data[i][j].size(), encoding), encodeArgs());
|
||||||
}
|
}
|
||||||
|
@ -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
|
@ -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)
|
@ -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)
|
@ -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"
|
@ -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
|
@ -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)
|
@ -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
|
@ -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"
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user