Merge pull request #10391 from ethereum/activateNestedArrayCopyingCalldata2Memory

Support copying of nested calldata arrays to memory.
This commit is contained in:
chriseth 2020-11-26 12:41:53 +01:00 committed by GitHub
commit 39adbfc0cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 116 additions and 60 deletions

View File

@ -2,6 +2,7 @@
Language Features: Language Features:
* Code generator: Support copying dynamically encoded structs from calldata to memory. * Code generator: Support copying dynamically encoded structs from calldata to memory.
* Code generator: Support copying of nested arrays from calldata to memory.
* The fallback function can now also have a single ``calldata`` argument (equaling ``msg.data``) and return ``bytes memory`` (which will not be ABI-encoded but returned as-is). * The fallback function can now also have a single ``calldata`` argument (equaling ``msg.data``) and return ``bytes memory`` (which will not be ABI-encoded but returned as-is).
Compiler Features: Compiler Features:

View File

@ -941,8 +941,8 @@ void CompilerUtils::convertType(
case Type::Category::Array: case Type::Category::Array:
{ {
solAssert(targetTypeCategory == stackTypeCategory, ""); solAssert(targetTypeCategory == stackTypeCategory, "");
ArrayType const& typeOnStack = dynamic_cast<ArrayType const&>(_typeOnStack); auto const& typeOnStack = dynamic_cast<ArrayType const&>(_typeOnStack);
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_targetType); auto const& targetType = dynamic_cast<ArrayType const&>(_targetType);
switch (targetType.location()) switch (targetType.location())
{ {
case DataLocation::Storage: case DataLocation::Storage:
@ -957,6 +957,25 @@ void CompilerUtils::convertType(
{ {
// Copy the array to a free position in memory, unless it is already in memory. // Copy the array to a free position in memory, unless it is already in memory.
if (typeOnStack.location() != DataLocation::Memory) if (typeOnStack.location() != DataLocation::Memory)
{
if (
typeOnStack.dataStoredIn(DataLocation::CallData) &&
typeOnStack.baseType()->isDynamicallyEncoded()
)
{
solAssert(m_context.useABICoderV2(), "");
// stack: offset length(optional in case of dynamically sized array)
solAssert(typeOnStack.sizeOnStack() == (typeOnStack.isDynamicallySized() ? 2 : 1), "");
if (typeOnStack.isDynamicallySized())
m_context << Instruction::SWAP1;
m_context.callYulFunction(
m_context.utilFunctions().conversionFunction(typeOnStack, targetType),
typeOnStack.isDynamicallySized() ? 2 : 1,
1
);
}
else
{ {
// stack: <source ref> (variably sized) // stack: <source ref> (variably sized)
unsigned stackSize = typeOnStack.sizeOnStack(); unsigned stackSize = typeOnStack.sizeOnStack();
@ -986,14 +1005,6 @@ void CompilerUtils::convertType(
} }
else else
{ {
if (auto baseType = dynamic_cast<ArrayType const*>(typeOnStack.baseType()))
solUnimplementedAssert(
typeOnStack.location() != DataLocation::CallData ||
!typeOnStack.isDynamicallyEncoded() ||
!baseType->isDynamicallySized(),
"Copying nested dynamic calldata arrays to memory is not implemented in the old code generator."
);
m_context << u256(0) << Instruction::SWAP1; m_context << u256(0) << Instruction::SWAP1;
// stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos> // stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
auto repeat = m_context.newTag(); auto repeat = m_context.newTag();
@ -1018,6 +1029,7 @@ void CompilerUtils::convertType(
popStackSlots(2 + stackSize); popStackSlots(2 + stackSize);
// Stack: <mem start> // Stack: <mem start>
} }
}
break; break;
} }
case DataLocation::CallData: case DataLocation::CallData:

View File

@ -30,7 +30,7 @@ contract c {
} }
} }
// ==== // ====
// compileViaYul: true // compileViaYul: also
// ---- // ----
// test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65 // test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65
// test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65 // test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65

View File

@ -18,6 +18,6 @@ contract C {
} }
} }
// ==== // ====
// compileViaYul: true // compileViaYul: also
// ---- // ----
// f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1 // f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1

View File

@ -0,0 +1,14 @@
pragma abicoder v2;
contract C {
function g(bytes[2] memory m) internal returns (bytes memory) {
return m[0];
}
function f(bytes[2] calldata c) external returns (bytes memory) {
return g(c);
}
}
// ====
// compileViaYul: also
// ----
// f(bytes[2]): 0x20, 0x40, 0x40, 2, "ab" -> 0x20, 2, "ab"

View File

@ -0,0 +1,18 @@
pragma abicoder v2;
contract C {
function g(bytes[2] memory m) internal {
assert(m[0].length > 1);
assert(m[1].length > 1);
assert(m[0][0] == m[1][0]);
assert(m[0][1] == m[1][1]);
}
function f(bytes[2] calldata c) external {
g(c);
}
}
// ====
// compileViaYul: also
// ----
// f(bytes[2]): 0x20, 0x40, 0x40, 2, "ab" ->
// f(bytes[2]): 0x20, 0x40, 0x40, 1, "a" -> FAILURE

View File

@ -0,0 +1,14 @@
pragma abicoder v2;
contract Test {
struct shouldBug {
uint256[][2] deadly;
}
function killer(uint256[][2] calldata weapon) pure external returns (shouldBug memory) {
return shouldBug(weapon);
}
}
// ====
// compileViaYul: also
// ----
// killer(uint256[][2]): 0x20, 0x40, 0x40, 2, 1, 2 -> 0x20, 0x20, 0x40, 0xa0, 2, 1, 2, 2, 1, 2

View File

@ -10,4 +10,3 @@ contract Test {
} }
// ---- // ----
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.

View File

@ -10,4 +10,3 @@ contract Test {
} }
// ---- // ----
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.

View File

@ -10,4 +10,3 @@ contract Test {
} }
// ---- // ----
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.